Merge pull request #8 from ribaguifi/dev/django2.1-admin
Refactor admin code to support Django 2.1
This commit is contained in:
commit
7b59931bcf
|
@ -5,7 +5,7 @@ from django import forms
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.forms.models import modelformset_factory, BaseModelFormSet
|
from django.forms.models import modelformset_factory, BaseModelFormSet
|
||||||
from django.template import Template, Context
|
from django.template import Template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms.widgets import SpanWidget
|
from orchestra.forms.widgets import SpanWidget
|
||||||
|
@ -28,9 +28,9 @@ class AdminFormMixin(object):
|
||||||
' {% include "admin/includes/fieldset.html" %}'
|
' {% include "admin/includes/fieldset.html" %}'
|
||||||
'{% endfor %}'
|
'{% endfor %}'
|
||||||
)
|
)
|
||||||
context = Context({
|
context = {
|
||||||
'adminform': adminform
|
'adminform': adminform
|
||||||
})
|
}
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,9 +71,9 @@ class AdminFormSet(BaseModelFormSet):
|
||||||
</div>
|
</div>
|
||||||
</div>""")
|
</div>""")
|
||||||
)
|
)
|
||||||
context = Context({
|
context = {
|
||||||
'formset': self
|
'formset': self
|
||||||
})
|
}
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
required=False, validators=[validate_password])
|
required=False, validators=[validate_password])
|
||||||
password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput,
|
password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput,
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
def __init__(self, user, *args, **kwargs):
|
def __init__(self, user, *args, **kwargs):
|
||||||
self.related = kwargs.pop('related', [])
|
self.related = kwargs.pop('related', [])
|
||||||
self.raw = kwargs.pop('raw', False)
|
self.raw = kwargs.pop('raw', False)
|
||||||
|
@ -109,7 +109,7 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
self.fields['password2_%i' % ix] = forms.CharField(label=_("Password (again)"),
|
self.fields['password2_%i' % ix] = forms.CharField(label=_("Password (again)"),
|
||||||
widget=forms.PasswordInput, required=False)
|
widget=forms.PasswordInput, required=False)
|
||||||
setattr(self, 'clean_password2_%i' % ix, partial(self.clean_password2, ix=ix))
|
setattr(self, 'clean_password2_%i' % ix, partial(self.clean_password2, ix=ix))
|
||||||
|
|
||||||
def clean_password2(self, ix=''):
|
def clean_password2(self, ix=''):
|
||||||
if ix != '':
|
if ix != '':
|
||||||
ix = '_%i' % ix
|
ix = '_%i' % ix
|
||||||
|
@ -129,7 +129,7 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
code='password_mismatch',
|
code='password_mismatch',
|
||||||
)
|
)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def clean_password(self, ix=''):
|
def clean_password(self, ix=''):
|
||||||
if ix != '':
|
if ix != '':
|
||||||
ix = '_%i' % ix
|
ix = '_%i' % ix
|
||||||
|
@ -146,14 +146,14 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
code='bad_hash',
|
code='bad_hash',
|
||||||
)
|
)
|
||||||
return password
|
return password
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.password_provided:
|
if not self.password_provided:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_missing'],
|
self.error_messages['password_missing'],
|
||||||
code='password_missing',
|
code='password_missing',
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
"""
|
"""
|
||||||
Saves the new password.
|
Saves the new password.
|
||||||
|
@ -182,7 +182,7 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
if commit:
|
if commit:
|
||||||
rel.save(update_fields=['password'])
|
rel.save(update_fields=['password'])
|
||||||
return self.user
|
return self.user
|
||||||
|
|
||||||
def _get_changed_data(self):
|
def _get_changed_data(self):
|
||||||
data = super().changed_data
|
data = super().changed_data
|
||||||
for name in self.fields.keys():
|
for name in self.fields.keys():
|
||||||
|
@ -202,7 +202,7 @@ class SendEmailForm(forms.Form):
|
||||||
widget=forms.TextInput(attrs={'size': '118'}))
|
widget=forms.TextInput(attrs={'size': '118'}))
|
||||||
message = forms.CharField(label=_("Message"),
|
message = forms.CharField(label=_("Message"),
|
||||||
widget=forms.Textarea(attrs={'cols': 118, 'rows': 15}))
|
widget=forms.Textarea(attrs={'cols': 118, 'rows': 15}))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
initial = kwargs.get('initial')
|
initial = kwargs.get('initial')
|
||||||
|
@ -210,7 +210,7 @@ class SendEmailForm(forms.Form):
|
||||||
self.fields['to'].widget = SpanWidget(original=initial['to'])
|
self.fields['to'].widget = SpanWidget(original=initial['to'])
|
||||||
else:
|
else:
|
||||||
self.fields.pop('to')
|
self.fields.pop('to')
|
||||||
|
|
||||||
def clean_comma_separated_emails(self, value):
|
def clean_comma_separated_emails(self, value):
|
||||||
clean_value = []
|
clean_value = []
|
||||||
for email in value.split(','):
|
for email in value.split(','):
|
||||||
|
@ -222,7 +222,7 @@ class SendEmailForm(forms.Form):
|
||||||
raise validators.ValidationError("Comma separated email addresses.")
|
raise validators.ValidationError("Comma separated email addresses.")
|
||||||
clean_value.append(email)
|
clean_value.append(email)
|
||||||
return clean_value
|
return clean_value
|
||||||
|
|
||||||
def clean_extra_to(self):
|
def clean_extra_to(self):
|
||||||
extra_to = self.cleaned_data['extra_to']
|
extra_to = self.cleaned_data['extra_to']
|
||||||
return self.clean_comma_separated_emails(extra_to)
|
return self.clean_comma_separated_emails(extra_to)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.urls import reverse, NoReverseMatch
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape, format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from orchestra.models.utils import get_field_value
|
from orchestra.models.utils import get_field_value
|
||||||
|
@ -113,21 +113,21 @@ def admin_link(*args, **kwargs):
|
||||||
return '---'
|
return '---'
|
||||||
if not getattr(obj, 'pk', None):
|
if not getattr(obj, 'pk', None):
|
||||||
return '---'
|
return '---'
|
||||||
display = kwargs.get('display')
|
display_ = kwargs.get('display')
|
||||||
if display:
|
if display_:
|
||||||
display = getattr(obj, display, display)
|
display_ = getattr(obj, display_, display_)
|
||||||
else:
|
else:
|
||||||
display = obj
|
display_ = obj
|
||||||
try:
|
try:
|
||||||
url = change_url(obj)
|
url = change_url(obj)
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
# Does not has admin
|
# Does not has admin
|
||||||
return str(display)
|
return str(display_)
|
||||||
extra = ''
|
extra = ''
|
||||||
if kwargs['popup']:
|
if kwargs['popup']:
|
||||||
extra = 'onclick="return showAddAnotherPopup(this);"'
|
extra = mark_safe('onclick="return showAddAnotherPopup(this);"')
|
||||||
title = "Change %s" % obj._meta.verbose_name
|
title = "Change %s" % obj._meta.verbose_name
|
||||||
return mark_safe('<a href="%s" title="%s" %s>%s</a>' % (url, title, extra, display))
|
return format_html('<a href="{}" title="{}" {}>{}</a>', url, title, extra, display_)
|
||||||
|
|
||||||
|
|
||||||
@admin_field
|
@admin_field
|
||||||
|
@ -158,7 +158,7 @@ def admin_date(*args, **kwargs):
|
||||||
date = date.strftime("%Y-%m-%d %H:%M:%S %Z")
|
date = date.strftime("%Y-%m-%d %H:%M:%S %Z")
|
||||||
else:
|
else:
|
||||||
date = date.strftime("%Y-%m-%d")
|
date = date.strftime("%Y-%m-%d")
|
||||||
return '<span title="{0}">{1}</span>'.format(date, escape(natural))
|
return format_html('<span title="{0}">{1}</span>', date, natural)
|
||||||
|
|
||||||
|
|
||||||
def get_object_from_url(modeladmin, request):
|
def get_object_from_url(modeladmin, request):
|
||||||
|
|
|
@ -175,7 +175,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
for model, objs in collector.model_objs.items():
|
for model, objs in collector.model_objs.items():
|
||||||
count = 0
|
count = 0
|
||||||
# discount main systemuser
|
# discount main systemuser
|
||||||
if model is modeladmin.model.main_systemuser.field.model:
|
if model is modeladmin.model.main_systemuser.field.related_model:
|
||||||
count = len(objs) - 1
|
count = len(objs) - 1
|
||||||
# Discount account
|
# Discount account
|
||||||
elif model is not modeladmin.model and model in registered_services:
|
elif model is not modeladmin.model and model in registered_services:
|
||||||
|
|
|
@ -158,6 +158,7 @@ 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'
|
||||||
|
|
||||||
|
@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
|
||||||
context = {
|
context = {
|
||||||
|
@ -167,7 +168,6 @@ class AccountListAdmin(AccountAdmin):
|
||||||
}
|
}
|
||||||
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.short_description = _("account")
|
||||||
select_account.allow_tags = True
|
|
||||||
select_account.admin_order_field = 'username'
|
select_account.admin_order_field = 'username'
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
def changelist_view(self, request, extra_context=None):
|
||||||
|
@ -207,6 +207,7 @@ class AccountAdminMixin(object):
|
||||||
account = None
|
account = None
|
||||||
list_select_related = ('account',)
|
list_select_related = ('account',)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_active(self, instance):
|
def display_active(self, instance):
|
||||||
if not instance.is_active:
|
if not instance.is_active:
|
||||||
return '<img src="%s" alt="False">' % static('admin/img/icon-no.svg')
|
return '<img src="%s" alt="False">' % static('admin/img/icon-no.svg')
|
||||||
|
@ -215,14 +216,12 @@ class AccountAdminMixin(object):
|
||||||
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.short_description = _("active")
|
||||||
display_active.allow_tags = True
|
|
||||||
display_active.admin_order_field = 'is_active'
|
display_active.admin_order_field = 'is_active'
|
||||||
|
|
||||||
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.short_description = _("account")
|
||||||
account_link.allow_tags = True
|
|
||||||
account_link.admin_order_field = 'account__username'
|
account_link.admin_order_field = 'account__username'
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
|
|
@ -47,7 +47,7 @@ def create_account_creation_form():
|
||||||
# Previous validation error
|
# Previous validation error
|
||||||
return
|
return
|
||||||
errors = {}
|
errors = {}
|
||||||
systemuser_model = Account.main_systemuser.field.model
|
systemuser_model = Account.main_systemuser.field.related_model
|
||||||
if systemuser_model.objects.filter(username=account.username).exists():
|
if systemuser_model.objects.filter(username=account.username).exists():
|
||||||
errors['username'] = _("A system user with this name already exists.")
|
errors['username'] = _("A system user with this name already exists.")
|
||||||
for model, key, related_kwargs, __ in create_related:
|
for model, key, related_kwargs, __ in create_related:
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.db import models
|
||||||
from django.db.models import F, Sum, Prefetch
|
from django.db.models import F, Sum, Prefetch
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
@ -15,7 +16,7 @@ from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import admin_date, insertattr, admin_link, change_url
|
from orchestra.admin.utils import admin_date, insertattr, admin_link, change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin, AccountAdmin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin, AccountAdmin
|
||||||
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
|
from orchestra.forms.widgets import PaddingCheckboxSelectMultiple
|
||||||
|
|
||||||
from . import settings, actions
|
from . import settings, actions
|
||||||
from .filters import (BillTypeListFilter, HasBillContactListFilter, TotalListFilter,
|
from .filters import (BillTypeListFilter, HasBillContactListFilter, TotalListFilter,
|
||||||
|
@ -67,6 +68,7 @@ class BillLineInline(admin.TabularInline):
|
||||||
|
|
||||||
order_link = admin_link('order', display='pk')
|
order_link = admin_link('order', display='pk')
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_total(self, line):
|
def display_total(self, line):
|
||||||
if line.pk:
|
if line.pk:
|
||||||
total = line.compute_total()
|
total = line.compute_total()
|
||||||
|
@ -78,7 +80,6 @@ class BillLineInline(admin.TabularInline):
|
||||||
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")
|
display_total.short_description = _("Total")
|
||||||
display_total.allow_tags = True
|
|
||||||
|
|
||||||
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 """
|
||||||
|
@ -104,27 +105,26 @@ class ClosedBillLineInline(BillLineInline):
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
can_delete = False
|
can_delete = False
|
||||||
|
|
||||||
|
@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(' '*4+subline.description)
|
descriptions.append(' ' * 4 + subline.description)
|
||||||
return '<br>'.join(descriptions)
|
return '<br>'.join(descriptions)
|
||||||
display_description.short_description = _("Description")
|
display_description.short_description = _("Description")
|
||||||
display_description.allow_tags = True
|
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_subtotal(self, line):
|
def display_subtotal(self, line):
|
||||||
subtotals = [' ' + str(line.subtotal)]
|
subtotals = [' ' + 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")
|
display_subtotal.short_description = _("Subtotal")
|
||||||
display_subtotal.allow_tags = True
|
|
||||||
|
|
||||||
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")
|
display_total.short_description = _("Total")
|
||||||
display_total.allow_tags = True
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
@ -242,6 +242,7 @@ class BillLineManagerAdmin(BillLineAdmin):
|
||||||
|
|
||||||
|
|
||||||
class BillAdminMixin(AccountAdminMixin):
|
class BillAdminMixin(AccountAdminMixin):
|
||||||
|
@mark_safe
|
||||||
def display_total_with_subtotals(self, bill):
|
def display_total_with_subtotals(self, bill):
|
||||||
if bill.pk:
|
if bill.pk:
|
||||||
currency = settings.BILLS_CURRENCY.lower()
|
currency = settings.BILLS_CURRENCY.lower()
|
||||||
|
@ -251,10 +252,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.allow_tags = True
|
|
||||||
display_total_with_subtotals.short_description = _("total")
|
display_total_with_subtotals.short_description = _("total")
|
||||||
display_total_with_subtotals.admin_order_field = 'approx_total'
|
display_total_with_subtotals.admin_order_field = 'approx_total'
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_payment_state(self, bill):
|
def display_payment_state(self, bill):
|
||||||
if bill.pk:
|
if bill.pk:
|
||||||
t_opts = bill.transactions.model._meta
|
t_opts = bill.transactions.model._meta
|
||||||
|
@ -276,7 +277,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.allow_tags = True
|
|
||||||
display_payment_state.short_description = _("Payment")
|
display_payment_state.short_description = _("Payment")
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
|
@ -376,16 +376,14 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
|
|
||||||
def display_total(self, bill):
|
def display_total(self, bill):
|
||||||
currency = settings.BILLS_CURRENCY.lower()
|
currency = settings.BILLS_CURRENCY.lower()
|
||||||
return '%s &%s;' % (bill.compute_total(), currency)
|
return format_html('{} &{};', bill.compute_total(), currency)
|
||||||
display_total.allow_tags = True
|
|
||||||
display_total.short_description = _("total")
|
display_total.short_description = _("total")
|
||||||
display_total.admin_order_field = 'approx_total'
|
display_total.admin_order_field = 'approx_total'
|
||||||
|
|
||||||
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 '<a href="%s">%s</a>' % (url, bill.get_type_display())
|
return format_html('<a href="{}">{}</a>', url, bill.get_type_display())
|
||||||
type_link.allow_tags = True
|
|
||||||
type_link.short_description = _("type")
|
type_link.short_description = _("type")
|
||||||
type_link.admin_order_field = 'type'
|
type_link.admin_order_field = 'type'
|
||||||
|
|
||||||
|
@ -479,7 +477,7 @@ class BillContactInline(admin.StackedInline):
|
||||||
if db_field.name == 'address':
|
if db_field.name == 'address':
|
||||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
||||||
if db_field.name == 'email_usage':
|
if db_field.name == 'email_usage':
|
||||||
kwargs['widget'] = paddingCheckboxSelectMultiple(45)
|
kwargs['widget'] = PaddingCheckboxSelectMultiple(45)
|
||||||
return super().formfield_for_dbfield(db_field, **kwargs)
|
return super().formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ def validate_contact(request, bill, error=True):
|
||||||
message = msg.format(relation=_("Related"), account=account, url=url)
|
message = msg.format(relation=_("Related"), account=account, url=url)
|
||||||
send(request, mark_safe(message))
|
send(request, mark_safe(message))
|
||||||
valid = False
|
valid = False
|
||||||
main = type(bill).account.field.model.objects.get_main()
|
main = type(bill).account.field.related_model.objects.get_main()
|
||||||
if not hasattr(main, 'billcontact'):
|
if not hasattr(main, 'billcontact'):
|
||||||
account = force_text(main)
|
account = force_text(main)
|
||||||
url = reverse('admin:accounts_account_change', args=(main.id,))
|
url = reverse('admin:accounts_account_change', args=(main.id,))
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.core.validators import ValidationError, RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Sum
|
from django.db.models import F, Sum
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.template import loader, Context
|
from django.template import loader
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
@ -303,7 +303,7 @@ class Bill(models.Model):
|
||||||
with translation.override(language or self.account.language):
|
with translation.override(language or self.account.language):
|
||||||
if payment is False:
|
if payment is False:
|
||||||
payment = self.account.paymentsources.get_default()
|
payment = self.account.paymentsources.get_default()
|
||||||
context = Context({
|
context = {
|
||||||
'bill': self,
|
'bill': self,
|
||||||
'lines': self.lines.all().prefetch_related('sublines'),
|
'lines': self.lines.all().prefetch_related('sublines'),
|
||||||
'seller': self.seller,
|
'seller': self.seller,
|
||||||
|
@ -318,7 +318,7 @@ class Bill(models.Model):
|
||||||
'payment': payment and payment.get_bill_context(),
|
'payment': payment and payment.get_bill_context(),
|
||||||
'default_due_date': self.get_due_date(payment=payment),
|
'default_due_date': self.get_due_date(payment=payment),
|
||||||
'now': timezone.now(),
|
'now': timezone.now(),
|
||||||
})
|
}
|
||||||
template_name = 'BILLS_%s_TEMPLATE' % self.get_type()
|
template_name = 'BILLS_%s_TEMPLATE' % self.get_type()
|
||||||
template = getattr(settings, template_name, settings.BILLS_DEFAULT_TEMPLATE)
|
template = getattr(settings, template_name, settings.BILLS_DEFAULT_TEMPLATE)
|
||||||
bill_template = loader.get_template(template)
|
bill_template = loader.get_template(template)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from orchestra.admin.actions import SendEmail
|
||||||
from orchestra.admin.utils import insertattr, change_url
|
from orchestra.admin.utils import insertattr, change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdmin, AccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdmin, AccountAdminMixin
|
||||||
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
|
from orchestra.forms.widgets import PaddingCheckboxSelectMultiple
|
||||||
|
|
||||||
from .filters import EmailUsageListFilter
|
from .filters import EmailUsageListFilter
|
||||||
from .models import Contact
|
from .models import Contact
|
||||||
|
@ -61,18 +61,18 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
actions = (SendEmail(), list_accounts)
|
actions = (SendEmail(), list_accounts)
|
||||||
|
|
||||||
def dispaly_name(self, contact):
|
def dispaly_name(self, contact):
|
||||||
return str(contact)
|
return str(contact)
|
||||||
dispaly_name.short_description = _("Name")
|
dispaly_name.short_description = _("Name")
|
||||||
dispaly_name.admin_order_field = 'short_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 """
|
||||||
if db_field.name == 'address':
|
if db_field.name == 'address':
|
||||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
||||||
if db_field.name == 'email_usage':
|
if db_field.name == 'email_usage':
|
||||||
kwargs['widget'] = paddingCheckboxSelectMultiple(130)
|
kwargs['widget'] = PaddingCheckboxSelectMultiple(130)
|
||||||
return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,14 +86,14 @@ class ContactInline(admin.StackedInline):
|
||||||
fields = (
|
fields = (
|
||||||
('short_name', 'full_name'), 'email', 'email_usage', ('phone', 'phone2'),
|
('short_name', 'full_name'), 'email', 'email_usage', ('phone', 'phone2'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_extra(self, request, obj=None, **kwargs):
|
def get_extra(self, request, obj=None, **kwargs):
|
||||||
return 0 if obj and obj.contacts.exists() else 1
|
return 0 if obj and obj.contacts.exists() else 1
|
||||||
|
|
||||||
def get_view_on_site_url(self, obj=None):
|
def get_view_on_site_url(self, obj=None):
|
||||||
if obj:
|
if obj:
|
||||||
return change_url(obj)
|
return change_url(obj)
|
||||||
|
|
||||||
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 """
|
||||||
if db_field.name == 'short_name':
|
if db_field.name == 'short_name':
|
||||||
|
@ -101,7 +101,7 @@ class ContactInline(admin.StackedInline):
|
||||||
if db_field.name == 'address':
|
if db_field.name == 'address':
|
||||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
||||||
if db_field.name == 'email_usage':
|
if db_field.name == 'email_usage':
|
||||||
kwargs['widget'] = paddingCheckboxSelectMultiple(45)
|
kwargs['widget'] = PaddingCheckboxSelectMultiple(45)
|
||||||
return super(ContactInline, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(ContactInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
|
@ -49,17 +51,17 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
filter_by_account_fields = ('users',)
|
filter_by_account_fields = ('users',)
|
||||||
list_prefetch_related = ('users',)
|
list_prefetch_related = ('users',)
|
||||||
actions = (list_accounts, save_selected)
|
actions = (list_accounts, save_selected)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_users(self, db):
|
def display_users(self, db):
|
||||||
links = []
|
links = []
|
||||||
for user in db.users.all():
|
for user in db.users.all():
|
||||||
link = '<a href="%s">%s</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.short_description = _("Users")
|
||||||
display_users.allow_tags = True
|
|
||||||
display_users.admin_order_field = 'users__username'
|
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)
|
||||||
if not change:
|
if not change:
|
||||||
|
@ -98,24 +100,24 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten
|
||||||
filter_by_account_fields = ('databases',)
|
filter_by_account_fields = ('databases',)
|
||||||
list_prefetch_related = ('databases',)
|
list_prefetch_related = ('databases',)
|
||||||
actions = (list_accounts, save_selected)
|
actions = (list_accounts, save_selected)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_databases(self, user):
|
def display_databases(self, user):
|
||||||
links = []
|
links = []
|
||||||
for db in user.databases.all():
|
for db in user.databases.all():
|
||||||
link = '<a href="%s">%s</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.short_description = _("Databases")
|
||||||
display_databases.allow_tags = True
|
|
||||||
display_databases.admin_order_field = 'databases__name'
|
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)
|
||||||
return [
|
return [
|
||||||
url(r'^(\d+)/password/$',
|
url(r'^(\d+)/password/$',
|
||||||
self.admin_site.admin_view(useradmin.user_change_password))
|
self.admin_site.admin_view(useradmin.user_change_password))
|
||||||
] + super(DatabaseUserAdmin, self).get_urls()
|
] + super(DatabaseUserAdmin, self).get_urls()
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
""" set password """
|
""" set password """
|
||||||
if not change:
|
if not change:
|
||||||
|
|
|
@ -17,11 +17,11 @@ class DatabaseUserCreationForm(forms.ModelForm):
|
||||||
password2 = forms.CharField(label=_("Password confirmation"), required=False,
|
password2 = forms.CharField(label=_("Password confirmation"), required=False,
|
||||||
widget=forms.PasswordInput,
|
widget=forms.PasswordInput,
|
||||||
help_text=_("Enter the same password as above, for verification."))
|
help_text=_("Enter the same password as above, for verification."))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DatabaseUser
|
model = DatabaseUser
|
||||||
fields = ('username', 'account', 'type')
|
fields = ('username', 'account', 'type')
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
password1 = self.cleaned_data.get("password1")
|
password1 = self.cleaned_data.get("password1")
|
||||||
password2 = self.cleaned_data.get("password2")
|
password2 = self.cleaned_data.get("password2")
|
||||||
|
@ -40,11 +40,11 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
|
||||||
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
|
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
|
||||||
"@/./+/-/_ characters.")})
|
"@/./+/-/_ characters.")})
|
||||||
user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects)
|
user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Database
|
model = Database
|
||||||
fields = ('username', 'account', 'type')
|
fields = ('username', 'account', 'type')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DatabaseCreationForm, self).__init__(*args, **kwargs)
|
super(DatabaseCreationForm, self).__init__(*args, **kwargs)
|
||||||
account_id = self.initial.get('account', self.initial_account)
|
account_id = self.initial.get('account', self.initial_account)
|
||||||
|
@ -53,13 +53,13 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
|
||||||
choices = [ (u.pk, "%s (%s)" % (u, u.get_type_display())) for u in qs ]
|
choices = [ (u.pk, "%s (%s)" % (u, u.get_type_display())) for u in qs ]
|
||||||
self.fields['user'].queryset = qs
|
self.fields['user'].queryset = qs
|
||||||
self.fields['user'].choices = [(None, '--------'),] + choices
|
self.fields['user'].choices = [(None, '--------'),] + choices
|
||||||
|
|
||||||
def clean_username(self):
|
def clean_username(self):
|
||||||
username = self.cleaned_data.get('username')
|
username = self.cleaned_data.get('username')
|
||||||
if DatabaseUser.objects.filter(username=username).exists():
|
if DatabaseUser.objects.filter(username=username).exists():
|
||||||
raise ValidationError("Provided username already exists.")
|
raise ValidationError("Provided username already exists.")
|
||||||
return username
|
return username
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
username = self.cleaned_data.get('username')
|
username = self.cleaned_data.get('username')
|
||||||
password1 = self.cleaned_data.get('password1')
|
password1 = self.cleaned_data.get('password1')
|
||||||
|
@ -70,14 +70,14 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
|
||||||
msg = _("The two password fields didn't match.")
|
msg = _("The two password fields didn't match.")
|
||||||
raise ValidationError(msg)
|
raise ValidationError(msg)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def clean_user(self):
|
def clean_user(self):
|
||||||
user = self.cleaned_data.get('user')
|
user = self.cleaned_data.get('user')
|
||||||
if user and user.type != self.cleaned_data.get('type'):
|
if user and user.type != self.cleaned_data.get('type'):
|
||||||
msg = _("Database type and user type doesn't match")
|
msg = _("Database type and user type doesn't match")
|
||||||
raise ValidationError(msg)
|
raise ValidationError(msg)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(DatabaseCreationForm, self).clean()
|
cleaned_data = super(DatabaseCreationForm, self).clean()
|
||||||
if 'user' in cleaned_data and 'username' in cleaned_data:
|
if 'user' in cleaned_data and 'username' in cleaned_data:
|
||||||
|
@ -91,7 +91,7 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
|
||||||
|
|
||||||
class ReadOnlySQLPasswordHashField(ReadOnlyPasswordHashField):
|
class ReadOnlySQLPasswordHashField(ReadOnlyPasswordHashField):
|
||||||
class ReadOnlyPasswordHashWidget(forms.Widget):
|
class ReadOnlyPasswordHashWidget(forms.Widget):
|
||||||
def render(self, name, value, attrs):
|
def render(self, name, value, attrs, renderer=None):
|
||||||
original = ReadOnlyPasswordHashField.widget().render(name, value, attrs)
|
original = ReadOnlyPasswordHashField.widget().render(name, value, attrs)
|
||||||
if 'Invalid' not in original:
|
if 'Invalid' not in original:
|
||||||
return original
|
return original
|
||||||
|
@ -114,10 +114,10 @@ class DatabaseUserChangeForm(forms.ModelForm):
|
||||||
"this user's password, but you can change the password "
|
"this user's password, but you can change the password "
|
||||||
"using <a href='../password/'>this form</a>. "
|
"using <a href='../password/'>this form</a>. "
|
||||||
"<a onclick='return showAddAnotherPopup(this);' href='../hash/'>Show hash</a>."))
|
"<a onclick='return showAddAnotherPopup(this);' href='../hash/'>Show hash</a>."))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DatabaseUser
|
model = DatabaseUser
|
||||||
fields = ('username', 'password', 'type', 'account')
|
fields = ('username', 'password', 'type', 'account')
|
||||||
|
|
||||||
def clean_password(self):
|
def clean_password(self):
|
||||||
return self.initial["password"]
|
return self.initial["password"]
|
||||||
|
|
|
@ -3,6 +3,8 @@ from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.functions import Concat, Coalesce
|
from django.db.models.functions import Concat, Coalesce
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -72,9 +74,8 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
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 ' '*4 + domain.name
|
return mark_safe(' '*4 + domain.name)
|
||||||
structured_name.short_description = _("name")
|
structured_name.short_description = _("name")
|
||||||
structured_name.allow_tags = True
|
|
||||||
structured_name.admin_order_field = 'structured_name'
|
structured_name.admin_order_field = 'structured_name'
|
||||||
|
|
||||||
def display_is_top(self, domain):
|
def display_is_top(self, domain):
|
||||||
|
@ -83,6 +84,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_is_top.boolean = True
|
display_is_top.boolean = True
|
||||||
display_is_top.admin_order_field = 'top'
|
display_is_top.admin_order_field = 'top'
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_websites(self, domain):
|
def display_websites(self, domain):
|
||||||
if apps.isinstalled('orchestra.contrib.websites'):
|
if apps.isinstalled('orchestra.contrib.websites'):
|
||||||
websites = domain.websites.all()
|
websites = domain.websites.all()
|
||||||
|
@ -92,22 +94,22 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
site_link = get_on_site_link(website.get_absolute_url())
|
site_link = get_on_site_link(website.get_absolute_url())
|
||||||
admin_url = change_url(website)
|
admin_url = change_url(website)
|
||||||
title = _("Edit website")
|
title = _("Edit website")
|
||||||
link = '<a href="%s" title="%s">%s %s</a>' % (
|
link = format_html('<a href="{}" title="{}">{} {}</a>',
|
||||||
admin_url, title, website.name, site_link)
|
admin_url, title, website.name, site_link)
|
||||||
links.append(link)
|
links.append(link)
|
||||||
return '<br>'.join(links)
|
return '<br>'.join(links)
|
||||||
add_url = reverse('admin:websites_website_add')
|
add_url = reverse('admin:websites_website_add')
|
||||||
add_url += '?account=%i&domains=%i' % (domain.account_id, domain.pk)
|
add_url += '?account=%i&domains=%i' % (domain.account_id, domain.pk)
|
||||||
image = '<img src="%s"></img>' % static('orchestra/images/add.png')
|
add_link = format_html(
|
||||||
add_link = '<a href="%s" title="%s">%s</a>' % (
|
'<a href="{}" title="{}"><img src="{}" /></a>', add_url,
|
||||||
add_url, _("Add website"), image
|
_("Add website"), static('orchestra/images/add.png'),
|
||||||
)
|
)
|
||||||
return _("No website %s") % (add_link)
|
return _("No website %s") % (add_link)
|
||||||
return '---'
|
return '---'
|
||||||
display_websites.admin_order_field = 'websites__name'
|
display_websites.admin_order_field = 'websites__name'
|
||||||
display_websites.short_description = _("Websites")
|
display_websites.short_description = _("Websites")
|
||||||
display_websites.allow_tags = True
|
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_addresses(self, domain):
|
def display_addresses(self, domain):
|
||||||
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
||||||
add_url = reverse('admin:mailboxes_address_add')
|
add_url = reverse('admin:mailboxes_address_add')
|
||||||
|
@ -126,10 +128,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '---'
|
return '---'
|
||||||
display_addresses.short_description = _("Addresses")
|
display_addresses.short_description = _("Addresses")
|
||||||
display_addresses.admin_order_field = 'addresses__count'
|
display_addresses.admin_order_field = 'addresses__count'
|
||||||
display_addresses.allow_tags = True
|
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def implicit_records(self, domain):
|
def implicit_records(self, domain):
|
||||||
defaults = []
|
|
||||||
types = set(domain.records.values_list('type', flat=True))
|
types = set(domain.records.values_list('type', flat=True))
|
||||||
ttl = settings.DOMAINS_DEFAULT_TTL
|
ttl = settings.DOMAINS_DEFAULT_TTL
|
||||||
lines = []
|
lines = []
|
||||||
|
@ -141,14 +142,13 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
value=record.value
|
value=record.value
|
||||||
)
|
)
|
||||||
if not domain.record_is_implicit(record, types):
|
if not domain.record_is_implicit(record, types):
|
||||||
line = '<strike>%s</strike>' % line
|
line = format_html('<strike>{}</strike>', line)
|
||||||
if record.type is Record.SOA:
|
if record.type is Record.SOA:
|
||||||
lines.insert(0, line)
|
lines.insert(0, line)
|
||||||
else:
|
else:
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
return '<br>'.join(lines)
|
return '<br>'.join(lines)
|
||||||
implicit_records.short_description = _("Implicit records")
|
implicit_records.short_description = _("Implicit records")
|
||||||
implicit_records.allow_tags = True
|
|
||||||
|
|
||||||
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 """
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.urls import reverse, NoReverseMatch
|
|
||||||
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.contrib.admin.utils import unquote
|
|
||||||
from django.contrib.admin.templatetags.admin_static import static
|
from django.contrib.admin.templatetags.admin_static import static
|
||||||
|
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
||||||
|
from django.contrib.admin.utils import unquote
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.urls import NoReverseMatch, reverse
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin.utils import admin_link, admin_date
|
from orchestra.admin.utils import admin_date, admin_link
|
||||||
|
|
||||||
|
|
||||||
class LogEntryAdmin(admin.ModelAdmin):
|
class LogEntryAdmin(admin.ModelAdmin):
|
||||||
|
@ -34,11 +36,12 @@ 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"))
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_message(self, log):
|
def display_message(self, log):
|
||||||
edit = '<a href="%(url)s"><img src="%(img)s"></img></a>' % {
|
edit = format_html('<a href="{url}"><img src="{img}"></img></a>', **{
|
||||||
'url': reverse('admin:admin_logentry_change', args=(log.pk,)),
|
'url': reverse('admin:admin_logentry_change', args=(log.pk,)),
|
||||||
'img': static('admin/img/icon-changelink.svg'),
|
'img': static('admin/img/icon-changelink.svg'),
|
||||||
}
|
})
|
||||||
if log.is_addition():
|
if log.is_addition():
|
||||||
return _('Added "%(link)s". %(edit)s') % {
|
return _('Added "%(link)s". %(edit)s') % {
|
||||||
'link': self.content_object_link(log),
|
'link': self.content_object_link(log),
|
||||||
|
@ -57,7 +60,6 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
}
|
}
|
||||||
display_message.short_description = _("Message")
|
display_message.short_description = _("Message")
|
||||||
display_message.admin_order_field = 'action_flag'
|
display_message.admin_order_field = 'action_flag'
|
||||||
display_message.allow_tags = True
|
|
||||||
|
|
||||||
def display_action(self, log):
|
def display_action(self, log):
|
||||||
if log.is_addition():
|
if log.is_addition():
|
||||||
|
@ -75,10 +77,9 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
url = reverse(view, args=(log.object_id,))
|
url = reverse(view, args=(log.object_id,))
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
return log.object_repr
|
return log.object_repr
|
||||||
return '<a href="%s">%s</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.short_description = _("Content object")
|
||||||
content_object_link.admin_order_field = 'object_repr'
|
content_object_link.admin_order_field = 'object_repr'
|
||||||
content_object_link.allow_tags = True
|
|
||||||
|
|
||||||
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 """
|
||||||
|
|
|
@ -5,7 +5,8 @@ from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import format_html, strip_tags
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ class MessageReadOnlyInline(admin.TabularInline):
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def content_html(self, msg):
|
def content_html(self, msg):
|
||||||
context = {
|
context = {
|
||||||
'number': msg.number,
|
'number': msg.number,
|
||||||
|
@ -58,12 +60,13 @@ class MessageReadOnlyInline(admin.TabularInline):
|
||||||
}
|
}
|
||||||
summary = _("#%(number)i Updated by %(author)s about %(time)s") % context
|
summary = _("#%(number)i Updated by %(author)s about %(time)s") % context
|
||||||
header = '<strong style="color:#666;">%s</strong><hr />' % summary
|
header = '<strong style="color:#666;">%s</strong><hr />' % summary
|
||||||
|
|
||||||
content = markdown(msg.content)
|
content = markdown(msg.content)
|
||||||
content = content.replace('>\n', '>')
|
content = content.replace('>\n', '>')
|
||||||
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")
|
content_html.short_description = _("Content")
|
||||||
content_html.allow_tags = True
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
@ -111,10 +114,10 @@ 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)
|
||||||
|
|
||||||
|
@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 = '#'
|
ticket_id.short_description = '#'
|
||||||
ticket_id.allow_tags = True
|
|
||||||
|
|
||||||
|
|
||||||
class TicketAdmin(ExtendedModelAdmin):
|
class TicketAdmin(ExtendedModelAdmin):
|
||||||
|
@ -192,6 +195,7 @@ 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)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_summary(self, ticket):
|
def display_summary(self, ticket):
|
||||||
context = {
|
context = {
|
||||||
'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
|
'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
|
||||||
|
@ -207,14 +211,12 @@ 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'
|
display_summary.short_description = 'Summary'
|
||||||
display_summary.allow_tags = True
|
|
||||||
|
|
||||||
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 '<span style="font-weight:normal;font-size:11px;">%s</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.allow_tags = True
|
|
||||||
unbold_id.short_description = "#"
|
unbold_id.short_description = "#"
|
||||||
unbold_id.admin_order_field = 'id'
|
unbold_id.admin_order_field = 'id'
|
||||||
|
|
||||||
|
@ -222,8 +224,7 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
""" 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 "<strong class='unread'>%s</strong>" % ticket.subject
|
return format_html("<strong class='unread'>{}</strong>", ticket.subject)
|
||||||
bold_subject.allow_tags = True
|
|
||||||
bold_subject.short_description = _("Subject")
|
bold_subject.short_description = _("Subject")
|
||||||
bold_subject.admin_order_field = 'subject'
|
bold_subject.admin_order_field = 'subject'
|
||||||
|
|
||||||
|
@ -297,10 +298,9 @@ class QueueAdmin(admin.ModelAdmin):
|
||||||
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 '<a href="%s">%d</a>' % (url, num)
|
return format_html('<a href="{}">{}</a>', url, num)
|
||||||
num_tickets.short_description = _("Tickets")
|
num_tickets.short_description = _("Tickets")
|
||||||
num_tickets.admin_order_field = 'tickets__count'
|
num_tickets.admin_order_field = 'tickets__count'
|
||||||
num_tickets.allow_tags = True
|
|
||||||
|
|
||||||
def get_list_display(self, request):
|
def get_list_display(self, request):
|
||||||
""" show notifications """
|
""" show notifications """
|
||||||
|
|
|
@ -13,7 +13,7 @@ from .models import Queue, Ticket
|
||||||
|
|
||||||
class MarkDownWidget(forms.Textarea):
|
class MarkDownWidget(forms.Textarea):
|
||||||
""" MarkDown textarea widget with syntax preview """
|
""" MarkDown textarea widget with syntax preview """
|
||||||
|
|
||||||
markdown_url = static('issues/markdown_syntax.html')
|
markdown_url = static('issues/markdown_syntax.html')
|
||||||
markdown_help_text = (
|
markdown_help_text = (
|
||||||
'<a href="%s" onclick=\'window.open("%s", "", "resizable=yes, '
|
'<a href="%s" onclick=\'window.open("%s", "", "resizable=yes, '
|
||||||
|
@ -21,8 +21,8 @@ class MarkDownWidget(forms.Textarea):
|
||||||
'return false;\'>markdown format</a>' % (markdown_url, markdown_url)
|
'return false;\'>markdown format</a>' % (markdown_url, markdown_url)
|
||||||
)
|
)
|
||||||
markdown_help_text = 'HTML not allowed, you can use %s' % markdown_help_text
|
markdown_help_text = 'HTML not allowed, you can use %s' % markdown_help_text
|
||||||
|
|
||||||
def render(self, name, value, attrs):
|
def render(self, name, value, attrs, renderer=None):
|
||||||
widget_id = attrs['id'] if attrs and 'id' in attrs else 'id_%s' % name
|
widget_id = attrs['id'] if attrs and 'id' in attrs else 'id_%s' % name
|
||||||
textarea = super(MarkDownWidget, self).render(name, value, attrs)
|
textarea = super(MarkDownWidget, self).render(name, value, attrs)
|
||||||
preview = ('<a class="load-preview" href="#" data-field="{0}">preview</a>'\
|
preview = ('<a class="load-preview" href="#" data-field="{0}">preview</a>'\
|
||||||
|
@ -35,18 +35,18 @@ class MessageInlineForm(forms.ModelForm):
|
||||||
""" Add message form """
|
""" Add message form """
|
||||||
created_on = forms.CharField(label="Created On", required=False)
|
created_on = forms.CharField(label="Created On", required=False)
|
||||||
content = forms.CharField(widget=MarkDownWidget(), required=False)
|
content = forms.CharField(widget=MarkDownWidget(), required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('author', 'author_name', 'created_on', 'content')
|
fields = ('author', 'author_name', 'created_on', 'content')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(MessageInlineForm, self).__init__(*args, **kwargs)
|
super(MessageInlineForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['created_on'].widget = SpanWidget(display='')
|
self.fields['created_on'].widget = SpanWidget(display='')
|
||||||
|
|
||||||
def clean_content(self):
|
def clean_content(self):
|
||||||
""" clean HTML tags """
|
""" clean HTML tags """
|
||||||
return strip_tags(self.cleaned_data['content'])
|
return strip_tags(self.cleaned_data['content'])
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.instance.pk is None:
|
if self.instance.pk is None:
|
||||||
self.instance.author = self.user
|
self.instance.author = self.user
|
||||||
|
@ -58,7 +58,7 @@ class UsersIterator(forms.models.ModelChoiceIterator):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.ticket = kwargs.pop('ticket', False)
|
self.ticket = kwargs.pop('ticket', False)
|
||||||
super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs)
|
super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield ('', '---------')
|
yield ('', '---------')
|
||||||
users = get_user_model().objects.exclude(is_active=False).order_by('name')
|
users = get_user_model().objects.exclude(is_active=False).order_by('name')
|
||||||
|
@ -73,14 +73,14 @@ class UsersIterator(forms.models.ModelChoiceIterator):
|
||||||
class TicketForm(forms.ModelForm):
|
class TicketForm(forms.ModelForm):
|
||||||
display_description = forms.CharField(label=_("Description"), required=False)
|
display_description = forms.CharField(label=_("Description"), required=False)
|
||||||
description = forms.CharField(widget=MarkDownWidget(attrs={'class':'vLargeTextField'}))
|
description = forms.CharField(widget=MarkDownWidget(attrs={'class':'vLargeTextField'}))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ticket
|
model = Ticket
|
||||||
fields = (
|
fields = (
|
||||||
'creator', 'creator_name', 'owner', 'queue', 'subject', 'description',
|
'creator', 'creator_name', 'owner', 'queue', 'subject', 'description',
|
||||||
'priority', 'state', 'cc', 'display_description'
|
'priority', 'state', 'cc', 'display_description'
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TicketForm, self).__init__(*args, **kwargs)
|
super(TicketForm, self).__init__(*args, **kwargs)
|
||||||
ticket = kwargs.get('instance', False)
|
ticket = kwargs.get('instance', False)
|
||||||
|
@ -101,7 +101,7 @@ class TicketForm(forms.ModelForm):
|
||||||
description = '<div style="padding-left: 95px;">%s</div>' % description
|
description = '<div style="padding-left: 95px;">%s</div>' % description
|
||||||
widget = SpanWidget(display=description)
|
widget = SpanWidget(display=description)
|
||||||
self.fields['display_description'].widget = widget
|
self.fields['display_description'].widget = widget
|
||||||
|
|
||||||
def clean_description(self):
|
def clean_description(self):
|
||||||
""" clean HTML tags """
|
""" clean HTML tags """
|
||||||
return strip_tags(self.cleaned_data['description'])
|
return strip_tags(self.cleaned_data['description'])
|
||||||
|
|
|
@ -12,7 +12,7 @@ from .models import List
|
||||||
|
|
||||||
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = List.address_domain.field.model
|
model = List.address_domain.field.related_model
|
||||||
fields = ('url', 'id', 'name')
|
fields = ('url', 'id', 'name')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.contrib import admin, messages
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import F, Count, Value as V
|
from django.db.models import F, Count, Value as V
|
||||||
from django.db.models.functions import Concat
|
from django.db.models.functions import Concat
|
||||||
|
from django.utils.html import format_html, format_html_join
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -82,6 +83,7 @@ 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(),)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_addresses(self, mailbox):
|
def display_addresses(self, mailbox):
|
||||||
# Get from forwards
|
# Get from forwards
|
||||||
cache = caches.get_request_cache()
|
cache = caches.get_request_cache()
|
||||||
|
@ -93,7 +95,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
qs = qs.values_list('id', 'email', 'forward')
|
qs = qs.values_list('id', 'email', 'forward')
|
||||||
for addr_id, email, mbox in qs:
|
for addr_id, email, mbox in qs:
|
||||||
url = reverse('admin:mailboxes_address_change', args=(addr_id,))
|
url = reverse('admin:mailboxes_address_change', args=(addr_id,))
|
||||||
link = '<a href="%s">%s</a>' % (url, email)
|
link = format_html('<a href="{}">{}</a>', url, email)
|
||||||
try:
|
try:
|
||||||
cached_forwards[mbox].append(link)
|
cached_forwards[mbox].append(link)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -107,26 +109,23 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
addresses = []
|
addresses = []
|
||||||
for addr in mailbox.addresses.all():
|
for addr in mailbox.addresses.all():
|
||||||
url = change_url(addr)
|
url = change_url(addr)
|
||||||
addresses.append('<a href="%s">%s</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")
|
display_addresses.short_description = _("Addresses")
|
||||||
display_addresses.allow_tags = True
|
|
||||||
|
|
||||||
def display_forwards(self, mailbox):
|
def display_forwards(self, mailbox):
|
||||||
forwards = []
|
forwards = mailbox.get_forwards()
|
||||||
for addr in mailbox.get_forwards():
|
return format_html_join(
|
||||||
url = change_url(addr)
|
'<br>', '<a href="{}">{}</a>',
|
||||||
forwards.append('<a href="%s">%s</a>' % (url, addr.email))
|
[(change_url(addr), addr.email) for addr in forwards]
|
||||||
return '<br>'.join(forwards)
|
)
|
||||||
display_forwards.short_description = _("Forward from")
|
display_forwards.short_description = _("Forward from")
|
||||||
display_forwards.allow_tags = True
|
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_filtering(self, mailbox):
|
def display_filtering(self, mailbox):
|
||||||
""" becacuse of allow_tags = True """
|
|
||||||
return mailbox.get_filtering_display()
|
return mailbox.get_filtering_display()
|
||||||
display_filtering.short_description = _("Filtering")
|
display_filtering.short_description = _("Filtering")
|
||||||
display_filtering.admin_order_field = 'filtering'
|
display_filtering.admin_order_field = 'filtering'
|
||||||
display_filtering.allow_tags = True
|
|
||||||
|
|
||||||
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':
|
||||||
|
@ -217,7 +216,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
elif obj.custom_filtering:
|
elif obj.custom_filtering:
|
||||||
messages.warning(request, msg)
|
messages.warning(request, msg)
|
||||||
super(MailboxAdmin, self).save_model(request, obj, form, change)
|
super(MailboxAdmin, self).save_model(request, obj, form, change)
|
||||||
obj.addresses = form.cleaned_data['addresses']
|
obj.addresses.set(form.cleaned_data['addresses'])
|
||||||
|
|
||||||
|
|
||||||
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
|
@ -247,29 +246,27 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
|
|
||||||
def email_link(self, address):
|
def email_link(self, address):
|
||||||
link = self.domain_link(address)
|
link = self.domain_link(address)
|
||||||
return "%s@%s" % (address.name, link)
|
return format_html("{}@{}", address.name, link)
|
||||||
email_link.short_description = _("Email")
|
email_link.short_description = _("Email")
|
||||||
email_link.allow_tags = True
|
|
||||||
|
|
||||||
def display_mailboxes(self, address):
|
def display_mailboxes(self, address):
|
||||||
boxes = []
|
boxes = address.mailboxes.all()
|
||||||
for mailbox in address.mailboxes.all():
|
return format_html_join(
|
||||||
url = change_url(mailbox)
|
'<br>', '<a href="{}">{}</a>',
|
||||||
boxes.append('<a href="%s">%s</a>' % (url, mailbox.name))
|
[(change_url(mailbox), mailbox.name) for mailbox in boxes]
|
||||||
return '<br>'.join(boxes)
|
)
|
||||||
display_mailboxes.short_description = _("Mailboxes")
|
display_mailboxes.short_description = _("Mailboxes")
|
||||||
display_mailboxes.allow_tags = True
|
|
||||||
display_mailboxes.admin_order_field = 'mailboxes__count'
|
display_mailboxes.admin_order_field = 'mailboxes__count'
|
||||||
|
|
||||||
def display_all_mailboxes(self, address):
|
def display_all_mailboxes(self, address):
|
||||||
boxes = []
|
boxes = address.get_mailboxes()
|
||||||
for mailbox in address.get_mailboxes():
|
return format_html_join(
|
||||||
url = change_url(mailbox)
|
'<br>', '<a href="{}">{}</a>',
|
||||||
boxes.append('<a href="%s">%s</a>' % (url, mailbox.name))
|
[(change_url(mailbox), mailbox.name) for mailbox in boxes]
|
||||||
return '<br>'.join(boxes)
|
)
|
||||||
display_all_mailboxes.short_description = _("Mailboxes links")
|
display_all_mailboxes.short_description = _("Mailboxes links")
|
||||||
display_all_mailboxes.allow_tags = True
|
|
||||||
|
|
||||||
|
@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()}
|
||||||
values = []
|
values = []
|
||||||
|
@ -281,7 +278,6 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
values.append(forward)
|
values.append(forward)
|
||||||
return '<br>'.join(values)
|
return '<br>'.join(values)
|
||||||
display_forward.short_description = _("Forward")
|
display_forward.short_description = _("Forward")
|
||||||
display_forward.allow_tags = True
|
|
||||||
display_forward.admin_order_field = 'forward'
|
display_forward.admin_order_field = 'forward'
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Mailbox(models.Model):
|
||||||
def active(self):
|
def active(self):
|
||||||
try:
|
try:
|
||||||
return self.is_active and self.account.is_active
|
return self.is_active and self.account.is_active
|
||||||
except type(self).account.field.model.DoesNotExist:
|
except type(self).account.field.related_model.DoesNotExist:
|
||||||
return self.is_active
|
return self.is_active
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -60,11 +62,10 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
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 subject[:64] + '…'
|
return mark_safe(subject[:64] + '…')
|
||||||
return subject
|
return subject
|
||||||
display_subject.short_description = _("Subject")
|
display_subject.short_description = _("Subject")
|
||||||
display_subject.admin_order_field = 'subject'
|
display_subject.admin_order_field = 'subject'
|
||||||
display_subject.allow_tags = True
|
|
||||||
|
|
||||||
def display_retries(self, instance):
|
def display_retries(self, instance):
|
||||||
num_logs = instance.logs__count
|
num_logs = instance.logs__count
|
||||||
|
@ -74,10 +75,9 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
else:
|
else:
|
||||||
url = reverse('admin:mailer_smtplog_changelist')
|
url = reverse('admin:mailer_smtplog_changelist')
|
||||||
url += '?&message=%i' % instance.pk
|
url += '?&message=%i' % instance.pk
|
||||||
return '<a href="%s" onclick="return showAddAnotherPopup(this);">%d</a>' % (url, instance.retries)
|
return format_html('<a href="{}" onclick="return showAddAnotherPopup(this);">{}</a>', url, instance.retries)
|
||||||
display_retries.short_description = _("Retries")
|
display_retries.short_description = _("Retries")
|
||||||
display_retries.admin_order_field = 'retries'
|
display_retries.admin_order_field = 'retries'
|
||||||
display_retries.allow_tags = True
|
|
||||||
|
|
||||||
def display_content(self, instance):
|
def display_content(self, instance):
|
||||||
part = email.message_from_string(instance.content)
|
part = email.message_from_string(instance.content)
|
||||||
|
@ -99,9 +99,8 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
payload = payload.decode(charset)
|
payload = payload.decode(charset)
|
||||||
if part.get_content_type() == 'text/plain':
|
if part.get_content_type() == 'text/plain':
|
||||||
payload = payload.replace('\n', '<br>').replace(' ', ' ')
|
payload = payload.replace('\n', '<br>').replace(' ', ' ')
|
||||||
return payload
|
return mark_safe(payload)
|
||||||
display_content.short_description = _("Content")
|
display_content.short_description = _("Content")
|
||||||
display_content.allow_tags = True
|
|
||||||
|
|
||||||
def display_full_subject(self, instance):
|
def display_full_subject(self, instance):
|
||||||
return instance.subject
|
return instance.subject
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -38,15 +39,13 @@ class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
actions = (disable, enable)
|
actions = (disable, enable)
|
||||||
|
|
||||||
def display_name(self, misc):
|
def display_name(self, misc):
|
||||||
return '<span title="%s">%s</span>' % (misc.description, misc.name)
|
return format_html('<span title="{}">{}</span>', misc.description, misc.name)
|
||||||
display_name.short_description = _("name")
|
display_name.short_description = _("name")
|
||||||
display_name.allow_tags = True
|
|
||||||
display_name.admin_order_field = 'name'
|
display_name.admin_order_field = 'name'
|
||||||
|
|
||||||
def display_verbose_name(self, misc):
|
def display_verbose_name(self, misc):
|
||||||
return '<span title="%s">%s</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.short_description = _("verbose name")
|
||||||
display_verbose_name.allow_tags = True
|
|
||||||
display_verbose_name.admin_order_field = 'verbose_name'
|
display_verbose_name.admin_order_field = 'verbose_name'
|
||||||
|
|
||||||
def num_instances(self, misc):
|
def num_instances(self, misc):
|
||||||
|
|
|
@ -51,19 +51,18 @@ class RouteAdmin(ExtendedModelAdmin):
|
||||||
|
|
||||||
def display_model(self, route):
|
def display_model(self, route):
|
||||||
try:
|
try:
|
||||||
return escape(route.backend_class.model)
|
return route.backend_class.model
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return "<span style='color: red;'>NOT AVAILABLE</span>"
|
return mark_safe("<span style='color: red;'>NOT AVAILABLE</span>")
|
||||||
display_model.short_description = _("model")
|
display_model.short_description = _("model")
|
||||||
display_model.allow_tags = True
|
|
||||||
|
|
||||||
|
@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")
|
display_actions.short_description = _("actions")
|
||||||
display_actions.allow_tags = True
|
|
||||||
|
|
||||||
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 """
|
||||||
|
@ -120,7 +119,6 @@ class BackendOperationInline(admin.TabularInline):
|
||||||
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.allow_tags = True
|
|
||||||
instance_link.short_description = _("Instance")
|
instance_link.short_description = _("Instance")
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
|
@ -179,14 +177,12 @@ class ServerAdmin(ExtendedModelAdmin):
|
||||||
change_view_actions = actions
|
change_view_actions = actions
|
||||||
|
|
||||||
def display_ping(self, instance):
|
def display_ping(self, instance):
|
||||||
return self._remote_state[instance.pk][0]
|
return mark_safe(self._remote_state[instance.pk][0])
|
||||||
display_ping.short_description = _("Ping")
|
display_ping.short_description = _("Ping")
|
||||||
display_ping.allow_tags = True
|
|
||||||
|
|
||||||
def display_uptime(self, instance):
|
def display_uptime(self, instance):
|
||||||
return self._remote_state[instance.pk][1]
|
return mark_safe(self._remote_state[instance.pk][1])
|
||||||
display_uptime.short_description = _("Uptime")
|
display_uptime.short_description = _("Uptime")
|
||||||
display_uptime.allow_tags = True
|
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
""" Order by structured name and imporve performance """
|
""" Order by structured name and imporve performance """
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from orchestra.forms.widgets import SpanWidget, paddingCheckboxSelectMultiple
|
from orchestra.forms.widgets import SpanWidget, PaddingCheckboxSelectMultiple
|
||||||
|
|
||||||
|
|
||||||
class RouteForm(forms.ModelForm):
|
class RouteForm(forms.ModelForm):
|
||||||
|
@ -16,5 +16,5 @@ class RouteForm(forms.ModelForm):
|
||||||
else:
|
else:
|
||||||
self.fields['backend'].widget = SpanWidget()
|
self.fields['backend'].widget = SpanWidget()
|
||||||
actions = backend_class.actions
|
actions = backend_class.actions
|
||||||
self.fields['async_actions'].widget = paddingCheckboxSelectMultiple(45)
|
self.fields['async_actions'].widget = PaddingCheckboxSelectMultiple(45)
|
||||||
self.fields['async_actions'].choices = ((action, action) for action in actions)
|
self.fields['async_actions'].choices = ((action, action) for action in actions)
|
||||||
|
|
|
@ -51,8 +51,9 @@ class Server(models.Model):
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.name = self.name.strip()
|
self.name = self.name.strip()
|
||||||
self.address = self.address.strip()
|
if self.address:
|
||||||
if self.name and not self.address:
|
self.address = self.address.strip()
|
||||||
|
elif self.name:
|
||||||
validate = OrValidator(validate_ip_address, validate_hostname)
|
validate = OrValidator(validate_ip_address, validate_hostname)
|
||||||
validate_hostname(self.name)
|
validate_hostname(self.name)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,7 +14,12 @@ def retrieve_state(servers):
|
||||||
state = {}
|
state = {}
|
||||||
for server, ping, uptime in zip(servers, pings, uptimes):
|
for server, ping, uptime in zip(servers, pings, uptimes):
|
||||||
ping = join(ping, silent=True)
|
ping = join(ping, silent=True)
|
||||||
ping = ping.stdout.splitlines()[-1].decode()
|
|
||||||
|
try:
|
||||||
|
ping = ping.stdout.splitlines()[-1].decode()
|
||||||
|
except IndexError:
|
||||||
|
ping = ''
|
||||||
|
|
||||||
if ping.startswith('rtt'):
|
if ping.startswith('rtt'):
|
||||||
ping = '%s ms' % ping.split('/')[4]
|
ping = '%s ms' % ping.split('/')[4]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from datetime import datetime
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape, format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -112,9 +113,8 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_cancelled_on = admin_date('cancelled_on')
|
display_cancelled_on = admin_date('cancelled_on')
|
||||||
|
|
||||||
def display_description(self, order):
|
def display_description(self, order):
|
||||||
return order.description[:64]
|
return format_html(order.description[:64])
|
||||||
display_description.short_description = _("Description")
|
display_description.short_description = _("Description")
|
||||||
display_description.allow_tags = True
|
|
||||||
display_description.admin_order_field = 'description'
|
display_description.admin_order_field = 'description'
|
||||||
|
|
||||||
def content_object_link(self, order):
|
def content_object_link(self, order):
|
||||||
|
@ -125,13 +125,13 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
# Does not has admin
|
# Does not has admin
|
||||||
return order.content_object_repr
|
return order.content_object_repr
|
||||||
description = str(order.content_object)
|
description = str(order.content_object)
|
||||||
return '<a href="{url}">{description}</a>'.format(
|
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.short_description = _("Content object")
|
||||||
content_object_link.allow_tags = True
|
|
||||||
content_object_link.admin_order_field = 'content_object_repr'
|
content_object_link.admin_order_field = 'content_object_repr'
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def bills_links(self, order):
|
def bills_links(self, order):
|
||||||
bills = []
|
bills = []
|
||||||
make_link = admin_link()
|
make_link = admin_link()
|
||||||
|
@ -139,7 +139,6 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
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")
|
bills_links.short_description = _("Bills")
|
||||||
bills_links.allow_tags = True
|
|
||||||
|
|
||||||
def display_billed_until(self, order):
|
def display_billed_until(self, order):
|
||||||
billed_until = order.billed_until
|
billed_until = order.billed_until
|
||||||
|
@ -156,12 +155,12 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
red = True
|
red = True
|
||||||
elif billed_until < timezone.now().date():
|
elif billed_until < timezone.now().date():
|
||||||
red = True
|
red = True
|
||||||
color = 'style="color:red;"' if red else ''
|
color = mark_safe('style="color:red;"') if red else ''
|
||||||
return '<span title="{raw}" {color}>{human}</span>'.format(
|
return format_html(
|
||||||
|
'<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.short_description = _("billed until")
|
||||||
display_billed_until.allow_tags = True
|
|
||||||
display_billed_until.admin_order_field = 'billed_until'
|
display_billed_until.admin_order_field = 'billed_until'
|
||||||
|
|
||||||
def display_metric(self, order):
|
def display_metric(self, order):
|
||||||
|
|
|
@ -15,7 +15,7 @@ def cancel_orders(sender, **kwargs):
|
||||||
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
# Account delete will delete all related orders, no need to maintain order consistency
|
# Account delete will delete all related orders, no need to maintain order consistency
|
||||||
if isinstance(instance, Order.account.field.model):
|
if isinstance(instance, Order.account.field.related_model):
|
||||||
return
|
return
|
||||||
if type(instance) in services:
|
if type(instance) in services:
|
||||||
for order in Order.objects.by_object(instance).active():
|
for order in Order.objects.by_object(instance).active():
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
|
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
|
||||||
|
@ -154,6 +156,7 @@ 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]
|
||||||
|
|
||||||
|
@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()
|
||||||
|
@ -161,7 +164,6 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
return state
|
return state
|
||||||
display_state.admin_order_field = 'state'
|
display_state.admin_order_field = 'state'
|
||||||
display_state.short_description = _("State")
|
display_state.short_description = _("State")
|
||||||
display_state.allow_tags = True
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
|
@ -184,10 +186,10 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
|
|
||||||
def file_url(self, process):
|
def file_url(self, process):
|
||||||
if process.file:
|
if process.file:
|
||||||
return '<a href="%s">%s</a>' % (process.file.url, process.file.name)
|
return format_html('<a href="{}">{}</a>', process.file.url, process.file.name)
|
||||||
file_url.allow_tags = True
|
|
||||||
file_url.admin_order_field = 'file'
|
file_url.admin_order_field = 'file'
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_transactions(self, process):
|
def display_transactions(self, process):
|
||||||
ids = []
|
ids = []
|
||||||
lines = []
|
lines = []
|
||||||
|
@ -207,7 +209,6 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
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")
|
display_transactions.short_description = _("Transactions")
|
||||||
display_transactions.allow_tags = True
|
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -33,10 +34,9 @@ class PlanAdmin(ExtendedModelAdmin):
|
||||||
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 '<a href="{0}">{1}</a>'.format(url, num)
|
return format_html('<a href="{0}">{1}</a>', url, num)
|
||||||
num_contracts.short_description = _("Contracts")
|
num_contracts.short_description = _("Contracts")
|
||||||
num_contracts.admin_order_field = 'contracts__count'
|
num_contracts.admin_order_field = 'contracts__count'
|
||||||
num_contracts.allow_tags = True
|
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(PlanAdmin, self).get_queryset(request)
|
qs = super(PlanAdmin, self).get_queryset(request)
|
||||||
|
|
|
@ -11,6 +11,7 @@ from django.db.models import Q
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -105,10 +106,9 @@ class ResourceAdmin(ExtendedModelAdmin):
|
||||||
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 '<a href="%s">%s</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.short_description = _("Content object")
|
||||||
content_object_link.admin_order_field = 'content_object_repr'
|
content_object_link.admin_order_field = 'content_object_repr'
|
||||||
content_object_link.allow_tags = True
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceDataAdmin(ExtendedModelAdmin):
|
class ResourceDataAdmin(ExtendedModelAdmin):
|
||||||
|
@ -155,10 +155,9 @@ class ResourceDataAdmin(ExtendedModelAdmin):
|
||||||
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 '<a href="%s">%s %s</a>' % (url, rdata.used, rdata.unit)
|
return format_html('<a href="{}">{} {}</a>', url, rdata.used, rdata.unit)
|
||||||
display_used.short_description = _("Used")
|
display_used.short_description = _("Used")
|
||||||
display_used.admin_order_field = 'used'
|
display_used.admin_order_field = 'used'
|
||||||
display_used.allow_tags = True
|
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
@ -304,6 +303,7 @@ 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)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_used(self, rdata):
|
def display_used(self, rdata):
|
||||||
update = ''
|
update = ''
|
||||||
history = ''
|
history = ''
|
||||||
|
@ -329,7 +329,6 @@ def resource_inline_factory(resources):
|
||||||
return _("Unknonw %s %s") % (update, history)
|
return _("Unknonw %s %s") % (update, history)
|
||||||
return _("No monitor")
|
return _("No monitor")
|
||||||
display_used.short_description = _("Used")
|
display_used.short_description = _("Used")
|
||||||
display_used.allow_tags = True
|
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
""" Hidde add another """
|
""" Hidde add another """
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
|
@ -26,7 +27,8 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
|
||||||
plugin_field = 'service'
|
plugin_field = 'service'
|
||||||
plugin_title = 'Software as a Service'
|
plugin_title = 'Software as a Service'
|
||||||
actions = (disable, enable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_url(self, saas):
|
def display_url(self, saas):
|
||||||
site_domain = saas.get_site_domain()
|
site_domain = saas.get_site_domain()
|
||||||
site_link = '<a href="http://%s">%s</a>' % (site_domain, site_domain)
|
site_link = '<a href="http://%s">%s</a>' % (site_domain, site_domain)
|
||||||
|
@ -46,9 +48,8 @@ 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.short_description = _("URL")
|
||||||
display_url.allow_tags = True
|
|
||||||
display_url.admin_order_field = 'name'
|
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)
|
||||||
if not self.plugin_instance.allow_custom_url:
|
if not self.plugin_instance.allow_custom_url:
|
||||||
|
|
|
@ -42,7 +42,7 @@ def clean_custom_url(saas):
|
||||||
)
|
)
|
||||||
except Website.DoesNotExist:
|
except Website.DoesNotExist:
|
||||||
# get or create domain
|
# get or create domain
|
||||||
Domain = Website.domains.field.model
|
Domain = Website.domains.field.related_model
|
||||||
try:
|
try:
|
||||||
domain = Domain.objects.get(name=url.netloc)
|
domain = Domain.objects.get(name=url.netloc)
|
||||||
except Domain.DoesNotExist:
|
except Domain.DoesNotExist:
|
||||||
|
@ -110,7 +110,7 @@ def create_or_update_directive(saas):
|
||||||
account=account,
|
account=account,
|
||||||
)
|
)
|
||||||
except Website.DoesNotExist:
|
except Website.DoesNotExist:
|
||||||
Domain = Website.domains.field.model
|
Domain = Website.domains.field.related_model
|
||||||
domain = Domain.objects.get(name=url.netloc)
|
domain = Domain.objects.get(name=url.netloc)
|
||||||
# Create new website for custom_url
|
# Create new website for custom_url
|
||||||
tgt_server = Server.objects.get(name='web.pangea.lan')
|
tgt_server = Server.objects.get(name='web.pangea.lan')
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ChangeViewActionsMixin
|
from orchestra.admin import ChangeViewActionsMixin
|
||||||
|
@ -69,10 +70,9 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
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 '<a href="%s">%d</a>' % (url, num)
|
return format_html('<a href="{}">{}</a>', url, num)
|
||||||
num_orders.short_description = _("Orders")
|
num_orders.short_description = _("Orders")
|
||||||
num_orders.admin_order_field = 'orders__count'
|
num_orders.admin_order_field = 'orders__count'
|
||||||
num_orders.allow_tags = True
|
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(ServiceAdmin, self).get_queryset(request)
|
qs = super(ServiceAdmin, self).get_queryset(request)
|
||||||
|
|
|
@ -61,7 +61,7 @@ class SystemUser(models.Model):
|
||||||
def active(self):
|
def active(self):
|
||||||
try:
|
try:
|
||||||
return self.is_active and self.account.is_active
|
return self.is_active and self.account.is_active
|
||||||
except type(self).account.field.model.DoesNotExist:
|
except type(self).account.field.related_model.DoesNotExist:
|
||||||
return self.is_active
|
return self.is_active
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -66,6 +67,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
||||||
|
|
||||||
display_type = display_plugin_field('type')
|
display_type = display_plugin_field('type')
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_websites(self, webapp):
|
def display_websites(self, webapp):
|
||||||
websites = []
|
websites = []
|
||||||
for content in webapp.content_set.all():
|
for content in webapp.content_set.all():
|
||||||
|
@ -82,29 +84,13 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
||||||
websites.append('<a href="%s">%s%s</a>' % (add_url, plus, ugettext("Add website")))
|
websites.append('<a href="%s">%s%s</a>' % (add_url, plus, ugettext("Add website")))
|
||||||
return '<br>'.join(websites)
|
return '<br>'.join(websites)
|
||||||
display_websites.short_description = _("web sites")
|
display_websites.short_description = _("web sites")
|
||||||
display_websites.allow_tags = True
|
|
||||||
|
|
||||||
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 "<span style='color:red;'>Not available</span>"
|
return mark_safe("<span style='color:red;'>Not available</span>")
|
||||||
display_detail.short_description = _("detail")
|
display_detail.short_description = _("detail")
|
||||||
display_detail.allow_tags = True
|
|
||||||
|
|
||||||
# def get_form(self, request, obj=None, **kwargs):
|
|
||||||
# form = super(WebAppAdmin, self).get_form(request, obj, **kwargs)
|
|
||||||
# if obj:
|
|
||||||
#
|
|
||||||
|
|
||||||
# def formfield_for_dbfield(self, db_field, **kwargs):
|
|
||||||
# """ Make value input widget bigger """
|
|
||||||
# if db_field.name == 'type':
|
|
||||||
# # Help text based on select widget
|
|
||||||
# kwargs['widget'] = DynamicHelpTextSelect(
|
|
||||||
# 'this.id.replace("name", "value")', self.TYPE_HELP_TEXT
|
|
||||||
# )
|
|
||||||
# kwargs['help_text'] = self.TYPE_HELP_TEXT.get(db_field.default, '')
|
|
||||||
# return super(WebAppAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
|
||||||
|
|
||||||
admin.site.register(WebApp, WebAppAdmin)
|
admin.site.register(WebApp, WebAppAdmin)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import textwrap
|
import textwrap
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.template import Template, Context
|
from django.template import Template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.contrib.orchestration import ServiceController
|
from orchestra.contrib.orchestration import ServiceController
|
||||||
|
@ -17,7 +17,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
It handles switching between these two PHP process management systemes.
|
It handles switching between these two PHP process management systemes.
|
||||||
"""
|
"""
|
||||||
MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
|
MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
|
||||||
|
|
||||||
verbose_name = _("PHP FPM/FCGID")
|
verbose_name = _("PHP FPM/FCGID")
|
||||||
default_route_match = "webapp.type.endswith('php')"
|
default_route_match = "webapp.type.endswith('php')"
|
||||||
doc_settings = (settings, (
|
doc_settings = (settings, (
|
||||||
|
@ -30,7 +30,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
'WEBAPPS_PHPFPM_POOL_PATH',
|
'WEBAPPS_PHPFPM_POOL_PATH',
|
||||||
'WEBAPPS_PHP_MAX_REQUESTS',
|
'WEBAPPS_PHP_MAX_REQUESTS',
|
||||||
))
|
))
|
||||||
|
|
||||||
def save(self, webapp):
|
def save(self, webapp):
|
||||||
self.delete_old_config(webapp)
|
self.delete_old_config(webapp)
|
||||||
context = self.get_context(webapp)
|
context = self.get_context(webapp)
|
||||||
|
@ -81,7 +81,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
}
|
}
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
|
|
||||||
def save_fcgid(self, webapp, context):
|
def save_fcgid(self, webapp, context):
|
||||||
self.append("mkdir -p %(wrapper_dir)s" % context)
|
self.append("mkdir -p %(wrapper_dir)s" % context)
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
|
@ -118,7 +118,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.append("rm -f %(cmd_options_path)s\n" % context)
|
self.append("rm -f %(cmd_options_path)s\n" % context)
|
||||||
|
|
||||||
def delete(self, webapp):
|
def delete(self, webapp):
|
||||||
context = self.get_context(webapp)
|
context = self.get_context(webapp)
|
||||||
self.delete_old_config(webapp)
|
self.delete_old_config(webapp)
|
||||||
|
@ -127,13 +127,13 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
# elif webapp.type_instance.is_fcgid:
|
# elif webapp.type_instance.is_fcgid:
|
||||||
# self.delete_fcgid(webapp, context)
|
# self.delete_fcgid(webapp, context)
|
||||||
self.delete_webapp_dir(context)
|
self.delete_webapp_dir(context)
|
||||||
|
|
||||||
def has_sibilings(self, webapp, context):
|
def has_sibilings(self, webapp, context):
|
||||||
return type(webapp).objects.filter(
|
return type(webapp).objects.filter(
|
||||||
account=webapp.account_id,
|
account=webapp.account_id,
|
||||||
data__contains='"php_version":"%s"' % context['php_version'],
|
data__contains='"php_version":"%s"' % context['php_version'],
|
||||||
).exclude(id=webapp.pk).exists()
|
).exclude(id=webapp.pk).exists()
|
||||||
|
|
||||||
def all_versions_to_delete(self, webapp, context, preserve=False):
|
def all_versions_to_delete(self, webapp, context, preserve=False):
|
||||||
context_copy = dict(context)
|
context_copy = dict(context)
|
||||||
for php_version, verbose in settings.WEBAPPS_PHP_VERSIONS:
|
for php_version, verbose in settings.WEBAPPS_PHP_VERSIONS:
|
||||||
|
@ -144,13 +144,13 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
context_copy['php_version_number'] = php_version_number
|
context_copy['php_version_number'] = php_version_number
|
||||||
if not self.MERGE or not self.has_sibilings(webapp, context_copy):
|
if not self.MERGE or not self.has_sibilings(webapp, context_copy):
|
||||||
yield context_copy
|
yield context_copy
|
||||||
|
|
||||||
def delete_fpm(self, webapp, context, preserve=False):
|
def delete_fpm(self, webapp, context, preserve=False):
|
||||||
""" delete all pools in order to efectively support changing php-fpm version """
|
""" delete all pools in order to efectively support changing php-fpm version """
|
||||||
for context_copy in self.all_versions_to_delete(webapp, context, preserve):
|
for context_copy in self.all_versions_to_delete(webapp, context, preserve):
|
||||||
context_copy['fpm_path'] = settings.WEBAPPS_PHPFPM_POOL_PATH % context_copy
|
context_copy['fpm_path'] = settings.WEBAPPS_PHPFPM_POOL_PATH % context_copy
|
||||||
self.append("rm -f %(fpm_path)s" % context_copy)
|
self.append("rm -f %(fpm_path)s" % context_copy)
|
||||||
|
|
||||||
def delete_fcgid(self, webapp, context, preserve=False):
|
def delete_fcgid(self, webapp, context, preserve=False):
|
||||||
""" delete all pools in order to efectively support changing php-fcgid version """
|
""" delete all pools in order to efectively support changing php-fcgid version """
|
||||||
for context_copy in self.all_versions_to_delete(webapp, context, preserve):
|
for context_copy in self.all_versions_to_delete(webapp, context, preserve):
|
||||||
|
@ -160,13 +160,13 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
})
|
})
|
||||||
self.append("rm -f %(wrapper_path)s" % context_copy)
|
self.append("rm -f %(wrapper_path)s" % context_copy)
|
||||||
self.append("rm -f %(cmd_options_path)s" % context_copy)
|
self.append("rm -f %(cmd_options_path)s" % context_copy)
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
super(PHPController, self).prepare()
|
super(PHPController, self).prepare()
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
BACKEND="PHPController"
|
BACKEND="PHPController"
|
||||||
echo "$BACKEND" >> /dev/shm/reload.apache2
|
echo "$BACKEND" >> /dev/shm/reload.apache2
|
||||||
|
|
||||||
function coordinate_apache_reload () {
|
function coordinate_apache_reload () {
|
||||||
# Coordinate Apache reload with other concurrent backends (e.g. Apache2Controller)
|
# Coordinate Apache reload with other concurrent backends (e.g. Apache2Controller)
|
||||||
is_last=0
|
is_last=0
|
||||||
|
@ -203,7 +203,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
fi
|
fi
|
||||||
}""")
|
}""")
|
||||||
)
|
)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
context = {
|
context = {
|
||||||
'reload_pool': settings.WEBAPPS_PHPFPM_RELOAD_POOL,
|
'reload_pool': settings.WEBAPPS_PHPFPM_RELOAD_POOL,
|
||||||
|
@ -217,7 +217,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
super(PHPController, self).commit()
|
super(PHPController, self).commit()
|
||||||
|
|
||||||
def get_fpm_config(self, webapp, context):
|
def get_fpm_config(self, webapp, context):
|
||||||
options = webapp.type_instance.get_options()
|
options = webapp.type_instance.get_options()
|
||||||
context.update({
|
context.update({
|
||||||
|
@ -231,11 +231,11 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
[{{ user }}-{{app_name}}]
|
[{{ user }}-{{app_name}}]
|
||||||
user = {{ user }}
|
user = {{ user }}
|
||||||
group = {{ group }}
|
group = {{ group }}
|
||||||
|
|
||||||
listen = {{ fpm_listen | safe }}
|
listen = {{ fpm_listen | safe }}
|
||||||
listen.owner = {{ user }}
|
listen.owner = {{ user }}
|
||||||
listen.group = {{ group }}
|
listen.group = {{ group }}
|
||||||
|
|
||||||
pm = ondemand
|
pm = ondemand
|
||||||
pm.max_requests = {{ max_requests }}
|
pm.max_requests = {{ max_requests }}
|
||||||
pm.max_children = {{ max_children }}
|
pm.max_children = {{ max_children }}
|
||||||
|
@ -245,8 +245,8 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
|
php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
|
||||||
"""
|
"""
|
||||||
))
|
))
|
||||||
return fpm_config.render(Context(context))
|
return fpm_config.render(context)
|
||||||
|
|
||||||
def get_fcgid_wrapper(self, webapp, context):
|
def get_fcgid_wrapper(self, webapp, context):
|
||||||
opt = webapp.type_instance
|
opt = webapp.type_instance
|
||||||
# Format PHP init vars
|
# Format PHP init vars
|
||||||
|
@ -268,7 +268,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
export PHP_INI_SCAN_DIR=%(php_ini_scan)s
|
export PHP_INI_SCAN_DIR=%(php_ini_scan)s
|
||||||
export PHP_FCGI_MAX_REQUESTS=%(max_requests)s
|
export PHP_FCGI_MAX_REQUESTS=%(max_requests)s
|
||||||
exec %(php_binary_path)s%(php_init_vars)s""") % context
|
exec %(php_binary_path)s%(php_init_vars)s""") % context
|
||||||
|
|
||||||
def get_fcgid_cmd_options(self, webapp, context):
|
def get_fcgid_cmd_options(self, webapp, context):
|
||||||
options = webapp.type_instance.get_options()
|
options = webapp.type_instance.get_options()
|
||||||
maps = OrderedDict(
|
maps = OrderedDict(
|
||||||
|
@ -288,7 +288,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
) % context
|
) % context
|
||||||
cmd_options.insert(0, head)
|
cmd_options.insert(0, head)
|
||||||
return ' \\\n '.join(cmd_options)
|
return ' \\\n '.join(cmd_options)
|
||||||
|
|
||||||
def update_fcgid_context(self, webapp, context):
|
def update_fcgid_context(self, webapp, context):
|
||||||
wrapper_path = settings.WEBAPPS_FCGID_WRAPPER_PATH % context
|
wrapper_path = settings.WEBAPPS_FCGID_WRAPPER_PATH % context
|
||||||
context.update({
|
context.update({
|
||||||
|
@ -301,14 +301,14 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
||||||
'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
|
'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def update_fpm_context(self, webapp, context):
|
def update_fpm_context(self, webapp, context):
|
||||||
context.update({
|
context.update({
|
||||||
'fpm_config': self.get_fpm_config(webapp, context),
|
'fpm_config': self.get_fpm_config(webapp, context),
|
||||||
'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
|
'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_context(self, webapp):
|
def get_context(self, webapp):
|
||||||
context = super().get_context(webapp)
|
context = super().get_context(webapp)
|
||||||
context.update({
|
context.update({
|
||||||
|
|
|
@ -3,6 +3,8 @@ from django.contrib import admin
|
||||||
from django.urls import resolve
|
from django.urls import resolve
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -78,6 +80,7 @@ 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)
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_domains(self, website):
|
def display_domains(self, website):
|
||||||
domains = []
|
domains = []
|
||||||
for domain in website.domains.all():
|
for domain in website.domains.all():
|
||||||
|
@ -85,9 +88,9 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
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.short_description = _("domains")
|
||||||
display_domains.allow_tags = True
|
|
||||||
display_domains.admin_order_field = 'domains'
|
display_domains.admin_order_field = 'domains'
|
||||||
|
|
||||||
|
@mark_safe
|
||||||
def display_webapps(self, website):
|
def display_webapps(self, website):
|
||||||
webapps = []
|
webapps = []
|
||||||
for content in website.content_set.all():
|
for content in website.content_set.all():
|
||||||
|
@ -100,9 +103,9 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
pass
|
pass
|
||||||
url = change_url(webapp)
|
url = change_url(webapp)
|
||||||
name = "%s on %s" % (webapp.name, content.path or '/')
|
name = "%s on %s" % (webapp.name, content.path or '/')
|
||||||
webapps.append('<a href="%s" title="%s">%s %s</a>' % (url, detail, name, site_link))
|
webapp_info = format_html('<a href="{}" title="{}">{}</a> {}', url, detail, name, site_link)
|
||||||
|
webapps.append(webapp_info)
|
||||||
return '<br>'.join(webapps)
|
return '<br>'.join(webapps)
|
||||||
display_webapps.allow_tags = True
|
|
||||||
display_webapps.short_description = _("Web apps")
|
display_webapps.short_description = _("Web apps")
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from django.template import Template, Context
|
from django.template import Template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.contrib.orchestration import ServiceController
|
from orchestra.contrib.orchestration import ServiceController
|
||||||
|
@ -20,7 +20,7 @@ class Apache2Controller(ServiceController):
|
||||||
"""
|
"""
|
||||||
HTTP_PORT = 80
|
HTTP_PORT = 80
|
||||||
HTTPS_PORT = 443
|
HTTPS_PORT = 443
|
||||||
|
|
||||||
model = 'websites.Website'
|
model = 'websites.Website'
|
||||||
related_models = (
|
related_models = (
|
||||||
('websites.Content', 'website'),
|
('websites.Content', 'website'),
|
||||||
|
@ -37,7 +37,7 @@ class Apache2Controller(ServiceController):
|
||||||
'WEBSITES_DEFAULT_IPS',
|
'WEBSITES_DEFAULT_IPS',
|
||||||
'WEBSITES_SAAS_DIRECTIVES',
|
'WEBSITES_SAAS_DIRECTIVES',
|
||||||
))
|
))
|
||||||
|
|
||||||
def get_extra_conf(self, site, context, ssl=False):
|
def get_extra_conf(self, site, context, ssl=False):
|
||||||
extra_conf = self.get_content_directives(site, context)
|
extra_conf = self.get_content_directives(site, context)
|
||||||
directives = site.get_directives()
|
directives = site.get_directives()
|
||||||
|
@ -53,7 +53,7 @@ class Apache2Controller(ServiceController):
|
||||||
# Order extra conf directives based on directives (longer first)
|
# Order extra conf directives based on directives (longer first)
|
||||||
extra_conf = sorted(extra_conf, key=lambda a: len(a[0]), reverse=True)
|
extra_conf = sorted(extra_conf, key=lambda a: len(a[0]), reverse=True)
|
||||||
return '\n'.join([conf for location, conf in extra_conf])
|
return '\n'.join([conf for location, conf in extra_conf])
|
||||||
|
|
||||||
def render_virtual_host(self, site, context, ssl=False):
|
def render_virtual_host(self, site, context, ssl=False):
|
||||||
context.update({
|
context.update({
|
||||||
'port': self.HTTPS_PORT if ssl else self.HTTP_PORT,
|
'port': self.HTTPS_PORT if ssl else self.HTTP_PORT,
|
||||||
|
@ -78,8 +78,8 @@ class Apache2Controller(ServiceController):
|
||||||
{{ line | safe }}{% endfor %}
|
{{ line | safe }}{% endfor %}
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
""")
|
""")
|
||||||
).render(Context(context))
|
).render(context)
|
||||||
|
|
||||||
def render_redirect_https(self, context):
|
def render_redirect_https(self, context):
|
||||||
context['port'] = self.HTTP_PORT
|
context['port'] = self.HTTP_PORT
|
||||||
return Template(textwrap.dedent("""
|
return Template(textwrap.dedent("""
|
||||||
|
@ -96,8 +96,8 @@ class Apache2Controller(ServiceController):
|
||||||
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
|
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
""")
|
""")
|
||||||
).render(Context(context))
|
).render(context)
|
||||||
|
|
||||||
def save(self, site):
|
def save(self, site):
|
||||||
context = self.get_context(site)
|
context = self.get_context(site)
|
||||||
if context['server_name']:
|
if context['server_name']:
|
||||||
|
@ -133,7 +133,7 @@ class Apache2Controller(ServiceController):
|
||||||
[[ $(a2dissite %(site_unique_name)s) =~ "already disabled" ]] || UPDATED_APACHE=1\
|
[[ $(a2dissite %(site_unique_name)s) =~ "already disabled" ]] || UPDATED_APACHE=1\
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete(self, site):
|
def delete(self, site):
|
||||||
context = self.get_context(site)
|
context = self.get_context(site)
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
|
@ -142,14 +142,14 @@ class Apache2Controller(ServiceController):
|
||||||
rm -f %(sites_available)s\
|
rm -f %(sites_available)s\
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
super(Apache2Controller, self).prepare()
|
super(Apache2Controller, self).prepare()
|
||||||
# Coordinate apache restart with php backend in order not to overdo it
|
# Coordinate apache restart with php backend in order not to overdo it
|
||||||
self.append(textwrap.dedent("""
|
self.append(textwrap.dedent("""
|
||||||
BACKEND="Apache2Controller"
|
BACKEND="Apache2Controller"
|
||||||
echo "$BACKEND" >> /dev/shm/reload.apache2
|
echo "$BACKEND" >> /dev/shm/reload.apache2
|
||||||
|
|
||||||
function coordinate_apache_reload () {
|
function coordinate_apache_reload () {
|
||||||
# Coordinate Apache reload with other concurrent backends (e.g. PHPController)
|
# Coordinate Apache reload with other concurrent backends (e.g. PHPController)
|
||||||
is_last=0
|
is_last=0
|
||||||
|
@ -186,12 +186,12 @@ class Apache2Controller(ServiceController):
|
||||||
fi
|
fi
|
||||||
}""")
|
}""")
|
||||||
)
|
)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
""" reload Apache2 if necessary """
|
""" reload Apache2 if necessary """
|
||||||
self.append("coordinate_apache_reload")
|
self.append("coordinate_apache_reload")
|
||||||
super(Apache2Controller, self).commit()
|
super(Apache2Controller, self).commit()
|
||||||
|
|
||||||
def get_directives(self, directive, context):
|
def get_directives(self, directive, context):
|
||||||
method, args = directive[0], directive[1:]
|
method, args = directive[0], directive[1:]
|
||||||
try:
|
try:
|
||||||
|
@ -200,7 +200,7 @@ class Apache2Controller(ServiceController):
|
||||||
context = (self.__class__.__name__, method)
|
context = (self.__class__.__name__, method)
|
||||||
raise AttributeError("%s does not has suport for '%s' directive." % context)
|
raise AttributeError("%s does not has suport for '%s' directive." % context)
|
||||||
return method(context, *args)
|
return method(context, *args)
|
||||||
|
|
||||||
def get_content_directives(self, site, context):
|
def get_content_directives(self, site, context):
|
||||||
directives = []
|
directives = []
|
||||||
for content in site.content_set.all():
|
for content in site.content_set.all():
|
||||||
|
@ -208,19 +208,19 @@ class Apache2Controller(ServiceController):
|
||||||
self.set_content_context(content, context)
|
self.set_content_context(content, context)
|
||||||
directives += self.get_directives(directive, context)
|
directives += self.get_directives(directive, context)
|
||||||
return directives
|
return directives
|
||||||
|
|
||||||
def get_static_directives(self, context, app_path):
|
def get_static_directives(self, context, app_path):
|
||||||
context['app_path'] = os.path.normpath(app_path % context)
|
context['app_path'] = os.path.normpath(app_path % context)
|
||||||
directive = self.get_location_filesystem_map(context)
|
directive = self.get_location_filesystem_map(context)
|
||||||
return [
|
return [
|
||||||
(context['location'], directive),
|
(context['location'], directive),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_location_filesystem_map(self, context):
|
def get_location_filesystem_map(self, context):
|
||||||
if not context['location']:
|
if not context['location']:
|
||||||
return 'DocumentRoot %(app_path)s' % context
|
return 'DocumentRoot %(app_path)s' % context
|
||||||
return 'Alias %(location)s %(app_path)s' % context
|
return 'Alias %(location)s %(app_path)s' % context
|
||||||
|
|
||||||
def get_fpm_directives(self, context, socket, app_path):
|
def get_fpm_directives(self, context, socket, app_path):
|
||||||
if ':' in socket:
|
if ':' in socket:
|
||||||
# TCP socket
|
# TCP socket
|
||||||
|
@ -243,7 +243,7 @@ class Apache2Controller(ServiceController):
|
||||||
return [
|
return [
|
||||||
(context['location'], directives),
|
(context['location'], directives),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_fcgid_directives(self, context, app_path, wrapper_path):
|
def get_fcgid_directives(self, context, app_path, wrapper_path):
|
||||||
context.update({
|
context.update({
|
||||||
'app_path': os.path.normpath(app_path),
|
'app_path': os.path.normpath(app_path),
|
||||||
|
@ -274,7 +274,7 @@ class Apache2Controller(ServiceController):
|
||||||
return [
|
return [
|
||||||
(context['location'], directives),
|
(context['location'], directives),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_uwsgi_directives(self, context, socket):
|
def get_uwsgi_directives(self, context, socket):
|
||||||
# requires apache2 mod_proxy_uwsgi
|
# requires apache2 mod_proxy_uwsgi
|
||||||
context['socket'] = socket
|
context['socket'] = socket
|
||||||
|
@ -283,7 +283,7 @@ class Apache2Controller(ServiceController):
|
||||||
return [
|
return [
|
||||||
(context['location'], directives),
|
(context['location'], directives),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_ssl(self, directives):
|
def get_ssl(self, directives):
|
||||||
cert = directives.get('ssl-cert')
|
cert = directives.get('ssl-cert')
|
||||||
key = directives.get('ssl-key')
|
key = directives.get('ssl-key')
|
||||||
|
@ -305,7 +305,7 @@ class Apache2Controller(ServiceController):
|
||||||
return [
|
return [
|
||||||
('', '\n'.join(ssl_config)),
|
('', '\n'.join(ssl_config)),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_security(self, directives):
|
def get_security(self, directives):
|
||||||
rules = []
|
rules = []
|
||||||
location = '/'
|
location = '/'
|
||||||
|
@ -329,7 +329,7 @@ class Apache2Controller(ServiceController):
|
||||||
</IfModule>""") % '\n '.join(rules)
|
</IfModule>""") % '\n '.join(rules)
|
||||||
security.append((location, rules))
|
security.append((location, rules))
|
||||||
return security
|
return security
|
||||||
|
|
||||||
def get_redirects(self, directives):
|
def get_redirects(self, directives):
|
||||||
redirects = []
|
redirects = []
|
||||||
for redirect in directives.get('redirect', []):
|
for redirect in directives.get('redirect', []):
|
||||||
|
@ -342,7 +342,7 @@ class Apache2Controller(ServiceController):
|
||||||
(location, redirect)
|
(location, redirect)
|
||||||
)
|
)
|
||||||
return redirects
|
return redirects
|
||||||
|
|
||||||
def get_proxies(self, directives):
|
def get_proxies(self, directives):
|
||||||
proxies = []
|
proxies = []
|
||||||
for proxy in directives.get('proxy', []):
|
for proxy in directives.get('proxy', []):
|
||||||
|
@ -360,7 +360,7 @@ class Apache2Controller(ServiceController):
|
||||||
(location, proxy)
|
(location, proxy)
|
||||||
)
|
)
|
||||||
return proxies
|
return proxies
|
||||||
|
|
||||||
def get_saas(self, directives):
|
def get_saas(self, directives):
|
||||||
saas = []
|
saas = []
|
||||||
for name, values in directives.items():
|
for name, values in directives.items():
|
||||||
|
@ -372,20 +372,20 @@ class Apache2Controller(ServiceController):
|
||||||
directive = settings.WEBSITES_SAAS_DIRECTIVES[name]
|
directive = settings.WEBSITES_SAAS_DIRECTIVES[name]
|
||||||
saas += self.get_directives(directive, context)
|
saas += self.get_directives(directive, context)
|
||||||
return saas
|
return saas
|
||||||
|
|
||||||
def get_username(self, site):
|
def get_username(self, site):
|
||||||
option = site.get_directives().get('user_group')
|
option = site.get_directives().get('user_group')
|
||||||
if option:
|
if option:
|
||||||
return option[0]
|
return option[0]
|
||||||
return site.get_username()
|
return site.get_username()
|
||||||
|
|
||||||
def get_groupname(self, site):
|
def get_groupname(self, site):
|
||||||
option = site.get_directives().get('user_group')
|
option = site.get_directives().get('user_group')
|
||||||
if option and ' ' in option:
|
if option and ' ' in option:
|
||||||
user, group = option.split()
|
user, group = option.split()
|
||||||
return group
|
return group
|
||||||
return site.get_groupname()
|
return site.get_groupname()
|
||||||
|
|
||||||
def get_server_names(self, site):
|
def get_server_names(self, site):
|
||||||
server_name = None
|
server_name = None
|
||||||
server_alias = []
|
server_alias = []
|
||||||
|
@ -395,7 +395,7 @@ class Apache2Controller(ServiceController):
|
||||||
else:
|
else:
|
||||||
server_alias.append(domain.name)
|
server_alias.append(domain.name)
|
||||||
return server_name, server_alias
|
return server_name, server_alias
|
||||||
|
|
||||||
def get_context(self, site):
|
def get_context(self, site):
|
||||||
base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF
|
base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF
|
||||||
sites_available = os.path.join(base_apache_conf, 'sites-available')
|
sites_available = os.path.join(base_apache_conf, 'sites-available')
|
||||||
|
@ -419,7 +419,7 @@ class Apache2Controller(ServiceController):
|
||||||
if not context['ips']:
|
if not context['ips']:
|
||||||
raise ValueError("WEBSITES_DEFAULT_IPS is empty.")
|
raise ValueError("WEBSITES_DEFAULT_IPS is empty.")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def set_content_context(self, content, context):
|
def set_content_context(self, content, context):
|
||||||
content_context = {
|
content_context = {
|
||||||
'type': content.webapp.type,
|
'type': content.webapp.type,
|
||||||
|
@ -442,7 +442,7 @@ class Apache2Traffic(ServiceMonitor):
|
||||||
doc_settings = (settings,
|
doc_settings = (settings,
|
||||||
('WEBSITES_TRAFFIC_IGNORE_HOSTS',)
|
('WEBSITES_TRAFFIC_IGNORE_HOSTS',)
|
||||||
)
|
)
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
super(Apache2Traffic, self).prepare()
|
super(Apache2Traffic, self).prepare()
|
||||||
ignore_hosts = '\\|'.join(settings.WEBSITES_TRAFFIC_IGNORE_HOSTS)
|
ignore_hosts = '\\|'.join(settings.WEBSITES_TRAFFIC_IGNORE_HOSTS)
|
||||||
|
@ -490,11 +490,11 @@ class Apache2Traffic(ServiceMonitor):
|
||||||
}' || [[ $? == 1 ]] && true
|
}' || [[ $? == 1 ]] && true
|
||||||
} | xargs echo ${OBJECT_ID}
|
} | xargs echo ${OBJECT_ID}
|
||||||
}""") % context)
|
}""") % context)
|
||||||
|
|
||||||
def monitor(self, site):
|
def monitor(self, site):
|
||||||
context = self.get_context(site)
|
context = self.get_context(site)
|
||||||
self.append('monitor {object_id} "{last_date}" {log_file}'.format(**context))
|
self.append('monitor {object_id} "{last_date}" {log_file}'.format(**context))
|
||||||
|
|
||||||
def get_context(self, site):
|
def get_context(self, site):
|
||||||
return {
|
return {
|
||||||
'log_file': '%s{,.1}' % site.get_www_access_log_path(),
|
'log_file': '%s{,.1}' % site.get_www_access_log_path(),
|
||||||
|
|
|
@ -13,13 +13,13 @@ from .validators import validate_domain_protocol
|
||||||
|
|
||||||
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
class RelatedDomainSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Website.domains.field.model
|
model = Website.domains.field.related_model
|
||||||
fields = ('url', 'id', 'name')
|
fields = ('url', 'id', 'name')
|
||||||
|
|
||||||
|
|
||||||
class RelatedWebAppSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
class RelatedWebAppSerializer(AccountSerializerMixin, RelatedHyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Content.webapp.field.model
|
model = Content.webapp.field.related_model
|
||||||
fields = ('url', 'id', 'name', 'type')
|
fields = ('url', 'id', 'name', 'type')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ class SpanWidget(forms.Widget):
|
||||||
self.original = kwargs.pop('original', '')
|
self.original = kwargs.pop('original', '')
|
||||||
self.display = kwargs.pop('display', None)
|
self.display = kwargs.pop('display', None)
|
||||||
super(SpanWidget, self).__init__(*args, **kwargs)
|
super(SpanWidget, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, extra_attrs={'name':name})
|
||||||
original = self.original or value
|
original = self.original or value
|
||||||
display = original if self.display is None else self.display
|
display = original if self.display is None else self.display
|
||||||
# Display icon
|
# Display icon
|
||||||
|
@ -29,25 +29,25 @@ class SpanWidget(forms.Widget):
|
||||||
tag = self.tag[:-1]
|
tag = self.tag[:-1]
|
||||||
endtag = '/'.join((self.tag[0], self.tag[1:]))
|
endtag = '/'.join((self.tag[0], self.tag[1:]))
|
||||||
return mark_safe('%s%s >%s%s' % (tag, forms.utils.flatatt(final_attrs), display, endtag))
|
return mark_safe('%s%s >%s%s' % (tag, forms.utils.flatatt(final_attrs), display, endtag))
|
||||||
|
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
return self.original
|
return self.original
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def paddingCheckboxSelectMultiple(padding):
|
class PaddingCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
|
||||||
""" Ugly hack to render this widget nicely on Django admin """
|
""" Ugly hack to render this widget nicely on Django admin """
|
||||||
widget = forms.CheckboxSelectMultiple()
|
def __init__(self, padding, attrs=None, choices=()):
|
||||||
old_render = widget.render
|
super().__init__(attrs=attrs, choices=choices)
|
||||||
|
self.padding = padding
|
||||||
|
|
||||||
def render(self, *args, **kwargs):
|
def render(self, *args, **kwargs):
|
||||||
value = old_render(self, *args, **kwargs)
|
value = super().render(*args, **kwargs)
|
||||||
value = re.sub(r'^<ul id=([^>]+)>',
|
value = re.sub(r'^<ul id=([^>]+)>',
|
||||||
r'<ul id=\1 style="padding-left:%ipx">' % padding, value, 1)
|
r'<ul id=\1 style="padding-left:%ipx">' % self.padding, value, 1)
|
||||||
return mark_safe(value)
|
return mark_safe(value)
|
||||||
widget.render = render
|
|
||||||
return widget
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicHelpTextSelect(forms.Select):
|
class DynamicHelpTextSelect(forms.Select):
|
||||||
|
@ -61,7 +61,7 @@ class DynamicHelpTextSelect(forms.Select):
|
||||||
attrs.update(kwargs.get('attrs', {}))
|
attrs.update(kwargs.get('attrs', {}))
|
||||||
kwargs['attrs'] = attrs
|
kwargs['attrs'] = attrs
|
||||||
super(DynamicHelpTextSelect, self).__init__(*args, **kwargs)
|
super(DynamicHelpTextSelect, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_dynamic_help_text(self, target, help_text):
|
def get_dynamic_help_text(self, target, help_text):
|
||||||
return textwrap.dedent("""\
|
return textwrap.dedent("""\
|
||||||
siteoptions = {help_text};
|
siteoptions = {help_text};
|
||||||
|
|
|
@ -90,7 +90,7 @@ class RelatedPermission(Permission):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
parent = cls
|
parent = cls
|
||||||
for relation in relations:
|
for relation in relations:
|
||||||
parent = getattr(parent, relation).field.model
|
parent = getattr(parent, relation).field.related_model
|
||||||
else:
|
else:
|
||||||
parent = functools.reduce(getattr, relations, obj)
|
parent = functools.reduce(getattr, relations, obj)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.utils.sys import run
|
from orchestra.utils.sys import run
|
||||||
|
@ -31,6 +32,6 @@ def get_on_site_link(url):
|
||||||
context = {
|
context = {
|
||||||
'title': _("View on site %s") % url,
|
'title': _("View on site %s") % url,
|
||||||
'url': url,
|
'url': url,
|
||||||
'image': '<img src="%s"></img>' % static('orchestra/images/view-on-site.png'),
|
'image': format_html('<img src="{}"></img>', static('orchestra/images/view-on-site.png')),
|
||||||
}
|
}
|
||||||
return '<a href="%(url)s" title="%(title)s">%(image)s</a>' % context
|
return format_html('<a href="{url}" title="{title}">{image}</a>', **context)
|
||||||
|
|
|
@ -2,7 +2,6 @@ from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.template import Context
|
|
||||||
|
|
||||||
|
|
||||||
def render_email_template(template, context):
|
def render_email_template(template, context):
|
||||||
|
@ -10,12 +9,9 @@ def render_email_template(template, context):
|
||||||
Renders an email template with this format:
|
Renders an email template with this format:
|
||||||
{% if subject %}Subject{% endif %}
|
{% if subject %}Subject{% endif %}
|
||||||
{% if message %}Email body{% endif %}
|
{% if message %}Email body{% endif %}
|
||||||
|
|
||||||
context can be a dictionary or a template.Context instance
|
context must be a dict
|
||||||
"""
|
"""
|
||||||
if isinstance(context, dict):
|
|
||||||
context = Context(context)
|
|
||||||
|
|
||||||
if not 'site' in context:
|
if not 'site' in context:
|
||||||
from orchestra import settings
|
from orchestra import settings
|
||||||
url = urlparse(settings.ORCHESTRA_SITE_URL)
|
url = urlparse(settings.ORCHESTRA_SITE_URL)
|
||||||
|
|
|
@ -12,7 +12,7 @@ ecdsa==0.11
|
||||||
Pygments==1.6
|
Pygments==1.6
|
||||||
django-filter==2.2.0
|
django-filter==2.2.0
|
||||||
jsonfield==0.9.22
|
jsonfield==0.9.22
|
||||||
python-dateutil==2.2
|
python-dateutil>=2.7.0
|
||||||
https://github.com/glic3rinu/passlib/archive/master.zip
|
https://github.com/glic3rinu/passlib/archive/master.zip
|
||||||
django-iban==0.3.0
|
django-iban==0.3.0
|
||||||
requests
|
requests
|
||||||
|
|
Loading…
Reference in New Issue