Merge branch 'devel'

fusion devel on master
This commit is contained in:
jorgepastorr 2023-09-01 17:54:47 +02:00
commit f3e0a62bf2
15 changed files with 109 additions and 55 deletions

View File

@ -26,12 +26,13 @@ from .actions import (list_contacts, service_report, delete_related_services, di
enable_selected) enable_selected)
from .forms import AccountCreationForm from .forms import AccountCreationForm
from .models import Account from .models import Account
from .filters import HasTipeServerFilter
class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin): class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin):
list_display = ('username', 'full_name', 'type', 'is_active') list_display = ('username', 'full_name', 'type', 'is_active')
list_filter = ( list_filter = (
'type', 'is_active', 'type', 'is_active', HasTipeServerFilter
) )
add_fieldsets = ( add_fieldsets = (
(_("User"), { (_("User"), {

View File

@ -1,7 +1,9 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration.models import Server
from orchestra.contrib.websites.models import Website
from orchestra.settings import WEB_SERVERS
class IsActiveListFilter(SimpleListFilter): class IsActiveListFilter(SimpleListFilter):
title = _("is active") title = _("is active")
@ -25,3 +27,16 @@ class IsActiveListFilter(SimpleListFilter):
elif self.value() == 'object': elif self.value() == 'object':
return queryset.filter(is_active=False) return queryset.filter(is_active=False)
return queryset return queryset
class HasTipeServerFilter(SimpleListFilter):
title = _("has type server")
parameter_name = 'has_servers'
def lookups(self, request, model_admin):
return [ (x.id, x.name) for x in Server.objects.filter(name__in=WEB_SERVERS) ]
def queryset(self, request, queryset):
if self.value() is not None:
serverWebsites = Website.objects.filter(target_server=self.value())
return queryset.filter(id__in=[ x.account.id for x in serverWebsites ] )
return queryset

View File

@ -16,7 +16,7 @@ from .filters import HasCustomAddressListFilter
from .models import List from .models import List
class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin): class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'name', 'address_name', 'address_domain_link', 'account_link', 'display_active' 'name', 'address_name', 'address_domain_link', 'account_link', 'display_active'
) )
@ -31,7 +31,7 @@ class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModel
}), }),
(_("Admin"), { (_("Admin"), {
'classes': ('wide',), 'classes': ('wide',),
'fields': ('admin_email', 'password1', 'password2'), 'fields': ('admin_email',),
}), }),
) )
fieldsets = ( fieldsets = (
@ -45,35 +45,15 @@ class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModel
) % settings.LISTS_DEFAULT_DOMAIN, ) % settings.LISTS_DEFAULT_DOMAIN,
'fields': (('address_name', 'address_domain'),) 'fields': (('address_name', 'address_domain'),)
}), }),
(_("Admin"), {
'classes': ('wide',),
'fields': ('password',),
}),
) )
search_fields = ('name', 'address_name', 'address_domain__name', 'account__username') search_fields = ('name', 'address_name', 'address_domain__name', 'account__username')
list_filter = (IsActiveListFilter, HasCustomAddressListFilter) list_filter = (IsActiveListFilter, HasCustomAddressListFilter)
readonly_fields = ('account_link',) readonly_fields = ('account_link',)
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
form = NonStoredUserChangeForm
add_form = UserCreationForm
list_select_related = ('account', 'address_domain',) list_select_related = ('account', 'address_domain',)
filter_by_account_fields = ['address_domain'] filter_by_account_fields = ['address_domain']
actions = (disable, enable, list_accounts) actions = (disable, enable, list_accounts)
address_domain_link = admin_link('address_domain', order='address_domain__name') address_domain_link = admin_link('address_domain', order='address_domain__name')
def get_urls(self):
useradmin = UserAdmin(List, self.admin_site)
return [
url(r'^(\d+)/password/$',
self.admin_site.admin_view(useradmin.user_change_password))
] + super(ListAdmin, self).get_urls()
def save_model(self, request, obj, form, change):
""" set password """
if not change:
obj.set_password(form.cleaned_data["password1"])
super(ListAdmin, self).save_model(request, obj, form, change)
admin.site.register(List, ListAdmin) admin.site.register(List, ListAdmin)

View File

@ -182,7 +182,6 @@ class MailmanController(MailmanVirtualDomainController):
context.update({ context.update({
'banner': self.get_banner(mail_list), 'banner': self.get_banner(mail_list),
'name': mail_list.name, 'name': mail_list.name,
'password': mail_list.password,
'domain': mail_list.address_domain or settings.LISTS_DEFAULT_DOMAIN, 'domain': mail_list.address_domain or settings.LISTS_DEFAULT_DOMAIN,
'address_name': mail_list.get_address_name(), 'address_name': mail_list.get_address_name(),
'address_domain': mail_list.address_domain, 'address_domain': mail_list.address_domain,

View File

@ -0,0 +1,34 @@
# Generated by Django 2.2.28 on 2023-09-01 14:59
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import orchestra.core.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
('domains', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='List',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Default list address <name>@grups.pangea.org', max_length=64, unique=True, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('address_name', models.CharField(blank=True, max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='address name')),
('admin_email', models.EmailField(help_text='Administration email address', max_length=254, verbose_name='admin email')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('address_domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='domains.Domain', verbose_name='address domain')),
],
options={
'unique_together': {('address_name', 'address_domain')},
},
),
]

View File

@ -8,17 +8,6 @@ from orchestra.core.validators import validate_name
from . import settings from . import settings
class ListQuerySet(models.QuerySet):
def create(self, **kwargs):
""" Sets password if provided, all within a single DB operation """
password = kwargs.pop('password')
instance = self.model(**kwargs)
if password:
instance.set_password(password)
instance.save()
return instance
# TODO address and domain, perhaps allow only domain? # TODO address and domain, perhaps allow only domain?
class List(models.Model): class List(models.Model):
name = models.CharField(_("name"), max_length=64, unique=True, validators=[validate_name], name = models.CharField(_("name"), max_length=64, unique=True, validators=[validate_name],
@ -35,9 +24,6 @@ class List(models.Model):
is_active = models.BooleanField(_("active"), default=True, is_active = models.BooleanField(_("active"), default=True,
help_text=_("Designates whether this account should be treated as active. " help_text=_("Designates whether this account should be treated as active. "
"Unselect this instead of deleting accounts.")) "Unselect this instead of deleting accounts."))
password = None
objects = ListQuerySet.as_manager()
class Meta: class Meta:
unique_together = ('address_name', 'address_domain') unique_together = ('address_name', 'address_domain')
@ -75,9 +61,6 @@ class List(models.Model):
def get_username(self): def get_username(self):
return self.name return self.name
def set_password(self, password):
self.password = password
def get_absolute_url(self): def get_absolute_url(self):
context = { context = {
'name': self.name 'name': self.name

View File

@ -9,13 +9,14 @@ LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL',
LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN', LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN',
'lists.{}'.format(ORCHESTRA_BASE_DOMAIN), 'grups.{}'.format(ORCHESTRA_BASE_DOMAIN),
help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default." help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default."
) )
LISTS_LIST_URL = Setting('LISTS_LIST_URL', LISTS_LIST_URL = Setting('LISTS_LIST_URL',
'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN), # 'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN),
'https://www.{0}/mailman3/lists/%(name)s.{0}'.format(LISTS_DEFAULT_DOMAIN),
help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default." help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default."
) )

View File

@ -20,7 +20,7 @@ from orchestra.core import caches
from . import settings from . import settings
from .actions import SendMailboxEmail, SendAddressEmail from .actions import SendMailboxEmail, SendAddressEmail
from .filters import HasMailboxListFilter, HasForwardListFilter, HasAddressListFilter from .filters import HasMailboxListFilter, HasForwardListFilter, HasAddressListFilter, HasTipeServerFilter
from .forms import MailboxCreationForm, MailboxChangeForm, AddressForm from .forms import MailboxCreationForm, MailboxChangeForm, AddressForm
from .models import Mailbox, Address, Autoresponse from .models import Mailbox, Address, Autoresponse
from .widgets import OpenCustomFilteringOnSelect from .widgets import OpenCustomFilteringOnSelect
@ -40,7 +40,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
list_display = ( list_display = (
'name', 'account_link', 'display_filtering', 'display_addresses', 'display_active', 'name', 'account_link', 'display_filtering', 'display_addresses', 'display_active',
) )
list_filter = (IsActiveListFilter, HasAddressListFilter, 'filtering') list_filter = (IsActiveListFilter, HasAddressListFilter, 'filtering', HasTipeServerFilter)
search_fields = ( search_fields = (
'account__username', 'account__short_name', 'account__full_name', 'name', 'account__username', 'account__short_name', 'account__full_name', 'name',
'addresses__name', 'addresses__domain__name', 'addresses__name', 'addresses__domain__name',

View File

@ -1,6 +1,8 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration.models import Server
from orchestra.contrib.websites.models import Website
from orchestra.settings import WEB_SERVERS
class HasMailboxListFilter(SimpleListFilter): class HasMailboxListFilter(SimpleListFilter):
""" Filter addresses whether they have any mailbox or not """ """ Filter addresses whether they have any mailbox or not """
@ -45,3 +47,17 @@ class HasAddressListFilter(HasMailboxListFilter):
elif self.value() == 'False': elif self.value() == 'False':
return queryset.filter(addresses__isnull=True) return queryset.filter(addresses__isnull=True)
return queryset return queryset
class HasTipeServerFilter(SimpleListFilter):
title = _("has type server")
parameter_name = 'has_servers'
def lookups(self, request, model_admin):
return [ (x.id, x.name) for x in Server.objects.filter(name__in=WEB_SERVERS) ]
def queryset(self, request, queryset):
if self.value() is not None:
serverWebsites = Website.objects.filter(target_server=self.value())
return queryset.filter(account__in=[ x.account.id for x in serverWebsites ] )
return queryset

View File

@ -15,6 +15,7 @@ from orchestra.contrib.systemusers.models import WebappUsers
from orchestra.forms.widgets import DynamicHelpTextSelect from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.plugins.admin import SelectPluginAdminMixin, display_plugin_field from orchestra.plugins.admin import SelectPluginAdminMixin, display_plugin_field
from orchestra.utils.html import get_on_site_link from orchestra.utils.html import get_on_site_link
from orchestra.settings import NEW_SERVERS
from .filters import HasWebsiteListFilter, DetailListFilter from .filters import HasWebsiteListFilter, DetailListFilter
from .models import WebApp, WebAppOption from .models import WebApp, WebAppOption
@ -57,7 +58,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
list_display = ( list_display = (
'name', 'display_type', 'display_detail', 'display_websites', 'account_link', 'target_server', 'name', 'display_type', 'display_detail', 'display_websites', 'account_link', 'target_server',
) )
list_filter = ('type', HasWebsiteListFilter, DetailListFilter) list_filter = ('type', HasWebsiteListFilter, DetailListFilter, 'target_server')
inlines = [WebAppOptionInline] inlines = [WebAppOptionInline]
readonly_fields = ('account_link',) readonly_fields = ('account_link',)
change_readonly_fields = ('name', 'type', 'display_websites', 'display_sftpuser', 'target_server',) change_readonly_fields = ('name', 'type', 'display_websites', 'display_sftpuser', 'target_server',)
@ -110,7 +111,8 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
if not change: if not change:
user = form.cleaned_data.get('username') user = form.cleaned_data.get('username')
if user: server = form.cleaned_data.get('target_server')
if user and server.name in NEW_SERVERS:
user = WebappUsers( user = WebappUsers(
username=form.cleaned_data['username'], username=form.cleaned_data['username'],
account_id=obj.account.pk, account_id=obj.account.pk,

View File

@ -3,7 +3,7 @@ import os
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from orchestra.plugins.forms import ExtendedPluginDataForm from orchestra.plugins.forms import ExtendedPluginDataForm, PluginDataForm
from ..options import AppOption from ..options import AppOption
from . import AppType from . import AppType
@ -32,6 +32,7 @@ class WebalizerApp(AppType):
"Statistics will be collected once this app is mounted into one or more Websites.") "Statistics will be collected once this app is mounted into one or more Websites.")
icon = 'orchestra/icons/apps/Stats.png' icon = 'orchestra/icons/apps/Stats.png'
option_groups = () option_groups = ()
form = PluginDataForm
def get_directive(self): def get_directive(self):
webalizer_path = os.path.join(self.instance.get_path(), '%(site_name)s') webalizer_path = os.path.join(self.instance.get_path(), '%(site_name)s')

View File

@ -63,7 +63,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
'name', 'display_domains', 'display_webapps', 'account_link', 'target_server', 'display_active' 'name', 'display_domains', 'display_webapps', 'account_link', 'target_server', 'display_active'
) )
list_filter = ( list_filter = (
'protocol', IsActiveListFilter, HasWebAppsListFilter, HasDomainsFilter 'protocol', IsActiveListFilter, HasWebAppsListFilter, HasDomainsFilter, 'target_server'
) )
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
inlines = (ContentInline, WebsiteDirectiveInline) inlines = (ContentInline, WebsiteDirectiveInline)

View File

@ -7,9 +7,11 @@ from orchestra.forms.widgets import SpanWidget
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from orchestra.core import validators from orchestra.core import validators
from orchestra.utils.python import random_ascii from orchestra.utils.python import random_ascii
from orchestra.settings import NEW_SERVERS from orchestra.settings import NEW_SERVERS, WEB_SERVERS
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import textwrap
from orchestra.contrib.orchestration.models import Server
class PluginForm(forms.ModelForm): class PluginForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -21,12 +23,13 @@ class PluginForm(forms.ModelForm):
help_text = self.fields[self.plugin_field].help_text help_text = self.fields[self.plugin_field].help_text
self.fields[self.plugin_field].help_text = getattr(self.plugin, 'help_text', help_text) self.fields[self.plugin_field].help_text = getattr(self.plugin, 'help_text', help_text)
class PluginDataForm(PluginForm): class PluginDataForm(PluginForm):
data = forms.CharField(widget=forms.HiddenInput, required=False) data = forms.CharField(widget=forms.HiddenInput, required=False)
target_server = forms.ModelChoiceField(queryset=Server.objects.filter(name__in=WEB_SERVERS),)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['sftpuser'].widget = forms.HiddenInput()
if self.instance: if self.instance:
for field in self.declared_fields: for field in self.declared_fields:
initial = self.fields[field].initial initial = self.fields[field].initial
@ -98,15 +101,25 @@ class ExtendedPluginDataForm(PluginDataForm):
widget=forms.PasswordInput, widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification.")) help_text=_("Enter the same password as above, for verification."))
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ExtendedPluginDataForm, self).__init__(*args, **kwargs) super(ExtendedPluginDataForm, self).__init__(*args, **kwargs)
self.fields['sftpuser'].widget = forms.HiddenInput()
if self.instance.id is not None: if self.instance.id is not None:
self.fields['username'].widget = forms.HiddenInput() self.fields['username'].widget = forms.HiddenInput()
self.fields['password1'].widget = forms.HiddenInput() self.fields['password1'].widget = forms.HiddenInput()
self.fields['password2'].widget = forms.HiddenInput() self.fields['password2'].widget = forms.HiddenInput()
if not self.instance.pk:
self.fields['target_server'].widget.attrs['onChange'] = textwrap.dedent("""\
field = $(".field-username, .field-password1, .field-password2");
input = $("#id_username, #id_password1, #id_password2");
if (%s.includes(this.options[this.selectedIndex].text)) {
field.removeClass("hidden");
} else {
field.addClass("hidden");
input.val("");
};""" % list(NEW_SERVERS)
)
def clean_username(self): def clean_username(self):
if not self.instance.id: if not self.instance.id:
webapp_server = self.cleaned_data.get("target_server") webapp_server = self.cleaned_data.get("target_server")

View File

@ -97,3 +97,12 @@ NEW_SERVERS = Setting('NEW_SERVERS',
'web-12.pangea.lan', 'web-12.pangea.lan',
) )
) )
WEB_SERVERS = Setting('WEBAPPS_SERVERS', (
'web.pangea.lan',
'web-ng',
'web-11.pangea.lan',
'web-12.pangea.lan',
'bookworm',
)
)