diff --git a/musician/api.py b/musician/api.py index 8b4139c..bbf77a5 100644 --- a/musician/api.py +++ b/musician/api.py @@ -6,7 +6,7 @@ from django.http import Http404 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, WebSite DOMAINS_PATH = 'domains/' @@ -25,6 +25,7 @@ API_PATHS = { 'mailbox-list': 'mailboxes/', 'mailinglist-list': 'lists/', 'saas-list': 'saas/', + 'website-list': 'websites/', # other 'bill-list': 'bills/', @@ -118,6 +119,8 @@ class Orchestra(object): def retrieve_domain_list(self): output = self.retrieve_service_list(Domain.api_name) + websites = self.retrieve_website_list() + domains = [] for domain_json in output: # filter querystring @@ -126,6 +129,10 @@ class Orchestra(object): # retrieve services associated to a domain domain_json['mails'] = self.retrieve_service_list( MailService.api_name, querystring) + + # retrieve websites (as they cannot be filtered by domain on the API we should do it here) + domain_json['websites'] = self.filter_websites_by_domain(websites, domain_json['id']) + # TODO(@slamora): databases and sass are not related to a domain, so cannot be filtered # domain_json['databases'] = self.retrieve_service_list(DatabaseService.api_name, querystring) # domain_json['saas'] = self.retrieve_service_list(SaasService.api_name, querystring) @@ -143,6 +150,19 @@ class Orchestra(object): return domains + def retrieve_website_list(self): + output = self.retrieve_service_list(WebSite.api_name) + return [WebSite.new_from_json(website_data) for website_data in output] + + def filter_websites_by_domain(self, websites, domain_id): + matching = [] + for website in websites: + web_domains = [web_domain.id for web_domain in website.domains] + if domain_id in web_domains: + matching.append(website) + + return matching + def verify_credentials(self): """ Returns: diff --git a/musician/models.py b/musician/models.py index 2f43e28..aa3ebd4 100644 --- a/musician/models.py +++ b/musician/models.py @@ -107,6 +107,7 @@ class UserAccount(OrchestraModel): @classmethod def new_from_json(cls, data, **kwargs): billing = None + last_login = None if 'billcontact' in data: billing = BillingContact.new_from_json(data['billcontact']) @@ -161,6 +162,7 @@ class Domain(OrchestraModel): "records": [], "mails": [], "usage": {}, + "websites": [], } @classmethod @@ -262,3 +264,23 @@ class SaasService(OrchestraModel): 'is_active': True, 'data': {}, } + + +class WebSite(OrchestraModel): + api_name = 'website' + param_defaults = { + "id": None, + "name": None, + "protocol": None, + "is_active": True, + "domains": [], + "contents": [], + } + + @classmethod + def new_from_json(cls, data, **kwargs): + domains = cls.param_defaults.get("domains") + if 'domains' in data: + domains = [Domain.new_from_json(domain_data) for domain_data in data['domains']] + + return super().new_from_json(data=data, domains=domains) diff --git a/musician/static/musician/css/default.css b/musician/static/musician/css/default.css index ecb1ff2..a95a573 100644 --- a/musician/static/musician/css/default.css +++ b/musician/static/musician/css/default.css @@ -40,15 +40,34 @@ a:hover { } #sidebar { - min-width: 250px; - max-width: 250px; + min-width: 280px; + max-width: 280px; min-height: 100vh; + + display: flex; + flex-direction: column; +} +#sidebar #sidebar-services { + flex-grow: 1; } #sidebar.active { margin-left: -250px; } +#sidebar .sidebar-branding { + padding-left: 2rem; + padding-right: 2rem; +} +#sidebar #sidebar-services { + padding-left: 1rem; + padding-right: 1rem; +} + +#sidebar #user-profile-menu { + background:rgba(254, 251, 242, 0.25); +} + #sidebar ul.components { padding: 20px 0; } @@ -107,6 +126,7 @@ a:hover { background: #ECECEB no-repeat url("../images/logo-pangea-light-gray-bg.svg"); background-position: right 5% top 10%; color: #343434; + padding-left: 2rem; } /** services **/ diff --git a/musician/templates/musician/base.html b/musician/templates/musician/base.html index 42158a5..dbd0ee0 100644 --- a/musician/templates/musician/base.html +++ b/musician/templates/musician/base.html @@ -34,27 +34,27 @@
- - - + +
+ Panel Version {{ version }} +
{% endblock sidebar %}
diff --git a/musician/templates/musician/dashboard.html b/musician/templates/musician/dashboard.html index 375fbe3..7988f31 100644 --- a/musician/templates/musician/dashboard.html +++ b/musician/templates/musician/dashboard.html @@ -4,7 +4,11 @@ {% block content %}

{% trans "Welcome back" %} {{ profile.username }}

+{% if profile.last_login %}

{% blocktrans with last_login=profile.last_login|date:"SHORT_DATE_FORMAT" %}Last time you logged in was: {{ last_login }}{% endblocktrans %}

+{% else %} +

{% trans "It's the first time you log into the system, welcome on board!" %}

+{% endif %}
{% for resource, usage in resource_usage.items %} @@ -39,11 +43,15 @@ {{ domain.name }}
+ {% with domain.websites.0 as website %} + {% with website.contents.0 as content %} + {% endwith %} + {% endwith %}
{% comment "@slamora: orchestra doesn't have this information [won't fix] See issue #2" %} @@ -58,9 +66,9 @@

{{ domain.mails|length }} {% trans "mail addresses created" %} - {% if domain.address_left.alert %} + {% if domain.address_left.alert_level %}
- {{ domain.address_left.count }} mail address left + {{ domain.address_left.count }} mail address left {% endif %}

@@ -70,26 +78,13 @@

-
-

{% trans "Databases" %}

-

-

- 0 {% trans "databases created" %} - {% comment %} - - {% endcomment %} -

- -

{% trans "Software as a Service" %}

Nothing installed

+

{% trans "Disk usage" %}

@@ -97,6 +92,7 @@ {% include "musician/components/usage_progress_bar.html" with detail=domain.usage %}
+
@@ -113,13 +109,22 @@ @@ -46,7 +48,21 @@ - {% endfor %} +{% empty %} +
+
+
+
+

+ {# Translators: database page when there isn't any database. #} +
{% trans "Ooops! Looks like there is nothing here!" %}
+
+
+
+
+{% endfor %} + {% if object_list|length > 0 %} {% include "musician/components/paginator.html" %} + {% endif %} {% endblock %} diff --git a/musician/templates/musician/profile.html b/musician/templates/musician/profile.html index c820007..6a49dd6 100644 --- a/musician/templates/musician/profile.html +++ b/musician/templates/musician/profile.html @@ -55,7 +55,9 @@ Details: {{ payment.data }} {% endif %} - +
+ {% trans "Check your last bills" %} +
diff --git a/musician/templates/musician/saas.html b/musician/templates/musician/saas.html index eaad20a..cff1e83 100644 --- a/musician/templates/musician/saas.html +++ b/musician/templates/musician/saas.html @@ -13,9 +13,11 @@
{{ saas.name }}
+ {% comment "Hidden until API provides this information" %}
{% trans "Installed on" %}: {{ saas.domain|default:"-" }}
+ {% endcomment %}
@@ -24,12 +26,11 @@

-

{% trans "Service info" %}

-

{% trans "Active" %}: {{ saas.is_active|yesno }}

- {# TODO (@slamora): implement saas details #} -
-                {{ saas.data }}
-            
+

{% trans "Service info" %}

+ {{ saas.is_active|yesno }}
+ {% for key, value in saas.data.items %} + {{ value }}
+ {% endfor %}
+ {% empty %} +
+
+
+
+

+ {# Translators: saas page when there isn't any saas. #} +
{% trans "Ooops! Looks like there is nothing here!" %}
+
+
+
+
{% endfor %} {% endblock %} diff --git a/musician/tests.py b/musician/tests.py index b25e23a..805ff05 100644 --- a/musician/tests.py +++ b/musician/tests.py @@ -1,5 +1,7 @@ from django.test import TestCase +from .models import UserAccount + class DomainsTestCase(TestCase): def test_domain_not_found(self): @@ -12,3 +14,26 @@ class DomainsTestCase(TestCase): response = self.client.get('/domains/3/') self.assertEqual(404, response.status_code) + + +class UserAccountTest(TestCase): + def test_user_never_logged(self): + data = { + 'billcontact': {'address': 'foo', + 'city': 'Barcelona', + 'country': 'ES', + 'name': '', + 'vat': '12345678Z', + 'zipcode': '08080'}, + 'date_joined': '2020-01-14T12:38:31.684495Z', + 'full_name': 'Pep', + 'id': 2, + 'is_active': True, + 'language': 'EN', + 'short_name': '', + 'type': 'INDIVIDUAL', + 'url': 'http://example.org/api/accounts/2/', + 'username': 'pepe' + } + account = UserAccount.new_from_json(data) + self.assertIsNone(account.last_login) diff --git a/musician/views.py b/musician/views.py index bbff6a0..e96fa24 100644 --- a/musician/views.py +++ b/musician/views.py @@ -66,16 +66,16 @@ class DashboardView(CustomContextMixin, UserTokenRequiredMixin, TemplateView): # TODO(@slamora): validate concept of limits with Pangea profile_type = context['profile'].type for domain in domains: - address_left = ALLOWED_RESOURCES[profile_type]['mailbox'] - len(domain.mails) - alert = None - if address_left == 1: - alert = 'warning' - elif address_left < 1: - alert = 'danger' + addresses_left = ALLOWED_RESOURCES[profile_type]['mailbox'] - len(domain.mails) + alert_level = None + if addresses_left == 1: + alert_level = 'warning' + elif addresses_left < 1: + alert_level = 'danger' - domain.address_left = { - 'count': address_left, - 'alert': alert, + domain.addresses_left = { + 'count': addresses_left, + 'alert_level': alert_level, } context.update({