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 @@
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 @@
-
{% trans "FTP access:" %}
-
-
- username
- password
-
-
root directory
+
+
{% trans "FTP access:" %}
+ {# Translators: domain configuration detail modal #}
+
{% trans "Contact with the support team to get details concerning FTP access." %}
+ {% comment %}
+
+
username
+
password
+ {% endcomment %}
+
+
+
{% trans "No website configured." %}
+
+ root directory
+ type
+
+ {% comment "@slamora: orchestra doesn't provide this information [won't fix] See issue #3" %}
{% trans "associated to" %}: {{ database.domain|default:"-" }}
+ {% endcomment %}
@@ -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 %}
-
+
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 %}
-
{% 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({