Deprecate ShowTextWidget and ReadOnlyWidget0
This commit is contained in:
parent
929d9beb5c
commit
9c065d401d
8
TODO.md
8
TODO.md
|
@ -280,17 +280,15 @@ https://code.djangoproject.com/ticket/24576
|
|||
# bill.totals make it 100% computed?
|
||||
* joomla: wget https://github.com/joomla/joomla-cms/releases/download/3.4.1/Joomla_3.4.1-Stable-Full_Package.tar.gz -O - | tar xvfz -
|
||||
|
||||
# replace multichoicefield and jsonfield by ArrayField, HStoreField
|
||||
|
||||
# bill confirmation: show total
|
||||
# Amend lines???
|
||||
|
||||
# Determine the difference between data serializer used for validation and used for the rest API!
|
||||
# Make PluginApiView that fills metadata and other stuff like modeladmin plugin support
|
||||
|
||||
# @classmethods do not need to be called with type(object)!
|
||||
|
||||
# Deprectae widgets.showtext and readonlyField by ReadOnlyFormMixin
|
||||
|
||||
# custom validation for settings
|
||||
# TODO orchestra related services code reload: celery/uwsgi reloading find aonther way without root and implement reload
|
||||
# insert settings on dashboard dynamically
|
||||
|
||||
# convert all complex settings to string
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.forms.models import modelformset_factory, BaseModelFormSet
|
|||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.forms.widgets import ShowTextWidget, ReadOnlyWidget
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
from ..core.validators import validate_password
|
||||
|
||||
|
@ -71,10 +71,10 @@ class AdminPasswordChangeForm(forms.Form):
|
|||
self.user = user
|
||||
super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
|
||||
for ix, rel in enumerate(self.related):
|
||||
self.fields['password1_%i' % ix] = forms.CharField(
|
||||
label=_("Password"), widget=forms.PasswordInput, required=False)
|
||||
self.fields['password2_%i' % ix] = forms.CharField(
|
||||
label=_("Password (again)"), widget=forms.PasswordInput, required=False)
|
||||
self.fields['password1_%i' % ix] = forms.CharField(label=_("Password"),
|
||||
widget=forms.PasswordInput, required=False)
|
||||
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=''):
|
||||
|
@ -138,21 +138,20 @@ class AdminPasswordChangeForm(forms.Form):
|
|||
|
||||
class SendEmailForm(forms.Form):
|
||||
email_from = forms.EmailField(label=_("From"),
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
to = forms.CharField(label="To", required=False,
|
||||
widget=ShowTextWidget())
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
to = forms.CharField(label="To", required=False)
|
||||
extra_to = forms.CharField(label="To (extra)", required=False,
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
subject = forms.CharField(label=_("Subject"),
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
widget=forms.TextInput(attrs={'size': '118'}))
|
||||
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):
|
||||
super(SendEmailForm, self).__init__(*args, **kwargs)
|
||||
initial = kwargs.get('initial')
|
||||
if 'to' in initial:
|
||||
self.fields['to'].widget = ReadOnlyWidget(initial['to'])
|
||||
self.fields['to'].widget = SpanWidget(original=initial['to'])
|
||||
else:
|
||||
self.fields.pop('to')
|
||||
|
||||
|
|
|
@ -5,29 +5,33 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
|||
|
||||
|
||||
ACCOUNTS_TYPES = Setting('ACCOUNTS_TYPES', (
|
||||
('INDIVIDUAL', _("Individual")),
|
||||
('ASSOCIATION', _("Association")),
|
||||
('CUSTOMER', _("Customer")),
|
||||
('COMPANY', _("Company")),
|
||||
('PUBLICBODY', _("Public body")),
|
||||
('STAFF', _("Staff")),
|
||||
('FRIEND', _("Friend")),
|
||||
))
|
||||
('INDIVIDUAL', _("Individual")),
|
||||
('ASSOCIATION', _("Association")),
|
||||
('CUSTOMER', _("Customer")),
|
||||
('COMPANY', _("Company")),
|
||||
('PUBLICBODY', _("Public body")),
|
||||
('STAFF', _("Staff")),
|
||||
('FRIEND', _("Friend")),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_DEFAULT_TYPE = Setting('ACCOUNTS_DEFAULT_TYPE', 'INDIVIDUAL', choices=ACCOUNTS_TYPES)
|
||||
|
||||
|
||||
ACCOUNTS_LANGUAGES = Setting('ACCOUNTS_LANGUAGES', (
|
||||
('EN', _('English')),
|
||||
))
|
||||
('EN', _('English')),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_DEFAULT_LANGUAGE = Setting('ACCOUNTS_DEFAULT_LANGUAGE', 'EN', choices=ACCOUNTS_LANGUAGES)
|
||||
|
||||
|
||||
ACCOUNTS_SYSTEMUSER_MODEL = Setting('ACCOUNTS_SYSTEMUSER_MODEL',
|
||||
'systemusers.SystemUser'
|
||||
ACCOUNTS_SYSTEMUSER_MODEL = Setting('ACCOUNTS_SYSTEMUSER_MODEL', 'systemusers.SystemUser',
|
||||
validators=[Setting.validate_model_label],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ def close_bills(modeladmin, request, queryset):
|
|||
for bill in queryset:
|
||||
if not validate_contact(request, bill):
|
||||
return
|
||||
SelectSourceFormSet = adminmodelformset_factory(SelectSourceForm, modeladmin, extra=0)
|
||||
SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm, extra=0)
|
||||
formset = SelectSourceFormSet(queryset=queryset)
|
||||
if request.POST.get('post') == 'generic_confirmation':
|
||||
formset = SelectSourceFormSet(request.POST, request.FILES, queryset=queryset)
|
||||
|
|
|
@ -2,37 +2,38 @@ from django import forms
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.admin.utils import admin_link
|
||||
from orchestra.forms.widgets import ShowTextWidget
|
||||
from orchestra.forms import SpanWidget
|
||||
|
||||
|
||||
class SelectSourceForm(forms.ModelForm):
|
||||
bill_link = forms.CharField(label=_("Number"), required=False, widget=ShowTextWidget())
|
||||
bill_link = forms.CharField(label=_("Number"), required=False, widget=SpanWidget)
|
||||
account_link = forms.CharField(label=_("Account"), required=False)
|
||||
display_total = forms.CharField(label=_("Total"), required=False)
|
||||
display_type = forms.CharField(label=_("Type"), required=False, widget=ShowTextWidget())
|
||||
show_total = forms.CharField(label=_("Total"), required=False, widget=SpanWidget)
|
||||
display_type = forms.CharField(label=_("Type"), required=False, widget=SpanWidget)
|
||||
source = forms.ChoiceField(label=_("Source"), required=False)
|
||||
|
||||
class Meta:
|
||||
fields = (
|
||||
'bill_link', 'display_type', 'account_link', 'display_total',
|
||||
'source'
|
||||
'bill_link', 'display_type', 'account_link', 'show_total', 'source'
|
||||
)
|
||||
readonly_fields = ('account_link', 'display_total')
|
||||
readonly_fields = ('account_link',)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SelectSourceForm, self).__init__(*args, **kwargs)
|
||||
bill = kwargs.get('instance')
|
||||
if bill:
|
||||
total = bill.get_total()
|
||||
sources = bill.account.paymentsources.filter(is_active=True)
|
||||
recharge = bool(bill.total < 0)
|
||||
recharge = bool(total < 0)
|
||||
choices = [(None, '-----------')]
|
||||
for source in sources:
|
||||
if not recharge or source.method_class().allow_recharge:
|
||||
choices.append((source.pk, str(source)))
|
||||
self.fields['source'].choices = choices
|
||||
self.fields['source'].initial = choices[-1][0]
|
||||
self.fields['bill_link'].initial = admin_link('__str__')(bill)
|
||||
self.fields['display_type'].initial = bill.get_type_display()
|
||||
self.fields['show_total'].widget.display = total
|
||||
self.fields['bill_link'].widget.display = admin_link('__str__')(bill)
|
||||
self.fields['display_type'].widget.display = bill.get_type_display()
|
||||
|
||||
def clean_source(self):
|
||||
source_id = self.cleaned_data['source']
|
||||
|
|
|
@ -7,69 +7,43 @@ from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
|||
BILLS_NUMBER_LENGTH = Setting('BILLS_NUMBER_LENGTH', 4)
|
||||
|
||||
|
||||
BILLS_INVOICE_NUMBER_PREFIX = Setting('BILLS_INVOICE_NUMBER_PREFIX',
|
||||
'I'
|
||||
)
|
||||
BILLS_INVOICE_NUMBER_PREFIX = Setting('BILLS_INVOICE_NUMBER_PREFIX', 'I')
|
||||
|
||||
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX',
|
||||
'A'
|
||||
)
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX', 'A')
|
||||
|
||||
|
||||
BILLS_FEE_NUMBER_PREFIX = Setting('BILLS_FEE_NUMBER_PREFIX',
|
||||
'F'
|
||||
)
|
||||
BILLS_FEE_NUMBER_PREFIX = Setting('BILLS_FEE_NUMBER_PREFIX', 'F')
|
||||
|
||||
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_FEE_NUMBER_PREFIX',
|
||||
'B'
|
||||
)
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_FEE_NUMBER_PREFIX', 'B')
|
||||
|
||||
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = Setting('BILLS_PROFORMA_NUMBER_PREFIX',
|
||||
'P'
|
||||
)
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = Setting('BILLS_PROFORMA_NUMBER_PREFIX', 'P')
|
||||
|
||||
|
||||
BILLS_DEFAULT_TEMPLATE = Setting('BILLS_DEFAULT_TEMPLATE',
|
||||
'bills/microspective.html'
|
||||
)
|
||||
BILLS_DEFAULT_TEMPLATE = Setting('BILLS_DEFAULT_TEMPLATE', 'bills/microspective.html')
|
||||
|
||||
|
||||
BILLS_FEE_TEMPLATE = Setting('BILLS_FEE_TEMPLATE',
|
||||
'bills/microspective-fee.html'
|
||||
)
|
||||
BILLS_FEE_TEMPLATE = Setting('BILLS_FEE_TEMPLATE', 'bills/microspective-fee.html')
|
||||
|
||||
|
||||
BILLS_PROFORMA_TEMPLATE = Setting('BILLS_PROFORMA_TEMPLATE',
|
||||
'bills/microspective-proforma.html'
|
||||
)
|
||||
BILLS_PROFORMA_TEMPLATE = Setting('BILLS_PROFORMA_TEMPLATE', 'bills/microspective-proforma.html')
|
||||
|
||||
|
||||
BILLS_CURRENCY = Setting('BILLS_CURRENCY',
|
||||
'euro'
|
||||
)
|
||||
BILLS_CURRENCY = Setting('BILLS_CURRENCY', 'euro')
|
||||
|
||||
|
||||
BILLS_SELLER_PHONE = Setting('BILLS_SELLER_PHONE',
|
||||
'111-112-11-222'
|
||||
)
|
||||
BILLS_SELLER_PHONE = Setting('BILLS_SELLER_PHONE', '111-112-11-222')
|
||||
|
||||
|
||||
BILLS_SELLER_EMAIL = Setting('BILLS_SELLER_EMAIL',
|
||||
'sales@{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
BILLS_SELLER_EMAIL = Setting('BILLS_SELLER_EMAIL', 'sales@{}'.format(ORCHESTRA_BASE_DOMAIN))
|
||||
|
||||
|
||||
BILLS_SELLER_WEBSITE = Setting('BILLS_SELLER_WEBSITE',
|
||||
'www.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
BILLS_SELLER_WEBSITE = Setting('BILLS_SELLER_WEBSITE', 'www.{}'.format(ORCHESTRA_BASE_DOMAIN))
|
||||
|
||||
|
||||
BILLS_SELLER_BANK_ACCOUNT = Setting('BILLS_SELLER_BANK_ACCOUNT',
|
||||
'0000 0000 00 00000000 (Orchestra Bank)'
|
||||
)
|
||||
BILLS_SELLER_BANK_ACCOUNT = Setting('BILLS_SELLER_BANK_ACCOUNT', '0000 0000 00 00000000 (Orchestra Bank)')
|
||||
|
||||
|
||||
BILLS_EMAIL_NOTIFICATION_TEMPLATE = Setting('BILLS_EMAIL_NOTIFICATION_TEMPLATE',
|
||||
|
@ -77,18 +51,16 @@ BILLS_EMAIL_NOTIFICATION_TEMPLATE = Setting('BILLS_EMAIL_NOTIFICATION_TEMPLATE',
|
|||
)
|
||||
|
||||
|
||||
BILLS_ORDER_MODEL = Setting('BILLS_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
BILLS_ORDER_MODEL = Setting('BILLS_ORDER_MODEL', 'orders.Order',
|
||||
validators=[Setting.validate_model_label]
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_CITY = Setting('BILLS_CONTACT_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
)
|
||||
BILLS_CONTACT_DEFAULT_CITY = Setting('BILLS_CONTACT_DEFAULT_CITY', 'Barcelona')
|
||||
|
||||
|
||||
BILLS_CONTACT_COUNTRIES = Setting('BILLS_CONTACT_COUNTRIES', tuple((k,v) for k,v in data.COUNTRIES.items()),
|
||||
editable=False
|
||||
serializable=False
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
from django.conf import settings
|
||||
from django_countries import data
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_EMAIL_USAGES = Setting('CONTACTS_DEFAULT_EMAIL_USAGES', (
|
||||
'SUPPORT',
|
||||
'ADMIN',
|
||||
'BILLING',
|
||||
'TECH',
|
||||
'ADDS',
|
||||
'EMERGENCY'
|
||||
))
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_CITY = Setting('CONTACTS_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
CONTACTS_DEFAULT_EMAIL_USAGES = Setting('CONTACTS_DEFAULT_EMAIL_USAGES',
|
||||
default=(
|
||||
'SUPPORT',
|
||||
'ADMIN',
|
||||
'BILLING',
|
||||
'TECH',
|
||||
'ADDS',
|
||||
'EMERGENCY'
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
CONTACTS_COUNTRIES = Setting('CONTACTS_COUNTRIES', tuple((k,v) for k,v in data.COUNTRIES.items()),
|
||||
editable=False)
|
||||
CONTACTS_DEFAULT_CITY = Setting('CONTACTS_DEFAULT_CITY',
|
||||
default='Barcelona'
|
||||
)
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_COUNTRY = Setting('CONTACTS_DEFAULT_COUNTRY', 'ES', choices=CONTACTS_COUNTRIES)
|
||||
CONTACTS_COUNTRIES = Setting('CONTACTS_COUNTRIES',
|
||||
default=tuple((k,v) for k,v in data.COUNTRIES.items()),
|
||||
serializable=False
|
||||
)
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_COUNTRY = Setting('CONTACTS_DEFAULT_COUNTRY',
|
||||
default='ES',
|
||||
choices=CONTACTS_COUNTRIES
|
||||
)
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
DATABASES_TYPE_CHOICES = Setting('DATABASES_TYPE_CHOICES', (
|
||||
('mysql', 'MySQL'),
|
||||
('postgres', 'PostgreSQL'),
|
||||
))
|
||||
('mysql', 'MySQL'),
|
||||
('postgres', 'PostgreSQL'),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
DATABASES_DEFAULT_TYPE = Setting('DATABASES_DEFAULT_TYPE', 'mysql', choices=DATABASES_TYPE_CHOICES)
|
||||
|
||||
|
||||
DATABASES_DEFAULT_HOST = Setting('DATABASES_DEFAULT_HOST',
|
||||
'localhost'
|
||||
)
|
||||
DATABASES_DEFAULT_HOST = Setting('DATABASES_DEFAULT_HOST', 'localhost')
|
||||
|
|
|
@ -94,7 +94,7 @@ class Domain(models.Model):
|
|||
return self.origin.subdomain_set.all().prefetch_related('records')
|
||||
|
||||
def get_parent(self, top=False):
|
||||
return type(self).get_parent_domain(self.name, top=top)
|
||||
return self.get_parent_domain(self.name, top=top)
|
||||
|
||||
def render_zone(self):
|
||||
origin = self.origin
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.core.validators import validate_ipv4_address, validate_ipv6_address
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
|
@ -58,18 +57,19 @@ DOMAINS_CHECKZONE_BIN_PATH = Setting('DOMAINS_CHECKZONE_BIN_PATH',
|
|||
)
|
||||
|
||||
|
||||
DOMAINS_ZONE_VALIDATION_TMP_DIR = Setting('DOMAINS_ZONE_VALIDATION_TMP_DIR', '/dev/shm',
|
||||
DOMAINS_ZONE_VALIDATION_TMP_DIR = Setting('DOMAINS_ZONE_VALIDATION_TMP_DIR',
|
||||
'/dev/shm',
|
||||
help_text="Used for creating temporary zone files used for validation."
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_A = Setting('DOMAINS_DEFAULT_A',
|
||||
'10.0.3.13'
|
||||
DOMAINS_DEFAULT_A = Setting('DOMAINS_DEFAULT_A', '10.0.3.13',
|
||||
validators=[validate_ipv4_address]
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_AAAA = Setting('DOMAINS_DEFAULT_AAAA',
|
||||
''
|
||||
DOMAINS_DEFAULT_AAAA = Setting('DOMAINS_DEFAULT_AAAA', '',
|
||||
validators=[validate_ipv6_address]
|
||||
)
|
||||
|
||||
|
||||
|
@ -96,11 +96,13 @@ DOMAINS_FORBIDDEN = Setting('DOMAINS_FORBIDDEN', '',
|
|||
)
|
||||
|
||||
|
||||
DOMAINS_MASTERS = Setting('DOMAINS_MASTERS', (),
|
||||
DOMAINS_MASTERS = Setting('DOMAINS_MASTERS',
|
||||
(),
|
||||
help_text="Additional master server ip addresses other than autodiscovered by router.get_servers()."
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_SLAVES = Setting('DOMAINS_SLAVES', (),
|
||||
DOMAINS_SLAVES = Setting('DOMAINS_SLAVES',
|
||||
(),
|
||||
help_text="Additional slave server ip addresses other than autodiscovered by router.get_servers()."
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from markdown import markdown
|
||||
|
||||
from orchestra.forms.widgets import ReadOnlyWidget
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
from .models import Queue, Ticket
|
||||
|
||||
|
@ -40,7 +40,7 @@ class MessageInlineForm(forms.ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MessageInlineForm, self).__init__(*args, **kwargs)
|
||||
self.fields['created_on'].widget = ReadOnlyWidget('')
|
||||
self.fields['created_on'].widget = SpanWidget(display='')
|
||||
|
||||
def clean_content(self):
|
||||
""" clean HTML tags """
|
||||
|
@ -98,7 +98,7 @@ class TicketForm(forms.ModelForm):
|
|||
description = description.replace('\n', '<br>')
|
||||
description = description.replace('#Ha9G9-?8', '>\n')
|
||||
description = '<div style="padding-left: 95px;">%s</div>' % description
|
||||
widget = ReadOnlyWidget(description, description)
|
||||
widget = SpanWidget(display=description)
|
||||
self.fields['display_description'].widget = widget
|
||||
|
||||
def clean_description(self):
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
ISSUES_SUPPORT_EMAILS = Setting('ISSUES_SUPPORT_EMAILS', [
|
||||
])
|
||||
ISSUES_SUPPORT_EMAILS = Setting('ISSUES_SUPPORT_EMAILS', ())
|
||||
|
||||
|
||||
ISSUES_NOTIFY_SUPERUSERS = Setting('ISSUES_NOTIFY_SUPERUSERS',
|
||||
True
|
||||
)
|
||||
ISSUES_NOTIFY_SUPERUSERS = Setting('ISSUES_NOTIFY_SUPERUSERS', True)
|
||||
|
|
|
@ -67,14 +67,15 @@ class MailmanBackend(ServiceController):
|
|||
context['aliases'] = self.get_virtual_aliases(context)
|
||||
# Preserve indentation
|
||||
self.append(textwrap.dedent("""\
|
||||
aliases='%(aliases)s'
|
||||
if [[ ! $(grep '\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
|
||||
echo '%(aliases)s' >> %(virtual_alias)s
|
||||
echo "${aliases}" >> %(virtual_alias)s
|
||||
UPDATED_VIRTUAL_ALIAS=1
|
||||
else
|
||||
if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
|
||||
sed -i -e '/^.*\s%(name)s\(%(address_regex)s\)\s*$/d' \\
|
||||
-e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s
|
||||
echo '%(aliases)s' >> %(virtual_alias)s
|
||||
echo "${aliases}" >> %(virtual_alias)s
|
||||
UPDATED_VIRTUAL_ALIAS=1
|
||||
fi
|
||||
fi""") % context
|
||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core.validators import validate_password
|
||||
from orchestra.forms.widgets import ReadOnlyWidget
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
|
||||
class CleanAddressMixin(object):
|
||||
|
@ -32,8 +32,8 @@ class ListCreationForm(CleanAddressMixin, forms.ModelForm):
|
|||
|
||||
|
||||
class ListChangeForm(CleanAddressMixin, forms.ModelForm):
|
||||
password = forms.CharField(label=_("Password"),
|
||||
widget=ReadOnlyWidget('<strong>Unknown password</strong>'),
|
||||
password = forms.CharField(label=_("Password"), required=False,
|
||||
widget=SpanWidget(display='<strong>Unknown password</strong>'),
|
||||
help_text=_("List passwords are not stored, so there is no way to see this "
|
||||
"list's password, but you can change the password using "
|
||||
"<a href=\"password/\">this form</a>."))
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL', 'domains.Domain',
|
||||
validators=[Setting.validate_model_label]
|
||||
)
|
||||
|
||||
|
||||
LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN',
|
||||
'lists.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN', 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN))
|
||||
|
||||
|
||||
LISTS_LIST_URL = Setting('LISTS_LIST_URL',
|
||||
|
@ -16,21 +14,13 @@ LISTS_LIST_URL = Setting('LISTS_LIST_URL',
|
|||
)
|
||||
|
||||
|
||||
LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH',
|
||||
'/var/log/mailman/post'
|
||||
)
|
||||
LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH', '/var/log/mailman/post')
|
||||
|
||||
|
||||
LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR',
|
||||
'/var/lib/mailman'
|
||||
)
|
||||
LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR', '/var/lib/mailman')
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH',
|
||||
'/etc/postfix/mailman_virtual_aliases'
|
||||
)
|
||||
LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH', '/etc/postfix/mailman_virtual_aliases')
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'/etc/postfix/mailman_virtual_domains'
|
||||
)
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH', '/etc/postfix/mailman_virtual_domains')
|
||||
|
|
|
@ -3,17 +3,16 @@ import textwrap
|
|||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core.validators import validate_name
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
MAILBOXES_DOMAIN_MODEL = Setting('MAILBOXES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
MAILBOXES_DOMAIN_MODEL = Setting('MAILBOXES_DOMAIN_MODEL', 'domains.Domain',
|
||||
validators=[Setting.validate_model_label]
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_HOME = Setting('MAILBOXES_HOME',
|
||||
'/home/%(name)s/'
|
||||
)
|
||||
MAILBOXES_HOME = Setting('MAILBOXES_HOME', '/home/%(name)s/')
|
||||
|
||||
|
||||
MAILBOXES_SIEVE_PATH = Setting('MAILBOXES_SIEVE_PATH',
|
||||
|
@ -21,13 +20,11 @@ MAILBOXES_SIEVE_PATH = Setting('MAILBOXES_SIEVE_PATH',
|
|||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_PATH = Setting('MAILBOXES_SIEVETEST_PATH',
|
||||
'/dev/shm'
|
||||
)
|
||||
MAILBOXES_SIEVETEST_PATH = Setting('MAILBOXES_SIEVETEST_PATH', '/dev/shm')
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_BIN_PATH = Setting('MAILBOXES_SIEVETEST_BIN_PATH',
|
||||
'%(orchestra_root)s/bin/sieve-test'
|
||||
MAILBOXES_SIEVETEST_BIN_PATH = Setting('MAILBOXES_SIEVETEST_BIN_PATH', '%(orchestra_root)s/bin/sieve-test',
|
||||
validators=[Setting.string_format_validator(('orchestra_root',))]
|
||||
)
|
||||
|
||||
|
||||
|
@ -46,14 +43,12 @@ MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('MAILBOXES_VIRTUAL_ALIAS_DOMAINS_
|
|||
)
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_DOMAIN = Setting('MAILBOXES_LOCAL_DOMAIN',
|
||||
ORCHESTRA_BASE_DOMAIN
|
||||
MAILBOXES_LOCAL_DOMAIN = Setting('MAILBOXES_LOCAL_DOMAIN', ORCHESTRA_BASE_DOMAIN,
|
||||
validators=[validate_name]
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_PASSWD_PATH = Setting('MAILBOXES_PASSWD_PATH',
|
||||
'/etc/dovecot/passwd'
|
||||
)
|
||||
MAILBOXES_PASSWD_PATH = Setting('MAILBOXES_PASSWD_PATH', '/etc/dovecot/passwd')
|
||||
|
||||
|
||||
MAILBOXES_MAILBOX_FILTERINGS = Setting('MAILBOXES_MAILBOX_FILTERINGS', {
|
||||
|
@ -80,21 +75,15 @@ MAILBOXES_MAILBOX_DEFAULT_FILTERING = Setting('MAILBOXES_MAILBOX_DEFAULT_FILTERI
|
|||
)
|
||||
|
||||
|
||||
MAILBOXES_MAILDIRSIZE_PATH = Setting('MAILBOXES_MAILDIRSIZE_PATH',
|
||||
'%(home)s/Maildir/maildirsize'
|
||||
MAILBOXES_MAILDIRSIZE_PATH = Setting('MAILBOXES_MAILDIRSIZE_PATH', '%(home)s/Maildir/maildirsize')
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_ADDRESS_DOMAIN = Setting('MAILBOXES_LOCAL_ADDRESS_DOMAIN', ORCHESTRA_BASE_DOMAIN,
|
||||
validators=[validate_name]
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_ADDRESS_DOMAIN = Setting('MAILBOXES_LOCAL_ADDRESS_DOMAIN',
|
||||
ORCHESTRA_BASE_DOMAIN
|
||||
)
|
||||
MAILBOXES_MAIL_LOG_PATH = Setting('MAILBOXES_MAIL_LOG_PATH', '/var/log/mail.log')
|
||||
|
||||
|
||||
MAILBOXES_MAIL_LOG_PATH = Setting('MAILBOXES_MAIL_LOG_PATH',
|
||||
'/var/log/mail.log'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MOVE_ON_DELETE_PATH = Setting('MAILBOXES_MOVE_ON_DELETE_PATH',
|
||||
''
|
||||
)
|
||||
MAILBOXES_MOVE_ON_DELETE_PATH = Setting('MAILBOXES_MOVE_ON_DELETE_PATH', '')
|
||||
|
|
|
@ -31,7 +31,7 @@ def SSH(backend, log, server, cmds, async=False):
|
|||
script = script.replace('\r', '')
|
||||
bscript = script.encode('utf-8')
|
||||
digest = hashlib.md5(bscript).hexdigest()
|
||||
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_PATH, digest)
|
||||
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_DIR, digest)
|
||||
remote_path = "%s.remote" % path
|
||||
log.script = '# %s\n%s' % (remote_path, script)
|
||||
log.save(update_fields=['script'])
|
||||
|
|
|
@ -94,7 +94,7 @@ class OperationsMiddleware(object):
|
|||
def process_response(self, request, response):
|
||||
""" Processes pending backend operations """
|
||||
if not isinstance(response, HttpResponseServerError):
|
||||
operations = type(self).get_pending_operations()
|
||||
operations = self.get_pending_operations()
|
||||
if operations:
|
||||
try:
|
||||
scripts, block = manager.generate(operations)
|
||||
|
|
|
@ -5,8 +5,10 @@ from orchestra.settings import Setting
|
|||
|
||||
|
||||
ORCHESTRATION_OS_CHOICES = Setting('ORCHESTRATION_OS_CHOICES', (
|
||||
('LINUX', "Linux"),
|
||||
))
|
||||
('LINUX', "Linux"),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRATION_DEFAULT_OS = Setting('ORCHESTRATION_DEFAULT_OS', 'LINUX',
|
||||
|
@ -17,19 +19,15 @@ ORCHESTRATION_SSH_KEY_PATH = Setting('ORCHESTRATION_SSH_KEY_PATH',
|
|||
path.join(path.expanduser('~'), '.ssh/id_rsa'))
|
||||
|
||||
|
||||
ORCHESTRATION_ROUTER = Setting('ORCHESTRATION_ROUTER',
|
||||
'orchestra.contrib.orchestration.models.Route'
|
||||
ORCHESTRATION_ROUTER = Setting('ORCHESTRATION_ROUTER', 'orchestra.contrib.orchestration.models.Route',
|
||||
validators=[Setting.validate_import_class]
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRATION_TEMP_SCRIPT_PATH = Setting('ORCHESTRATION_TEMP_SCRIPT_PATH',
|
||||
'/dev/shm'
|
||||
)
|
||||
ORCHESTRATION_TEMP_SCRIPT_DIR = Setting('ORCHESTRATION_TEMP_SCRIPT_DIR', '/dev/shm')
|
||||
|
||||
|
||||
ORCHESTRATION_DISABLE_EXECUTION = Setting('ORCHESTRATION_DISABLE_EXECUTION',
|
||||
False
|
||||
)
|
||||
ORCHESTRATION_DISABLE_EXECUTION = Setting('ORCHESTRATION_DISABLE_EXECUTION', False)
|
||||
|
||||
|
||||
ORCHESTRATION_BACKEND_CLEANUP_DELTA = Setting('ORCHESTRATION_BACKEND_CLEANUP_DELTA',
|
||||
|
|
|
@ -91,7 +91,6 @@ class BillSelectedOrders(object):
|
|||
url = reverse('admin:bills_bill_changelist')
|
||||
ids = ','.join(map(str, bills))
|
||||
url += '?id__in=%s' % ids
|
||||
num = len(bills)
|
||||
msg = ungettext(
|
||||
'<a href="{url}">One bill</a> has been created.',
|
||||
'<a href="{url}">{num} bills</a> have been created.',
|
||||
|
@ -100,11 +99,18 @@ class BillSelectedOrders(object):
|
|||
self.modeladmin.message_user(request, msg, messages.INFO)
|
||||
return
|
||||
bills = self.queryset.bill(commit=False, **self.options)
|
||||
bills_with_total = []
|
||||
for account, lines in bills:
|
||||
total = 0
|
||||
for line in lines:
|
||||
discount = sum([discount.total for discount in line.discounts])
|
||||
total += line.subtotal + discount
|
||||
bills_with_total.append((account, total, lines))
|
||||
self.context.update({
|
||||
'title': _("Confirmation for billing selected orders"),
|
||||
'step': 3,
|
||||
'form': form,
|
||||
'bills': bills,
|
||||
'bills': bills_with_total,
|
||||
})
|
||||
return render(request, self.template, self.context)
|
||||
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
# Pluggable backend for bill generation.
|
||||
ORDERS_BILLING_BACKEND = Setting('ORDERS_BILLING_BACKEND',
|
||||
'orchestra.contrib.orders.billing.BillsBackend'
|
||||
ORDERS_BILLING_BACKEND = Setting('ORDERS_BILLING_BACKEND', 'orchestra.contrib.orders.billing.BillsBackend',
|
||||
validators=[Setting.validate_import_class],
|
||||
help_text="Pluggable backend for bill generation.",
|
||||
)
|
||||
|
||||
|
||||
# Pluggable service class
|
||||
ORDERS_SERVICE_MODEL = Setting('ORDERS_SERVICE_MODEL',
|
||||
'services.Service'
|
||||
ORDERS_SERVICE_MODEL = Setting('ORDERS_SERVICE_MODEL', 'services.Service',
|
||||
validators=[Setting.validate_model_label],
|
||||
help_text="Pluggable service class."
|
||||
)
|
||||
|
||||
|
||||
# Prevent inspecting these apps for service accounting
|
||||
ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS', (
|
||||
'orders',
|
||||
'admin',
|
||||
'contenttypes',
|
||||
'auth',
|
||||
'migrations',
|
||||
'sessions',
|
||||
'orchestration',
|
||||
'bills',
|
||||
'services',
|
||||
))
|
||||
|
||||
|
||||
# Only account for significative changes
|
||||
# metric_storage new value: lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue
|
||||
ORDERS_METRIC_ERROR = Setting('ORDERS_METRIC_ERROR',
|
||||
0.01
|
||||
'orders',
|
||||
'admin',
|
||||
'contenttypes',
|
||||
'auth',
|
||||
'migrations',
|
||||
'sessions',
|
||||
'orchestration',
|
||||
'bills',
|
||||
'services',
|
||||
),
|
||||
help_text="Prevent inspecting these apps for service accounting."
|
||||
)
|
||||
|
||||
|
||||
ORDERS_METRIC_ERROR = Setting('ORDERS_METRIC_ERROR', 0.01,
|
||||
help_text=("Only account for significative changes.<br>"
|
||||
"metric_storage new value: <tt>lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue</tt>.")
|
||||
)
|
||||
|
|
|
@ -28,11 +28,11 @@
|
|||
<div>
|
||||
<div style="margin:20px;">
|
||||
{% if bills %}
|
||||
{% for account, lines in bills %}
|
||||
{% for account, total, lines in bills %}
|
||||
<div class="inline-group" id="rates-group">
|
||||
<div class="tabular inline-related last-related">
|
||||
<fieldset class="module">
|
||||
<h2><a href="{% url 'admin:accounts_account_change' account.pk %}">{{ account }}</a></h2>
|
||||
<h2><a href="{% url 'admin:accounts_account_change' account.pk %}">{{ account }}</a><span style="float:right">{{ total | floatformat:"-2" }} €</span></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th style="width:30%;">Description</th> <th style="width:30%;">Period</th> <th style="width:10%;">Quantity</th> <th style="width:10%;">Price</th></tr>
|
||||
|
|
|
@ -3,25 +3,19 @@ from orchestra.settings import Setting
|
|||
from .. import payments
|
||||
|
||||
|
||||
PAYMENT_CURRENCY = Setting('PAYMENT_CURRENCY',
|
||||
'Eur'
|
||||
)
|
||||
PAYMENT_CURRENCY = Setting('PAYMENT_CURRENCY', 'Eur')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_NAME = Setting('PAYMENTS_DD_CREDITOR_NAME',
|
||||
'Orchestra')
|
||||
PAYMENTS_DD_CREDITOR_NAME = Setting('PAYMENTS_DD_CREDITOR_NAME', 'Orchestra')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_IBAN = Setting('PAYMENTS_DD_CREDITOR_IBAN',
|
||||
'IE98BOFI90393912121212')
|
||||
PAYMENTS_DD_CREDITOR_IBAN = Setting('PAYMENTS_DD_CREDITOR_IBAN', 'IE98BOFI90393912121212')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_BIC = Setting('PAYMENTS_DD_CREDITOR_BIC',
|
||||
'BOFIIE2D')
|
||||
PAYMENTS_DD_CREDITOR_BIC = Setting('PAYMENTS_DD_CREDITOR_BIC', 'BOFIIE2D')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = Setting('PAYMENTS_DD_CREDITOR_AT02_ID',
|
||||
'InvalidAT02ID')
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = Setting('PAYMENTS_DD_CREDITOR_AT02_ID', 'InvalidAT02ID')
|
||||
|
||||
|
||||
PAYMENTS_ENABLED_METHODS = Setting('PAYMENTS_ENABLED_METHODS', (
|
||||
|
|
|
@ -42,7 +42,7 @@ class ServiceMonitor(ServiceBackend):
|
|||
from .models import MonitorData
|
||||
try:
|
||||
return MonitorData.objects.filter(content_type=self.content_type,
|
||||
monitor=type(self).get_name(), object_id=object_id).latest()
|
||||
monitor=self.get_name(), object_id=object_id).latest()
|
||||
except MonitorData.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
|
|
@ -1,30 +1,38 @@
|
|||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.forms.widgets import ShowTextWidget, ReadOnlyWidget
|
||||
from orchestra.forms import ReadOnlyFormMixin
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
|
||||
class ResourceForm(forms.ModelForm):
|
||||
class ResourceForm(ReadOnlyFormMixin, forms.ModelForm):
|
||||
verbose_name = forms.CharField(label=_("Name"), required=False,
|
||||
widget=ShowTextWidget(tag='<b>'))
|
||||
widget=SpanWidget(tag='<b>'))
|
||||
allocated = forms.DecimalField(label=_("Allocated"))
|
||||
unit = forms.CharField(label=_("Unit"), widget=ShowTextWidget(), required=False)
|
||||
unit = forms.CharField(label=_("Unit"), required=False)
|
||||
|
||||
class Meta:
|
||||
fields = ('verbose_name', 'used', 'last_update', 'allocated', 'unit')
|
||||
readonly_fields = ('verbose_name', 'unit')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.resource = kwargs.pop('resource', None)
|
||||
if self.resource:
|
||||
initial = kwargs.get('initial', {})
|
||||
initial.update({
|
||||
'verbose_name': self.resource.get_verbose_name(),
|
||||
'unit': self.resource.unit,
|
||||
})
|
||||
kwargs['initial'] = initial
|
||||
super(ResourceForm, self).__init__(*args, **kwargs)
|
||||
if self.resource:
|
||||
self.fields['verbose_name'].initial = self.resource.get_verbose_name()
|
||||
self.fields['unit'].initial = self.resource.unit
|
||||
if self.resource.on_demand:
|
||||
self.fields['allocated'].required = False
|
||||
self.fields['allocated'].widget = ReadOnlyWidget(None, '')
|
||||
self.fields['allocated'].widget = SpanWidget(original=None, display='')
|
||||
else:
|
||||
self.fields['allocated'].required = True
|
||||
self.fields['allocated'].initial = self.resource.default_allocation
|
||||
|
||||
# def has_changed(self):
|
||||
# """ Make sure resourcedata objects are created for all resources """
|
||||
# if not self.instance.pk:
|
||||
|
|
|
@ -14,8 +14,8 @@ class GitLabForm(SoftwareServiceForm):
|
|||
help_text=_("Initial email address, changes on the GitLab server are not reflected here."))
|
||||
|
||||
|
||||
class GitLaChangebForm(GitLabForm):
|
||||
user_id = forms.IntegerField(label=("User ID"), widget=widgets.ShowTextWidget,
|
||||
class GitLaChangeForm(GitLabForm):
|
||||
user_id = forms.IntegerField(label=("User ID"), widget=widgets.SpanWidget,
|
||||
help_text=_("ID of this user on the GitLab server, the only attribute that not changes."))
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@ class GitLabSerializer(serializers.Serializer):
|
|||
class GitLabService(SoftwareService):
|
||||
name = 'gitlab'
|
||||
form = GitLabForm
|
||||
change_form = GitLaChangebForm
|
||||
change_form = GitLaChangeForm
|
||||
serializer = GitLabSerializer
|
||||
site_domain = settings.SAAS_GITLAB_DOMAIN
|
||||
change_readonly_fileds = ('email', 'user_id',)
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from orchestra import plugins
|
||||
from orchestra.contrib.orchestration import Operation
|
||||
from orchestra.core import validators
|
||||
from orchestra.forms import widgets
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
from orchestra.plugins.forms import PluginDataForm
|
||||
from orchestra.utils.functional import cached
|
||||
from orchestra.utils.python import import_class, random_ascii
|
||||
|
@ -15,9 +15,9 @@ from .. import settings
|
|||
|
||||
|
||||
class SoftwareServiceForm(PluginDataForm):
|
||||
site_url = forms.CharField(label=_("Site URL"), widget=widgets.ShowTextWidget, required=False)
|
||||
site_url = forms.CharField(label=_("Site URL"), widget=SpanWidget(), required=False)
|
||||
password = forms.CharField(label=_("Password"), required=False,
|
||||
widget=widgets.ReadOnlyWidget('<strong>Unknown password</strong>'),
|
||||
widget=SpanWidget(display='<strong>Unknown password</strong>'),
|
||||
validators=[
|
||||
validators.validate_password,
|
||||
RegexValidator(r'^[^"\'\\]+$',
|
||||
|
@ -36,6 +36,7 @@ class SoftwareServiceForm(PluginDataForm):
|
|||
|
||||
class Meta:
|
||||
exclude = ('database',)
|
||||
readonly_fields = ('site_url',)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SoftwareServiceForm, self).__init__(*args, **kwargs)
|
||||
|
@ -54,7 +55,7 @@ class SoftwareServiceForm(PluginDataForm):
|
|||
site_link = '<a href="http://%s">%s</a>' % (site_domain, site_domain)
|
||||
else:
|
||||
site_link = '<site_name>.%s' % self.plugin.site_base_domain
|
||||
self.fields['site_url'].initial = site_link
|
||||
self.fields['site_url'].widget.display = site_link
|
||||
self.fields['name'].label = _("Username")
|
||||
|
||||
def clean_password2(self):
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.contrib.databases.models import Database, DatabaseUser
|
||||
from orchestra.forms import widgets
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
from .. import settings
|
||||
from .options import SoftwareService, SoftwareServiceForm
|
||||
|
@ -13,7 +13,7 @@ from .options import SoftwareService, SoftwareServiceForm
|
|||
|
||||
class PHPListForm(SoftwareServiceForm):
|
||||
admin_username = forms.CharField(label=_("Admin username"), required=False,
|
||||
widget=widgets.ReadOnlyWidget('admin'))
|
||||
widget=SpanWidget(display='admin'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PHPListForm, self).__init__(*args, **kwargs)
|
||||
|
@ -37,7 +37,7 @@ class PHPListChangeForm(PHPListForm):
|
|||
db = self.instance.database
|
||||
db_url = reverse('admin:databases_database_change', args=(db.pk,))
|
||||
db_link = mark_safe('<a href="%s">%s</a>' % (db_url, db.name))
|
||||
self.fields['database'].widget = widgets.ReadOnlyWidget(db.name, db_link)
|
||||
self.fields['database'].widget = SpanWidget(original=db.name, display=db_link)
|
||||
|
||||
|
||||
class PHPListService(SoftwareService):
|
||||
|
@ -57,7 +57,7 @@ class PHPListService(SoftwareService):
|
|||
return settings.SAAS_PHPLIST_DB_NAME
|
||||
|
||||
def get_account(self):
|
||||
return type(self.instance.account).get_main()
|
||||
return self.instance.account.get_main()
|
||||
|
||||
def validate(self):
|
||||
super(PHPListService, self).validate()
|
||||
|
|
|
@ -19,9 +19,7 @@ SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', (
|
|||
)
|
||||
|
||||
|
||||
SAAS_WORDPRESS_ADMIN_PASSWORD = Setting('SAAS_WORDPRESSMU_ADMIN_PASSWORD',
|
||||
'secret'
|
||||
)
|
||||
SAAS_WORDPRESS_ADMIN_PASSWORD = Setting('SAAS_WORDPRESSMU_ADMIN_PASSWORD', 'secret')
|
||||
|
||||
|
||||
SAAS_WORDPRESS_BASE_URL = Setting('SAAS_WORDPRESS_BASE_URL',
|
||||
|
@ -86,4 +84,3 @@ SAAS_GITLAB_ROOT_PASSWORD = Setting('SAAS_GITLAB_ROOT_PASSWORD',
|
|||
SAAS_GITLAB_DOMAIN = Setting('SAAS_GITLAB_DOMAIN',
|
||||
'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
|
|||
|
||||
from orchestra import plugins
|
||||
from orchestra.utils.humanize import text2int
|
||||
from orchestra.utils.python import AttrDict, cmp_to_key
|
||||
from orchestra.utils.python import AttrDict, cmp_to_key, format_exception
|
||||
|
||||
from . import settings, helpers
|
||||
|
||||
|
@ -50,8 +50,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
|
|||
try:
|
||||
bool(getattr(self, method)(obj))
|
||||
except Exception as exception:
|
||||
name = type(exception).__name__
|
||||
raise ValidationError(': '.join((name, str(exception))))
|
||||
raise ValidationError(format_exception(exc))
|
||||
|
||||
def validate_match(self, service):
|
||||
if not service.match:
|
||||
|
|
|
@ -4,9 +4,11 @@ from orchestra.settings import Setting
|
|||
|
||||
|
||||
SERVICES_SERVICE_TAXES = Setting('SERVICES_SERVICE_TAXES', (
|
||||
(0, _("Duty free")),
|
||||
(21, "21%"),
|
||||
))
|
||||
(0, _("Duty free")),
|
||||
(21, "21%"),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
SERVICES_SERVICE_DEFAULT_TAX = Setting('SERVICES_SERVICE_DEFAULT_TAX', 0,
|
||||
|
@ -19,19 +21,17 @@ SERVICES_SERVICE_ANUAL_BILLING_MONTH = Setting('SERVICES_SERVICE_ANUAL_BILLING_M
|
|||
)
|
||||
|
||||
|
||||
SERVICES_ORDER_MODEL = Setting('SERVICES_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
SERVICES_ORDER_MODEL = Setting('SERVICES_ORDER_MODEL', 'orders.Order',
|
||||
validators=[Setting.validate_model_label]
|
||||
)
|
||||
|
||||
|
||||
SERVICES_RATE_CLASS = Setting('SERVICES_RATE_CLASS',
|
||||
'orchestra.contrib.plans.models.Rate'
|
||||
SERVICES_RATE_CLASS = Setting('SERVICES_RATE_CLASS', 'orchestra.contrib.plans.models.Rate',
|
||||
validators=[Setting.validate_import_class]
|
||||
)
|
||||
|
||||
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = Setting('SERVICES_DEFAULT_IGNORE_PERIOD',
|
||||
'TEN_DAYS'
|
||||
)
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = Setting('SERVICES_DEFAULT_IGNORE_PERIOD', 'TEN_DAYS')
|
||||
|
||||
|
||||
SERVICES_IGNORE_ACCOUNT_TYPE = Setting('SERVICES_IGNORE_ACCOUNT_TYPE', (
|
||||
|
|
|
@ -5,9 +5,11 @@ from functools import partial
|
|||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms.formsets import formset_factory
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.forms import ReadOnlyFormMixin, widgets
|
||||
from orchestra.utils.python import format_exception
|
||||
|
||||
from . import parser
|
||||
|
||||
|
@ -20,21 +22,21 @@ class SettingForm(ReadOnlyFormMixin, forms.Form):
|
|||
widget=forms.Textarea(attrs={
|
||||
'cols': 65,
|
||||
'rows': 2,
|
||||
'style': 'font-family:monospace'
|
||||
'style': 'font-family: monospace',
|
||||
}))
|
||||
CHARFIELD = partial(forms.CharField,
|
||||
widget=forms.TextInput(attrs={
|
||||
'size': 65,
|
||||
'style': 'font-family:monospace'
|
||||
}))
|
||||
NON_EDITABLE = partial(forms.CharField, widget=widgets.ShowTextWidget(), required=False)
|
||||
widget=forms.TextInput(attrs={
|
||||
'size': 65,
|
||||
'style': 'font-family: monospace',
|
||||
}))
|
||||
NON_EDITABLE = partial(forms.CharField, widget=widgets.SpanWidget, required=False)
|
||||
FORMFIELD_FOR_SETTING_TYPE = {
|
||||
bool: partial(forms.BooleanField, required=False),
|
||||
int: forms.IntegerField,
|
||||
tuple: TEXTAREA,
|
||||
list: TEXTAREA,
|
||||
dict: TEXTAREA,
|
||||
type(_()): CHARFIELD,
|
||||
Promise: CHARFIELD,
|
||||
str: CHARFIELD,
|
||||
}
|
||||
|
||||
|
@ -50,8 +52,12 @@ class SettingForm(ReadOnlyFormMixin, forms.Form):
|
|||
self.setting_type = initial['type']
|
||||
self.setting = initial['setting']
|
||||
setting = self.setting
|
||||
serialized_value = parser.serialize(initial['value'])
|
||||
serialized_default = parser.serialize(initial['default'])
|
||||
if setting.serializable:
|
||||
serialized_value = parser.serialize(initial['value'])
|
||||
serialized_default = parser.serialize(initial['default'])
|
||||
else:
|
||||
serialized_value = parser.NotSupported()
|
||||
serialized_default = parser.NotSupported()
|
||||
if not setting.editable or isinstance(serialized_value, parser.NotSupported):
|
||||
field = self.NON_EDITABLE
|
||||
else:
|
||||
|
@ -101,7 +107,7 @@ class SettingForm(ReadOnlyFormMixin, forms.Form):
|
|||
try:
|
||||
value = eval(value, parser.get_eval_context())
|
||||
except Exception as exc:
|
||||
raise ValidationError(str(exc))
|
||||
raise ValidationError(format_exception(exc))
|
||||
self.setting.validate_value(value)
|
||||
if not isinstance(value, self.setting_type):
|
||||
if self.setting_type in (tuple, list) and isinstance(value, (tuple, list)):
|
||||
|
|
|
@ -3,6 +3,7 @@ import os
|
|||
import re
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.functional import Promise
|
||||
|
||||
from orchestra.utils.paths import get_project_dir
|
||||
|
||||
|
@ -60,7 +61,7 @@ def get_eval_context():
|
|||
def serialize(obj, init=True):
|
||||
if isinstance(obj, NotSupported):
|
||||
return obj
|
||||
elif isinstance(obj, type(_())):
|
||||
elif isinstance(obj, Promise):
|
||||
_obj = LazyUgettextRepr(obj)
|
||||
elif isinstance(obj, dict):
|
||||
_obj = {}
|
||||
|
|
|
@ -4,11 +4,13 @@ from orchestra.settings import Setting
|
|||
|
||||
|
||||
SYSTEMUSERS_SHELLS = Setting('SYSTEMUSERS_SHELLS', (
|
||||
('/dev/null', _("No shell, FTP only")),
|
||||
('/bin/rssh', _("No shell, SFTP/RSYNC only")),
|
||||
('/bin/bash', "/bin/bash"),
|
||||
('/bin/sh', "/bin/sh"),
|
||||
))
|
||||
('/dev/null', _("No shell, FTP only")),
|
||||
('/bin/rssh', _("No shell, SFTP/RSYNC only")),
|
||||
('/bin/bash', "/bin/bash"),
|
||||
('/bin/sh', "/bin/sh"),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_DEFAULT_SHELL = Setting('SYSTEMUSERS_DEFAULT_SHELL', '/dev/null',
|
||||
|
@ -16,10 +18,12 @@ SYSTEMUSERS_DEFAULT_SHELL = Setting('SYSTEMUSERS_DEFAULT_SHELL', '/dev/null',
|
|||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_DISABLED_SHELLS = Setting('SYSTEMUSERS_DISABLED_SHELLS', (
|
||||
'/dev/null',
|
||||
'/bin/rssh',
|
||||
))
|
||||
SYSTEMUSERS_DISABLED_SHELLS = Setting('SYSTEMUSERS_DISABLED_SHELLS',
|
||||
default=(
|
||||
'/dev/null',
|
||||
'/bin/rssh',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_HOME = Setting('SYSTEMUSERS_HOME',
|
||||
|
|
|
@ -2,16 +2,20 @@ from orchestra.settings import Setting
|
|||
|
||||
|
||||
VPS_TYPES = Setting('VPS_TYPES', (
|
||||
('openvz', 'OpenVZ container'),
|
||||
))
|
||||
('openvz', 'OpenVZ container'),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
VPS_DEFAULT_TYPE = Setting('VPS_DEFAULT_TYPE', 'openvz', choices=VPS_TYPES)
|
||||
|
||||
|
||||
VPS_TEMPLATES = Setting('VPS_TEMPLATES', (
|
||||
('debian7', 'Debian 7 - Wheezy'),
|
||||
))
|
||||
('debian7', 'Debian 7 - Wheezy'),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
VPS_DEFAULT_TEMPLATE = Setting('VPS_DEFAULT_TEMPLATE', 'debian7', choices=VPS_TEMPLATES)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import orchestra.core.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapps', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='webapp',
|
||||
name='name',
|
||||
field=models.CharField(validators=[orchestra.core.validators.validate_name], max_length=128, verbose_name='name', help_text='The app will be installed in %(home)s/webapps/%(app_name)s'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='webapp',
|
||||
name='type',
|
||||
field=models.CharField(max_length=32, verbose_name='type', choices=[('php', 'PHP'), ('python', 'Python'), ('static', 'Static'), ('symbolic-link', 'Symbolic link'), ('webalizer', 'Webalizer'), ('wordpress-php', 'WordPress')]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='webappoption',
|
||||
name='name',
|
||||
field=models.CharField(max_length=128, verbose_name='name', choices=[(None, '-------'), ('FileSystem', [('public-root', 'Public root')]), ('Process', [('timeout', 'Process timeout'), ('processes', 'Number of processes')]), ('PHP', [('enable_functions', 'Enable functions'), ('allow_url_include', 'Allow URL include'), ('allow_url_fopen', 'Allow URL fopen'), ('auto_append_file', 'Auto append file'), ('auto_prepend_file', 'Auto prepend file'), ('date.timezone', 'date.timezone'), ('default_socket_timeout', 'Default socket timeout'), ('display_errors', 'Display errors'), ('extension', 'Extension'), ('magic_quotes_gpc', 'Magic quotes GPC'), ('magic_quotes_runtime', 'Magic quotes runtime'), ('magic_quotes_sybase', 'Magic quotes sybase'), ('max_input_time', 'Max input time'), ('max_input_vars', 'Max input vars'), ('memory_limit', 'Memory limit'), ('mysql.connect_timeout', 'Mysql connect timeout'), ('output_buffering', 'Output buffering'), ('register_globals', 'Register globals'), ('post_max_size', 'Post max size'), ('sendmail_path', 'Sendmail path'), ('session.bug_compat_warn', 'Session bug compat warning'), ('session.auto_start', 'Session auto start'), ('safe_mode', 'Safe mode'), ('suhosin.post.max_vars', 'Suhosin POST max vars'), ('suhosin.get.max_vars', 'Suhosin GET max vars'), ('suhosin.request.max_vars', 'Suhosin request max vars'), ('suhosin.session.encrypt', 'Suhosin session encrypt'), ('suhosin.simulation', 'Suhosin simulation'), ('suhosin.executor.include.whitelist', 'Suhosin executor include whitelist'), ('upload_max_filesize', 'Upload max filesize'), ('zend_extension', 'Zend extension')])]),
|
||||
),
|
||||
]
|
|
@ -25,35 +25,32 @@ WEBAPPS_PHPFPM_POOL_PATH = Setting('WEBAPPS_PHPFPM_POOL_PATH',
|
|||
|
||||
|
||||
WEBAPPS_FCGID_WRAPPER_PATH = Setting('WEBAPPS_FCGID_WRAPPER_PATH',
|
||||
# Inside SuExec Document root
|
||||
# Make sure all account wrappers are in the same DIR
|
||||
'/home/httpd/fcgi-bin.d/%(user)s/%(app_name)s-wrapper'
|
||||
'/home/httpd/fcgi-bin.d/%(user)s/%(app_name)s-wrapper',
|
||||
help_text=("Inside SuExec Document root.<br>"
|
||||
"Make sure all account wrappers are in the same DIR.")
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FCGID_CMD_OPTIONS_PATH = Setting('WEBAPPS_FCGID_CMD_OPTIONS_PATH',
|
||||
# Loaded by Apache
|
||||
'/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf'
|
||||
'/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf',
|
||||
help_text="Loaded by Apache."
|
||||
)
|
||||
|
||||
|
||||
# Greater or equal to your FcgidMaxRequestsPerProcess
|
||||
# http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#examples
|
||||
WEBAPPS_PHP_MAX_REQUESTS = Setting('WEBAPPS_PHP_MAX_REQUESTS',
|
||||
400
|
||||
400,
|
||||
help_text='Greater or equal to your <a href="http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#examples">FcgidMaxRequestsPerProcess</a>'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_ERROR_LOG_PATH = Setting('WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||
''
|
||||
)
|
||||
WEBAPPS_PHP_ERROR_LOG_PATH = Setting('WEBAPPS_PHP_ERROR_LOG_PATH', '')
|
||||
|
||||
|
||||
WEBAPPS_MERGE_PHP_WEBAPPS = Setting('WEBAPPS_MERGE_PHP_WEBAPPS',
|
||||
# Combine all fcgid-wrappers/fpm-pools into one per account-php_version
|
||||
# to better control num processes per account and save memory
|
||||
False)
|
||||
|
||||
False,
|
||||
help_text=("Combine all fcgid-wrappers/fpm-pools into one per account-php_version "
|
||||
"to better control num processes per account and save memory")
|
||||
)
|
||||
|
||||
WEBAPPS_TYPES = Setting('WEBAPPS_TYPES', (
|
||||
'orchestra.contrib.webapps.types.php.PHPApp',
|
||||
|
@ -70,13 +67,15 @@ WEBAPPS_TYPES = Setting('WEBAPPS_TYPES', (
|
|||
|
||||
|
||||
WEBAPPS_PHP_VERSIONS = Setting('WEBAPPS_PHP_VERSIONS', (
|
||||
# Execution modle choose by ending -fpm or -cgi
|
||||
('5.4-fpm', 'PHP 5.4 FPM'),
|
||||
('5.4-cgi', 'PHP 5.4 FCGID'),
|
||||
('5.3-cgi', 'PHP 5.3 FCGID'),
|
||||
('5.2-cgi', 'PHP 5.2 FCGID'),
|
||||
('4-cgi', 'PHP 4 FCGID'),
|
||||
))
|
||||
('5.4-fpm', 'PHP 5.4 FPM'),
|
||||
('5.4-cgi', 'PHP 5.4 FCGID'),
|
||||
('5.3-cgi', 'PHP 5.3 FCGID'),
|
||||
('5.2-cgi', 'PHP 5.2 FCGID'),
|
||||
('4-cgi', 'PHP 4 FCGID'),
|
||||
),
|
||||
help_text="Execution modle choose by ending -fpm or -cgi.",
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_DEFAULT_PHP_VERSION = Setting('WEBAPPS_DEFAULT_PHP_VERSION', '5.4-cgi',
|
||||
|
@ -84,28 +83,27 @@ WEBAPPS_DEFAULT_PHP_VERSION = Setting('WEBAPPS_DEFAULT_PHP_VERSION', '5.4-cgi',
|
|||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = Setting('WEBAPPS_PHP_CGI_BINARY_PATH',
|
||||
# Path of the cgi binary used by fcgid
|
||||
'/usr/bin/php%(php_version_number)s-cgi'
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = Setting('WEBAPPS_PHP_CGI_BINARY_PATH', '/usr/bin/php%(php_version_number)s-cgi',
|
||||
help_text="Path of the cgi binary used by fcgid."
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_RC_DIR = Setting('WEBAPPS_PHP_CGI_RC_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version_number)s/cgi/'
|
||||
WEBAPPS_PHP_CGI_RC_DIR = Setting('WEBAPPS_PHP_CGI_RC_DIR', '/etc/php%(php_version_number)s/cgi/',
|
||||
help_text="Path to php.ini."
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_INI_SCAN_DIR = Setting('WEBAPPS_PHP_CGI_INI_SCAN_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version_number)s/cgi/conf.d'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PYTHON_VERSIONS = Setting('WEBAPPS_PYTHON_VERSIONS', (
|
||||
('3.4-uwsgi', 'Python 3.4 uWSGI'),
|
||||
('2.7-uwsgi', 'Python 2.7 uWSGI'),
|
||||
))
|
||||
('3.4-uwsgi', 'Python 3.4 uWSGI'),
|
||||
('2.7-uwsgi', 'Python 2.7 uWSGI'),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_DEFAULT_PYTHON_VERSION = Setting('WEBAPPS_DEFAULT_PYTHON_VERSION', '3.4-uwsgi',
|
||||
|
@ -153,7 +151,7 @@ WEBAPPS_UNDER_CONSTRUCTION_PATH = Setting('WEBAPPS_UNDER_CONSTRUCTION_PATH', '',
|
|||
|
||||
|
||||
|
||||
WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', [
|
||||
WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
|
||||
'exec',
|
||||
'passthru',
|
||||
'shell_exec',
|
||||
|
@ -174,7 +172,7 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', [
|
|||
'escapeshellcmd',
|
||||
'escapeshellarg',
|
||||
'dl'
|
||||
])
|
||||
))
|
||||
|
||||
|
||||
WEBAPPS_ENABLED_OPTIONS = Setting('WEBAPPS_ENABLED_OPTIONS', (
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
|
||||
from orchestra.contrib.databases.models import Database, DatabaseUser
|
||||
from orchestra.forms import widgets
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
from orchestra.utils.python import random_ascii
|
||||
|
||||
from .php import PHPApp, PHPAppForm, PHPAppSerializer
|
||||
|
@ -30,13 +30,13 @@ class CMSAppForm(PHPAppForm):
|
|||
db_id = data.get('db_id')
|
||||
db_url = reverse('admin:databases_database_change', args=(db_id,))
|
||||
db_link = mark_safe('<a href="%s">%s</a>' % (db_url, db_name))
|
||||
self.fields['db_name'].widget = widgets.ReadOnlyWidget(db_name, db_link)
|
||||
self.fields['db_name'].widget = SpanWidget(original=db_name, display=db_link)
|
||||
# DB user link
|
||||
db_user = data.get('db_user')
|
||||
db_user_id = data.get('db_user_id')
|
||||
db_user_url = reverse('admin:databases_databaseuser_change', args=(db_user_id,))
|
||||
db_user_link = mark_safe('<a href="%s">%s</a>' % (db_user_url, db_user))
|
||||
self.fields['db_user'].widget = widgets.ReadOnlyWidget(db_user, db_user_link)
|
||||
self.fields['db_user'].widget = SpanWidget(original=db_user, display=db_user_link)
|
||||
|
||||
|
||||
class CMSAppSerializer(PHPAppSerializer):
|
||||
|
|
|
@ -24,7 +24,7 @@ class Apache2Backend(ServiceController):
|
|||
model = 'websites.Website'
|
||||
related_models = (
|
||||
('websites.Content', 'website'),
|
||||
('websites.WebsiteDirective', 'directives'),
|
||||
('websites.WebsiteDirective', 'website'),
|
||||
('webapps.WebApp', 'website_set'),
|
||||
)
|
||||
verbose_name = _("Apache 2")
|
||||
|
|
|
@ -6,40 +6,40 @@ from .. import websites
|
|||
|
||||
|
||||
WEBSITES_UNIQUE_NAME_FORMAT = Setting('WEBSITES_UNIQUE_NAME_FORMAT',
|
||||
'%(user)s-%(site_name)s'
|
||||
default='%(user)s-%(site_name)s'
|
||||
)
|
||||
|
||||
|
||||
# TODO 'http', 'https', 'https-only', 'http and https' and rename to PROTOCOL
|
||||
#WEBSITES_PORT_CHOICES = getattr(settings, 'WEBSITES_PORT_CHOICES', (
|
||||
# (80, 'HTTP'),
|
||||
# (443, 'HTTPS'),
|
||||
#))
|
||||
WEBSITES_PROTOCOL_CHOICES = Setting('WEBSITES_PROTOCOL_CHOICES',
|
||||
default=(
|
||||
('http', "HTTP"),
|
||||
('https', "HTTPS"),
|
||||
('http/https', _("HTTP and HTTPS")),
|
||||
('https-only', _("HTTPS only")),
|
||||
),
|
||||
validators=[Setting.validate_choices]
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_PROTOCOL_CHOICES = Setting('WEBSITES_PROTOCOL_CHOICES', (
|
||||
('http', "HTTP"),
|
||||
('https', "HTTPS"),
|
||||
('http/https', _("HTTP and HTTPS")),
|
||||
('https-only', _("HTTPS only")),
|
||||
))
|
||||
|
||||
WEBSITES_DEFAULT_PROTOCOL = Setting('WEBSITES_DEFAULT_PROTOCOL', 'http',
|
||||
WEBSITES_DEFAULT_PROTOCOL = Setting('WEBSITES_DEFAULT_PROTOCOL',
|
||||
default='http',
|
||||
choices=WEBSITES_PROTOCOL_CHOICES
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_DEFAULT_IPS = Setting('WEBSITES_DEFAULT_IPS', (
|
||||
'*',
|
||||
))
|
||||
|
||||
|
||||
WEBSITES_DOMAIN_MODEL = Setting('WEBSITES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
WEBSITES_DEFAULT_IPS = Setting('WEBSITES_DEFAULT_IPS',
|
||||
default=('*',)
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_ENABLED_DIRECTIVES = Setting('WEBSITES_ENABLED_DIRECTIVES', (
|
||||
WEBSITES_DOMAIN_MODEL = Setting('WEBSITES_DOMAIN_MODEL',
|
||||
'domains.Domain',
|
||||
validators=[Setting.validate_model_label]
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_ENABLED_DIRECTIVES = Setting('WEBSITES_ENABLED_DIRECTIVES',
|
||||
(
|
||||
'orchestra.contrib.websites.directives.Redirect',
|
||||
'orchestra.contrib.websites.directives.Proxy',
|
||||
'orchestra.contrib.websites.directives.ErrorDocument',
|
||||
|
|
|
@ -16,7 +16,7 @@ def all_valid(*args):
|
|||
if len(args) == 1:
|
||||
# Dict
|
||||
errors = {}
|
||||
kwargs = args
|
||||
kwargs = args[0]
|
||||
for field, validator in kwargs.items():
|
||||
try:
|
||||
validator[0](*validator[1:])
|
||||
|
@ -36,7 +36,7 @@ def all_valid(*args):
|
|||
|
||||
|
||||
def validate_ipv4_address(value):
|
||||
msg = _("%s is not a valid IPv4 address") % value
|
||||
msg = _("Not a valid IPv4 address")
|
||||
try:
|
||||
ip = IP(value)
|
||||
except:
|
||||
|
@ -46,7 +46,7 @@ def validate_ipv4_address(value):
|
|||
|
||||
|
||||
def validate_ipv6_address(value):
|
||||
msg = _("%s is not a valid IPv6 address") % value
|
||||
msg = _("Not a valid IPv6 address")
|
||||
try:
|
||||
ip = IP(value)
|
||||
except:
|
||||
|
@ -56,7 +56,7 @@ def validate_ipv6_address(value):
|
|||
|
||||
|
||||
def validate_ip_address(value):
|
||||
msg = _("%s is not a valid IP address") % value
|
||||
msg = _("Not a valid IP address")
|
||||
try:
|
||||
IP(value)
|
||||
except:
|
||||
|
|
|
@ -81,11 +81,10 @@ class ReadOnlyFormMixin(object):
|
|||
for name in self.Meta.readonly_fields:
|
||||
field = self.fields[name]
|
||||
if not isinstance(field, SpanField):
|
||||
field.widget = SpanWidget()
|
||||
if not isinstance(field.widget, SpanWidget):
|
||||
field.widget = SpanWidget()
|
||||
original = self.initial.get(name)
|
||||
if hasattr(self, 'instance'):
|
||||
# Model form
|
||||
original_value = getattr(self.instance, name)
|
||||
else:
|
||||
original_value = self.initial.get(name)
|
||||
field.widget.original_value = original_value
|
||||
original = getattr(self.instance, name, original)
|
||||
field.widget.original = original
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.encoding import force_text
|
|||
|
||||
from django.contrib.admin.templatetags.admin_static import static
|
||||
|
||||
# TODO rename readonlywidget
|
||||
|
||||
class SpanWidget(forms.Widget):
|
||||
"""
|
||||
Renders a value wrapped in a <span> tag.
|
||||
|
@ -15,74 +15,29 @@ class SpanWidget(forms.Widget):
|
|||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.tag = kwargs.pop('tag', '<span>')
|
||||
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)
|
||||
original_value = self.original_value
|
||||
original = self.original or value
|
||||
display = original if self.display is None else self.display
|
||||
# Display icon
|
||||
if isinstance(original_value, bool):
|
||||
icon = static('admin/img/icon-%s.gif' % ('yes' if original_value else 'no',))
|
||||
return mark_safe('<img src="%s" alt="%s">' % (icon, str(original_value)))
|
||||
if isinstance(original, bool):
|
||||
icon = static('admin/img/icon-%s.gif' % ('yes' if original else 'no',))
|
||||
return mark_safe('<img src="%s" alt="%s">' % (icon, str(display)))
|
||||
tag = self.tag[:-1]
|
||||
endtag = '/'.join((self.tag[0], self.tag[1:]))
|
||||
return mark_safe('%s%s >%s%s' % (tag, forms.util.flatatt(final_attrs), original_value, endtag))
|
||||
return mark_safe('%s%s >%s%s' % (tag, forms.util.flatatt(final_attrs), display, endtag))
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.original_value
|
||||
return self.original
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
return False
|
||||
|
||||
|
||||
class ShowTextWidget(forms.Widget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
for kwarg in ['tag', 'warning', 'hidden']:
|
||||
setattr(self, kwarg, kwargs.pop(kwarg, False))
|
||||
super(ShowTextWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
def render(self, name, value, attrs):
|
||||
value = force_text(value)
|
||||
if value is None:
|
||||
return ''
|
||||
if hasattr(self, 'initial'):
|
||||
value = self.initial
|
||||
if self.tag:
|
||||
endtag = '/'.join((self.tag[0], self.tag[1:]))
|
||||
final_value = ''.join((self.tag, value, endtag))
|
||||
else:
|
||||
final_value = '<br/>'.join(value.split('\n'))
|
||||
if self.warning:
|
||||
final_value = (
|
||||
'<ul class="messagelist"><li class="warning">%s</li></ul>'
|
||||
% final_value)
|
||||
if self.hidden:
|
||||
final_value = (
|
||||
'%s<input type="hidden" name="%s" value="%s"/>'
|
||||
% (final_value, name, value))
|
||||
return mark_safe(final_value)
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
return False
|
||||
|
||||
|
||||
class ReadOnlyWidget(forms.Widget):
|
||||
def __init__(self, *args):
|
||||
if len(args) == 1:
|
||||
args = (args[0], args[0])
|
||||
self.original_value = args[0]
|
||||
self.display_value = args[1]
|
||||
super(ReadOnlyWidget, self).__init__()
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if self.display_value is not None:
|
||||
return mark_safe(self.display_value)
|
||||
return mark_safe(self.original_value)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.original_value
|
||||
|
||||
|
||||
def paddingCheckboxSelectMultiple(padding):
|
||||
""" Ugly hack to render this widget nicely on Django admin """
|
||||
widget = forms.CheckboxSelectMultiple()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django import forms
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from orchestra.forms.widgets import ReadOnlyWidget
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
|
||||
class PluginDataForm(forms.ModelForm):
|
||||
|
@ -12,7 +12,7 @@ class PluginDataForm(forms.ModelForm):
|
|||
if self.plugin_field in self.fields:
|
||||
value = self.plugin.get_name()
|
||||
display = '%s <a href=".">change</a>' % force_text(self.plugin.verbose_name)
|
||||
self.fields[self.plugin_field].widget = ReadOnlyWidget(value, display)
|
||||
self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
|
||||
help_text = self.fields[self.plugin_field].help_text
|
||||
self.fields[self.plugin_field].help_text = getattr(self.plugin, 'help_text', help_text)
|
||||
if self.instance:
|
||||
|
@ -34,7 +34,7 @@ class PluginDataForm(forms.ModelForm):
|
|||
if foo_display:
|
||||
display = foo_display()
|
||||
self.fields[field].required = False
|
||||
self.fields[field].widget = ReadOnlyWidget(value, display)
|
||||
self.fields[field].widget = SpanWidget(original=value, display=display)
|
||||
|
||||
def clean(self):
|
||||
super(PluginDataForm, self).clean()
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import re
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ValidationError, AppRegistryNotReady
|
||||
from django.db.models import get_model
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.utils.python import import_class, format_exception
|
||||
|
||||
from .core import validators
|
||||
|
||||
|
||||
class Setting(object):
|
||||
"""
|
||||
Keeps track of the defined settings.
|
||||
Instances of this class are the native value of the setting.
|
||||
Keeps track of the defined settings and provides extra batteries like value validation.
|
||||
"""
|
||||
conf_settings = settings
|
||||
settings = OrderedDict()
|
||||
|
@ -23,12 +28,12 @@ class Setting(object):
|
|||
value = ("'%s'" if isinstance(value, str) else '%s') % value
|
||||
return '<%s: %s>' % (self.name, value)
|
||||
|
||||
def __new__(cls, name, default, help_text="", choices=None, editable=True, multiple=False,
|
||||
validators=[], types=[], call_init=False):
|
||||
def __new__(cls, name, default, help_text="", choices=None, editable=True, serializable=True,
|
||||
multiple=False, validators=[], types=[], call_init=False):
|
||||
if call_init:
|
||||
return super(Setting, cls).__new__(cls)
|
||||
cls.settings[name] = cls(name, default, help_text=help_text, choices=choices,
|
||||
editable=editable, multiple=multiple, validators=validators, types=types, call_init=True)
|
||||
cls.settings[name] = cls(name, default, help_text=help_text, choices=choices, editable=editable,
|
||||
serializable=serializable, multiple=multiple, validators=validators, types=types, call_init=True)
|
||||
return cls.get_value(name, default)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -36,11 +41,67 @@ class Setting(object):
|
|||
for name, value in kwargs.items():
|
||||
setattr(self, name, value)
|
||||
self.value = self.get_value(self.name, self.default)
|
||||
self.validate_value(self.value)
|
||||
try:
|
||||
self.validate_value(self.value)
|
||||
except ValidationError as exc:
|
||||
# Init time warning
|
||||
sys.stderr.write("Error validating setting %s with value %s\n" % (self.name, self.value))
|
||||
sys.stderr.write(format_exception(exc))
|
||||
raise exc
|
||||
except AppRegistryNotReady:
|
||||
# lazy bastards
|
||||
pass
|
||||
self.settings[name] = self
|
||||
|
||||
|
||||
@classmethod
|
||||
def validate_choices(cls, value):
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise ValidationError("%s is not a valid choices." % str(value))
|
||||
for choice in value:
|
||||
if not isinstance(choice, (list, tuple)) or len(choice) != 2:
|
||||
raise ValidationError("%s is not a valid choice." % str(choice))
|
||||
value, verbose = choice
|
||||
if not isinstance(verbose, (str, Promise)):
|
||||
raise ValidationError("%s is not a valid verbose name." % value)
|
||||
|
||||
@classmethod
|
||||
def validate_import_class(cls, value):
|
||||
try:
|
||||
import_class(value)
|
||||
except ImportError as exc:
|
||||
if "cannot import name 'settings'" in str(exc):
|
||||
# circular dependency on init time
|
||||
pass
|
||||
except Exception as exc:
|
||||
raise ValidationError(format_exception(exc))
|
||||
|
||||
@classmethod
|
||||
def validate_model_label(cls, value):
|
||||
try:
|
||||
get_model(*value.split('.'))
|
||||
except AppRegistryNotReady:
|
||||
# circular dependency on init time
|
||||
pass
|
||||
except Exception as exc:
|
||||
raise ValidationError(format_exception(exc))
|
||||
|
||||
@classmethod
|
||||
def string_format_validator(cls, names, modulo=True):
|
||||
def validate_string_format(value, names=names, modulo=modulo):
|
||||
errors = []
|
||||
regex = r'%\(([^\)]+)\)' if modulo else r'{([^}]+)}'
|
||||
for n in re.findall(regex, value):
|
||||
if n not in names:
|
||||
errors.append(
|
||||
ValidationError('%s is not a valid format name.' % n)
|
||||
)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
return validate_string_format
|
||||
|
||||
def validate_value(self, value):
|
||||
validators.all_valid(value, self.validators)
|
||||
if value:
|
||||
validators.all_valid(value, self.validators)
|
||||
valid_types = list(self.types)
|
||||
if isinstance(self.default, (list, tuple)):
|
||||
valid_types.extend([list, tuple])
|
||||
|
@ -56,48 +117,61 @@ class Setting(object):
|
|||
|
||||
|
||||
ORCHESTRA_BASE_DOMAIN = Setting('ORCHESTRA_BASE_DOMAIN',
|
||||
'orchestra.lan'
|
||||
'orchestra.lan',
|
||||
help_text=("Base domain name used for other settings.<br>"
|
||||
"If you're editing the settings via the admin interface <b>it is advisable to "
|
||||
"commit this change before changing any other variables which could be affected</b>.")
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_SITE_URL = Setting('ORCHESTRA_SITE_URL', 'https://orchestra.%s' % ORCHESTRA_BASE_DOMAIN,
|
||||
ORCHESTRA_SITE_URL = Setting('ORCHESTRA_SITE_URL',
|
||||
'https://orchestra.%s' % ORCHESTRA_BASE_DOMAIN,
|
||||
help_text=_("Domain name used when it will not be possible to infere the domain from a request."
|
||||
"For example in periodic tasks.")
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_SITE_NAME = Setting('ORCHESTRA_SITE_NAME', 'orchestra')
|
||||
ORCHESTRA_SITE_NAME = Setting('ORCHESTRA_SITE_NAME',
|
||||
'orchestra',
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_SITE_VERBOSE_NAME = Setting('ORCHESTRA_SITE_VERBOSE_NAME',
|
||||
_("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize())
|
||||
_("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize()),
|
||||
)
|
||||
|
||||
|
||||
# Service management commands
|
||||
|
||||
ORCHESTRA_START_SERVICES = Setting('ORCHESTRA_START_SERVICES', (
|
||||
'postgresql',
|
||||
'celeryevcam',
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
('uwsgi', 'nginx'),
|
||||
))
|
||||
ORCHESTRA_START_SERVICES = Setting('ORCHESTRA_START_SERVICES',
|
||||
default=(
|
||||
'postgresql',
|
||||
'celeryevcam',
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
('uwsgi', 'nginx'),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_RESTART_SERVICES = Setting('ORCHESTRA_RESTART_SERVICES', (
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
'uwsgi'
|
||||
))
|
||||
ORCHESTRA_RESTART_SERVICES = Setting('ORCHESTRA_RESTART_SERVICES',
|
||||
default=(
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
'uwsgi'
|
||||
),
|
||||
)
|
||||
|
||||
ORCHESTRA_STOP_SERVICES = Setting('ORCHESTRA_STOP_SERVICES', (
|
||||
('uwsgi', 'nginx'),
|
||||
'celerybeat',
|
||||
'celeryd',
|
||||
'celeryevcam',
|
||||
'postgresql'
|
||||
))
|
||||
|
||||
ORCHESTRA_STOP_SERVICES = Setting('ORCHESTRA_STOP_SERVICES',
|
||||
default=(
|
||||
('uwsgi', 'nginx'),
|
||||
'celerybeat',
|
||||
'celeryd',
|
||||
'celeryevcam',
|
||||
'postgresql'
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_API_ROOT_VIEW = Setting('ORCHESTRA_API_ROOT_VIEW',
|
||||
|
@ -110,4 +184,6 @@ ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = Setting('ORCHESTRA_DEFAULT_SUPPORT_FROM_E
|
|||
)
|
||||
|
||||
|
||||
ORCHESTRA_EDIT_SETTINGS = Setting('ORCHESTRA_EDIT_SETTINGS', True)
|
||||
ORCHESTRA_EDIT_SETTINGS = Setting('ORCHESTRA_EDIT_SETTINGS',
|
||||
True
|
||||
)
|
||||
|
|
|
@ -16,6 +16,11 @@ def random_ascii(length):
|
|||
return ''.join([random.SystemRandom().choice(string.hexdigits) for i in range(0, length)]).lower()
|
||||
|
||||
|
||||
def format_exception(exception):
|
||||
name = type(exception).__name__
|
||||
return ': '.join((name, str(exception)))
|
||||
|
||||
|
||||
class OrderedSet(collections.MutableSet):
|
||||
def __init__(self, iterable=None):
|
||||
self.end = end = []
|
||||
|
|
Loading…
Reference in a new issue