WIP: django-3.2-compat #3

Draft
slamora wants to merge 8 commits from django-3.2-compat into main
65 changed files with 445 additions and 308 deletions

View File

@ -1,12 +1,13 @@
from functools import partial from functools import partial
from django.contrib import admin from django.contrib import admin
from django.core.exceptions import PermissionDenied
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 ngettext, gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from .. import settings from .. import settings
from .decorators import action_with_confirmation from .decorators import action_with_confirmation
from .forms import SendEmailForm from .forms import SendEmailForm
@ -18,7 +19,7 @@ class SendEmail(object):
template = 'admin/orchestra/generic_confirmation.html' template = 'admin/orchestra/generic_confirmation.html'
default_from = settings.ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL default_from = settings.ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL
__name__ = 'semd_email' __name__ = 'semd_email'
def __call__(self, modeladmin, request, queryset): def __call__(self, modeladmin, request, queryset):
""" make this monster behave like a function """ """ make this monster behave like a function """
self.modeladmin = modeladmin self.modeladmin = modeladmin
@ -34,10 +35,10 @@ class SendEmail(object):
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
} }
return self.write_email(request) return self.write_email(request)
def write_email(self, request): def write_email(self, request):
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied()
initial={ initial={
'email_from': self.default_from, 'email_from': self.default_from,
'to': ' '.join(self.get_email_addresses()) 'to': ' '.join(self.get_email_addresses())
@ -51,7 +52,7 @@ class SendEmail(object):
'extra_to': form.cleaned_data['extra_to'], 'extra_to': form.cleaned_data['extra_to'],
'subject': form.cleaned_data['subject'], 'subject': form.cleaned_data['subject'],
'message': form.cleaned_data['message'], 'message': form.cleaned_data['message'],
} }
return self.confirm_email(request, **options) return self.confirm_email(request, **options)
self.context.update({ self.context.update({
@ -62,10 +63,10 @@ class SendEmail(object):
}) })
# Display confirmation page # Display confirmation page
return render(request, self.template, self.context) return render(request, self.template, self.context)
def get_email_addresses(self): def get_email_addresses(self):
return self.queryset.values_list('email', flat=True) return self.queryset.values_list('email', flat=True)
def confirm_email(self, request, **options): def confirm_email(self, request, **options):
email_from = options['email_from'] email_from = options['email_from']
extra_to = options['extra_to'] extra_to = options['extra_to']
@ -88,7 +89,7 @@ class SendEmail(object):
) )
self.modeladmin.message_user(request, msg) self.modeladmin.message_user(request, msg)
return None return None
form = self.form(initial={ form = self.form(initial={
'email_from': email_from, 'email_from': email_from,
'extra_to': ', '.join(extra_to), 'extra_to': ', '.join(extra_to),
@ -131,15 +132,19 @@ def base_disable(modeladmin, request, queryset, disable=True):
modeladmin.message_user(request, msg) modeladmin.message_user(request, msg)
@admin.action(
description=_("Disable")
)
@action_with_confirmation() @action_with_confirmation()
def disable(modeladmin, request, queryset): def disable(modeladmin, request, queryset):
return base_disable(modeladmin, request, queryset) return base_disable(modeladmin, request, queryset)
disable.url_name = 'disable' disable.url_name = 'disable'
disable.short_description = _("Disable")
@admin.action(
description=_("Enable")
)
@action_with_confirmation() @action_with_confirmation()
def enable(modeladmin, request, queryset): def enable(modeladmin, request, queryset):
return base_disable(modeladmin, request, queryset, disable=False) return base_disable(modeladmin, request, queryset, disable=False)
enable.url_name = 'enable' enable.url_name = 'enable'
enable.short_description = _("Enable")

View File

@ -1,16 +1,16 @@
from urllib import parse from urllib import parse
from django import forms from django import forms
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
from django.contrib.auth import update_session_auth_hash from django.contrib.auth import update_session_auth_hash
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.forms.models import BaseInlineFormSet from django.forms.models import BaseInlineFormSet
from django.http import Http404, HttpResponse, HttpResponseRedirect
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.urls import re_path as url
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.html import escape 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 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 from .forms import AdminPasswordChangeForm
#, AdminRawPasswordChangeForm #, AdminRawPasswordChangeForm
#from django.contrib.auth.forms import AdminPasswordChangeForm #from django.contrib.auth.forms import AdminPasswordChangeForm
from .utils import action_to_view from .utils import action_to_view
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters()) 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 = (('my_nodes', 'True'),)
""" """
default_changelist_filters = () default_changelist_filters = ()
def changelist_view(self, request, extra_context=None): def changelist_view(self, request, extra_context=None):
# defaults = [] # defaults = []
# for key, value in self.default_changelist_filters: # for key, value in self.default_changelist_filters:
@ -79,7 +77,7 @@ class EnhaceSearchMixin(object):
if 'password' in lookup: if 'password' in lookup:
return False return False
return True return True
def get_search_results(self, request, queryset, search_term): def get_search_results(self, request, queryset, search_term):
""" allows to specify field <field_name>:<search_term> """ """ allows to specify field <field_name>:<search_term> """
search_fields = self.get_search_fields(request) search_fields = self.get_search_fields(request)
@ -109,7 +107,7 @@ class ChangeViewActionsMixin(object):
""" Makes actions visible on the admin change view page. """ """ Makes actions visible on the admin change view page. """
change_view_actions = () change_view_actions = ()
change_form_template = 'orchestra/admin/change_form.html' change_form_template = 'orchestra/admin/change_form.html'
def get_urls(self): def get_urls(self):
"""Returns the additional urls for the change view links""" """Returns the additional urls for the change view links"""
urls = super(ChangeViewActionsMixin, self).get_urls() urls = super(ChangeViewActionsMixin, self).get_urls()
@ -124,7 +122,7 @@ class ChangeViewActionsMixin(object):
) )
) )
return new_urls + urls return new_urls + urls
def get_change_view_actions(self, obj=None): def get_change_view_actions(self, obj=None):
""" allow customization on modelamdin """ """ allow customization on modelamdin """
views = [] views = []
@ -145,7 +143,7 @@ class ChangeViewActionsMixin(object):
view.hidden = getattr(action, 'hidden', False) view.hidden = getattr(action, 'hidden', False)
views.append(view) views.append(view)
return views return views
def change_view(self, request, object_id, **kwargs): def change_view(self, request, object_id, **kwargs):
if kwargs.get('extra_context', None) is None: if kwargs.get('extra_context', None) is None:
kwargs['extra_context'] = {} kwargs['extra_context'] = {}
@ -165,21 +163,21 @@ class ChangeAddFieldsMixin(object):
change_readonly_fields = () change_readonly_fields = ()
change_form = None change_form = None
add_inlines = None add_inlines = None
def get_prepopulated_fields(self, request, obj=None): def get_prepopulated_fields(self, request, obj=None):
if not obj: if not obj:
return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj) return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj)
return {} return {}
def get_change_readonly_fields(self, request, obj=None): def get_change_readonly_fields(self, request, obj=None):
return self.change_readonly_fields return self.change_readonly_fields
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj) fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj)
if obj: if obj:
return fields + self.get_change_readonly_fields(request, obj) return fields + self.get_change_readonly_fields(request, obj)
return fields return fields
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
if not obj: if not obj:
if self.add_fieldsets: if self.add_fieldsets:
@ -187,7 +185,7 @@ class ChangeAddFieldsMixin(object):
elif self.add_fields: elif self.add_fields:
return [(None, {'fields': self.add_fields})] return [(None, {'fields': self.add_fields})]
return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj) return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj)
def get_inline_instances(self, request, obj=None): def get_inline_instances(self, request, obj=None):
""" add_inlines and inline.parent_object """ """ add_inlines and inline.parent_object """
if obj: if obj:
@ -198,7 +196,7 @@ class ChangeAddFieldsMixin(object):
for inline in inlines: for inline in inlines:
inline.parent_object = obj inline.parent_object = obj
return inlines return inlines
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
""" Use special form during user creation """ """ Use special form during user creation """
defaults = {} defaults = {}
@ -218,13 +216,13 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
EnhaceSearchMixin, EnhaceSearchMixin,
admin.ModelAdmin): admin.ModelAdmin):
list_prefetch_related = None list_prefetch_related = None
def get_queryset(self, request): def get_queryset(self, request):
qs = super(ExtendedModelAdmin, self).get_queryset(request) qs = super(ExtendedModelAdmin, self).get_queryset(request)
if self.list_prefetch_related: if self.list_prefetch_related:
qs = qs.prefetch_related(*self.list_prefetch_related) qs = qs.prefetch_related(*self.list_prefetch_related)
return qs return qs
def get_object(self, request, object_id, from_field=None): def get_object(self, request, object_id, from_field=None):
obj = super(ExtendedModelAdmin, self).get_object(request, object_id, from_field) obj = super(ExtendedModelAdmin, self).get_object(request, object_id, from_field)
if obj is None: if obj is None:
@ -237,7 +235,7 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
class ChangePasswordAdminMixin(object): class ChangePasswordAdminMixin(object):
change_password_form = AdminPasswordChangeForm change_password_form = AdminPasswordChangeForm
change_user_password_template = 'admin/orchestra/change_password.html' change_user_password_template = 'admin/orchestra/change_password.html'
def get_urls(self): def get_urls(self):
opts = self.model._meta opts = self.model._meta
info = opts.app_label, opts.model_name info = opts.app_label, opts.model_name
@ -249,14 +247,14 @@ class ChangePasswordAdminMixin(object):
self.admin_site.admin_view(self.show_hash), self.admin_site.admin_view(self.show_hash),
name='%s_%s_show_hash' % info) name='%s_%s_show_hash' % info)
] + super().get_urls() ] + super().get_urls()
def get_change_password_username(self, obj): def get_change_password_username(self, obj):
return str(obj) return str(obj)
@sensitive_post_parameters_m @sensitive_post_parameters_m
def change_password(self, request, id, form_url=''): def change_password(self, request, id, form_url=''):
if not self.has_change_permission(request): if not self.has_change_permission(request):
raise PermissionDenied raise PermissionDenied()
# TODO use this insetad of self.get_object(), in other places # TODO use this insetad of self.get_object(), in other places
obj = get_object_or_404(self.get_queryset(request), pk=id) obj = get_object_or_404(self.get_queryset(request), pk=id)
raw = request.GET.get('raw', '0') == '1' raw = request.GET.get('raw', '0') == '1'
@ -281,7 +279,7 @@ class ChangePasswordAdminMixin(object):
for rel in account.get_related_passwords(db_field=raw): for rel in account.get_related_passwords(db_field=raw):
if not isinstance(obj, type(rel)): if not isinstance(obj, type(rel)):
related.append(rel) related.append(rel)
if request.method == 'POST': if request.method == 'POST':
form = self.change_password_form(obj, request.POST, related=related, raw=raw) form = self.change_password_form(obj, request.POST, related=related, raw=raw)
if form.is_valid(): if form.is_valid():
@ -293,7 +291,7 @@ class ChangePasswordAdminMixin(object):
return HttpResponseRedirect('..') return HttpResponseRedirect('..')
else: else:
form = self.change_password_form(obj, related=related, raw=raw) form = self.change_password_form(obj, related=related, raw=raw)
fieldsets = [ fieldsets = [
(obj._meta.verbose_name.capitalize(), { (obj._meta.verbose_name.capitalize(), {
'classes': ('wide',), 'classes': ('wide',),
@ -305,7 +303,7 @@ class ChangePasswordAdminMixin(object):
'classes': ('wide',), 'classes': ('wide',),
'fields': ('password_%i' % ix,) if raw else ('password1_%i' % ix, 'password2_%i' % ix) 'fields': ('password_%i' % ix,) if raw else ('password1_%i' % ix, 'password2_%i' % ix)
})) }))
obj_username = self.get_change_password_username(obj) obj_username = self.get_change_password_username(obj)
adminForm = admin.helpers.AdminForm(form, fieldsets, {}) adminForm = admin.helpers.AdminForm(form, fieldsets, {})
context = { context = {
@ -331,9 +329,9 @@ class ChangePasswordAdminMixin(object):
} }
context.update(admin.site.each_context(request)) context.update(admin.site.each_context(request))
return TemplateResponse(request, self.change_user_password_template, context) return TemplateResponse(request, self.change_user_password_template, context)
def show_hash(self, request, id): def show_hash(self, request, id):
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied()
obj = get_object_or_404(self.get_queryset(request), pk=id) obj = get_object_or_404(self.get_queryset(request), pk=id)
return HttpResponse(obj.password) return HttpResponse(obj.password)

View File

@ -145,6 +145,8 @@ DATABASES = {
} }
} }
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Password validation # Password validation
# https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators

View File

@ -1,6 +1,6 @@
from django.conf.urls import include, url from django.urls import include, path
urlpatterns = [ urlpatterns = [
url(r'', include('orchestra.urls')), path('', include('orchestra.urls')),
] ]

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.accounts.apps.AccountConfig'

View File

@ -29,6 +29,7 @@ from .models import Account
from .filters import HasTipeServerFilter from .filters import HasTipeServerFilter
@admin.register(Account)
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 = (
@ -150,7 +151,6 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
return actions return actions
admin.site.register(Account, AccountAdmin)
class AccountListAdmin(AccountAdmin): class AccountListAdmin(AccountAdmin):
@ -159,6 +159,10 @@ class AccountListAdmin(AccountAdmin):
actions = None actions = None
change_list_template = 'admin/accounts/account/select_account_list.html' change_list_template = 'admin/accounts/account/select_account_list.html'
@admin.display(
description=_("account"),
ordering='username',
)
@mark_safe @mark_safe
def select_account(self, instance): def select_account(self, instance):
# TODO get query string from request.META['QUERY_STRING'] to preserve filters # TODO get query string from request.META['QUERY_STRING'] to preserve filters
@ -168,8 +172,6 @@ class AccountListAdmin(AccountAdmin):
'plus': '<strong style="color:green; font-size:12px">+</strong>', 'plus': '<strong style="color:green; font-size:12px">+</strong>',
} }
return _('<a href="%(url)s">%(plus)s Add to %(name)s</a>') % context return _('<a href="%(url)s">%(plus)s Add to %(name)s</a>') % context
select_account.short_description = _("account")
select_account.admin_order_field = 'username'
def changelist_view(self, request, extra_context=None): def changelist_view(self, request, extra_context=None):
app_label = request.META['PATH_INFO'].split('/')[-5] app_label = request.META['PATH_INFO'].split('/')[-5]
@ -208,6 +210,10 @@ class AccountAdminMixin(object):
account = None account = None
list_select_related = ('account',) list_select_related = ('account',)
@admin.display(
description=_("active"),
ordering='is_active',
)
@mark_safe @mark_safe
def display_active(self, instance): def display_active(self, instance):
if not instance.is_active: if not instance.is_active:
@ -216,14 +222,14 @@ class AccountAdminMixin(object):
msg = _("Account disabled") msg = _("Account disabled")
return '<img style="width:13px" src="%s" alt="False" title="%s">' % (static('admin/img/inline-delete.svg'), msg) return '<img style="width:13px" src="%s" alt="False" title="%s">' % (static('admin/img/inline-delete.svg'), msg)
return '<img src="%s" alt="False">' % static('admin/img/icon-yes.svg') return '<img src="%s" alt="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): def account_link(self, instance):
account = instance.account if instance.pk else self.account account = instance.account if instance.pk else self.account
return admin_link()(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): def get_form(self, request, obj=None, **kwargs):
""" Warns user when object's account is disabled """ """ Warns user when object's account is disabled """

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.bills.apps.BillsConfig'

View File

@ -68,6 +68,9 @@ class BillLineInline(admin.TabularInline):
order_link = admin_link('order', display='pk') order_link = admin_link('order', display='pk')
@admin.display(
description=_("Total")
)
@mark_safe @mark_safe
def display_total(self, line): def display_total(self, line):
if line.pk: if line.pk:
@ -79,7 +82,6 @@ class BillLineInline(admin.TabularInline):
img = static('admin/img/icon-alert.svg') img = static('admin/img/icon-alert.svg')
return '<a href="%s" title="%s">%s <img src="%s"></img></a>' % (url, content, total, img) return '<a href="%s" title="%s">%s <img src="%s"></img></a>' % (url, content, total, img)
return '<a href="%s">%s</a>' % (url, total) return '<a href="%s">%s</a>' % (url, total)
display_total.short_description = _("Total")
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """ """ Make value input widget bigger """
@ -105,31 +107,38 @@ class ClosedBillLineInline(BillLineInline):
readonly_fields = fields readonly_fields = fields
can_delete = False can_delete = False
@admin.display(
description=_("Description")
)
@mark_safe @mark_safe
def display_description(self, line): def display_description(self, line):
descriptions = [line.description] descriptions = [line.description]
for subline in line.sublines.all(): for subline in line.sublines.all():
descriptions.append('&nbsp;' * 4 + subline.description) descriptions.append('&nbsp;' * 4 + subline.description)
return '<br>'.join(descriptions) return '<br>'.join(descriptions)
display_description.short_description = _("Description")
@admin.display(
description=_("Subtotal")
)
@mark_safe @mark_safe
def display_subtotal(self, line): def display_subtotal(self, line):
subtotals = ['&nbsp;' + str(line.subtotal)] subtotals = ['&nbsp;' + str(line.subtotal)]
for subline in line.sublines.all(): for subline in line.sublines.all():
subtotals.append(str(subline.total)) subtotals.append(str(subline.total))
return '<br>'.join(subtotals) return '<br>'.join(subtotals)
display_subtotal.short_description = _("Subtotal")
@admin.display(
description=_("Total")
)
def display_total(self, line): def display_total(self, line):
if line.pk: if line.pk:
return line.compute_total() return line.compute_total()
display_total.short_description = _("Total")
def has_add_permission(self, request, obj): def has_add_permission(self, request, obj):
return False return False
@admin.register(BillLine)
class BillLineAdmin(admin.ModelAdmin): class BillLineAdmin(admin.ModelAdmin):
list_display = ( list_display = (
'description', 'bill_link', 'display_is_open', 'account_link', 'rate', 'quantity', 'description', 'bill_link', 'display_is_open', 'account_link', 'rate', 'quantity',
@ -164,21 +173,27 @@ class BillLineAdmin(admin.ModelAdmin):
order_link = admin_link('order') order_link = admin_link('order')
amended_line_link = admin_link('amended_line') amended_line_link = admin_link('amended_line')
@admin.display(
description=_("Is open"),
boolean=True,
)
def display_is_open(self, instance): def display_is_open(self, instance):
return instance.bill.is_open 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): def display_sublinetotal(self, instance):
total = instance.subline_total total = instance.subline_total
return total if total is not None else '---' 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): def display_total(self, instance):
return round(instance.computed_total or 0, 2) 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): def get_readonly_fields(self, request, obj=None):
fields = super().get_readonly_fields(request, obj) fields = super().get_readonly_fields(request, obj)
@ -242,6 +257,10 @@ class BillLineManagerAdmin(BillLineAdmin):
class BillAdminMixin(AccountAdminMixin): class BillAdminMixin(AccountAdminMixin):
@admin.display(
description=_("total"),
ordering='approx_total',
)
@mark_safe @mark_safe
def display_total_with_subtotals(self, bill): def display_total_with_subtotals(self, bill):
if bill.pk: if bill.pk:
@ -252,9 +271,10 @@ class BillAdminMixin(AccountAdminMixin):
subtotals.append(_("Taxes %s%% VAT %s &%s;") % (tax, subtotal[1], currency)) subtotals.append(_("Taxes %s%% VAT %s &%s;") % (tax, subtotal[1], currency))
subtotals = '\n'.join(subtotals) subtotals = '\n'.join(subtotals)
return '<span title="%s">%s &%s;</span>' % (subtotals, bill.compute_total(), currency) return '<span title="%s">%s &%s;</span>' % (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 @mark_safe
def display_payment_state(self, bill): def display_payment_state(self, bill):
if bill.pk: if bill.pk:
@ -277,7 +297,6 @@ class BillAdminMixin(AccountAdminMixin):
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey') color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format( return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format(
url=url, color=color, name=state, title=title) url=url, color=color, name=state, title=title)
display_payment_state.short_description = _("Payment")
def get_queryset(self, request): def get_queryset(self, request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
@ -311,6 +330,7 @@ class AmendInline(BillAdminMixin, admin.TabularInline):
return False return False
@admin.register(AbonoInvoice, AmendmentFee, AmendmentInvoice, Bill, Fee, Invoice, ProForma)
class BillAdmin(BillAdminMixin, ExtendedModelAdmin): class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'number', 'type_link', 'account_link', 'closed_on_display', 'updated_on_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.short_description = _("Amends")
# amend_links.allow_tags = True # amend_links.allow_tags = True
@admin.display(
description=_("lines"),
ordering='lines__count',
)
def num_lines(self, bill): def num_lines(self, bill):
return bill.lines__count 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): def display_total(self, bill):
currency = settings.BILLS_CURRENCY.lower() currency = settings.BILLS_CURRENCY.lower()
return format_html('{} &{};', bill.compute_total(), currency) 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): def type_link(self, bill):
bill_type = bill.type.lower() bill_type = bill.type.lower()
url = reverse('admin:bills_%s_changelist' % bill_type) url = reverse('admin:bills_%s_changelist' % bill_type)
return format_html('<a href="{}">{}</a>', url, bill.get_type_display()) return format_html('<a href="{}">{}</a>', url, bill.get_type_display())
type_link.short_description = _("type")
type_link.admin_order_field = 'type'
def get_urls(self): def get_urls(self):
""" Hook bill lines management URLs on bill admin """ """ Hook bill lines management URLs on bill admin """
@ -456,14 +482,6 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
return super().change_view(request, object_id, **kwargs) 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): class BillContactInline(admin.StackedInline):
@ -481,10 +499,12 @@ class BillContactInline(admin.StackedInline):
return super().formfield_for_dbfield(db_field, **kwargs) return super().formfield_for_dbfield(db_field, **kwargs)
@admin.display(
boolean=True,
ordering='billcontact',
)
def has_bill_contact(account): def has_bill_contact(account):
return hasattr(account, 'billcontact') return hasattr(account, 'billcontact')
has_bill_contact.boolean = True
has_bill_contact.admin_order_field = 'billcontact'
insertattr(AccountAdmin, 'inlines', BillContactInline) insertattr(AccountAdmin, 'inlines', BillContactInline)

View File

@ -18,7 +18,7 @@ class BillViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
@action(detail=True, methods=['get']) @action(detail=True, methods=['get'])
def document(self, request, pk): def document(self, request, pk):
bill = self.get_object() bill = self.get_object()
content_type = request.META.get('HTTP_ACCEPT') content_type = request.headers.get('accept')
if content_type == 'application/pdf': if content_type == 'application/pdf':
pdf = html_to_pdf(bill.html or bill.render()) pdf = html_to_pdf(bill.html or bill.render())
return HttpResponse(pdf, content_type='application/pdf') return HttpResponse(pdf, content_type='application/pdf')

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.contacts.apps.ContactsConfig'

View File

@ -13,6 +13,7 @@ from .filters import EmailUsageListFilter
from .models import Contact from .models import Contact
@admin.register(Contact)
class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin): class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link' 'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link'
@ -62,10 +63,12 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
) )
actions = (SendEmail(), list_accounts) actions = (SendEmail(), list_accounts)
@admin.display(
description=_("Name"),
ordering='short_name',
)
def dispaly_name(self, contact): def dispaly_name(self, contact):
return str(contact) return str(contact)
dispaly_name.short_description = _("Name")
dispaly_name.admin_order_field = 'short_name'
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """ """ Make value input widget bigger """
@ -76,7 +79,6 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs) return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs)
admin.site.register(Contact, ContactAdmin)
class ContactInline(admin.StackedInline): class ContactInline(admin.StackedInline):

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.databases.apps.DatabasesConfig'

View File

@ -14,11 +14,14 @@ from .filters import HasUserListFilter, HasDatabaseListFilter
from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm, DatabaseForm from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm, DatabaseForm
from .models import Database, DatabaseUser from .models import Database, DatabaseUser
@admin.action(
description="Re-save selected objects"
)
def save_selected(modeladmin, request, queryset): def save_selected(modeladmin, request, queryset):
for selected in queryset: for selected in queryset:
selected.save() selected.save()
save_selected.short_description = "Re-save selected objects"
@admin.register(Database)
class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'type', 'target_server', 'display_users', 'account_link') list_display = ('name', 'type', 'target_server', 'display_users', 'account_link')
list_filter = ('type', HasUserListFilter) list_filter = ('type', HasUserListFilter)
@ -51,6 +54,10 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_horizontal = ['users'] filter_horizontal = ['users']
actions = (list_accounts, save_selected) actions = (list_accounts, save_selected)
@admin.display(
description=_("Users"),
ordering='users__username',
)
@mark_safe @mark_safe
def display_users(self, db): def display_users(self, db):
links = [] links = []
@ -58,8 +65,6 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
link = format_html('<a href="{}">{}</a>', change_url(user), user.username) link = format_html('<a href="{}">{}</a>', change_url(user), user.username)
links.append(link) links.append(link)
return '<br>'.join(links) return '<br>'.join(links)
display_users.short_description = _("Users")
display_users.admin_order_field = 'users__username'
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
super(DatabaseAdmin, self).save_model(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) obj.users.add(user)
@admin.register(DatabaseUser)
class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin): class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin):
list_display = ('username', 'target_server', 'type', 'display_databases', 'account_link') list_display = ('username', 'target_server', 'type', 'display_databases', 'account_link')
list_filter = ('type', HasDatabaseListFilter) list_filter = ('type', HasDatabaseListFilter)
@ -101,6 +107,10 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten
list_prefetch_related = ('databases',) list_prefetch_related = ('databases',)
actions = (list_accounts, save_selected) actions = (list_accounts, save_selected)
@admin.display(
description=_("Databases"),
ordering='databases__name',
)
@mark_safe @mark_safe
def display_databases(self, user): def display_databases(self, user):
links = [] links = []
@ -108,8 +118,6 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten
link = format_html('<a href="{}">{}</a>', change_url(db), db.name) link = format_html('<a href="{}">{}</a>', change_url(db), db.name)
links.append(link) links.append(link)
return '<br>'.join(links) return '<br>'.join(links)
display_databases.short_description = _("Databases")
display_databases.admin_order_field = 'databases__name'
def get_urls(self): def get_urls(self):
useradmin = UserAdmin(DatabaseUser, self.admin_site) useradmin = UserAdmin(DatabaseUser, self.admin_site)
@ -125,5 +133,3 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten
super(DatabaseUserAdmin, self).save_model(request, obj, form, change) super(DatabaseUserAdmin, self).save_model(request, obj, form, change)
admin.site.register(Database, DatabaseAdmin)
admin.site.register(DatabaseUser, DatabaseUserAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.domains.apps.DomainsConfig'

View File

@ -39,9 +39,11 @@ class DomainInline(admin.TabularInline):
domain_link.short_description = _("Name") domain_link.short_description = _("Name")
account_link = admin_link('account') account_link = admin_link('account')
@admin.display(
description=_("Declared records")
)
def display_records(self, domain): def display_records(self, domain):
return ', '.join([record.type for record in domain.records.all()]) return ', '.join([record.type for record in domain.records.all()])
display_records.short_description = _("Declared records")
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
return False return False
@ -52,6 +54,7 @@ class DomainInline(admin.TabularInline):
return qs.select_related('account').prefetch_related('records') return qs.select_related('account').prefetch_related('records')
@admin.register(Domain)
class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'structured_name', 'display_is_top', 'display_websites', 'display_addresses', 'account_link' 'structured_name', 'display_is_top', 'display_websites', 'display_addresses', 'account_link'
@ -71,19 +74,27 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
top_link = admin_link('top') top_link = admin_link('top')
@admin.display(
description=_("name"),
ordering='structured_name',
)
def structured_name(self, domain): def structured_name(self, domain):
if domain.is_top: if domain.is_top:
return domain.name return domain.name
return mark_safe('&nbsp;'*4 + domain.name) return mark_safe('&nbsp;'*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): def display_is_top(self, domain):
return domain.is_top 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 @mark_safe
def display_websites(self, domain): def display_websites(self, domain):
if apps.isinstalled('orchestra.contrib.websites'): if apps.isinstalled('orchestra.contrib.websites'):
@ -106,9 +117,11 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
) )
return _("No website %s") % (add_link) return _("No website %s") % (add_link)
return '---' return '---'
display_websites.admin_order_field = 'websites__name'
display_websites.short_description = _("Websites")
@admin.display(
description=_("Addresses"),
ordering='addresses__count',
)
@mark_safe @mark_safe
def display_addresses(self, domain): def display_addresses(self, domain):
if apps.isinstalled('orchestra.contrib.mailboxes'): if apps.isinstalled('orchestra.contrib.mailboxes'):
@ -126,9 +139,10 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
return '<a href="%s" title="%s">%s</a> %s' % (url, title, len(addresses), add_link) return '<a href="%s" title="%s">%s</a> %s' % (url, title, len(addresses), add_link)
return _("No address %s") % (add_link) return _("No address %s") % (add_link)
return '---' return '---'
display_addresses.short_description = _("Addresses")
display_addresses.admin_order_field = 'addresses__count'
@admin.display(
description=_("Implicit records")
)
@mark_safe @mark_safe
def implicit_records(self, domain): def implicit_records(self, domain):
types = set(domain.records.values_list('type', flat=True)) types = set(domain.records.values_list('type', flat=True))
@ -148,7 +162,6 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
else: else:
lines.append(line) lines.append(line)
return '<br>'.join(lines) return '<br>'.join(lines)
implicit_records.short_description = _("Implicit records")
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
""" Add SOA fields when domain is top """ """ Add SOA fields when domain is top """
@ -224,4 +237,3 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
self.save_formset(request, form, formset, change) self.save_formset(request, form, formset, change)
admin.site.register(Domain, DomainAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.history.apps.HistoryConfig'

View File

@ -36,6 +36,10 @@ class LogEntryAdmin(admin.ModelAdmin):
user_link = admin_link('user') user_link = admin_link('user')
display_action_time = admin_date('action_time', short_description=_("Time")) display_action_time = admin_date('action_time', short_description=_("Time"))
@admin.display(
description=_("Message"),
ordering='action_flag',
)
@mark_safe @mark_safe
def display_message(self, log): def display_message(self, log):
edit = format_html('<a href="{url}"><img src="{img}"></img></a>', **{ edit = format_html('<a href="{url}"><img src="{img}"></img></a>', **{
@ -58,18 +62,22 @@ class LogEntryAdmin(admin.ModelAdmin):
'object': log.object_repr, 'object': log.object_repr,
'edit': edit, '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): def display_action(self, log):
if log.is_addition(): if log.is_addition():
return _("Added") return _("Added")
elif log.is_change(): elif log.is_change():
return _("Changed") return _("Changed")
return _("Deleted") 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): def content_object_link(self, log):
ct = log.content_type ct = log.content_type
view = 'admin:%s_%s_change' % (ct.app_label, ct.model) view = 'admin:%s_%s_change' % (ct.app_label, ct.model)
@ -78,8 +86,6 @@ class LogEntryAdmin(admin.ModelAdmin):
except NoReverseMatch: except NoReverseMatch:
return log.object_repr return log.object_repr
return format_html('<a href="{}">{}</a>', url, log.object_repr) return format_html('<a href="{}">{}</a>', 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): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
""" Add rel_opts and object to context """ """ Add rel_opts and object to context """

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.issues.apps.IssuesConfig'

View File

@ -51,6 +51,9 @@ class MessageReadOnlyInline(admin.TabularInline):
'all': ('orchestra/css/hide-inline-id.css',) 'all': ('orchestra/css/hide-inline-id.css',)
} }
@admin.display(
description=_("Content")
)
@mark_safe @mark_safe
def content_html(self, msg): def content_html(self, msg):
context = { context = {
@ -66,7 +69,6 @@ class MessageReadOnlyInline(admin.TabularInline):
content = '<div style="padding-left:20px;">%s</div>' % content content = '<div style="padding-left:20px;">%s</div>' % content
return header + content return header + content
content_html.short_description = _("Content")
def has_add_permission(self, request, obj): def has_add_permission(self, request, obj):
return False return False
@ -114,12 +116,15 @@ class TicketInline(admin.TabularInline):
colored_state = admin_colored('state', colors=STATE_COLORS, bold=False) colored_state = admin_colored('state', colors=STATE_COLORS, bold=False)
colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False) colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
@admin.display(
description='#'
)
@mark_safe @mark_safe
def ticket_id(self, instance): def ticket_id(self, instance):
return '<b>%s</b>' % admin_link()(instance) return '<b>%s</b>' % admin_link()(instance)
ticket_id.short_description = '#'
@admin.register(Ticket)
class TicketAdmin(ExtendedModelAdmin): class TicketAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'unbold_id', 'bold_subject', 'display_creator', 'display_owner', '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_state = admin_colored('state', colors=STATE_COLORS, bold=False)
display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False) display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
@admin.display(
description='Summary'
)
@mark_safe @mark_safe
def display_summary(self, ticket): def display_summary(self, ticket):
context = { context = {
@ -210,23 +218,26 @@ class TicketAdmin(ExtendedModelAdmin):
}) })
context['updated'] = '. Updated by %(updater)s about %(updated)s' % context context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
display_summary.short_description = 'Summary'
@admin.display(
description="#",
ordering='id',
)
def unbold_id(self, ticket): def unbold_id(self, ticket):
""" Unbold id if ticket is read """ """ Unbold id if ticket is read """
if ticket.is_read_by(self.user): if ticket.is_read_by(self.user):
return format_html('<span style="font-weight:normal;font-size:11px;">{}</span>', ticket.pk) return format_html('<span style="font-weight:normal;font-size:11px;">{}</span>', ticket.pk)
return 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): def bold_subject(self, ticket):
""" Bold subject when tickets are unread for request.user """ """ Bold subject when tickets are unread for request.user """
if ticket.is_read_by(self.user): if ticket.is_read_by(self.user):
return ticket.subject return ticket.subject
return format_html("<strong class='unread'>{}</strong>", ticket.subject) return format_html("<strong class='unread'>{}</strong>", ticket.subject)
bold_subject.short_description = _("Subject")
bold_subject.admin_order_field = 'subject'
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """ """ Make value input widget bigger """
@ -283,6 +294,7 @@ class TicketAdmin(ExtendedModelAdmin):
return HttpResponse(data_formated) return HttpResponse(data_formated)
@admin.register(Queue)
class QueueAdmin(admin.ModelAdmin): class QueueAdmin(admin.ModelAdmin):
list_display = ('name', 'default', 'num_tickets') list_display = ('name', 'default', 'num_tickets')
actions = (set_default_queue,) actions = (set_default_queue,)
@ -294,13 +306,15 @@ class QueueAdmin(admin.ModelAdmin):
'all': ('orchestra/css/hide-inline-id.css',) 'all': ('orchestra/css/hide-inline-id.css',)
} }
@admin.display(
description=_("Tickets"),
ordering='tickets__count',
)
def num_tickets(self, queue): def num_tickets(self, queue):
num = queue.tickets__count num = queue.tickets__count
url = reverse('admin:issues_ticket_changelist') url = reverse('admin:issues_ticket_changelist')
url += '?queue=%i' % queue.pk url += '?queue=%i' % queue.pk
return format_html('<a href="{}">{}</a>', url, num) return format_html('<a href="{}">{}</a>', url, num)
num_tickets.short_description = _("Tickets")
num_tickets.admin_order_field = 'tickets__count'
def get_list_display(self, request): def get_list_display(self, request):
""" show notifications """ """ show notifications """
@ -319,5 +333,3 @@ class QueueAdmin(admin.ModelAdmin):
return qs return qs
admin.site.register(Ticket, TicketAdmin)
admin.site.register(Queue, QueueAdmin)

View File

@ -10,6 +10,9 @@ from .helpers import is_valid_domain, read_live_lineages, configure_cert
from .forms import LetsEncryptForm from .forms import LetsEncryptForm
@admin.action(
description="Let's encrypt!"
)
def letsencrypt(modeladmin, request, queryset): def letsencrypt(modeladmin, request, queryset):
wildcards = set() wildcards = set()
domains = set() domains = set()
@ -112,4 +115,3 @@ def letsencrypt(modeladmin, request, queryset):
'form': form, 'form': form,
} }
return TemplateResponse(request, 'admin/orchestra/generic_confirmation.html', context) return TemplateResponse(request, 'admin/orchestra/generic_confirmation.html', context)
letsencrypt.short_description = "Let's encrypt!"

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.lists.apps.ListsConfig'

View File

@ -16,6 +16,7 @@ from .filters import HasCustomAddressListFilter
from .models import List from .models import List
@admin.register(List)
class ListAdmin(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'
@ -56,4 +57,3 @@ class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
address_domain_link = admin_link('address_domain', order='address_domain__name') address_domain_link = admin_link('address_domain', order='address_domain__name')
admin.site.register(List, ListAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.mailboxes.apps.MailboxesConfig'

View File

@ -83,6 +83,9 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
if settings.MAILBOXES_LOCAL_DOMAIN: if settings.MAILBOXES_LOCAL_DOMAIN:
type(self).actions = self.actions + (SendMailboxEmail(),) type(self).actions = self.actions + (SendMailboxEmail(),)
@admin.display(
description=_("Addresses")
)
@mark_safe @mark_safe
def display_addresses(self, mailbox): def display_addresses(self, mailbox):
# Get from forwards # Get from forwards
@ -111,21 +114,24 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
url = change_url(addr) url = change_url(addr)
addresses.append(format_html('<a href="{}">{}</a>', url, addr.email)) addresses.append(format_html('<a href="{}">{}</a>', url, addr.email))
return '<br>'.join(addresses+forwards) return '<br>'.join(addresses+forwards)
display_addresses.short_description = _("Addresses")
@admin.display(
description=_("Forward from")
)
def display_forwards(self, mailbox): def display_forwards(self, mailbox):
forwards = mailbox.get_forwards() forwards = mailbox.get_forwards()
return format_html_join( return format_html_join(
'<br>', '<a href="{}">{}</a>', '<br>', '<a href="{}">{}</a>',
[(change_url(addr), addr.email) for addr in forwards] [(change_url(addr), addr.email) for addr in forwards]
) )
display_forwards.short_description = _("Forward from")
@admin.display(
description=_("Filtering"),
ordering='filtering',
)
@mark_safe @mark_safe
def display_filtering(self, mailbox): def display_filtering(self, mailbox):
return mailbox.get_filtering_display() return mailbox.get_filtering_display()
display_filtering.short_description = _("Filtering")
display_filtering.admin_order_field = 'filtering'
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'filtering': if db_field.name == 'filtering':
@ -221,6 +227,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
obj.addresses.set(form.cleaned_data['addresses']) obj.addresses.set(form.cleaned_data['addresses'])
@admin.register(Address)
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'display_email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward', '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') domain_link = admin_link('domain', order='domain__name')
@admin.display(
description=_("Email"),
ordering='computed_email',
)
def display_email(self, address): def display_email(self, address):
return address.computed_email 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): def email_link(self, address):
link = self.domain_link(address) link = self.domain_link(address)
return format_html("{}@{}", address.name, link) return format_html("{}@{}", address.name, link)
email_link.short_description = _("Email")
@admin.display(
description=_("Mailboxes"),
ordering='mailboxes__count',
)
def display_mailboxes(self, address): def display_mailboxes(self, address):
boxes = address.mailboxes.all() boxes = address.mailboxes.all()
return format_html_join( return format_html_join(
mark_safe('<br>'), '<a href="{}">{}</a>', mark_safe('<br>'), '<a href="{}">{}</a>',
[(change_url(mailbox), mailbox.name) for mailbox in boxes] [(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): def display_all_mailboxes(self, address):
boxes = address.get_mailboxes() boxes = address.get_mailboxes()
return format_html_join( return format_html_join(
mark_safe('<br>'), '<a href="{}">{}</a>', mark_safe('<br>'), '<a href="{}">{}</a>',
[(change_url(mailbox), mailbox.name) for mailbox in boxes] [(change_url(mailbox), mailbox.name) for mailbox in boxes]
) )
display_all_mailboxes.short_description = _("Mailboxes links")
@admin.display(
description=_("Forward"),
ordering='forward',
)
@mark_safe @mark_safe
def display_forward(self, address): def display_forward(self, address):
forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()} forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()}
@ -279,8 +298,6 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
else: else:
values.append(forward) values.append(forward)
return '<br>'.join(values) return '<br>'.join(values)
display_forward.short_description = _("Forward")
display_forward.admin_order_field = 'forward'
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'forward': if db_field.name == 'forward':
@ -326,4 +343,3 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
admin.site.register(Mailbox, MailboxAdmin) admin.site.register(Mailbox, MailboxAdmin)
admin.site.register(Address, AddressAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.mailer.apps.MailerConfig'

View File

@ -29,6 +29,7 @@ COLORS = {
} }
@admin.register(Message)
class MessageAdmin(ExtendedModelAdmin): class MessageAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'display_subject', 'colored_state', 'priority', 'to_address', 'from_address', 'display_subject', 'colored_state', 'priority', 'to_address', 'from_address',
@ -59,14 +60,20 @@ class MessageAdmin(ExtendedModelAdmin):
created_at_delta = admin_date('created_at') created_at_delta = admin_date('created_at')
last_try_delta = admin_date('last_try') last_try_delta = admin_date('last_try')
@admin.display(
description=_("Subject"),
ordering='subject',
)
def display_subject(self, instance): def display_subject(self, instance):
subject = instance.subject subject = instance.subject
if len(subject) > 64: if len(subject) > 64:
return mark_safe(subject[:64] + '&hellip;') return mark_safe(subject[:64] + '&hellip;')
return subject return subject
display_subject.short_description = _("Subject")
display_subject.admin_order_field = 'subject'
@admin.display(
description=_("Retries"),
ordering='retries',
)
def display_retries(self, instance): def display_retries(self, instance):
num_logs = instance.logs__count num_logs = instance.logs__count
if num_logs == 1: if num_logs == 1:
@ -76,9 +83,10 @@ class MessageAdmin(ExtendedModelAdmin):
url = reverse('admin:mailer_smtplog_changelist') url = reverse('admin:mailer_smtplog_changelist')
url += '?&message=%i' % instance.pk url += '?&message=%i' % instance.pk
return format_html('<a href="{}" onclick="return showAddAnotherPopup(this);">{}</a>', url, instance.retries) return format_html('<a href="{}" onclick="return showAddAnotherPopup(this);">{}</a>', url, instance.retries)
display_retries.short_description = _("Retries")
display_retries.admin_order_field = 'retries'
@admin.display(
description=_("Content")
)
def display_content(self, instance): def display_content(self, instance):
part = email.message_from_string(instance.content) part = email.message_from_string(instance.content)
payload = part.get_payload() payload = part.get_payload()
@ -100,19 +108,24 @@ class MessageAdmin(ExtendedModelAdmin):
if part.get_content_type() == 'text/plain': if part.get_content_type() == 'text/plain':
payload = payload.replace('\n', '<br>').replace(' ', '&nbsp;') payload = payload.replace('\n', '<br>').replace(' ', '&nbsp;')
return mark_safe(payload) return mark_safe(payload)
display_content.short_description = _("Content")
@admin.display(
description=_("Subject")
)
def display_full_subject(self, instance): def display_full_subject(self, instance):
return instance.subject return instance.subject
display_full_subject.short_description = _("Subject")
@admin.display(
description=_("From")
)
def display_from(self, instance): def display_from(self, instance):
return instance.from_address return instance.from_address
display_from.short_description = _("From")
@admin.display(
description=_("To")
)
def display_to(self, instance): def display_to(self, instance):
return instance.to_address return instance.to_address
display_to.short_description = _("To")
def get_urls(self): def get_urls(self):
from django.urls import re_path as url from django.urls import re_path as url
@ -140,6 +153,7 @@ class MessageAdmin(ExtendedModelAdmin):
return super().formfield_for_dbfield(db_field, **kwargs) return super().formfield_for_dbfield(db_field, **kwargs)
@admin.register(SMTPLog)
class SMTPLogAdmin(admin.ModelAdmin): class SMTPLogAdmin(admin.ModelAdmin):
list_display = ( list_display = (
'id', 'message_link', 'colored_result', 'date_delta', 'log_message' 'id', 'message_link', 'colored_result', 'date_delta', 'log_message'
@ -153,5 +167,3 @@ class SMTPLogAdmin(admin.ModelAdmin):
date_delta = admin_date('date') date_delta = admin_date('date')
admin.site.register(Message, MessageAdmin)
admin.site.register(SMTPLog, SMTPLogAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.miscellaneous.apps.MiscellaneousConfig'

View File

@ -25,6 +25,7 @@ class MiscServicePlugin(PluginModelAdapter):
plugin_field = 'service' plugin_field = 'service'
@admin.register(MiscService)
class MiscServiceAdmin(ExtendedModelAdmin): class MiscServiceAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'display_name', 'display_verbose_name', 'num_instances', 'has_identifier', 'has_amount', 'is_active' 'display_name', 'display_verbose_name', 'num_instances', 'has_identifier', 'has_amount', 'is_active'
@ -38,24 +39,30 @@ class MiscServiceAdmin(ExtendedModelAdmin):
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
actions = (disable, enable) actions = (disable, enable)
@admin.display(
description=_("name"),
ordering='name',
)
def display_name(self, misc): def display_name(self, misc):
return format_html('<span title="{}">{}</span>', misc.description, misc.name) return format_html('<span title="{}">{}</span>', 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): def display_verbose_name(self, misc):
return format_html('<span title="{}">{}</span>', misc.description, misc.verbose_name) return format_html('<span title="{}">{}</span>', 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): def num_instances(self, misc):
""" return num slivers as a link to slivers changelist view """ """ return num slivers as a link to slivers changelist view """
num = misc.instances__count num = misc.instances__count
url = reverse('admin:miscellaneous_miscellaneous_changelist') url = reverse('admin:miscellaneous_miscellaneous_changelist')
url += '?service__name={}'.format(misc.name) url += '?service__name={}'.format(misc.name)
return mark_safe('<a href="{0}">{1}</a>'.format(url, num)) return mark_safe('<a href="{0}">{1}</a>'.format(url, num))
num_instances.short_description = _("Instances")
num_instances.admin_order_field = 'instances__count'
def get_queryset(self, request): def get_queryset(self, request):
qs = super(MiscServiceAdmin, self).get_queryset(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) return super(MiscServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
@admin.register(Miscellaneous)
class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'__str__', 'service_link', 'amount', 'account_link', 'dispaly_active' '__str__', 'service_link', 'amount', 'account_link', 'dispaly_active'
@ -85,11 +93,13 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode
service_link = admin_link('service') service_link = admin_link('service')
@admin.display(
description=_("Active"),
boolean=True,
ordering='is_active',
)
def dispaly_active(self, instance): def dispaly_active(self, instance):
return instance.active return instance.active
dispaly_active.short_description = _("Active")
dispaly_active.boolean = True
dispaly_active.admin_order_field = 'is_active'
def get_service(self, obj): def get_service(self, obj):
if obj is None: if obj is None:
@ -146,5 +156,3 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode
obj.save() obj.save()
admin.site.register(MiscService, MiscServiceAdmin)
admin.site.register(Miscellaneous, MiscellaneousAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.musician.apps.MusicianConfig'

View File

@ -160,25 +160,25 @@ msgstr "Cancel·lar"
msgid "Save" msgid "Save"
msgstr "Desar" msgstr "Desar"
#: templates/musician/addresses.html:15 #: templates/musician/address_list.html:15
msgid "Email" msgid "Email"
msgstr "Correu electrònic" msgstr "Correu electrònic"
#: templates/musician/addresses.html:16 #: templates/musician/address_list.html:16
msgid "Domain" msgid "Domain"
msgstr "Domini" msgstr "Domini"
#. Translators: This message appears on the page title #. 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 #: views.py:355
msgid "Mailboxes" msgid "Mailboxes"
msgstr "Bústies de correu" msgstr "Bústies de correu"
#: templates/musician/addresses.html:18 #: templates/musician/address_list.html:18
msgid "Forward" msgid "Forward"
msgstr "Redirecció" msgstr "Redirecció"
#: templates/musician/addresses.html:38 #: templates/musician/address_list.html:38
msgid "New mail address" msgid "New mail address"
msgstr "Nova adreça de correu" 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: database page when there isn't any database.
#. Translators: saas page when there isn't any saas. #. 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!" msgid "Ooops! Looks like there is nothing here!"
msgstr "Mmmh, sembla que aquí no hi ha res!" msgstr "Mmmh, sembla que aquí no hi ha res!"
@ -370,15 +370,15 @@ msgstr "Consulta aquí la teva configuració DNS."
msgid "Value" msgid "Value"
msgstr "Valor" 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" msgid "Go to global"
msgstr "Totes les adreces" 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" msgid "for"
msgstr "per a" 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" msgid "Addresses"
msgstr "Adreces de correu" msgstr "Adreces de correu"
@ -415,27 +415,27 @@ msgstr ""
msgid "Close" msgid "Close"
msgstr "Tancar" msgstr "Tancar"
#: templates/musician/mailboxes.html:14 #: templates/musician/mailbox_list.html:14
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
#: templates/musician/mailboxes.html:15 #: templates/musician/mailbox_list.html:15
msgid "Filtering" msgid "Filtering"
msgstr "Filtrat" msgstr "Filtrat"
#: templates/musician/mailboxes.html:27 #: templates/musician/mailbox_list.html:27
msgid "Update password" msgid "Update password"
msgstr "Actualitza la contrasenya" msgstr "Actualitza la contrasenya"
#: templates/musician/mailboxes.html:43 #: templates/musician/mailbox_list.html:43
msgid "New mailbox" msgid "New mailbox"
msgstr "Nova bústia de correu" msgstr "Nova bústia de correu"
#: templates/musician/mailinglists.html:34 #: templates/musician/mailinglist_list.html:34
msgid "Active" msgid "Active"
msgstr "Actiu" msgstr "Actiu"
#: templates/musician/mailinglists.html:36 #: templates/musician/mailinglist_list.html:36
msgid "Inactive" msgid "Inactive"
msgstr "Inactiu" msgstr "Inactiu"
@ -463,19 +463,19 @@ msgstr "mètode de pagament:"
msgid "Check your last bills" msgid "Check your last bills"
msgstr "Consulta les teves darreres factures" msgstr "Consulta les teves darreres factures"
#: templates/musician/saas.html:18 #: templates/musician/saas_list.html:18
msgid "Installed on" msgid "Installed on"
msgstr "Instal·lat a" msgstr "Instal·lat a"
#: templates/musician/saas.html:29 #: templates/musician/saas_list.html:29
msgid "Service info" msgid "Service info"
msgstr "Informació del servei" msgstr "Informació del servei"
#: templates/musician/saas.html:30 #: templates/musician/saas_list.html:30
msgid "active" msgid "active"
msgstr "actiu" msgstr "actiu"
#: templates/musician/saas.html:37 #: templates/musician/saas_list.html:37
msgid "Open service admin panel" msgid "Open service admin panel"
msgstr "Obre el panell dadministració del servei" msgstr "Obre el panell dadministració del servei"

View File

@ -162,25 +162,25 @@ msgstr "Cancelar"
msgid "Save" msgid "Save"
msgstr "Guardar" msgstr "Guardar"
#: templates/musician/addresses.html:15 #: templates/musician/address_list.html:15
msgid "Email" msgid "Email"
msgstr "Correo electrónico" msgstr "Correo electrónico"
#: templates/musician/addresses.html:16 #: templates/musician/address_list.html:16
msgid "Domain" msgid "Domain"
msgstr "Dominio" msgstr "Dominio"
#. Translators: This message appears on the page title #. 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 #: views.py:355
msgid "Mailboxes" msgid "Mailboxes"
msgstr "Buzones de correo" msgstr "Buzones de correo"
#: templates/musician/addresses.html:18 #: templates/musician/address_list.html:18
msgid "Forward" msgid "Forward"
msgstr "Redirección" msgstr "Redirección"
#: templates/musician/addresses.html:38 #: templates/musician/address_list.html:38
msgid "New mail address" msgid "New mail address"
msgstr "Nueva dirección de correo" 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: database page when there isn't any database.
#. Translators: saas page when there isn't any saas. #. 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!" msgid "Ooops! Looks like there is nothing here!"
msgstr "Mmmh… ¡parece que aquí no hay nada!" msgstr "Mmmh… ¡parece que aquí no hay nada!"
@ -372,15 +372,15 @@ msgstr "Consulta aquí tu configuración DNS."
msgid "Value" msgid "Value"
msgstr "Valor" 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" msgid "Go to global"
msgstr "Todas las direcciones" 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" msgid "for"
msgstr "para" 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" msgid "Addresses"
msgstr "Direcciones de correo" msgstr "Direcciones de correo"
@ -417,27 +417,27 @@ msgstr ""
msgid "Close" msgid "Close"
msgstr "Cerrar" msgstr "Cerrar"
#: templates/musician/mailboxes.html:14 #: templates/musician/mailbox_list.html:14
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: templates/musician/mailboxes.html:15 #: templates/musician/mailbox_list.html:15
msgid "Filtering" msgid "Filtering"
msgstr "Filtrado" msgstr "Filtrado"
#: templates/musician/mailboxes.html:27 #: templates/musician/mailbox_list.html:27
msgid "Update password" msgid "Update password"
msgstr "Actualiza la contraseña" msgstr "Actualiza la contraseña"
#: templates/musician/mailboxes.html:43 #: templates/musician/mailbox_list.html:43
msgid "New mailbox" msgid "New mailbox"
msgstr "Nuevo buzón de correo" msgstr "Nuevo buzón de correo"
#: templates/musician/mailinglists.html:34 #: templates/musician/mailinglist_list.html:34
msgid "Active" msgid "Active"
msgstr "Activo" msgstr "Activo"
#: templates/musician/mailinglists.html:36 #: templates/musician/mailinglist_list.html:36
msgid "Inactive" msgid "Inactive"
msgstr "Inactivo" msgstr "Inactivo"
@ -465,19 +465,19 @@ msgstr "método de pago:"
msgid "Check your last bills" msgid "Check your last bills"
msgstr "Consulta tus últimas facturas" msgstr "Consulta tus últimas facturas"
#: templates/musician/saas.html:18 #: templates/musician/saas_list.html:18
msgid "Installed on" msgid "Installed on"
msgstr "Instalado en" msgstr "Instalado en"
#: templates/musician/saas.html:29 #: templates/musician/saas_list.html:29
msgid "Service info" msgid "Service info"
msgstr "Información del servicio" msgstr "Información del servicio"
#: templates/musician/saas.html:30 #: templates/musician/saas_list.html:30
msgid "active" msgid "active"
msgstr "activo" msgstr "activo"
#: templates/musician/saas.html:37 #: templates/musician/saas_list.html:37
msgid "Open service admin panel" msgid "Open service admin panel"
msgstr "Abre el panel de administración del servicio" msgstr "Abre el panel de administración del servicio"

View File

@ -12,7 +12,7 @@ from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils import translation from django.utils import translation
from django.utils.html import format_html 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.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.generic.base import RedirectView, TemplateView from django.views.generic.base import RedirectView, TemplateView
@ -153,7 +153,7 @@ def profile_set_language(request, code):
translation.activate(user_language) translation.activate(user_language)
redirect_to = request.GET.get('next', '') redirect_to = request.GET.get('next', '')
url_is_safe = is_safe_url( url_is_safe = url_has_allowed_host_and_scheme(
url=redirect_to, url=redirect_to,
allowed_hosts={request.get_host()}, allowed_hosts={request.get_host()},
require_https=request.is_secure(), require_https=request.is_secure(),
@ -232,7 +232,7 @@ class BillDownloadView(CustomContextMixin, UserTokenRequiredMixin, View):
bill = self.get_object() bill = self.get_object()
# TODO(@slamora): implement download as PDF, now only HTML is reachable via link # 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': if content_type == 'application/pdf':
pdf = html_to_pdf(bill.html or bill.render()) pdf = html_to_pdf(bill.html or bill.render())
return HttpResponse(pdf, content_type='application/pdf') return HttpResponse(pdf, content_type='application/pdf')
@ -243,7 +243,7 @@ class BillDownloadView(CustomContextMixin, UserTokenRequiredMixin, View):
class AddressListView(ServiceListView): class AddressListView(ServiceListView):
service_class = AddressService service_class = AddressService
model = Address model = Address
template_name = "musician/addresses.html" template_name = "musician/address_list.html"
extra_context = { extra_context = {
# Translators: This message appears on the page title # Translators: This message appears on the page title
'title': _('Mail addresses'), 'title': _('Mail addresses'),
@ -317,7 +317,7 @@ class AddressDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
class MailingListsView(ServiceListView): class MailingListsView(ServiceListView):
service_class = MailinglistService service_class = MailinglistService
model = List model = List
template_name = "musician/mailinglists.html" template_name = "musician/mailinglist_list.html"
extra_context = { extra_context = {
# Translators: This message appears on the page title # Translators: This message appears on the page title
'title': _('Mailing lists'), 'title': _('Mailing lists'),
@ -348,7 +348,7 @@ class MailingListsView(ServiceListView):
class MailboxListView(ServiceListView): class MailboxListView(ServiceListView):
service_class = MailboxService service_class = MailboxService
model = Mailbox model = Mailbox
template_name = "musician/mailboxes.html" template_name = "musician/mailbox_list.html"
extra_context = { extra_context = {
# Translators: This message appears on the page title # Translators: This message appears on the page title
'title': _('Mailboxes'), 'title': _('Mailboxes'),
@ -447,7 +447,7 @@ class DatabasesView(ServiceListView):
class SaasListView(ServiceListView): class SaasListView(ServiceListView):
service_class = SaasService service_class = SaasService
model = SaaS model = SaaS
template_name = "musician/saas.html" template_name = "musician/saas_list.html"
extra_context = { extra_context = {
# Translators: This message appears on the page title # Translators: This message appears on the page title
'title': _('Software as a Service'), 'title': _('Software as a Service'),
@ -496,7 +496,7 @@ class DomainUpdateRecordView(CustomContextMixin, UserTokenRequiredMixin, UpdateV
class DomainDeleteRecordView(CustomContextMixin, UserTokenRequiredMixin, DeleteView): class DomainDeleteRecordView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
model = Record model = Record
template_name = "musician/record_confirm_delete.html" template_name = "musician/record_check_delete.html"
pk_url_kwarg = "record_pk" pk_url_kwarg = "record_pk"
def get_queryset(self): def get_queryset(self):
@ -545,7 +545,7 @@ class LoginView(FormView):
self.redirect_field_name, self.redirect_field_name,
self.request.GET.get(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, url=redirect_to,
allowed_hosts={self.request.get_host()}, allowed_hosts={self.request.get_host()},
require_https=self.request.is_secure(), require_https=self.request.is_secure(),

View File

@ -6,7 +6,6 @@ from orchestra.utils.python import AttrDict
from .backends import ServiceBackend, ServiceController, replace from .backends import ServiceBackend, ServiceController, replace
default_app_config = 'orchestra.contrib.orchestration.apps.OrchestrationConfig'
class Operation(): class Operation():

View File

@ -28,6 +28,7 @@ STATE_COLORS = {
} }
@admin.register(Route)
class RouteAdmin(ExtendedModelAdmin): class RouteAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'display_backend', 'host', 'match', 'display_model', 'display_actions', 'run_async', 'display_backend', 'host', 'match', 'display_model', 'display_actions', 'run_async',
@ -49,20 +50,24 @@ class RouteAdmin(ExtendedModelAdmin):
display_backend = display_plugin_field('backend') display_backend = display_plugin_field('backend')
@admin.display(
description=_("model")
)
def display_model(self, route): def display_model(self, route):
try: try:
return route.backend_class.model return route.backend_class.model
except KeyError: except KeyError:
return mark_safe("<span style='color: red;'>NOT AVAILABLE</span>") return mark_safe("<span style='color: red;'>NOT AVAILABLE</span>")
display_model.short_description = _("model")
@admin.display(
description=_("actions")
)
@mark_safe @mark_safe
def display_actions(self, route): def display_actions(self, route):
try: try:
return '<br>'.join(route.backend_class.get_actions()) return '<br>'.join(route.backend_class.get_actions())
except KeyError: except KeyError:
return "<span style='color: red;'>NOT AVAILABLE</span>" return "<span style='color: red;'>NOT AVAILABLE</span>"
display_actions.short_description = _("actions")
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Provides dynamic help text on backend form field """ """ Provides dynamic help text on backend form field """
@ -113,13 +118,15 @@ class BackendOperationInline(admin.TabularInline):
'all': ('orchestra/css/hide-inline-id.css',) 'all': ('orchestra/css/hide-inline-id.css',)
} }
@admin.display(
description=_("Instance")
)
def instance_link(self, operation): def instance_link(self, operation):
link = admin_link('instance')(self, operation) link = admin_link('instance')(self, operation)
if link == '---': if link == '---':
return _("Deleted {0}").format(operation.instance_repr or '-'.join( return _("Deleted {0}").format(operation.instance_repr or '-'.join(
(escape(operation.content_type), escape(operation.object_id)))) (escape(operation.content_type), escape(operation.object_id))))
return link return link
instance_link.short_description = _("Instance")
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
return False return False
@ -129,6 +136,7 @@ class BackendOperationInline(admin.TabularInline):
return queryset.prefetch_related('instance') return queryset.prefetch_related('instance')
@admin.register(BackendLog)
class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin): class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ( list_display = (
'id', 'backend', 'server_link', 'display_state', 'exit_code', 'id', 'backend', 'server_link', 'display_state', 'exit_code',
@ -170,19 +178,24 @@ class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
return False return False
@admin.register(Server)
class ServerAdmin(ExtendedModelAdmin): class ServerAdmin(ExtendedModelAdmin):
list_display = ('name', 'address', 'os', 'display_ping', 'display_uptime') list_display = ('name', 'address', 'os', 'display_ping', 'display_uptime')
list_filter = ('os',) list_filter = ('os',)
actions = (orchestrate,) actions = (orchestrate,)
change_view_actions = actions change_view_actions = actions
@admin.display(
description=_("Ping")
)
def display_ping(self, instance): def display_ping(self, instance):
return mark_safe(self._remote_state[instance.pk][0]) return mark_safe(self._remote_state[instance.pk][0])
display_ping.short_description = _("Ping")
@admin.display(
description=_("Uptime")
)
def display_uptime(self, instance): def display_uptime(self, instance):
return mark_safe(self._remote_state[instance.pk][1]) return mark_safe(self._remote_state[instance.pk][1])
display_uptime.short_description = _("Uptime")
def get_queryset(self, request): def get_queryset(self, request):
""" Order by structured name and imporve performance """ """ Order by structured name and imporve performance """
@ -191,6 +204,3 @@ class ServerAdmin(ExtendedModelAdmin):
self._remote_state = retrieve_state(qs) self._remote_state = retrieve_state(qs)
return qs return qs
admin.site.register(Server, ServerAdmin)
admin.site.register(BackendLog, BackendLogAdmin)
admin.site.register(Route, RouteAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.orders.apps.OrdersConfig'

View File

@ -50,6 +50,7 @@ class MetricStorageInline(admin.TabularInline):
return qs return qs
@admin.register(Order)
class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'display_description', 'service_link', 'account_link', 'content_object_link', '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_registered_on = admin_date('registered_on')
display_cancelled_on = admin_date('cancelled_on') display_cancelled_on = admin_date('cancelled_on')
@admin.display(
description=_("Description"),
ordering='description',
)
def display_description(self, order): def display_description(self, order):
return format_html(order.description[:64]) 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): def content_object_link(self, order):
if order.content_object: if order.content_object:
try: try:
@ -128,9 +135,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
return format_html('<a href="{url}">{description}</a>', return format_html('<a href="{url}">{description}</a>',
url=url, description=description) url=url, description=description)
return order.content_object_repr 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 @mark_safe
def bills_links(self, order): def bills_links(self, order):
bills = [] bills = []
@ -138,8 +146,11 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
for line in order.lines.select_related('bill').distinct('bill'): for line in order.lines.select_related('bill').distinct('bill'):
bills.append(make_link(line.bill)) bills.append(make_link(line.bill))
return '<br>'.join(bills) return '<br>'.join(bills)
bills_links.short_description = _("Bills")
@admin.display(
description=_("billed until"),
ordering='billed_until',
)
def display_billed_until(self, order): def display_billed_until(self, order):
billed_until = order.billed_until billed_until = order.billed_until
red = False red = False
@ -160,9 +171,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
'<span title="{raw}" {color}>{human}</span>', '<span title="{raw}" {color}>{human}</span>',
raw=escape(str(billed_until)), color=color, human=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): def display_metric(self, order):
""" """
dispalys latest metric value, don't uses latest() because not loosing prefetch_related dispalys latest metric value, don't uses latest() because not loosing prefetch_related
@ -172,7 +184,6 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
except IndexError: except IndexError:
return '' return ''
return metric.value return metric.value
display_metric.short_description = _("Metric")
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """ """ Make value input widget bigger """
@ -197,11 +208,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
# return OrderFilterChangeList # return OrderFilterChangeList
@admin.register(MetricStorage)
class MetricStorageAdmin(admin.ModelAdmin): class MetricStorageAdmin(admin.ModelAdmin):
list_display = ('order', 'value', 'created_on', 'updated_on') list_display = ('order', 'value', 'created_on', 'updated_on')
list_filter = ('order__service',) list_filter = ('order__service',)
raw_id_fields = ('order',) raw_id_fields = ('order',)
admin.site.register(Order, OrderAdmin)
admin.site.register(MetricStorage, MetricStorageAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.payments.apps.PaymentsConfig'

View File

@ -32,6 +32,7 @@ PROCESS_STATE_COLORS = {
} }
@admin.register(PaymentSource)
class PaymentSourceAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class PaymentSourceAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('label', 'method', 'number', 'account_link', 'is_active') list_display = ('label', 'method', 'number', 'account_link', 'is_active')
list_filter = ('method', 'is_active') list_filter = ('method', 'is_active')
@ -69,6 +70,7 @@ class TransactionInline(admin.TabularInline):
return qs.select_related('source', 'bill') return qs.select_related('source', 'bill')
@admin.register(Transaction)
class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'id', 'bill_link', 'account_link', 'source_link', 'display_created_at', 'id', 'bill_link', 'account_link', 'source_link', 'display_created_at',
@ -156,16 +158,19 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
return [] return []
return [action for action in actions if action.__name__ not in exclude] return [action for action in actions if action.__name__ not in exclude]
@admin.display(
description=_("State"),
ordering='state',
)
@mark_safe @mark_safe
def display_state(self, obj): def display_state(self, obj):
state = admin_colored('state', colors=STATE_COLORS)(obj) state = admin_colored('state', colors=STATE_COLORS)(obj)
help_text = obj.get_state_help() help_text = obj.get_state_help()
state = state.replace('<span ', '<span title="%s" ' % help_text) state = state.replace('<span ', '<span title="%s" ' % help_text)
return state return state
display_state.admin_order_field = 'state'
display_state.short_description = _("State")
@admin.register(TransactionProcess)
class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin): class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ( list_display = (
'id', 'file_url', 'display_transactions', 'display_state', 'display_created_at', 'id', 'file_url', 'display_transactions', 'display_state', 'display_created_at',
@ -184,11 +189,16 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
display_state = admin_colored('state', colors=PROCESS_STATE_COLORS) display_state = admin_colored('state', colors=PROCESS_STATE_COLORS)
display_created_at = admin_date('created_at', short_description=_("Created")) display_created_at = admin_date('created_at', short_description=_("Created"))
@admin.display(
ordering='file'
)
def file_url(self, process): def file_url(self, process):
if process.file: if process.file:
return format_html('<a href="{}">{}</a>', process.file.url, process.file.name) return format_html('<a href="{}">{}</a>', process.file.url, process.file.name)
file_url.admin_order_field = 'file'
@admin.display(
description=_("Transactions")
)
@mark_safe @mark_safe
def display_transactions(self, process): def display_transactions(self, process):
ids = [] ids = []
@ -208,7 +218,6 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
url = reverse('admin:payments_transaction_changelist') url = reverse('admin:payments_transaction_changelist')
url += '?process_id=%i' % process.id url += '?process_id=%i' % process.id
return '<a href="%s">%s</a>' % (url, transactions) return '<a href="%s">%s</a>' % (url, transactions)
display_transactions.short_description = _("Transactions")
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
return False return False
@ -240,6 +249,3 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
helpers.post_delete_processes(self, request, related_transactions) helpers.post_delete_processes(self, request, related_transactions)
admin.site.register(PaymentSource, PaymentSourceAdmin)
admin.site.register(Transaction, TransactionAdmin)
admin.site.register(TransactionProcess, TransactionProcessAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.plans.apps.PlansConfig'

View File

@ -18,6 +18,7 @@ class RateInline(admin.TabularInline):
ordering = ('service', 'plan', 'quantity') ordering = ('service', 'plan', 'quantity')
@admin.register(Plan)
class PlanAdmin(ExtendedModelAdmin): class PlanAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'name', 'is_default', 'is_combinable', 'allow_multiple', 'is_active', 'num_contracts', 'name', 'is_default', 'is_combinable', 'allow_multiple', 'is_active', 'num_contracts',
@ -30,19 +31,22 @@ class PlanAdmin(ExtendedModelAdmin):
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
inlines = [RateInline] inlines = [RateInline]
@admin.display(
description=_("Contracts"),
ordering='contracts__count',
)
def num_contracts(self, plan): def num_contracts(self, plan):
num = plan.contracts__count num = plan.contracts__count
url = reverse('admin:plans_contractedplan_changelist') url = reverse('admin:plans_contractedplan_changelist')
url += '?plan__name={}'.format(plan.name) url += '?plan__name={}'.format(plan.name)
return format_html('<a href="{0}">{1}</a>', url, num) return format_html('<a href="{0}">{1}</a>', url, num)
num_contracts.short_description = _("Contracts")
num_contracts.admin_order_field = 'contracts__count'
def get_queryset(self, request): def get_queryset(self, request):
qs = super(PlanAdmin, self).get_queryset(request) qs = super(PlanAdmin, self).get_queryset(request)
return qs.annotate(models.Count('contracts', distinct=True)) return qs.annotate(models.Count('contracts', distinct=True))
@admin.register(ContractedPlan)
class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin): class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = ('id', 'plan_link', 'account_link') list_display = ('id', 'plan_link', 'account_link')
list_filter = ('plan__name',) list_filter = ('plan__name',)
@ -53,7 +57,5 @@ class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin):
plan_link = admin_link('plan') plan_link = admin_link('plan')
admin.site.register(Plan, PlanAdmin)
admin.site.register(ContractedPlan, ContractedPlanAdmin)
insertattr(Service, 'inlines', RateInline) insertattr(Service, 'inlines', RateInline)

View File

@ -1,4 +1,3 @@
from .backends import ServiceMonitor from .backends import ServiceMonitor
default_app_config = 'orchestra.contrib.resources.apps.ResourcesConfig'

View File

@ -6,7 +6,7 @@ from django.utils.translation import ngettext, gettext_lazy as _
def run_monitor(modeladmin, request, queryset): def run_monitor(modeladmin, request, queryset):
""" Resource and ResourceData run monitors """ """ Resource and ResourceData run monitors """
referer = request.META.get('HTTP_REFERER') referer = request.headers.get('referer')
run_async = modeladmin.model.monitor.__defaults__[0] run_async = modeladmin.model.monitor.__defaults__[0]
logs = set() logs = set()
for resource in queryset: for resource in queryset:

View File

@ -29,6 +29,7 @@ from .forms import ResourceForm
from .models import Resource, ResourceData, MonitorData from .models import Resource, ResourceData, MonitorData
@admin.register(Resource)
class ResourceAdmin(ExtendedModelAdmin): class ResourceAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'id', 'verbose_name', 'content_type', 'aggregation', 'on_demand', '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) return super(ResourceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
@admin.display(
description=_("Content object"),
ordering='content_object_repr',
)
def content_object_link(data): def content_object_link(data):
ct = data.content_type ct = data.content_type
url = reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=(data.object_id,)) url = reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=(data.object_id,))
return format_html('<a href="{}">{}</a>', url, data.content_object_repr) return format_html('<a href="{}">{}</a>', 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): class ResourceDataAdmin(ExtendedModelAdmin):
list_display = ( list_display = (
'id', 'resource_link', content_object_link, 'allocated', 'display_used', 'id', 'resource_link', content_object_link, 'allocated', 'display_used',
@ -151,13 +155,15 @@ class ResourceDataAdmin(ExtendedModelAdmin):
), ),
] + urls ] + urls
@admin.display(
description=_("Used"),
ordering='used',
)
def display_used(self, rdata): def display_used(self, rdata):
if rdata.used is None: if rdata.used is None:
return '' return ''
url = reverse('admin:resources_resourcedata_used_monitordata', args=(rdata.pk,)) url = reverse('admin:resources_resourcedata_used_monitordata', args=(rdata.pk,))
return format_html('<a href="{}">{} {}</a>', url, rdata.used, rdata.unit) return format_html('<a href="{}">{} {}</a>', url, rdata.used, rdata.unit)
display_used.short_description = _("Used")
display_used.admin_order_field = 'used'
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
return False return False
@ -193,6 +199,7 @@ class ResourceDataAdmin(ExtendedModelAdmin):
return redirect(url) return redirect(url)
@admin.register(MonitorData)
class MonitorDataAdmin(ExtendedModelAdmin): class MonitorDataAdmin(ExtendedModelAdmin):
list_display = ('id', 'monitor', content_object_link, 'display_created', 'value') list_display = ('id', 'monitor', content_object_link, 'display_created', 'value')
list_filter = ('monitor', ResourceDataListFilter) list_filter = ('monitor', ResourceDataListFilter)
@ -227,9 +234,6 @@ class MonitorDataAdmin(ExtendedModelAdmin):
return queryset.prefetch_related('content_object') return queryset.prefetch_related('content_object')
admin.site.register(Resource, ResourceAdmin)
admin.site.register(ResourceData, ResourceDataAdmin)
admin.site.register(MonitorData, MonitorDataAdmin)
# Mokey-patching # Mokey-patching
@ -303,6 +307,9 @@ def resource_inline_factory(resources):
self.verbose_name_plural = mark_safe(_("Resources") + ' ' + link) self.verbose_name_plural = mark_safe(_("Resources") + ' ' + link)
return super(ResourceInline, self).get_fieldsets(request, obj) return super(ResourceInline, self).get_fieldsets(request, obj)
@admin.display(
description=_("Used")
)
@mark_safe @mark_safe
def display_used(self, rdata): def display_used(self, rdata):
update = '' update = ''
@ -328,7 +335,6 @@ def resource_inline_factory(resources):
if rdata.resource.monitors: if rdata.resource.monitors:
return _("Unknonw %s %s") % (update, history) return _("Unknonw %s %s") % (update, history)
return _("No monitor") return _("No monitor")
display_used.short_description = _("Used")
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
""" Hidde add another """ """ Hidde add another """

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.saas.apps.SaaSConfig'

View File

@ -18,6 +18,7 @@ from .models import SaaS
from .services import SoftwareService from .services import SoftwareService
@admin.register(SaaS)
class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'service', 'display_url', 'account_link', 'display_active') list_display = ('name', 'service', 'display_url', 'account_link', 'display_active')
list_filter = ('service', IsActiveListFilter, CustomURLListFilter) list_filter = ('service', IsActiveListFilter, CustomURLListFilter)
@ -28,6 +29,10 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
plugin_title = 'Software as a Service' plugin_title = 'Software as a Service'
actions = (disable, enable, list_accounts) actions = (disable, enable, list_accounts)
@admin.display(
description=_("URL"),
ordering='name',
)
@mark_safe @mark_safe
def display_url(self, saas): def display_url(self, saas):
site_domain = saas.get_site_domain() site_domain = saas.get_site_domain()
@ -47,8 +52,6 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
) )
links.append(link) links.append(link)
return '<br>'.join(links) return '<br>'.join(links)
display_url.short_description = _("URL")
display_url.admin_order_field = 'name'
def get_fields(self, *args, **kwargs): def get_fields(self, *args, **kwargs):
fields = super(SaaSAdmin, self).get_fields(*args, **kwargs) fields = super(SaaSAdmin, self).get_fields(*args, **kwargs)
@ -57,4 +60,3 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
return fields return fields
admin.site.register(SaaS, SaaSAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.services.apps.ServicesConfig'

View File

@ -15,6 +15,7 @@ from .actions import update_orders, view_help, clone
from .models import Service from .models import Service
@admin.register(Service)
class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin): class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ( list_display = (
'description', 'content_type', 'handler_type', 'num_orders', 'is_active' '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'}) kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs) return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
@admin.display(
description=_("Orders"),
ordering='orders__count',
)
def num_orders(self, service): def num_orders(self, service):
num = service.orders__count num = service.orders__count
url = reverse('admin:orders_order_changelist') url = reverse('admin:orders_order_changelist')
url += '?service__id__exact=%i&is_active=True' % service.pk url += '?service__id__exact=%i&is_active=True' % service.pk
return format_html('<a href="{}">{}</a>', url, num) return format_html('<a href="{}">{}</a>', url, num)
num_orders.short_description = _("Orders")
num_orders.admin_order_field = 'orders__count'
def get_queryset(self, request): def get_queryset(self, request):
qs = super(ServiceAdmin, self).get_queryset(request) qs = super(ServiceAdmin, self).get_queryset(request)
@ -104,4 +107,3 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
help_view.verbose_name = _("Help") help_view.verbose_name = _("Help")
admin.site.register(Service, ServiceAdmin)

View File

@ -9,7 +9,6 @@ from orchestra.core import validators
from orchestra.utils.python import import_class, format_exception from orchestra.utils.python import import_class, format_exception
default_app_config = 'orchestra.contrib.settings.apps.SettingsConfig'
class Setting(object): class Setting(object):

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.systemusers.apps.SystemUsersConfig'

View File

@ -13,6 +13,7 @@ from .forms import SystemUserCreationForm, SystemUserChangeForm, WebappUserChang
from .models import SystemUser, WebappUsers from .models import SystemUser, WebappUsers
@admin.register(SystemUser)
class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin): class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'username', 'account_link', 'shell', 'display_home', 'display_active', 'display_main' '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) change_view_actions = (set_permission, create_link)
actions = (disable, enable, list_accounts) + change_view_actions actions = (disable, enable, list_accounts) + change_view_actions
@admin.display(
description=_("Main"),
boolean=True,
)
def display_main(self, user): def display_main(self, user):
return user.is_main return user.is_main
display_main.short_description = _("Main")
display_main.boolean = True
@admin.display(
description=_("Home"),
ordering='home',
)
def display_home(self, user): def display_home(self, user):
return user.get_home() return user.get_home()
display_home.short_description = _("Home")
display_home.admin_order_field = 'home'
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
form = super(SystemUserAdmin, self).get_form(request, obj, **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): class WebappUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'username', 'account_link', 'home', 'target_server' 'username', 'account_link', 'home', 'target_server'
@ -107,5 +113,3 @@ class WebappUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
ordering = ('-id',) ordering = ('-id',)
admin.site.register(SystemUser, SystemUserAdmin)
admin.site.register(WebappUsers, WebappUserAdmin)

View File

@ -2,4 +2,3 @@ from . import settings
from .decorators import task, periodic_task, keep_state, apply_async from .decorators import task, periodic_task, keep_state, apply_async
default_app_config = 'orchestra.contrib.tasks.apps.TasksConfig'

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.vps.apps.VPSConfig'

View File

@ -10,6 +10,7 @@ from orchestra.forms import UserCreationForm, NonStoredUserChangeForm
from .models import VPS from .models import VPS
@admin.register(VPS)
class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('hostname', 'type', 'template', 'display_active', 'account_link') list_display = ('hostname', 'type', 'template', 'display_active', 'account_link')
list_filter = ('type', IsActiveListFilter, 'template') list_filter = ('type', IsActiveListFilter, 'template')
@ -44,4 +45,3 @@ class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
return 'root@%s' % obj.hostname return 'root@%s' % obj.hostname
admin.site.register(VPS, VPSAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.webapps.apps.WebAppsConfig'

View File

@ -74,6 +74,7 @@ class WebAppOptionInline(admin.TabularInline):
return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs) return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs)
@admin.register(WebApp)
class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'name', 'display_type', 'display_detail', 'display_websites', 'account_link', 'target_server', 'name', 'display_type', 'display_detail', 'display_websites', 'account_link', 'target_server',
@ -91,6 +92,9 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
display_type = display_plugin_field('type') display_type = display_plugin_field('type')
@admin.display(
description=_("user sftp")
)
def display_sftpuser(self, obj): def display_sftpuser(self, obj):
salida = "" salida = ""
if obj.sftpuser is None: 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) url = resolve_url(admin_urlname(WebappUsers._meta, 'change'), obj.sftpuser.id)
salida += f'<a href="{url}">{obj.sftpuser}</a> <br />' salida += f'<a href="{url}">{obj.sftpuser}</a> <br />'
return mark_safe(salida) return mark_safe(salida)
display_sftpuser.short_description = _("user sftp")
@admin.display(
description=_("web sites")
)
@mark_safe @mark_safe
def display_websites(self, webapp): def display_websites(self, webapp):
websites = [] websites = []
@ -118,14 +124,15 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
plus = '<strong style="color:green; font-size:12px">+</strong>' plus = '<strong style="color:green; font-size:12px">+</strong>'
websites.append('<a href="%s">%s%s</a>' % (add_url, plus, gettext("Add website"))) websites.append('<a href="%s">%s%s</a>' % (add_url, plus, gettext("Add website")))
return '<br>'.join(websites) return '<br>'.join(websites)
display_websites.short_description = _("web sites")
@admin.display(
description=_("detail")
)
def display_detail(self, webapp): def display_detail(self, webapp):
try: try:
return webapp.type_instance.get_detail() return webapp.type_instance.get_detail()
except KeyError: except KeyError:
return mark_safe("<span style='color:red;'>Not available</span>") return mark_safe("<span style='color:red;'>Not available</span>")
display_detail.short_description = _("detail")
def save_model(self, request, obj, form, change): 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) return super().response_add(request, obj, post_url_continue)
admin.site.register(WebApp, WebAppAdmin)

View File

@ -1 +0,0 @@
default_app_config = 'orchestra.contrib.websites.apps.WebsitesConfig'

View File

@ -51,13 +51,16 @@ class ContentInline(AccountAdminMixin, admin.TabularInline):
webapp_link = admin_link('webapp', popup=True) webapp_link = admin_link('webapp', popup=True)
webapp_link.short_description = _("Web App") webapp_link.short_description = _("Web App")
@admin.display(
description=_("Web App type")
)
def webapp_type(self, content): def webapp_type(self, content):
if not content.pk: if not content.pk:
return '' return ''
return content.webapp.get_type_display() return content.webapp.get_type_display()
webapp_type.short_description = _("Web App type")
@admin.register(Website)
class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'name', 'display_domains', 'display_webapps', 'account_link', 'target_server', 'display_active' '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') search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
actions = (disable, enable, list_accounts) actions = (disable, enable, list_accounts)
@admin.display(
description=_("domains"),
ordering='domains',
)
@mark_safe @mark_safe
def display_domains(self, website): def display_domains(self, website):
domains = [] domains = []
@ -87,9 +94,10 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
url = '%s://%s' % (website.get_protocol(), domain) url = '%s://%s' % (website.get_protocol(), domain)
domains.append('<a href="%s">%s</a>' % (url, url)) domains.append('<a href="%s">%s</a>' % (url, url))
return '<br>'.join(domains) return '<br>'.join(domains)
display_domains.short_description = _("domains")
display_domains.admin_order_field = 'domains'
@admin.display(
description=_("Web apps")
)
@mark_safe @mark_safe
def display_webapps(self, website): def display_webapps(self, website):
webapps = [] webapps = []
@ -106,7 +114,6 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
webapp_info = format_html('<a href="{}" title="{}">{}</a> {}', url, detail, name, site_link) webapp_info = format_html('<a href="{}" title="{}">{}</a> {}', url, detail, name, site_link)
webapps.append(webapp_info) webapps.append(webapp_info)
return '<br>'.join(webapps) return '<br>'.join(webapps)
display_webapps.short_description = _("Web apps")
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" """
@ -136,4 +143,3 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
return formsets, inline_instances return formsets, inline_instances
admin.site.register(Website, WebsiteAdmin)

View File

@ -71,7 +71,7 @@ class SelectPluginAdminMixin(object):
if not plugin_value and request.method == 'POST': if not plugin_value and request.method == 'POST':
# HACK baceuse django add_preserved_filters removes extising queryargs # HACK baceuse django add_preserved_filters removes extising queryargs
value = re.search(r"%s=([^&^']+)[&']" % self.plugin_field, value = re.search(r"%s=([^&^']+)[&']" % self.plugin_field,
request.META.get('HTTP_REFERER', '')) request.headers.get('referer', ''))
if value: if value:
plugin_value = value.groups()[0] plugin_value = value.groups()[0]
return plugin_value return plugin_value

View File

@ -1,5 +1,5 @@
from django.contrib import admin 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 django.urls import path
from rest_framework.authtoken.views import obtain_auth_token from rest_framework.authtoken.views import obtain_auth_token
@ -15,13 +15,13 @@ api.autodiscover()
urlpatterns = [ urlpatterns = [
# Admin # Admin
url(r'^admin/', admin.site.urls), re_path(r'^admin/', admin.site.urls),
url(r'^admin_tools/', include('admin_tools.urls')), path('admin_tools/', include('admin_tools.urls')),
# REST API # REST API
url(r'^api/', include(api.router.urls)), path('api/', include(api.router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api-token-auth/', obtain_auth_token, name='api-token-auth'), re_path(r'^api-token-auth/', obtain_auth_token, name='api-token-auth'),
url(r'^media/(.+)/(.+)/(.+)/(.+)/(.+)$', serve_private_media, name='private-media'), re_path(r'^media/(.+)/(.+)/(.+)/(.+)/(.+)$', serve_private_media, name='private-media'),
# url(r'search', 'orchestra.views.search', name='search'), # url(r'search', 'orchestra.views.search', name='search'),
# MUSICIAN # MUSICIAN
path('panel/', include('orchestra.contrib.musician.urls')), path('panel/', include('orchestra.contrib.musician.urls')),
@ -31,5 +31,5 @@ urlpatterns = [
if isinstalled('debug_toolbar'): if isinstalled('debug_toolbar'):
import debug_toolbar import debug_toolbar
urlpatterns.append( urlpatterns.append(
url(r'^__debug__/', include(debug_toolbar.urls)), path('__debug__/', include(debug_toolbar.urls)),
) )

View File

@ -1,24 +1,25 @@
Django==2.2.24
django-fluent-dashboard==1.0.1 Django==3.2.23
django-admin-tools==0.9.1 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-bootstrap4
django-extensions==3.1.3
django-celery==3.2.1
celery==3.1.23
kombu==3.0.35
billiard==3.3.0.23 billiard==3.3.0.23
Markdown==3.3.4 Markdown==3.5.1
djangorestframework==3.12.4 djangorestframework==3.14.0
Pygments==2.9.0 Pygments==2.17.2
django-filter==2.4.0 django-filter==23.4
jsonfield==3.1.0 jsonfield==3.1.0
python-dateutil>=2.7.0 python-dateutil==2.8.2
passlib==1.7.4 passlib==1.7.4
django-iban==0.3.0 django-iban==0.3.1
requests requests
phonenumbers==8.12.27 phonenumbers==8.13.26
django-countries django-countries
django-localflavor==3.1 django-localflavor==4.0
amqp amqp
anyjson anyjson
pytz pytz