From 2d9ae1c89b878fdb221cac21a2743bf4e9daeeea Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 25 Jan 2023 13:57:44 +0100 Subject: [PATCH] Added information page about components in UI v2 --- frontend/src/api/admin.js | 7 ++ .../admin/ComponentsInformationTable.vue | 72 +++++++++++++++++++ .../src/components/domains/DomainList.vue | 2 +- frontend/src/components/layout/Navbar.vue | 6 ++ frontend/src/router/index.js | 9 +++ frontend/src/views/admin/Information.vue | 19 +++++ .../src/views/user/PasswordRecoveryForm.vue | 2 +- modoboa/core/api/v2/serializers.py | 12 ++++ modoboa/core/api/v2/tests.py | 8 +++ modoboa/core/api/v2/urls.py | 2 + modoboa/core/api/v2/views.py | 17 ++++- modoboa/core/app_settings.py | 3 +- 12 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 frontend/src/api/admin.js create mode 100644 frontend/src/components/admin/ComponentsInformationTable.vue create mode 100644 frontend/src/views/admin/Information.vue diff --git a/frontend/src/api/admin.js b/frontend/src/api/admin.js new file mode 100644 index 000000000..45f53e838 --- /dev/null +++ b/frontend/src/api/admin.js @@ -0,0 +1,7 @@ +import repository from './repository' + +export default { + getComponentsInformation () { + return repository.get('/admin/components') + } +} diff --git a/frontend/src/components/admin/ComponentsInformationTable.vue b/frontend/src/components/admin/ComponentsInformationTable.vue new file mode 100644 index 000000000..84b3fda86 --- /dev/null +++ b/frontend/src/components/admin/ComponentsInformationTable.vue @@ -0,0 +1,72 @@ + + + diff --git a/frontend/src/components/domains/DomainList.vue b/frontend/src/components/domains/DomainList.vue index 1a2b7db4d..681346f0d 100644 --- a/frontend/src/components/domains/DomainList.vue +++ b/frontend/src/components/domains/DomainList.vue @@ -165,7 +165,7 @@ export default { headers: [ { text: this.$gettext('Name'), value: 'name' }, { text: this.$gettext('Aliases'), value: 'domainalias_count' }, - { text: this.$gettext('DNS status'), value: 'dns_global_status', sortable: false, align: 'center' }, + { text: this.$gettext('DNS status'), value: 'dns_global_status', sortable: false }, { text: this.$gettext('Sending limit'), value: 'message_limit' }, { text: this.$gettext('Quota'), value: 'allocated_quota_in_percent' }, { text: this.$gettext('Actions'), value: 'actions', sortable: false, align: 'right' } diff --git a/frontend/src/components/layout/Navbar.vue b/frontend/src/components/layout/Navbar.vue index c7c54c5e9..4c4ddc9a7 100644 --- a/frontend/src/components/layout/Navbar.vue +++ b/frontend/src/components/layout/Navbar.vue @@ -201,6 +201,12 @@ export default { text: this.$gettext('Settings'), children: [], roles: ['SuperAdmins'] + }, + { + icon: 'mdi-information', + text: this.$gettext('Information'), + roles: ['SuperAdmins'], + to: { name: 'Information' } } ], userSettingsMenuItems: [ diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 2f0c51e2e..49e554976 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -136,6 +136,15 @@ const routes = [ allowedRoles: ['DomainAdmins', 'SuperAdmins'] } }, + { + path: '/information', + name: 'Information', + component: () => import('../views/admin/Information.vue'), + meta: { + requiresAuth: true, + allowedRoles: ['SuperAdmins'] + } + }, { path: '/user/api', name: 'APISetup', diff --git a/frontend/src/views/admin/Information.vue b/frontend/src/views/admin/Information.vue new file mode 100644 index 000000000..e088b62eb --- /dev/null +++ b/frontend/src/views/admin/Information.vue @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/views/user/PasswordRecoveryForm.vue b/frontend/src/views/user/PasswordRecoveryForm.vue index 2aecb0607..516991229 100644 --- a/frontend/src/views/user/PasswordRecoveryForm.vue +++ b/frontend/src/views/user/PasswordRecoveryForm.vue @@ -35,7 +35,7 @@
- Return to login? + Return to login
diff --git a/modoboa/core/api/v2/serializers.py b/modoboa/core/api/v2/serializers.py index d38123777..7afaff798 100644 --- a/modoboa/core/api/v2/serializers.py +++ b/modoboa/core/api/v2/serializers.py @@ -468,3 +468,15 @@ class PasswordRecoveryConfirmSerializer(serializers.Serializer): user.set_password(self.validated_data["new_password1"]) user.save() return True + + +class ModoboaComponentSerializer(serializers.Serializer): + """Serializer used for information endpoint.""" + + label = serializers.CharField() + name = serializers.CharField() + version = serializers.CharField() + last_version = serializers.CharField(required=False) + description = serializers.CharField() + update = serializers.BooleanField(default=False) + changelog_url = serializers.URLField(required=False) diff --git a/modoboa/core/api/v2/tests.py b/modoboa/core/api/v2/tests.py index 6c72b102f..ed6bdd5e4 100644 --- a/modoboa/core/api/v2/tests.py +++ b/modoboa/core/api/v2/tests.py @@ -441,3 +441,11 @@ class LanguageViewSetTestCase(ModoAPITestCase): url = reverse("v2:language-list") resp = self.client.get(url) self.assertEqual(resp.status_code, 200) + + +class ComponentAPITestCase(ModoAPITestCase): + + def test_information(self): + url = reverse("v2:components_information") + resp = self.client.get(url) + self.assertEqual(resp.status_code, 200) diff --git a/modoboa/core/api/v2/urls.py b/modoboa/core/api/v2/urls.py index 3435412ad..da5784b20 100644 --- a/modoboa/core/api/v2/urls.py +++ b/modoboa/core/api/v2/urls.py @@ -28,4 +28,6 @@ urlpatterns += [ name="password_reset_confirm_v2"), path('sms_totp/', views.PasswordResetSmsTOTP.as_view(), name="sms_totp"), + path('admin/components/', views.ComponentsInformationAPIView.as_view(), + name="components_information"), ] diff --git a/modoboa/core/api/v2/views.py b/modoboa/core/api/v2/views.py index ef07211af..7366e3161 100644 --- a/modoboa/core/api/v2/views.py +++ b/modoboa/core/api/v2/views.py @@ -8,12 +8,14 @@ from django.utils.datastructures import MultiValueDictKeyError from django.contrib.auth import login +from drf_spectacular.utils import extend_schema from rest_framework import response, status from rest_framework_simplejwt import views as jwt_views from rest_framework_simplejwt.exceptions import InvalidToken, TokenError from rest_framework.views import APIView from modoboa.core.password_hashers import get_password_hasher +from modoboa.core.utils import check_for_updates from modoboa.parameters import tools as param_tools from smtplib import SMTPException @@ -145,7 +147,8 @@ class PasswordResetConfirmView(APIView): """ Get and set new user password. """ def post(self, request, *args, **kwargs): - serializer = serializers.PasswordRecoveryConfirmSerializer(data=request.data) + serializer = serializers.PasswordRecoveryConfirmSerializer( + data=request.data) try: serializer.is_valid(raise_exception=True) except serializers.PasswordRequirementsFailure as e: @@ -157,3 +160,15 @@ class PasswordResetConfirmView(APIView): return response.Response(data, 400) serializer.save() return response.Response(status=200) + + +class ComponentsInformationAPIView(APIView): + """Retrieve information about installed components.""" + + @extend_schema(responses=serializers.ModoboaComponentSerializer(many=True)) + def get(self, request, *args, **kwargs): + status, extensions = check_for_updates() + serializer = serializers.ModoboaComponentSerializer( + extensions, many=True + ) + return response.Response(serializer.data) diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index a48d54045..7b4aaeee1 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -528,10 +528,11 @@ class GeneralParametersForm(param_forms.AdminParametersForm): self._add_visibilty_rules( sms_backends.get_all_backend_visibility_rules() ) + available_schemes = get_dovecot_schemes() self.fields["password_scheme"].choices = [ (hasher.name, ugettext_lazy(hasher.label)) for hasher in PasswordHasher.get_password_hashers() - if hasher().scheme in get_dovecot_schemes() + if hasher().scheme in available_schemes ] def _add_dynamic_fields(self):