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