Compare commits

...

25 Commits

Author SHA1 Message Date
Jorge Pastor 38d31d42e8 max length 32 databaseuser 2023-11-24 02:23:54 +01:00
Jorge Pastor 7bf7518339 nextcloud size on cron 2023-11-24 02:23:54 +01:00
Jorge Pastor be039bbeeb fix resource agragation otion last value query 2023-11-24 02:23:54 +01:00
Jorge Pastor 465862e444 change update user password on mysql 2023-11-24 02:23:54 +01:00
jorgepastorr 38a671a18a add icon nextcloud on SAS 2023-11-24 02:23:54 +01:00
jorgepastorr bf84b27e8a fix last nyapa with target_server on SAS 2023-11-24 02:23:54 +01:00
jorgepastorr a41243bc70 nyapa hiden target server in SAS 2023-11-24 02:23:54 +01:00
jorgepastorr b2d335879a fix webappUser create user with groups dont exist 2023-11-24 02:23:54 +01:00
jorgepastorr 87ee955531 fix problem with sftpuser onnSAslist and webalizer 2023-11-24 02:23:54 +01:00
jorgepastorr 8ff8676773 delete local bokworm settiongs 2023-11-24 02:23:54 +01:00
jorgepastorr f8e20f448c delete fields password in lists
edited 2023/11/24 by pedro
2023-11-24 02:23:54 +01:00
jorgepastorr 727e63c9af add filter servers on accounts and mailboxes to send messages 2023-11-24 02:23:54 +01:00
jorgepastorr 9f50bcc672 hidden sftpuser in webalizer form 2023-11-24 02:23:54 +01:00
jorgepastorr 8f84b4fee9 hidden user,password in servers without webappusers 2023-11-24 02:23:54 +01:00
jorgepastorr 4475b3257e delete local bookworm 2023-11-24 02:23:54 +01:00
jorgepastorr 708bbb3a3f websites complete fusion 2023-11-24 02:23:54 +01:00
jorgepastorr 4368b40d00 wordpress complete 2023-11-24 02:23:54 +01:00
jorgepastorr dacc046dc0 webapp php complete 2023-11-24 02:23:54 +01:00
jorgepastorr 50b83c31bd webapps static complete 2023-11-24 02:23:54 +01:00
jorgepastorr 24baf1bb70 webapp static form
edited 2023/11/24 by pedro
2023-11-24 02:23:54 +01:00
jorgepastorr 7d9805869d webappusers in new servers
edited 2023/11/24 by pedro
2023-11-24 02:23:54 +01:00
jorgepastorr d6c9620b54 mainusers only in newservers 2023-11-24 02:23:54 +01:00
jorgepastorr 27463807f5 systemusers in new servers 2023-11-24 02:23:54 +01:00
jorgepastorr f63134f3fe main system users in new servers 2023-11-24 02:23:54 +01:00
Jorge Pastor 39eea70b79 inicio 2023/07/09
edited 2023/11/24 by pedro
2023-11-24 02:23:54 +01:00
224 changed files with 1902 additions and 586 deletions

View File

@ -170,10 +170,10 @@ django-admin.py compilemessages -l ca
https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#joining-strings-string-concat https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#joining-strings-string-concat
from django.utils.translation import ugettext from django.utils.translation import gettext
from django.utils import translation from django.utils import translation
translation.activate('ca') translation.activate('ca')
ugettext("Description") gettext("Description")
* saas validate_creation generic approach, for all backends. standard output * saas validate_creation generic approach, for all backends. standard output

View File

@ -59,7 +59,7 @@
```python ```python
import os import os
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.orchestration import ServiceController, replace
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor

View File

@ -6,7 +6,7 @@ from django.contrib import admin
from django.urls import reverse from django.urls import reverse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .dashboard import * from .dashboard import *
from .options import * from .options import *

View File

@ -3,7 +3,7 @@ from functools import partial
from django.contrib import admin from django.contrib import admin
from django.core.mail import send_mass_mail from django.core.mail import send_mass_mail
from django.shortcuts import render from django.shortcuts import render
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from .. import settings from .. import settings
@ -81,7 +81,7 @@ class SendEmail(object):
if extra_to: if extra_to:
emails.append((subject, message, email_from, extra_to)) emails.append((subject, message, email_from, extra_to))
send_mass_mail(emails, fail_silently=False) send_mass_mail(emails, fail_silently=False)
msg = ungettext( msg = ngettext(
_("Message has been sent to one %s.") % self.opts.verbose_name_plural, _("Message has been sent to one %s.") % self.opts.verbose_name_plural,
_("Message has been sent to %i %s.") % (num, self.opts.verbose_name_plural), _("Message has been sent to %i %s.") % (num, self.opts.verbose_name_plural),
num num
@ -124,7 +124,7 @@ def base_disable(modeladmin, request, queryset, disable=True):
'verbose_name_plural': opts.verbose_name_plural, 'verbose_name_plural': opts.verbose_name_plural,
'num': num 'num': num
} }
msg = ungettext( msg = ngettext(
_("Selected %(verbose_name)s and related services has been %(action_name)s.") % context, _("Selected %(verbose_name)s and related services has been %(action_name)s.") % context,
_("%(num)s selected %(verbose_name_plural)s and related services have been %(action_name)s.") % context, _("%(num)s selected %(verbose_name_plural)s and related services have been %(action_name)s.") % context,
num) num)

View File

@ -1,5 +1,5 @@
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from fluent_dashboard import dashboard, appsettings from fluent_dashboard import dashboard, appsettings
from fluent_dashboard.modules import CmsAppIconList from fluent_dashboard.modules import CmsAppIconList

View File

@ -4,11 +4,10 @@ from django.contrib import messages
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import available_attrs from django.utils.encoding import force_str
from django.utils.encoding import force_text
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
def admin_field(method): def admin_field(method):
@ -50,7 +49,7 @@ def action_with_confirmation(action_name=None, extra_context=None, validator=Non
""" """
def decorator(func, extra_context=extra_context, template=template, action_name=action_name, validatior=validator): def decorator(func, extra_context=extra_context, template=template, action_name=action_name, validatior=validator):
@wraps(func, assigned=available_attrs(func)) @wraps(func)
def inner(modeladmin, request, queryset, action_name=action_name, extra_context=extra_context, validator=validator): def inner(modeladmin, request, queryset, action_name=action_name, extra_context=extra_context, validator=validator):
if validator is not None: if validator is not None:
try: try:
@ -69,10 +68,10 @@ def action_with_confirmation(action_name=None, extra_context=None, validator=Non
action_value = func.__name__ action_value = func.__name__
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
obj = queryset.get() obj = queryset.get()
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
obj = None obj = None
if not action_name: if not action_name:
action_name = func.__name__ action_name = func.__name__

View File

@ -5,8 +5,8 @@ from django import forms
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.core import validators from django.core import validators
from django.forms.models import modelformset_factory, BaseModelFormSet from django.forms.models import modelformset_factory, BaseModelFormSet
from django.template import Template from django.template import Template, Context
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.forms.widgets import SpanWidget from orchestra.forms.widgets import SpanWidget
@ -31,7 +31,7 @@ class AdminFormMixin(object):
context = { context = {
'adminform': adminform 'adminform': adminform
} }
return template.render(context) return template.render(Context(context))
class AdminFormSet(BaseModelFormSet): class AdminFormSet(BaseModelFormSet):
@ -74,7 +74,7 @@ class AdminFormSet(BaseModelFormSet):
context = { context = {
'formset': self 'formset': self
} }
return template.render(context) return template.render(Context(context))
class AdminPasswordChangeForm(forms.Form): class AdminPasswordChangeForm(forms.Form):

View File

@ -3,7 +3,7 @@ from copy import deepcopy
from admin_tools.menu import items, Menu from admin_tools.menu import items, Menu
from django.urls import reverse from django.urls import reverse
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services, accounts, administration from orchestra.core import services, accounts, administration
@ -73,7 +73,7 @@ class OrchestraMenu(Menu):
self.children = [ self.children = [
# items.MenuItem( # items.MenuItem(
# mark_safe('{site_name} <span style="{version_style}">v{version}</span>'.format( # mark_safe('{site_name} <span style="{version_style}">v{version}</span>'.format(
# site_name=force_text(settings.SITE_VERBOSE_NAME), # site_name=force_str(settings.SITE_VERBOSE_NAME),
# version_style="text-transform:none; float:none; font-size:smaller; background:none;", # version_style="text-transform:none; float:none; font-size:smaller; background:none;",
# version=get_version())), # version=get_version())),
# reverse('admin:index') # reverse('admin:index')

View File

@ -1,7 +1,7 @@
from urllib import parse from urllib import parse
from django import forms from django import forms
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
@ -12,9 +12,9 @@ from django.forms.models import BaseInlineFormSet
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
from orchestra.models.utils import has_db_field from orchestra.models.utils import has_db_field
@ -230,7 +230,7 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
if obj is None: if obj is None:
opts = self.model._meta opts = self.model._meta
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % { raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {
'name': force_text(opts.verbose_name), 'key': escape(object_id)}) 'name': force_str(opts.verbose_name), 'key': escape(object_id)})
return obj return obj

View File

@ -1,8 +1,8 @@
from django.contrib.admin.options import get_content_type_for_model from django.contrib.admin.options import get_content_type_for_model
from django.conf import settings as django_settings from django.conf import settings as django_settings
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.module_loading import autodiscover_modules from django.utils.module_loading import autodiscover_modules
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from orchestra import settings from orchestra import settings
@ -52,7 +52,7 @@ class LogApiMixin(object):
user_id=request.user.pk, user_id=request.user.pk,
content_type_id=get_content_type_for_model(instance).pk, content_type_id=get_content_type_for_model(instance).pk,
object_id=instance.pk, object_id=instance.pk,
object_repr=force_text(instance), object_repr=force_str(instance),
action_flag=action, action_flag=action,
change_message=message, change_message=message,
) )

View File

@ -3,7 +3,7 @@ import copy
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.forms import widgets from django.forms import widgets
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.utils import model_meta from rest_framework.utils import model_meta

View File

@ -9,10 +9,10 @@ from django.db import router
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.core import services from orchestra.core import services
@ -84,7 +84,7 @@ def delete_related_services(modeladmin, request, queryset):
def format(obj, account=False): def format(obj, account=False):
has_admin = obj.__class__ in admin_site._registry has_admin = obj.__class__ in admin_site._registry
opts = obj._meta opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_str(obj))
if has_admin: if has_admin:
try: try:
@ -154,7 +154,7 @@ def delete_related_services(modeladmin, request, queryset):
if accounts: if accounts:
relateds = len(to_delete) relateds = len(to_delete)
for obj in to_delete: for obj in to_delete:
obj_display = force_text(obj) obj_display = force_str(obj)
modeladmin.log_deletion(request, obj, obj_display) modeladmin.log_deletion(request, obj, obj_display)
obj.delete() obj.delete()
context = { context = {
@ -167,9 +167,9 @@ def delete_related_services(modeladmin, request, queryset):
return None return None
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
model_count = {} model_count = {}
for model, objs in collector.model_objs.items(): for model, objs in collector.model_objs.items():
@ -214,7 +214,7 @@ def disable_selected(modeladmin, request, queryset, disable=True):
account.disable() if disable else account.enable() account.disable() if disable else account.enable()
modeladmin.log_change(request, account, verbose_action_name.capitalize()) modeladmin.log_change(request, account, verbose_action_name.capitalize())
n += 1 n += 1
modeladmin.message_user(request, ungettext( modeladmin.message_user(request, ngettext(
_("One account has been successfully %s.") % verbose_action_name, _("One account has been successfully %s.") % verbose_action_name,
_("%i accounts have been successfully %s.") % (n, verbose_action_name), _("%i accounts have been successfully %s.") % (n, verbose_action_name),
n) n)
@ -227,7 +227,7 @@ def disable_selected(modeladmin, request, queryset, disable=True):
def format(obj): def format(obj):
has_admin = obj.__class__ in admin_site._registry has_admin = obj.__class__ in admin_site._registry
opts = obj._meta opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_str(obj))
if has_admin: if has_admin:
try: try:
admin_url = reverse( admin_url = reverse(
@ -258,9 +258,9 @@ def disable_selected(modeladmin, request, queryset, disable=True):
display.append([format(account), current]) display.append([format(account), current])
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
context = dict( context = dict(
admin_site.each_context(request), admin_site.each_context(request),

View File

@ -4,7 +4,7 @@ from urllib.parse import parse_qsl
from django import forms from django import forms
from django.apps import apps from django.apps import apps
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.contrib.auth import admin as auth from django.contrib.auth import admin as auth
@ -12,7 +12,7 @@ from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import SendEmail from orchestra.admin.actions import SendEmail
@ -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,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import viewsets, exceptions from rest_framework import viewsets, exceptions
from orchestra.api import router, SetPasswordApiMixin, LogApiMixin from orchestra.api import router, SetPasswordApiMixin, LogApiMixin

View File

@ -1,6 +1,6 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services, accounts from orchestra.core import services, accounts

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 ugettext_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

@ -4,7 +4,7 @@ from collections import OrderedDict
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.apps import apps from django.apps import apps
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.forms import UserCreationForm from orchestra.forms import UserCreationForm

View File

@ -5,7 +5,7 @@ from django.db import models
from django.db.models import signals from django.db.models import signals
from django.apps import apps from django.apps import apps
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
#from orchestra.contrib.orchestration.middlewares import OperationsMiddleware #from orchestra.contrib.orchestration.middlewares import OperationsMiddleware
#from orchestra.contrib.orchestration import Operation #from orchestra.contrib.orchestration import Operation
@ -165,7 +165,6 @@ class Account(auth.AbstractBaseUser):
elif obj and getattr(obj, 'account', None) == self: elif obj and getattr(obj, 'account', None) == self:
return True return True
def has_perms(self, perm_list, obj=None): def has_perms(self, perm_list, obj=None):
""" """
Returns True if the user has each of the specified permissions. If Returns True if the user has each of the specified permissions. If

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting
from orchestra.settings import ORCHESTRA_BASE_DOMAIN from orchestra.settings import ORCHESTRA_BASE_DOMAIN

View File

@ -1,5 +1,5 @@
{% extends "orchestra/admin/change_form.html" %} {% extends "orchestra/admin/change_form.html" %}
{% load i18n admin_urls admin_static admin_modify %} {% load i18n admin_urls static admin_modify %}
{% block breadcrumbs %} {% block breadcrumbs %}

View File

@ -12,7 +12,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils import translation, timezone from django.utils import translation, timezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation from orchestra.admin.decorators import action_with_confirmation
from orchestra.admin.forms import AdminFormSet from orchestra.admin.forms import AdminFormSet
@ -69,7 +69,7 @@ def close_bills(modeladmin, request, queryset, action='close_bills'):
'url': url, 'url': url,
'num': num, 'num': num,
} }
message = ungettext( message = ngettext(
_('<a href="%(url)s">One related transaction</a> has been created') % context, _('<a href="%(url)s">One related transaction</a> has been created') % context,
_('<a href="%(url)s">%(num)i related transactions</a> have been created') % context, _('<a href="%(url)s">%(num)i related transactions</a> have been created') % context,
num) num)
@ -111,7 +111,7 @@ def send_bills_action(modeladmin, request, queryset):
bill.send() bill.send()
modeladmin.log_change(request, bill, 'Sent') modeladmin.log_change(request, bill, 'Sent')
num += 1 num += 1
messages.success(request, ungettext( messages.success(request, ngettext(
_("One bill has been sent."), _("One bill has been sent."),
_("%i bills have been sent.") % num, _("%i bills have been sent.") % num,
num)) num))
@ -135,7 +135,7 @@ def download_bills(modeladmin, request, queryset):
pdf = bill.as_pdf() pdf = bill.as_pdf()
archive.writestr('%s.pdf' % bill.number, pdf) archive.writestr('%s.pdf' % bill.number, pdf)
archive.close() archive.close()
response = HttpResponse(bytesio.getvalue(), content_type='application/pdf') response = HttpResponse(bytesio.getvalue(), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="orchestra-bills.zip"' response['Content-Disposition'] = 'attachment; filename="orchestra-bills.zip"'
return response return response
bill = queryset[0] bill = queryset[0]
@ -299,7 +299,7 @@ def amend_bills(modeladmin, request, queryset):
'url': amend_url, 'url': amend_url,
'num': num, 'num': num,
} }
messages.success(request, mark_safe(ungettext( messages.success(request, mark_safe(ngettext(
_('<a href="%(url)s">One amendment bill</a> have been generated.') % context, _('<a href="%(url)s">One amendment bill</a> have been generated.') % context,
_('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context, _('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context,
num num

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.urls import reverse from django.urls import reverse
@ -9,7 +9,7 @@ from django.db.models.functions import Coalesce
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.shortcuts import redirect from django.shortcuts import redirect
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
@ -126,7 +126,7 @@ class ClosedBillLineInline(BillLineInline):
return line.compute_total() return line.compute_total()
display_total.short_description = _("Total") display_total.short_description = _("Total")
def has_add_permission(self, request): def has_add_permission(self, request, obj):
return False return False

View File

@ -2,7 +2,7 @@ from django.contrib.admin import SimpleListFilter
from django.urls import reverse from django.urls import reverse
from django.db.models import Q from django.db.models import Q
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . models import Bill from . models import Bill

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import admin_link from orchestra.admin.utils import admin_link
from orchestra.forms import SpanWidget from orchestra.forms import SpanWidget

View File

@ -1,10 +1,10 @@
from django.contrib import messages from django.contrib import messages
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -16,14 +16,14 @@ def validate_contact(request, bill, error=True):
valid = True valid = True
send = messages.error if error else messages.warning send = messages.error if error else messages.warning
if not hasattr(bill.account, 'billcontact'): if not hasattr(bill.account, 'billcontact'):
account = force_text(bill.account) account = force_str(bill.account)
url = reverse('admin:accounts_account_change', args=(bill.account_id,)) url = reverse('admin:accounts_account_change', args=(bill.account_id,))
message = msg.format(relation=_("Related"), account=account, url=url) message = msg.format(relation=_("Related"), account=account, url=url)
send(request, mark_safe(message)) send(request, mark_safe(message))
valid = False valid = False
main = type(bill).account.field.related_model.objects.get_main() main = type(bill).account.field.related_model.objects.get_main()
if not hasattr(main, 'billcontact'): if not hasattr(main, 'billcontact'):
account = force_text(main) account = force_str(main)
url = reverse('admin:accounts_account_change', args=(main.id,)) url = reverse('admin:accounts_account_change', args=(main.id,))
message = msg.format(relation=_("Main"), account=account, url=url) message = msg.format(relation=_("Main"), account=account, url=url)
send(request, mark_safe(message)) send(request, mark_safe(message))

View File

@ -8,9 +8,9 @@ from django.db.models import F, Sum
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.template import loader from django.template import loader
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
from orchestra.contrib.accounts.models import Account from orchestra.contrib.accounts.models import Account
@ -209,7 +209,7 @@ class Bill(models.Model):
def get_payment_state_display(self): def get_payment_state_display(self):
value = self.payment_state value = self.payment_state
return force_text(dict(self.PAYMENT_STATES).get(value, value)) return force_str(dict(self.PAYMENT_STATES).get(value, value))
def get_current_transaction(self): def get_current_transaction(self):
return self.transactions.exclude_rejected().first() return self.transactions.exclude_rejected().first()

View File

@ -1,6 +1,6 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin
from orchestra.admin.actions import SendEmail from orchestra.admin.actions import SendEmail

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Contact from .models import Contact

View File

@ -1,7 +1,7 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
from orchestra.models.fields import MultiSelectField from orchestra.models.fields import MultiSelectField

View File

@ -1,9 +1,9 @@
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -11,7 +11,7 @@ from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from .filters import HasUserListFilter, HasDatabaseListFilter from .filters import HasUserListFilter, HasDatabaseListFilter
from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm, DatabaseForm
from .models import Database, DatabaseUser from .models import Database, DatabaseUser
def save_selected(modeladmin, request, queryset): def save_selected(modeladmin, request, queryset):
@ -20,21 +20,21 @@ def save_selected(modeladmin, request, queryset):
save_selected.short_description = "Re-save selected objects" save_selected.short_description = "Re-save selected objects"
class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'type', 'display_users', 'account_link') list_display = ('name', 'type', 'target_server', 'display_users', 'account_link')
list_filter = ('type', HasUserListFilter) list_filter = ('type', HasUserListFilter)
search_fields = ('name', 'account__username') search_fields = ('name', 'account__username')
change_readonly_fields = ('name', 'type') change_readonly_fields = ('name', 'type', 'target_server')
extra = 1 extra = 1
fieldsets = ( fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'name', 'type', 'users', 'display_users', 'comments'), 'fields': ('account_link', 'name', 'type', 'users', 'display_users', 'comments', 'target_server'),
}), }),
) )
add_fieldsets = ( add_fieldsets = (
(None, { (None, {
'classes': ('wide',), 'classes': ('wide',),
'fields': ('account_link', 'name', 'type') 'fields': ('account_link', 'name', 'type', 'target_server')
}), }),
(_("Create new user"), { (_("Create new user"), {
'classes': ('wide',), 'classes': ('wide',),
@ -45,11 +45,10 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
'fields': ('user',) 'fields': ('user',)
}), }),
) )
form = DatabaseForm
add_form = DatabaseCreationForm add_form = DatabaseCreationForm
readonly_fields = ('account_link', 'display_users',) readonly_fields = ('account_link', 'display_users',)
filter_horizontal = ['users'] filter_horizontal = ['users']
filter_by_account_fields = ('users',)
list_prefetch_related = ('users',)
actions = (list_accounts, save_selected) actions = (list_accounts, save_selected)
@mark_safe @mark_safe
@ -71,6 +70,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
username=form.cleaned_data['username'], username=form.cleaned_data['username'],
type=obj.type, type=obj.type,
account_id=obj.account.pk, account_id=obj.account.pk,
target_server=form.cleaned_data['target_server'],
) )
user.set_password(form.cleaned_data["password1"]) user.set_password(form.cleaned_data["password1"])
user.save() user.save()
@ -78,22 +78,22 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin): class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin):
list_display = ('username', 'type', 'display_databases', 'account_link') list_display = ('username', 'target_server', 'type', 'display_databases', 'account_link')
list_filter = ('type', HasDatabaseListFilter) list_filter = ('type', HasDatabaseListFilter)
search_fields = ('username', 'account__username') search_fields = ('username', 'account__username')
form = DatabaseUserChangeForm form = DatabaseUserChangeForm
add_form = DatabaseUserCreationForm add_form = DatabaseUserCreationForm
change_readonly_fields = ('username', 'type') change_readonly_fields = ('username', 'type', 'target_server')
fieldsets = ( fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'username', 'password', 'type', 'display_databases') 'fields': ('account_link', 'username', 'password', 'type', 'display_databases', 'target_server', 'permision')
}), }),
) )
add_fieldsets = ( add_fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'username', 'password1', 'password2', 'type') 'fields': ('account_link', 'username', 'password1', 'password2', 'type', 'target_server', 'permision')
}), }),
) )
readonly_fields = ('account_link', 'display_databases',) readonly_fields = ('account_link', 'display_databases',)

View File

@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services from orchestra.core import services

View File

@ -1,6 +1,6 @@
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.orchestration import ServiceController, replace
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor
@ -36,10 +36,16 @@ class MySQLController(ServiceController):
'username': user.username, 'username': user.username,
'grant': 'WITH GRANT OPTION' if user == context['owner'] else '' 'grant': 'WITH GRANT OPTION' if user == context['owner'] else ''
}) })
self.append(textwrap.dedent("""\ if user.permision == "ro":
mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\ self.append(textwrap.dedent("""\
""") % context mysql -e 'GRANT SELECT ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\
) """) % context
)
else:
self.append(textwrap.dedent("""\
mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\
""") % context
)
def delete(self, database): def delete(self, database):
if database.type != database.MYSQL: if database.type != database.MYSQL:
@ -85,8 +91,8 @@ class MySQLUserController(ServiceController):
context = self.get_context(user) context = self.get_context(user)
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""\
# Create user %(username)s # Create user %(username)s
mysql -e 'CREATE USER "%(username)s"@"%(host)s";' || true # User already exists mysql -e 'CREATE USER IF NOT EXISTS "%(username)s"@"%(host)s";'
mysql -e 'UPDATE mysql.user SET Password="%(password)s" WHERE User="%(username)s";'\ mysql -e 'ALTER USER IF EXISTS "%(username)s"@"%(host)s" IDENTIFIED BY PASSWORD "%(password)s";'\
""") % context """) % context
) )
@ -172,7 +178,7 @@ class MysqlDisk(ServiceMonitor):
def get_context(self, db): def get_context(self, db):
context = { context = {
'db_name': db.name, 'db_name': db.name,
'db_dirname': db.name.replace('-', '@003f'), 'db_dirname': db.name.replace('-', '@002d'),
'db_id': db.pk, 'db_id': db.pk,
'db_type': db.type, 'db_type': db.type,
} }

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class HasUserListFilter(SimpleListFilter): class HasUserListFilter(SimpleListFilter):

View File

@ -3,7 +3,7 @@ from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
@ -31,13 +31,39 @@ class DatabaseUserCreationForm(forms.ModelForm):
return password2 return password2
class DatabaseForm(forms.ModelForm):
class Meta:
model = Database
fields = ('name', 'users', 'type', 'account', 'target_server')
def __init__(self, *args, **kwargs):
super(DatabaseForm, self).__init__(*args, **kwargs)
# muestra solo los usuarios del mismo server
account_id = self.instance.account_id
database_server_id = self.instance.target_server_id
if account_id:
self.fields['users'].queryset = DatabaseUser.objects.filter(account=account_id, target_server=database_server_id)
def clean(self):
# verifica que los usuarios petenecen al servidor de la bbdd
database_server_id = self.instance.target_server_id
users = self.cleaned_data.get('users')
if users and database_server_id:
for user in users:
if user.target_server_id != database_server_id:
self.add_error("users", _(f"{user.username} does not belong to the database server"))
return self.cleaned_data
class DatabaseCreationForm(DatabaseUserCreationForm): class DatabaseCreationForm(DatabaseUserCreationForm):
username = forms.CharField(label=_("Username"), max_length=16, username = forms.CharField(label=_("Username"), max_length=32,
required=False, validators=[validators.validate_name], required=False, validators=[validators.validate_name],
help_text=_("Required. 16 characters or fewer. Letters, digits and " help_text=_("Required. 32 characters or fewer. Letters, digits and "
"@/./+/-/_ only."), "@/./+/-/_ only."),
error_messages={ error_messages={
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and " 'invalid': _("This value may contain 32 characters or fewer, only letters, numbers and "
"@/./+/-/_ characters.")}) "@/./+/-/_ characters.")})
user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects) user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects)
@ -50,13 +76,14 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
account_id = self.initial.get('account', self.initial_account) account_id = self.initial.get('account', self.initial_account)
if account_id: if account_id:
qs = self.fields['user'].queryset.filter(account=account_id).order_by('username') qs = self.fields['user'].queryset.filter(account=account_id).order_by('username')
choices = [ (u.pk, "%s (%s)" % (u, u.get_type_display())) for u in qs ] choices = [ (u.pk, "%s (%s) (%s)" % (u, u.get_type_display(), str(u.target_server.name) )) for u in qs ]
self.fields['user'].queryset = qs self.fields['user'].queryset = qs
self.fields['user'].choices = [(None, '--------'),] + choices self.fields['user'].choices = [(None, '--------'),] + choices
def clean_username(self): def clean_username(self):
username = self.cleaned_data.get('username') username = self.cleaned_data.get('username')
if DatabaseUser.objects.filter(username=username).exists(): server = self.cleaned_data.get('target_server')
if DatabaseUser.objects.filter(username=username, target_server=server).exists():
raise ValidationError("Provided username already exists.") raise ValidationError("Provided username already exists.")
return username return username
@ -76,6 +103,9 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
if user and user.type != self.cleaned_data.get('type'): if user and user.type != self.cleaned_data.get('type'):
msg = _("Database type and user type doesn't match") msg = _("Database type and user type doesn't match")
raise ValidationError(msg) raise ValidationError(msg)
if user and user.target_server != self.cleaned_data.get('target_server'):
msg = _("Database server and user server doesn't match")
raise ValidationError(msg)
return user return user
def clean(self): def clean(self):

View File

@ -1,52 +1,46 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.28 on 2023-06-28 17:06
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import orchestra.core.validators import orchestra.core.validators
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel(
name='Database',
fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=64, validators=[orchestra.core.validators.validate_name])),
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='DatabaseUser', name='DatabaseUser',
fields=[ fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(verbose_name='username', max_length=16, validators=[orchestra.core.validators.validate_name])), ('username', models.CharField(max_length=16, validators=[orchestra.core.validators.validate_name], verbose_name='username')),
('password', models.CharField(verbose_name='password', max_length=256)), ('password', models.CharField(max_length=256, verbose_name='password')),
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)), ('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', verbose_name='Account', to=settings.AUTH_USER_MODEL)), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
], ],
options={ options={
'verbose_name_plural': 'DB users', 'verbose_name_plural': 'DB users',
'unique_together': {('username', 'type')},
}, },
), ),
migrations.AddField( migrations.CreateModel(
model_name='database', name='Database',
name='users', fields=[
field=models.ManyToManyField(related_name='databases', to='databases.DatabaseUser', verbose_name='users', blank=True), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
), ('name', models.CharField(max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
migrations.AlterUniqueTogether( ('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type')),
name='databaseuser', ('comments', models.TextField(blank=True, default='')),
unique_together=set([('username', 'type')]), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
), ('users', models.ManyToManyField(blank=True, related_name='databases', to='databases.DatabaseUser', verbose_name='users')),
migrations.AlterUniqueTogether( ],
name='database', options={
unique_together=set([('name', 'type')]), 'unique_together': {('name', 'type')},
},
), ),
] ]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.28 on 2023-06-28 17:11
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='databaseuser',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Target Server'),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 2.2.28 on 2023-06-29 16:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0002_databaseuser_target_server'),
]
operations = [
migrations.AddField(
model_name='databaseuser',
name='permision',
field=models.CharField(choices=[('all', 'all'), ('ro', 'read only')], default='all', max_length=20, verbose_name='Permisson'),
),
migrations.AlterField(
model_name='databaseuser',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Server'),
),
migrations.AlterUniqueTogether(
name='databaseuser',
unique_together={('username', 'type', 'target_server')},
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.28 on 2023-06-29 16:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0003_auto_20230629_1838'),
]
operations = [
migrations.AddField(
model_name='database',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Server'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-07-05 10:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0004_database_target_server'),
]
operations = [
migrations.AlterUniqueTogether(
name='database',
unique_together={('name', 'type', 'target_server')},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2023-07-05 10:37
from django.db import migrations, models
import orchestra.core.validators
class Migration(migrations.Migration):
dependencies = [
('databases', '0005_auto_20230705_1208'),
]
operations = [
migrations.AlterField(
model_name='databaseuser',
name='username',
field=models.CharField(max_length=32, validators=[orchestra.core.validators.validate_name], verbose_name='username'),
),
]

View File

@ -1,7 +1,7 @@
import hashlib import hashlib
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
@ -23,9 +23,11 @@ class Database(models.Model):
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE, account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
verbose_name=_("Account"), related_name='databases') verbose_name=_("Account"), related_name='databases')
comments = models.TextField(default="", blank=True) comments = models.TextField(default="", blank=True)
target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE,
verbose_name=_("Server"), default=3 )
class Meta: class Meta:
unique_together = ('name', 'type') unique_together = ('name', 'type', 'target_server')
def __str__(self): def __str__(self):
return "%s" % self.name return "%s" % self.name
@ -54,7 +56,12 @@ class DatabaseUser(models.Model):
MYSQL = Database.MYSQL MYSQL = Database.MYSQL
POSTGRESQL = Database.POSTGRESQL POSTGRESQL = Database.POSTGRESQL
username = models.CharField(_("username"), max_length=16, # MySQL usernames 16 char long typeOfPermision = [
('all','all'),
('ro', 'read only'),
]
username = models.CharField(_("username"), max_length=32, # MySQL usernames 16 char long
validators=[validators.validate_name]) validators=[validators.validate_name])
password = models.CharField(_("password"), max_length=256) password = models.CharField(_("password"), max_length=256)
type = models.CharField(_("type"), max_length=32, type = models.CharField(_("type"), max_length=32,
@ -62,10 +69,14 @@ class DatabaseUser(models.Model):
default=settings.DATABASES_DEFAULT_TYPE) default=settings.DATABASES_DEFAULT_TYPE)
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE, account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
verbose_name=_("Account"), related_name='databaseusers') verbose_name=_("Account"), related_name='databaseusers')
target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE,
verbose_name=_("Server"), default=3 )
permision = models.CharField(verbose_name=_("Permisson"), max_length=20, choices=typeOfPermision, default='all')
class Meta: class Meta:
verbose_name_plural = _("DB users") verbose_name_plural = _("DB users")
unique_together = ('username', 'type') unique_together = ('username', 'type', 'target_server')
def __str__(self): def __str__(self):
return self.username return self.username

View File

@ -7,7 +7,7 @@ from django.db.models.functions import Concat, Coalesce
from django.forms.models import modelformset_factory from django.forms.models import modelformset_factory
from django.shortcuts import render from django.shortcuts import render
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from orchestra.admin.utils import get_object_from_url, change_url, admin_link from orchestra.admin.utils import get_object_from_url, change_url, admin_link
@ -84,7 +84,7 @@ def edit_records(modeladmin, request, queryset):
change_message = modeladmin.construct_change_message(request, fake_form, [formset]) change_message = modeladmin.construct_change_message(request, fake_form, [formset])
modeladmin.log_change(request, formset.instance, change_message) modeladmin.log_change(request, formset.instance, change_message)
num = len(formsets) num = len(formsets)
message = ungettext( message = ngettext(
_("Records for one selected domain have been updated."), _("Records for one selected domain have been updated."),
_("Records for %i selected domains have been updated.") % num, _("Records for %i selected domains have been updated.") % num,
num) num)
@ -127,7 +127,7 @@ def set_soa(modeladmin, request, queryset):
modeladmin.log_change(request, domain, change_message) modeladmin.log_change(request, domain, change_message)
domain.save() domain.save()
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("SOA record for one domain has been updated."), _("SOA record for one domain has been updated."),
_("SOA record for %s domains has been updated.") % num, _("SOA record for %s domains has been updated.") % num,
num num

View File

@ -5,7 +5,7 @@ from django.db.models.functions import Concat, Coalesce
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, change_url from orchestra.admin.utils import admin_link, change_url

View File

@ -2,7 +2,7 @@ import re
import socket import socket
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController from orchestra.contrib.orchestration import ServiceController
from orchestra.contrib.orchestration import Operation from orchestra.contrib.orchestration import Operation

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class TopDomainListFilter(SimpleListFilter): class TopDomainListFilter(SimpleListFilter):

View File

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.forms import AdminFormSet, AdminFormMixin from orchestra.admin.forms import AdminFormSet, AdminFormMixin

View File

@ -1,6 +1,6 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.core.validators import validate_ipv4_address, validate_ipv6_address, validate_ascii from orchestra.core.validators import validate_ipv4_address, validate_ipv6_address, validate_ascii
from orchestra.utils.python import AttrDict from orchestra.utils.python import AttrDict
@ -114,7 +114,7 @@ class Domain(models.Model):
def get_description(self): def get_description(self):
if self.is_top: if self.is_top:
num = self.subdomains.count() num = self.subdomains.count()
return ungettext( return ngettext(
_("top domain with one subdomain"), _("top domain with one subdomain"),
_("top domain with %d subdomains") % num, _("top domain with %d subdomains") % num,
num) num)
@ -144,6 +144,10 @@ class Domain(models.Model):
tail.append(subdomain) tail.append(subdomain)
else: else:
zone += subdomain.render_records() zone += subdomain.render_records()
###darmengo 2021-03-25 add autoconfig
if self.has_default_mx():
zone += 'autoconfig.{}. 30m IN A 109.69.8.133\n'.format(self.name)
###END darmengo 2021-03-25 add autoconfig
for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True): for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True):
zone += subdomain.render_records() zone += subdomain.render_records()
return zone.strip() return zone.strip()

View File

@ -1,5 +1,5 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer from orchestra.api.serializers import HyperlinkedModelSerializer

View File

@ -1,5 +1,5 @@
{% extends "admin/change_form.html" %} {% extends "admin/change_form.html" %}
{% load i18n admin_urls admin_static admin_modify %} {% load i18n admin_urls static admin_modify %}
{% block object-tools-items %} {% block object-tools-items %}

View File

@ -3,7 +3,7 @@ import os
import re import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core.validators import validate_hostname from orchestra.core.validators import validate_hostname
from orchestra.utils import paths from orchestra.utils import paths

View File

@ -1,12 +1,12 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.templatetags.admin_static import static from django.templatetags.static import static
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import admin_date, admin_link from orchestra.admin.utils import admin_date, admin_link

View File

@ -2,7 +2,7 @@ import sys
from django.contrib import messages from django.contrib import messages
from django.db import transaction from django.db import transaction
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation from orchestra.admin.decorators import action_with_confirmation
@ -102,7 +102,7 @@ def mark_as_unread(modeladmin, request, queryset):
ticket.mark_as_unread_by(request.user) ticket.mark_as_unread_by(request.user)
modeladmin.log_change(request, ticket, 'Marked as unread') modeladmin.log_change(request, ticket, 'Marked as unread')
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("Selected ticket has been marked as unread."), _("Selected ticket has been marked as unread."),
_("%i selected tickets have been marked as unread.") % num, _("%i selected tickets have been marked as unread.") % num,
num) num)
@ -116,7 +116,7 @@ def mark_as_read(modeladmin, request, queryset):
ticket.mark_as_read_by(request.user) ticket.mark_as_read_by(request.user)
modeladmin.log_change(request, ticket, 'Marked as read') modeladmin.log_change(request, ticket, 'Marked as read')
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("Selected ticket has been marked as read."), _("Selected ticket has been marked as read."),
_("%i selected tickets have been marked as read.") % num, _("%i selected tickets have been marked as read.") % num,
num) num)

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin from django.contrib import admin
from django.urls import reverse from django.urls import reverse
from django.db import models from django.db import models
@ -7,7 +7,7 @@ from django.http import HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.html import format_html, strip_tags from django.utils.html import format_html, strip_tags
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from markdown import markdown from markdown import markdown
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
@ -68,7 +68,7 @@ class MessageReadOnlyInline(admin.TabularInline):
return header + content return header + content
content_html.short_description = _("Content") content_html.short_description = _("Content")
def has_add_permission(self, request): def has_add_permission(self, request, obj):
return False return False
def has_delete_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None):

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Ticket from .models import Ticket

View File

@ -2,7 +2,7 @@ from django import forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.templatetags.static import static from django.templatetags.static import static
from markdown import markdown from markdown import markdown

View File

@ -1,7 +1,7 @@
from django.conf import settings as djsettings from django.conf import settings as djsettings
from django.db import models from django.db import models
from django.db.models import query, Q from django.db.models import query, Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.contacts import settings as contacts_settings from orchestra.contrib.contacts import settings as contacts_settings
from orchestra.contrib.contacts.models import Contact from orchestra.contrib.contacts.models import Contact

View File

@ -1,7 +1,7 @@
from django.contrib import messages, admin from django.contrib import messages, admin
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext, gettext_lazy as _
from orchestra.admin.utils import admin_link from orchestra.admin.utils import admin_link
from orchestra.contrib.orchestration import Operation, helpers from orchestra.contrib.orchestration import Operation, helpers
@ -16,12 +16,12 @@ def letsencrypt(modeladmin, request, queryset):
content_error = '' content_error = ''
contentless = queryset.exclude(content__path='/').distinct() contentless = queryset.exclude(content__path='/').distinct()
if contentless: if contentless:
content_error = ungettext( content_error = ngettext(
ugettext("Selected website %s doesn't have a webapp mounted on <tt>/</tt>."), gettext("Selected website %s doesn't have a webapp mounted on <tt>/</tt>."),
ugettext("Selected websites %s don't have a webapp mounted on <tt>/</tt>."), gettext("Selected websites %s don't have a webapp mounted on <tt>/</tt>."),
len(contentless), len(contentless),
) )
content_error += ugettext("<br>Websites need a webapp (e.g. static) mounted on </tt>/</tt> " content_error += gettext("<br>Websites need a webapp (e.g. static) mounted on </tt>/</tt> "
"for let's encrypt HTTP-01 challenge to work.") "for let's encrypt HTTP-01 challenge to work.")
content_error = content_error % ', '.join((admin_link()(website) for website in contentless)) content_error = content_error % ', '.join((admin_link()(website) for website in contentless))
content_error = '<ul class="errorlist"><li>%s</li></ul>' % content_error content_error = '<ul class="errorlist"><li>%s</li></ul>' % content_error
@ -76,19 +76,19 @@ def letsencrypt(modeladmin, request, queryset):
'no_https': no_https 'no_https': no_https
} }
if errors: if errors:
msg = ungettext( msg = ngettext(
_("No lineages found for websites {name}."), _("No lineages found for websites {name}."),
_("No lineages found for {errors} websites."), _("No lineages found for {errors} websites."),
errors) errors)
messages.error(request, msg % context) messages.error(request, msg % context)
if successes: if successes:
msg = ungettext( msg = ngettext(
_("{name} website has successfully been encrypted."), _("{name} website has successfully been encrypted."),
_("{successes} websites have been successfully encrypted."), _("{successes} websites have been successfully encrypted."),
successes) successes)
messages.success(request, msg.format(**context)) messages.success(request, msg.format(**context))
if no_https: if no_https:
msg = ungettext( msg = ngettext(
_("{name} website does not have <b>HTTPS protocol</b> enabled."), _("{name} website does not have <b>HTTPS protocol</b> enabled."),
_("{no_https} websites do not have <b>HTTPS protocol</b> enabled."), _("{no_https} websites do not have <b>HTTPS protocol</b> enabled."),
no_https) no_https)
@ -99,7 +99,7 @@ def letsencrypt(modeladmin, request, queryset):
context = { context = {
'title': _("Let's encrypt!"), 'title': _("Let's encrypt!"),
'action_name': _("Encrypt"), 'action_name': _("Encrypt"),
'content_message': ugettext("You are going to request certificates for the following domains.<br>" 'content_message': gettext("You are going to request certificates for the following domains.<br>"
"This operation is safe to run multiple times, " "This operation is safe to run multiple times, "
"existing certificates will not be regenerated. " "existing certificates will not be regenerated. "
"Also notice that let's encrypt does not currently support wildcard certificates.") + content_error, "Also notice that let's encrypt does not currently support wildcard certificates.") + content_error,

View File

@ -1,6 +1,6 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from .helpers import is_valid_domain from .helpers import is_valid_domain

View File

@ -1,7 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.conf.urls import url from django.urls import re_path as url
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable, enable from orchestra.admin.actions import disable, enable
@ -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

@ -1,6 +1,6 @@
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.orchestration import ServiceController, replace
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor
@ -48,11 +48,11 @@ class MailmanVirtualDomainController(ServiceController):
def save(self, mail_list): def save(self, mail_list):
context = self.get_context(mail_list) context = self.get_context(mail_list)
#self.include_virtual_alias_domain(context) self.include_virtual_alias_domain(context)
def delete(self, mail_list): def delete(self, mail_list):
context = self.get_context(mail_list) context = self.get_context(mail_list)
#self.exclude_virtual_alias_domain(context) self.exclude_virtual_alias_domain(context)
def commit(self): def commit(self):
context = self.get_context_files() context = self.get_context_files()
@ -100,29 +100,70 @@ class MailmanController(MailmanVirtualDomainController):
aliases = ['# %(banner)s' % context] aliases = ['# %(banner)s' % context]
for suffix in self.address_suffixes: for suffix in self.address_suffixes:
context['suffix'] = suffix context['suffix'] = suffix
# Because mailman doesn't properly handle lists aliases we need two virtual aliases # Because mailman doesn't properly handle lists aliases we need virtual aliases
aliases.append("%(address_name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s@grups.pangea.org" % context)
if context['address_name'] != context['name']: if context['address_name'] != context['name']:
# And another with the original list name; Mailman generates links with it aliases.append("%(address_name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s@grups.pangea.org" % context)
aliases.append("%(name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s" % context)
return '\n'.join(aliases) return '\n'.join(aliases)
def save(self, mail_list): def save(self, mail_list):
context = self.get_context(mail_list) context = self.get_context(mail_list)
# Create list # Create list
cmd = "/opt/mailman/venv/bin/python /usr/local/admin/orchestra_mailman3/save.py %(name)s %(admin)s %(address_name)s@%(domain)s" % context cmd = "/opt/mailman/venv/bin/python /usr/local/admin/orchestra_mailman3/save.py %(name)s %(admin)s %(address_name)s@%(domain)s" % context
if not mail_list.active: if not mail_list.active:
cmd += ' --inactive' cmd += ' --inactive'
self.append(cmd) self.append(cmd)
# Custom domain
if mail_list.address:
context.update({
'aliases': self.get_virtual_aliases(context),
'num_entries': 2 if context['address_name'] != context['name'] else 1,
})
self.append(textwrap.dedent("""\
# Create list alias for custom domain
aliases='%(aliases)s'
if ! grep '\s\s*%(name)s\s*$' %(virtual_alias)s > /dev/null; then
echo "${aliases}" >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1
else
if grep -E '(%(address_name)s|%(name)s)@(%(address_domain)s|grups.pangea.org)' %(virtual_alias)s > /dev/null ; then
sed -i -e '/^.*%(name)s\(-admin\|-bounces\|-confirm\|-join\|-leave\|-owner\|-request\|-subscribe\|-unsubscribe\|@\).*$/d' \\
-e '/# .*%(name)s$/d' %(virtual_alias)s
echo "${aliases}" >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1
fi
fi """) % context
)
else:
self.append(textwrap.dedent("""\
# Cleanup possible ex-custom domain
if grep '\s\s*%(name)s\s*$' %(virtual_alias)s > /dev/null; then
#sed -i "/^.*\s%(name)s\s*$/d" %(virtual_alias)s
sed -i -e '/^.*%(name)s\(-admin\|-bounces\|-confirm\|-join\|-leave\|-owner\|-request\|-subscribe\|-unsubscribe\|@\).*$/d' \\
-e '/# .*%(name)s$/d' %(virtual_alias)s
fi""") % context
)
def delete(self, mail_list): def delete(self, mail_list):
context = self.get_context(mail_list) context = self.get_context(mail_list)
# Custom domain delete
self.append(textwrap.dedent("""\
# Cleanup possible ex-custom domain
if grep '\s\s*%(name)s\s*$' %(virtual_alias)s > /dev/null; then
sed -i -e '/^.*%(name)s\(-admin\|-bounces\|-confirm\|-join\|-leave\|-owner\|-request\|-subscribe\|-unsubscribe\|@\).*$/d' \\
-e '/# .*%(name)s$/d' %(virtual_alias)s
fi""") % context
)
# Delete list # Delete list
cmd = "/opt/mailman/venv/bin/python /usr/local/admin/orchestra_mailman3/delete.py %(name)s %(admin)s %(address_name)s@%(domain)s" % context cmd = "/opt/mailman/venv/bin/python /usr/local/admin/orchestra_mailman3/delete.py %(name)s" % context
if not mail_list.active:
cmd += ' --inactive'
self.append(cmd) self.append(cmd)
def commit(self): def commit(self):
pass pass
@ -141,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

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class HasCustomAddressListFilter(SimpleListFilter): class HasCustomAddressListFilter(SimpleListFilter):

View File

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

View File

@ -1,24 +1,13 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core.validators import validate_name 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

@ -1,6 +1,6 @@
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.forms import widgets from django.forms import widgets
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from orchestra.api.serializers import SetPasswordHyperlinkedSerializer, RelatedHyperlinkedModelSerializer from orchestra.api.serializers import SetPasswordHyperlinkedSerializer, RelatedHyperlinkedModelSerializer

View File

@ -9,32 +9,33 @@ 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."
) )
LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH', LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH',
'/var/log/mailman/post' '/var/log/mailman3/smtp'
) )
LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR', LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR',
'/var/lib/mailman' '/var/lib/mailman3'
) )
LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH', LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH',
'/etc/postfix/mailman_virtual_aliases' '/etc/postfix/mailman3_virtusertable'
) )
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH', LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
'/etc/postfix/mailman_virtual_domains' '/etc/postfix/mailman3_virtdomains'
) )

View File

@ -8,7 +8,7 @@ from django.db.models import F, Count, Value as V
from django.db.models.functions import Concat from django.db.models.functions import Concat
from django.utils.html import format_html, format_html_join from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable, enable from orchestra.admin.actions import disable, enable
@ -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

@ -4,7 +4,7 @@ import re
import textwrap import textwrap
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController from orchestra.contrib.orchestration import ServiceController
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor
@ -437,14 +437,19 @@ class DovecotMaildirDisk(ServiceMonitor):
def prepare(self): def prepare(self):
super().prepare() super().prepare()
current_date = self.current_date.strftime("%Y-%m-%d %H:%M:%S %Z") current_date = self.current_date.strftime("%Y-%m-%d %H:%M:%S %Z")
# self.append(textwrap.dedent("""\
# function monitor () {
# awk 'BEGIN { size = 0 } NR > 1 { size += $1 } END { print size }' $1 || echo 0
# }"""))
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""\
function monitor () { function monitor () {
awk 'BEGIN { size = 0 } NR > 1 { size += $1 } END { print size }' $1 || echo 0 SIZE=$(du -sb $1/Maildir/ 2> /dev/null || echo 0) && echo $SIZE | awk '{print $1}'
}""")) }"""))
def monitor(self, mailbox): def monitor(self, mailbox):
context = self.get_context(mailbox) context = self.get_context(mailbox)
self.append("echo %(object_id)s $(monitor %(maildir_path)s)" % context) # self.append("echo %(object_id)s $(monitor %(maildir_path)s)" % context)
self.append("echo %(object_id)s $(monitor %(home)s)" % context)
def get_context(self, mailbox): def get_context(self, mailbox):
context = { context = {

View File

@ -1,6 +1,8 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_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

@ -2,7 +2,7 @@ from django import forms
from django.contrib.admin import widgets from django.contrib.admin import widgets
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.forms import UserCreationForm, UserChangeForm from orchestra.forms import UserCreationForm, UserChangeForm
from orchestra.utils.python import AttrDict from orchestra.utils.python import AttrDict

View File

@ -6,7 +6,7 @@ from django.contrib.auth.hashers import make_password
from django.core.validators import RegexValidator, ValidationError from django.core.validators import RegexValidator, ValidationError
from django.db import models from django.db import models
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import validators, settings from . import validators, settings

View File

@ -3,7 +3,7 @@ import textwrap
from django.utils.functional import lazy from django.utils.functional import lazy
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting
from orchestra.core.validators import validate_name from orchestra.core.validators import validate_name

View File

@ -3,7 +3,7 @@ import os
import re import re
from django.core.validators import ValidationError, EmailValidator from django.core.validators import ValidationError, EmailValidator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.utils import paths from orchestra.utils import paths
from orchestra.utils.sys import run from orchestra.utils.sys import run

View File

@ -8,7 +8,7 @@ from django.db.models import Count
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, admin_colored, admin_date, wrap_admin_view from orchestra.admin.utils import admin_link, admin_colored, admin_date, wrap_admin_view
@ -115,7 +115,7 @@ class MessageAdmin(ExtendedModelAdmin):
display_to.short_description = _("To") display_to.short_description = _("To")
def get_urls(self): def get_urls(self):
from django.conf.urls import url from django.urls import re_path as url
urls = super().get_urls() urls = super().get_urls()
info = self.model._meta.app_label, self.model._meta.model_name info = self.model._meta.app_label, self.model._meta.model_name
urls.insert(0, urls.insert(0,

View File

@ -1,3 +1,4 @@
import smtplib import smtplib
from datetime import timedelta from datetime import timedelta
from socket import error as SocketError from socket import error as SocketError
@ -5,7 +6,6 @@ from socket import error as SocketError
from django.core.mail import get_connection from django.core.mail import get_connection
from django.db.models import Q from django.db.models import Q
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import smart_str
from orchestra.utils.sys import LockFile, OperationLocked from orchestra.utils.sys import LockFile, OperationLocked
@ -31,7 +31,7 @@ def send_message(message, connection=None, bulk=settings.MAILER_BULK_MESSAGES):
return return
error = None error = None
try: try:
connection.connection.sendmail(message.from_address, [message.to_address], smart_str(message.content)) connection.connection.sendmail(message.from_address, [message.to_address], message.content.encode())
except (SocketError, except (SocketError,
smtplib.SMTPSenderRefused, smtplib.SMTPSenderRefused,
smtplib.SMTPRecipientsRefused, smtplib.SMTPRecipientsRefused,

View File

@ -1,5 +1,5 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import settings from . import settings

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting

View File

@ -1,5 +1,5 @@
{% extends "admin/change_list.html" %} {% extends "admin/change_list.html" %}
{% load i18n admin_urls admin_static admin_list %} {% load i18n admin_urls static admin_list %}
{% block object-tools-items %} {% block object-tools-items %}

View File

@ -4,7 +4,7 @@ from django.urls import reverse
from django.db import models from django.db import models
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.actions import disable, enable from orchestra.admin.actions import disable, enable

View File

@ -1,6 +1,6 @@
from django.db import models from django.db import models
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core.validators import validate_name from orchestra.core.validators import validate_name
from orchestra.models.fields import NullableCharField from orchestra.models.fields import NullableCharField

View File

@ -4,7 +4,7 @@ from django.contrib import messages
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.shortcuts import render from django.shortcuts import render
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.admin.utils import get_object_from_url, change_url from orchestra.admin.utils import get_object_from_url, change_url
from orchestra.contrib.orchestration.helpers import message_user from orchestra.contrib.orchestration.helpers import message_user

View File

@ -1,7 +1,7 @@
from django.contrib import admin, messages from django.contrib import admin, messages
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangeViewActionsMixin from orchestra.admin import ExtendedModelAdmin, ChangeViewActionsMixin
from orchestra.admin.utils import admin_link, admin_date, admin_colored, display_mono, display_code from orchestra.admin.utils import admin_link, admin_date, admin_colored, display_mono, display_code

View File

@ -4,7 +4,7 @@ from functools import partial
from django.apps import apps from django.apps import apps
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra import plugins from orchestra import plugins

View File

@ -5,7 +5,7 @@ from django.core.mail import mail_admins
from django.urls import reverse, NoReverseMatch from django.urls import reverse, NoReverseMatch
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra import settings as orchestra_settings from orchestra import settings as orchestra_settings
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -125,7 +125,7 @@ def get_messages(logs):
async_url = get_backend_url(async_ids) async_url = get_backend_url(async_ids)
async_msg = '' async_msg = ''
if run_async: if run_async:
async_msg = ungettext( async_msg = ngettext(
_('<a href="{async_url}">{name}</a> is running on the background'), _('<a href="{async_url}">{name}</a> is running on the background'),
_('<a href="{async_url}">{run_async} backends</a> are running on the background'), _('<a href="{async_url}">{run_async} backends</a> are running on the background'),
run_async) run_async)
@ -133,7 +133,7 @@ def get_messages(logs):
if total == 1: if total == 1:
msg = _('<a href="{url}">{name}</a> has fail to execute') msg = _('<a href="{url}">{name}</a> has fail to execute')
else: else:
msg = ungettext( msg = ngettext(
_('<a href="{url}">{errors} out of {total} backends</a> has fail to execute'), _('<a href="{url}">{errors} out of {total} backends</a> has fail to execute'),
_('<a href="{url}">{errors} out of {total} backends</a> have fail to execute'), _('<a href="{url}">{errors} out of {total} backends</a> have fail to execute'),
errors) errors)
@ -147,13 +147,13 @@ def get_messages(logs):
if total == 1: if total == 1:
msg = _('<a href="{url}">{name}</a> has been executed') msg = _('<a href="{url}">{name}</a> has been executed')
else: else:
msg = ungettext( msg = ngettext(
_('<a href="{url}">{successes} out of {total} backends</a> has been executed'), _('<a href="{url}">{successes} out of {total} backends</a> has been executed'),
_('<a href="{url}">{successes} out of {total} backends</a> have been executed'), _('<a href="{url}">{successes} out of {total} backends</a> have been executed'),
successes) successes)
msg += ', ' + str(async_msg) msg += ', ' + str(async_msg)
else: else:
msg = ungettext( msg = ngettext(
_('<a href="{url}">{name}</a> has been executed'), _('<a href="{url}">{name}</a> has been executed'),
_('<a href="{url}">{total} backends</a> have been executed'), _('<a href="{url}">{total} backends</a> have been executed'),
total) total)

View File

@ -5,10 +5,10 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.module_loading import autodiscover_modules from django.utils.module_loading import autodiscover_modules
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core.validators import validate_ip_address, validate_hostname, OrValidator from orchestra.core.validators import validate_ip_address, validate_hostname, OrValidator
from orchestra.models.fields import NullableCharField, MultiSelectField from orchestra.models.fields import NullableCharField, MultiSelectField
@ -128,7 +128,7 @@ class BackendOperationQuerySet(models.QuerySet):
def create(self, **kwargs): def create(self, **kwargs):
instance = kwargs.get('instance') instance = kwargs.get('instance')
if instance and 'instance_repr' not in kwargs: if instance and 'instance_repr' not in kwargs:
kwargs['instance_repr'] = force_text(instance)[:256] kwargs['instance_repr'] = force_str(instance)[:256]
return super(BackendOperationQuerySet, self).create(**kwargs) return super(BackendOperationQuerySet, self).create(**kwargs)
@ -207,7 +207,7 @@ class Route(models.Model):
run_async = models.BooleanField(default=False, run_async = models.BooleanField(default=False,
help_text=_("Whether or not block the request/response cycle waitting this backend to " help_text=_("Whether or not block the request/response cycle waitting this backend to "
"finish its execution. Usually you want slave servers to run asynchronously.")) "finish its execution. Usually you want slave servers to run asynchronously."))
async_actions = MultiSelectField(max_length=256, blank=True, async_actions = MultiSelectField(max_length=256, blank=True, choices=[],
help_text=_("Specify individual actions to be executed asynchronoulsy.")) help_text=_("Specify individual actions to be executed asynchronoulsy."))
# method = models.CharField(_("method"), max_lenght=32, choices=method_choices, # method = models.CharField(_("method"), max_lenght=32, choices=method_choices,
# default=MethodBackend.get_default()) # default=MethodBackend.get_default())

View File

@ -1,6 +1,6 @@
from os import path from os import path
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting

View File

@ -1,14 +1,14 @@
import django.dispatch import django.dispatch
pre_action = django.dispatch.Signal()
pre_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action']) post_action = django.dispatch.Signal()
post_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action']) pre_prepare = django.dispatch.Signal()
pre_prepare = django.dispatch.Signal(providing_args=['backend']) post_prepare = django.dispatch.Signal()
post_prepare = django.dispatch.Signal(providing_args=['backend']) pre_commit = django.dispatch.Signal()
pre_commit = django.dispatch.Signal(providing_args=['backend']) post_commit = django.dispatch.Signal()
post_commit = django.dispatch.Signal(providing_args=['backend'])

View File

@ -3,7 +3,7 @@ from django.urls import reverse
from django.db import transaction from django.db import transaction
from django.utils import timezone from django.utils import timezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from django.shortcuts import render from django.shortcuts import render
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -95,7 +95,7 @@ class BillSelectedOrders(object):
url = reverse('admin:bills_bill_changelist') url = reverse('admin:bills_bill_changelist')
ids = ','.join([str(b.id) for b in bills]) ids = ','.join([str(b.id) for b in bills])
url += '?id__in=%s' % ids url += '?id__in=%s' % ids
msg = ungettext( msg = ngettext(
'<a href="{url}">One bill</a> has been created.', '<a href="{url}">One bill</a> has been created.',
'<a href="{url}">{num} bills</a> have been created.', '<a href="{url}">{num} bills</a> have been created.',
num).format(url=url, num=num) num).format(url=url, num=num)
@ -126,7 +126,7 @@ def mark_as_ignored(modeladmin, request, queryset):
order.mark_as_ignored() order.mark_as_ignored()
modeladmin.log_change(request, order, 'Marked as ignored') modeladmin.log_change(request, order, 'Marked as ignored')
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("Selected order has been marked as ignored."), _("Selected order has been marked as ignored."),
_("%i selected orders have been marked as ignored.") % num, _("%i selected orders have been marked as ignored.") % num,
num) num)
@ -140,7 +140,7 @@ def mark_as_not_ignored(modeladmin, request, queryset):
order.mark_as_not_ignored() order.mark_as_not_ignored()
modeladmin.log_change(request, order, 'Marked as not ignored') modeladmin.log_change(request, order, 'Marked as not ignored')
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("Selected order has been marked as not ignored."), _("Selected order has been marked as not ignored."),
_("%i selected orders have been marked as not ignored.") % num, _("%i selected orders have been marked as not ignored.") % num,
num) num)

View File

@ -6,7 +6,7 @@ from django.db.models import Prefetch
from django.utils import timezone from django.utils import timezone
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, admin_date, change_url from orchestra.admin.utils import admin_link, admin_date, change_url

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.bills.models import Invoice, Fee, ProForma from orchestra.contrib.bills.models import Invoice, Fee, ProForma

View File

@ -4,8 +4,8 @@ from django.apps import apps
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.db.models import Q, Prefetch, F from django.db.models import Q, Prefetch, F
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import settings from . import settings
from .models import MetricStorage from .models import MetricStorage
@ -118,7 +118,7 @@ class IgnoreOrderListFilter(SimpleListFilter):
""" Enable default selection different than All """ """ Enable default selection different than All """
for lookup, title in self.lookup_choices: for lookup, title in self.lookup_choices:
title = title._proxy____args[0] title = title._proxy____args[0]
selected = self.value() == force_text(lookup) selected = self.value() == force_str(lookup)
if not selected and title == "Not ignored" and self.value() is None: if not selected and title == "Not ignored" and self.value() is None:
selected = True selected = True
# end of workaround # end of workaround

View File

@ -2,7 +2,7 @@ from django import forms
from django.contrib.admin import widgets from django.contrib.admin import widgets
from django.utils import timezone from django.utils import timezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.forms import AdminFormMixin from orchestra.admin.forms import AdminFormMixin
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url

View File

@ -9,7 +9,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.models import queryset from orchestra.models import queryset
from orchestra.utils.python import import_class from orchestra.utils.python import import_class

View File

@ -1,5 +1,5 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n l10n staticfiles admin_urls utils orders %} {% load i18n l10n static admin_urls utils orders %}
{% block extrastyle %} {% block extrastyle %}
{{ block.super }} {{ block.super }}

View File

@ -7,7 +7,7 @@ from django.db import transaction
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation from orchestra.admin.decorators import action_with_confirmation
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -38,11 +38,11 @@ def process_transactions(modeladmin, request, queryset):
opts = modeladmin.model._meta opts = modeladmin.model._meta
num = len(queryset) num = len(queryset)
context = { context = {
'title': ungettext( 'title': ngettext(
_("One selected transaction has been processed."), _("One selected transaction has been processed."),
_("%s Selected transactions have been processed.") % num, _("%s Selected transactions have been processed.") % num,
num), num),
'content_message': ungettext( 'content_message': ngettext(
_("The following transaction process has been generated, " _("The following transaction process has been generated, "
"you may want to save it on your computer now."), "you may want to save it on your computer now."),
_("The following %s transaction processes have been generated, " _("The following %s transaction processes have been generated, "
@ -63,7 +63,7 @@ def mark_as_executed(modeladmin, request, queryset):
transaction.mark_as_executed() transaction.mark_as_executed()
modeladmin.log_change(request, transaction, _("Executed")) modeladmin.log_change(request, transaction, _("Executed"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected transaction has been marked as executed."), _("One selected transaction has been marked as executed."),
_("%s selected transactions have been marked as executed.") % num, _("%s selected transactions have been marked as executed.") % num,
num) num)
@ -79,7 +79,7 @@ def mark_as_secured(modeladmin, request, queryset):
transaction.mark_as_secured() transaction.mark_as_secured()
modeladmin.log_change(request, transaction, _("Secured")) modeladmin.log_change(request, transaction, _("Secured"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected transaction has been marked as secured."), _("One selected transaction has been marked as secured."),
_("%s selected transactions have been marked as secured.") % num, _("%s selected transactions have been marked as secured.") % num,
num) num)
@ -95,7 +95,7 @@ def mark_as_rejected(modeladmin, request, queryset):
transaction.mark_as_rejected() transaction.mark_as_rejected()
modeladmin.log_change(request, transaction, _("Rejected")) modeladmin.log_change(request, transaction, _("Rejected"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected transaction has been marked as rejected."), _("One selected transaction has been marked as rejected."),
_("%s selected transactions have been marked as rejected.") % num, _("%s selected transactions have been marked as rejected.") % num,
num) num)
@ -134,7 +134,7 @@ def mark_process_as_executed(modeladmin, request, queryset):
process.mark_as_executed() process.mark_as_executed()
modeladmin.log_change(request, process, _("Executed")) modeladmin.log_change(request, process, _("Executed"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected process has been marked as executed."), _("One selected process has been marked as executed."),
_("%s selected processes have been marked as executed.") % num, _("%s selected processes have been marked as executed.") % num,
num) num)
@ -150,7 +150,7 @@ def abort(modeladmin, request, queryset):
process.abort() process.abort()
modeladmin.log_change(request, process, _("Aborted")) modeladmin.log_change(request, process, _("Aborted"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected process has been aborted."), _("One selected process has been aborted."),
_("%s selected processes have been aborted.") % num, _("%s selected processes have been aborted.") % num,
num) num)
@ -166,7 +166,7 @@ def commit(modeladmin, request, queryset):
transaction.mark_as_rejected() transaction.mark_as_rejected()
modeladmin.log_change(request, transaction, _("Rejected")) modeladmin.log_change(request, transaction, _("Rejected"))
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("One selected transaction has been marked as rejected."), _("One selected transaction has been marked as rejected."),
_("%s selected transactions have been marked as rejected.") % num, _("%s selected transactions have been marked as rejected.") % num,
num) num)

View File

@ -3,7 +3,7 @@ from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
from orchestra.admin.utils import admin_colored, admin_link, admin_date from orchestra.admin.utils import admin_colored, admin_link, admin_date

View File

@ -1,5 +1,5 @@
from django.contrib import messages from django.contrib import messages
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from .models import Transaction from .models import Transaction
@ -29,7 +29,7 @@ def post_delete_processes(modeladmin, request, related_transactions):
transaction.save(update_fields=('state', 'modified_at')) transaction.save(update_fields=('state', 'modified_at'))
num += 1 num += 1
modeladmin.log_change(request, transaction, _("Unprocessed")) modeladmin.log_change(request, transaction, _("Unprocessed"))
messages.success(request, ungettext( messages.success(request, ngettext(
"One related transaction has been marked as <i>waitting for processing</i>", "One related transaction has been marked as <i>waitting for processing</i>",
"%i related transactions have been marked as <i>waitting for processing</i>." % num, "%i related transactions have been marked as <i>waitting for processing</i>." % num,
num num

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.utils.translation import ugettext_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 PluginDataForm from orchestra.plugins.forms import PluginDataForm

View File

@ -5,7 +5,7 @@ from io import StringIO
from django import forms from django import forms
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH
from rest_framework import serializers from rest_framework import serializers

View File

@ -1,7 +1,7 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from jsonfield import JSONField from jsonfield import JSONField
from orchestra.models.fields import PrivateFileField from orchestra.models.fields import PrivateFileField

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting

Some files were not shown because too many files have changed in this diff Show More