diff --git a/orchestra/admin/actions.py b/orchestra/admin/actions.py index 9bec0488..83cfc7c5 100644 --- a/orchestra/admin/actions.py +++ b/orchestra/admin/actions.py @@ -1,12 +1,13 @@ from functools import partial from django.contrib import admin +from django.core.exceptions import PermissionDenied from django.core.mail import send_mass_mail from django.shortcuts import render -from django.utils.translation import ngettext, gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext from .. import settings - from .decorators import action_with_confirmation from .forms import SendEmailForm @@ -18,7 +19,7 @@ class SendEmail(object): template = 'admin/orchestra/generic_confirmation.html' default_from = settings.ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL __name__ = 'semd_email' - + def __call__(self, modeladmin, request, queryset): """ make this monster behave like a function """ self.modeladmin = modeladmin @@ -34,10 +35,10 @@ class SendEmail(object): 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, } return self.write_email(request) - + def write_email(self, request): if not request.user.is_superuser: - raise PermissionDenied + raise PermissionDenied() initial={ 'email_from': self.default_from, 'to': ' '.join(self.get_email_addresses()) @@ -51,7 +52,7 @@ class SendEmail(object): 'extra_to': form.cleaned_data['extra_to'], 'subject': form.cleaned_data['subject'], 'message': form.cleaned_data['message'], - + } return self.confirm_email(request, **options) self.context.update({ @@ -62,10 +63,10 @@ class SendEmail(object): }) # Display confirmation page return render(request, self.template, self.context) - + def get_email_addresses(self): return self.queryset.values_list('email', flat=True) - + def confirm_email(self, request, **options): email_from = options['email_from'] extra_to = options['extra_to'] @@ -88,7 +89,7 @@ class SendEmail(object): ) self.modeladmin.message_user(request, msg) return None - + form = self.form(initial={ 'email_from': email_from, 'extra_to': ', '.join(extra_to), @@ -131,15 +132,19 @@ def base_disable(modeladmin, request, queryset, disable=True): modeladmin.message_user(request, msg) +@admin.action( + description=_("Disable") +) @action_with_confirmation() def disable(modeladmin, request, queryset): return base_disable(modeladmin, request, queryset) disable.url_name = 'disable' -disable.short_description = _("Disable") +@admin.action( + description=_("Enable") +) @action_with_confirmation() def enable(modeladmin, request, queryset): return base_disable(modeladmin, request, queryset, disable=False) enable.url_name = 'enable' -enable.short_description = _("Enable") diff --git a/orchestra/admin/options.py b/orchestra/admin/options.py index 3849bc20..079565a2 100644 --- a/orchestra/admin/options.py +++ b/orchestra/admin/options.py @@ -1,16 +1,16 @@ from urllib import parse from django import forms -from django.urls import re_path as url from django.contrib import admin, messages from django.contrib.admin.options import IS_POPUP_VAR from django.contrib.admin.utils import unquote from django.contrib.auth import update_session_auth_hash from django.core.exceptions import PermissionDenied -from django.http import HttpResponseRedirect, Http404, HttpResponse from django.forms.models import BaseInlineFormSet +from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.template.response import TemplateResponse +from django.urls import re_path as url from django.utils.decorators import method_decorator from django.utils.encoding import force_str from django.utils.html import escape @@ -19,14 +19,12 @@ from django.views.decorators.debug import sensitive_post_parameters from orchestra.models.utils import has_db_field -from ..utils.python import random_ascii, pairwise - +from ..utils.python import pairwise, random_ascii from .forms import AdminPasswordChangeForm #, AdminRawPasswordChangeForm #from django.contrib.auth.forms import AdminPasswordChangeForm from .utils import action_to_view - sensitive_post_parameters_m = method_decorator(sensitive_post_parameters()) @@ -37,7 +35,7 @@ class ChangeListDefaultFilter(object): default_changelist_filters = (('my_nodes', 'True'),) """ default_changelist_filters = () - + def changelist_view(self, request, extra_context=None): # defaults = [] # for key, value in self.default_changelist_filters: @@ -79,7 +77,7 @@ class EnhaceSearchMixin(object): if 'password' in lookup: return False return True - + def get_search_results(self, request, queryset, search_term): """ allows to specify field : """ search_fields = self.get_search_fields(request) @@ -109,7 +107,7 @@ class ChangeViewActionsMixin(object): """ Makes actions visible on the admin change view page. """ change_view_actions = () change_form_template = 'orchestra/admin/change_form.html' - + def get_urls(self): """Returns the additional urls for the change view links""" urls = super(ChangeViewActionsMixin, self).get_urls() @@ -124,7 +122,7 @@ class ChangeViewActionsMixin(object): ) ) return new_urls + urls - + def get_change_view_actions(self, obj=None): """ allow customization on modelamdin """ views = [] @@ -145,7 +143,7 @@ class ChangeViewActionsMixin(object): view.hidden = getattr(action, 'hidden', False) views.append(view) return views - + def change_view(self, request, object_id, **kwargs): if kwargs.get('extra_context', None) is None: kwargs['extra_context'] = {} @@ -165,21 +163,21 @@ class ChangeAddFieldsMixin(object): change_readonly_fields = () change_form = None add_inlines = None - + def get_prepopulated_fields(self, request, obj=None): if not obj: return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj) return {} - + def get_change_readonly_fields(self, request, obj=None): return self.change_readonly_fields - + def get_readonly_fields(self, request, obj=None): fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj) if obj: return fields + self.get_change_readonly_fields(request, obj) return fields - + def get_fieldsets(self, request, obj=None): if not obj: if self.add_fieldsets: @@ -187,7 +185,7 @@ class ChangeAddFieldsMixin(object): elif self.add_fields: return [(None, {'fields': self.add_fields})] return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj) - + def get_inline_instances(self, request, obj=None): """ add_inlines and inline.parent_object """ if obj: @@ -198,7 +196,7 @@ class ChangeAddFieldsMixin(object): for inline in inlines: inline.parent_object = obj return inlines - + def get_form(self, request, obj=None, **kwargs): """ Use special form during user creation """ defaults = {} @@ -218,13 +216,13 @@ class ExtendedModelAdmin(ChangeViewActionsMixin, EnhaceSearchMixin, admin.ModelAdmin): list_prefetch_related = None - + def get_queryset(self, request): qs = super(ExtendedModelAdmin, self).get_queryset(request) if self.list_prefetch_related: qs = qs.prefetch_related(*self.list_prefetch_related) return qs - + def get_object(self, request, object_id, from_field=None): obj = super(ExtendedModelAdmin, self).get_object(request, object_id, from_field) if obj is None: @@ -237,7 +235,7 @@ class ExtendedModelAdmin(ChangeViewActionsMixin, class ChangePasswordAdminMixin(object): change_password_form = AdminPasswordChangeForm change_user_password_template = 'admin/orchestra/change_password.html' - + def get_urls(self): opts = self.model._meta info = opts.app_label, opts.model_name @@ -249,14 +247,14 @@ class ChangePasswordAdminMixin(object): self.admin_site.admin_view(self.show_hash), name='%s_%s_show_hash' % info) ] + super().get_urls() - + def get_change_password_username(self, obj): return str(obj) - + @sensitive_post_parameters_m def change_password(self, request, id, form_url=''): if not self.has_change_permission(request): - raise PermissionDenied + raise PermissionDenied() # TODO use this insetad of self.get_object(), in other places obj = get_object_or_404(self.get_queryset(request), pk=id) raw = request.GET.get('raw', '0') == '1' @@ -281,7 +279,7 @@ class ChangePasswordAdminMixin(object): for rel in account.get_related_passwords(db_field=raw): if not isinstance(obj, type(rel)): related.append(rel) - + if request.method == 'POST': form = self.change_password_form(obj, request.POST, related=related, raw=raw) if form.is_valid(): @@ -293,7 +291,7 @@ class ChangePasswordAdminMixin(object): return HttpResponseRedirect('..') else: form = self.change_password_form(obj, related=related, raw=raw) - + fieldsets = [ (obj._meta.verbose_name.capitalize(), { 'classes': ('wide',), @@ -305,7 +303,7 @@ class ChangePasswordAdminMixin(object): 'classes': ('wide',), 'fields': ('password_%i' % ix,) if raw else ('password1_%i' % ix, 'password2_%i' % ix) })) - + obj_username = self.get_change_password_username(obj) adminForm = admin.helpers.AdminForm(form, fieldsets, {}) context = { @@ -331,9 +329,9 @@ class ChangePasswordAdminMixin(object): } context.update(admin.site.each_context(request)) return TemplateResponse(request, self.change_user_password_template, context) - + def show_hash(self, request, id): if not request.user.is_superuser: - raise PermissionDenied + raise PermissionDenied() obj = get_object_or_404(self.get_queryset(request), pk=id) return HttpResponse(obj.password) diff --git a/orchestra/conf/project_template/project_name/settings.py b/orchestra/conf/project_template/project_name/settings.py index 76d3f7ab..78ee4177 100644 --- a/orchestra/conf/project_template/project_name/settings.py +++ b/orchestra/conf/project_template/project_name/settings.py @@ -145,6 +145,8 @@ DATABASES = { } } +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + # Password validation # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators diff --git a/orchestra/conf/project_template/project_name/urls.py b/orchestra/conf/project_template/project_name/urls.py index 3ae27421..63f4ce63 100644 --- a/orchestra/conf/project_template/project_name/urls.py +++ b/orchestra/conf/project_template/project_name/urls.py @@ -1,6 +1,6 @@ -from django.conf.urls import include, url +from django.urls import include, path urlpatterns = [ - url(r'', include('orchestra.urls')), + path('', include('orchestra.urls')), ] diff --git a/orchestra/contrib/accounts/__init__.py b/orchestra/contrib/accounts/__init__.py index 122f30b7..e69de29b 100644 --- a/orchestra/contrib/accounts/__init__.py +++ b/orchestra/contrib/accounts/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.accounts.apps.AccountConfig' diff --git a/orchestra/contrib/accounts/admin.py b/orchestra/contrib/accounts/admin.py index 0c1e81c0..ddfc7f6d 100644 --- a/orchestra/contrib/accounts/admin.py +++ b/orchestra/contrib/accounts/admin.py @@ -29,6 +29,7 @@ from .models import Account from .filters import HasTipeServerFilter +@admin.register(Account) class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin): list_display = ('username', 'full_name', 'type', 'is_active') list_filter = ( @@ -150,7 +151,6 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin) return actions -admin.site.register(Account, AccountAdmin) class AccountListAdmin(AccountAdmin): @@ -159,6 +159,10 @@ class AccountListAdmin(AccountAdmin): actions = None change_list_template = 'admin/accounts/account/select_account_list.html' + @admin.display( + description=_("account"), + ordering='username', + ) @mark_safe def select_account(self, instance): # TODO get query string from request.META['QUERY_STRING'] to preserve filters @@ -168,8 +172,6 @@ class AccountListAdmin(AccountAdmin): 'plus': '+', } return _('%(plus)s Add to %(name)s') % context - select_account.short_description = _("account") - select_account.admin_order_field = 'username' def changelist_view(self, request, extra_context=None): app_label = request.META['PATH_INFO'].split('/')[-5] @@ -208,6 +210,10 @@ class AccountAdminMixin(object): account = None list_select_related = ('account',) + @admin.display( + description=_("active"), + ordering='is_active', + ) @mark_safe def display_active(self, instance): if not instance.is_active: @@ -216,14 +222,14 @@ class AccountAdminMixin(object): msg = _("Account disabled") return 'False' % (static('admin/img/inline-delete.svg'), msg) return 'False' % static('admin/img/icon-yes.svg') - display_active.short_description = _("active") - display_active.admin_order_field = 'is_active' + @admin.display( + description=_("account"), + ordering='account__username', + ) def account_link(self, instance): account = instance.account if instance.pk else self.account return admin_link()(account) - account_link.short_description = _("account") - account_link.admin_order_field = 'account__username' def get_form(self, request, obj=None, **kwargs): """ Warns user when object's account is disabled """ diff --git a/orchestra/contrib/bills/__init__.py b/orchestra/contrib/bills/__init__.py index c568ce60..e69de29b 100644 --- a/orchestra/contrib/bills/__init__.py +++ b/orchestra/contrib/bills/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.bills.apps.BillsConfig' diff --git a/orchestra/contrib/bills/admin.py b/orchestra/contrib/bills/admin.py index ac453dbe..8c268b32 100644 --- a/orchestra/contrib/bills/admin.py +++ b/orchestra/contrib/bills/admin.py @@ -68,6 +68,9 @@ class BillLineInline(admin.TabularInline): order_link = admin_link('order', display='pk') + @admin.display( + description=_("Total") + ) @mark_safe def display_total(self, line): if line.pk: @@ -79,7 +82,6 @@ class BillLineInline(admin.TabularInline): img = static('admin/img/icon-alert.svg') return '%s ' % (url, content, total, img) return '%s' % (url, total) - display_total.short_description = _("Total") def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ @@ -105,31 +107,38 @@ class ClosedBillLineInline(BillLineInline): readonly_fields = fields can_delete = False + @admin.display( + description=_("Description") + ) @mark_safe def display_description(self, line): descriptions = [line.description] for subline in line.sublines.all(): descriptions.append(' ' * 4 + subline.description) return '
'.join(descriptions) - display_description.short_description = _("Description") + @admin.display( + description=_("Subtotal") + ) @mark_safe def display_subtotal(self, line): subtotals = [' ' + str(line.subtotal)] for subline in line.sublines.all(): subtotals.append(str(subline.total)) return '
'.join(subtotals) - display_subtotal.short_description = _("Subtotal") + @admin.display( + description=_("Total") + ) def display_total(self, line): if line.pk: return line.compute_total() - display_total.short_description = _("Total") def has_add_permission(self, request, obj): return False +@admin.register(BillLine) class BillLineAdmin(admin.ModelAdmin): list_display = ( 'description', 'bill_link', 'display_is_open', 'account_link', 'rate', 'quantity', @@ -164,21 +173,27 @@ class BillLineAdmin(admin.ModelAdmin): order_link = admin_link('order') amended_line_link = admin_link('amended_line') + @admin.display( + description=_("Is open"), + boolean=True, + ) def display_is_open(self, instance): return instance.bill.is_open - display_is_open.short_description = _("Is open") - display_is_open.boolean = True + @admin.display( + description=_("Sublines"), + ordering='subline_total', + ) def display_sublinetotal(self, instance): total = instance.subline_total return total if total is not None else '---' - display_sublinetotal.short_description = _("Sublines") - display_sublinetotal.admin_order_field = 'subline_total' + @admin.display( + description=_("Total"), + ordering='computed_total', + ) def display_total(self, instance): return round(instance.computed_total or 0, 2) - display_total.short_description = _("Total") - display_total.admin_order_field = 'computed_total' def get_readonly_fields(self, request, obj=None): fields = super().get_readonly_fields(request, obj) @@ -242,6 +257,10 @@ class BillLineManagerAdmin(BillLineAdmin): class BillAdminMixin(AccountAdminMixin): + @admin.display( + description=_("total"), + ordering='approx_total', + ) @mark_safe def display_total_with_subtotals(self, bill): if bill.pk: @@ -252,9 +271,10 @@ class BillAdminMixin(AccountAdminMixin): subtotals.append(_("Taxes %s%% VAT %s &%s;") % (tax, subtotal[1], currency)) subtotals = '\n'.join(subtotals) return '%s &%s;' % (subtotals, bill.compute_total(), currency) - display_total_with_subtotals.short_description = _("total") - display_total_with_subtotals.admin_order_field = 'approx_total' + @admin.display( + description=_("Payment") + ) @mark_safe def display_payment_state(self, bill): if bill.pk: @@ -277,7 +297,6 @@ class BillAdminMixin(AccountAdminMixin): color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey') return '{name}'.format( url=url, color=color, name=state, title=title) - display_payment_state.short_description = _("Payment") def get_queryset(self, request): qs = super().get_queryset(request) @@ -311,6 +330,7 @@ class AmendInline(BillAdminMixin, admin.TabularInline): return False +@admin.register(AbonoInvoice, AmendmentFee, AmendmentInvoice, Bill, Fee, Invoice, ProForma) class BillAdmin(BillAdminMixin, ExtendedModelAdmin): list_display = ( 'number', 'type_link', 'account_link', 'closed_on_display', 'updated_on_display', @@ -369,23 +389,29 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin): # amend_links.short_description = _("Amends") # amend_links.allow_tags = True + @admin.display( + description=_("lines"), + ordering='lines__count', + ) def num_lines(self, bill): return bill.lines__count - num_lines.admin_order_field = 'lines__count' - num_lines.short_description = _("lines") + @admin.display( + description=_("total"), + ordering='approx_total', + ) def display_total(self, bill): currency = settings.BILLS_CURRENCY.lower() return format_html('{} &{};', bill.compute_total(), currency) - display_total.short_description = _("total") - display_total.admin_order_field = 'approx_total' + @admin.display( + description=_("type"), + ordering='type', + ) def type_link(self, bill): bill_type = bill.type.lower() url = reverse('admin:bills_%s_changelist' % bill_type) return format_html('{}', url, bill.get_type_display()) - type_link.short_description = _("type") - type_link.admin_order_field = 'type' def get_urls(self): """ Hook bill lines management URLs on bill admin """ @@ -456,14 +482,6 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin): return super().change_view(request, object_id, **kwargs) -admin.site.register(Bill, BillAdmin) -admin.site.register(Invoice, BillAdmin) -admin.site.register(AmendmentInvoice, BillAdmin) -admin.site.register(AbonoInvoice, BillAdmin) -admin.site.register(Fee, BillAdmin) -admin.site.register(AmendmentFee, BillAdmin) -admin.site.register(ProForma, BillAdmin) -admin.site.register(BillLine, BillLineAdmin) class BillContactInline(admin.StackedInline): @@ -481,10 +499,12 @@ class BillContactInline(admin.StackedInline): return super().formfield_for_dbfield(db_field, **kwargs) +@admin.display( + boolean=True, + ordering='billcontact', +) def has_bill_contact(account): return hasattr(account, 'billcontact') -has_bill_contact.boolean = True -has_bill_contact.admin_order_field = 'billcontact' insertattr(AccountAdmin, 'inlines', BillContactInline) diff --git a/orchestra/contrib/bills/api.py b/orchestra/contrib/bills/api.py index 7d050b4c..30ab7c39 100644 --- a/orchestra/contrib/bills/api.py +++ b/orchestra/contrib/bills/api.py @@ -18,7 +18,7 @@ class BillViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet): @action(detail=True, methods=['get']) def document(self, request, pk): bill = self.get_object() - content_type = request.META.get('HTTP_ACCEPT') + content_type = request.headers.get('accept') if content_type == 'application/pdf': pdf = html_to_pdf(bill.html or bill.render()) return HttpResponse(pdf, content_type='application/pdf') diff --git a/orchestra/contrib/contacts/__init__.py b/orchestra/contrib/contacts/__init__.py index 3af15748..e69de29b 100644 --- a/orchestra/contrib/contacts/__init__.py +++ b/orchestra/contrib/contacts/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.contacts.apps.ContactsConfig' diff --git a/orchestra/contrib/contacts/admin.py b/orchestra/contrib/contacts/admin.py index c5c76471..3adad4c7 100644 --- a/orchestra/contrib/contacts/admin.py +++ b/orchestra/contrib/contacts/admin.py @@ -13,6 +13,7 @@ from .filters import EmailUsageListFilter from .models import Contact +@admin.register(Contact) class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin): list_display = ( 'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link' @@ -62,10 +63,12 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin): ) actions = (SendEmail(), list_accounts) + @admin.display( + description=_("Name"), + ordering='short_name', + ) def dispaly_name(self, contact): return str(contact) - dispaly_name.short_description = _("Name") - dispaly_name.admin_order_field = 'short_name' def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ @@ -76,7 +79,6 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin): return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs) -admin.site.register(Contact, ContactAdmin) class ContactInline(admin.StackedInline): diff --git a/orchestra/contrib/databases/__init__.py b/orchestra/contrib/databases/__init__.py index f21f8dda..e69de29b 100644 --- a/orchestra/contrib/databases/__init__.py +++ b/orchestra/contrib/databases/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.databases.apps.DatabasesConfig' diff --git a/orchestra/contrib/databases/admin.py b/orchestra/contrib/databases/admin.py index 4a18d6df..e9c298d7 100644 --- a/orchestra/contrib/databases/admin.py +++ b/orchestra/contrib/databases/admin.py @@ -14,11 +14,14 @@ from .filters import HasUserListFilter, HasDatabaseListFilter from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm, DatabaseForm from .models import Database, DatabaseUser +@admin.action( + description="Re-save selected objects" +) def save_selected(modeladmin, request, queryset): for selected in queryset: selected.save() -save_selected.short_description = "Re-save selected objects" +@admin.register(Database) class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ('name', 'type', 'target_server', 'display_users', 'account_link') list_filter = ('type', HasUserListFilter) @@ -51,6 +54,10 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): filter_horizontal = ['users'] actions = (list_accounts, save_selected) + @admin.display( + description=_("Users"), + ordering='users__username', + ) @mark_safe def display_users(self, db): links = [] @@ -58,8 +65,6 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): link = format_html('{}', change_url(user), user.username) links.append(link) return '
'.join(links) - display_users.short_description = _("Users") - display_users.admin_order_field = 'users__username' def save_model(self, request, obj, form, change): super(DatabaseAdmin, self).save_model(request, obj, form, change) @@ -77,6 +82,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): obj.users.add(user) +@admin.register(DatabaseUser) class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin): list_display = ('username', 'target_server', 'type', 'display_databases', 'account_link') list_filter = ('type', HasDatabaseListFilter) @@ -101,6 +107,10 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten list_prefetch_related = ('databases',) actions = (list_accounts, save_selected) + @admin.display( + description=_("Databases"), + ordering='databases__name', + ) @mark_safe def display_databases(self, user): links = [] @@ -108,8 +118,6 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten link = format_html('{}', change_url(db), db.name) links.append(link) return '
'.join(links) - display_databases.short_description = _("Databases") - display_databases.admin_order_field = 'databases__name' def get_urls(self): useradmin = UserAdmin(DatabaseUser, self.admin_site) @@ -125,5 +133,3 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten super(DatabaseUserAdmin, self).save_model(request, obj, form, change) -admin.site.register(Database, DatabaseAdmin) -admin.site.register(DatabaseUser, DatabaseUserAdmin) diff --git a/orchestra/contrib/domains/__init__.py b/orchestra/contrib/domains/__init__.py index 5c85353e..e69de29b 100644 --- a/orchestra/contrib/domains/__init__.py +++ b/orchestra/contrib/domains/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.domains.apps.DomainsConfig' diff --git a/orchestra/contrib/domains/admin.py b/orchestra/contrib/domains/admin.py index 10994cf4..a95fb10e 100644 --- a/orchestra/contrib/domains/admin.py +++ b/orchestra/contrib/domains/admin.py @@ -39,9 +39,11 @@ class DomainInline(admin.TabularInline): domain_link.short_description = _("Name") account_link = admin_link('account') + @admin.display( + description=_("Declared records") + ) def display_records(self, domain): return ', '.join([record.type for record in domain.records.all()]) - display_records.short_description = _("Declared records") def has_add_permission(self, *args, **kwargs): return False @@ -52,6 +54,7 @@ class DomainInline(admin.TabularInline): return qs.select_related('account').prefetch_related('records') +@admin.register(Domain) class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): list_display = ( 'structured_name', 'display_is_top', 'display_websites', 'display_addresses', 'account_link' @@ -71,19 +74,27 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): top_link = admin_link('top') + @admin.display( + description=_("name"), + ordering='structured_name', + ) def structured_name(self, domain): if domain.is_top: return domain.name return mark_safe(' '*4 + domain.name) - structured_name.short_description = _("name") - structured_name.admin_order_field = 'structured_name' + @admin.display( + description=_("Is top"), + boolean=True, + ordering='top', + ) def display_is_top(self, domain): return domain.is_top - display_is_top.short_description = _("Is top") - display_is_top.boolean = True - display_is_top.admin_order_field = 'top' + @admin.display( + description=_("Websites"), + ordering='websites__name', + ) @mark_safe def display_websites(self, domain): if apps.isinstalled('orchestra.contrib.websites'): @@ -106,9 +117,11 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): ) return _("No website %s") % (add_link) return '---' - display_websites.admin_order_field = 'websites__name' - display_websites.short_description = _("Websites") + @admin.display( + description=_("Addresses"), + ordering='addresses__count', + ) @mark_safe def display_addresses(self, domain): if apps.isinstalled('orchestra.contrib.mailboxes'): @@ -126,9 +139,10 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): return '%s %s' % (url, title, len(addresses), add_link) return _("No address %s") % (add_link) return '---' - display_addresses.short_description = _("Addresses") - display_addresses.admin_order_field = 'addresses__count' + @admin.display( + description=_("Implicit records") + ) @mark_safe def implicit_records(self, domain): types = set(domain.records.values_list('type', flat=True)) @@ -148,7 +162,6 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): else: lines.append(line) return '
'.join(lines) - implicit_records.short_description = _("Implicit records") def get_fieldsets(self, request, obj=None): """ Add SOA fields when domain is top """ @@ -224,4 +237,3 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): self.save_formset(request, form, formset, change) -admin.site.register(Domain, DomainAdmin) diff --git a/orchestra/contrib/history/__init__.py b/orchestra/contrib/history/__init__.py index d39042d6..e69de29b 100644 --- a/orchestra/contrib/history/__init__.py +++ b/orchestra/contrib/history/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.history.apps.HistoryConfig' diff --git a/orchestra/contrib/history/admin.py b/orchestra/contrib/history/admin.py index 903eeda0..a738c2d9 100644 --- a/orchestra/contrib/history/admin.py +++ b/orchestra/contrib/history/admin.py @@ -36,6 +36,10 @@ class LogEntryAdmin(admin.ModelAdmin): user_link = admin_link('user') display_action_time = admin_date('action_time', short_description=_("Time")) + @admin.display( + description=_("Message"), + ordering='action_flag', + ) @mark_safe def display_message(self, log): edit = format_html('', **{ @@ -58,18 +62,22 @@ class LogEntryAdmin(admin.ModelAdmin): 'object': log.object_repr, 'edit': edit, } - display_message.short_description = _("Message") - display_message.admin_order_field = 'action_flag' + @admin.display( + description=_("Action"), + ordering='action_flag', + ) def display_action(self, log): if log.is_addition(): return _("Added") elif log.is_change(): return _("Changed") return _("Deleted") - display_action.short_description = _("Action") - display_action.admin_order_field = 'action_flag' + @admin.display( + description=_("Content object"), + ordering='object_repr', + ) def content_object_link(self, log): ct = log.content_type view = 'admin:%s_%s_change' % (ct.app_label, ct.model) @@ -78,8 +86,6 @@ class LogEntryAdmin(admin.ModelAdmin): except NoReverseMatch: return log.object_repr return format_html('{}', url, log.object_repr) - content_object_link.short_description = _("Content object") - content_object_link.admin_order_field = 'object_repr' def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): """ Add rel_opts and object to context """ diff --git a/orchestra/contrib/issues/__init__.py b/orchestra/contrib/issues/__init__.py index 650ba7fe..e69de29b 100644 --- a/orchestra/contrib/issues/__init__.py +++ b/orchestra/contrib/issues/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.issues.apps.IssuesConfig' diff --git a/orchestra/contrib/issues/admin.py b/orchestra/contrib/issues/admin.py index 25ae3f21..ca42d623 100644 --- a/orchestra/contrib/issues/admin.py +++ b/orchestra/contrib/issues/admin.py @@ -51,6 +51,9 @@ class MessageReadOnlyInline(admin.TabularInline): 'all': ('orchestra/css/hide-inline-id.css',) } + @admin.display( + description=_("Content") + ) @mark_safe def content_html(self, msg): context = { @@ -66,7 +69,6 @@ class MessageReadOnlyInline(admin.TabularInline): content = '
%s
' % content return header + content - content_html.short_description = _("Content") def has_add_permission(self, request, obj): return False @@ -114,12 +116,15 @@ class TicketInline(admin.TabularInline): colored_state = admin_colored('state', colors=STATE_COLORS, bold=False) colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False) + @admin.display( + description='#' + ) @mark_safe def ticket_id(self, instance): return '%s' % admin_link()(instance) - ticket_id.short_description = '#' +@admin.register(Ticket) class TicketAdmin(ExtendedModelAdmin): list_display = ( 'unbold_id', 'bold_subject', 'display_creator', 'display_owner', @@ -195,6 +200,9 @@ class TicketAdmin(ExtendedModelAdmin): display_state = admin_colored('state', colors=STATE_COLORS, bold=False) display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False) + @admin.display( + description='Summary' + ) @mark_safe def display_summary(self, ticket): context = { @@ -210,23 +218,26 @@ class TicketAdmin(ExtendedModelAdmin): }) context['updated'] = '. Updated by %(updater)s about %(updated)s' % context return '

Added by %(creator)s about %(created)s%(updated)s

' % context - display_summary.short_description = 'Summary' + @admin.display( + description="#", + ordering='id', + ) def unbold_id(self, ticket): """ Unbold id if ticket is read """ if ticket.is_read_by(self.user): return format_html('{}', ticket.pk) return ticket.pk - unbold_id.short_description = "#" - unbold_id.admin_order_field = 'id' + @admin.display( + description=_("Subject"), + ordering='subject', + ) def bold_subject(self, ticket): """ Bold subject when tickets are unread for request.user """ if ticket.is_read_by(self.user): return ticket.subject return format_html("{}", ticket.subject) - bold_subject.short_description = _("Subject") - bold_subject.admin_order_field = 'subject' def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ @@ -283,6 +294,7 @@ class TicketAdmin(ExtendedModelAdmin): return HttpResponse(data_formated) +@admin.register(Queue) class QueueAdmin(admin.ModelAdmin): list_display = ('name', 'default', 'num_tickets') actions = (set_default_queue,) @@ -294,13 +306,15 @@ class QueueAdmin(admin.ModelAdmin): 'all': ('orchestra/css/hide-inline-id.css',) } + @admin.display( + description=_("Tickets"), + ordering='tickets__count', + ) def num_tickets(self, queue): num = queue.tickets__count url = reverse('admin:issues_ticket_changelist') url += '?queue=%i' % queue.pk return format_html('{}', url, num) - num_tickets.short_description = _("Tickets") - num_tickets.admin_order_field = 'tickets__count' def get_list_display(self, request): """ show notifications """ @@ -319,5 +333,3 @@ class QueueAdmin(admin.ModelAdmin): return qs -admin.site.register(Ticket, TicketAdmin) -admin.site.register(Queue, QueueAdmin) diff --git a/orchestra/contrib/letsencrypt/actions.py b/orchestra/contrib/letsencrypt/actions.py index 375933ac..ebbc5bfe 100644 --- a/orchestra/contrib/letsencrypt/actions.py +++ b/orchestra/contrib/letsencrypt/actions.py @@ -10,6 +10,9 @@ from .helpers import is_valid_domain, read_live_lineages, configure_cert from .forms import LetsEncryptForm +@admin.action( + description="Let's encrypt!" +) def letsencrypt(modeladmin, request, queryset): wildcards = set() domains = set() @@ -112,4 +115,3 @@ def letsencrypt(modeladmin, request, queryset): 'form': form, } return TemplateResponse(request, 'admin/orchestra/generic_confirmation.html', context) -letsencrypt.short_description = "Let's encrypt!" diff --git a/orchestra/contrib/lists/__init__.py b/orchestra/contrib/lists/__init__.py index 413f2e03..e69de29b 100644 --- a/orchestra/contrib/lists/__init__.py +++ b/orchestra/contrib/lists/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.lists.apps.ListsConfig' diff --git a/orchestra/contrib/lists/admin.py b/orchestra/contrib/lists/admin.py index fc6255f9..4585527f 100644 --- a/orchestra/contrib/lists/admin.py +++ b/orchestra/contrib/lists/admin.py @@ -16,6 +16,7 @@ from .filters import HasCustomAddressListFilter from .models import List +@admin.register(List) class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'name', 'address_name', 'address_domain_link', 'account_link', 'display_active' @@ -56,4 +57,3 @@ class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): address_domain_link = admin_link('address_domain', order='address_domain__name') -admin.site.register(List, ListAdmin) diff --git a/orchestra/contrib/mailboxes/__init__.py b/orchestra/contrib/mailboxes/__init__.py index dbf89749..e69de29b 100644 --- a/orchestra/contrib/mailboxes/__init__.py +++ b/orchestra/contrib/mailboxes/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.mailboxes.apps.MailboxesConfig' diff --git a/orchestra/contrib/mailboxes/admin.py b/orchestra/contrib/mailboxes/admin.py index 5315a86c..0d59672f 100644 --- a/orchestra/contrib/mailboxes/admin.py +++ b/orchestra/contrib/mailboxes/admin.py @@ -83,6 +83,9 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo if settings.MAILBOXES_LOCAL_DOMAIN: type(self).actions = self.actions + (SendMailboxEmail(),) + @admin.display( + description=_("Addresses") + ) @mark_safe def display_addresses(self, mailbox): # Get from forwards @@ -111,21 +114,24 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo url = change_url(addr) addresses.append(format_html('{}', url, addr.email)) return '
'.join(addresses+forwards) - display_addresses.short_description = _("Addresses") + @admin.display( + description=_("Forward from") + ) def display_forwards(self, mailbox): forwards = mailbox.get_forwards() return format_html_join( '
', '{}', [(change_url(addr), addr.email) for addr in forwards] ) - display_forwards.short_description = _("Forward from") + @admin.display( + description=_("Filtering"), + ordering='filtering', + ) @mark_safe def display_filtering(self, mailbox): return mailbox.get_filtering_display() - display_filtering.short_description = _("Filtering") - display_filtering.admin_order_field = 'filtering' def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name == 'filtering': @@ -221,6 +227,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo obj.addresses.set(form.cleaned_data['addresses']) +@admin.register(Address) class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'display_email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward', @@ -241,33 +248,45 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): domain_link = admin_link('domain', order='domain__name') + @admin.display( + description=_("Email"), + ordering='computed_email', + ) def display_email(self, address): return address.computed_email - display_email.short_description = _("Email") - display_email.admin_order_field = 'computed_email' + @admin.display( + description=_("Email") + ) def email_link(self, address): link = self.domain_link(address) return format_html("{}@{}", address.name, link) - email_link.short_description = _("Email") + @admin.display( + description=_("Mailboxes"), + ordering='mailboxes__count', + ) def display_mailboxes(self, address): boxes = address.mailboxes.all() return format_html_join( mark_safe('
'), '{}', [(change_url(mailbox), mailbox.name) for mailbox in boxes] ) - display_mailboxes.short_description = _("Mailboxes") - display_mailboxes.admin_order_field = 'mailboxes__count' + @admin.display( + description=_("Mailboxes links") + ) def display_all_mailboxes(self, address): boxes = address.get_mailboxes() return format_html_join( mark_safe('
'), '{}', [(change_url(mailbox), mailbox.name) for mailbox in boxes] ) - display_all_mailboxes.short_description = _("Mailboxes links") + @admin.display( + description=_("Forward"), + ordering='forward', + ) @mark_safe def display_forward(self, address): forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()} @@ -279,8 +298,6 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): else: values.append(forward) return '
'.join(values) - display_forward.short_description = _("Forward") - display_forward.admin_order_field = 'forward' def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name == 'forward': @@ -326,4 +343,3 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): admin.site.register(Mailbox, MailboxAdmin) -admin.site.register(Address, AddressAdmin) diff --git a/orchestra/contrib/mailer/__init__.py b/orchestra/contrib/mailer/__init__.py index 335e52d3..e69de29b 100644 --- a/orchestra/contrib/mailer/__init__.py +++ b/orchestra/contrib/mailer/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.mailer.apps.MailerConfig' diff --git a/orchestra/contrib/mailer/admin.py b/orchestra/contrib/mailer/admin.py index 9d508c11..84e10ef7 100644 --- a/orchestra/contrib/mailer/admin.py +++ b/orchestra/contrib/mailer/admin.py @@ -29,6 +29,7 @@ COLORS = { } +@admin.register(Message) class MessageAdmin(ExtendedModelAdmin): list_display = ( 'display_subject', 'colored_state', 'priority', 'to_address', 'from_address', @@ -59,14 +60,20 @@ class MessageAdmin(ExtendedModelAdmin): created_at_delta = admin_date('created_at') last_try_delta = admin_date('last_try') + @admin.display( + description=_("Subject"), + ordering='subject', + ) def display_subject(self, instance): subject = instance.subject if len(subject) > 64: return mark_safe(subject[:64] + '…') return subject - display_subject.short_description = _("Subject") - display_subject.admin_order_field = 'subject' + @admin.display( + description=_("Retries"), + ordering='retries', + ) def display_retries(self, instance): num_logs = instance.logs__count if num_logs == 1: @@ -76,9 +83,10 @@ class MessageAdmin(ExtendedModelAdmin): url = reverse('admin:mailer_smtplog_changelist') url += '?&message=%i' % instance.pk return format_html('{}', url, instance.retries) - display_retries.short_description = _("Retries") - display_retries.admin_order_field = 'retries' + @admin.display( + description=_("Content") + ) def display_content(self, instance): part = email.message_from_string(instance.content) payload = part.get_payload() @@ -100,19 +108,24 @@ class MessageAdmin(ExtendedModelAdmin): if part.get_content_type() == 'text/plain': payload = payload.replace('\n', '
').replace(' ', ' ') return mark_safe(payload) - display_content.short_description = _("Content") + @admin.display( + description=_("Subject") + ) def display_full_subject(self, instance): return instance.subject - display_full_subject.short_description = _("Subject") + @admin.display( + description=_("From") + ) def display_from(self, instance): return instance.from_address - display_from.short_description = _("From") + @admin.display( + description=_("To") + ) def display_to(self, instance): return instance.to_address - display_to.short_description = _("To") def get_urls(self): from django.urls import re_path as url @@ -140,6 +153,7 @@ class MessageAdmin(ExtendedModelAdmin): return super().formfield_for_dbfield(db_field, **kwargs) +@admin.register(SMTPLog) class SMTPLogAdmin(admin.ModelAdmin): list_display = ( 'id', 'message_link', 'colored_result', 'date_delta', 'log_message' @@ -153,5 +167,3 @@ class SMTPLogAdmin(admin.ModelAdmin): date_delta = admin_date('date') -admin.site.register(Message, MessageAdmin) -admin.site.register(SMTPLog, SMTPLogAdmin) diff --git a/orchestra/contrib/miscellaneous/__init__.py b/orchestra/contrib/miscellaneous/__init__.py index 6294909f..e69de29b 100644 --- a/orchestra/contrib/miscellaneous/__init__.py +++ b/orchestra/contrib/miscellaneous/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.miscellaneous.apps.MiscellaneousConfig' diff --git a/orchestra/contrib/miscellaneous/admin.py b/orchestra/contrib/miscellaneous/admin.py index 2fce1981..56fc3daa 100644 --- a/orchestra/contrib/miscellaneous/admin.py +++ b/orchestra/contrib/miscellaneous/admin.py @@ -25,6 +25,7 @@ class MiscServicePlugin(PluginModelAdapter): plugin_field = 'service' +@admin.register(MiscService) class MiscServiceAdmin(ExtendedModelAdmin): list_display = ( 'display_name', 'display_verbose_name', 'num_instances', 'has_identifier', 'has_amount', 'is_active' @@ -38,24 +39,30 @@ class MiscServiceAdmin(ExtendedModelAdmin): change_readonly_fields = ('name',) actions = (disable, enable) + @admin.display( + description=_("name"), + ordering='name', + ) def display_name(self, misc): return format_html('{}', misc.description, misc.name) - display_name.short_description = _("name") - display_name.admin_order_field = 'name' + @admin.display( + description=_("verbose name"), + ordering='verbose_name', + ) def display_verbose_name(self, misc): return format_html('{}', misc.description, misc.verbose_name) - display_verbose_name.short_description = _("verbose name") - display_verbose_name.admin_order_field = 'verbose_name' + @admin.display( + description=_("Instances"), + ordering='instances__count', + ) def num_instances(self, misc): """ return num slivers as a link to slivers changelist view """ num = misc.instances__count url = reverse('admin:miscellaneous_miscellaneous_changelist') url += '?service__name={}'.format(misc.name) return mark_safe('{1}'.format(url, num)) - num_instances.short_description = _("Instances") - num_instances.admin_order_field = 'instances__count' def get_queryset(self, request): qs = super(MiscServiceAdmin, self).get_queryset(request) @@ -68,6 +75,7 @@ class MiscServiceAdmin(ExtendedModelAdmin): return super(MiscServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs) +@admin.register(Miscellaneous) class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ( '__str__', 'service_link', 'amount', 'account_link', 'dispaly_active' @@ -85,11 +93,13 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode service_link = admin_link('service') + @admin.display( + description=_("Active"), + boolean=True, + ordering='is_active', + ) def dispaly_active(self, instance): return instance.active - dispaly_active.short_description = _("Active") - dispaly_active.boolean = True - dispaly_active.admin_order_field = 'is_active' def get_service(self, obj): if obj is None: @@ -146,5 +156,3 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode obj.save() -admin.site.register(MiscService, MiscServiceAdmin) -admin.site.register(Miscellaneous, MiscellaneousAdmin) diff --git a/orchestra/contrib/musician/__init__.py b/orchestra/contrib/musician/__init__.py index b4562ad6..e69de29b 100644 --- a/orchestra/contrib/musician/__init__.py +++ b/orchestra/contrib/musician/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.musician.apps.MusicianConfig' diff --git a/orchestra/contrib/musician/locale/ca/LC_MESSAGES/django.po b/orchestra/contrib/musician/locale/ca/LC_MESSAGES/django.po index d41d6697..dc21cc00 100644 --- a/orchestra/contrib/musician/locale/ca/LC_MESSAGES/django.po +++ b/orchestra/contrib/musician/locale/ca/LC_MESSAGES/django.po @@ -160,25 +160,25 @@ msgstr "Cancel·lar" msgid "Save" msgstr "Desar" -#: templates/musician/addresses.html:15 +#: templates/musician/address_list.html:15 msgid "Email" msgstr "Correu electrònic" -#: templates/musician/addresses.html:16 +#: templates/musician/address_list.html:16 msgid "Domain" msgstr "Domini" #. Translators: This message appears on the page title -#: templates/musician/addresses.html:17 templates/musician/mail_base.html:22 +#: templates/musician/address_list.html:17 templates/musician/mail_base.html:22 #: views.py:355 msgid "Mailboxes" msgstr "Bústies de correu" -#: templates/musician/addresses.html:18 +#: templates/musician/address_list.html:18 msgid "Forward" msgstr "Redirecció" -#: templates/musician/addresses.html:38 +#: templates/musician/address_list.html:38 msgid "New mail address" msgstr "Nova adreça de correu" @@ -350,7 +350,7 @@ msgstr "Obre el gestor de bases de dades" #. Translators: database page when there isn't any database. #. Translators: saas page when there isn't any saas. -#: templates/musician/databases.html:58 templates/musician/saas.html:49 +#: templates/musician/databases.html:58 templates/musician/saas_list.html:49 msgid "Ooops! Looks like there is nothing here!" msgstr "Mmmh, sembla que aquí no hi ha res!" @@ -370,15 +370,15 @@ msgstr "Consulta aquí la teva configuració DNS." msgid "Value" msgstr "Valor" -#: templates/musician/mail_base.html:6 templates/musician/mailinglists.html:6 +#: templates/musician/mail_base.html:6 templates/musician/mailinglist_list.html:6 msgid "Go to global" msgstr "Totes les adreces" -#: templates/musician/mail_base.html:10 templates/musician/mailinglists.html:9 +#: templates/musician/mail_base.html:10 templates/musician/mailinglist_list.html:9 msgid "for" msgstr "per a" -#: templates/musician/mail_base.html:18 templates/musician/mailboxes.html:16 +#: templates/musician/mail_base.html:18 templates/musician/mailbox_list.html:16 msgid "Addresses" msgstr "Adreces de correu" @@ -415,27 +415,27 @@ msgstr "" msgid "Close" msgstr "Tancar" -#: templates/musician/mailboxes.html:14 +#: templates/musician/mailbox_list.html:14 msgid "Name" msgstr "Nom" -#: templates/musician/mailboxes.html:15 +#: templates/musician/mailbox_list.html:15 msgid "Filtering" msgstr "Filtrat" -#: templates/musician/mailboxes.html:27 +#: templates/musician/mailbox_list.html:27 msgid "Update password" msgstr "Actualitza la contrasenya" -#: templates/musician/mailboxes.html:43 +#: templates/musician/mailbox_list.html:43 msgid "New mailbox" msgstr "Nova bústia de correu" -#: templates/musician/mailinglists.html:34 +#: templates/musician/mailinglist_list.html:34 msgid "Active" msgstr "Actiu" -#: templates/musician/mailinglists.html:36 +#: templates/musician/mailinglist_list.html:36 msgid "Inactive" msgstr "Inactiu" @@ -463,19 +463,19 @@ msgstr "mètode de pagament:" msgid "Check your last bills" msgstr "Consulta les teves darreres factures" -#: templates/musician/saas.html:18 +#: templates/musician/saas_list.html:18 msgid "Installed on" msgstr "Instal·lat a" -#: templates/musician/saas.html:29 +#: templates/musician/saas_list.html:29 msgid "Service info" msgstr "Informació del servei" -#: templates/musician/saas.html:30 +#: templates/musician/saas_list.html:30 msgid "active" msgstr "actiu" -#: templates/musician/saas.html:37 +#: templates/musician/saas_list.html:37 msgid "Open service admin panel" msgstr "Obre el panell d’administració del servei" diff --git a/orchestra/contrib/musician/locale/es/LC_MESSAGES/django.po b/orchestra/contrib/musician/locale/es/LC_MESSAGES/django.po index 9d8c7960..8f70e1c9 100644 --- a/orchestra/contrib/musician/locale/es/LC_MESSAGES/django.po +++ b/orchestra/contrib/musician/locale/es/LC_MESSAGES/django.po @@ -162,25 +162,25 @@ msgstr "Cancelar" msgid "Save" msgstr "Guardar" -#: templates/musician/addresses.html:15 +#: templates/musician/address_list.html:15 msgid "Email" msgstr "Correo electrónico" -#: templates/musician/addresses.html:16 +#: templates/musician/address_list.html:16 msgid "Domain" msgstr "Dominio" #. Translators: This message appears on the page title -#: templates/musician/addresses.html:17 templates/musician/mail_base.html:22 +#: templates/musician/address_list.html:17 templates/musician/mail_base.html:22 #: views.py:355 msgid "Mailboxes" msgstr "Buzones de correo" -#: templates/musician/addresses.html:18 +#: templates/musician/address_list.html:18 msgid "Forward" msgstr "Redirección" -#: templates/musician/addresses.html:38 +#: templates/musician/address_list.html:38 msgid "New mail address" msgstr "Nueva dirección de correo" @@ -352,7 +352,7 @@ msgstr "Abre el gestor de bases de datos" #. Translators: database page when there isn't any database. #. Translators: saas page when there isn't any saas. -#: templates/musician/databases.html:58 templates/musician/saas.html:49 +#: templates/musician/databases.html:58 templates/musician/saas_list.html:49 msgid "Ooops! Looks like there is nothing here!" msgstr "Mmmh… ¡parece que aquí no hay nada!" @@ -372,15 +372,15 @@ msgstr "Consulta aquí tu configuración DNS." msgid "Value" msgstr "Valor" -#: templates/musician/mail_base.html:6 templates/musician/mailinglists.html:6 +#: templates/musician/mail_base.html:6 templates/musician/mailinglist_list.html:6 msgid "Go to global" msgstr "Todas las direcciones" -#: templates/musician/mail_base.html:10 templates/musician/mailinglists.html:9 +#: templates/musician/mail_base.html:10 templates/musician/mailinglist_list.html:9 msgid "for" msgstr "para" -#: templates/musician/mail_base.html:18 templates/musician/mailboxes.html:16 +#: templates/musician/mail_base.html:18 templates/musician/mailbox_list.html:16 msgid "Addresses" msgstr "Direcciones de correo" @@ -417,27 +417,27 @@ msgstr "" msgid "Close" msgstr "Cerrar" -#: templates/musician/mailboxes.html:14 +#: templates/musician/mailbox_list.html:14 msgid "Name" msgstr "Nombre" -#: templates/musician/mailboxes.html:15 +#: templates/musician/mailbox_list.html:15 msgid "Filtering" msgstr "Filtrado" -#: templates/musician/mailboxes.html:27 +#: templates/musician/mailbox_list.html:27 msgid "Update password" msgstr "Actualiza la contraseña" -#: templates/musician/mailboxes.html:43 +#: templates/musician/mailbox_list.html:43 msgid "New mailbox" msgstr "Nuevo buzón de correo" -#: templates/musician/mailinglists.html:34 +#: templates/musician/mailinglist_list.html:34 msgid "Active" msgstr "Activo" -#: templates/musician/mailinglists.html:36 +#: templates/musician/mailinglist_list.html:36 msgid "Inactive" msgstr "Inactivo" @@ -465,19 +465,19 @@ msgstr "método de pago:" msgid "Check your last bills" msgstr "Consulta tus últimas facturas" -#: templates/musician/saas.html:18 +#: templates/musician/saas_list.html:18 msgid "Installed on" msgstr "Instalado en" -#: templates/musician/saas.html:29 +#: templates/musician/saas_list.html:29 msgid "Service info" msgstr "Información del servicio" -#: templates/musician/saas.html:30 +#: templates/musician/saas_list.html:30 msgid "active" msgstr "activo" -#: templates/musician/saas.html:37 +#: templates/musician/saas_list.html:37 msgid "Open service admin panel" msgstr "Abre el panel de administración del servicio" diff --git a/orchestra/contrib/musician/templates/musician/addresses.html b/orchestra/contrib/musician/templates/musician/address_list.html similarity index 100% rename from orchestra/contrib/musician/templates/musician/addresses.html rename to orchestra/contrib/musician/templates/musician/address_list.html diff --git a/orchestra/contrib/musician/templates/musician/mailboxes.html b/orchestra/contrib/musician/templates/musician/mailbox_list.html similarity index 100% rename from orchestra/contrib/musician/templates/musician/mailboxes.html rename to orchestra/contrib/musician/templates/musician/mailbox_list.html diff --git a/orchestra/contrib/musician/templates/musician/mailinglists.html b/orchestra/contrib/musician/templates/musician/mailinglist_list.html similarity index 100% rename from orchestra/contrib/musician/templates/musician/mailinglists.html rename to orchestra/contrib/musician/templates/musician/mailinglist_list.html diff --git a/orchestra/contrib/musician/templates/musician/record_confirm_delete.html b/orchestra/contrib/musician/templates/musician/record_check_delete.html similarity index 100% rename from orchestra/contrib/musician/templates/musician/record_confirm_delete.html rename to orchestra/contrib/musician/templates/musician/record_check_delete.html diff --git a/orchestra/contrib/musician/templates/musician/saas.html b/orchestra/contrib/musician/templates/musician/saas_list.html similarity index 100% rename from orchestra/contrib/musician/templates/musician/saas.html rename to orchestra/contrib/musician/templates/musician/saas_list.html diff --git a/orchestra/contrib/musician/views.py b/orchestra/contrib/musician/views.py index a89fd0fb..b99a7613 100644 --- a/orchestra/contrib/musician/views.py +++ b/orchestra/contrib/musician/views.py @@ -12,7 +12,7 @@ from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils import translation from django.utils.html import format_html -from django.utils.http import is_safe_url +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.generic.base import RedirectView, TemplateView @@ -153,7 +153,7 @@ def profile_set_language(request, code): translation.activate(user_language) redirect_to = request.GET.get('next', '') - url_is_safe = is_safe_url( + url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={request.get_host()}, require_https=request.is_secure(), @@ -232,7 +232,7 @@ class BillDownloadView(CustomContextMixin, UserTokenRequiredMixin, View): bill = self.get_object() # TODO(@slamora): implement download as PDF, now only HTML is reachable via link - content_type = request.META.get('HTTP_ACCEPT') + content_type = request.headers.get('accept') if content_type == 'application/pdf': pdf = html_to_pdf(bill.html or bill.render()) return HttpResponse(pdf, content_type='application/pdf') @@ -243,7 +243,7 @@ class BillDownloadView(CustomContextMixin, UserTokenRequiredMixin, View): class AddressListView(ServiceListView): service_class = AddressService model = Address - template_name = "musician/addresses.html" + template_name = "musician/address_list.html" extra_context = { # Translators: This message appears on the page title 'title': _('Mail addresses'), @@ -317,7 +317,7 @@ class AddressDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView): class MailingListsView(ServiceListView): service_class = MailinglistService model = List - template_name = "musician/mailinglists.html" + template_name = "musician/mailinglist_list.html" extra_context = { # Translators: This message appears on the page title 'title': _('Mailing lists'), @@ -348,7 +348,7 @@ class MailingListsView(ServiceListView): class MailboxListView(ServiceListView): service_class = MailboxService model = Mailbox - template_name = "musician/mailboxes.html" + template_name = "musician/mailbox_list.html" extra_context = { # Translators: This message appears on the page title 'title': _('Mailboxes'), @@ -447,7 +447,7 @@ class DatabasesView(ServiceListView): class SaasListView(ServiceListView): service_class = SaasService model = SaaS - template_name = "musician/saas.html" + template_name = "musician/saas_list.html" extra_context = { # Translators: This message appears on the page title 'title': _('Software as a Service'), @@ -496,7 +496,7 @@ class DomainUpdateRecordView(CustomContextMixin, UserTokenRequiredMixin, UpdateV class DomainDeleteRecordView(CustomContextMixin, UserTokenRequiredMixin, DeleteView): model = Record - template_name = "musician/record_confirm_delete.html" + template_name = "musician/record_check_delete.html" pk_url_kwarg = "record_pk" def get_queryset(self): @@ -545,7 +545,7 @@ class LoginView(FormView): self.redirect_field_name, self.request.GET.get(self.redirect_field_name, '') ) - url_is_safe = is_safe_url( + url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={self.request.get_host()}, require_https=self.request.is_secure(), diff --git a/orchestra/contrib/orchestration/__init__.py b/orchestra/contrib/orchestration/__init__.py index c4774481..f04afda3 100644 --- a/orchestra/contrib/orchestration/__init__.py +++ b/orchestra/contrib/orchestration/__init__.py @@ -6,7 +6,6 @@ from orchestra.utils.python import AttrDict from .backends import ServiceBackend, ServiceController, replace -default_app_config = 'orchestra.contrib.orchestration.apps.OrchestrationConfig' class Operation(): diff --git a/orchestra/contrib/orchestration/admin.py b/orchestra/contrib/orchestration/admin.py index 703fba84..203166ed 100644 --- a/orchestra/contrib/orchestration/admin.py +++ b/orchestra/contrib/orchestration/admin.py @@ -28,6 +28,7 @@ STATE_COLORS = { } +@admin.register(Route) class RouteAdmin(ExtendedModelAdmin): list_display = ( 'display_backend', 'host', 'match', 'display_model', 'display_actions', 'run_async', @@ -49,20 +50,24 @@ class RouteAdmin(ExtendedModelAdmin): display_backend = display_plugin_field('backend') + @admin.display( + description=_("model") + ) def display_model(self, route): try: return route.backend_class.model except KeyError: return mark_safe("NOT AVAILABLE") - display_model.short_description = _("model") + @admin.display( + description=_("actions") + ) @mark_safe def display_actions(self, route): try: return '
'.join(route.backend_class.get_actions()) except KeyError: return "NOT AVAILABLE" - display_actions.short_description = _("actions") def formfield_for_dbfield(self, db_field, **kwargs): """ Provides dynamic help text on backend form field """ @@ -113,13 +118,15 @@ class BackendOperationInline(admin.TabularInline): 'all': ('orchestra/css/hide-inline-id.css',) } + @admin.display( + description=_("Instance") + ) def instance_link(self, operation): link = admin_link('instance')(self, operation) if link == '---': return _("Deleted {0}").format(operation.instance_repr or '-'.join( (escape(operation.content_type), escape(operation.object_id)))) return link - instance_link.short_description = _("Instance") def has_add_permission(self, *args, **kwargs): return False @@ -129,6 +136,7 @@ class BackendOperationInline(admin.TabularInline): return queryset.prefetch_related('instance') +@admin.register(BackendLog) class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin): list_display = ( 'id', 'backend', 'server_link', 'display_state', 'exit_code', @@ -170,19 +178,24 @@ class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin): return False +@admin.register(Server) class ServerAdmin(ExtendedModelAdmin): list_display = ('name', 'address', 'os', 'display_ping', 'display_uptime') list_filter = ('os',) actions = (orchestrate,) change_view_actions = actions + @admin.display( + description=_("Ping") + ) def display_ping(self, instance): return mark_safe(self._remote_state[instance.pk][0]) - display_ping.short_description = _("Ping") + @admin.display( + description=_("Uptime") + ) def display_uptime(self, instance): return mark_safe(self._remote_state[instance.pk][1]) - display_uptime.short_description = _("Uptime") def get_queryset(self, request): """ Order by structured name and imporve performance """ @@ -191,6 +204,3 @@ class ServerAdmin(ExtendedModelAdmin): self._remote_state = retrieve_state(qs) return qs -admin.site.register(Server, ServerAdmin) -admin.site.register(BackendLog, BackendLogAdmin) -admin.site.register(Route, RouteAdmin) diff --git a/orchestra/contrib/orders/__init__.py b/orchestra/contrib/orders/__init__.py index 753c362c..e69de29b 100644 --- a/orchestra/contrib/orders/__init__.py +++ b/orchestra/contrib/orders/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.orders.apps.OrdersConfig' diff --git a/orchestra/contrib/orders/admin.py b/orchestra/contrib/orders/admin.py index 3c3086b8..b9931d96 100644 --- a/orchestra/contrib/orders/admin.py +++ b/orchestra/contrib/orders/admin.py @@ -50,6 +50,7 @@ class MetricStorageInline(admin.TabularInline): return qs +@admin.register(Order) class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): list_display = ( 'display_description', 'service_link', 'account_link', 'content_object_link', @@ -112,11 +113,17 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): display_registered_on = admin_date('registered_on') display_cancelled_on = admin_date('cancelled_on') + @admin.display( + description=_("Description"), + ordering='description', + ) def display_description(self, order): return format_html(order.description[:64]) - display_description.short_description = _("Description") - display_description.admin_order_field = 'description' + @admin.display( + description=_("Content object"), + ordering='content_object_repr', + ) def content_object_link(self, order): if order.content_object: try: @@ -128,9 +135,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): return format_html('{description}', url=url, description=description) return order.content_object_repr - content_object_link.short_description = _("Content object") - content_object_link.admin_order_field = 'content_object_repr' + @admin.display( + description=_("Bills") + ) @mark_safe def bills_links(self, order): bills = [] @@ -138,8 +146,11 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): for line in order.lines.select_related('bill').distinct('bill'): bills.append(make_link(line.bill)) return '
'.join(bills) - bills_links.short_description = _("Bills") + @admin.display( + description=_("billed until"), + ordering='billed_until', + ) def display_billed_until(self, order): billed_until = order.billed_until red = False @@ -160,9 +171,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): '{human}', raw=escape(str(billed_until)), color=color, human=human, ) - display_billed_until.short_description = _("billed until") - display_billed_until.admin_order_field = 'billed_until' + @admin.display( + description=_("Metric") + ) def display_metric(self, order): """ dispalys latest metric value, don't uses latest() because not loosing prefetch_related @@ -172,7 +184,6 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): except IndexError: return '' return metric.value - display_metric.short_description = _("Metric") def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ @@ -197,11 +208,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): # return OrderFilterChangeList +@admin.register(MetricStorage) class MetricStorageAdmin(admin.ModelAdmin): list_display = ('order', 'value', 'created_on', 'updated_on') list_filter = ('order__service',) raw_id_fields = ('order',) -admin.site.register(Order, OrderAdmin) -admin.site.register(MetricStorage, MetricStorageAdmin) diff --git a/orchestra/contrib/payments/__init__.py b/orchestra/contrib/payments/__init__.py index 970bd432..e69de29b 100644 --- a/orchestra/contrib/payments/__init__.py +++ b/orchestra/contrib/payments/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.payments.apps.PaymentsConfig' diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py index e134c2e5..fb1f7d00 100644 --- a/orchestra/contrib/payments/admin.py +++ b/orchestra/contrib/payments/admin.py @@ -32,6 +32,7 @@ PROCESS_STATE_COLORS = { } +@admin.register(PaymentSource) class PaymentSourceAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ('label', 'method', 'number', 'account_link', 'is_active') list_filter = ('method', 'is_active') @@ -69,6 +70,7 @@ class TransactionInline(admin.TabularInline): return qs.select_related('source', 'bill') +@admin.register(Transaction) class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'id', 'bill_link', 'account_link', 'source_link', 'display_created_at', @@ -156,16 +158,19 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): return [] return [action for action in actions if action.__name__ not in exclude] + @admin.display( + description=_("State"), + ordering='state', + ) @mark_safe def display_state(self, obj): state = admin_colored('state', colors=STATE_COLORS)(obj) help_text = obj.get_state_help() state = state.replace('{}', process.file.url, process.file.name) - file_url.admin_order_field = 'file' + @admin.display( + description=_("Transactions") + ) @mark_safe def display_transactions(self, process): ids = [] @@ -208,7 +218,6 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin): url = reverse('admin:payments_transaction_changelist') url += '?process_id=%i' % process.id return '%s' % (url, transactions) - display_transactions.short_description = _("Transactions") def has_add_permission(self, *args, **kwargs): return False @@ -240,6 +249,3 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin): helpers.post_delete_processes(self, request, related_transactions) -admin.site.register(PaymentSource, PaymentSourceAdmin) -admin.site.register(Transaction, TransactionAdmin) -admin.site.register(TransactionProcess, TransactionProcessAdmin) diff --git a/orchestra/contrib/plans/__init__.py b/orchestra/contrib/plans/__init__.py index e09642bf..e69de29b 100644 --- a/orchestra/contrib/plans/__init__.py +++ b/orchestra/contrib/plans/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.plans.apps.PlansConfig' diff --git a/orchestra/contrib/plans/admin.py b/orchestra/contrib/plans/admin.py index 0a9ebacb..35658ca2 100644 --- a/orchestra/contrib/plans/admin.py +++ b/orchestra/contrib/plans/admin.py @@ -18,6 +18,7 @@ class RateInline(admin.TabularInline): ordering = ('service', 'plan', 'quantity') +@admin.register(Plan) class PlanAdmin(ExtendedModelAdmin): list_display = ( 'name', 'is_default', 'is_combinable', 'allow_multiple', 'is_active', 'num_contracts', @@ -30,19 +31,22 @@ class PlanAdmin(ExtendedModelAdmin): change_readonly_fields = ('name',) inlines = [RateInline] + @admin.display( + description=_("Contracts"), + ordering='contracts__count', + ) def num_contracts(self, plan): num = plan.contracts__count url = reverse('admin:plans_contractedplan_changelist') url += '?plan__name={}'.format(plan.name) return format_html('{1}', url, num) - num_contracts.short_description = _("Contracts") - num_contracts.admin_order_field = 'contracts__count' def get_queryset(self, request): qs = super(PlanAdmin, self).get_queryset(request) return qs.annotate(models.Count('contracts', distinct=True)) +@admin.register(ContractedPlan) class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ('id', 'plan_link', 'account_link') list_filter = ('plan__name',) @@ -53,7 +57,5 @@ class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin): plan_link = admin_link('plan') -admin.site.register(Plan, PlanAdmin) -admin.site.register(ContractedPlan, ContractedPlanAdmin) insertattr(Service, 'inlines', RateInline) diff --git a/orchestra/contrib/resources/__init__.py b/orchestra/contrib/resources/__init__.py index 7c273a58..1ae84c0f 100644 --- a/orchestra/contrib/resources/__init__.py +++ b/orchestra/contrib/resources/__init__.py @@ -1,4 +1,3 @@ from .backends import ServiceMonitor -default_app_config = 'orchestra.contrib.resources.apps.ResourcesConfig' diff --git a/orchestra/contrib/resources/actions.py b/orchestra/contrib/resources/actions.py index 3120213b..f00fdaf2 100644 --- a/orchestra/contrib/resources/actions.py +++ b/orchestra/contrib/resources/actions.py @@ -6,7 +6,7 @@ from django.utils.translation import ngettext, gettext_lazy as _ def run_monitor(modeladmin, request, queryset): """ Resource and ResourceData run monitors """ - referer = request.META.get('HTTP_REFERER') + referer = request.headers.get('referer') run_async = modeladmin.model.monitor.__defaults__[0] logs = set() for resource in queryset: diff --git a/orchestra/contrib/resources/admin.py b/orchestra/contrib/resources/admin.py index 53f573ee..49bcf305 100644 --- a/orchestra/contrib/resources/admin.py +++ b/orchestra/contrib/resources/admin.py @@ -29,6 +29,7 @@ from .forms import ResourceForm from .models import Resource, ResourceData, MonitorData +@admin.register(Resource) class ResourceAdmin(ExtendedModelAdmin): list_display = ( 'id', 'verbose_name', 'content_type', 'aggregation', 'on_demand', @@ -103,14 +104,17 @@ class ResourceAdmin(ExtendedModelAdmin): return super(ResourceAdmin, self).formfield_for_dbfield(db_field, **kwargs) +@admin.display( + description=_("Content object"), + ordering='content_object_repr', +) def content_object_link(data): ct = data.content_type url = reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=(data.object_id,)) return format_html('{}', url, data.content_object_repr) -content_object_link.short_description = _("Content object") -content_object_link.admin_order_field = 'content_object_repr' +@admin.register(ResourceData) class ResourceDataAdmin(ExtendedModelAdmin): list_display = ( 'id', 'resource_link', content_object_link, 'allocated', 'display_used', @@ -151,13 +155,15 @@ class ResourceDataAdmin(ExtendedModelAdmin): ), ] + urls + @admin.display( + description=_("Used"), + ordering='used', + ) def display_used(self, rdata): if rdata.used is None: return '' url = reverse('admin:resources_resourcedata_used_monitordata', args=(rdata.pk,)) return format_html('{} {}', url, rdata.used, rdata.unit) - display_used.short_description = _("Used") - display_used.admin_order_field = 'used' def has_add_permission(self, *args, **kwargs): return False @@ -193,6 +199,7 @@ class ResourceDataAdmin(ExtendedModelAdmin): return redirect(url) +@admin.register(MonitorData) class MonitorDataAdmin(ExtendedModelAdmin): list_display = ('id', 'monitor', content_object_link, 'display_created', 'value') list_filter = ('monitor', ResourceDataListFilter) @@ -227,9 +234,6 @@ class MonitorDataAdmin(ExtendedModelAdmin): return queryset.prefetch_related('content_object') -admin.site.register(Resource, ResourceAdmin) -admin.site.register(ResourceData, ResourceDataAdmin) -admin.site.register(MonitorData, MonitorDataAdmin) # Mokey-patching @@ -303,6 +307,9 @@ def resource_inline_factory(resources): self.verbose_name_plural = mark_safe(_("Resources") + ' ' + link) return super(ResourceInline, self).get_fieldsets(request, obj) + @admin.display( + description=_("Used") + ) @mark_safe def display_used(self, rdata): update = '' @@ -328,7 +335,6 @@ def resource_inline_factory(resources): if rdata.resource.monitors: return _("Unknonw %s %s") % (update, history) return _("No monitor") - display_used.short_description = _("Used") def has_add_permission(self, *args, **kwargs): """ Hidde add another """ diff --git a/orchestra/contrib/saas/__init__.py b/orchestra/contrib/saas/__init__.py index 0bc8af8b..e69de29b 100644 --- a/orchestra/contrib/saas/__init__.py +++ b/orchestra/contrib/saas/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.saas.apps.SaaSConfig' diff --git a/orchestra/contrib/saas/admin.py b/orchestra/contrib/saas/admin.py index 90176d87..fbed1331 100644 --- a/orchestra/contrib/saas/admin.py +++ b/orchestra/contrib/saas/admin.py @@ -18,6 +18,7 @@ from .models import SaaS from .services import SoftwareService +@admin.register(SaaS) class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ('name', 'service', 'display_url', 'account_link', 'display_active') list_filter = ('service', IsActiveListFilter, CustomURLListFilter) @@ -28,6 +29,10 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi plugin_title = 'Software as a Service' actions = (disable, enable, list_accounts) + @admin.display( + description=_("URL"), + ordering='name', + ) @mark_safe def display_url(self, saas): site_domain = saas.get_site_domain() @@ -47,8 +52,6 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi ) links.append(link) return '
'.join(links) - display_url.short_description = _("URL") - display_url.admin_order_field = 'name' def get_fields(self, *args, **kwargs): fields = super(SaaSAdmin, self).get_fields(*args, **kwargs) @@ -57,4 +60,3 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi return fields -admin.site.register(SaaS, SaaSAdmin) diff --git a/orchestra/contrib/services/__init__.py b/orchestra/contrib/services/__init__.py index cafed625..e69de29b 100644 --- a/orchestra/contrib/services/__init__.py +++ b/orchestra/contrib/services/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.services.apps.ServicesConfig' diff --git a/orchestra/contrib/services/admin.py b/orchestra/contrib/services/admin.py index 2f4b835f..d1a13e0d 100644 --- a/orchestra/contrib/services/admin.py +++ b/orchestra/contrib/services/admin.py @@ -15,6 +15,7 @@ from .actions import update_orders, view_help, clone from .models import Service +@admin.register(Service) class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin): list_display = ( 'description', 'content_type', 'handler_type', 'num_orders', 'is_active' @@ -66,13 +67,15 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin): kwargs['widget'] = forms.TextInput(attrs={'size':'160'}) return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs) + @admin.display( + description=_("Orders"), + ordering='orders__count', + ) def num_orders(self, service): num = service.orders__count url = reverse('admin:orders_order_changelist') url += '?service__id__exact=%i&is_active=True' % service.pk return format_html('{}', url, num) - num_orders.short_description = _("Orders") - num_orders.admin_order_field = 'orders__count' def get_queryset(self, request): qs = super(ServiceAdmin, self).get_queryset(request) @@ -104,4 +107,3 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin): help_view.verbose_name = _("Help") -admin.site.register(Service, ServiceAdmin) diff --git a/orchestra/contrib/settings/__init__.py b/orchestra/contrib/settings/__init__.py index 1dc831a5..be43f745 100644 --- a/orchestra/contrib/settings/__init__.py +++ b/orchestra/contrib/settings/__init__.py @@ -9,7 +9,6 @@ from orchestra.core import validators from orchestra.utils.python import import_class, format_exception -default_app_config = 'orchestra.contrib.settings.apps.SettingsConfig' class Setting(object): diff --git a/orchestra/contrib/systemusers/__init__.py b/orchestra/contrib/systemusers/__init__.py index 1fbedd5f..e69de29b 100644 --- a/orchestra/contrib/systemusers/__init__.py +++ b/orchestra/contrib/systemusers/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.systemusers.apps.SystemUsersConfig' diff --git a/orchestra/contrib/systemusers/admin.py b/orchestra/contrib/systemusers/admin.py index f00d7259..be3c2bf6 100644 --- a/orchestra/contrib/systemusers/admin.py +++ b/orchestra/contrib/systemusers/admin.py @@ -13,6 +13,7 @@ from .forms import SystemUserCreationForm, SystemUserChangeForm, WebappUserChang from .models import SystemUser, WebappUsers +@admin.register(SystemUser) class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'username', 'account_link', 'shell', 'display_home', 'display_active', 'display_main' @@ -45,15 +46,19 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende change_view_actions = (set_permission, create_link) actions = (disable, enable, list_accounts) + change_view_actions + @admin.display( + description=_("Main"), + boolean=True, + ) def display_main(self, user): return user.is_main - display_main.short_description = _("Main") - display_main.boolean = True + @admin.display( + description=_("Home"), + ordering='home', + ) def display_home(self, user): return user.get_home() - display_home.short_description = _("Home") - display_home.admin_order_field = 'home' def get_form(self, request, obj=None, **kwargs): form = super(SystemUserAdmin, self).get_form(request, obj, **kwargs) @@ -79,6 +84,7 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende +@admin.register(WebappUsers) class WebappUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'username', 'account_link', 'home', 'target_server' @@ -107,5 +113,3 @@ class WebappUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende ordering = ('-id',) -admin.site.register(SystemUser, SystemUserAdmin) -admin.site.register(WebappUsers, WebappUserAdmin) \ No newline at end of file diff --git a/orchestra/contrib/tasks/__init__.py b/orchestra/contrib/tasks/__init__.py index 61023b65..279504cc 100644 --- a/orchestra/contrib/tasks/__init__.py +++ b/orchestra/contrib/tasks/__init__.py @@ -2,4 +2,3 @@ from . import settings from .decorators import task, periodic_task, keep_state, apply_async -default_app_config = 'orchestra.contrib.tasks.apps.TasksConfig' diff --git a/orchestra/contrib/vps/__init__.py b/orchestra/contrib/vps/__init__.py index 96cf9729..e69de29b 100644 --- a/orchestra/contrib/vps/__init__.py +++ b/orchestra/contrib/vps/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.vps.apps.VPSConfig' diff --git a/orchestra/contrib/vps/admin.py b/orchestra/contrib/vps/admin.py index 346ddaf9..f7c8a460 100644 --- a/orchestra/contrib/vps/admin.py +++ b/orchestra/contrib/vps/admin.py @@ -10,6 +10,7 @@ from orchestra.forms import UserCreationForm, NonStoredUserChangeForm from .models import VPS +@admin.register(VPS) class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ('hostname', 'type', 'template', 'display_active', 'account_link') list_filter = ('type', IsActiveListFilter, 'template') @@ -44,4 +45,3 @@ class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin): return 'root@%s' % obj.hostname -admin.site.register(VPS, VPSAdmin) diff --git a/orchestra/contrib/webapps/__init__.py b/orchestra/contrib/webapps/__init__.py index f08acd2d..e69de29b 100644 --- a/orchestra/contrib/webapps/__init__.py +++ b/orchestra/contrib/webapps/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.webapps.apps.WebAppsConfig' diff --git a/orchestra/contrib/webapps/admin.py b/orchestra/contrib/webapps/admin.py index 8da41282..e07955a8 100644 --- a/orchestra/contrib/webapps/admin.py +++ b/orchestra/contrib/webapps/admin.py @@ -74,6 +74,7 @@ class WebAppOptionInline(admin.TabularInline): return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs) +@admin.register(WebApp) class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ( 'name', 'display_type', 'display_detail', 'display_websites', 'account_link', 'target_server', @@ -91,6 +92,9 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin) display_type = display_plugin_field('type') + @admin.display( + description=_("user sftp") + ) def display_sftpuser(self, obj): salida = "" if obj.sftpuser is None: @@ -99,8 +103,10 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin) url = resolve_url(admin_urlname(WebappUsers._meta, 'change'), obj.sftpuser.id) salida += f'{obj.sftpuser}
' return mark_safe(salida) - display_sftpuser.short_description = _("user sftp") + @admin.display( + description=_("web sites") + ) @mark_safe def display_websites(self, webapp): websites = [] @@ -118,14 +124,15 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin) plus = '+' websites.append('%s%s' % (add_url, plus, gettext("Add website"))) return '
'.join(websites) - display_websites.short_description = _("web sites") + @admin.display( + description=_("detail") + ) def display_detail(self, webapp): try: return webapp.type_instance.get_detail() except KeyError: return mark_safe("Not available") - display_detail.short_description = _("detail") def save_model(self, request, obj, form, change): @@ -155,5 +162,4 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin) ) return super().response_add(request, obj, post_url_continue) -admin.site.register(WebApp, WebAppAdmin) diff --git a/orchestra/contrib/websites/__init__.py b/orchestra/contrib/websites/__init__.py index 93cab2d3..e69de29b 100644 --- a/orchestra/contrib/websites/__init__.py +++ b/orchestra/contrib/websites/__init__.py @@ -1 +0,0 @@ -default_app_config = 'orchestra.contrib.websites.apps.WebsitesConfig' diff --git a/orchestra/contrib/websites/admin.py b/orchestra/contrib/websites/admin.py index f47f28b9..efd105e9 100644 --- a/orchestra/contrib/websites/admin.py +++ b/orchestra/contrib/websites/admin.py @@ -51,13 +51,16 @@ class ContentInline(AccountAdminMixin, admin.TabularInline): webapp_link = admin_link('webapp', popup=True) webapp_link.short_description = _("Web App") + @admin.display( + description=_("Web App type") + ) def webapp_type(self, content): if not content.pk: return '' return content.webapp.get_type_display() - webapp_type.short_description = _("Web App type") +@admin.register(Website) class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): list_display = ( 'name', 'display_domains', 'display_webapps', 'account_link', 'target_server', 'display_active' @@ -80,6 +83,10 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name') actions = (disable, enable, list_accounts) + @admin.display( + description=_("domains"), + ordering='domains', + ) @mark_safe def display_domains(self, website): domains = [] @@ -87,9 +94,10 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): url = '%s://%s' % (website.get_protocol(), domain) domains.append('%s' % (url, url)) return '
'.join(domains) - display_domains.short_description = _("domains") - display_domains.admin_order_field = 'domains' + @admin.display( + description=_("Web apps") + ) @mark_safe def display_webapps(self, website): webapps = [] @@ -106,7 +114,6 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): webapp_info = format_html('{} {}', url, detail, name, site_link) webapps.append(webapp_info) return '
'.join(webapps) - display_webapps.short_description = _("Web apps") def formfield_for_dbfield(self, db_field, **kwargs): """ @@ -136,4 +143,3 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): return formsets, inline_instances -admin.site.register(Website, WebsiteAdmin) diff --git a/orchestra/plugins/admin.py b/orchestra/plugins/admin.py index 4183ced8..e67b0dac 100644 --- a/orchestra/plugins/admin.py +++ b/orchestra/plugins/admin.py @@ -71,7 +71,7 @@ class SelectPluginAdminMixin(object): if not plugin_value and request.method == 'POST': # HACK baceuse django add_preserved_filters removes extising queryargs value = re.search(r"%s=([^&^']+)[&']" % self.plugin_field, - request.META.get('HTTP_REFERER', '')) + request.headers.get('referer', '')) if value: plugin_value = value.groups()[0] return plugin_value diff --git a/orchestra/urls.py b/orchestra/urls.py index 590ad608..7811981b 100644 --- a/orchestra/urls.py +++ b/orchestra/urls.py @@ -1,5 +1,5 @@ from django.contrib import admin -from django.conf.urls import include, url +from django.urls import include, path, re_path from django.urls import path from rest_framework.authtoken.views import obtain_auth_token @@ -15,13 +15,13 @@ api.autodiscover() urlpatterns = [ # Admin - url(r'^admin/', admin.site.urls), - url(r'^admin_tools/', include('admin_tools.urls')), + re_path(r'^admin/', admin.site.urls), + path('admin_tools/', include('admin_tools.urls')), # REST API - url(r'^api/', include(api.router.urls)), - url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - url(r'^api-token-auth/', obtain_auth_token, name='api-token-auth'), - url(r'^media/(.+)/(.+)/(.+)/(.+)/(.+)$', serve_private_media, name='private-media'), + path('api/', include(api.router.urls)), + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), + re_path(r'^api-token-auth/', obtain_auth_token, name='api-token-auth'), + re_path(r'^media/(.+)/(.+)/(.+)/(.+)/(.+)$', serve_private_media, name='private-media'), # url(r'search', 'orchestra.views.search', name='search'), # MUSICIAN path('panel/', include('orchestra.contrib.musician.urls')), @@ -31,5 +31,5 @@ urlpatterns = [ if isinstalled('debug_toolbar'): import debug_toolbar urlpatterns.append( - url(r'^__debug__/', include(debug_toolbar.urls)), + path('__debug__/', include(debug_toolbar.urls)), ) diff --git a/requirements.txt b/requirements.txt index 3d1ce1d8..3b6ce789 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,25 @@ -Django==2.2.24 -django-fluent-dashboard==1.0.1 -django-admin-tools==0.9.1 + +Django==3.2.23 +django-fluent-dashboard==2.0 +django-admin-tools==0.9.3 +django-extensions==3.2.3 +django-celery==3.3.1 +celery>=3.1.15,<4.0 +kombu==3.0.37 django-bootstrap4 -django-extensions==3.1.3 -django-celery==3.2.1 -celery==3.1.23 -kombu==3.0.35 billiard==3.3.0.23 -Markdown==3.3.4 -djangorestframework==3.12.4 -Pygments==2.9.0 -django-filter==2.4.0 +Markdown==3.5.1 +djangorestframework==3.14.0 +Pygments==2.17.2 +django-filter==23.4 jsonfield==3.1.0 -python-dateutil>=2.7.0 +python-dateutil==2.8.2 passlib==1.7.4 -django-iban==0.3.0 +django-iban==0.3.1 requests -phonenumbers==8.12.27 +phonenumbers==8.13.26 django-countries -django-localflavor==3.1 +django-localflavor==4.0 amqp anyjson pytz