Merge branch 'domain-detail'
This commit is contained in:
commit
16a59792a0
|
@ -2,7 +2,9 @@ import requests
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http import Http404
|
||||||
from django.urls.exceptions import NoReverseMatch
|
from django.urls.exceptions import NoReverseMatch
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import Domain, DatabaseService, MailService, SaasService, UserAccount
|
from .models import Domain, DatabaseService, MailService, SaasService, UserAccount
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@ API_PATHS = {
|
||||||
# services
|
# services
|
||||||
'database-list': 'databases/',
|
'database-list': 'databases/',
|
||||||
'domain-list': 'domains/',
|
'domain-list': 'domains/',
|
||||||
|
'domain-detail': 'domains/{pk}/',
|
||||||
'address-list': 'addresses/',
|
'address-list': 'addresses/',
|
||||||
'mailbox-list': 'mailboxes/',
|
'mailbox-list': 'mailboxes/',
|
||||||
'mailinglist-list': 'lists/',
|
'mailinglist-list': 'lists/',
|
||||||
|
@ -55,9 +58,13 @@ class Orchestra(object):
|
||||||
|
|
||||||
return response.json().get("token", None)
|
return response.json().get("token", None)
|
||||||
|
|
||||||
def request(self, verb, resource, querystring=None, raise_exception=True):
|
def request(self, verb, resource=None, querystring=None, url=None, raise_exception=True):
|
||||||
assert verb in ["HEAD", "GET", "POST", "PATCH", "PUT", "DELETE"]
|
assert verb in ["HEAD", "GET", "POST", "PATCH", "PUT", "DELETE"]
|
||||||
|
if resource is not None:
|
||||||
url = self.build_absolute_uri(resource)
|
url = self.build_absolute_uri(resource)
|
||||||
|
elif url is None:
|
||||||
|
raise AttributeError("Provide `resource` or `url` params")
|
||||||
|
|
||||||
if querystring is not None:
|
if querystring is not None:
|
||||||
url = "{}?{}".format(url, querystring)
|
url = "{}?{}".format(url, querystring)
|
||||||
|
|
||||||
|
@ -86,6 +93,15 @@ class Orchestra(object):
|
||||||
raise PermissionError("Cannot retrieve profile of an anonymous user.")
|
raise PermissionError("Cannot retrieve profile of an anonymous user.")
|
||||||
return UserAccount.new_from_json(output[0])
|
return UserAccount.new_from_json(output[0])
|
||||||
|
|
||||||
|
def retrieve_domain(self, pk):
|
||||||
|
path = API_PATHS.get('domain-detail').format_map({'pk': pk})
|
||||||
|
|
||||||
|
url = urllib.parse.urljoin(self.base_url, path)
|
||||||
|
status, domain_json = self.request("GET", url=url, raise_exception=False)
|
||||||
|
if status == 404:
|
||||||
|
raise Http404(_("No domain found matching the query"))
|
||||||
|
return Domain.new_from_json(domain_json)
|
||||||
|
|
||||||
def retrieve_domain_list(self):
|
def retrieve_domain_list(self):
|
||||||
output = self.retrieve_service_list(Domain.api_name)
|
output = self.retrieve_service_list(Domain.api_name)
|
||||||
domains = []
|
domains = []
|
||||||
|
|
|
@ -7,6 +7,32 @@ a:hover {
|
||||||
color: rgba(0,0,0,.7);
|
color: rgba(0,0,0,.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-arrow-left{
|
||||||
|
color: #eee;
|
||||||
|
background: #D3D0DA;
|
||||||
|
position: relative;
|
||||||
|
padding: 8px 20px 8px 30px;
|
||||||
|
margin-left: 1em; /** equal value than arrow.left **/
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-arrow-left::after,
|
||||||
|
.btn-arrow-left::before{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: -1em;
|
||||||
|
|
||||||
|
margin-top: -19px;
|
||||||
|
border-top: 19px solid transparent;
|
||||||
|
border-bottom: 19px solid transparent;
|
||||||
|
border-right: 1em solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-arrow-left::after{
|
||||||
|
border-right-color: #D3D0DA;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -40,7 +40,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<button type="button" class="btn text-secondary" data-toggle="modal" data-target="#configDetailsModal"
|
<button type="button" class="btn text-secondary" data-toggle="modal" data-target="#configDetailsModal"
|
||||||
data-domain="{{ domain.name }}" data-username="john" data-password="s3cre3t" data-root="/domainname/">
|
data-domain="{{ domain.name }}" data-username="john" data-password="s3cre3t" data-root="/domainname/"
|
||||||
|
data-url="{% url 'musician:domain-detail' domain.id %}">
|
||||||
{% trans "view configuration" %} <strong class="fas fa-tools"></strong>
|
{% trans "view configuration" %} <strong class="fas fa-tools"></strong>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,7 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary">{% trans "View DNS records" %}</button>
|
<a href="#domain-detail" class="btn btn-primary">{% trans "View DNS records" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -137,6 +138,7 @@ $('#configDetailsModal').on('show.bs.modal', function (event) {
|
||||||
modal.find('.modal-body #config-username').text(button.data('username'));
|
modal.find('.modal-body #config-username').text(button.data('username'));
|
||||||
modal.find('.modal-body #config-password').text(button.data('password'));
|
modal.find('.modal-body #config-password').text(button.data('password'));
|
||||||
modal.find('.modal-body #config-root').text(button.data('root'));
|
modal.find('.modal-body #config-root').text(button.data('root'));
|
||||||
|
modal.find('.modal-footer .btn').attr('href', button.data('url'));
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "musician/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<a id="vert_menu" class="btn-arrow-left" href="{% url 'musician:dashboard' %}">{% trans "Go back" %}</a>
|
||||||
|
|
||||||
|
<h1 class="service-name">{% trans "DNS settings for" %} <span class="font-weight-light">{{ object.name }}</span></h1>
|
||||||
|
<p class="service-description">Litle description of what to be expected in this section to aid the user. Even a link to more help if there is one available.</p>
|
||||||
|
|
||||||
|
<table class="table service-list">
|
||||||
|
<colgroup>
|
||||||
|
<col span="1" style="width: 12%;">
|
||||||
|
<col span="1" style="width: 88%;">
|
||||||
|
</colgroup>
|
||||||
|
<thead class="thead-dark">
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{% trans "Type" %}</th>
|
||||||
|
<th scope="col">{% trans "Value" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for record in object.records %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ record.type }}</td>
|
||||||
|
<td>{{ record.value }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
|
@ -1,3 +1,14 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
class DomainsTestCase(TestCase):
|
||||||
|
def test_domain_not_found(self):
|
||||||
|
response = self.client.post(
|
||||||
|
'/auth/login/',
|
||||||
|
{'username': 'admin', 'password': 'admin'},
|
||||||
|
follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
response = self.client.get('/domains/3/')
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
|
@ -15,6 +15,7 @@ urlpatterns = [
|
||||||
path('auth/login/', views.LoginView.as_view(), name='login'),
|
path('auth/login/', views.LoginView.as_view(), name='login'),
|
||||||
path('auth/logout/', views.LogoutView.as_view(), name='logout'),
|
path('auth/logout/', views.LogoutView.as_view(), name='logout'),
|
||||||
path('dashboard/', views.DashboardView.as_view(), name='dashboard'),
|
path('dashboard/', views.DashboardView.as_view(), name='dashboard'),
|
||||||
|
path('domains/<int:pk>/', views.DomainDetailView.as_view(), name='domain-detail'),
|
||||||
path('billing/', views.BillingView.as_view(), name='billing'),
|
path('billing/', views.BillingView.as_view(), name='billing'),
|
||||||
path('profile/', views.ProfileView.as_view(), name='profile'),
|
path('profile/', views.ProfileView.as_view(), name='profile'),
|
||||||
path('mails/', views.MailView.as_view(), name='mails'),
|
path('mails/', views.MailView.as_view(), name='mails'),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -186,6 +185,25 @@ class SaasView(ServiceListView):
|
||||||
template_name = "musician/saas.html"
|
template_name = "musician/saas.html"
|
||||||
|
|
||||||
|
|
||||||
|
class DomainDetailView(CustomContextMixin, UserTokenRequiredMixin, DetailView):
|
||||||
|
template_name = "musician/domain_detail.html"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
# Return an empty list to avoid a request to retrieve all the
|
||||||
|
# user domains. We will get a 404 if the domain doesn't exists
|
||||||
|
# while invoking `get_object`
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
if queryset is None:
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
|
||||||
|
pk = self.kwargs.get(self.pk_url_kwarg)
|
||||||
|
domain = self.orchestra.retrieve_domain(pk)
|
||||||
|
|
||||||
|
return domain
|
||||||
|
|
||||||
|
|
||||||
class LoginView(FormView):
|
class LoginView(FormView):
|
||||||
template_name = 'auth/login.html'
|
template_name = 'auth/login.html'
|
||||||
form_class = LoginForm
|
form_class = LoginForm
|
||||||
|
|
Loading…
Reference in New Issue