forked from GithubBackups/healthchecks
Move project-specific settings to a new "Project Settings" page
This commit is contained in:
parent
64158c83a8
commit
b013a92c43
@ -4,13 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- Database schema: set Check.user to not null
|
|
||||||
- Database schema: add uniqueness constraint to Check.code
|
- Database schema: add uniqueness constraint to Check.code
|
||||||
- Database schema: add Ping.kind field
|
- Database schema: add Ping.kind field. Remove "start" and "fail" fields.
|
||||||
- Database schema: remove Ping.start and Ping.fail fields
|
|
||||||
- Add "Email Settings..." dialog and "Subject Must Contain" setting
|
- Add "Email Settings..." dialog and "Subject Must Contain" setting
|
||||||
- Database schema: add the Project model
|
- Database schema: add the Project model
|
||||||
|
- Move project-specific settings to a new "Project Settings" page
|
||||||
|
|
||||||
|
|
||||||
## 1.4.0 - 2018-12-25
|
## 1.4.0 - 2018-12-25
|
||||||
|
@ -95,5 +95,5 @@ class RemoveTeamMemberForm(forms.Form):
|
|||||||
email = LowercaseEmailField()
|
email = LowercaseEmailField()
|
||||||
|
|
||||||
|
|
||||||
class TeamNameForm(forms.Form):
|
class ProjectNameForm(forms.Form):
|
||||||
team_name = forms.CharField(max_length=200, required=True)
|
name = forms.CharField(max_length=200, required=True)
|
||||||
|
@ -79,7 +79,7 @@ class Profile(models.Model):
|
|||||||
def check_token(self, token, salt):
|
def check_token(self, token, salt):
|
||||||
return salt in self.token and check_password(token, self.token)
|
return salt in self.token and check_password(token, self.token)
|
||||||
|
|
||||||
def send_instant_login_link(self, inviting_profile=None, redirect_url=None):
|
def send_instant_login_link(self, inviting_project=None, redirect_url=None):
|
||||||
token = self.prepare_token("login")
|
token = self.prepare_token("login")
|
||||||
path = reverse("hc-check-token", args=[self.user.username, token])
|
path = reverse("hc-check-token", args=[self.user.username, token])
|
||||||
if redirect_url:
|
if redirect_url:
|
||||||
@ -88,7 +88,7 @@ class Profile(models.Model):
|
|||||||
ctx = {
|
ctx = {
|
||||||
"button_text": "Sign In",
|
"button_text": "Sign In",
|
||||||
"button_url": settings.SITE_ROOT + path,
|
"button_url": settings.SITE_ROOT + path,
|
||||||
"inviting_profile": inviting_profile
|
"inviting_project": inviting_project
|
||||||
}
|
}
|
||||||
emails.login(self.user.email, ctx)
|
emails.login(self.user.email, ctx)
|
||||||
|
|
||||||
@ -166,20 +166,6 @@ class Profile(models.Model):
|
|||||||
emails.report(self.user.email, ctx, headers)
|
emails.report(self.user.email, ctx, headers)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def can_invite(self):
|
|
||||||
return self.member_count() < self.team_limit
|
|
||||||
|
|
||||||
def invite(self, user):
|
|
||||||
project = self.get_own_project()
|
|
||||||
Member.objects.create(user=user, project=project)
|
|
||||||
|
|
||||||
# Switch the invited user over to the new team so they
|
|
||||||
# notice the new team on next visit:
|
|
||||||
user.profile.current_project = project
|
|
||||||
user.profile.save()
|
|
||||||
|
|
||||||
user.profile.send_instant_login_link(self)
|
|
||||||
|
|
||||||
def sms_sent_this_month(self):
|
def sms_sent_this_month(self):
|
||||||
# IF last_sms_date was never set, we have not sent any messages yet.
|
# IF last_sms_date was never set, we have not sent any messages yet.
|
||||||
if not self.last_sms_date:
|
if not self.last_sms_date:
|
||||||
@ -210,12 +196,6 @@ class Profile(models.Model):
|
|||||||
|
|
||||||
return project
|
return project
|
||||||
|
|
||||||
def member_count(self):
|
|
||||||
return Member.objects.filter(project__owner__profile=self).count()
|
|
||||||
|
|
||||||
def members(self):
|
|
||||||
return Member.objects.filter(project__owner__profile=self).all()
|
|
||||||
|
|
||||||
|
|
||||||
class Project(models.Model):
|
class Project(models.Model):
|
||||||
code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||||
@ -242,6 +222,19 @@ class Project(models.Model):
|
|||||||
self.api_key_readonly = urlsafe_b64encode(os.urandom(24)).decode()
|
self.api_key_readonly = urlsafe_b64encode(os.urandom(24)).decode()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def can_invite(self):
|
||||||
|
return self.member_set.count() < self.owner_profile.team_limit
|
||||||
|
|
||||||
|
def invite(self, user):
|
||||||
|
Member.objects.create(user=user, project=self)
|
||||||
|
|
||||||
|
# Switch the invited user over to the new team so they
|
||||||
|
# notice the new team on next visit:
|
||||||
|
user.profile.current_project = self
|
||||||
|
user.profile.save()
|
||||||
|
|
||||||
|
user.profile.send_instant_login_link(self)
|
||||||
|
|
||||||
def set_next_nag_date(self):
|
def set_next_nag_date(self):
|
||||||
""" Set next_nag_date on profiles of all members of this project. """
|
""" Set next_nag_date on profiles of all members of this project. """
|
||||||
|
|
||||||
|
@ -27,45 +27,6 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
expected_subject = "Set password on %s" % 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_shows_api_keys(self):
|
|
||||||
self.project.api_key_readonly = "R" * 32
|
|
||||||
self.project.save()
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"show_api_keys": "1"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
|
|
||||||
self.assertContains(r, "X" * 32)
|
|
||||||
self.assertContains(r, "R" * 32)
|
|
||||||
|
|
||||||
def test_it_creates_api_key(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"create_api_keys": "1"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
|
|
||||||
self.project.refresh_from_db()
|
|
||||||
api_key = self.project.api_key
|
|
||||||
self.assertTrue(len(api_key) > 10)
|
|
||||||
self.assertFalse("b'" in api_key)
|
|
||||||
|
|
||||||
def test_it_revokes_api_key(self):
|
|
||||||
self.project.api_key_readonly = "R" * 32
|
|
||||||
self.project.save()
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"revoke_api_keys": "1"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
self.project.refresh_from_db()
|
|
||||||
self.assertEqual(self.project.api_key, "")
|
|
||||||
self.assertEqual(self.project.api_key_readonly, "")
|
|
||||||
|
|
||||||
def test_it_sends_report(self):
|
def test_it_sends_report(self):
|
||||||
check = Check(project=self.project, name="Test Check")
|
check = Check(project=self.project, name="Test Check")
|
||||||
check.last_ping = now()
|
check.last_ping = now()
|
||||||
@ -132,59 +93,6 @@ class ProfileTestCase(BaseTestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(mail.outbox), 0)
|
self.assertEqual(len(mail.outbox), 0)
|
||||||
|
|
||||||
def test_it_adds_team_member(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
|
|
||||||
members = self.project.member_set.all()
|
|
||||||
self.assertEqual(members.count(), 2)
|
|
||||||
|
|
||||||
member = Member.objects.get(project=self.project,
|
|
||||||
user__email="frank@example.org")
|
|
||||||
|
|
||||||
profile = member.user.profile
|
|
||||||
self.assertEqual(profile.current_project, self.project)
|
|
||||||
|
|
||||||
# And an email should have been sent
|
|
||||||
subj = ('You have been invited to join'
|
|
||||||
' alice@example.org on %s' % settings.SITE_NAME)
|
|
||||||
self.assertEqual(mail.outbox[0].subject, subj)
|
|
||||||
|
|
||||||
def test_it_checks_team_size(self):
|
|
||||||
self.profile.team_limit = 0
|
|
||||||
self.profile.save()
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 403)
|
|
||||||
|
|
||||||
def test_it_removes_team_member(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"remove_team_member": "1", "email": "bob@example.org"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
|
|
||||||
self.assertEqual(Member.objects.count(), 0)
|
|
||||||
|
|
||||||
self.bobs_profile.refresh_from_db()
|
|
||||||
self.assertEqual(self.bobs_profile.current_project, None)
|
|
||||||
|
|
||||||
def test_it_sets_team_name(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
form = {"set_team_name": "1", "team_name": "Alpha Team"}
|
|
||||||
r = self.client.post("/accounts/profile/", form)
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
|
|
||||||
self.project.refresh_from_db()
|
|
||||||
self.assertEqual(self.project.name, "Alpha Team")
|
|
||||||
|
|
||||||
def test_it_switches_to_own_team(self):
|
def test_it_switches_to_own_team(self):
|
||||||
self.client.login(username="bob@example.org", password="password")
|
self.client.login(username="bob@example.org", password="password")
|
||||||
|
|
||||||
|
104
hc/accounts/tests/test_project.py
Normal file
104
hc/accounts/tests/test_project.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
from django.core import mail
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
from hc.accounts.models import Member
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileTestCase(BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(ProfileTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.url = "/projects/%s/settings/" % self.project.code
|
||||||
|
|
||||||
|
def test_it_shows_api_keys(self):
|
||||||
|
self.project.api_key_readonly = "R" * 32
|
||||||
|
self.project.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"show_api_keys": "1"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
self.assertContains(r, "X" * 32)
|
||||||
|
self.assertContains(r, "R" * 32)
|
||||||
|
|
||||||
|
def test_it_creates_api_key(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"create_api_keys": "1"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
self.project.refresh_from_db()
|
||||||
|
api_key = self.project.api_key
|
||||||
|
self.assertTrue(len(api_key) > 10)
|
||||||
|
self.assertFalse("b'" in api_key)
|
||||||
|
|
||||||
|
def test_it_revokes_api_key(self):
|
||||||
|
self.project.api_key_readonly = "R" * 32
|
||||||
|
self.project.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"revoke_api_keys": "1"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
self.project.refresh_from_db()
|
||||||
|
self.assertEqual(self.project.api_key, "")
|
||||||
|
self.assertEqual(self.project.api_key_readonly, "")
|
||||||
|
|
||||||
|
def test_it_adds_team_member(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
members = self.project.member_set.all()
|
||||||
|
self.assertEqual(members.count(), 2)
|
||||||
|
|
||||||
|
member = Member.objects.get(project=self.project,
|
||||||
|
user__email="frank@example.org")
|
||||||
|
|
||||||
|
profile = member.user.profile
|
||||||
|
self.assertEqual(profile.current_project, self.project)
|
||||||
|
|
||||||
|
# And an email should have been sent
|
||||||
|
subj = ('You have been invited to join'
|
||||||
|
' alice@example.org on %s' % settings.SITE_NAME)
|
||||||
|
self.assertEqual(mail.outbox[0].subject, subj)
|
||||||
|
|
||||||
|
def test_it_checks_team_size(self):
|
||||||
|
self.profile.team_limit = 0
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"invite_team_member": "1", "email": "frank@example.org"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
|
def test_it_removes_team_member(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"remove_team_member": "1", "email": "bob@example.org"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
self.assertEqual(Member.objects.count(), 0)
|
||||||
|
|
||||||
|
self.bobs_profile.refresh_from_db()
|
||||||
|
self.assertEqual(self.bobs_profile.current_project, None)
|
||||||
|
|
||||||
|
def test_it_sets_project_name(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
form = {"set_project_name": "1", "name": "Alpha Team"}
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
self.project.refresh_from_db()
|
||||||
|
self.assertEqual(self.project.name, "Alpha Team")
|
@ -19,7 +19,7 @@ from django.views.decorators.http import require_POST
|
|||||||
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
|
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
|
||||||
InviteTeamMemberForm, RemoveTeamMemberForm,
|
InviteTeamMemberForm, RemoveTeamMemberForm,
|
||||||
ReportSettingsForm, SetPasswordForm,
|
ReportSettingsForm, SetPasswordForm,
|
||||||
TeamNameForm, AvailableEmailForm,
|
ProjectNameForm, AvailableEmailForm,
|
||||||
ExistingEmailForm)
|
ExistingEmailForm)
|
||||||
from hc.accounts.models import Profile, Project, Member
|
from hc.accounts.models import Profile, Project, Member
|
||||||
from hc.api.models import Channel, Check
|
from hc.api.models import Channel, Check
|
||||||
@ -194,10 +194,7 @@ def profile(request):
|
|||||||
ctx = {
|
ctx = {
|
||||||
"page": "profile",
|
"page": "profile",
|
||||||
"profile": profile,
|
"profile": profile,
|
||||||
"project": project,
|
"project": project
|
||||||
"show_api_keys": False,
|
|
||||||
"api_status": "default",
|
|
||||||
"team_status": "default"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@ -207,7 +204,27 @@ def profile(request):
|
|||||||
elif "set_password" in request.POST:
|
elif "set_password" in request.POST:
|
||||||
profile.send_set_password_link()
|
profile.send_set_password_link()
|
||||||
return redirect("hc-link-sent")
|
return redirect("hc-link-sent")
|
||||||
elif "create_api_keys" in request.POST:
|
|
||||||
|
return render(request, "accounts/profile.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def project(request, code):
|
||||||
|
project = Project.objects.get(code=code, owner_id=request.user.id)
|
||||||
|
profile = project.owner_profile
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"page": "profile",
|
||||||
|
"project": project,
|
||||||
|
"profile": profile,
|
||||||
|
"show_api_keys": False,
|
||||||
|
"project_name_status": "default",
|
||||||
|
"api_status": "default",
|
||||||
|
"team_status": "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
if "create_api_keys" in request.POST:
|
||||||
project.set_api_keys()
|
project.set_api_keys()
|
||||||
project.save()
|
project.save()
|
||||||
|
|
||||||
@ -224,7 +241,7 @@ def profile(request):
|
|||||||
elif "show_api_keys" in request.POST:
|
elif "show_api_keys" in request.POST:
|
||||||
ctx["show_api_keys"] = True
|
ctx["show_api_keys"] = True
|
||||||
elif "invite_team_member" in request.POST:
|
elif "invite_team_member" in request.POST:
|
||||||
if not profile.can_invite():
|
if not project.can_invite():
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
form = InviteTeamMemberForm(request.POST)
|
form = InviteTeamMemberForm(request.POST)
|
||||||
@ -236,7 +253,7 @@ def profile(request):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
user = _make_user(email)
|
user = _make_user(email)
|
||||||
|
|
||||||
profile.invite(user)
|
project.invite(user)
|
||||||
ctx["team_member_invited"] = email
|
ctx["team_member_invited"] = email
|
||||||
ctx["team_status"] = "success"
|
ctx["team_status"] = "success"
|
||||||
|
|
||||||
@ -249,21 +266,27 @@ def profile(request):
|
|||||||
farewell_user.profile.current_project = None
|
farewell_user.profile.current_project = None
|
||||||
farewell_user.profile.save()
|
farewell_user.profile.save()
|
||||||
|
|
||||||
Member.objects.filter(project=request.project,
|
Member.objects.filter(project=project,
|
||||||
user=farewell_user).delete()
|
user=farewell_user).delete()
|
||||||
|
|
||||||
ctx["team_member_removed"] = email
|
ctx["team_member_removed"] = email
|
||||||
ctx["team_status"] = "info"
|
ctx["team_status"] = "info"
|
||||||
elif "set_team_name" in request.POST:
|
elif "set_project_name" in request.POST:
|
||||||
form = TeamNameForm(request.POST)
|
form = ProjectNameForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
request.project.name = form.cleaned_data["team_name"]
|
project.name = form.cleaned_data["name"]
|
||||||
request.project.save()
|
project.save()
|
||||||
|
|
||||||
ctx["team_name_updated"] = True
|
if request.project.id == project.id:
|
||||||
ctx["team_status"] = "success"
|
request.project = project
|
||||||
|
|
||||||
return render(request, "accounts/profile.html", ctx)
|
ctx["project_name_updated"] = True
|
||||||
|
ctx["project_name_status"] = "success"
|
||||||
|
|
||||||
|
# Count members right before rendering the template, in case
|
||||||
|
# we just invited or removed someone
|
||||||
|
ctx["num_members"] = project.member_set.count()
|
||||||
|
return render(request, "accounts/project.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -34,7 +34,7 @@ class PricingTestCase(BaseTestCase):
|
|||||||
self.client.login(username="bob@example.org", password="password")
|
self.client.login(username="bob@example.org", password="password")
|
||||||
|
|
||||||
r = self.client.get("/pricing/")
|
r = self.client.get("/pricing/")
|
||||||
self.assertContains(r, "To manage this team")
|
self.assertContains(r, "To manage billing for this project")
|
||||||
|
|
||||||
def test_it_shows_active_plan(self):
|
def test_it_shows_active_plan(self):
|
||||||
self.sub = Subscription(user=self.alice)
|
self.sub = Subscription(user=self.alice)
|
||||||
|
@ -60,8 +60,6 @@ def billing(request):
|
|||||||
"profile": request.profile,
|
"profile": request.profile,
|
||||||
"sub": sub,
|
"sub": sub,
|
||||||
"num_checks": Check.objects.filter(project__owner=request.user).count(),
|
"num_checks": Check.objects.filter(project__owner=request.user).count(),
|
||||||
"team_size": request.profile.member_count() + 1,
|
|
||||||
"team_max": request.profile.team_limit + 1,
|
|
||||||
"send_invoices_status": send_invoices_status,
|
"send_invoices_status": send_invoices_status,
|
||||||
"set_plan_status": "default",
|
"set_plan_status": "default",
|
||||||
"address_status": "default",
|
"address_status": "default",
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
from hc.accounts.views import login as hc_login
|
from hc.accounts import views as accounts_views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/login/', hc_login),
|
path('admin/login/', accounts_views.login),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('accounts/', include('hc.accounts.urls')),
|
path('accounts/', include('hc.accounts.urls')),
|
||||||
|
path('projects/<uuid:code>/settings/', accounts_views.project, name="hc-project-settings"),
|
||||||
path('', include('hc.api.urls')),
|
path('', include('hc.api.urls')),
|
||||||
path('', include('hc.front.urls')),
|
path('', include('hc.front.urls')),
|
||||||
path('', include('hc.payments.urls'))
|
path('', include('hc.payments.urls'))
|
||||||
|
@ -58,19 +58,6 @@
|
|||||||
<span>{{ num_checks }} of {{ profile.check_limit }}</span>
|
<span>{{ num_checks }} of {{ profile.check_limit }}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>Team Size</td>
|
|
||||||
<td {% if team_size >= profile.team_limit %} class="at-limit" {% endif %}>
|
|
||||||
<span>
|
|
||||||
{{ team_size }} of
|
|
||||||
{% if profile.team_limit == 500 %}
|
|
||||||
unlimited
|
|
||||||
{% else %}
|
|
||||||
{{ team_max }}
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h1 class="settings-title">Settings</h1>
|
<h1 class="settings-title">
|
||||||
|
Settings
|
||||||
|
<small>{{ request.user.email }}</small>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@ -57,141 +60,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-{{ api_status }}">
|
|
||||||
<div class="panel-body settings-block">
|
|
||||||
<h2>API Access</h2>
|
|
||||||
{% if project.api_key %}
|
|
||||||
{% if show_api_keys %}
|
|
||||||
<p>
|
|
||||||
API key: <br />
|
|
||||||
<code>{{ project.api_key }}</code>
|
|
||||||
</p>
|
|
||||||
{% if project.api_key_readonly %}
|
|
||||||
<p>
|
|
||||||
API key (read-only): <br />
|
|
||||||
<code>{{ project.api_key_readonly }}</code>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
<button
|
|
||||||
data-toggle="modal"
|
|
||||||
data-target="#revoke-api-key-modal"
|
|
||||||
class="btn btn-danger pull-right">Revoke</button>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<form method="post">
|
|
||||||
<span class="icon-ok"></span>
|
|
||||||
API access is enabled.
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="show_api_keys"
|
|
||||||
class="btn btn-default pull-right">Show API keys</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<span class="icon-cancel"></span>
|
|
||||||
API access is disabled.
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="create_api_keys"
|
|
||||||
class="btn btn-default pull-right">Create API keys</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if api_keys_created %}
|
|
||||||
<div class="panel-footer">
|
|
||||||
API keys created
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if api_keys_revoked %}
|
|
||||||
<div class="panel-footer">
|
|
||||||
API keys revoked
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel panel-{{ team_status }}">
|
|
||||||
<div class="panel-body settings-block">
|
|
||||||
<h2>Team Access</h2>
|
|
||||||
{% if profile.member_count %}
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td>{{ profile.user.email }}</td>
|
|
||||||
<td>Owner</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
{% for member in profile.members %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ member.user.email }} </td>
|
|
||||||
<td>Member</td>
|
|
||||||
<td>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
data-email="{{ member.user.email }}"
|
|
||||||
class="pull-right member-remove">Remove</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<p>
|
|
||||||
<strong>Invite team members to your account.</strong>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Share access to your checks and configured integrations
|
|
||||||
without having to share a login.
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{% if not profile.can_invite %}
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<strong>Team size limit reached.</strong>
|
|
||||||
To invite more members to your team, please
|
|
||||||
<a href="{% url 'hc-pricing' %}">upgrade your account!</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="btn btn-default"
|
|
||||||
data-toggle="modal"
|
|
||||||
data-target="#set-team-name-modal">Set Team Name</a>
|
|
||||||
|
|
||||||
{% if profile.can_invite %}
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="btn btn-primary pull-right"
|
|
||||||
data-toggle="modal"
|
|
||||||
data-target="#invite-team-member-modal">Invite a Team Member</a>
|
|
||||||
{% endif %}
|
|
||||||
</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 class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body settings-block">
|
<div class="panel-body settings-block">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -210,136 +78,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="revoke-api-key-modal" class="modal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form id="revoke-api-key-form" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
<h4 class="remove-check-title">Revoke API Keys?</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p>You are about to revoke your current API keys.</p>
|
|
||||||
<p>Afterwards, you can create new API keys, but there will
|
|
||||||
be <strong>no way of getting the current API
|
|
||||||
keys back</strong>.
|
|
||||||
</p>
|
|
||||||
<p>Are you sure?</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="revoke_api_keys"
|
|
||||||
class="btn btn-danger">Revoke API Keys</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="remove-team-member-modal" class="modal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form id="remove-team-member-form" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
<h4 class="remove-check-title">Remove Team Member</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p>You are about to remove <span id="rtm-email"></span> from the team.</p>
|
|
||||||
<p>Are you sure?</p>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="email"
|
|
||||||
id="remove-team-member-email" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="remove_team_member"
|
|
||||||
class="btn btn-danger">Remove Member from Team</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="invite-team-member-modal" class="modal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form method="post" class="form-horizontal">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
<h4 class="remove-check-title">Invite a Team Member</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<ul>
|
|
||||||
<li>Team Members can create and manage Checks and Integrations</li>
|
|
||||||
<li>Only the team owner (you) can view and edit billing settings</li>
|
|
||||||
</ul>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="itm-email" class="col-sm-2 control-label">Email</label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
class="form-control"
|
|
||||||
id="itm-email"
|
|
||||||
name="email"
|
|
||||||
placeholder="friend@example.org">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="invite_team_member"
|
|
||||||
class="btn btn-primary">Send Invite</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="set-team-name-modal" class="modal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form method="post" class="form-horizontal">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
||||||
<h4 class="remove-check-title">Set Team Name</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="team-name" class="col-sm-4 control-label">Team Name</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="team-name"
|
|
||||||
name="team_name"
|
|
||||||
value="{{ project }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="set_team_name"
|
|
||||||
class="btn btn-primary">Set Team Name</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="close-account-modal" class="modal">
|
<div id="close-account-modal" class="modal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<form id="close-account-form" method="post" action="{% url 'hc-close' %}">
|
<form id="close-account-form" method="post" action="{% url 'hc-close' %}">
|
||||||
@ -367,11 +105,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
{% compress js %}
|
|
||||||
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/profile.js' %}"></script>
|
|
||||||
{% endcompress %}
|
|
||||||
{% endblock %}
|
|
||||||
|
292
templates/accounts/project.html
Normal file
292
templates/accounts/project.html
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load compress static hc_extras %}
|
||||||
|
|
||||||
|
{% block title %}Project Settings - {{ project }}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-9 col-md-6">
|
||||||
|
<h1 class="settings-title">Project Settings</h1>
|
||||||
|
{% for message in messages %}
|
||||||
|
<p class="alert alert-{{ message.tags }}">{{ message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="panel panel-{{ project_name_status }}">
|
||||||
|
<div class="panel-body settings-block">
|
||||||
|
<h2>Project Name</h2>
|
||||||
|
{{ project }}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="btn btn-default pull-right"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#set-project-name-modal">Change Project Name</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if project_name_updated %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
Project name updated
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-{{ api_status }}">
|
||||||
|
<div class="panel-body settings-block">
|
||||||
|
<h2>API Access</h2>
|
||||||
|
{% if project.api_key %}
|
||||||
|
{% if show_api_keys %}
|
||||||
|
<p>
|
||||||
|
API key: <br />
|
||||||
|
<code>{{ project.api_key }}</code>
|
||||||
|
</p>
|
||||||
|
{% if project.api_key_readonly %}
|
||||||
|
<p>
|
||||||
|
API key (read-only): <br />
|
||||||
|
<code>{{ project.api_key_readonly }}</code>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<button
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#revoke-api-key-modal"
|
||||||
|
class="btn btn-danger pull-right">Revoke</button>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<form method="post">
|
||||||
|
<span class="icon-ok"></span>
|
||||||
|
API access is enabled.
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="show_api_keys"
|
||||||
|
class="btn btn-default pull-right">Show API keys</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<span class="icon-cancel"></span>
|
||||||
|
API access is disabled.
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="create_api_keys"
|
||||||
|
class="btn btn-default pull-right">Create API keys</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if api_keys_created %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
API keys created
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if api_keys_revoked %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
API keys revoked
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-{{ team_status }}">
|
||||||
|
<div class="panel-body settings-block">
|
||||||
|
<h2>Team Access</h2>
|
||||||
|
{% if num_members %}
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td>{{ project.owner.email }}</td>
|
||||||
|
<td>Owner</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% for member in project.member_set.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ member.user.email }} </td>
|
||||||
|
<td>Member</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
data-email="{{ member.user.email }}"
|
||||||
|
class="pull-right member-remove">Remove</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
<strong>Invite team members to your project.</strong>
|
||||||
|
Share access to your checks and configured integrations
|
||||||
|
without having to share login details.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{% if project.can_invite %}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="btn btn-primary pull-right"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#invite-team-member-modal">Invite a Team Member</a>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Team size limit reached.</strong>
|
||||||
|
To invite more members, please
|
||||||
|
<a href="{% url 'hc-pricing' %}">upgrade your account!</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</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 %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="revoke-api-key-modal" class="modal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form id="revoke-api-key-form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="remove-check-title">Revoke API Keys?</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>You are about to revoke your current API keys.</p>
|
||||||
|
<p>Afterwards, you can create new API keys, but there will
|
||||||
|
be <strong>no way of getting the current API
|
||||||
|
keys back</strong>.
|
||||||
|
</p>
|
||||||
|
<p>Are you sure?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="revoke_api_keys"
|
||||||
|
class="btn btn-danger">Revoke API Keys</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="remove-team-member-modal" class="modal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form id="remove-team-member-form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="remove-check-title">Remove Team Member</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>You are about to remove <strong id="rtm-email"></strong> from the project.</p>
|
||||||
|
<p>Are you sure?</p>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="email"
|
||||||
|
id="remove-team-member-email" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="remove_team_member"
|
||||||
|
class="btn btn-danger">Remove Member from Project</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="invite-team-member-modal" class="modal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="remove-check-title">Invite a Team Member</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<ul>
|
||||||
|
<li>Team Members can create and manage Checks and Integrations</li>
|
||||||
|
<li>Only the project owner (you) can view and edit billing settings</li>
|
||||||
|
</ul>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="itm-email" class="col-sm-2 control-label">Email</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
class="form-control"
|
||||||
|
id="itm-email"
|
||||||
|
name="email"
|
||||||
|
placeholder="friend@example.org">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="invite_team_member"
|
||||||
|
class="btn btn-primary">Send Invite</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="set-project-name-modal" class="modal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4>Change Project Name</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="project-name" class="col-sm-4 control-label">Project Name</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="project-name"
|
||||||
|
name="name"
|
||||||
|
value="{{ project }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="set_project_name"
|
||||||
|
class="btn btn-primary">Set Project Name</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% compress js %}
|
||||||
|
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/project.js' %}"></script>
|
||||||
|
{% endcompress %}
|
||||||
|
{% endblock %}
|
@ -125,28 +125,20 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% with projects=request.get_projects %}
|
{% with projects=request.get_projects %}
|
||||||
{% for project in projects %}
|
{% for project in projects %}
|
||||||
{% if project.owner == request.user %}
|
|
||||||
<li class="dropdown-header">{{ project }}</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'hc-switch-team' project.owner.username %}">Checks</a>
|
|
||||||
</li>
|
|
||||||
<li><a href="{% url 'hc-profile' %}">Account Settings</a></li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% for project in projects %}
|
|
||||||
{% if project.owner == request.user %}
|
|
||||||
{% else %}
|
|
||||||
<li class="dropdown-header">{{ project }}</li>
|
<li class="dropdown-header">{{ project }}</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'hc-switch-team' project.owner.username %}">Checks</a>
|
<a href="{% url 'hc-switch-team' project.owner.username %}">Checks</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="separator" class="divider"></li>
|
{% if project.owner == request.user %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'hc-project-settings' project.code %}">Project Settings</a>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
<li><a href="{% url 'hc-profile' %}">Account Settings</a></li>
|
||||||
<li><a href="{% url 'hc-logout' %}">Log Out</a></li>
|
<li><a href="{% url 'hc-logout' %}">Log Out</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
@ -5,9 +5,15 @@
|
|||||||
Hello,
|
Hello,
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{% if inviting_profile %}
|
{% if inviting_project %}
|
||||||
<strong>{{ inviting_profile.user.email }}</strong> invites you to their
|
{% if inviting_project.name %}
|
||||||
<a href="{% site_root %}">{% site_name %}</a> account.
|
<strong>{{ inviting_project.owner.email }}</strong> invites you to their
|
||||||
|
<a href="{% site_root %}">{% site_name %}</a>
|
||||||
|
project <strong>{{ inviting_project }}</strong>.
|
||||||
|
{% else %}
|
||||||
|
<strong>{{ inviting_project.owner.email }}</strong> invites you to their
|
||||||
|
<a href="{% site_root %}">{% site_name %}</a> account.
|
||||||
|
{% endif %}
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
You will be able to manage their
|
You will be able to manage their
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% load hc_extras %}
|
{% load hc_extras %}
|
||||||
{% block content %}Hello,
|
{% block content %}Hello,
|
||||||
{% if inviting_profile %}
|
{% if inviting_project %}
|
||||||
{{ inviting_profile.user.email }} invites you to their {% site_name %} account.
|
{{ inviting_project.owner.email }} invites you to their {% site_name %} account.
|
||||||
|
|
||||||
You will be able to manage their existing monitoring checks and set up new
|
You will be able to manage their existing monitoring checks and set up new
|
||||||
ones. If you already have your own account on {% site_name %}, you will
|
ones. If you already have your own account on {% site_name %}, you will
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% load hc_extras %}
|
{% load hc_extras %}
|
||||||
{% if inviting_profile %}
|
{% if inviting_project %}
|
||||||
You have been invited to join {{ inviting_profile.user.email }} on {% site_name %}
|
You have been invited to join {{ inviting_project }} on {% site_name %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Log in to {% site_name %}
|
Log in to {% site_name %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -14,16 +14,12 @@
|
|||||||
You are currently viewing project <strong>{{ request.project }}</strong>.
|
You are currently viewing project <strong>{{ request.project }}</strong>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To manage this team, please log in as <strong>{{ request.project.owner.email }}</strong>.
|
To manage billing for this project, please log in as <strong>{{ request.project.owner.email }}</strong>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-default"
|
|
||||||
href="{% url 'hc-switch-team' request.user.username %}">
|
|
||||||
Switch to {{ request.profile }}
|
|
||||||
</a>
|
|
||||||
<a class="btn btn-default" href="{% url 'hc-logout' %}">Log Out</a>
|
<a class="btn btn-default" href="{% url 'hc-logout' %}">Log Out</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user