Compare commits

..

No commits in common. "d88745f46b2b5e62da39be6072803c6aa4be8f6f" and "2d05580ed3f9d1933599cf1403fcab5ead5fb234" have entirely different histories.

23 changed files with 321 additions and 168 deletions

View file

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% translate "Welcome" %}{% endblock title %}
{% block page_title %}{% translate "Welcome to our customer self service" %}{% endblock page_title %}
{% block content %}
<p>{% url 'customer_dashboard' slug=user.username as dashboard_url %}
{% blocktranslate with full_name=user.get_full_name trimmed %}
Hello {{ full_name }},<br/>
You can visit your <a href="{{ dashboard_url }}">Dashboard</a> to view and modify your hosting options.
{% endblocktranslate %}</p>
{% endblock content %}

View file

@ -1,9 +1,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %} {% load i18n %}
{% block title %}{{ block.super }} - {% blocktranslate with full_name=request.user.get_full_name trimmed %} {% block title %}{{ block.super }} - {% blocktranslate with full_name=dashboard_user.get_full_name trimmed %}
Dashboard for {{ full_name }} Dashboard for {{ full_name }}
{% endblocktranslate %}{% endblock title %} {% endblocktranslate %}{% endblock title %}
{% block page_title %}{% blocktranslate with full_name=request.user.get_full_name trimmed %} {% block page_title %}{% blocktranslate with full_name=dashboard_user.get_full_name trimmed %}
Dashboard for {{ full_name }} Dashboard for {{ full_name }}
{% endblocktranslate %}{% endblock page_title %} {% endblocktranslate %}{% endblock page_title %}
{% block content %} {% block content %}
@ -15,20 +15,37 @@
<thead> <thead>
<tr> <tr>
<th>{% translate "Name" %}</th> <th>{% translate "Name" %}</th>
<th>{% translate "Setup date" %}</th> <th>{% translate "Disk space" %}</th>
<th>{% translate "Mailboxes" %}</th>
<th>{% translate "Databases" %}</th>
<th>{% translate "Actions" %}</th> <th>{% translate "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for package in hosting_packages %} {% for package in hosting_packages %}
<tr> <tr>
<td><a href="{{ package.get_absolute_url }}" <th><a href="{{ package.get_absolute_url }}"
title="{% blocktranslate with packagename=package.name trimmed %} title="{% blocktranslate with packagename=package.name trimmed %}
Show details for {{ packagename }} Show details for {{ packagename }}
{% endblocktranslate %}">{{ package.name }}</a> {% endblocktranslate %}">{{ package.name }}</a>
</td> </th>
<td>{{ package.created }}</td> <th>
<td></td> {% with diskspace=package.get_disk_space %}
<span title="{% blocktranslate trimmed %}
The reserved disk space for your hosting package is {{ diskspace }} bytes.
{% endblocktranslate %}">{{ diskspace|filesizeformat }}</span>
{% endwith %}
</th>
<th>
{% blocktranslate with num=package.used_mailbox_count total=package.mailbox_count trimmed %}
used {{ num }} of {{ total }}
{% endblocktranslate %}</th>
<th>{% for dbtype in package.get_databases %}
{{ dbtype.number }}
{% include "userdbs/snippets/db_type.html" with db_type=dbtype.db_type %}
{% if not forloop.last %} / {% endif %}
{% endfor %}</th>
<th></th>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -39,7 +56,7 @@
{% translate "This user has no hosting packages assigned yet." %}{% endif %}</p> {% translate "This user has no hosting packages assigned yet." %}{% endif %}</p>
{% endif %} {% endif %}
{% if user.is_staff %} {% if user.is_staff %}
<a href="{% url "create_customer_hosting_package" user=request.user.username %}" <a href="{% url "create_customer_hosting_package" user=object.username %}"
class="btn btn-primary">{% translate "Add hosting package" %}</a> class="btn btn-primary">{% translate "Add hosting package" %}</a>
{% endif %} {% endif %}
</div> </div>

View file

@ -2,7 +2,7 @@
Tests for :py:mod:`dashboard.views`. Tests for :py:mod:`dashboard.views`.
""" """
from django import http
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
@ -13,35 +13,65 @@ TEST_USER = "test"
TEST_PASSWORD = "secret" TEST_PASSWORD = "secret"
class IndexViewTest(TestCase):
def test_index_view_anonymous(self):
response = self.client.get(reverse("dashboard"))
self.assertRedirects(response, "/accounts/login/?next=/")
def test_index_view(self):
user = User.objects.create(username=TEST_USER)
user.set_password(TEST_PASSWORD)
user.save()
self.client.login(username=TEST_USER, password=TEST_PASSWORD)
response = self.client.get(reverse("dashboard"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "dashboard/index.html")
class UserDashboardViewTest(TestCase): class UserDashboardViewTest(TestCase):
def _create_test_user(self): def _create_test_user(self):
self.user = User.objects.create(username=TEST_USER) self.user = User.objects.create(username=TEST_USER)
self.user.set_password(TEST_PASSWORD) self.user.set_password(TEST_PASSWORD)
self.user.save() self.user.save()
def test_user_dashboard_view_no_user(self):
response = self.client.get(
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
)
self.assertEqual(response.status_code, 404)
def test_user_dashboard_view_anonymous(self): def test_user_dashboard_view_anonymous(self):
User.objects.create(username=TEST_USER) User.objects.create(username=TEST_USER)
response = self.client.get(reverse("customer_dashboard")) response = self.client.get(
self.assertEqual(response.status_code, 302) reverse("customer_dashboard", kwargs={"slug": TEST_USER})
self.assertRedirects(response, "/accounts/login/?next=/") )
self.assertEqual(response.status_code, 403)
def test_user_dashboard_view_logged_in_ok(self): def test_user_dashboard_view_logged_in_ok(self):
self._create_test_user() self._create_test_user()
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
response = self.client.get(reverse("customer_dashboard")) response = self.client.get(
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_user_dashboard_view_logged_in_template(self): def test_user_dashboard_view_logged_in_template(self):
self._create_test_user() self._create_test_user()
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
response = self.client.get( response = self.client.get(
reverse("customer_dashboard") reverse("customer_dashboard", kwargs={"slug": TEST_USER})
) )
self.assertTemplateUsed(response, "dashboard/user_dashboard.html") self.assertTemplateUsed(response, "dashboard/user_dashboard.html")
def test_user_dashboard_view_logged_in_context_fresh(self): def test_user_dashboard_view_logged_in_context_fresh(self):
self._create_test_user() self._create_test_user()
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
response = self.client.get(reverse("customer_dashboard")) response = self.client.get(
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
)
self.assertIn("dashboard_user", response.context)
self.assertEqual(self.user, response.context["dashboard_user"])
self.assertIn("hosting_packages", response.context) self.assertIn("hosting_packages", response.context)
self.assertEqual(len(response.context["hosting_packages"]), 0) self.assertEqual(len(response.context["hosting_packages"]), 0)

View file

@ -1,9 +1,14 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.urls import path from django.urls import re_path
from .views import UserDashboardView from .views import IndexView, UserDashboardView
urlpatterns = [ urlpatterns = [
path("", UserDashboardView.as_view(), name="customer_dashboard"), re_path(r"^$", IndexView.as_view(), name="dashboard"),
re_path(
r"^user/(?P<slug>[\w0-9@.+-_]+)/$",
UserDashboardView.as_view(),
name="customer_dashboard",
),
] ]

View file

@ -4,24 +4,42 @@ This module defines the views for the gnuviechadmin customer dashboard.
""" """
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.views.generic import DetailView, TemplateView from django.views.generic import DetailView, TemplateView
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
from hostingpackages.models import CustomerHostingPackage from hostingpackages.models import CustomerHostingPackage
class UserDashboardView(LoginRequiredMixin, TemplateView): class IndexView(LoginRequiredMixin, TemplateView):
"""
This is the dashboard view.
"""
template_name = "dashboard/index.html"
class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView):
""" """
This is the user dashboard view. This is the user dashboard view.
""" """
model = get_user_model()
context_object_name = "dashboard_user"
slug_field = "username"
template_name = "dashboard/user_dashboard.html" template_name = "dashboard/user_dashboard.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserDashboardView, self).get_context_data(**kwargs) context = super(UserDashboardView, self).get_context_data(**kwargs)
context["hosting_packages"] = CustomerHostingPackage.objects.filter( context["hosting_packages"] = CustomerHostingPackage.objects.filter(
customer=self.request.user customer=self.object
) )
return context return context
def get_customer_object(self):
"""
Returns the customer object.
"""
return self.get_object()

View file

@ -56,6 +56,15 @@ class NavigationContextProcessorTest(TestCase):
self._check_static_urls(response.context) self._check_static_urls(response.context)
self.assertEqual(response.context["active_item"], "contact") self.assertEqual(response.context["active_item"], "contact")
def test_hostingpackage_page_context(self):
User.objects.create_user("test", password="test")
self.client.login(username="test", password="test")
response = self.client.get(reverse("hosting_packages", kwargs={"user": "test"}))
for item in self.EXPECTED_ITEMS:
self.assertIn(item, response.context)
self._check_static_urls(response.context)
self.assertEqual(response.context["active_item"], "hostingpackage")
def _test_page_context_by_viewmodule(self, viewmodule, expecteditem): def _test_page_context_by_viewmodule(self, viewmodule, expecteditem):
request = HttpRequest() request = HttpRequest()
request.resolver_match = MagicMock() request.resolver_match = MagicMock()

View file

@ -11,9 +11,6 @@
<th>{% translate "Name" %}</th> <th>{% translate "Name" %}</th>
<th>{% translate "Customer" %}</th> <th>{% translate "Customer" %}</th>
<th>{% translate "OS User" %}</th> <th>{% translate "OS User" %}</th>
<th>{% translate "Disk space" %}</th>
<th>{% translate "Mailboxes" %}</th>
<th>{% translate "Databases" %}</th>
<th>{% translate "Setup date" %}</th> <th>{% translate "Setup date" %}</th>
</tr> </tr>
</thead> </thead>
@ -21,24 +18,10 @@
{% for package in customerhostingpackage_list %} {% for package in customerhostingpackage_list %}
<tr> <tr>
<td><a href="{{ package.get_absolute_url }}">{{ package.name }}</a></td> <td><a href="{{ package.get_absolute_url }}">{{ package.name }}</a></td>
<td>{{ package.customer }}</td>
<td>{{ package.osuser.username }}</td>
<td> <td>
{% with diskspace=package.get_disk_space %} <a href="{% url 'customer_dashboard' slug=package.customer.username %}">{{ package.customer }}</a>
<span title="{% blocktranslate trimmed %}
The reserved disk space for your hosting package is {{ diskspace }} bytes.
{% endblocktranslate %}">{{ diskspace|filesizeformat }}</span>
{% endwith %}
</td> </td>
<td> <td>{{ package.osuser.username }}</td>
{% blocktranslate with num=package.used_mailbox_count total=package.mailbox_count trimmed %}
used {{ num }} of {{ total }}
{% endblocktranslate %}</td>
<td>{% for dbtype in package.get_databases %}
{{ dbtype.number }}
{% include "userdbs/snippets/db_type.html" with db_type=dbtype.db_type %}
{% if not forloop.last %} / {% endif %}
{% endfor %}</td>
<td>{{ package.created }}</td> <td>{{ package.created }}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -0,0 +1,48 @@
{% extends "hostingpackages/base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if user == customer %}
{% translate "Your hosting packages" %}
{% else %}
{% blocktranslate with customer=customer.get_full_name trimmed %}Hosting Packages of
{{ customer }}{% endblocktranslate %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}{% spaceless %}
{% if user == customer %}
{% translate "Your hosting packages" %}
{% else %}
{% blocktranslate with customer=customer.get_full_name trimmed %}Hosting Packages
<small>of {{ customer }}</small>{% endblocktranslate %}
{% endif %}
{% endspaceless %}{% endblock page_title %}
{% block content %}
{% if customerhostingpackage_list %}
<table class="table table-condensed">
<thead>
<tr>
<th>{% translate "Name" %}</th>
<th>{% translate "Setup date" %}</th>
</tr>
</thead>
<tbody>
{% for package in customerhostingpackage_list %}
<tr>
<td><a href="{{ package.get_absolute_url }}">{{ package.name }}</a></td>
<td>{{ package.created }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-info">
{% if user == customer %}{% translate "You have no hosting packages setup yet." %}{% else %}
{% translate "There are no hosting packages setup for this customer yet." %}{% endif %}</p>
{% endif %}
{% if user.is_staff %}
<p><a href="{% url 'create_customer_hosting_package' user=customer.username %}"
class="btn btn-primary">{% translate "Add hosting package" %}</a></p>
{% endif %}
{% endblock content %}

View file

@ -12,6 +12,7 @@ from .views import (
CreateCustomerHostingPackage, CreateCustomerHostingPackage,
CreateHostingPackage, CreateHostingPackage,
CustomerHostingPackageDetails, CustomerHostingPackageDetails,
CustomerHostingPackageList,
HostingOptionChoices, HostingOptionChoices,
) )
@ -22,6 +23,11 @@ urlpatterns = [
AllCustomerHostingPackageList.as_view(), AllCustomerHostingPackageList.as_view(),
name="all_hosting_packages", name="all_hosting_packages",
), ),
re_path(
r"^(?P<user>[-\w0-9@.+_]+)/$",
CustomerHostingPackageList.as_view(),
name="hosting_packages",
),
re_path( re_path(
r"^(?P<user>[-\w0-9@.+_]+)/create$", r"^(?P<user>[-\w0-9@.+_]+)/create$",
CreateCustomerHostingPackage.as_view(), CreateCustomerHostingPackage.as_view(),

View file

@ -149,6 +149,35 @@ class AllCustomerHostingPackageList(StaffUserRequiredMixin, ListView):
) )
class CustomerHostingPackageList(StaffOrSelfLoginRequiredMixin, ListView):
"""
This view is used for showing a list of a customer's hosting packages.
"""
model = CustomerHostingPackage
customer = None
def get_customer_object(self):
if self.customer is None:
self.customer = get_object_or_404(
get_user_model(), username=self.kwargs["user"]
)
return self.customer
def get_context_data(self, **kwargs):
context = super(CustomerHostingPackageList, self).get_context_data(**kwargs)
context["customer"] = self.get_customer_object()
return context
def get_queryset(self):
return (
super(CustomerHostingPackageList, self)
.get_queryset()
.filter(customer__username=self.kwargs["user"])
)
class HostingOptionChoices(StaffUserRequiredMixin, DetailView): class HostingOptionChoices(StaffUserRequiredMixin, DetailView):
""" """
This view displays choices of hosting options for a customer hosting This view displays choices of hosting options for a customer hosting

View file

@ -212,7 +212,7 @@ class GroupTest(TestCaseWithCeleryTasks):
group.delete() group.delete()
self.assertEqual(len(Group.objects.all()), 0) self.assertEqual(len(Group.objects.all()), 0)
self.assertEqual(len(TaskResult.objects.all()), 2) self.assertEqual(len(TaskResult.objects.all()), 2)
tr = TaskResult.objects.order_by("created").first() tr = TaskResult.objects.first()
self.assertEqual(tr.creator, "handle_group_created") self.assertEqual(tr.creator, "handle_group_created")

View file

@ -32,7 +32,7 @@ class FetchTaskResultsCommandTest(TestCase):
aresult.state = "PENDING" aresult.state = "PENDING"
aresult.ready.return_value = False aresult.ready.return_value = False
Command().handle(verbosity=0) Command().handle()
tr = TaskResult.objects.get(task_id=TEST_TASK_UUID) tr = TaskResult.objects.get(task_id=TEST_TASK_UUID)
self.assertTrue(asyncresult.called_with(TEST_TASK_UUID)) self.assertTrue(asyncresult.called_with(TEST_TASK_UUID))
@ -55,7 +55,7 @@ class FetchTaskResultsCommandTest(TestCase):
aresult.ready.return_value = True aresult.ready.return_value = True
aresult.get.return_value = TEST_TASK_RESULT aresult.get.return_value = TEST_TASK_RESULT
Command().handle(verbosity=0) Command().handle()
tr = TaskResult.objects.get(task_id=TEST_TASK_UUID) tr = TaskResult.objects.get(task_id=TEST_TASK_UUID)
self.assertTrue(asyncresult.called_with(TEST_TASK_UUID)) self.assertTrue(asyncresult.called_with(TEST_TASK_UUID))

View file

@ -4,9 +4,9 @@
{% block page_title %}{% translate "E-mail Addresses" %}{% endblock page_title %} {% block page_title %}{% translate "E-mail Addresses" %}{% endblock page_title %}
{% block content %} {% block content %}
{% if user.emailaddress_set.all %} {% if user.emailaddress_set.all %}
<p>{% translate 'The following e-mail addresses are associated with your account:' %}</p> <p>{% translate 'The following e-mail addresses are associated with your account:' %}</p>
<form action="{% url 'account_email' %}" class="email_list" method="post"> <form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %} {% csrf_token %}
<table class="table table-condensed"> <table class="table table-condensed">
<thead> <thead>
@ -14,7 +14,6 @@
<th>{% translate "Email address" %}</th> <th>{% translate "Email address" %}</th>
<th>{% translate "Verified" %}</th> <th>{% translate "Verified" %}</th>
<th>{% translate "Primary" %}</th> <th>{% translate "Primary" %}</th>
<th>{% translate "Selection" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -23,68 +22,54 @@
<td>{{ emailaddress.email }}</td> <td>{{ emailaddress.email }}</td>
<td> <td>
{% if emailaddress.verified %} {% if emailaddress.verified %}
<span class="text-success"><i class="bi-check-circle-fill" title="{% translate "Verified" %}"></i></span> <span class="verified">{% translate "Verified" %}</span>
{% else %} {% else %}
<span class="text-warning"><i class="bi-dash-circle-fill" title="{% translate "Unverified" %}"></i></span> <span class="unverified">{% translate "Unverified" %}</span>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if emailaddress.primary %} {% if emailaddress.primary %}
<span class="text-success"><i class="bi-check-circle-fill" title="{% translate "This is the current primary Email address" %}"></i></span> <span class="glyphicon glyphicon-star" title="{% translate "This is the current primary Email address" %}"></span>
{% else %} {% else %}
<span class="text-secondary"><i class="bi-circle"></i></span> <span class="glyphicon glyphicon-star-empty"></span>
{% endif %} {% endif %}
</td> <input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary %}checked="checked"{%endif %} value="{{emailaddress.email}}" />
<td>
<input id="email_radio_{{ forloop.counter }}" type="radio" name="email"
{% if emailaddress.primary %}checked="checked"{% endif %}
value="{{ emailaddress.email }}"/><label
for="email_radio_{{ forloop.counter }}"
aria-label="{% blocktranslate trimmed with address=emailaddress.email %}
Select {{ address }}
{% endblocktranslate %}"></label>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<p> <p>
{% if user.emailaddress_set.count > 1 %} <button class="btn btn-default" type="submit" name="action_primary" >{% translate 'Make Primary' %}</button>
<button class="btn btn-secondary" type="submit" <button class="btn btn-default" type="submit" name="action_send" >{% translate 'Re-send Verification' %}</button>
name="action_primary">{% translate 'Make Primary' %}</button> <button class="btn btn-warning" type="submit" name="action_remove" >{% translate 'Remove' %}</button>
{% endif %}
<button class="btn btn-secondary" type="submit"
name="action_send">{% translate 'Re-send Verification' %}</button>
<button class="btn btn-warning" type="submit" name="action_remove">{% translate 'Remove' %}</button>
</p> </p>
</form> </form>
{% else %} {% else %}
<p class="text-warning"> <p class="text-warning"><strong>{% translate 'Warning:'%}</strong> {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}</p>
<strong>{% translate 'Warning:' %}</strong> {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %} {% endif %}
</p>
{% endif %}
<h2>{% translate "Add E-mail Address" %}</h2> <h2>{% translate "Add E-mail Address" %}</h2>
<form method="post" action="{% url 'account_email' %}" class="add_email"> <form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %} {% csrf_token %}
{{ form | crispy }} {{ form | crispy }}
<button name="action_add" type="submit" class="btn btn-primary">{% translate "Add E-mail" %}</button> <button name="action_add" type="submit" class="btn btn-primary">{% translate "Add E-mail" %}</button>
</form> </form>
{% endblock content %} {% endblock content %}
{% block extra_js %} {% block extra_js %}
<script type="text/javascript"> <script type="text/javascript">
(function () { (function() {
const message = "{% translate 'Do you really want to remove the selected e-mail address?' %}"; var message = "{% translate 'Do you really want to remove the selected e-mail address?' %}";
let actions = document.getElementsByName('action_remove'); var actions = document.getElementsByName('action_remove');
if (actions.length) { if (actions.length) {
actions[0].addEventListener("click", function (e) { actions[0].addEventListener("click", function(e) {
if (!confirm(message)) { if (! confirm(message)) {
e.preventDefault(); e.preventDefault();
} }
}); });
} }
})(); })();
</script> </script>
{% endblock extra_js %} {% endblock extra_js %}

View file

@ -11,7 +11,7 @@
{% endblocktranslate %}</p> {% endblocktranslate %}</p>
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}"> <form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
{% csrf_token %} {% csrf_token %}
<button type="submit" class="btn btn-primary">{% translate 'Confirm' %}</button> <button type="submit" class="btn btn-default">{% translate 'Confirm' %}</button>
</form> </form>
{% else %} {% else %}
{% url 'account_email' as email_url %} {% url 'account_email' as email_url %}

View file

@ -14,7 +14,7 @@
{% if redirect_field_value %} {% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/> <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %} {% endif %}
<a class="btn btn-secondary" href="{% url 'account_reset_password' %}">{% translate "Forgot Password?" %}</a> <a class="btn btn-default" href="{% url 'account_reset_password' %}">{% translate "Forgot Password?" %}</a>
<button class="btn btn-primary" type="submit">{% translate "Sign In" %}</button> <button class="btn btn-primary" type="submit">{% translate "Sign In" %}</button>
</form> </form>
</div> </div>

View file

@ -23,7 +23,13 @@
<body> <body>
<nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark"> <nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
<div class="container"> <div class="container">
<a class="navbar-brand" href="{% url "customer_dashboard" %}" title="{% translate "Dashboard" %}">gnuviechadmin</a> <a class="navbar-brand" href="{% spaceless %}
{% if user.is_authenticated %}
{% url 'customer_dashboard' slug=user.username %}
{% else %}
{% url 'dashboard' %}
{% endif %}
{% endspaceless %}" title="{% translate "Dashboard" %}">gnuviechadmin</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent"
aria-controls="navbarContent" aria-expanded="false" aria-label="{% translate "Toggle navigation" %}"> aria-controls="navbarContent" aria-expanded="false" aria-label="{% translate "Toggle navigation" %}">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
@ -31,12 +37,21 @@
<div class="collapes navbar-collapse" id="navbarContent"> <div class="collapes navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto mb-2 mg-lg-0"> <ul class="navbar-nav me-auto mb-2 mg-lg-0">
{% if user.is_staff %} {% if user.is_staff %}
<li class="nav-item{% if active_item == "hosting" %} active{% endif %}"> <li class="nav-item dropdown{% if active_item == 'hostingpackage' %} active{% endif %}">
<a class="nav-link" href="{% url 'all_hosting_packages' %}"> <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button"
<i class="bi-boxes"></i> {% translate "Hosting" %}</a> aria-expanded="false"><i class="bi-server"></i> {% translate "Hosting" %}</a>
<ul class="dropdown-menu" role="menu">
<li><a class="dropdown-item" href="{% url 'hosting_packages' user=user.username %}"><i
class="bi-box2-fill"></i> {% translate "Your hosting packages" %}</a></li>
<li><a class="dropdown-item" href="{% url 'all_hosting_packages' %}"><i
class="bi-boxes"></i> {% translate "All hosting packages" %}</a></li>
</ul>
</li> </li>
{% elif user.is_authenticated %}
<li class="nav-item{% if active_item == 'hostingpackage' %} active{% endif %}"><a
class="nav-link" href="{% url 'hosting_packages' user=user.username %}"><i
class="bi-server"></i> {% translate "Hosting" %}</a></li>
{% endif %} {% endif %}
{% if user.is_authenticated %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button"
aria-expanded="false"><i class="bi-link"></i> {% translate "Links" %} </a> aria-expanded="false"><i class="bi-link"></i> {% translate "Links" %} </a>
@ -52,13 +67,11 @@
class="icon-postgres"></i> {% translate "phpPgAdmin" %}</a></li> class="icon-postgres"></i> {% translate "phpPgAdmin" %}</a></li>
</ul> </ul>
</li> </li>
{% endif %}
<li class="nav-item{% if active_item == 'imprint' %} active{% endif %}"><a <li class="nav-item{% if active_item == 'imprint' %} active{% endif %}"><a
class="nav-link" href="{% url 'imprint' %}"><i class="bi-info"></i> {% translate "Imprint" %} class="nav-link" href="{% url 'imprint' %}"><i class="bi-info"></i> {% translate "Imprint" %}
</a></li> </a></li>
<li class="nav-item{% if active_item == 'privacy' %} active{% endif %}"><a <li class="nav-item{% if active_item == 'privacy' %} active{% endif %}"><a
class="nav-link" href="{% url 'privacy' %}"><i class="nav-link" href="{% url 'privacy' %}"><i class="bi-file-text"></i> {% translate "Privacy policy" %}
class="bi-file-text"></i> {% translate "Privacy policy" %}
</a></li> </a></li>
<li class="nav-item{% if active_item == 'contact' %} active{% endif %}"><a <li class="nav-item{% if active_item == 'contact' %} active{% endif %}"><a
class="nav-link" href="{% url 'contact_form' %}"><i class="nav-link" href="{% url 'contact_form' %}"><i
@ -66,7 +79,7 @@
</ul> </ul>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% user_display user as user_display %} {% user_display user as user_display %}
{% url "customer_dashboard" as profile_url %} {% url 'user_profile' slug=user.username as profile_url %}
{% if user.is_impersonate %} {% if user.is_impersonate %}
{% user_display user.impersonator as impersonator_display %} {% user_display user.impersonator as impersonator_display %}
{% url 'impersonate-stop' as stop_impersonation_url %} {% url 'impersonate-stop' as stop_impersonation_url %}
@ -74,8 +87,7 @@
Signed in as Signed in as
<a href="{{ profile_url }}" class="navbar-link" title="My Profile">{{ user_display }}</a> <a href="{{ profile_url }}" class="navbar-link" title="My Profile">{{ user_display }}</a>
<i class="bi-incognito" title="impersonated by {{ impersonator_display }}"></i> <i class="bi-incognito" title="impersonated by {{ impersonator_display }}"></i>
<a href="{{ stop_impersonation_url }}" class="navbar-link"><i class="bi-sign-stop" <a href="{{ stop_impersonation_url }}" class="navbar-link"><i class="bi-sign-stop" title="stop impersonation"></i></a>
title="stop impersonation"></i></a>
{% endblocktranslate %}</span> {% endblocktranslate %}</span>
{% else %} {% else %}
<span class="navbar-text justify-content-end">{% blocktranslate trimmed %} <span class="navbar-text justify-content-end">{% blocktranslate trimmed %}

View file

@ -10,8 +10,8 @@
{% if page.object_list %} {% if page.object_list %}
<ul class="list-group"> <ul class="list-group">
{% for user in page.object_list %} {% for user in page.object_list %}
<li class="list-group-item"><a href="{% url 'impersonate-start' user.pk %}{{ redirect }}"> <li class="list-group-item"><a href="{% url 'impersonate-start' user.pk %}{{ redirect }}">{{ user }}</a>
{% blocktranslate %}Impersonate {{ user }}{% endblocktranslate %}</a> - Impersonate
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -13,15 +13,15 @@
placeholder="{% translate "user name part" %}" value="{% if query %}{{ query }}{% endif %}"> placeholder="{% translate "user name part" %}" value="{% if query %}{{ query }}{% endif %}">
</div> </div>
<button type="submit" class="btn btn-primary">{% translate "Search" %}</button> <button type="submit" class="btn btn-primary">{% translate "Search" %}</button>
<a class="btn btn-link" href="{% url 'impersonate-list' %}">{% translate "List all users" %}</a> <a class="btn btn-secondary" href="{% url 'impersonate-list' %}">{% translate "List all users" %}</a>
</form> </form>
<p> <p>
{% if query and page.object_list %} {% if query and page.object_list %}
<ul class="list-group"> <ul class="list-group">
{% for user in page.object_list %} {% for user in page.object_list %}
<li class="list-group-item"><a href="{% url 'impersonate-start' user.pk %}{{ redirect }}"> <li class="list-group-item"><a
{% blocktranslate %}Impersonate {{ user }}{% endblocktranslate %}</a> href="{% url 'impersonate-start' user.pk %}{{ redirect }}">{{ user }}</a> - Impersonate
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -31,7 +31,7 @@
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
<input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/> <input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/>
<a class="btn btn-secondary" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a> <a class="btn btn-default" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a>
</form> </form>
</div> </div>
</div> </div>

View file

@ -39,7 +39,7 @@
<form action="{% url 'delete_ssh_key' package=hostingpackage.id pk=key.id %}" method="post"> <form action="{% url 'delete_ssh_key' package=hostingpackage.id pk=key.id %}" method="post">
{% csrf_token %} {% csrf_token %}
<input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/> <input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/>
<a class="btn btn-secondary" href="{% url 'list_ssh_keys' package=hostingpackage.id %}" <a class="btn btn-default" href="{% url 'list_ssh_keys' package=hostingpackage.id %}"
title="{% translate "Cancel and go back to the SSH key list" %}">{% translate "Cancel" %}</a> title="{% translate "Cancel and go back to the SSH key list" %}">{% translate "Cancel" %}</a>
</form> </form>
</div> </div>

View file

@ -40,7 +40,7 @@
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
<input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/> <input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/>
<a class="btn btn-secondary" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a> <a class="btn btn-default" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a>
</form> </form>
</div> </div>
</div> </div>

View file

@ -36,7 +36,7 @@
method="post" class="form"> method="post" class="form">
{% csrf_token %} {% csrf_token %}
<input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/> <input class="btn btn-warning" type="submit" value="{% translate "Yes, do it!" %}"/>
<a class="btn btn-secondary" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a> <a class="btn btn-default" href="{{ hostingpackage.get_absolute_url }}">{% translate "Cancel" %}</a>
</form> </form>
</div> </div>
</div> </div>

View file

@ -94,7 +94,7 @@ class DatabaseUserManagerTest(TestCaseWithCeleryTasks):
self.assertEqual(dbu.name, "{user}db01".format(user=self.osuser.username)) self.assertEqual(dbu.name, "{user}db01".format(user=self.osuser.username))
self.assertEqual(dbu.osuser, self.osuser) self.assertEqual(dbu.osuser, self.osuser)
self.assertEqual(dbu.db_type, DB_TYPES.mysql) self.assertEqual(dbu.db_type, DB_TYPES.mysql)
taskres = TaskResult.objects.order_by("created").all() taskres = TaskResult.objects.all()
self.assertEqual(len(taskres), 4) self.assertEqual(len(taskres), 4)
self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].creator, "handle_dbuser_created")
self.assertEqual(taskres[0].notes, "mysql user creation") self.assertEqual(taskres[0].notes, "mysql user creation")
@ -154,7 +154,7 @@ class DatabaseUserTest(TestCaseWithCeleryTasks):
dbid = db.id dbid = db.id
self.dbu.delete() self.dbu.delete()
self.assertFalse(UserDatabase.objects.filter(id=dbid).exists()) self.assertFalse(UserDatabase.objects.filter(id=dbid).exists())
taskres = TaskResult.objects.order_by("created").all() taskres = TaskResult.objects.all()
self.assertEqual(len(taskres), 3) self.assertEqual(len(taskres), 3)
self.assertEqual(taskres[0].creator, "handle_userdb_created") self.assertEqual(taskres[0].creator, "handle_userdb_created")
self.assertEqual(taskres[0].notes, "pgsql database creation") self.assertEqual(taskres[0].notes, "pgsql database creation")
@ -187,7 +187,7 @@ class UserDatabaseManagerTest(TestCaseWithCeleryTasks):
self.assertEqual( self.assertEqual(
db.db_user.name, "{user}db01".format(user=self.osuser.username) db.db_user.name, "{user}db01".format(user=self.osuser.username)
) )
taskres = TaskResult.objects.order_by("created").all() taskres = TaskResult.objects.all()
self.assertEqual(len(taskres), 2) self.assertEqual(len(taskres), 2)
self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].creator, "handle_dbuser_created")
self.assertEqual(taskres[0].notes, "mysql user creation") self.assertEqual(taskres[0].notes, "mysql user creation")
@ -202,7 +202,7 @@ class UserDatabaseManagerTest(TestCaseWithCeleryTasks):
self.assertEqual( self.assertEqual(
db.db_user.name, "{user}db01".format(user=self.osuser.username) db.db_user.name, "{user}db01".format(user=self.osuser.username)
) )
taskres = TaskResult.objects.order_by("created").all() taskres = TaskResult.objects.all()
self.assertEqual(len(taskres), 2) self.assertEqual(len(taskres), 2)
self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].creator, "handle_dbuser_created")
self.assertEqual(taskres[0].notes, "pgsql user creation") self.assertEqual(taskres[0].notes, "pgsql user creation")
@ -267,20 +267,20 @@ class UserDatabaseTest(TestCaseWithCeleryTasks):
def test___str__(self): def test___str__(self):
customer = Customer.objects.create_user(username="testcustomer") customer = Customer.objects.create_user(username="testcustomer")
os_user = User.objects.create_user(customer=customer) osuser = User.objects.create_user(customer=customer)
db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.pgsql, os_user) db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.pgsql, osuser)
self.assertEqual( self.assertEqual(
str(db), str(db),
"{user}db01 ({dbuser})".format(user=os_user.username, dbuser=db.db_user), "{user}db01 ({dbuser})".format(user=osuser.username, dbuser=db.db_user),
) )
def test_delete_mysql_db(self): def test_delete_mysql_db(self):
customer = Customer.objects.create_user(username="testcustomer") customer = Customer.objects.create_user(username="testcustomer")
os_user = User.objects.create_user(customer=customer) osuser = User.objects.create_user(customer=customer)
TaskResult.objects.all().delete() TaskResult.objects.all().delete()
db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.mysql, os_user) db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.mysql, osuser)
db.delete() db.delete()
task_result = TaskResult.objects.order_by("created").all() taskres = TaskResult.objects.all()
self.assertEqual(len(task_result), 3) self.assertEqual(len(taskres), 3)
self.assertEqual(task_result[2].creator, "handle_userdb_deleted") self.assertEqual(taskres[2].creator, "handle_userdb_deleted")
self.assertEqual(task_result[2].notes, "mysql database deletion") self.assertEqual(taskres[2].notes, "mysql database deletion")