forked from GithubBackups/healthchecks
Users can create and remove Projects -- WIP
This commit is contained in:
parent
6d7942d7f9
commit
4e6fa38ec6
@ -107,6 +107,7 @@ class ProfileAdmin(admin.ModelAdmin):
|
||||
|
||||
@admin.register(Project)
|
||||
class ProjectAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ("code", "owner")
|
||||
list_select_related = ("owner", )
|
||||
list_display = ("id", "name_", "users", "engagement", "switch")
|
||||
|
||||
|
@ -202,7 +202,7 @@ class Profile(models.Model):
|
||||
|
||||
|
||||
class Project(models.Model):
|
||||
code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
code = models.UUIDField(default=uuid.uuid4, unique=True)
|
||||
name = models.CharField(max_length=200, blank=True)
|
||||
owner = models.ForeignKey(User, models.CASCADE)
|
||||
api_key = models.CharField(max_length=128, blank=True)
|
||||
|
@ -183,6 +183,7 @@ def profile(request):
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"profile": profile,
|
||||
"my_projects_status": "default"
|
||||
}
|
||||
|
||||
if request.method == "POST":
|
||||
@ -192,16 +193,50 @@ def profile(request):
|
||||
elif "set_password" in request.POST:
|
||||
profile.send_set_password_link()
|
||||
return redirect("hc-link-sent")
|
||||
elif "leave_project" in request.POST:
|
||||
code = request.POST["code"]
|
||||
try:
|
||||
project = Project.objects.get(code=code,
|
||||
member__user=request.user)
|
||||
except Project.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if profile.current_project == project:
|
||||
profile.current_project = None
|
||||
profile.save()
|
||||
|
||||
Member.objects.filter(project=project, user=request.user).delete()
|
||||
|
||||
ctx["left_project"] = project
|
||||
ctx["my_projects_status"] = "info"
|
||||
|
||||
# Retrieve projects right before rendering the template--
|
||||
# The list of the projects might have *just* changed
|
||||
ctx["projects"] = list(profile.projects())
|
||||
return render(request, "accounts/profile.html", ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def add_project(request):
|
||||
form = ProjectNameForm(request.POST)
|
||||
if not form.is_valid():
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
project = Project(owner=request.user)
|
||||
project.code = project.badge_key = str(uuid.uuid4())
|
||||
project.name = form.cleaned_data["name"]
|
||||
project.save()
|
||||
|
||||
return redirect("hc-switch-project", project.code)
|
||||
|
||||
|
||||
@login_required
|
||||
def project(request, code):
|
||||
project = Project.objects.get(code=code, owner_id=request.user.id)
|
||||
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"page": "project",
|
||||
"project": project,
|
||||
"show_api_keys": False,
|
||||
"project_name_status": "default",
|
||||
@ -460,3 +495,11 @@ def close(request):
|
||||
|
||||
request.session.flush()
|
||||
return redirect("hc-index")
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def remove_project(request, code):
|
||||
project = Project.objects.get(code=code, owner=request.user)
|
||||
project.delete()
|
||||
return redirect("hc-profile")
|
||||
|
@ -7,7 +7,7 @@ from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core import signing
|
||||
from django.db.models import Count, Q
|
||||
from django.db.models import Count
|
||||
from django.http import (Http404, HttpResponse, HttpResponseBadRequest,
|
||||
HttpResponseForbidden, JsonResponse)
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
@ -17,7 +17,6 @@ from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_POST
|
||||
from hc.accounts.models import Project
|
||||
from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
|
||||
Ping, Notification)
|
||||
from hc.api.transports import Telegram
|
||||
|
@ -7,7 +7,9 @@ urlpatterns = [
|
||||
path('admin/login/', accounts_views.login),
|
||||
path('admin/', admin.site.urls),
|
||||
path('accounts/', include('hc.accounts.urls')),
|
||||
path('projects/add/', accounts_views.add_project, name="hc-add-project"),
|
||||
path('projects/<uuid:code>/settings/', accounts_views.project, name="hc-project-settings"),
|
||||
path('projects/<uuid:code>/remove/', accounts_views.remove_project, name="hc-remove-project"),
|
||||
path('', include('hc.api.urls')),
|
||||
path('', include('hc.front.urls')),
|
||||
path('', include('hc.payments.urls'))
|
||||
|
@ -45,4 +45,24 @@ span.loading {
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
#my-projects th {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
#my-projects td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#my-projects .num-checks {
|
||||
|
||||
}
|
||||
|
||||
#my-projects .name {
|
||||
width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
#close-account {
|
||||
#close-account, #remove-project {
|
||||
margin-left: 24px;
|
||||
border-color: #d43f3a;
|
||||
color: #d43f3a;
|
||||
}
|
||||
|
||||
#close-account:hover {
|
||||
#close-account:hover, #remove-project:hover {
|
||||
background-color: #ffebea;
|
||||
}
|
||||
|
||||
|
13
static/js/profile.js
Normal file
13
static/js/profile.js
Normal file
@ -0,0 +1,13 @@
|
||||
$(function() {
|
||||
|
||||
$(".leave-project").click(function() {
|
||||
var $this = $(this);
|
||||
|
||||
$("#leave-project-name").text($this.data("name"));
|
||||
$("#leave-project-code").val($this.data("code"));
|
||||
$('#leave-project-modal').modal("show");
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
@ -180,7 +180,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="remove-check-title">Change Billing Plan</h4>
|
||||
<h4>Change Billing Plan</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
|
@ -60,6 +60,73 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-{{ my_projects_status }}">
|
||||
<div class="panel-body settings-block">
|
||||
{% csrf_token %}
|
||||
<h2>My Projects</h2>
|
||||
|
||||
{% if projects %}
|
||||
<table id="my-projects" class="table">
|
||||
<tr>
|
||||
<th>Project</th>
|
||||
<th>Checks</th>
|
||||
<th>My Role</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% for project in profile.projects %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="name">{{ project }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<a class="num-checks" href="{% url 'hc-switch-project' project.code %}">
|
||||
{% with project.check_set.count as n %}
|
||||
{{ n }} check{{ n|pluralize }}
|
||||
{% endwith %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if project.owner == request.user %}
|
||||
Owner
|
||||
{% else %}
|
||||
Member
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if project.owner == request.user %}
|
||||
<a
|
||||
href="{% url 'hc-project-settings' project.code %}"
|
||||
class="pull-right">Settings</a>
|
||||
{% else %}
|
||||
<a
|
||||
href="#"
|
||||
data-name="{{ project }}"
|
||||
data-code="{{ project.code }}"
|
||||
class="pull-right leave-project">Leave</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>You don't have any projects. Create one!</p>
|
||||
{% endif %}
|
||||
|
||||
<a
|
||||
id="create-project"
|
||||
href="#"
|
||||
class="btn btn-default pull-right"
|
||||
data-toggle="modal"
|
||||
data-target="#create-project-modal">Create Project</a>
|
||||
</div>
|
||||
|
||||
{% if left_project %}
|
||||
<div class="panel-footer">
|
||||
Left project <strong>{{ left_project }}</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
{% csrf_token %}
|
||||
@ -71,8 +138,6 @@
|
||||
data-toggle="modal"
|
||||
data-target="#close-account-modal">Close Account</a>
|
||||
This will permanently remove your {% site_name %} account
|
||||
<form action="{% url 'hc-close' %}" method="post">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -80,12 +145,12 @@
|
||||
|
||||
<div id="close-account-modal" class="modal">
|
||||
<div class="modal-dialog">
|
||||
<form id="close-account-form" method="post" action="{% url 'hc-close' %}">
|
||||
<form method="post" action="{% url 'hc-close' %}">
|
||||
{% csrf_token %}
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="remove-check-title">Close Account?</h4>
|
||||
<h4>Close Account?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
@ -104,4 +169,78 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="create-project-modal" class="modal">
|
||||
<div class="modal-dialog">
|
||||
<form method="post" action="{% url 'hc-add-project' %}" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4>Create Project</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">
|
||||
</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">Create Project</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="leave-project-modal" class="modal">
|
||||
<div class="modal-dialog">
|
||||
<form id="leave-project-form" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4>Leave This Project?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
You are about to leave project <strong id="leave-project-name"></strong>.
|
||||
You will lose access to its checks and integrations.
|
||||
</p>
|
||||
<p>Are you sure?</p>
|
||||
<input
|
||||
type="hidden"
|
||||
name="code"
|
||||
id="leave-project-code" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button
|
||||
type="submit"
|
||||
name="leave_project"
|
||||
class="btn btn-danger">Leave Project</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/profile.js' %}"></script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
||||
|
@ -149,6 +149,21 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
{% csrf_token %}
|
||||
<h2>Remove Project</h2>
|
||||
<a href="#"
|
||||
id="remove-project"
|
||||
class="btn btn-default pull-right"
|
||||
data-toggle="modal"
|
||||
data-target="#remove-project-modal">Remove Project</a>
|
||||
This will permanently remove project {{ project }}.
|
||||
<form action="{% url 'hc-remove-project' project.code %}" method="post">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -159,7 +174,7 @@
|
||||
<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>
|
||||
<h4>Revoke API Keys?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>You are about to revoke your current API keys.</p>
|
||||
@ -188,7 +203,7 @@
|
||||
<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>
|
||||
<h4>Remove Team Member</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>You are about to remove <strong id="rtm-email"></strong> from the project.</p>
|
||||
@ -217,7 +232,7 @@
|
||||
<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>
|
||||
<h4>Invite a Team Member</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul>
|
||||
@ -281,6 +296,33 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="remove-project-modal" class="modal">
|
||||
<div class="modal-dialog">
|
||||
<form method="post" action="{% url 'hc-remove-project' project.code %}">
|
||||
{% csrf_token %}
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4>Remove "{{ project }}"?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Danger zone! You are about to permanently remove
|
||||
project <strong>{{ project }}</strong> and all
|
||||
of its associated checks and integrations. Are you sure?
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-danger">Remove Project</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
@ -95,7 +95,6 @@
|
||||
<li {% if page == 'channels' %} class="active" {% endif %}>
|
||||
<a href="{% url 'hc-channels' %}">Integrations</a>
|
||||
</li>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
@ -306,7 +306,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="remove-check-title">
|
||||
<h4>
|
||||
Remove this
|
||||
<span class="remove-channel-kind">---</span>
|
||||
Integration?
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="remove-check-title">Remove Check <span class="remove-check-name">{{ check.name_then_code }}</span></h4>
|
||||
<h4>Remove Check <span class="remove-check-name">{{ check.name_then_code }}</span></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>You are about to remove check
|
||||
|
Loading…
x
Reference in New Issue
Block a user