Added admin support for settings
This commit is contained in:
parent
e8759578b5
commit
759c01c64c
23
TODO.md
23
TODO.md
|
@ -257,8 +257,6 @@ https://code.djangoproject.com/ticket/24576
|
|||
+ Query Expressions, Conditional Expressions, and Database Functions¶
|
||||
* forms: You can now pass a callable that returns an iterable of choices when instantiating a ChoiceField.
|
||||
|
||||
* migrate to DRF3.x
|
||||
|
||||
* move all tests to django-orchestra/tests
|
||||
* *natural keys: those fields that uniquely identify a service, list.name, website.name, webapp.name+account, make sure rest api can not edit thos things
|
||||
|
||||
|
@ -287,3 +285,24 @@ https://code.djangoproject.com/ticket/24576
|
|||
|
||||
# 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
|
||||
|
||||
# rename "edit settings" -> change settings
|
||||
|
||||
# View settings file
|
||||
contrib/orders/models.py: if type(instance) in services:
|
||||
contrib/orders/models.py: if type(instance) in services:
|
||||
contrib/orders/helpers.py: if type(node) in services:
|
||||
contrib/bills/admin.py: return [inline for inline in inlines if type(inline) is not BillLineInline]
|
||||
contrib/bills/admin.py: return [inline for inline in inlines if type(inline) is not ClosedBillLineInline]
|
||||
contrib/accounts/actions.py.save: if type(service) in registered_services:
|
||||
contrib/accounts/actions.py: if type(service) in registered_services:
|
||||
permissions/options.py: for func in inspect.getmembers(type(self), predicate=inspect.ismethod):
|
||||
|
||||
|
|
|
@ -1,2 +1,32 @@
|
|||
from .options import *
|
||||
from functools import update_wrapper
|
||||
|
||||
from django.contrib.admin import site
|
||||
|
||||
from .dashboard import *
|
||||
from .options import *
|
||||
|
||||
|
||||
# monkey-patch admin.site in order to porvide some extra admin urls
|
||||
|
||||
urls = []
|
||||
def register_url(pattern, view, name=""):
|
||||
global urls
|
||||
urls.append((pattern, view, name))
|
||||
site.register_url = register_url
|
||||
|
||||
|
||||
site_get_urls = site.get_urls
|
||||
def get_urls():
|
||||
def wrap(view, cacheable=False):
|
||||
def wrapper(*args, **kwargs):
|
||||
return site.admin_view(view, cacheable)(*args, **kwargs)
|
||||
wrapper.admin_site = site
|
||||
return update_wrapper(wrapper, view)
|
||||
global urls
|
||||
extra_patterns = []
|
||||
for pattern, view, name in urls:
|
||||
extra_patterns.append(
|
||||
url(pattern, wrap(view), name=name)
|
||||
)
|
||||
return site_get_urls() + extra_patterns
|
||||
site.get_urls = get_urls
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
from django.conf import settings
|
||||
from fluent_dashboard import dashboard
|
||||
from fluent_dashboard.modules import CmsAppIconList
|
||||
|
||||
from orchestra.core import services
|
||||
|
||||
|
||||
def generate_services_group():
|
||||
class OrchestraIndexDashboard(dashboard.FluentIndexDashboard):
|
||||
def get_application_modules(self):
|
||||
modules = super(OrchestraIndexDashboard, self).get_application_modules()
|
||||
models = []
|
||||
for model, options in services.get().items():
|
||||
if options.get('menu', True):
|
||||
models.append("%s.%s" % (model.__module__, model._meta.object_name))
|
||||
|
||||
settings.FLUENT_DASHBOARD_APP_GROUPS += (
|
||||
('Services', {
|
||||
'models': models,
|
||||
'collapsible': True,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
generate_services_group()
|
||||
# TODO make this dynamic
|
||||
for module in modules:
|
||||
if module.title == 'Administration':
|
||||
module.children.append({
|
||||
'models': [{
|
||||
'add_url': '/admin/settings/',
|
||||
'app_name': 'settings',
|
||||
'change_url': '/admin/settings/setting/',
|
||||
'name': 'setting',
|
||||
'title': "Settings" }],
|
||||
'name': 'settings',
|
||||
'title': 'Settings',
|
||||
'url': '/admin/settings/'
|
||||
})
|
||||
service_icon_list = CmsAppIconList('Services', models=models, collapsible=True)
|
||||
modules.append(service_icon_list)
|
||||
return modules
|
||||
|
|
|
@ -47,8 +47,7 @@ class AdminFormSet(BaseModelFormSet):
|
|||
|
||||
|
||||
def adminmodelformset_factory(modeladmin, form, formset=AdminFormSet, **kwargs):
|
||||
formset = modelformset_factory(modeladmin.model, form=form, formset=formset,
|
||||
**kwargs)
|
||||
formset = modelformset_factory(modeladmin.model, form=form, formset=formset, **kwargs)
|
||||
formset.modeladmin = modeladmin
|
||||
return formset
|
||||
|
||||
|
|
|
@ -59,6 +59,9 @@ def get_accounts():
|
|||
|
||||
def get_administration_items():
|
||||
childrens = []
|
||||
if isinstalled('orchestra.contrib.settings'):
|
||||
url = reverse('admin:settings_edit_settings')
|
||||
childrens.append(items.MenuItem(_("Settings"), url))
|
||||
if isinstalled('orchestra.contrib.services'):
|
||||
url = reverse('admin:services_service_changelist')
|
||||
childrens.append(items.MenuItem(_("Services"), url))
|
||||
|
|
|
@ -114,6 +114,8 @@ INSTALLED_APPS = (
|
|||
|
||||
# Last to load
|
||||
'orchestra.contrib.resources',
|
||||
'orchestra.contrib.settings',
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
@ -139,7 +141,7 @@ ADMIN_TOOLS_MENU = 'orchestra.admin.menu.OrchestraMenu'
|
|||
|
||||
# Fluent dashboard
|
||||
# TODO subclass like in admin_tools_menu
|
||||
ADMIN_TOOLS_INDEX_DASHBOARD = 'fluent_dashboard.dashboard.FluentIndexDashboard'
|
||||
ADMIN_TOOLS_INDEX_DASHBOARD = 'orchestra.admin.dashboard.OrchestraIndexDashboard'
|
||||
FLUENT_DASHBOARD_ICON_THEME = '../orchestra/icons'
|
||||
|
||||
FLUENT_DASHBOARD_APP_GROUPS = (
|
||||
|
@ -204,6 +206,7 @@ FLUENT_DASHBOARD_APP_ICONS = {
|
|||
'issues/ticket': 'Ticket_star.png',
|
||||
'miscellaneous/miscservice': 'Misc-Misc-Box-icon.png',
|
||||
# Administration
|
||||
'settings/setting': 'preferences.png',
|
||||
'djcelery/taskstate': 'taskstate.png',
|
||||
'orchestration/server': 'vps.png',
|
||||
'orchestration/route': 'hal.png',
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
|
||||
ACCOUNTS_TYPES = Setting('ACCOUNTS_TYPES', (
|
||||
('INDIVIDUAL', _("Individual")),
|
||||
('ASSOCIATION', _("Association")),
|
||||
('CUSTOMER', _("Customer")),
|
||||
|
@ -15,32 +15,26 @@ ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
|
|||
))
|
||||
|
||||
|
||||
ACCOUNTS_DEFAULT_TYPE = getattr(settings, 'ACCOUNTS_DEFAULT_TYPE',
|
||||
'INDIVIDUAL'
|
||||
)
|
||||
ACCOUNTS_DEFAULT_TYPE = Setting('ACCOUNTS_DEFAULT_TYPE', 'INDIVIDUAL', choices=ACCOUNTS_TYPES)
|
||||
|
||||
|
||||
ACCOUNTS_LANGUAGES = getattr(settings, 'ACCOUNTS_LANGUAGES', (
|
||||
ACCOUNTS_LANGUAGES = Setting('ACCOUNTS_LANGUAGES', (
|
||||
('EN', _('English')),
|
||||
))
|
||||
|
||||
|
||||
ACCOUNTS_SYSTEMUSER_MODEL = getattr(settings, 'ACCOUNTS_SYSTEMUSER_MODEL',
|
||||
ACCOUNTS_DEFAULT_LANGUAGE = Setting('ACCOUNTS_DEFAULT_LANGUAGE', 'EN', choices=ACCOUNTS_LANGUAGES)
|
||||
|
||||
|
||||
ACCOUNTS_SYSTEMUSER_MODEL = Setting('ACCOUNTS_SYSTEMUSER_MODEL',
|
||||
'systemusers.SystemUser'
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_DEFAULT_LANGUAGE = getattr(settings, 'ACCOUNTS_DEFAULT_LANGUAGE',
|
||||
'EN'
|
||||
)
|
||||
ACCOUNTS_MAIN_PK = Setting('ACCOUNTS_MAIN_PK', 1)
|
||||
|
||||
|
||||
ACCOUNTS_MAIN_PK = getattr(settings, 'ACCOUNTS_MAIN_PK',
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', (
|
||||
ACCOUNTS_CREATE_RELATED = Setting('ACCOUNTS_CREATE_RELATED', (
|
||||
# <model>, <key field>, <kwargs>, <help_text>
|
||||
('mailboxes.Mailbox',
|
||||
'name',
|
||||
|
@ -60,6 +54,6 @@ ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', (
|
|||
))
|
||||
|
||||
|
||||
ACCOUNTS_SERVICE_REPORT_TEMPLATE = getattr(settings, 'ACCOUNTS_SERVICE_REPORT_TEMPLATE',
|
||||
ACCOUNTS_SERVICE_REPORT_TEMPLATE = Setting('ACCOUNTS_SERVICE_REPORT_TEMPLATE',
|
||||
'admin/accounts/account/service_report.html'
|
||||
)
|
||||
|
|
|
@ -56,7 +56,7 @@ def close_bills(modeladmin, request, queryset):
|
|||
for bill in queryset:
|
||||
if not validate_contact(request, bill):
|
||||
return
|
||||
SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm, extra=0)
|
||||
SelectSourceFormSet = adminmodelformset_factory(SelectSourceForm, modeladmin, extra=0)
|
||||
formset = SelectSourceFormSet(queryset=queryset)
|
||||
if request.POST.get('post') == 'generic_confirmation':
|
||||
formset = SelectSourceFormSet(request.POST, request.FILES, queryset=queryset)
|
||||
|
|
|
@ -1,99 +1,97 @@
|
|||
from django.conf import settings
|
||||
from django_countries import data
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
BILLS_NUMBER_LENGTH = getattr(settings, 'BILLS_NUMBER_LENGTH',
|
||||
4
|
||||
)
|
||||
BILLS_NUMBER_LENGTH = Setting('BILLS_NUMBER_LENGTH', 4)
|
||||
|
||||
|
||||
BILLS_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_INVOICE_NUMBER_PREFIX',
|
||||
BILLS_INVOICE_NUMBER_PREFIX = Setting('BILLS_INVOICE_NUMBER_PREFIX',
|
||||
'I'
|
||||
)
|
||||
|
||||
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX',
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX',
|
||||
'A'
|
||||
)
|
||||
|
||||
|
||||
BILLS_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_FEE_NUMBER_PREFIX',
|
||||
BILLS_FEE_NUMBER_PREFIX = Setting('BILLS_FEE_NUMBER_PREFIX',
|
||||
'F'
|
||||
)
|
||||
|
||||
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_FEE_NUMBER_PREFIX',
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = Setting('BILLS_AMENDMENT_FEE_NUMBER_PREFIX',
|
||||
'B'
|
||||
)
|
||||
|
||||
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = getattr(settings, 'BILLS_PROFORMA_NUMBER_PREFIX',
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = Setting('BILLS_PROFORMA_NUMBER_PREFIX',
|
||||
'P'
|
||||
)
|
||||
|
||||
|
||||
BILLS_DEFAULT_TEMPLATE = getattr(settings, 'BILLS_DEFAULT_TEMPLATE',
|
||||
BILLS_DEFAULT_TEMPLATE = Setting('BILLS_DEFAULT_TEMPLATE',
|
||||
'bills/microspective.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_FEE_TEMPLATE = getattr(settings, 'BILLS_FEE_TEMPLATE',
|
||||
BILLS_FEE_TEMPLATE = Setting('BILLS_FEE_TEMPLATE',
|
||||
'bills/microspective-fee.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_PROFORMA_TEMPLATE = getattr(settings, 'BILLS_PROFORMA_TEMPLATE',
|
||||
BILLS_PROFORMA_TEMPLATE = Setting('BILLS_PROFORMA_TEMPLATE',
|
||||
'bills/microspective-proforma.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CURRENCY = getattr(settings, 'BILLS_CURRENCY',
|
||||
BILLS_CURRENCY = Setting('BILLS_CURRENCY',
|
||||
'euro'
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_PHONE = getattr(settings, 'BILLS_SELLER_PHONE',
|
||||
BILLS_SELLER_PHONE = Setting('BILLS_SELLER_PHONE',
|
||||
'111-112-11-222'
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_EMAIL = getattr(settings, 'BILLS_SELLER_EMAIL',
|
||||
BILLS_SELLER_EMAIL = Setting('BILLS_SELLER_EMAIL',
|
||||
'sales@{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_WEBSITE = getattr(settings, 'BILLS_SELLER_WEBSITE',
|
||||
BILLS_SELLER_WEBSITE = Setting('BILLS_SELLER_WEBSITE',
|
||||
'www.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_BANK_ACCOUNT = getattr(settings, 'BILLS_SELLER_BANK_ACCOUNT',
|
||||
BILLS_SELLER_BANK_ACCOUNT = Setting('BILLS_SELLER_BANK_ACCOUNT',
|
||||
'0000 0000 00 00000000 (Orchestra Bank)'
|
||||
)
|
||||
|
||||
|
||||
BILLS_EMAIL_NOTIFICATION_TEMPLATE = getattr(settings, 'BILLS_EMAIL_NOTIFICATION_TEMPLATE',
|
||||
BILLS_EMAIL_NOTIFICATION_TEMPLATE = Setting('BILLS_EMAIL_NOTIFICATION_TEMPLATE',
|
||||
'bills/bill-notification.email'
|
||||
)
|
||||
|
||||
|
||||
BILLS_ORDER_MODEL = getattr(settings, 'BILLS_ORDER_MODEL',
|
||||
BILLS_ORDER_MODEL = Setting('BILLS_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_CITY = getattr(settings, 'BILLS_CONTACT_DEFAULT_CITY',
|
||||
BILLS_CONTACT_DEFAULT_CITY = Setting('BILLS_CONTACT_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_COUNTRIES = getattr(settings, 'BILLS_CONTACT_COUNTRIES',
|
||||
((k,v) for k,v in data.COUNTRIES.items())
|
||||
BILLS_CONTACT_COUNTRIES = Setting('BILLS_CONTACT_COUNTRIES', tuple((k,v) for k,v in data.COUNTRIES.items()),
|
||||
editable=False
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_COUNTRY = getattr(settings, 'BILLS_CONTACT_DEFAULT_COUNTRY',
|
||||
'ES'
|
||||
BILLS_CONTACT_DEFAULT_COUNTRY = Setting('BILLS_CONTACT_DEFAULT_COUNTRY', 'ES',
|
||||
choices=BILLS_CONTACT_COUNTRIES
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from django.conf import settings
|
||||
from django_countries import data
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES', (
|
||||
|
||||
CONTACTS_DEFAULT_EMAIL_USAGES = Setting('CONTACTS_DEFAULT_EMAIL_USAGES', (
|
||||
'SUPPORT',
|
||||
'ADMIN',
|
||||
'BILLING',
|
||||
|
@ -12,16 +14,13 @@ CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES
|
|||
))
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY',
|
||||
CONTACTS_DEFAULT_CITY = Setting('CONTACTS_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
)
|
||||
|
||||
|
||||
CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', tuple(
|
||||
((k,v) for k,v in data.COUNTRIES.items())
|
||||
))
|
||||
CONTACTS_COUNTRIES = Setting('CONTACTS_COUNTRIES', tuple((k,v) for k,v in data.COUNTRIES.items()),
|
||||
editable=False)
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY',
|
||||
'ES'
|
||||
)
|
||||
CONTACTS_DEFAULT_COUNTRY = Setting('CONTACTS_DEFAULT_COUNTRY', 'ES', choices=CONTACTS_COUNTRIES)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
DATABASES_TYPE_CHOICES = getattr(settings, 'DATABASES_TYPE_CHOICES', (
|
||||
|
||||
DATABASES_TYPE_CHOICES = Setting('DATABASES_TYPE_CHOICES', (
|
||||
('mysql', 'MySQL'),
|
||||
('postgres', 'PostgreSQL'),
|
||||
))
|
||||
|
||||
|
||||
DATABASES_DEFAULT_TYPE = getattr(settings, 'DATABASES_DEFAULT_TYPE',
|
||||
'mysql'
|
||||
)
|
||||
DATABASES_DEFAULT_TYPE = Setting('DATABASES_DEFAULT_TYPE', 'mysql', choices=DATABASES_TYPE_CHOICES)
|
||||
|
||||
|
||||
DATABASES_DEFAULT_HOST = getattr(settings, 'DATABASES_DEFAULT_HOST',
|
||||
DATABASES_DEFAULT_HOST = Setting('DATABASES_DEFAULT_HOST',
|
||||
'localhost'
|
||||
)
|
||||
|
|
|
@ -1,109 +1,106 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_NAME_SERVER = getattr(settings, 'DOMAINS_DEFAULT_NAME_SERVER',
|
||||
DOMAINS_DEFAULT_NAME_SERVER = Setting('DOMAINS_DEFAULT_NAME_SERVER',
|
||||
'ns.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_HOSTMASTER = getattr(settings, 'DOMAINS_DEFAULT_HOSTMASTER',
|
||||
DOMAINS_DEFAULT_HOSTMASTER = Setting('DOMAINS_DEFAULT_HOSTMASTER',
|
||||
'hostmaster@{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_TTL = getattr(settings, 'DOMAINS_DEFAULT_TTL',
|
||||
DOMAINS_DEFAULT_TTL = Setting('DOMAINS_DEFAULT_TTL',
|
||||
'1h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_REFRESH = getattr(settings, 'DOMAINS_DEFAULT_REFRESH',
|
||||
DOMAINS_DEFAULT_REFRESH = Setting('DOMAINS_DEFAULT_REFRESH',
|
||||
'1d'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_RETRY = getattr(settings, 'DOMAINS_DEFAULT_RETRY',
|
||||
DOMAINS_DEFAULT_RETRY = Setting('DOMAINS_DEFAULT_RETRY',
|
||||
'2h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_EXPIRATION = getattr(settings, 'DOMAINS_DEFAULT_EXPIRATION',
|
||||
DOMAINS_DEFAULT_EXPIRATION = Setting('DOMAINS_DEFAULT_EXPIRATION',
|
||||
'4w'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_MIN_CACHING_TIME = getattr(settings, 'DOMAINS_DEFAULT_MIN_CACHING_TIME',
|
||||
DOMAINS_DEFAULT_MIN_CACHING_TIME = Setting('DOMAINS_DEFAULT_MIN_CACHING_TIME',
|
||||
'1h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_ZONE_PATH = getattr(settings, 'DOMAINS_ZONE_PATH',
|
||||
DOMAINS_ZONE_PATH = Setting('DOMAINS_ZONE_PATH',
|
||||
'/etc/bind/master/%(name)s'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_MASTERS_PATH = getattr(settings, 'DOMAINS_MASTERS_PATH',
|
||||
DOMAINS_MASTERS_PATH = Setting('DOMAINS_MASTERS_PATH',
|
||||
'/etc/bind/named.conf.local'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_SLAVES_PATH = getattr(settings, 'DOMAINS_SLAVES_PATH',
|
||||
DOMAINS_SLAVES_PATH = Setting('DOMAINS_SLAVES_PATH',
|
||||
'/etc/bind/named.conf.local'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_CHECKZONE_BIN_PATH = getattr(settings, 'DOMAINS_CHECKZONE_BIN_PATH',
|
||||
DOMAINS_CHECKZONE_BIN_PATH = Setting('DOMAINS_CHECKZONE_BIN_PATH',
|
||||
'/usr/sbin/named-checkzone -i local -k fail -n fail'
|
||||
)
|
||||
|
||||
|
||||
# Used for creating temporary zone files used for validation
|
||||
DOMAINS_ZONE_VALIDATION_TMP_DIR = getattr(settings, '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 = getattr(settings, 'DOMAINS_DEFAULT_A',
|
||||
DOMAINS_DEFAULT_A = Setting('DOMAINS_DEFAULT_A',
|
||||
'10.0.3.13'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_AAAA = getattr(settings, 'DOMAINS_DEFAULT_AAAA',
|
||||
DOMAINS_DEFAULT_AAAA = Setting('DOMAINS_DEFAULT_AAAA',
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', (
|
||||
DOMAINS_DEFAULT_MX = Setting('DOMAINS_DEFAULT_MX', (
|
||||
'10 mail.{}.'.format(ORCHESTRA_BASE_DOMAIN),
|
||||
'10 mail2.{}.'.format(ORCHESTRA_BASE_DOMAIN),
|
||||
))
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', (
|
||||
DOMAINS_DEFAULT_NS = Setting('DOMAINS_DEFAULT_NS', (
|
||||
'ns1.{}.'.format(ORCHESTRA_BASE_DOMAIN),
|
||||
'ns2.{}.'.format(ORCHESTRA_BASE_DOMAIN),
|
||||
))
|
||||
|
||||
|
||||
DOMAINS_FORBIDDEN = getattr(settings, 'DOMAINS_FORBIDDEN',
|
||||
# This setting prevents users from providing random domain names, i.e. google.com
|
||||
# You can generate a 5K forbidden domains list from Alexa's top 1M
|
||||
# wget http://s3.amazonaws.com/alexa-static/top-1m.csv.zip -O /tmp/top-1m.csv.zip
|
||||
# unzip -p /tmp/top-1m.csv.zip | head -n 5000 | sed "s/^.*,//" > forbidden_domains.list
|
||||
|
||||
# '%(site_dir)s/forbidden_domains.list')
|
||||
''
|
||||
DOMAINS_FORBIDDEN = Setting('DOMAINS_FORBIDDEN', '',
|
||||
help_text=(
|
||||
"This setting prevents users from providing random domain names, i.e. google.com"
|
||||
"You can generate a 5K forbidden domains list from Alexa's top 1M"
|
||||
"wget http://s3.amazonaws.com/alexa-static/top-1m.csv.zip -O /tmp/top-1m.csv.zip"
|
||||
"unzip -p /tmp/top-1m.csv.zip | head -n 5000 | sed 's/^.*,//' > forbidden_domains.list"
|
||||
"'%(site_dir)s/forbidden_domains.list')"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_MASTERS = getattr(settings, 'DOMAINS_MASTERS',
|
||||
# Additional master server ip addresses other than autodiscovered by router.get_servers()
|
||||
()
|
||||
DOMAINS_MASTERS = Setting('DOMAINS_MASTERS', (),
|
||||
help_text="Additional master server ip addresses other than autodiscovered by router.get_servers()."
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_SLAVES = getattr(settings, 'DOMAINS_SLAVES',
|
||||
# Additional slave server ip addresses other than autodiscovered by router.get_servers()
|
||||
()
|
||||
DOMAINS_SLAVES = Setting('DOMAINS_SLAVES', (),
|
||||
help_text="Additional slave server ip addresses other than autodiscovered by router.get_servers()."
|
||||
)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
ISSUES_SUPPORT_EMAILS = getattr(settings, 'ISSUES_SUPPORT_EMAILS', [
|
||||
ISSUES_SUPPORT_EMAILS = Setting('ISSUES_SUPPORT_EMAILS', [
|
||||
])
|
||||
|
||||
|
||||
ISSUES_NOTIFY_SUPERUSERS = getattr(settings, 'ISSUES_NOTIFY_SUPERUSERS',
|
||||
ISSUES_NOTIFY_SUPERUSERS = Setting('ISSUES_NOTIFY_SUPERUSERS',
|
||||
True
|
||||
)
|
||||
|
|
|
@ -1,38 +1,36 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL',
|
||||
LISTS_DOMAIN_MODEL = Setting('LISTS_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
LISTS_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN',
|
||||
LISTS_DEFAULT_DOMAIN = Setting('LISTS_DEFAULT_DOMAIN',
|
||||
'lists.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
LISTS_LIST_URL = getattr(settings, 'LISTS_LIST_URL',
|
||||
LISTS_LIST_URL = Setting('LISTS_LIST_URL',
|
||||
'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
LISTS_MAILMAN_POST_LOG_PATH = getattr(settings, 'LISTS_MAILMAN_POST_LOG_PATH',
|
||||
LISTS_MAILMAN_POST_LOG_PATH = Setting('LISTS_MAILMAN_POST_LOG_PATH',
|
||||
'/var/log/mailman/post'
|
||||
)
|
||||
|
||||
|
||||
LISTS_MAILMAN_ROOT_DIR = getattr(settings, 'LISTS_MAILMAN_ROOT_DIR',
|
||||
LISTS_MAILMAN_ROOT_DIR = Setting('LISTS_MAILMAN_ROOT_DIR',
|
||||
'/var/lib/mailman'
|
||||
)
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_PATH',
|
||||
LISTS_VIRTUAL_ALIAS_PATH = Setting('LISTS_VIRTUAL_ALIAS_PATH',
|
||||
'/etc/postfix/mailman_virtual_aliases'
|
||||
)
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'/etc/postfix/mailman_virtual_domains'
|
||||
)
|
||||
|
|
|
@ -58,7 +58,7 @@ class UNIXUserMaildirBackend(ServiceController):
|
|||
|
||||
def delete(self, mailbox):
|
||||
context = self.get_context(mailbox)
|
||||
self.append('mv %(home)s %(home)s.deleted || exit_code=1' % context)
|
||||
self.append('mv %(home)s %(home)s.deleted || exit_code=$?' % context)
|
||||
self.append(textwrap.dedent("""
|
||||
{ sleep 2 && killall -u %(user)s -s KILL; } &
|
||||
killall -u %(user)s || true
|
||||
|
@ -133,7 +133,7 @@ class DovecotPostfixPasswdVirtualUserBackend(ServiceController):
|
|||
UPDATED_VIRTUAL_MAILBOX_MAPS=1""") % context
|
||||
)
|
||||
if context['deleted_home']:
|
||||
self.append("mv %(home)s %(deleted_home)s || exit_code=1" % context)
|
||||
self.append("mv %(home)s %(deleted_home)s || exit_code=$?" % context)
|
||||
else:
|
||||
self.append("rm -fr %(home)s" % context)
|
||||
|
||||
|
|
|
@ -1,63 +1,62 @@
|
|||
import os
|
||||
import textwrap
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
|
||||
MAILBOXES_DOMAIN_MODEL = getattr(settings, 'MAILBOXES_DOMAIN_MODEL',
|
||||
MAILBOXES_DOMAIN_MODEL = Setting('MAILBOXES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_HOME = getattr(settings, 'MAILBOXES_HOME',
|
||||
MAILBOXES_HOME = Setting('MAILBOXES_HOME',
|
||||
'/home/%(name)s/'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVE_PATH = getattr(settings, 'MAILBOXES_SIEVE_PATH',
|
||||
MAILBOXES_SIEVE_PATH = Setting('MAILBOXES_SIEVE_PATH',
|
||||
os.path.join(MAILBOXES_HOME, 'Maildir/sieve/orchestra.sieve')
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_PATH = getattr(settings, 'MAILBOXES_SIEVETEST_PATH',
|
||||
MAILBOXES_SIEVETEST_PATH = Setting('MAILBOXES_SIEVETEST_PATH',
|
||||
'/dev/shm'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_BIN_PATH = getattr(settings, 'MAILBOXES_SIEVETEST_BIN_PATH',
|
||||
MAILBOXES_SIEVETEST_BIN_PATH = Setting('MAILBOXES_SIEVETEST_BIN_PATH',
|
||||
'%(orchestra_root)s/bin/sieve-test'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH',
|
||||
MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH = Setting('MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH',
|
||||
'/etc/postfix/virtual_mailboxes'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH',
|
||||
MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = Setting('MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH',
|
||||
'/etc/postfix/virtual_aliases'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = Setting('MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'/etc/postfix/virtual_domains'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_DOMAIN = getattr(settings, 'MAILBOXES_LOCAL_DOMAIN',
|
||||
MAILBOXES_LOCAL_DOMAIN = Setting('MAILBOXES_LOCAL_DOMAIN',
|
||||
ORCHESTRA_BASE_DOMAIN
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_PASSWD_PATH = getattr(settings, 'MAILBOXES_PASSWD_PATH',
|
||||
MAILBOXES_PASSWD_PATH = Setting('MAILBOXES_PASSWD_PATH',
|
||||
'/etc/dovecot/passwd'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MAILBOX_FILTERINGS = getattr(settings, 'MAILBOXES_MAILBOX_FILTERINGS', {
|
||||
MAILBOXES_MAILBOX_FILTERINGS = Setting('MAILBOXES_MAILBOX_FILTERINGS', {
|
||||
# value: (verbose_name, filter)
|
||||
'DISABLE': (_("Disable"), ''),
|
||||
'REJECT': (_("Reject spam"), textwrap.dedent("""
|
||||
|
@ -76,26 +75,26 @@ MAILBOXES_MAILBOX_FILTERINGS = getattr(settings, 'MAILBOXES_MAILBOX_FILTERINGS',
|
|||
})
|
||||
|
||||
|
||||
MAILBOXES_MAILBOX_DEFAULT_FILTERING = getattr(settings, 'MAILBOXES_MAILBOX_DEFAULT_FILTERING',
|
||||
'REDIRECT'
|
||||
MAILBOXES_MAILBOX_DEFAULT_FILTERING = Setting('MAILBOXES_MAILBOX_DEFAULT_FILTERING', 'REDIRECT',
|
||||
choices=tuple((k, v[0]) for k,v in MAILBOXES_MAILBOX_FILTERINGS.items())
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MAILDIRSIZE_PATH = getattr(settings, 'MAILBOXES_MAILDIRSIZE_PATH',
|
||||
MAILBOXES_MAILDIRSIZE_PATH = Setting('MAILBOXES_MAILDIRSIZE_PATH',
|
||||
'%(home)s/Maildir/maildirsize'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_ADDRESS_DOMAIN = getattr(settings, 'MAILBOXES_LOCAL_ADDRESS_DOMAIN',
|
||||
MAILBOXES_LOCAL_ADDRESS_DOMAIN = Setting('MAILBOXES_LOCAL_ADDRESS_DOMAIN',
|
||||
ORCHESTRA_BASE_DOMAIN
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MAIL_LOG_PATH = getattr(settings, 'MAILBOXES_MAIL_LOG_PATH',
|
||||
MAILBOXES_MAIL_LOG_PATH = Setting('MAILBOXES_MAIL_LOG_PATH',
|
||||
'/var/log/mail.log'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MOVE_ON_DELETE_PATH = getattr(settings, 'MAILBOXES_MOVE_ON_DELETE_PATH',
|
||||
MAILBOXES_MOVE_ON_DELETE_PATH = Setting('MAILBOXES_MOVE_ON_DELETE_PATH',
|
||||
''
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
MISCELLANEOUS_IDENTIFIER_VALIDATORS = getattr(settings, 'MISCELLANEOUS_IDENTIFIER_VALIDATORS', {
|
||||
MISCELLANEOUS_IDENTIFIER_VALIDATORS = Setting('MISCELLANEOUS_IDENTIFIER_VALIDATORS', {
|
||||
# <miscservice__name>: <validator_function>
|
||||
})
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
from datetime import timedelta
|
||||
from os import path
|
||||
|
||||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
ORCHESTRATION_OS_CHOICES = getattr(settings, 'ORCHESTRATION_OS_CHOICES', (
|
||||
ORCHESTRATION_OS_CHOICES = Setting('ORCHESTRATION_OS_CHOICES', (
|
||||
('LINUX', "Linux"),
|
||||
))
|
||||
|
||||
|
||||
ORCHESTRATION_DEFAULT_OS = getattr(settings, 'ORCHESTRATION_DEFAULT_OS', 'LINUX')
|
||||
ORCHESTRATION_DEFAULT_OS = Setting('ORCHESTRATION_DEFAULT_OS', 'LINUX',
|
||||
choices=ORCHESTRATION_OS_CHOICES)
|
||||
|
||||
|
||||
ORCHESTRATION_SSH_KEY_PATH = getattr(settings, 'ORCHESTRATION_SSH_KEY_PATH',
|
||||
ORCHESTRATION_SSH_KEY_PATH = Setting('ORCHESTRATION_SSH_KEY_PATH',
|
||||
path.join(path.expanduser('~'), '.ssh/id_rsa'))
|
||||
|
||||
|
||||
ORCHESTRATION_ROUTER = getattr(settings, 'ORCHESTRATION_ROUTER',
|
||||
ORCHESTRATION_ROUTER = Setting('ORCHESTRATION_ROUTER',
|
||||
'orchestra.contrib.orchestration.models.Route'
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRATION_TEMP_SCRIPT_PATH = getattr(settings, 'ORCHESTRATION_TEMP_SCRIPT_PATH',
|
||||
ORCHESTRATION_TEMP_SCRIPT_PATH = Setting('ORCHESTRATION_TEMP_SCRIPT_PATH',
|
||||
'/dev/shm'
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRATION_DISABLE_EXECUTION = getattr(settings, 'ORCHESTRATION_DISABLE_EXECUTION',
|
||||
ORCHESTRATION_DISABLE_EXECUTION = Setting('ORCHESTRATION_DISABLE_EXECUTION',
|
||||
False
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRATION_BACKEND_CLEANUP_DELTA = getattr(settings, 'ORCHESTRATION_BACKEND_CLEANUP_DELTA',
|
||||
ORCHESTRATION_BACKEND_CLEANUP_DELTA = Setting('ORCHESTRATION_BACKEND_CLEANUP_DELTA',
|
||||
timedelta(days=15)
|
||||
)
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
# Pluggable backend for bill generation.
|
||||
ORDERS_BILLING_BACKEND = getattr(settings, 'ORDERS_BILLING_BACKEND',
|
||||
ORDERS_BILLING_BACKEND = Setting('ORDERS_BILLING_BACKEND',
|
||||
'orchestra.contrib.orders.billing.BillsBackend'
|
||||
)
|
||||
|
||||
|
||||
# Pluggable service class
|
||||
ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL',
|
||||
ORDERS_SERVICE_MODEL = Setting('ORDERS_SERVICE_MODEL',
|
||||
'services.Service'
|
||||
)
|
||||
|
||||
|
||||
# Prevent inspecting these apps for service accounting
|
||||
ORDERS_EXCLUDED_APPS = getattr(settings, 'ORDERS_EXCLUDED_APPS', (
|
||||
ORDERS_EXCLUDED_APPS = Setting('ORDERS_EXCLUDED_APPS', (
|
||||
'orders',
|
||||
'admin',
|
||||
'contenttypes',
|
||||
|
@ -29,6 +29,6 @@ ORDERS_EXCLUDED_APPS = getattr(settings, 'ORDERS_EXCLUDED_APPS', (
|
|||
|
||||
# Only account for significative changes
|
||||
# metric_storage new value: lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue
|
||||
ORDERS_METRIC_ERROR = getattr(settings, 'ORDERS_METRIC_ERROR',
|
||||
ORDERS_METRIC_ERROR = Setting('ORDERS_METRIC_ERROR',
|
||||
0.01
|
||||
)
|
||||
|
|
|
@ -1,28 +1,34 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
from .. import payments
|
||||
|
||||
|
||||
PAYMENT_CURRENCY = getattr(settings, 'PAYMENT_CURRENCY',
|
||||
PAYMENT_CURRENCY = Setting('PAYMENT_CURRENCY',
|
||||
'Eur'
|
||||
)
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_NAME = getattr(settings, 'PAYMENTS_DD_CREDITOR_NAME',
|
||||
PAYMENTS_DD_CREDITOR_NAME = Setting('PAYMENTS_DD_CREDITOR_NAME',
|
||||
'Orchestra')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_IBAN = getattr(settings, 'PAYMENTS_DD_CREDITOR_IBAN',
|
||||
PAYMENTS_DD_CREDITOR_IBAN = Setting('PAYMENTS_DD_CREDITOR_IBAN',
|
||||
'IE98BOFI90393912121212')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_BIC = getattr(settings, 'PAYMENTS_DD_CREDITOR_BIC',
|
||||
PAYMENTS_DD_CREDITOR_BIC = Setting('PAYMENTS_DD_CREDITOR_BIC',
|
||||
'BOFIIE2D')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = getattr(settings, 'PAYMENTS_DD_CREDITOR_AT02_ID',
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = Setting('PAYMENTS_DD_CREDITOR_AT02_ID',
|
||||
'InvalidAT02ID')
|
||||
|
||||
|
||||
PAYMENTS_ENABLED_METHODS = getattr(settings, 'PAYMENTS_ENABLED_METHODS', [
|
||||
PAYMENTS_ENABLED_METHODS = Setting('PAYMENTS_ENABLED_METHODS', (
|
||||
'orchestra.contrib.payments.methods.sepadirectdebit.SEPADirectDebit',
|
||||
'orchestra.contrib.payments.methods.creditcard.CreditCard',
|
||||
])
|
||||
),
|
||||
# lazy loading
|
||||
choices=lambda : ((m.get_class_path(), m.get_class_path()) for m in payments.methods.PaymentMethod.get_plugins()),
|
||||
multiple=True,
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ from orchestra.forms.widgets import ShowTextWidget, ReadOnlyWidget
|
|||
|
||||
class ResourceForm(forms.ModelForm):
|
||||
verbose_name = forms.CharField(label=_("Name"), required=False,
|
||||
widget=ShowTextWidget(bold=True))
|
||||
widget=ShowTextWidget(tag='<b>'))
|
||||
allocated = forms.DecimalField(label=_("Allocated"))
|
||||
unit = forms.CharField(label=_("Unit"), widget=ShowTextWidget(), required=False)
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ class Resource(models.Model):
|
|||
self.sync_periodic_task()
|
||||
# This only work on tests (multiprocessing used on real deployments)
|
||||
apps.get_app_config('resources').reload_relations()
|
||||
run('sleep 2 && touch %s/wsgi.py' % get_project_dir(), async=True)
|
||||
run('{ sleep 2 && touch %s/wsgi.py; } &' % get_project_dir(), async=True)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super(Resource, self).delete(*args, **kwargs)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from .. import saas
|
||||
|
||||
|
||||
SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
|
||||
SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', (
|
||||
'orchestra.contrib.saas.services.moodle.MoodleService',
|
||||
'orchestra.contrib.saas.services.bscw.BSCWService',
|
||||
'orchestra.contrib.saas.services.gitlab.GitLabService',
|
||||
|
@ -12,74 +12,78 @@ SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
|
|||
'orchestra.contrib.saas.services.dokuwiki.DokuWikiService',
|
||||
'orchestra.contrib.saas.services.drupal.DrupalService',
|
||||
'orchestra.contrib.saas.services.seafile.SeaFileService',
|
||||
))
|
||||
),
|
||||
# lazy loading
|
||||
choices=lambda : ((s.get_class_path(), s.get_class_path()) for s in saas.services.SoftwareService.get_plugins()),
|
||||
multiple=True,
|
||||
)
|
||||
|
||||
|
||||
SAAS_WORDPRESS_ADMIN_PASSWORD = getattr(settings, 'SAAS_WORDPRESSMU_ADMIN_PASSWORD',
|
||||
SAAS_WORDPRESS_ADMIN_PASSWORD = Setting('SAAS_WORDPRESSMU_ADMIN_PASSWORD',
|
||||
'secret'
|
||||
)
|
||||
|
||||
|
||||
SAAS_WORDPRESS_BASE_URL = getattr(settings, 'SAAS_WORDPRESS_BASE_URL',
|
||||
SAAS_WORDPRESS_BASE_URL = Setting('SAAS_WORDPRESS_BASE_URL',
|
||||
'http://blogs.{}/'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_DOKUWIKI_TEMPLATE_PATH = getattr(settings, 'SAAS_DOKUWIKI_TEMPLATE_PATH',
|
||||
SAAS_DOKUWIKI_TEMPLATE_PATH = Setting('SAAS_DOKUWIKI_TEMPLATE_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/template.tar.gz'
|
||||
)
|
||||
|
||||
|
||||
SAAS_DOKUWIKI_FARM_PATH = getattr(settings, 'WEBSITES_DOKUWIKI_FARM_PATH',
|
||||
SAAS_DOKUWIKI_FARM_PATH = Setting('WEBSITES_DOKUWIKI_FARM_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/farm'
|
||||
)
|
||||
|
||||
|
||||
SAAS_DRUPAL_SITES_PATH = getattr(settings, 'WEBSITES_DRUPAL_SITES_PATH',
|
||||
SAAS_DRUPAL_SITES_PATH = Setting('WEBSITES_DRUPAL_SITES_PATH',
|
||||
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s'
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_DB_NAME = getattr(settings, 'SAAS_PHPLIST_DB_NAME',
|
||||
SAAS_PHPLIST_DB_NAME = Setting('SAAS_PHPLIST_DB_NAME',
|
||||
'phplist_mu'
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_BASE_DOMAIN = getattr(settings, 'SAAS_PHPLIST_BASE_DOMAIN',
|
||||
SAAS_PHPLIST_BASE_DOMAIN = Setting('SAAS_PHPLIST_BASE_DOMAIN',
|
||||
'lists.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_SEAFILE_DOMAIN = getattr(settings, 'SAAS_SEAFILE_DOMAIN',
|
||||
SAAS_SEAFILE_DOMAIN = Setting('SAAS_SEAFILE_DOMAIN',
|
||||
'seafile.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_SEAFILE_DEFAULT_QUOTA = getattr(settings, 'SAAS_SEAFILE_DEFAULT_QUOTA',
|
||||
SAAS_SEAFILE_DEFAULT_QUOTA = Setting('SAAS_SEAFILE_DEFAULT_QUOTA',
|
||||
50
|
||||
)
|
||||
|
||||
|
||||
SAAS_BSCW_DOMAIN = getattr(settings, 'SAAS_BSCW_DOMAIN',
|
||||
SAAS_BSCW_DOMAIN = Setting('SAAS_BSCW_DOMAIN',
|
||||
'bscw.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_BSCW_DEFAULT_QUOTA = getattr(settings, 'SAAS_BSCW_DEFAULT_QUOTA',
|
||||
SAAS_BSCW_DEFAULT_QUOTA = Setting('SAAS_BSCW_DEFAULT_QUOTA',
|
||||
50
|
||||
)
|
||||
|
||||
SAAS_BSCW_BSADMIN_PATH = getattr(settings, 'SAAS_BSCW_BSADMIN_PATH',
|
||||
SAAS_BSCW_BSADMIN_PATH = Setting('SAAS_BSCW_BSADMIN_PATH',
|
||||
'/home/httpd/bscw/bin/bsadmin',
|
||||
)
|
||||
|
||||
|
||||
SAAS_GITLAB_ROOT_PASSWORD = getattr(settings, 'SAAS_GITLAB_ROOT_PASSWORD',
|
||||
SAAS_GITLAB_ROOT_PASSWORD = Setting('SAAS_GITLAB_ROOT_PASSWORD',
|
||||
'secret'
|
||||
)
|
||||
|
||||
|
||||
SAAS_GITLAB_DOMAIN = getattr(settings, 'SAAS_GITLAB_DOMAIN',
|
||||
SAAS_GITLAB_DOMAIN = Setting('SAAS_GITLAB_DOMAIN',
|
||||
'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
|
|
@ -1,39 +1,40 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
SERVICES_SERVICE_TAXES = getattr(settings, 'SERVICES_SERVICE_TAXES', (
|
||||
|
||||
SERVICES_SERVICE_TAXES = Setting('SERVICES_SERVICE_TAXES', (
|
||||
(0, _("Duty free")),
|
||||
(21, "21%"),
|
||||
))
|
||||
|
||||
|
||||
SERVICES_SERVICE_DEFAULT_TAX = getattr(settings, 'SERVICES_SERVICE_DEFAULT_TAX',
|
||||
0
|
||||
SERVICES_SERVICE_DEFAULT_TAX = Setting('SERVICES_SERVICE_DEFAULT_TAX', 0,
|
||||
choices=SERVICES_SERVICE_TAXES
|
||||
)
|
||||
|
||||
|
||||
SERVICES_SERVICE_ANUAL_BILLING_MONTH = getattr(settings, 'SERVICES_SERVICE_ANUAL_BILLING_MONTH',
|
||||
1
|
||||
SERVICES_SERVICE_ANUAL_BILLING_MONTH = Setting('SERVICES_SERVICE_ANUAL_BILLING_MONTH', 1,
|
||||
choices=tuple((n, n) for n in range(1, 13))
|
||||
)
|
||||
|
||||
|
||||
SERVICES_ORDER_MODEL = getattr(settings, 'SERVICES_ORDER_MODEL',
|
||||
SERVICES_ORDER_MODEL = Setting('SERVICES_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
)
|
||||
|
||||
|
||||
SERVICES_RATE_CLASS = getattr(settings, 'SERVICES_RATE_CLASS',
|
||||
SERVICES_RATE_CLASS = Setting('SERVICES_RATE_CLASS',
|
||||
'orchestra.contrib.plans.models.Rate'
|
||||
)
|
||||
|
||||
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD',
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = Setting('SERVICES_DEFAULT_IGNORE_PERIOD',
|
||||
'TEN_DAYS'
|
||||
)
|
||||
|
||||
|
||||
SERVICES_IGNORE_ACCOUNT_TYPE = getattr(settings, 'SERVICES_IGNORE_ACCOUNT_TYPE', (
|
||||
SERVICES_IGNORE_ACCOUNT_TYPE = Setting('SERVICES_IGNORE_ACCOUNT_TYPE', (
|
||||
'superuser',
|
||||
'STAFF',
|
||||
'FRIEND',
|
||||
|
|
0
orchestra/contrib/settings/__init__.py
Normal file
0
orchestra/contrib/settings/__init__.py
Normal file
89
orchestra/contrib/settings/admin.py
Normal file
89
orchestra/contrib/settings/admin.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
from functools import partial
|
||||
|
||||
from django.contrib import admin, messages
|
||||
from django.db import models
|
||||
|
||||
from django.views.generic.edit import FormView
|
||||
from django.utils.translation import ngettext, ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import Setting
|
||||
from orchestra.utils import sys, paths
|
||||
|
||||
from . import parser
|
||||
from .forms import SettingFormSet
|
||||
|
||||
|
||||
class SettingView(FormView):
|
||||
template_name = 'admin/settings/change_form.html'
|
||||
form_class = SettingFormSet
|
||||
success_url = '.'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SettingView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': _("Change settings"),
|
||||
'settings_file': parser.get_settings_file(),
|
||||
})
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
initial_data = []
|
||||
prev_app = None
|
||||
account = 0
|
||||
for name, setting in Setting.settings.items():
|
||||
app = name.split('_')[0]
|
||||
initial = {
|
||||
'name': setting.name,
|
||||
'help_text': setting.help_text,
|
||||
'default': setting.default,
|
||||
'type': type(setting.default),
|
||||
'value': setting.value,
|
||||
'choices': setting.choices,
|
||||
'app': app,
|
||||
'editable': setting.editable,
|
||||
'multiple': setting.multiple,
|
||||
}
|
||||
if app == 'ORCHESTRA':
|
||||
initial_data.insert(account, initial)
|
||||
account += 1
|
||||
else:
|
||||
initial_data.append(initial)
|
||||
return initial_data
|
||||
|
||||
def form_valid(self, form):
|
||||
settings = Setting.settings
|
||||
changes = {}
|
||||
for data in form.cleaned_data:
|
||||
setting = settings[data['name']]
|
||||
if not isinstance(data['value'], parser.NotSupported) and setting.editable:
|
||||
if setting.value != data['value']:
|
||||
if setting.default == data['value']:
|
||||
changes[setting.name] = parser.Remove()
|
||||
else:
|
||||
changes[setting.name] = parser.serialize(data['value'])
|
||||
if changes:
|
||||
# Display confirmation
|
||||
if not self.request.POST.get('confirmation'):
|
||||
settings_file = parser.get_settings_file()
|
||||
new_content = parser.apply(changes)
|
||||
diff = sys.run("cat <<EOF | diff %s -\n%s\nEOF" % (settings_file, new_content), error_codes=[1, 0]).stdout
|
||||
context = self.get_context_data(form=form)
|
||||
context['diff'] = diff
|
||||
return self.render_to_response(context)
|
||||
|
||||
# Save changes
|
||||
parser.save(changes)
|
||||
n = len(changes)
|
||||
messages.success(self.request, ngettext(
|
||||
_("One change successfully applied, the orchestra is going to be restarted..."),
|
||||
_("%s changes successfully applied, the orchestra is going to be restarted...") % n,
|
||||
n)
|
||||
)
|
||||
# TODO find aonther way without root and implement reload
|
||||
# sys.run('echo { sleep 2 && python3 %s/manage.py reload; } &' % paths.get_site_dir(), async=True)
|
||||
else:
|
||||
messages.success(self.request, _("No changes have been detected."))
|
||||
return super(SettingView, self).form_valid(form)
|
||||
|
||||
|
||||
admin.site.register_url(r'^settings/setting/$', SettingView.as_view(), 'settings_edit_settings')
|
112
orchestra/contrib/settings/forms.py
Normal file
112
orchestra/contrib/settings/forms.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
import math
|
||||
from copy import deepcopy
|
||||
from functools import partial
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms.formsets import formset_factory
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.forms import ReadOnlyFormMixin, widgets
|
||||
|
||||
from . import parser
|
||||
|
||||
from django import forms
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
class SettingForm(ReadOnlyFormMixin, forms.Form):
|
||||
TEXTAREA = partial(forms.CharField,
|
||||
widget=forms.Textarea(attrs={
|
||||
'cols': 65,
|
||||
'rows': 2,
|
||||
'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)
|
||||
FORMFIELD_FOR_SETTING_TYPE = {
|
||||
bool: partial(forms.BooleanField, required=False),
|
||||
int: forms.IntegerField,
|
||||
tuple: TEXTAREA,
|
||||
list: TEXTAREA,
|
||||
dict: TEXTAREA,
|
||||
type(_()): CHARFIELD,
|
||||
str: CHARFIELD,
|
||||
}
|
||||
|
||||
name = forms.CharField(label=_("name"))
|
||||
default = forms.CharField(label=_("default"))
|
||||
|
||||
class Meta:
|
||||
readonly_fields = ('name', 'default')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
initial = kwargs.get('initial')
|
||||
if initial:
|
||||
self.setting_type = initial['type']
|
||||
serialized_value = parser.serialize(initial['value'])
|
||||
serialized_default = parser.serialize(initial['default'])
|
||||
if not initial['editable'] or isinstance(serialized_value, parser.NotSupported):
|
||||
field = self.NON_EDITABLE
|
||||
else:
|
||||
choices = initial.get('choices')
|
||||
field = forms.ChoiceField
|
||||
multiple = initial['multiple']
|
||||
if multiple:
|
||||
field = partial(forms.MultipleChoiceField, widget=forms.CheckboxSelectMultiple)
|
||||
if choices:
|
||||
# Lazy loading
|
||||
if callable(choices):
|
||||
choices = choices()
|
||||
if not multiple:
|
||||
choices = tuple((parser.serialize(val), verb) for val, verb in choices)
|
||||
field = partial(field, choices=choices)
|
||||
else:
|
||||
field = self.FORMFIELD_FOR_SETTING_TYPE.get(self.setting_type, self.NON_EDITABLE)
|
||||
field = deepcopy(field)
|
||||
value = initial['value']
|
||||
default = initial['default']
|
||||
real_field = field
|
||||
while isinstance(real_field, partial):
|
||||
real_field = real_field.func
|
||||
# Do not serialize following form types
|
||||
if real_field not in (forms.MultipleChoiceField, forms.BooleanField):
|
||||
value = serialized_value
|
||||
if real_field is not forms.BooleanField:
|
||||
default = serialized_default
|
||||
initial['value'] = value
|
||||
initial['default'] = default
|
||||
super(SettingForm, self).__init__(*args, **kwargs)
|
||||
if initial:
|
||||
self.changed = bool(value != default)
|
||||
self.fields['value'] = field(label=_("value"))
|
||||
if isinstance(self.fields['value'].widget, forms.Textarea):
|
||||
rows = math.ceil(len(value)/65)
|
||||
self.fields['value'].widget.attrs['rows'] = rows
|
||||
self.fields['name'].help_text = initial['help_text']
|
||||
self.fields['name'].widget.attrs['readonly'] = True
|
||||
self.app = initial['app']
|
||||
|
||||
def clean_value(self):
|
||||
value = self.cleaned_data['value']
|
||||
if not value:
|
||||
return parser.NotSupported()
|
||||
if not isinstance(value, str):
|
||||
value = parser.serialize(value)
|
||||
try:
|
||||
value = eval(value, parser.get_eval_context())
|
||||
except Exception as exc:
|
||||
raise ValidationError(str(exc))
|
||||
if not isinstance(value, self.setting_type):
|
||||
if self.setting_type in (tuple, list) and isinstance(value, (tuple, list)):
|
||||
value = self.setting_type(value)
|
||||
else:
|
||||
raise ValidationError("Please provide a %s." % self.setting_type.__name__)
|
||||
return value
|
||||
|
||||
|
||||
SettingFormSet = formset_factory(SettingForm, extra=0)
|
148
orchestra/contrib/settings/parser.py
Normal file
148
orchestra/contrib/settings/parser.py
Normal file
|
@ -0,0 +1,148 @@
|
|||
import ast
|
||||
import os
|
||||
import re
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.utils.paths import get_project_dir
|
||||
|
||||
|
||||
class Remove(object):
|
||||
""" used to signal a setting remove """
|
||||
pass
|
||||
|
||||
|
||||
def get_settings_file():
|
||||
return os.path.join(get_project_dir(), 'settings.py')
|
||||
|
||||
|
||||
def _find_updates(changes, settings_file):
|
||||
""" find all updates needed for applying changes on settings_file content """
|
||||
with open(settings_file, 'rb') as handler:
|
||||
p = ast.parse(handler.read())
|
||||
updates = []
|
||||
for elem in p.body:
|
||||
if updates and updates[-1][-1] is None:
|
||||
updates[-1][-1] = elem.lineno-1
|
||||
targets = getattr(elem, 'targets', None)
|
||||
if targets:
|
||||
var_name = targets[0].id
|
||||
if var_name in changes:
|
||||
updates.append([var_name, elem.lineno, None])
|
||||
return updates
|
||||
|
||||
|
||||
class LazyUgettextRepr(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return '_("%s")' % self.value
|
||||
|
||||
def __len__(self):
|
||||
return len(repr(self.value))
|
||||
|
||||
|
||||
class NotSupported(object):
|
||||
def __repr__(self):
|
||||
return 'Serialization not supported'
|
||||
|
||||
def __len__(self):
|
||||
return 0
|
||||
|
||||
|
||||
def get_eval_context():
|
||||
return {
|
||||
'NotSupported': NotSupported,
|
||||
'_': _,
|
||||
}
|
||||
|
||||
def serialize(obj, init=True):
|
||||
if isinstance(obj, NotSupported):
|
||||
return obj
|
||||
elif isinstance(obj, type(_())):
|
||||
_obj = LazyUgettextRepr(obj)
|
||||
elif isinstance(obj, dict):
|
||||
_obj = {}
|
||||
for name, value in obj.items():
|
||||
name = serialize(name, init=False)
|
||||
value = serialize(value, init=False)
|
||||
if isinstance(name, NotSupported) or isinstance(value, NotSupported):
|
||||
return NotSupported()
|
||||
_obj[name] = value
|
||||
elif isinstance(obj, (tuple, list)):
|
||||
_obj = []
|
||||
for nested in obj:
|
||||
nested = serialize(nested, init=False)
|
||||
if isinstance(nested, NotSupported):
|
||||
return nested
|
||||
_obj.append(nested)
|
||||
_obj = type(obj)(_obj)
|
||||
elif isinstance(obj, (str, bool, int)):
|
||||
_obj = obj
|
||||
else:
|
||||
_obj = NotSupported()
|
||||
return repr(_obj) if init else _obj
|
||||
|
||||
|
||||
def _format_setting(name, value):
|
||||
if isinstance(value, Remove):
|
||||
return ""
|
||||
value = serialize(eval(value), get_eval_context())
|
||||
return "{name} = {value}".format(name=name, value=value)
|
||||
|
||||
|
||||
def apply(changes, settings_file=get_settings_file()):
|
||||
""" returns settings_file content with applied changes """
|
||||
updates = _find_updates(changes, settings_file)
|
||||
content = []
|
||||
inside = False
|
||||
lineno = None
|
||||
if updates:
|
||||
name, lineno, end = updates.pop(0)
|
||||
# update existing variable declarations
|
||||
with open(settings_file, 'r') as handler:
|
||||
for num, line in enumerate(handler.readlines(), 1):
|
||||
line = line.rstrip()
|
||||
if num == lineno:
|
||||
value = changes.pop(name)
|
||||
line = _format_setting(name, value)
|
||||
if line:
|
||||
content.append(line)
|
||||
inside = True
|
||||
comments = []
|
||||
lastend = end
|
||||
try:
|
||||
name, lineno, end = updates.pop(0)
|
||||
except IndexError:
|
||||
if lastend is None:
|
||||
break
|
||||
if not inside:
|
||||
content.append(line)
|
||||
else:
|
||||
# Discard lines since variable will be replaced
|
||||
# But save comments and blank lines
|
||||
if re.match(r'^\s*(#.*)*\s*$', line):
|
||||
comments.append(line)
|
||||
else:
|
||||
comments = []
|
||||
# End of variable declaration
|
||||
if num == lastend:
|
||||
content.extend(comments)
|
||||
inside = False
|
||||
|
||||
# insert new variables
|
||||
for name, value in changes.items():
|
||||
content.append(_format_setting(name, value))
|
||||
return '\n'.join(content)
|
||||
|
||||
|
||||
def save(changes, settings_file=get_settings_file(), backup=True):
|
||||
""" apply changes to project.settings file, saving a backup """
|
||||
new_settings = apply(changes, settings_file)
|
||||
tmp_settings_file = settings_file + '.tmp'
|
||||
with open(tmp_settings_file, 'w') as handle:
|
||||
handle.write(new_settings)
|
||||
if backup:
|
||||
os.rename(settings_file, settings_file + '.backup')
|
||||
os.rename(tmp_settings_file, settings_file)
|
|
@ -0,0 +1,86 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n l10n %}
|
||||
{% load url from future %}
|
||||
{% load admin_urls static utils %}
|
||||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static "orchestra/css/hide-inline-id.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
{% if diff %} › <a href=".">Settings</a> › Confirm changes{% else %} › Settings{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<form method="post" action="">{% csrf_token %}
|
||||
{% if diff %}
|
||||
{% blocktrans %}
|
||||
<p>The following changes will be performed to <tt>{{ settings_file }}</tt> file.</p>
|
||||
{% endblocktrans %}
|
||||
<PRE>{{ diff }}</PRE>
|
||||
{{ form.management_form }}
|
||||
<table style="display:none">
|
||||
{% for form in form %}
|
||||
{{ form }}
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="hidden" name="confirmation" value="True" />
|
||||
<div class="submit-row"><input type="submit" value="Confirm changes" class="default" name="_diff"></div>
|
||||
{% else %}
|
||||
{% blocktrans %}
|
||||
<p><tt>{{ settings_file }}</tt> file will be automatically updated and Orchestra restarted according to your changes.
|
||||
{% endblocktrans %}
|
||||
{% if form.errors %}
|
||||
<p class="errornote">
|
||||
{% trans "Please correct the errors below." %}
|
||||
</p>
|
||||
{{ form.non_form_errors.as_ul }}
|
||||
{% endif %}
|
||||
{{ form.management_form }}
|
||||
{% regroup form.forms by app as formlist %}
|
||||
{% for app in formlist %}
|
||||
<fieldset class="module">
|
||||
<h2>{{ app.grouper|lower|capfirst }}</h2>
|
||||
<table id="formset" class="form" style="width:100%">
|
||||
{% for form in app.list %}
|
||||
{{ form.non_field_errors }}
|
||||
{% if forloop.first %}
|
||||
<thead><tr>
|
||||
{% for field in form.visible_fields %}
|
||||
<th style="width:{% if forloop.first %}30{% else %}35{% endif %}%">{{ field.label|capfirst }}</th>
|
||||
{% endfor %}
|
||||
</tr></thead>
|
||||
{% endif %}
|
||||
<tr class="{% cycle row1,row2 %}">
|
||||
{% for field in form.visible_fields %}
|
||||
<td>
|
||||
{# Include the hidden fields in the form #}
|
||||
{% if forloop.first %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{ hidden }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ field.errors.as_ul }}
|
||||
<div style="font-family:monospace">{{ field }}{% if forloop.last %}{% if form.changed %}<div style="float:right" title="Changed">†</div>{% endif %}{% endif %}</div>
|
||||
<p class="help" style="max-width:100px; white-space:nowrap;">{{ field.help_text }}</p>
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
<div class="submit-row"><input type="submit" value="Diff changes" class="default" name="_diff"></div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -56,12 +56,12 @@ class UNIXUserBackend(ServiceController):
|
|||
self.append(textwrap.dedent("""\
|
||||
{ sleep 2 && killall -u %(user)s -s KILL; } &
|
||||
killall -u %(user)s || true
|
||||
userdel %(user)s || exit_code=1
|
||||
groupdel %(group)s || exit_code=1
|
||||
userdel %(user)s || exit_code=$?
|
||||
groupdel %(group)s || exit_code=$?
|
||||
""") % context
|
||||
)
|
||||
if context['deleted_home']:
|
||||
self.append("mv %(base_home)s %(deleted_home)s || exit_code=1" % context)
|
||||
self.append("mv %(base_home)s %(deleted_home)s || exit_code=$?" % context)
|
||||
else:
|
||||
self.append("rm -fr %(base_home)s" % context)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from django.conf import settings
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
SYSTEMUSERS_SHELLS = getattr(settings, 'SYSTEMUSERS_SHELLS', (
|
||||
|
||||
SYSTEMUSERS_SHELLS = Setting('SYSTEMUSERS_SHELLS', (
|
||||
('/dev/null', _("No shell, FTP only")),
|
||||
('/bin/rssh', _("No shell, SFTP/RSYNC only")),
|
||||
('/bin/bash', "/bin/bash"),
|
||||
|
@ -11,36 +11,36 @@ SYSTEMUSERS_SHELLS = getattr(settings, 'SYSTEMUSERS_SHELLS', (
|
|||
))
|
||||
|
||||
|
||||
SYSTEMUSERS_DEFAULT_SHELL = getattr(settings, 'SYSTEMUSERS_DEFAULT_SHELL',
|
||||
'/dev/null'
|
||||
SYSTEMUSERS_DEFAULT_SHELL = Setting('SYSTEMUSERS_DEFAULT_SHELL', '/dev/null',
|
||||
choices=SYSTEMUSERS_SHELLS
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_DISABLED_SHELLS = getattr(settings, 'SYSTEMUSERS_DISABLED_SHELLS', (
|
||||
SYSTEMUSERS_DISABLED_SHELLS = Setting('SYSTEMUSERS_DISABLED_SHELLS', (
|
||||
'/dev/null',
|
||||
'/bin/rssh',
|
||||
))
|
||||
|
||||
|
||||
SYSTEMUSERS_HOME = getattr(settings, 'SYSTEMUSERS_HOME',
|
||||
SYSTEMUSERS_HOME = Setting('SYSTEMUSERS_HOME',
|
||||
'/home/%(user)s'
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_FTP_LOG_PATH = getattr(settings, 'SYSTEMUSERS_FTP_LOG_PATH',
|
||||
SYSTEMUSERS_FTP_LOG_PATH = Setting('SYSTEMUSERS_FTP_LOG_PATH',
|
||||
'/var/log/vsftpd.log'
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_MAIL_LOG_PATH = getattr(settings, 'SYSTEMUSERS_MAIL_LOG_PATH',
|
||||
SYSTEMUSERS_MAIL_LOG_PATH = Setting('SYSTEMUSERS_MAIL_LOG_PATH',
|
||||
'/var/log/exim4/mainlog'
|
||||
)
|
||||
|
||||
SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = getattr(settings, 'SYSTEMUSERS_DEFAULT_GROUP_MEMBERS',
|
||||
SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = Setting('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS',
|
||||
('www-data',)
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_MOVE_ON_DELETE_PATH = getattr(settings, 'SYSTEMUSERS_MOVE_ON_DELETE_PATH',
|
||||
SYSTEMUSERS_MOVE_ON_DELETE_PATH = Setting('SYSTEMUSERS_MOVE_ON_DELETE_PATH',
|
||||
''
|
||||
)
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import Setting
|
||||
|
||||
|
||||
VPS_TYPES = getattr(settings, 'VPS_TYPES', (
|
||||
VPS_TYPES = Setting('VPS_TYPES', (
|
||||
('openvz', 'OpenVZ container'),
|
||||
))
|
||||
|
||||
|
||||
VPS_DEFAULT_TYPE = getattr(settings, 'VPS_DEFAULT_TYPE',
|
||||
'openvz'
|
||||
)
|
||||
VPS_DEFAULT_TYPE = Setting('VPS_DEFAULT_TYPE', 'openvz', choices=VPS_TYPES)
|
||||
|
||||
|
||||
VPS_TEMPLATES = getattr(settings, 'VPS_TEMPLATES', (
|
||||
VPS_TEMPLATES = Setting('VPS_TEMPLATES', (
|
||||
('debian7', 'Debian 7 - Wheezy'),
|
||||
))
|
||||
|
||||
|
||||
VPS_DEFAULT_TEMPLATE = getattr(settings, 'VPS_DEFAULT_TEMPLATE',
|
||||
'debian7'
|
||||
)
|
||||
VPS_DEFAULT_TEMPLATE = Setting('VPS_DEFAULT_TEMPLATE', 'debian7', choices=VPS_TEMPLATES)
|
||||
|
|
|
@ -36,7 +36,7 @@ class WebAppServiceMixin(object):
|
|||
|
||||
def delete_webapp_dir(self, context):
|
||||
if context['deleted_app_path']:
|
||||
self.append("mv %(app_path)s %(deleted_app_path)s || exit_code=1" % context)
|
||||
self.append("mv %(app_path)s %(deleted_app_path)s || exit_code=$?" % context)
|
||||
else:
|
||||
self.append("rm -fr %(app_path)s" % context)
|
||||
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
from django.conf import settings
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN, Setting
|
||||
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
from .. import webapps
|
||||
|
||||
|
||||
WEBAPPS_BASE_DIR = getattr(settings, 'WEBAPPS_BASE_DIR',
|
||||
WEBAPPS_BASE_DIR = Setting('WEBAPPS_BASE_DIR',
|
||||
'%(home)s/webapps/%(app_name)s'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FPM_LISTEN = getattr(settings, 'WEBAPPS_FPM_LISTEN',
|
||||
WEBAPPS_FPM_LISTEN = Setting('WEBAPPS_FPM_LISTEN',
|
||||
# '127.0.0.1:9%(app_id)03d
|
||||
'/opt/php/5.4/socks/%(user)s-%(app_name)s.sock'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = getattr(settings, 'WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
|
||||
WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = Setting('WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
|
||||
3
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHPFPM_POOL_PATH = getattr(settings, 'WEBAPPS_PHPFPM_POOL_PATH',
|
||||
WEBAPPS_PHPFPM_POOL_PATH = Setting('WEBAPPS_PHPFPM_POOL_PATH',
|
||||
'/etc/php5/fpm/pool.d/%(user)s-%(app_name)s.conf'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FCGID_WRAPPER_PATH = getattr(settings, 'WEBAPPS_FCGID_WRAPPER_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'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PATH',
|
||||
WEBAPPS_FCGID_CMD_OPTIONS_PATH = Setting('WEBAPPS_FCGID_CMD_OPTIONS_PATH',
|
||||
# Loaded by Apache
|
||||
'/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf'
|
||||
)
|
||||
|
@ -39,33 +39,37 @@ WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PA
|
|||
|
||||
# Greater or equal to your FcgidMaxRequestsPerProcess
|
||||
# http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#examples
|
||||
WEBAPPS_PHP_MAX_REQUESTS = getattr(settings, 'WEBAPPS_PHP_MAX_REQUESTS',
|
||||
WEBAPPS_PHP_MAX_REQUESTS = Setting('WEBAPPS_PHP_MAX_REQUESTS',
|
||||
400
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||
WEBAPPS_PHP_ERROR_LOG_PATH = Setting('WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_MERGE_PHP_WEBAPPS = getattr(settings, 'WEBAPPS_MERGE_PHP_WEBAPPS',
|
||||
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)
|
||||
|
||||
|
||||
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', (
|
||||
WEBAPPS_TYPES = Setting('WEBAPPS_TYPES', (
|
||||
'orchestra.contrib.webapps.types.php.PHPApp',
|
||||
'orchestra.contrib.webapps.types.misc.StaticApp',
|
||||
'orchestra.contrib.webapps.types.misc.WebalizerApp',
|
||||
'orchestra.contrib.webapps.types.misc.SymbolicLinkApp',
|
||||
'orchestra.contrib.webapps.types.wordpress.WordPressApp',
|
||||
'orchestra.contrib.webapps.types.python.PythonApp',
|
||||
))
|
||||
),
|
||||
# lazy loading
|
||||
choices=lambda : ((t.get_class_path(), t.get_class_path()) for t in webapps.types.AppType.get_plugins()),
|
||||
multiple=True,
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_VERSIONS = getattr(settings, 'WEBAPPS_PHP_VERSIONS', (
|
||||
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'),
|
||||
|
@ -75,69 +79,68 @@ WEBAPPS_PHP_VERSIONS = getattr(settings, 'WEBAPPS_PHP_VERSIONS', (
|
|||
))
|
||||
|
||||
|
||||
WEBAPPS_DEFAULT_PHP_VERSION = getattr(settings, 'WEBAPPS_DEFAULT_PHP_VERSION',
|
||||
'5.4-cgi'
|
||||
WEBAPPS_DEFAULT_PHP_VERSION = Setting('WEBAPPS_DEFAULT_PHP_VERSION', '5.4-cgi',
|
||||
choices=WEBAPPS_PHP_VERSIONS
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_BINARY_PATH',
|
||||
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_RC_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_RC_DIR',
|
||||
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_INI_SCAN_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
|
||||
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 = getattr(settings, 'WEBAPPS_PYTHON_VERSIONS', (
|
||||
WEBAPPS_PYTHON_VERSIONS = Setting('WEBAPPS_PYTHON_VERSIONS', (
|
||||
('3.4-uwsgi', 'Python 3.4 uWSGI'),
|
||||
('2.7-uwsgi', 'Python 2.7 uWSGI'),
|
||||
))
|
||||
|
||||
|
||||
WEBAPPS_DEFAULT_PYTHON_VERSION = getattr(settings, 'WEBAPPS_DEFAULT_PYTHON_VERSION',
|
||||
'3.4-uwsgi'
|
||||
WEBAPPS_DEFAULT_PYTHON_VERSION = Setting('WEBAPPS_DEFAULT_PYTHON_VERSION', '3.4-uwsgi',
|
||||
choices=WEBAPPS_PYTHON_VERSIONS
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_UWSGI_SOCKET = getattr(settings, 'WEBAPPS_UWSGI_SOCKET',
|
||||
WEBAPPS_UWSGI_SOCKET = Setting('WEBAPPS_UWSGI_SOCKET',
|
||||
'/var/run/uwsgi/app/%(app_name)s/socket'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_UWSGI_BASE_DIR = getattr(settings, 'WEBAPPS_UWSGI_BASE_DIR',
|
||||
WEBAPPS_UWSGI_BASE_DIR = Setting('WEBAPPS_UWSGI_BASE_DIR',
|
||||
'/etc/uwsgi/'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PYTHON_MAX_REQUESTS = getattr(settings, 'WEBAPPS_PYTHON_MAX_REQUESTS',
|
||||
WEBAPPS_PYTHON_MAX_REQUESTS = Setting('WEBAPPS_PYTHON_MAX_REQUESTS',
|
||||
500
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS = getattr(settings, 'WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS',
|
||||
WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS = Setting('WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS',
|
||||
3
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PYTHON_DEFAULT_TIMEOUT = getattr(settings, 'WEBAPPS_PYTHON_DEFAULT_TIMEOUT',
|
||||
WEBAPPS_PYTHON_DEFAULT_TIMEOUT = Setting('WEBAPPS_PYTHON_DEFAULT_TIMEOUT',
|
||||
30
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_PATH',
|
||||
# Server-side path where a under construction stock page is
|
||||
# '/var/www/undercontruction/index.html',
|
||||
''
|
||||
WEBAPPS_UNDER_CONSTRUCTION_PATH = Setting('WEBAPPS_UNDER_CONSTRUCTION_PATH', '',
|
||||
help_text=("Server-side path where a under construction stock page is "
|
||||
"'/var/www/undercontruction/index.html'")
|
||||
)
|
||||
|
||||
|
||||
|
@ -150,7 +153,7 @@ WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_
|
|||
|
||||
|
||||
|
||||
WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTION', [
|
||||
WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', [
|
||||
'exec',
|
||||
'passthru',
|
||||
'shell_exec',
|
||||
|
@ -174,7 +177,7 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTIO
|
|||
])
|
||||
|
||||
|
||||
WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', (
|
||||
WEBAPPS_ENABLED_OPTIONS = Setting('WEBAPPS_ENABLED_OPTIONS', (
|
||||
'orchestra.contrib.webapps.options.PublicRoot',
|
||||
'orchestra.contrib.webapps.options.Timeout',
|
||||
'orchestra.contrib.webapps.options.Processes',
|
||||
|
@ -209,14 +212,18 @@ WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', (
|
|||
'orchestra.contrib.webapps.options.PHPSuhosinExecutorIncludeWhitelist',
|
||||
'orchestra.contrib.webapps.options.PHPUploadMaxFileSize',
|
||||
'orchestra.contrib.webapps.options.PHPZendExtension',
|
||||
))
|
||||
),
|
||||
# lazy loading
|
||||
choices=lambda : ((o.get_class_path(), o.get_class_path()) for o in webapps.options.AppOption.get_plugins()),
|
||||
multiple=True,
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = getattr(settings, 'WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',
|
||||
WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = Setting('WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',
|
||||
'mysql.{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_MOVE_ON_DELETE_PATH = getattr(settings, 'WEBAPPS_MOVE_ON_DELETE_PATH',
|
||||
WEBAPPS_MOVE_ON_DELETE_PATH = Setting('WEBAPPS_MOVE_ON_DELETE_PATH',
|
||||
''
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ from ..utils import normurlpath
|
|||
|
||||
class Apache2Backend(ServiceController):
|
||||
"""
|
||||
Apache 2.4 backend with support for the following directives:
|
||||
Apache ≥2.4 backend with support for the following directives:
|
||||
<tt>static</tt>, <tt>location</tt>, <tt>fpm</tt>, <tt>fcgid</tt>, <tt>uwsgi</tt>, \
|
||||
<tt>ssl</tt>, <tt>security</tt>, <tt>redirects</tt>, <tt>proxies</tt>, <tt>saas</tt>
|
||||
"""
|
||||
|
@ -24,6 +24,7 @@ class Apache2Backend(ServiceController):
|
|||
model = 'websites.Website'
|
||||
related_models = (
|
||||
('websites.Content', 'website'),
|
||||
('websites.WebsiteDirective', 'directives'),
|
||||
('webapps.WebApp', 'website_set'),
|
||||
)
|
||||
verbose_name = _("Apache 2")
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import Setting
|
||||
|
||||
WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT',
|
||||
from .. import websites
|
||||
|
||||
|
||||
WEBSITES_UNIQUE_NAME_FORMAT = Setting('WEBSITES_UNIQUE_NAME_FORMAT',
|
||||
'%(user)s-%(site_name)s'
|
||||
)
|
||||
|
||||
|
@ -14,29 +17,29 @@ WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT',
|
|||
#))
|
||||
|
||||
|
||||
WEBSITES_PROTOCOL_CHOICES = getattr(settings, 'WEBSITES_PROTOCOL_CHOICES', (
|
||||
WEBSITES_PROTOCOL_CHOICES = Setting('WEBSITES_PROTOCOL_CHOICES', (
|
||||
('http', "HTTP"),
|
||||
('https', "HTTPS"),
|
||||
('http/https', _("HTTP and HTTPS")),
|
||||
('https-only', _("HTTPS only")),
|
||||
))
|
||||
|
||||
WEBSITES_DEFAULT_PROTOCOL = getattr(settings, 'WEBSITES_DEFAULT_PROTOCOL',
|
||||
'http'
|
||||
WEBSITES_DEFAULT_PROTOCOL = Setting('WEBSITES_DEFAULT_PROTOCOL', 'http',
|
||||
choices=WEBSITES_PROTOCOL_CHOICES
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_DEFAULT_IPS = getattr(settings, 'WEBSITES_DEFAULT_IPS', (
|
||||
'*'
|
||||
WEBSITES_DEFAULT_IPS = Setting('WEBSITES_DEFAULT_IPS', (
|
||||
'*',
|
||||
))
|
||||
|
||||
|
||||
WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL',
|
||||
WEBSITES_DOMAIN_MODEL = Setting('WEBSITES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_ENABLED_DIRECTIVES = getattr(settings, 'WEBSITES_ENABLED_DIRECTIVES', (
|
||||
WEBSITES_ENABLED_DIRECTIVES = Setting('WEBSITES_ENABLED_DIRECTIVES', (
|
||||
'orchestra.contrib.websites.directives.Redirect',
|
||||
'orchestra.contrib.websites.directives.Proxy',
|
||||
'orchestra.contrib.websites.directives.ErrorDocument',
|
||||
|
@ -48,30 +51,34 @@ WEBSITES_ENABLED_DIRECTIVES = getattr(settings, 'WEBSITES_ENABLED_DIRECTIVES', (
|
|||
'orchestra.contrib.websites.directives.WordPressSaaS',
|
||||
'orchestra.contrib.websites.directives.DokuWikiSaaS',
|
||||
'orchestra.contrib.websites.directives.DrupalSaaS',
|
||||
))
|
||||
),
|
||||
# lazy loading
|
||||
choices=lambda : ((d.get_class_path(), d.get_class_path()) for d in websites.directives.SiteDirective.get_plugins()),
|
||||
multiple=True,
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_BASE_APACHE_CONF = getattr(settings, 'WEBSITES_BASE_APACHE_CONF',
|
||||
WEBSITES_BASE_APACHE_CONF = Setting('WEBSITES_BASE_APACHE_CONF',
|
||||
'/etc/apache2/'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBALIZER_PATH = getattr(settings, 'WEBSITES_WEBALIZER_PATH',
|
||||
WEBSITES_WEBALIZER_PATH = Setting('WEBSITES_WEBALIZER_PATH',
|
||||
'/home/httpd/webalizer/'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH = getattr(settings, 'WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH',
|
||||
WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH = Setting('WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH',
|
||||
'/var/log/apache2/virtual/%(unique_name)s.log'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH = getattr(settings, 'WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH',
|
||||
WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH = Setting('WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH',
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_TRAFFIC_IGNORE_HOSTS = getattr(settings, 'WEBSITES_TRAFFIC_IGNORE_HOSTS',
|
||||
WEBSITES_TRAFFIC_IGNORE_HOSTS = Setting('WEBSITES_TRAFFIC_IGNORE_HOSTS',
|
||||
('127.0.0.1',)
|
||||
)
|
||||
|
||||
|
@ -86,26 +93,28 @@ WEBSITES_TRAFFIC_IGNORE_HOSTS = getattr(settings, 'WEBSITES_TRAFFIC_IGNORE_HOSTS
|
|||
# '')
|
||||
|
||||
|
||||
WEBSITES_SAAS_DIRECTIVES = getattr(settings, 'WEBSITES_SAAS_DIRECTIVES', {
|
||||
WEBSITES_SAAS_DIRECTIVES = Setting('WEBSITES_SAAS_DIRECTIVES', {
|
||||
'wordpress-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock', '/home/httpd/wordpress-mu/'),
|
||||
'drupal-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/drupal-mu/'),
|
||||
'dokuwiki-saas': ('fpm', '/opt/php/5.4/socks/pangea.sock','/home/httpd/moodle-mu/'),
|
||||
})
|
||||
|
||||
|
||||
WEBSITES_DEFAULT_SSL_CERT = getattr(settings, 'WEBSITES_DEFAULT_SSL_CERT',
|
||||
WEBSITES_DEFAULT_SSL_CERT = Setting('WEBSITES_DEFAULT_SSL_CERT',
|
||||
''
|
||||
)
|
||||
|
||||
WEBSITES_DEFAULT_SSL_KEY = getattr(settings, 'WEBSITES_DEFAULT_SSL_KEY',
|
||||
WEBSITES_DEFAULT_SSL_KEY = Setting('WEBSITES_DEFAULT_SSL_KEY',
|
||||
''
|
||||
)
|
||||
|
||||
WEBSITES_DEFAULT_SSL_CA = getattr(settings, 'WEBSITES_DEFAULT_SSL_CA',
|
||||
WEBSITES_DEFAULT_SSL_CA = Setting('WEBSITES_DEFAULT_SSL_CA',
|
||||
''
|
||||
)
|
||||
|
||||
WEBSITES_VHOST_EXTRA_DIRECTIVES = getattr(settings, 'WEBSITES_VHOST_EXTRA_DIRECTIVES', (
|
||||
# (<location>, <directive>),
|
||||
# ('/cgi-bin/', 'ScriptAlias /cgi-bin/ %(home)s/cgi-bin/'),
|
||||
))
|
||||
WEBSITES_VHOST_EXTRA_DIRECTIVES = Setting('WEBSITES_VHOST_EXTRA_DIRECTIVES', (),
|
||||
help_text=(
|
||||
"(<location>, <directive>), <br>"
|
||||
"i.e. ('/cgi-bin/', 'ScriptAlias /cgi-bin/ %(home)s/cgi-bin/')"
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django import forms
|
||||
|
||||
from .widgets import SpanWidget
|
||||
|
||||
|
||||
class MultiSelectFormField(forms.MultipleChoiceField):
|
||||
""" http://djangosnippets.org/snippets/1200/ """
|
||||
|
@ -13,3 +15,15 @@ class MultiSelectFormField(forms.MultipleChoiceField):
|
|||
if not value and self.required:
|
||||
raise forms.ValidationError(self.error_messages['required'])
|
||||
return value
|
||||
|
||||
|
||||
class SpanField(forms.Field):
|
||||
"""
|
||||
A field which renders a value wrapped in a <span> tag.
|
||||
|
||||
Requires use of specific form support. (see ReadonlyForm or ReadonlyModelForm)
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['widget'] = kwargs.get('widget', SpanWidget)
|
||||
super(SpanField, self).__init__(*args, **kwargs)
|
||||
|
|
|
@ -6,6 +6,9 @@ from orchestra.utils.python import random_ascii
|
|||
|
||||
from ..core.validators import validate_password
|
||||
|
||||
from .fields import SpanField
|
||||
from .widgets import SpanWidget
|
||||
|
||||
|
||||
class UserCreationForm(forms.ModelForm):
|
||||
"""
|
||||
|
@ -65,3 +68,24 @@ class UserChangeForm(forms.ModelForm):
|
|||
# This is done here, rather than on the field, because the
|
||||
# field does not have access to the initial value
|
||||
return self.initial["password"]
|
||||
|
||||
|
||||
class ReadOnlyFormMixin(object):
|
||||
"""
|
||||
Mixin class for ModelForm or Form that provides support for SpanField on readonly fields
|
||||
Meta:
|
||||
readonly_fileds = (ro_field1, ro_field2)
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ReadOnlyFormMixin, self).__init__(*args, **kwargs)
|
||||
for name in self.Meta.readonly_fields:
|
||||
field = self.fields[name]
|
||||
if not isinstance(field, SpanField):
|
||||
field.widget = SpanWidget()
|
||||
if hasattr(self, 'instance'):
|
||||
# Model form
|
||||
original_value = str(getattr(self.instance, name))
|
||||
else:
|
||||
original_value = str(self.initial.get(name))
|
||||
field.widget.original_value = original_value
|
||||
|
||||
|
|
|
@ -5,10 +5,40 @@ from django import forms
|
|||
from django.utils.safestring import mark_safe
|
||||
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.
|
||||
Requires use of specific form support. (see ReadonlyForm or ReadonlyModelForm)
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.tag = kwargs.pop('tag', '<span>')
|
||||
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
|
||||
# Display icon
|
||||
if original_value in ('True', 'False') or 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)))
|
||||
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))
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.original_value
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
return False
|
||||
|
||||
|
||||
class ShowTextWidget(forms.Widget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
for kwarg in ['bold', 'warning', 'hidden']:
|
||||
for kwarg in ['tag', 'warning', 'hidden']:
|
||||
setattr(self, kwarg, kwargs.pop(kwarg, False))
|
||||
super(ShowTextWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
|
@ -18,8 +48,9 @@ class ShowTextWidget(forms.Widget):
|
|||
return ''
|
||||
if hasattr(self, 'initial'):
|
||||
value = self.initial
|
||||
if self.bold:
|
||||
final_value = '<b>%s</b>' % (value)
|
||||
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:
|
||||
|
|
|
@ -19,7 +19,7 @@ def run_tuple(services, action, options, optional=False):
|
|||
|
||||
|
||||
def flatten(nested, depth=0):
|
||||
if hasattr(nested, '__iter__'):
|
||||
if isinstance(nested, (list, tuple)):
|
||||
for sublist in nested:
|
||||
for element in flatten(sublist, depth+1):
|
||||
yield element
|
||||
|
|
|
@ -55,6 +55,10 @@ class Plugin(object):
|
|||
def get_change_readonly_fileds(cls):
|
||||
return cls.change_readonly_fileds
|
||||
|
||||
@classmethod
|
||||
def get_class_path(cls):
|
||||
return '.'.join((cls.__module__, cls.__name__))
|
||||
|
||||
def clean_data(self):
|
||||
""" model clean, uses cls.serizlier by default """
|
||||
if self.serializer:
|
||||
|
|
|
@ -1,58 +1,100 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
# Domain name used when it will not be possible to infere the domain from a request
|
||||
# For example in periodic tasks
|
||||
ORCHESTRA_SITE_URL = getattr(settings, 'ORCHESTRA_SITE_URL',
|
||||
'http://localhost'
|
||||
)
|
||||
class Setting(object):
|
||||
"""
|
||||
Keeps track of the defined settings.
|
||||
Instances of this class are the native value of the setting.
|
||||
"""
|
||||
conf_settings = settings
|
||||
settings = OrderedDict()
|
||||
|
||||
ORCHESTRA_SITE_NAME = getattr(settings, 'ORCHESTRA_SITE_NAME',
|
||||
'orchestra'
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
value = str(self.value)
|
||||
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, 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, call_init=True)
|
||||
return cls.get_value(name, default)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.name, self.default = args
|
||||
for name, value in kwargs.items():
|
||||
setattr(self, name, value)
|
||||
self.value = self.get_value(self.name, self.default)
|
||||
self.settings[name] = self
|
||||
|
||||
@classmethod
|
||||
def get_value(cls, name, default):
|
||||
return getattr(cls.conf_settings, name, default)
|
||||
|
||||
|
||||
# TODO validation, defaults to same type
|
||||
|
||||
|
||||
ORCHESTRA_BASE_DOMAIN = Setting('ORCHESTRA_BASE_DOMAIN',
|
||||
'orchestra.lan'
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_SITE_VERBOSE_NAME = getattr(settings, 'ORCHESTRA_SITE_VERBOSE_NAME',
|
||||
ORCHESTRA_SITE_URL = Setting('ORCHESTRA_SITE_URL', 'http://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_VERBOSE_NAME = Setting('ORCHESTRA_SITE_VERBOSE_NAME',
|
||||
_("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize())
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_BASE_DOMAIN = getattr(settings, 'ORCHESTRA_BASE_DOMAIN',
|
||||
'orchestra.lan'
|
||||
)
|
||||
|
||||
# Service management commands
|
||||
|
||||
ORCHESTRA_START_SERVICES = getattr(settings, 'ORCHESTRA_START_SERVICES', [
|
||||
ORCHESTRA_START_SERVICES = Setting('ORCHESTRA_START_SERVICES', (
|
||||
'postgresql',
|
||||
'celeryevcam',
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
('uwsgi', 'nginx'),
|
||||
])
|
||||
))
|
||||
|
||||
|
||||
ORCHESTRA_RESTART_SERVICES = getattr(settings, 'ORCHESTRA_RESTART_SERVICES', [
|
||||
ORCHESTRA_RESTART_SERVICES = Setting('ORCHESTRA_RESTART_SERVICES', (
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
'uwsgi'
|
||||
])
|
||||
))
|
||||
|
||||
ORCHESTRA_STOP_SERVICES = getattr(settings, 'ORCHESTRA_STOP_SERVICES', [
|
||||
ORCHESTRA_STOP_SERVICES = Setting('ORCHESTRA_STOP_SERVICES', (
|
||||
('uwsgi', 'nginx'),
|
||||
'celerybeat',
|
||||
'celeryd',
|
||||
'celeryevcam',
|
||||
'postgresql'
|
||||
])
|
||||
))
|
||||
|
||||
|
||||
ORCHESTRA_API_ROOT_VIEW = getattr(settings, 'ORCHESTRA_API_ROOT_VIEW',
|
||||
ORCHESTRA_API_ROOT_VIEW = Setting('ORCHESTRA_API_ROOT_VIEW',
|
||||
'orchestra.api.root.APIRoot'
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL',
|
||||
ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = Setting('ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL',
|
||||
'support@{}'.format(ORCHESTRA_BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
ORCHESTRA_EDIT_SETTINGS = Setting('ORCHESTRA_EDIT_SETTINGS', True)
|
||||
|
|
BIN
orchestra/static/orchestra/icons/Preferences.png
Normal file
BIN
orchestra/static/orchestra/icons/Preferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
783
orchestra/static/orchestra/icons/Preferences.svg
Normal file
783
orchestra/static/orchestra/icons/Preferences.svg
Normal file
|
@ -0,0 +1,783 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="90.000000"
|
||||
inkscape:export-xdpi="90.000000"
|
||||
inkscape:export-filename="/home/glic3/orchestra/django-orchestra/orchestra/static/orchestra/icons/Preferences.png"
|
||||
width="48px"
|
||||
height="48px"
|
||||
id="svg11300"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="Preferences.svg"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs3">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient6719"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
|
||||
cx="605.71429"
|
||||
cy="486.64789"
|
||||
fx="605.71429"
|
||||
fy="486.64789"
|
||||
r="117.14286" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5060">
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5062" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop5064" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient6717"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
|
||||
cx="605.71429"
|
||||
cy="486.64789"
|
||||
fx="605.71429"
|
||||
fy="486.64789"
|
||||
r="117.14286" />
|
||||
<linearGradient
|
||||
id="linearGradient5048">
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop5050" />
|
||||
<stop
|
||||
id="stop5056"
|
||||
offset="0.5"
|
||||
style="stop-color:black;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop5052" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5048"
|
||||
id="linearGradient6715"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
|
||||
x1="302.85715"
|
||||
y1="366.64789"
|
||||
x2="302.85715"
|
||||
y2="609.50507" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient24290">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop24292" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop24294" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient24276">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop24278" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop24280" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient24266">
|
||||
<stop
|
||||
style="stop-color:#a5a5a5;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop24268" />
|
||||
<stop
|
||||
style="stop-color:#a5a5a5;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop24270" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient24230">
|
||||
<stop
|
||||
style="stop-color:#677579;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop24232" />
|
||||
<stop
|
||||
style="stop-color:#333333;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop24234" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11594">
|
||||
<stop
|
||||
id="stop11596"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11598"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#d1d1d1;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.061966,0,0,0.837825,-0.593045,3.987819)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="38.947163"
|
||||
x2="31.799011"
|
||||
y1="8.9471626"
|
||||
x1="20.092352"
|
||||
id="linearGradient11600"
|
||||
xlink:href="#linearGradient11594"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient11602">
|
||||
<stop
|
||||
id="stop11604"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#f6f6f6;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop11606"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#e0e0e0;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1,0,0,0.837825,0.921766,3.987819)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="39.447163"
|
||||
x2="24.445671"
|
||||
y1="12.947163"
|
||||
x1="24.445671"
|
||||
id="linearGradient11608"
|
||||
xlink:href="#linearGradient11602"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient11615">
|
||||
<stop
|
||||
id="stop11617"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#636363;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop11619"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#000000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="4.7500000"
|
||||
fy="27.749998"
|
||||
fx="25.000000"
|
||||
cy="27.749998"
|
||||
cx="25.000000"
|
||||
gradientTransform="matrix(3.070491,2.727143e-15,-3.444813e-15,3.878514,-51.46548,-78.83433)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient12909"
|
||||
xlink:href="#linearGradient11615"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="27.375000"
|
||||
x2="21.500000"
|
||||
y1="30.000000"
|
||||
x1="21.500000"
|
||||
gradientTransform="matrix(0.985,0,0,1.022813,2.121141,-2.815681)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient12924"
|
||||
xlink:href="#linearGradient11625"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="27.375000"
|
||||
x2="21.500000"
|
||||
y1="30.000000"
|
||||
x1="21.500000"
|
||||
gradientTransform="matrix(0.985,0,0,1,4.111767,-2.176922)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient12921"
|
||||
xlink:href="#linearGradient11625"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient11625"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11627"
|
||||
offset="0"
|
||||
style="stop-color:#fce94f;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11629"
|
||||
offset="1"
|
||||
style="stop-color:#fce94f;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="27.375000"
|
||||
x2="21.500000"
|
||||
y1="30.000000"
|
||||
x1="21.500000"
|
||||
gradientTransform="matrix(1.01625,0,0,1,5.455516,-2.176922)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient12918"
|
||||
xlink:href="#linearGradient11625"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient11520">
|
||||
<stop
|
||||
id="stop11522"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop11524"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#dcdcdc;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11508"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11510"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11512"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11494"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11496"
|
||||
offset="0"
|
||||
style="stop-color:#ef2929;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11498"
|
||||
offset="1"
|
||||
style="stop-color:#ef2929;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11415">
|
||||
<stop
|
||||
id="stop11417"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#204a87;stop-opacity:0.0000000;" />
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1.0000000;"
|
||||
offset="0.50000000"
|
||||
id="stop11423" />
|
||||
<stop
|
||||
id="stop11419"
|
||||
offset="1"
|
||||
style="stop-color:#204a87;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11399"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11401"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11403"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-60.28571,-0.285714)"
|
||||
y2="34.462429"
|
||||
x2="43.615788"
|
||||
y1="3.7744560"
|
||||
x1="15.828360"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11425"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-60.57143,0.000000)"
|
||||
y2="39.033859"
|
||||
x2="35.679932"
|
||||
y1="9.3458843"
|
||||
x1="9.6957054"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11427"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="33.462429"
|
||||
x2="26.758644"
|
||||
y1="19.774456"
|
||||
x1="13.267134"
|
||||
gradientTransform="translate(-60.85714,0.428571)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11439"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="8.5000000"
|
||||
fy="39.142857"
|
||||
fx="12.071428"
|
||||
cy="39.142857"
|
||||
cx="12.071428"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.487395,0.000000,20.06483)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11441"
|
||||
xlink:href="#linearGradient11399"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="3.8335034"
|
||||
fy="15.048258"
|
||||
fx="27.577173"
|
||||
cy="15.048258"
|
||||
cx="27.577173"
|
||||
id="radialGradient11500"
|
||||
xlink:href="#linearGradient11494"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="3.8335034"
|
||||
fy="16.049133"
|
||||
fx="27.577173"
|
||||
cy="16.049133"
|
||||
cx="27.577173"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11504"
|
||||
xlink:href="#linearGradient11494"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.166583e-14,29.48178)"
|
||||
r="6.5659914"
|
||||
fy="44.565483"
|
||||
fx="30.203562"
|
||||
cy="44.565483"
|
||||
cx="30.203562"
|
||||
id="radialGradient11514"
|
||||
xlink:href="#linearGradient11508"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.8227153,0,0,1.5134373,-18.449633,-14.322885)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20.530962"
|
||||
fy="35.878170"
|
||||
fx="24.445690"
|
||||
cy="35.878170"
|
||||
cx="24.445690"
|
||||
id="radialGradient11526"
|
||||
xlink:href="#linearGradient11520"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="6.5659914"
|
||||
fy="44.565483"
|
||||
fx="30.203562"
|
||||
cy="44.565483"
|
||||
cx="30.203562"
|
||||
gradientTransform="matrix(1,0,0,0.338462,8.404809e-16,29.48178)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11532"
|
||||
xlink:href="#linearGradient11508"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11508"
|
||||
id="radialGradient1348"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,-1.353344e-14,29.48178)"
|
||||
cx="30.203562"
|
||||
cy="44.565483"
|
||||
fx="30.203562"
|
||||
fy="44.565483"
|
||||
r="6.5659914" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11520"
|
||||
id="radialGradient1350"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
|
||||
cx="24.445690"
|
||||
cy="35.878170"
|
||||
fx="24.445690"
|
||||
fy="35.878170"
|
||||
r="20.530962" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11494"
|
||||
id="radialGradient1352"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
cx="27.577173"
|
||||
cy="16.049133"
|
||||
fx="27.577173"
|
||||
fy="16.049133"
|
||||
r="3.8335034" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11494"
|
||||
id="radialGradient1354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
cx="27.577173"
|
||||
cy="15.048258"
|
||||
fx="27.577173"
|
||||
fy="15.048258"
|
||||
r="3.8335034" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11508"
|
||||
id="radialGradient1356"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.220359e-14,29.48178)"
|
||||
cx="30.203562"
|
||||
cy="44.565483"
|
||||
fx="30.203562"
|
||||
fy="44.565483"
|
||||
r="6.5659914" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11520"
|
||||
id="radialGradient1366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.049266,-1.696401e-32,0.000000,2.049266,-25.65002,-37.31089)"
|
||||
cx="24.445690"
|
||||
cy="35.878170"
|
||||
fx="24.445690"
|
||||
fy="35.878170"
|
||||
r="20.530962" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24230"
|
||||
id="linearGradient24236"
|
||||
x1="12.51301"
|
||||
y1="30.585787"
|
||||
x2="12.51301"
|
||||
y2="16.885592"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.142857,0,0,0.67154739,0.10214033,14.945674)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24230"
|
||||
id="linearGradient24240"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.142857,0,0,0.67154739,9.2152552,14.915937)"
|
||||
x1="12.51301"
|
||||
y1="30.585787"
|
||||
x2="12.51301"
|
||||
y2="16.885592" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24230"
|
||||
id="linearGradient24244"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.142857,0,0,0.67154739,18.358111,14.915937)"
|
||||
x1="12.51301"
|
||||
y1="30.585787"
|
||||
x2="12.51301"
|
||||
y2="16.885592" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24266"
|
||||
id="linearGradient24272"
|
||||
x1="23.5"
|
||||
y1="19.812498"
|
||||
x2="23.5"
|
||||
y2="12.687223"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.142857,0,0,1.142857,-4.627741,-7.290132)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24276"
|
||||
id="radialGradient24282"
|
||||
cx="24.90625"
|
||||
cy="35.46875"
|
||||
fx="24.90625"
|
||||
fy="35.46875"
|
||||
r="17.40625"
|
||||
gradientTransform="matrix(1,0,0,0.321364,0,24.07035)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24290"
|
||||
id="linearGradient24296"
|
||||
x1="24.53125"
|
||||
y1="19.0625"
|
||||
x2="26.3125"
|
||||
y2="40.25"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.142857,0,0,1.142857,-4.5633838,-5.1132743)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
stroke="#ef2929"
|
||||
fill="#eeeeec"
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="0.25490196"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="11.977443"
|
||||
inkscape:cy="19.615128"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="1024"
|
||||
inkscape:window-height="992"
|
||||
inkscape:window-x="774"
|
||||
inkscape:window-y="112"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Steiner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://jimmac.musichall.cz</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||
<dc:title></dc:title>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>preferences</rdf:li>
|
||||
<rdf:li>system</rdf:li>
|
||||
<rdf:li>category</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
transform="matrix(0.02263057,0,0,0.02384865,43.8493,37.63317)"
|
||||
id="g6707">
|
||||
<rect
|
||||
style="opacity:0.40206185;color:#000000;fill:url(#linearGradient6715);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect6709"
|
||||
width="1339.6335"
|
||||
height="478.35718"
|
||||
x="-1559.2523"
|
||||
y="-150.69685" />
|
||||
<path
|
||||
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient6717);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
|
||||
id="path6711"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
id="path6713"
|
||||
d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
|
||||
style="opacity:0.40206185;color:#000000;fill:url(#radialGradient6719);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<rect
|
||||
ry="0.98129779"
|
||||
rx="0.98129815"
|
||||
y="13.852715"
|
||||
x="5.1806402"
|
||||
height="27.278023"
|
||||
width="36.601158"
|
||||
id="rect11518"
|
||||
style="color:#000000;fill:url(#radialGradient11526);fill-opacity:1;fill-rule:evenodd;stroke:#9b9b9b;stroke-width:1.14285839;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14285684;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect11528"
|
||||
width="34.315437"
|
||||
height="24.992298"
|
||||
x="6.3234916"
|
||||
y="14.995586"
|
||||
rx="0"
|
||||
ry="0"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
style="color:#000000;fill:url(#linearGradient24236);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect23355"
|
||||
width="5.7440214"
|
||||
height="11.398832"
|
||||
x="11.530714"
|
||||
y="25.690432"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
y="25.660698"
|
||||
x="20.643827"
|
||||
height="11.398832"
|
||||
width="5.7440214"
|
||||
id="rect24238"
|
||||
style="color:#000000;fill:url(#linearGradient24240);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<rect
|
||||
style="color:#000000;fill:url(#linearGradient24244);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24242"
|
||||
width="5.7440214"
|
||||
height="11.398832"
|
||||
x="29.786686"
|
||||
y="25.660698"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#9b9b9b;stroke-width:1.14285707;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24246"
|
||||
width="6.8571434"
|
||||
height="5.7360334"
|
||||
x="10.989021"
|
||||
y="25.097256"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
y="31.954399"
|
||||
x="20.131872"
|
||||
height="5.7360334"
|
||||
width="6.8571434"
|
||||
id="rect24248"
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#9b9b9b;stroke-width:1.14285707;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<rect
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#9b9b9b;stroke-width:1.14285707;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24250"
|
||||
width="6.8571434"
|
||||
height="5.7360334"
|
||||
x="29.274731"
|
||||
y="31.954399"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14285696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24252"
|
||||
width="4.695261"
|
||||
height="3.4820046"
|
||||
x="12.097212"
|
||||
y="26.2272"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
y="33.119007"
|
||||
x="21.150898"
|
||||
height="3.4820046"
|
||||
width="4.695261"
|
||||
id="rect24254"
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14285696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<rect
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14285696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24256"
|
||||
width="4.695261"
|
||||
height="3.4820046"
|
||||
x="30.293756"
|
||||
y="33.119007"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<path
|
||||
style="color:#000000;fill:url(#linearGradient24272);fill-opacity:1;fill-rule:nonzero;stroke:#787878;stroke-width:1.1428566;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 5.0865439,16.138433 1.0865441,4.7098648 l 45.7142829,0 -4.928571,11.4285682 -36.7857121,0 z"
|
||||
id="path24258"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="color:#000000;fill:#dddddd;fill-opacity:1;fill-rule:nonzero;stroke:#9f9f9f;stroke-width:1.14285719;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24260"
|
||||
width="46.857162"
|
||||
height="3.3571427"
|
||||
x="0.51511782"
|
||||
y="4.7098675"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true" />
|
||||
<rect
|
||||
style="color:#000000;fill:#dddddd;fill-opacity:1;fill-rule:nonzero;stroke:#9f9f9f;stroke-width:1.14285696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24262"
|
||||
width="6.357142"
|
||||
height="2.2857139"
|
||||
x="8.5151148"
|
||||
y="14.995582"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
rx="1.142857"
|
||||
ry="1.142857" />
|
||||
<rect
|
||||
ry="1.142857"
|
||||
rx="1.142857"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
y="14.995582"
|
||||
x="31.872257"
|
||||
height="2.2857139"
|
||||
width="6.357142"
|
||||
id="rect24264"
|
||||
style="color:#000000;fill:#dddddd;fill-opacity:1;fill-rule:nonzero;stroke:#9f9f9f;stroke-width:1.14285696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<rect
|
||||
style="opacity:0.43406593;color:#000000;fill:#9f9f9f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="rect24284"
|
||||
width="18.357143"
|
||||
height="2.2857139"
|
||||
x="10.293758"
|
||||
y="20.029581"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
rx="1.142857"
|
||||
ry="1.142857" />
|
||||
<path
|
||||
style="opacity:0.83406587;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m 1.1509015,7.4581538 0,-2.2857145 45.7142805,0 -1.142852,1.1428569 -43.4285715,0 -1.142857,1.1428576 z"
|
||||
id="path24286"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:0.09890113;color:#000000;fill:url(#linearGradient24296);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m 5.7223298,16.815297 0,8.034661 1.1428566,-0.92857 c 19.1111696,0.53615 23.7380926,0.155813 33.1428546,5.965335 l 1.214288,1.5 -0.07144,-14.714285 -35.4285696,0.142859 z"
|
||||
id="path24288"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 28 KiB |
BIN
orchestra/static/orchestra/icons/preferences.png
Normal file
BIN
orchestra/static/orchestra/icons/preferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -81,6 +81,12 @@ def content_type_id(label):
|
|||
return ContentType.objects.filter(app_label=app_label, model=model).values_list('id', flat=True)[0]
|
||||
|
||||
|
||||
@register.filter
|
||||
def split(value, sep=' '):
|
||||
parts = value.split(sep)
|
||||
return (parts[0], sep.join(parts[1:]))
|
||||
|
||||
|
||||
@register.filter
|
||||
def admin_url(obj):
|
||||
return change_url(obj)
|
||||
|
|
|
@ -76,9 +76,9 @@ def runiterator(command, display=False, error_codes=[0], silent=False, stdin=b''
|
|||
#.decode('ascii'), errors='replace')
|
||||
|
||||
if display and stdout:
|
||||
sys.stdout.write(stdout)
|
||||
sys.stdout.write(stdout.decode('utf8'))
|
||||
if display and stderr:
|
||||
sys.stderr.write(stderr)
|
||||
sys.stderr.write(stderr.decode('utf8'))
|
||||
|
||||
state = _Attribute(stdout)
|
||||
state.stderr = stderr
|
||||
|
|
Loading…
Reference in a new issue