mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Added information page about components in UI v2
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import repository from './repository'
|
||||
|
||||
export default {
|
||||
getComponentsInformation () {
|
||||
return repository.get('/admin/components')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<v-card class="mt-6">
|
||||
<v-card-title>
|
||||
<translate>Installed components</translate>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert
|
||||
outlined
|
||||
type="success"
|
||||
text
|
||||
border="left"
|
||||
v-if="updatesAvailable"
|
||||
>
|
||||
<translate tag="p">One or more updates are available</translate>
|
||||
<translate tag="p" class="text-body-2">
|
||||
Check out the following list to find related components
|
||||
</translate>
|
||||
</v-alert>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="components"
|
||||
:item-class="getItemBackground"
|
||||
hide-default-footer
|
||||
>
|
||||
<template v-slot:item.last_version="{ item }">
|
||||
<template v-if="item.changelog_url">
|
||||
<a :href="item.changelog_url" target="_blank">{{ item.last_version }}</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.last_version }}
|
||||
</template>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import adminApi from '@/api/admin'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
updatesAvailable () {
|
||||
return this.components.filter(item => item.update).length > 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
components: [],
|
||||
headers: [
|
||||
{ text: this.$gettext('Name'), value: 'name' },
|
||||
{ text: this.$gettext('Installed version'), value: 'version' },
|
||||
{ text: this.$gettext('Latest version'), value: 'last_version' },
|
||||
{ text: this.$gettext('Description'), value: 'description' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getItemBackground (item) {
|
||||
if (item.update) {
|
||||
return 'green lighten-5'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
adminApi.getComponentsInformation().then(resp => {
|
||||
this.components = resp.data
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -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' }
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title><translate>Information</translate></v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<components-information-table />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ComponentsInformationTable from '@/components/admin/ComponentsInformationTable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ComponentsInformationTable
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -35,7 +35,7 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
<div>
|
||||
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login?</translate></a>
|
||||
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login</translate></a>
|
||||
</div>
|
||||
</v-col>
|
||||
<confirm-dialog ref="dialog_response">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user