forked from GithubBackups/healthchecks
Users can update their email addresses. Fixes #105
This commit is contained in:
parent
8d58a3a361
commit
2393dad09e
@ -1,4 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
class LowercaseEmailField(forms.EmailField):
|
class LowercaseEmailField(forms.EmailField):
|
||||||
@ -21,6 +22,18 @@ class SetPasswordForm(forms.Form):
|
|||||||
password = forms.CharField()
|
password = forms.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeEmailForm(forms.Form):
|
||||||
|
error_css_class = "has-error"
|
||||||
|
email = LowercaseEmailField()
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
v = self.cleaned_data["email"]
|
||||||
|
if User.objects.filter(email=v).exists():
|
||||||
|
raise forms.ValidationError("%s is not available" % v)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class InviteTeamMemberForm(forms.Form):
|
class InviteTeamMemberForm(forms.Form):
|
||||||
email = LowercaseEmailField()
|
email = LowercaseEmailField()
|
||||||
|
|
||||||
|
@ -79,6 +79,18 @@ class Profile(models.Model):
|
|||||||
}
|
}
|
||||||
emails.set_password(self.user.email, ctx)
|
emails.set_password(self.user.email, ctx)
|
||||||
|
|
||||||
|
def send_change_email_link(self):
|
||||||
|
token = str(uuid.uuid4())
|
||||||
|
self.token = make_password(token)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
path = reverse("hc-change-email", args=[token])
|
||||||
|
ctx = {
|
||||||
|
"button_text": "Change Email",
|
||||||
|
"button_url": settings.SITE_ROOT + path
|
||||||
|
}
|
||||||
|
emails.change_email(self.user.email, ctx)
|
||||||
|
|
||||||
def set_api_key(self):
|
def set_api_key(self):
|
||||||
self.api_key = base64.urlsafe_b64encode(os.urandom(24))
|
self.api_key = base64.urlsafe_b64encode(os.urandom(24))
|
||||||
self.save()
|
self.save()
|
||||||
|
41
hc/accounts/tests/test_change_email.py
Normal file
41
hc/accounts/tests/test_change_email.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from django.contrib.auth.hashers import make_password
|
||||||
|
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeEmailTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def test_it_shows_form(self):
|
||||||
|
self.profile.token = make_password("foo")
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
r = self.client.get("/accounts/change_email/foo/")
|
||||||
|
self.assertContains(r, "Change Account's Email Address")
|
||||||
|
|
||||||
|
def test_it_changes_password(self):
|
||||||
|
self.profile.token = make_password("foo")
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
payload = {"email": "alice2@example.org"}
|
||||||
|
self.client.post("/accounts/change_email/foo/", payload)
|
||||||
|
|
||||||
|
self.alice.refresh_from_db()
|
||||||
|
self.assertEqual(self.alice.email, "alice2@example.org")
|
||||||
|
self.assertFalse(self.alice.has_usable_password())
|
||||||
|
|
||||||
|
def test_it_requires_unique_email(self):
|
||||||
|
self.profile.token = make_password("foo")
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
payload = {"email": "bob@example.org"}
|
||||||
|
r = self.client.post("/accounts/change_email/foo/", payload)
|
||||||
|
self.assertContains(r, "bob@example.org is not available")
|
||||||
|
|
||||||
|
self.alice.refresh_from_db()
|
||||||
|
self.assertEqual(self.alice.email, "alice@example.org")
|
@ -22,7 +22,7 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
# And an email should have been sent
|
# And an email should have been sent
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
expected_subject = 'Set password on {0}'.format(getattr(settings, "SITE_NAME"))
|
expected_subject = "Set password on %s" % settings.SITE_NAME
|
||||||
self.assertEqual(mail.outbox[0].subject, expected_subject)
|
self.assertEqual(mail.outbox[0].subject, expected_subject)
|
||||||
|
|
||||||
def test_it_creates_api_key(self):
|
def test_it_creates_api_key(self):
|
||||||
@ -30,7 +30,7 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
form = {"create_api_key": "1"}
|
form = {"create_api_key": "1"}
|
||||||
r = self.client.post("/accounts/profile/", form)
|
r = self.client.post("/accounts/profile/", form)
|
||||||
assert r.status_code == 200
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
self.alice.profile.refresh_from_db()
|
self.alice.profile.refresh_from_db()
|
||||||
api_key = self.alice.profile.api_key
|
api_key = self.alice.profile.api_key
|
||||||
@ -64,7 +64,7 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
||||||
r = self.client.post("/accounts/profile/", form)
|
r = self.client.post("/accounts/profile/", form)
|
||||||
assert r.status_code == 200
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
member_emails = set()
|
member_emails = set()
|
||||||
for member in self.alice.profile.member_set.all():
|
for member in self.alice.profile.member_set.all():
|
||||||
@ -90,7 +90,7 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
form = {"remove_team_member": "1", "email": "bob@example.org"}
|
form = {"remove_team_member": "1", "email": "bob@example.org"}
|
||||||
r = self.client.post("/accounts/profile/", form)
|
r = self.client.post("/accounts/profile/", form)
|
||||||
assert r.status_code == 200
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
self.assertEqual(Member.objects.count(), 0)
|
self.assertEqual(Member.objects.count(), 0)
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
form = {"set_team_name": "1", "team_name": "Alpha Team"}
|
form = {"set_team_name": "1", "team_name": "Alpha Team"}
|
||||||
r = self.client.post("/accounts/profile/", form)
|
r = self.client.post("/accounts/profile/", form)
|
||||||
assert r.status_code == 200
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
self.alice.profile.refresh_from_db()
|
self.alice.profile.refresh_from_db()
|
||||||
self.assertEqual(self.alice.profile.team_name, "Alpha Team")
|
self.assertEqual(self.alice.profile.team_name, "Alpha Team")
|
||||||
@ -123,3 +123,20 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
# to user's default team.
|
# to user's default team.
|
||||||
self.bobs_profile.refresh_from_db()
|
self.bobs_profile.refresh_from_db()
|
||||||
self.assertEqual(self.bobs_profile.current_team, self.bobs_profile)
|
self.assertEqual(self.bobs_profile.current_team, self.bobs_profile)
|
||||||
|
|
||||||
|
def test_it_sends_change_email_link(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"change_email": "1"}
|
||||||
|
r = self.client.post("/accounts/profile/", form)
|
||||||
|
assert r.status_code == 302
|
||||||
|
|
||||||
|
# profile.token should be set now
|
||||||
|
self.alice.profile.refresh_from_db()
|
||||||
|
token = self.alice.profile.token
|
||||||
|
self.assertTrue(len(token) > 10)
|
||||||
|
|
||||||
|
# And an email should have been sent
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
expected_subject = "Change email address on %s" % settings.SITE_NAME
|
||||||
|
self.assertEqual(mail.outbox[0].subject, expected_subject)
|
||||||
|
@ -7,8 +7,8 @@ urlpatterns = [
|
|||||||
url(r'^login_link_sent/$',
|
url(r'^login_link_sent/$',
|
||||||
views.login_link_sent, name="hc-login-link-sent"),
|
views.login_link_sent, name="hc-login-link-sent"),
|
||||||
|
|
||||||
url(r'^set_password_link_sent/$',
|
url(r'^link_sent/$',
|
||||||
views.set_password_link_sent, name="hc-set-password-link-sent"),
|
views.link_sent, name="hc-link-sent"),
|
||||||
|
|
||||||
url(r'^check_token/([\w-]+)/([\w-]+)/$',
|
url(r'^check_token/([\w-]+)/([\w-]+)/$',
|
||||||
views.check_token, name="hc-check-token"),
|
views.check_token, name="hc-check-token"),
|
||||||
@ -24,8 +24,13 @@ urlpatterns = [
|
|||||||
url(r'^set_password/([\w-]+)/$',
|
url(r'^set_password/([\w-]+)/$',
|
||||||
views.set_password, name="hc-set-password"),
|
views.set_password, name="hc-set-password"),
|
||||||
|
|
||||||
|
url(r'^change_email/done/$',
|
||||||
|
views.change_email_done, name="hc-change-email-done"),
|
||||||
|
|
||||||
|
url(r'^change_email/([\w-]+)/$',
|
||||||
|
views.change_email, name="hc-change-email"),
|
||||||
|
|
||||||
url(r'^switch_team/([\w-]+)/$',
|
url(r'^switch_team/([\w-]+)/$',
|
||||||
views.switch_team, name="hc-switch-team"),
|
views.switch_team, name="hc-switch-team"),
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -13,9 +13,10 @@ from django.core import signing
|
|||||||
from django.http import HttpResponseForbidden, HttpResponseBadRequest
|
from django.http import HttpResponseForbidden, HttpResponseBadRequest
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
from hc.accounts.forms import (EmailPasswordForm, InviteTeamMemberForm,
|
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
|
||||||
RemoveTeamMemberForm, ReportSettingsForm,
|
InviteTeamMemberForm, RemoveTeamMemberForm,
|
||||||
SetPasswordForm, TeamNameForm)
|
ReportSettingsForm, SetPasswordForm,
|
||||||
|
TeamNameForm)
|
||||||
from hc.accounts.models import Profile, Member
|
from hc.accounts.models import Profile, Member
|
||||||
from hc.api.models import Channel, Check
|
from hc.api.models import Channel, Check
|
||||||
from hc.lib.badges import get_badge_url
|
from hc.lib.badges import get_badge_url
|
||||||
@ -114,8 +115,8 @@ def login_link_sent(request):
|
|||||||
return render(request, "accounts/login_link_sent.html")
|
return render(request, "accounts/login_link_sent.html")
|
||||||
|
|
||||||
|
|
||||||
def set_password_link_sent(request):
|
def link_sent(request):
|
||||||
return render(request, "accounts/set_password_link_sent.html")
|
return render(request, "accounts/link_sent.html")
|
||||||
|
|
||||||
|
|
||||||
def check_token(request, username, token):
|
def check_token(request, username, token):
|
||||||
@ -156,21 +157,33 @@ def profile(request):
|
|||||||
profile.current_team = profile
|
profile.current_team = profile
|
||||||
profile.save()
|
profile.save()
|
||||||
|
|
||||||
show_api_key = False
|
ctx = {
|
||||||
|
"page": "profile",
|
||||||
|
"profile": profile,
|
||||||
|
"show_api_key": False,
|
||||||
|
"api_status": "default",
|
||||||
|
"team_status": "default"
|
||||||
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if "set_password" in request.POST:
|
if "change_email" in request.POST:
|
||||||
|
profile.send_change_email_link()
|
||||||
|
return redirect("hc-link-sent")
|
||||||
|
elif "set_password" in request.POST:
|
||||||
profile.send_set_password_link()
|
profile.send_set_password_link()
|
||||||
return redirect("hc-set-password-link-sent")
|
return redirect("hc-link-sent")
|
||||||
elif "create_api_key" in request.POST:
|
elif "create_api_key" in request.POST:
|
||||||
profile.set_api_key()
|
profile.set_api_key()
|
||||||
show_api_key = True
|
ctx["show_api_key"] = True
|
||||||
messages.success(request, "The API key has been created!")
|
ctx["api_key_created"] = True
|
||||||
|
ctx["api_status"] = "success"
|
||||||
elif "revoke_api_key" in request.POST:
|
elif "revoke_api_key" in request.POST:
|
||||||
profile.api_key = ""
|
profile.api_key = ""
|
||||||
profile.save()
|
profile.save()
|
||||||
messages.info(request, "The API key has been revoked!")
|
ctx["api_key_revoked"] = True
|
||||||
|
ctx["api_status"] = "info"
|
||||||
elif "show_api_key" in request.POST:
|
elif "show_api_key" in request.POST:
|
||||||
show_api_key = True
|
ctx["show_api_key"] = True
|
||||||
elif "invite_team_member" in request.POST:
|
elif "invite_team_member" in request.POST:
|
||||||
if not profile.team_access_allowed:
|
if not profile.team_access_allowed:
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
@ -185,7 +198,9 @@ def profile(request):
|
|||||||
user = _make_user(email)
|
user = _make_user(email)
|
||||||
|
|
||||||
profile.invite(user)
|
profile.invite(user)
|
||||||
messages.success(request, "Invitation to %s sent!" % email)
|
ctx["team_member_invited"] = email
|
||||||
|
ctx["team_status"] = "success"
|
||||||
|
|
||||||
elif "remove_team_member" in request.POST:
|
elif "remove_team_member" in request.POST:
|
||||||
form = RemoveTeamMemberForm(request.POST)
|
form = RemoveTeamMemberForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@ -198,7 +213,8 @@ def profile(request):
|
|||||||
Member.objects.filter(team=profile,
|
Member.objects.filter(team=profile,
|
||||||
user=farewell_user).delete()
|
user=farewell_user).delete()
|
||||||
|
|
||||||
messages.info(request, "%s removed from team!" % email)
|
ctx["team_member_removed"] = email
|
||||||
|
ctx["team_status"] = "info"
|
||||||
elif "set_team_name" in request.POST:
|
elif "set_team_name" in request.POST:
|
||||||
if not profile.team_access_allowed:
|
if not profile.team_access_allowed:
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
@ -207,13 +223,8 @@ def profile(request):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
profile.team_name = form.cleaned_data["team_name"]
|
profile.team_name = form.cleaned_data["team_name"]
|
||||||
profile.save()
|
profile.save()
|
||||||
messages.success(request, "Team Name updated!")
|
ctx["team_name_updated"] = True
|
||||||
|
ctx["team_status"] = "success"
|
||||||
ctx = {
|
|
||||||
"page": "profile",
|
|
||||||
"profile": profile,
|
|
||||||
"show_api_key": show_api_key
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, "accounts/profile.html", ctx)
|
return render(request, "accounts/profile.html", ctx)
|
||||||
|
|
||||||
@ -301,6 +312,33 @@ def set_password(request, token):
|
|||||||
return render(request, "accounts/set_password.html", {})
|
return render(request, "accounts/set_password.html", {})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def change_email(request, token):
|
||||||
|
profile = request.user.profile
|
||||||
|
if not check_password(token, profile.token):
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
form = ChangeEmailForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
request.user.email = form.cleaned_data["email"]
|
||||||
|
request.user.set_unusable_password()
|
||||||
|
request.user.save()
|
||||||
|
|
||||||
|
profile.token = ""
|
||||||
|
profile.save()
|
||||||
|
|
||||||
|
return redirect("hc-change-email-done")
|
||||||
|
else:
|
||||||
|
form = ChangeEmailForm()
|
||||||
|
|
||||||
|
return render(request, "accounts/change_email.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
|
def change_email_done(request):
|
||||||
|
return render(request, "accounts/change_email_done.html")
|
||||||
|
|
||||||
|
|
||||||
def unsubscribe_reports(request, username):
|
def unsubscribe_reports(request, username):
|
||||||
try:
|
try:
|
||||||
signing.Signer().unsign(request.GET.get("token"))
|
signing.Signer().unsign(request.GET.get("token"))
|
||||||
|
@ -44,6 +44,10 @@ def set_password(to, ctx):
|
|||||||
send("set-password", to, ctx)
|
send("set-password", to, ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def change_email(to, ctx):
|
||||||
|
send("change-email", to, ctx)
|
||||||
|
|
||||||
|
|
||||||
def alert(to, ctx, headers={}):
|
def alert(to, ctx, headers={}):
|
||||||
send("alert", to, ctx, headers)
|
send("alert", to, ctx, headers)
|
||||||
|
|
||||||
|
18
static/css/profile.css
Normal file
18
static/css/profile.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.panel-success .panel-footer {
|
||||||
|
background: #dff0d8;
|
||||||
|
color: #3c763d;
|
||||||
|
font-size: small;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 0;
|
||||||
|
padding: 6px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-info .panel-footer {
|
||||||
|
background: #d9edf7;
|
||||||
|
color: #31708f;
|
||||||
|
font-size: small;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 0;
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
|
72
templates/accounts/change_email.html
Normal file
72
templates/accounts/change_email.html
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load hc_extras %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-sm-offset-3">
|
||||||
|
<div class="hc-dialog">
|
||||||
|
<h1>Change Account's Email Address</h1>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<p>
|
||||||
|
Your account's email address is used for sending
|
||||||
|
the sign-in links and monthly reports.
|
||||||
|
|
||||||
|
<strong>
|
||||||
|
Make sure you can receive emails at the new address.
|
||||||
|
</strong>
|
||||||
|
|
||||||
|
Otherwise, you may get locked out of
|
||||||
|
your {% site_name %} account.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if request.user.has_usable_password %}
|
||||||
|
<p>
|
||||||
|
Note: Changing the email address will also
|
||||||
|
<strong>reset your current password</strong>
|
||||||
|
and log you out.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="form-horizontal" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Current Email</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
disabled
|
||||||
|
value="{{ request.user.email }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group {{ form.email.css_classes }}">
|
||||||
|
<label for="ce-email" class="col-sm-3 control-label">New Email</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
class="form-control"
|
||||||
|
id="ce-email"
|
||||||
|
name="email"
|
||||||
|
placeholder="you@example.org">
|
||||||
|
{% if form.email.errors %}
|
||||||
|
<div class="help-block">
|
||||||
|
{{ form.email.errors|join:"" }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix">
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary pull-right">
|
||||||
|
Change Email
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
18
templates/accounts/change_email_done.html
Normal file
18
templates/accounts/change_email_done.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-sm-offset-3">
|
||||||
|
<div class="hc-dialog">
|
||||||
|
<h1>Email Address Updated</h1>
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
Your account's email address has been updated.
|
||||||
|
You can now <a href="{% url 'hc-login' %}">sign in</a>
|
||||||
|
with the new email address.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -7,8 +7,8 @@
|
|||||||
<h1>Email with Instructions Sent!</h1>
|
<h1>Email with Instructions Sent!</h1>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
We've sent you an email with instructions to set
|
We've sent you an email with further instructions.
|
||||||
a password for your account. Please check your inbox!
|
Please check your inbox!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
@ -19,7 +19,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li class="active"><a href="{% url 'hc-profile' %}">Account</a></li>
|
<li class="active"><a href="{% url 'hc-profile' %}">Account</a></li>
|
||||||
@ -33,17 +32,29 @@
|
|||||||
<div class="panel-body settings-block">
|
<div class="panel-body settings-block">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h2>Set Password</h2>
|
<h2>Email and Password</h2>
|
||||||
|
<p>
|
||||||
|
Your account's email address is
|
||||||
|
<code>{{ request.user.email }}</code>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="change_email"
|
||||||
|
class="btn btn-default pull-right">Change Email</button>
|
||||||
|
</p>
|
||||||
|
<p class="clearfix"></p>
|
||||||
|
<p>
|
||||||
Attach a password to your {% site_name %} account
|
Attach a password to your {% site_name %} account
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
name="set_password"
|
name="set_password"
|
||||||
class="btn btn-default pull-right">Set Password</button>
|
class="btn btn-default pull-right">Set Password</button>
|
||||||
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-{{ api_status }}">
|
||||||
<div class="panel-body settings-block">
|
<div class="panel-body settings-block">
|
||||||
<h2>API Access</h2>
|
<h2>API Access</h2>
|
||||||
{% if profile.api_key %}
|
{% if profile.api_key %}
|
||||||
@ -78,9 +89,21 @@
|
|||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if api_key_created %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
API key created
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if api_key_revoked %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
API key revoked
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-{{ team_status }}">
|
||||||
<div class="panel-body settings-block">
|
<div class="panel-body settings-block">
|
||||||
<h2>Team Access</h2>
|
<h2>Team Access</h2>
|
||||||
{% if profile.member_set.count %}
|
{% if profile.member_set.count %}
|
||||||
@ -135,6 +158,24 @@
|
|||||||
data-target="#invite-team-member-modal">Invite a Team Member</a>
|
data-target="#invite-team-member-modal">Invite a Team Member</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if team_member_invited %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
{{ team_member_invited }} invited to team
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if team_member_removed %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
{{ team_member_removed }} removed from team
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if team_name_updated %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
Team name updated
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/last_ping.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/last_ping.css' %}" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{% static 'css/profile.css' %}" type="text/css">
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
</head>
|
</head>
|
||||||
<body class="page-{{ page }}">
|
<body class="page-{{ page }}">
|
||||||
|
13
templates/emails/change-email-body-html.html
Normal file
13
templates/emails/change-email-body-html.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{% extends "emails/base.html" %}
|
||||||
|
{% load hc_extras %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Hello,<br />
|
||||||
|
To change the email address for your account on {% site_name %}, please press
|
||||||
|
the button below:</p>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content_more %}
|
||||||
|
Regards,<br />
|
||||||
|
The {% escaped_site_name %} Team
|
||||||
|
{% endblock %}
|
11
templates/emails/change-email-body-text.html
Normal file
11
templates/emails/change-email-body-text.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% load hc_extras %}
|
||||||
|
Hello,
|
||||||
|
|
||||||
|
Here's a link to change the email address for your account on {% site_name %}:
|
||||||
|
|
||||||
|
{{ button_url }}
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
Regards,
|
||||||
|
{% site_name %}
|
2
templates/emails/change-email-subject.html
Normal file
2
templates/emails/change-email-subject.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{% load hc_extras %}
|
||||||
|
Change email address on {% site_name %}
|
Loading…
x
Reference in New Issue
Block a user