Improved backends help texts
This commit is contained in:
parent
eebaee1097
commit
c2b0186034
4
TODO.md
4
TODO.md
|
@ -266,7 +266,6 @@ https://code.djangoproject.com/ticket/24576
|
|||
|
||||
* MultiCHoiceField proper serialization
|
||||
|
||||
# Apache restart fails: detect if appache running, and execute start
|
||||
* UNIFY PHP FPM settings name
|
||||
# virtualhost name: name-account?
|
||||
* add a delay to changes on the webserver apache to no overwelm it with backend executions?
|
||||
|
@ -278,6 +277,7 @@ https://code.djangoproject.com/ticket/24576
|
|||
* rename resource.monitors to resource.backends ?
|
||||
* abstract model classes that enabling overriding, and ORCHESTRA_DATABASE_MODEL settings + orchestra.get_database_model() instead of explicitly importing from orchestra.contrib.databases.models import Database.. (Admin and REST API are fucked then?)
|
||||
|
||||
# billing order list filter detect metrics that are greater from those of billing_date
|
||||
# Ignore superusers & co on billing: list filter doesn't work nor ignore detection
|
||||
# bill.totals make it 100% computed?
|
||||
* joomla: wget https://github.com/joomla/joomla-cms/releases/download/3.4.1/Joomla_3.4.1-Stable-Full_Package.tar.gz -O - | tar xvfz -
|
||||
|
@ -285,7 +285,5 @@ https://code.djangoproject.com/ticket/24576
|
|||
# replace multichoicefield and jsonfield by ArrayField, HStoreField
|
||||
# Amend lines???
|
||||
|
||||
# Add icon on select contact view
|
||||
|
||||
# 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
|
||||
|
|
|
@ -122,12 +122,13 @@ class AccountListAdmin(AccountAdmin):
|
|||
# TODO get query string from request.META['QUERY_STRING'] to preserve filters
|
||||
context = {
|
||||
'url': '../?account=' + str(instance.pk),
|
||||
'name': instance.username
|
||||
'name': instance.username,
|
||||
'plus': '<strong style="color:green; font-size:12px">+</strong>',
|
||||
}
|
||||
return '<a href="%(url)s">%(name)s</a>' % context
|
||||
return _('<a href="%(url)s">%(plus)s Add to %(name)s</a>') % context
|
||||
select_account.short_description = _("account")
|
||||
select_account.allow_tags = True
|
||||
select_account.order_admin_field = 'username'
|
||||
select_account.admin_order_field = 'username'
|
||||
|
||||
def changelist_view(self, request, extra_context=None):
|
||||
original_app_label = request.META['PATH_INFO'].split('/')[-5]
|
||||
|
|
|
@ -14,7 +14,7 @@ from .models import Database, DatabaseUser
|
|||
class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||
list_display = ('name', 'type', 'display_users', 'account_link')
|
||||
list_filter = ('type',)
|
||||
search_fields = ['name']
|
||||
search_fields = ('name', 'account__username')
|
||||
change_readonly_fields = ('name', 'type')
|
||||
extra = 1
|
||||
fieldsets = (
|
||||
|
@ -71,7 +71,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin):
|
||||
list_display = ('username', 'type', 'display_databases', 'account_link')
|
||||
list_filter = ('type',)
|
||||
search_fields = ['username']
|
||||
search_fields = ('username', 'account__username')
|
||||
form = DatabaseUserChangeForm
|
||||
add_form = DatabaseUserCreationForm
|
||||
change_readonly_fields = ('username', 'type')
|
||||
|
|
|
@ -10,14 +10,14 @@ from . import settings
|
|||
|
||||
class MySQLBackend(ServiceController):
|
||||
"""
|
||||
Simple backend for creating MySQL databases using `CREATE DATABASE` statement.
|
||||
DATABASES_DEFAULT_HOST = %s
|
||||
Simple backend for creating MySQL databases using <tt>CREATE DATABASE</tt> statement.
|
||||
"""
|
||||
format_docstring = (settings.DATABASES_DEFAULT_HOST,)
|
||||
|
||||
verbose_name = "MySQL database"
|
||||
model = 'databases.Database'
|
||||
default_route_match = "database.type == 'mysql'"
|
||||
doc_settings = (settings,
|
||||
('DATABASES_DEFAULT_HOST',)
|
||||
)
|
||||
|
||||
def save(self, database):
|
||||
if database.type != database.MYSQL:
|
||||
|
@ -61,13 +61,14 @@ class MySQLBackend(ServiceController):
|
|||
|
||||
class MySQLUserBackend(ServiceController):
|
||||
"""
|
||||
Simple backend for creating MySQL users using `CREATE USER` statement.
|
||||
DATABASES_DEFAULT_HOST = %s
|
||||
""" % settings.DATABASES_DEFAULT_HOST
|
||||
|
||||
Simple backend for creating MySQL users using <tt>CREATE USER</tt> statement.
|
||||
"""
|
||||
verbose_name = "MySQL user"
|
||||
model = 'databases.DatabaseUser'
|
||||
default_route_match = "databaseuser.type == 'mysql'"
|
||||
doc_settings = (settings,
|
||||
('DATABASES_DEFAULT_HOST',)
|
||||
)
|
||||
|
||||
def save(self, user):
|
||||
if user.type != user.MYSQL:
|
||||
|
@ -105,7 +106,7 @@ class MySQLUserBackend(ServiceController):
|
|||
|
||||
class MysqlDisk(ServiceMonitor):
|
||||
"""
|
||||
du -bs <database_path>
|
||||
<tt>du -bs <database_path></tt>
|
||||
Implements triggers for resource limit exceeded and recovery, disabling insert and create privileges.
|
||||
"""
|
||||
model = 'databases.Database'
|
||||
|
|
|
@ -67,7 +67,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
inlines = [RecordInline, DomainInline]
|
||||
list_filter = [TopDomainListFilter]
|
||||
change_readonly_fields = ('name',)
|
||||
search_fields = ['name',]
|
||||
search_fields = ('name', 'account__username')
|
||||
add_form = BatchDomainCreationAdminForm
|
||||
change_view_actions = [view_zone]
|
||||
|
||||
|
|
|
@ -14,14 +14,8 @@ class Bind9MasterDomainBackend(ServiceController):
|
|||
"""
|
||||
Bind9 zone and config generation.
|
||||
It auto-discovers slave Bind9 servers based on your routing configuration or you can use DOMAINS_SLAVES to explicitly configure the slaves.
|
||||
DOMAINS_SLAVES = %s
|
||||
DOMAINS_MASTERS_PATH = '%s'
|
||||
"""
|
||||
|
||||
format_docstring = (
|
||||
str(settings.DOMAINS_SLAVES),
|
||||
settings.DOMAINS_MASTERS_PATH,
|
||||
)
|
||||
CONF_PATH = settings.DOMAINS_MASTERS_PATH
|
||||
|
||||
verbose_name = _("Bind9 master domain")
|
||||
model = 'domains.Domain'
|
||||
|
@ -30,7 +24,9 @@ class Bind9MasterDomainBackend(ServiceController):
|
|||
('domains.Domain', 'origin'),
|
||||
)
|
||||
ignore_fields = ['serial']
|
||||
CONF_PATH = settings.DOMAINS_MASTERS_PATH
|
||||
doc_settings = (settings,
|
||||
('DOMAINS_SLAVES', 'DOMAINS_MASTERS_PATH')
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_main(cls, obj):
|
||||
|
@ -136,15 +132,16 @@ class Bind9SlaveDomainBackend(Bind9MasterDomainBackend):
|
|||
"""
|
||||
Generate the configuartion for slave servers
|
||||
It auto-discover the master server based on your routing configuration or you can use DOMAINS_MASTERS to explicitly configure the master.
|
||||
DOMAINS_MASTERS = %s
|
||||
""" % str(settings.DOMAINS_MASTERS)
|
||||
"""
|
||||
CONF_PATH = settings.DOMAINS_SLAVES_PATH
|
||||
|
||||
verbose_name = _("Bind9 slave domain")
|
||||
related_models = (
|
||||
('domains.Domain', 'origin'),
|
||||
)
|
||||
CONF_PATH = settings.DOMAINS_SLAVES_PATH
|
||||
|
||||
doc_settings = (settings,
|
||||
('DOMAINS_MASTERS', 'DOMAINS_SLAVES_PATH')
|
||||
)
|
||||
def save(self, domain):
|
||||
context = self.get_context(domain)
|
||||
self.update_conf(context)
|
||||
|
|
|
@ -42,8 +42,8 @@ class MessageReadOnlyInline(admin.TabularInline):
|
|||
model = Message
|
||||
extra = 0
|
||||
can_delete = False
|
||||
fields = ['content_html']
|
||||
readonly_fields = ['content_html']
|
||||
fields = ('content_html',)
|
||||
readonly_fields = ('content_html',)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
|
@ -79,7 +79,7 @@ class MessageInline(admin.TabularInline):
|
|||
max_num = 1
|
||||
form = MessageInlineForm
|
||||
can_delete = False
|
||||
fields = ['content']
|
||||
fields = ('content',)
|
||||
|
||||
def get_formset(self, request, obj=None, **kwargs):
|
||||
""" hook request.user on the inline form """
|
||||
|
@ -93,14 +93,14 @@ class MessageInline(admin.TabularInline):
|
|||
|
||||
|
||||
class TicketInline(admin.TabularInline):
|
||||
fields = [
|
||||
fields = (
|
||||
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
|
||||
'colored_priority', 'created', 'updated'
|
||||
]
|
||||
readonly_fields = [
|
||||
)
|
||||
readonly_fields = (
|
||||
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
|
||||
'colored_priority', 'created', 'updated'
|
||||
]
|
||||
)
|
||||
model = Ticket
|
||||
extra = 0
|
||||
max_num = 0
|
||||
|
@ -119,35 +119,35 @@ class TicketInline(admin.TabularInline):
|
|||
|
||||
|
||||
class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin):
|
||||
list_display = [
|
||||
list_display = (
|
||||
'unbold_id', 'bold_subject', 'display_creator', 'display_owner',
|
||||
'display_queue', 'display_priority', 'display_state', 'updated'
|
||||
]
|
||||
)
|
||||
list_display_links = ('unbold_id', 'bold_subject')
|
||||
list_filter = [
|
||||
list_filter = (
|
||||
MyTicketsListFilter, 'queue__name', 'priority', TicketStateListFilter,
|
||||
]
|
||||
)
|
||||
default_changelist_filters = (
|
||||
('my_tickets', lambda r: 'True' if not r.user.is_superuser else 'False'),
|
||||
('state', 'OPEN')
|
||||
)
|
||||
date_hierarchy = 'created_at'
|
||||
search_fields = [
|
||||
search_fields = (
|
||||
'id', 'subject', 'creator__username', 'creator__email', 'queue__name',
|
||||
'owner__username'
|
||||
]
|
||||
actions = [
|
||||
)
|
||||
actions = (
|
||||
mark_as_unread, mark_as_read, 'delete_selected', reject_tickets,
|
||||
resolve_tickets, close_tickets, take_tickets
|
||||
]
|
||||
sudo_actions = ['delete_selected']
|
||||
change_view_actions = [
|
||||
)
|
||||
sudo_actions = ('delete_selected',)
|
||||
change_view_actions = (
|
||||
resolve_tickets, close_tickets, reject_tickets, take_tickets
|
||||
]
|
||||
)
|
||||
# change_form_template = "admin/orchestra/change_form.html"
|
||||
form = TicketForm
|
||||
add_inlines = []
|
||||
inlines = [ MessageReadOnlyInline, MessageInline ]
|
||||
add_inlines = ()
|
||||
inlines = (MessageReadOnlyInline, MessageInline)
|
||||
readonly_fields = (
|
||||
'display_summary', 'display_queue', 'display_owner', 'display_state',
|
||||
'display_priority'
|
||||
|
@ -286,10 +286,10 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin):
|
|||
|
||||
|
||||
class QueueAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'default', 'num_tickets']
|
||||
actions = [set_default_queue]
|
||||
inlines = [TicketInline]
|
||||
ordering = ['name']
|
||||
list_display = ('name', 'default', 'num_tickets')
|
||||
actions = (set_default_queue,)
|
||||
inlines = (TicketInline,)
|
||||
ordering = ('name',)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
|
|
|
@ -11,18 +11,8 @@ from .models import List
|
|||
|
||||
class MailmanBackend(ServiceController):
|
||||
"""
|
||||
Mailman backend based on `newlist`, it handles custom domains.
|
||||
LISTS_VIRTUAL_ALIAS_PATH = '%s'
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = '%s'
|
||||
LISTS_DEFAULT_DOMAIN = '%s'
|
||||
LISTS_MAILMAN_ROOT_DIR = '%s'
|
||||
Mailman 2 backend based on <tt>newlist</tt>, it handles custom domains.
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.LISTS_VIRTUAL_ALIAS_PATH,
|
||||
settings.LISTS_VIRTUAL_ALIAS_DOMAINS_PATH,
|
||||
settings.LISTS_DEFAULT_DOMAIN,
|
||||
settings.LISTS_MAILMAN_ROOT_DIR,
|
||||
)
|
||||
verbose_name = "Mailman"
|
||||
model = 'lists.List'
|
||||
addresses = [
|
||||
|
@ -37,6 +27,12 @@ class MailmanBackend(ServiceController):
|
|||
'-subscribe',
|
||||
'-unsubscribe'
|
||||
]
|
||||
doc_settings = (settings, (
|
||||
'LISTS_VIRTUAL_ALIAS_PATH',
|
||||
'LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'LISTS_DEFAULT_DOMAIN',
|
||||
'LISTS_MAILMAN_ROOT_DIR'
|
||||
))
|
||||
|
||||
def include_virtual_alias_domain(self, context):
|
||||
if context['address_domain']:
|
||||
|
@ -165,16 +161,15 @@ class MailmanBackend(ServiceController):
|
|||
|
||||
class MailmanTraffic(ServiceMonitor):
|
||||
"""
|
||||
Parses mailman log file looking for email size and multiples it by `list_members` count.
|
||||
LISTS_MAILMAN_POST_LOG_PATH = '%s'
|
||||
Parses mailman log file looking for email size and multiples it by <tt>list_members</tt> count.
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.LISTS_MAILMAN_POST_LOG_PATH,
|
||||
)
|
||||
model = 'lists.List'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Mailman traffic")
|
||||
script_executable = '/usr/bin/python'
|
||||
doc_settings = (settings,
|
||||
('LISTS_MAILMAN_POST_LOG_PATH',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
postlog = settings.LISTS_MAILMAN_POST_LOG_PATH
|
||||
|
@ -272,7 +267,7 @@ class MailmanTraffic(ServiceMonitor):
|
|||
|
||||
class MailmanSubscribers(ServiceMonitor):
|
||||
"""
|
||||
Monitors number of list subscribers via `list_members`
|
||||
Monitors number of list subscribers via <tt>list_members</tt>
|
||||
"""
|
||||
model = 'lists.List'
|
||||
verbose_name = _("Mailman subscribers")
|
||||
|
|
|
@ -3,6 +3,8 @@ from urllib.parse import parse_qs
|
|||
|
||||
from django import forms
|
||||
from django.contrib import admin
|
||||
from django.db.models import F, Value as V
|
||||
from django.db.models.functions import Concat
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||
|
@ -104,13 +106,15 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
|||
|
||||
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||
list_display = (
|
||||
'email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward',
|
||||
'display_email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward',
|
||||
)
|
||||
list_filter = (HasMailboxListFilter, HasForwardListFilter)
|
||||
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
|
||||
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
|
||||
inlines = [AutoresponseInline]
|
||||
search_fields = ('name', 'domain__name', 'forward', 'mailboxes__name', 'account__username')
|
||||
search_fields = (
|
||||
'name', 'domain__name', 'forward', 'mailboxes__name', 'account__username', 'computed_email'
|
||||
)
|
||||
readonly_fields = ('account_link', 'domain_link', 'email_link')
|
||||
filter_by_account_fields = ('domain', 'mailboxes')
|
||||
filter_horizontal = ['mailboxes']
|
||||
|
@ -119,6 +123,11 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
|
||||
domain_link = admin_link('domain', order='domain__name')
|
||||
|
||||
def display_email(self, address):
|
||||
return address.computed_email
|
||||
display_email.short_description = _("Email")
|
||||
display_email.admin_order_field = 'computed_email'
|
||||
|
||||
def email_link(self, address):
|
||||
link = self.domain_link(address)
|
||||
return "%s@%s" % (address.name, link)
|
||||
|
@ -154,6 +163,10 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
fields.remove('mailboxes')
|
||||
return fields
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super(AddressAdmin, self).get_queryset(request)
|
||||
return qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
|
||||
|
||||
|
||||
admin.site.register(Mailbox, MailboxAdmin)
|
||||
admin.site.register(Address, AddressAdmin)
|
||||
|
|
|
@ -185,21 +185,15 @@ class DovecotPostfixPasswdVirtualUserBackend(ServiceController):
|
|||
class PostfixAddressBackend(ServiceController):
|
||||
"""
|
||||
Addresses based on Postfix virtual alias domains.
|
||||
<tt>MAILBOXES_LOCAL_DOMAIN = '%s'
|
||||
MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = '%s'
|
||||
MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = '%s'</tt>
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.MAILBOXES_LOCAL_DOMAIN,
|
||||
settings.MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH,
|
||||
settings.MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH,
|
||||
)
|
||||
verbose_name = _("Postfix address")
|
||||
model = 'mailboxes.Address'
|
||||
related_models = (
|
||||
('mailboxes.Mailbox', 'addresses'),
|
||||
)
|
||||
|
||||
doc_settings = (settings,
|
||||
('MAILBOXES_LOCAL_DOMAIN', 'MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH', 'MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH',)
|
||||
)
|
||||
def include_virtual_alias_domain(self, context):
|
||||
if context['domain'] != context['local_domain']:
|
||||
self.append(textwrap.dedent("""
|
||||
|
@ -296,15 +290,13 @@ class DovecotMaildirDisk(ServiceMonitor):
|
|||
"""
|
||||
Maildir disk usage based on Dovecot maildirsize file
|
||||
http://wiki2.dovecot.org/Quota/Maildir
|
||||
|
||||
MAILBOXES_MAILDIRSIZE_PATH = '%s'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.MAILBOXES_MAILDIRSIZE_PATH,
|
||||
)
|
||||
model = 'mailboxes.Mailbox'
|
||||
resource = ServiceMonitor.DISK
|
||||
verbose_name = _("Dovecot Maildir size")
|
||||
doc_settings = (settings,
|
||||
('MAILBOXES_MAILDIRSIZE_PATH',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
super(DovecotMaildirDisk, self).prepare()
|
||||
|
@ -331,15 +323,14 @@ class PostfixMailscannerTraffic(ServiceMonitor):
|
|||
"""
|
||||
A high-performance log parser.
|
||||
Reads the mail.log file only once, for all users.
|
||||
MAILBOXES_MAIL_LOG_PATH = '%s'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.MAILBOXES_MAIL_LOG_PATH,
|
||||
)
|
||||
model = 'mailboxes.Mailbox'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Postfix-Mailscanner traffic")
|
||||
script_executable = '/usr/bin/python'
|
||||
doc_settings = (settings,
|
||||
('MAILBOXES_MAIL_LOG_PATH',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
mail_log = settings.MAILBOXES_MAIL_LOG_PATH
|
||||
|
|
|
@ -58,7 +58,7 @@ class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelA
|
|||
)
|
||||
list_filter = ('service__name', 'is_active')
|
||||
list_select_related = ('service', 'account')
|
||||
search_fields = ('identifier', 'description')
|
||||
search_fields = ('identifier', 'description', 'account__username')
|
||||
plugin_field = 'service'
|
||||
plugin = MiscServicePlugin
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
|
|||
default_route_match = 'True'
|
||||
# Force the backend manager to block in multiple backend executions executing them synchronously
|
||||
block = False
|
||||
format_docstring = ()
|
||||
doc_settings = None
|
||||
|
||||
def __str__(self):
|
||||
return type(self).__name__
|
||||
|
|
|
@ -11,30 +11,36 @@ from django.utils.translation import ungettext, ugettext_lazy as _
|
|||
def get_backends_help_text(backends):
|
||||
help_texts = {}
|
||||
for backend in backends:
|
||||
help_text = backend.__doc__ or ''
|
||||
context = {
|
||||
'model': backend.model,
|
||||
'related_models': str(backend.related_models),
|
||||
'script_executable': backend.script_executable,
|
||||
'script_method': str(backend.script_method),
|
||||
'function_method': str(backend.script_method),
|
||||
'actions': ', '.join(backend.actions),
|
||||
'script_method': '.'.join((backend.script_method.__module__, backend.script_method.__name__)),
|
||||
'function_method': '.'.join((backend.function_method.__module__, backend.function_method.__name__)),
|
||||
'actions': str(backend.actions),
|
||||
}
|
||||
help_text = textwrap.dedent("""
|
||||
- Model: '%(model)s'<br>
|
||||
- Related models: %(related_models)s<br>
|
||||
- Script executable: %(script_executable)s<br>
|
||||
- Script method: %(script_method)s<br>
|
||||
- Function method: %(function_method)s<br>
|
||||
- Actions: %(actions)s<br>"""
|
||||
help_text += textwrap.dedent("""
|
||||
- Model: <tt>'%(model)s'</tt>
|
||||
- Related models: <tt>%(related_models)s</tt>
|
||||
- Script executable: <tt>%(script_executable)s</tt>
|
||||
- Script method: <tt>%(script_method)s</tt>
|
||||
- Function method: <tt>%(function_method)s</tt>
|
||||
- Actions: <tt>%(actions)s</tt>
|
||||
"""
|
||||
) % context
|
||||
docstring = backend.__doc__
|
||||
if docstring:
|
||||
try:
|
||||
docstring = (docstring % backend.format_docstring).strip().splitlines()
|
||||
except TypeError as e:
|
||||
raise TypeError(str(backend) + str(e))
|
||||
help_text += '<br>' + '<br>'.join(docstring)
|
||||
help_texts[backend.get_name()] = help_text
|
||||
help_text = help_text.lstrip().splitlines()
|
||||
help_settings = ['']
|
||||
if backend.doc_settings:
|
||||
module, names = backend.doc_settings
|
||||
for name in names:
|
||||
value = getattr(module, name)
|
||||
if isinstance(value, str):
|
||||
help_settings.append("<tt>%s = '%s'</tt>" % (name, value))
|
||||
else:
|
||||
help_settings.append("<tt>%s = %s</tt>" % (name, str(value)))
|
||||
help_text += help_settings
|
||||
help_texts[backend.get_name()] = '<br>'.join(help_text)
|
||||
return help_texts
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ class BSCWBackend(ServiceController):
|
|||
model = 'saas.SaaS'
|
||||
default_route_match = "saas.service == 'bscw'"
|
||||
actions = ('save', 'delete', 'validate_creation')
|
||||
doc_settings = (settings,
|
||||
('SAAS_BSCW_BSADMIN_PATH',)
|
||||
)
|
||||
|
||||
def validate_creation(self, saas):
|
||||
context = self.get_context(saas)
|
||||
|
|
|
@ -8,8 +8,14 @@ from .. import settings
|
|||
|
||||
|
||||
class DokuWikiMuBackend(ServiceController):
|
||||
"""
|
||||
Creates a DokuWiki site on a DokuWiki multisite installation.
|
||||
"""
|
||||
verbose_name = _("DokuWiki multisite")
|
||||
model = 'webapps.WebApp'
|
||||
doc_settings = (settings,
|
||||
('SAAS_DOKUWIKI_TEMPLATE_PATH', 'SAAS_DOKUWIKI_FARM_PATH')
|
||||
)
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
|
@ -25,7 +31,7 @@ class DokuWikiMuBackend(ServiceController):
|
|||
def get_context(self, webapp):
|
||||
context = super(DokuWikiMuBackend, self).get_context(webapp)
|
||||
context.update({
|
||||
'template': settings.WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH,
|
||||
'app_path': os.path.join(settings.WEBAPPS_DOKUWIKIMU_FARM_PATH, webapp.name)
|
||||
'template': settings.SAAS_DOKUWIKI_TEMPLATE_PATH,
|
||||
'app_path': os.path.join(settings.SAAS_DOKUWIKI_FARM_PATH, webapp.name)
|
||||
})
|
||||
return replace(context, "'", '"')
|
||||
|
|
|
@ -9,8 +9,14 @@ from .. import settings
|
|||
|
||||
|
||||
class DrupalMuBackend(ServiceController):
|
||||
"""
|
||||
Creates a Drupal site on a Drupal multisite installation
|
||||
"""
|
||||
verbose_name = _("Drupal multisite")
|
||||
model = 'webapps.WebApp'
|
||||
doc_settings = (settings,
|
||||
('SAAS_DRUPAL_SITES_PATH',)
|
||||
)
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
|
@ -32,6 +38,6 @@ class DrupalMuBackend(ServiceController):
|
|||
|
||||
def get_context(self, webapp):
|
||||
context = super(DrupalMuBackend, self).get_context(webapp)
|
||||
context['drupal_path'] = settings.WEBAPPS_DRUPAL_SITES_PATH % context
|
||||
context['drupal_path'] = settings.SAAS_DRUPAL_SITES_PATH % context
|
||||
context['drupal_settings'] = os.path.join(context['drupal_path'], 'settings.php')
|
||||
return replace(context, "'", '"')
|
||||
|
|
|
@ -14,6 +14,9 @@ class GitLabSaaSBackend(ServiceController):
|
|||
default_route_match = "saas.service == 'gitlab'"
|
||||
block = True
|
||||
actions = ('save', 'delete', 'validate_creation')
|
||||
doc_settings = (settings,
|
||||
('SAAS_GITLAB_DOMAIN', 'SAAS_GITLAB_ROOT_PASSWORD'),
|
||||
)
|
||||
|
||||
def get_base_url(self):
|
||||
return 'https://%s/api/v3' % settings.SAAS_GITLAB_DOMAIN
|
||||
|
|
|
@ -7,6 +7,14 @@ from orchestra.contrib.orchestration import ServiceController
|
|||
|
||||
|
||||
class PhpListSaaSBackend(ServiceController):
|
||||
"""
|
||||
Creates a new phplist instance on a phpList multisite installation.
|
||||
The site is created by means of creating a new database per phpList site, but all sites share the same code.
|
||||
|
||||
<tt>// config/config.php
|
||||
$site = array_shift((explode(".",$_SERVER['HTTP_HOST'])));
|
||||
$database_name = "phplist_mu_{$site}";</tt>
|
||||
"""
|
||||
verbose_name = _("phpList SaaS")
|
||||
model = 'saas.SaaS'
|
||||
default_route_match = "saas.service == 'phplist'"
|
||||
|
|
|
@ -9,16 +9,22 @@ from .. import settings
|
|||
|
||||
|
||||
class WordpressMuBackend(ServiceController):
|
||||
"""
|
||||
Creates a wordpress site on a WordPress MultiSite installation.
|
||||
"""
|
||||
verbose_name = _("Wordpress multisite")
|
||||
model = 'webapps.WebApp'
|
||||
default_route_match = "webapp.type == 'wordpress-mu'"
|
||||
doc_settings = (settings,
|
||||
('SAAS_WORDPRESS_ADMIN_PASSWORD', 'SAAS_WORDPRESS_BASE_URL')
|
||||
)
|
||||
|
||||
def login(self, session):
|
||||
base_url = self.get_base_url()
|
||||
login_url = base_url + '/wp-login.php'
|
||||
login_data = {
|
||||
'log': 'admin',
|
||||
'pwd': settings.WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD,
|
||||
'pwd': settings.SAAS_WORDPRESS_ADMIN_PASSWORD,
|
||||
'redirect_to': '/wp-admin/'
|
||||
}
|
||||
response = session.post(login_url, data=login_data)
|
||||
|
@ -26,7 +32,7 @@ class WordpressMuBackend(ServiceController):
|
|||
raise IOError("Failure login to remote application")
|
||||
|
||||
def get_base_url(self):
|
||||
base_url = settings.WEBAPPS_WORDPRESSMU_BASE_URL
|
||||
base_url = settings.SAAS_WORDPRESS_BASE_URL
|
||||
return base_url.rstrip('/')
|
||||
|
||||
def validate_response(self, response):
|
||||
|
|
|
@ -11,18 +11,14 @@ from . import settings
|
|||
|
||||
class UNIXUserBackend(ServiceController):
|
||||
"""
|
||||
Basic UNIX system user/group support based on `useradd`, `usermod`, `userdel` and `groupdel`.
|
||||
<tt>SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = '%s'
|
||||
SYSTEMUSERS_MOVE_ON_DELETE_PATH = '%s'</tt>
|
||||
Basic UNIX system user/group support based on <tt>useradd</tt>, <tt>usermod</tt>, <tt>userdel</tt> and <tt>groupdel</tt>.
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.SYSTEMUSERS_DEFAULT_GROUP_MEMBERS,
|
||||
settings.SYSTEMUSERS_MOVE_ON_DELETE_PATH,
|
||||
)
|
||||
|
||||
verbose_name = _("UNIX user")
|
||||
model = 'systemusers.SystemUser'
|
||||
actions = ('save', 'delete', 'grant_permission')
|
||||
doc_settings = (settings,
|
||||
('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', 'SYSTEMUSERS_MOVE_ON_DELETE_PATH')
|
||||
)
|
||||
|
||||
def save(self, user):
|
||||
context = self.get_context(user)
|
||||
|
@ -95,7 +91,7 @@ class UNIXUserBackend(ServiceController):
|
|||
|
||||
class UNIXUserDisk(ServiceMonitor):
|
||||
"""
|
||||
`du -bs <home>`
|
||||
<tt>du -bs <home></tt>
|
||||
"""
|
||||
model = 'systemusers.SystemUser'
|
||||
resource = ServiceMonitor.DISK
|
||||
|
@ -123,17 +119,15 @@ class UNIXUserDisk(ServiceMonitor):
|
|||
|
||||
class Exim4Traffic(ServiceMonitor):
|
||||
"""
|
||||
Exim4 mainlog parser for mails sent on the webserver by system users (e.g. via PHP mail())
|
||||
SYSTEMUSERS_MAIL_LOG_PATH = '%s'
|
||||
Exim4 mainlog parser for mails sent on the webserver by system users (e.g. via PHP <tt>mail()</tt>)
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.SYSTEMUSERS_MAIL_LOG_PATH,
|
||||
)
|
||||
|
||||
model = 'systemusers.SystemUser'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Exim4 traffic")
|
||||
script_executable = '/usr/bin/python'
|
||||
doc_settings = (settings,
|
||||
('SYSTEMUSERS_MAIL_LOG_PATH',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
mainlog = settings.SYSTEMUSERS_MAIL_LOG_PATH
|
||||
|
@ -211,15 +205,14 @@ class Exim4Traffic(ServiceMonitor):
|
|||
class VsFTPdTraffic(ServiceMonitor):
|
||||
"""
|
||||
vsFTPd log parser.
|
||||
SYSTEMUSERS_FTP_LOG_PATH = '%s'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.SYSTEMUSERS_FTP_LOG_PATH,
|
||||
)
|
||||
model = 'systemusers.SystemUser'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _('VsFTPd traffic')
|
||||
script_executable = '/usr/bin/python'
|
||||
doc_settings = (settings,
|
||||
('SYSTEMUSERS_FTP_LOG_PATH',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
vsftplog = settings.SYSTEMUSERS_FTP_LOG_PATH
|
||||
|
|
|
@ -12,6 +12,9 @@ class WebAppServiceMixin(object):
|
|||
('webapps.WebAppOption', 'webapp'),
|
||||
)
|
||||
directive = None
|
||||
doc_settings = (settings,
|
||||
('WEBAPPS_UNDER_CONSTRUCTION_PATH', 'WEBAPPS_MOVE_ON_DELETE_PATH',)
|
||||
)
|
||||
|
||||
def create_webapp_dir(self, context):
|
||||
self.append(textwrap.dedent("""\
|
||||
|
@ -45,7 +48,7 @@ class WebAppServiceMixin(object):
|
|||
'type': webapp.type,
|
||||
'app_path': webapp.get_path(),
|
||||
'banner': self.get_banner(),
|
||||
'under_construction_path': settings.settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
|
||||
'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
|
||||
'is_mounted': webapp.content_set.exists(),
|
||||
}
|
||||
context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context
|
||||
|
|
|
@ -14,29 +14,21 @@ class PHPBackend(WebAppServiceMixin, ServiceController):
|
|||
"""
|
||||
PHP support for apache-mod-fcgid and php-fpm.
|
||||
It handles switching between these two PHP process management systemes.
|
||||
WEBAPPS_MERGE_PHP_WEBAPPS = %s
|
||||
WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = %s
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = '%s'
|
||||
WEBAPPS_PHP_CGI_RC_DIR = '%s'
|
||||
WEBAPPS_PHP_CGI_INI_SCAN_DIR = '%s'
|
||||
WEBAPPS_FCGID_CMD_OPTIONS_PATH = '%s'
|
||||
WEBAPPS_PHPFPM_POOL_PATH = '%s'
|
||||
WEBAPPS_PHP_MAX_REQUESTS = %s
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.WEBAPPS_MERGE_PHP_WEBAPPS,
|
||||
settings.WEBAPPS_FPM_DEFAULT_MAX_CHILDREN,
|
||||
settings.WEBAPPS_PHP_CGI_BINARY_PATH,
|
||||
settings.WEBAPPS_PHP_CGI_RC_DIR,
|
||||
settings.WEBAPPS_PHP_CGI_INI_SCAN_DIR,
|
||||
settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH,
|
||||
settings.WEBAPPS_PHPFPM_POOL_PATH,
|
||||
settings.WEBAPPS_PHP_MAX_REQUESTS,
|
||||
)
|
||||
MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
|
||||
|
||||
verbose_name = _("PHP FPM/FCGID")
|
||||
default_route_match = "webapp.type.endswith('php')"
|
||||
MERGE = settings.WEBAPPS_MERGE_PHP_WEBAPPS
|
||||
doc_settings = (settings, (
|
||||
'WEBAPPS_MERGE_PHP_WEBAPPS',
|
||||
'WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
|
||||
'WEBAPPS_PHP_CGI_BINARY_PATH',
|
||||
'WEBAPPS_PHP_CGI_RC_DIR',
|
||||
'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
|
||||
'WEBAPPS_FCGID_CMD_OPTIONS_PATH',
|
||||
'WEBAPPS_PHPFPM_POOL_PATH',
|
||||
'WEBAPPS_PHP_MAX_REQUESTS',
|
||||
))
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
|
|
|
@ -12,22 +12,16 @@ from .. import settings
|
|||
|
||||
class uWSGIPythonBackend(WebAppServiceMixin, ServiceController):
|
||||
"""
|
||||
Emperor mode
|
||||
http://uwsgi-docs.readthedocs.org/en/latest/Emperor.html
|
||||
WEBAPPS_UWSGI_BASE_DIR = '%s'
|
||||
WEBAPPS_PYTHON_MAX_REQUESTS = %s
|
||||
WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS = %s
|
||||
WEBAPPS_PYTHON_DEFAULT_TIMEOUT = %s
|
||||
<a href="http://uwsgi-docs.readthedocs.org/en/latest/Emperor.html">Emperor mode</a>
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.WEBAPPS_UWSGI_BASE_DIR,
|
||||
settings.WEBAPPS_PYTHON_MAX_REQUESTS,
|
||||
settings.WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS,
|
||||
settings.WEBAPPS_PYTHON_DEFAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
verbose_name = _("Python uWSGI")
|
||||
default_route_match = "webapp.type.endswith('python')"
|
||||
doc_settings = (settings, (
|
||||
'WEBAPPS_UWSGI_BASE_DIR',
|
||||
'WEBAPPS_PYTHON_MAX_REQUESTS',
|
||||
'WEBAPPS_PYTHON_DEFAULT_MAX_WORKERS',
|
||||
'WEBAPPS_PYTHON_DEFAULT_TIMEOUT',
|
||||
))
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
|
|
|
@ -11,7 +11,6 @@ class SymbolicLinkBackend(PHPBackend, ServiceController):
|
|||
"""
|
||||
Same as PHPBackend but allows you to have the webapps on a directory diferent than the webapps dir.
|
||||
"""
|
||||
format_docstring = ()
|
||||
verbose_name = _("Symbolic link webapp")
|
||||
model = 'webapps.WebApp'
|
||||
default_route_match = "webapp.type == 'symbolic-link'"
|
||||
|
|
|
@ -14,15 +14,14 @@ class WordPressBackend(WebAppServiceMixin, ServiceController):
|
|||
"""
|
||||
Installs the latest version of WordPress available on www.wordpress.org
|
||||
It fully configures the wp-config.php (keys included) and sets up the database with initial admin password.
|
||||
WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = '%s'
|
||||
"""
|
||||
format_docstring = (
|
||||
settings.WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST,
|
||||
)
|
||||
verbose_name = _("Wordpress")
|
||||
model = 'webapps.WebApp'
|
||||
default_route_match = "webapp.type == 'wordpress-php'"
|
||||
script_executable = '/usr/bin/php'
|
||||
doc_settings = (settings,
|
||||
('WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
self.append(textwrap.dedent("""\
|
||||
|
|
|
@ -308,7 +308,7 @@ class PHPSuhosinExecutorIncludeWhitelist(PHPAppOption):
|
|||
|
||||
class PHPUploadMaxFileSize(PHPAppOption):
|
||||
name = 'upload_max_filesize'
|
||||
verbose_name = _("Upload max filezise")
|
||||
verbose_name = _("Upload max filesize")
|
||||
help_text = _("Value between 0M and 999M.")
|
||||
regex = r'^[0-9]{1,3}M$'
|
||||
|
||||
|
|
|
@ -13,6 +13,11 @@ from ..utils import normurlpath
|
|||
|
||||
|
||||
class Apache2Backend(ServiceController):
|
||||
"""
|
||||
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>
|
||||
"""
|
||||
HTTP_PORT = 80
|
||||
HTTPS_PORT = 443
|
||||
|
||||
|
@ -22,6 +27,15 @@ class Apache2Backend(ServiceController):
|
|||
('webapps.WebApp', 'website_set'),
|
||||
)
|
||||
verbose_name = _("Apache 2")
|
||||
doc_settings = (settings, (
|
||||
'WEBSITES_VHOST_EXTRA_DIRECTIVES',
|
||||
'WEBSITES_DEFAULT_SSL_CERT',
|
||||
'WEBSITES_DEFAULT_SSL_KEY',
|
||||
'WEBSITES_DEFAULT_SSL_CA',
|
||||
'WEBSITES_BASE_APACHE_CONF',
|
||||
'WEBSITES_DEFAULT_IPS',
|
||||
'WEBSITES_SAAS_DIRECTIVES',
|
||||
))
|
||||
|
||||
def render_virtual_host(self, site, context, ssl=False):
|
||||
context['port'] = self.HTTPS_PORT if ssl else self.HTTP_PORT
|
||||
|
@ -362,12 +376,14 @@ class Apache2Backend(ServiceController):
|
|||
class Apache2Traffic(ServiceMonitor):
|
||||
"""
|
||||
Parses apache logs,
|
||||
looking for the size of each request on the last word of the log line
|
||||
looking for the size of each request on the last word of the log line.
|
||||
"""
|
||||
model = 'websites.Website'
|
||||
resource = ServiceMonitor.TRAFFIC
|
||||
verbose_name = _("Apache 2 Traffic")
|
||||
|
||||
doc_settings = (settings,
|
||||
('WEBSITES_TRAFFIC_IGNORE_HOSTS',)
|
||||
)
|
||||
def prepare(self):
|
||||
super(Apache2Traffic, self).prepare()
|
||||
ignore_hosts = '\\|'.join(settings.WEBSITES_TRAFFIC_IGNORE_HOSTS)
|
||||
|
|
|
@ -15,6 +15,9 @@ class WebalizerBackend(ServiceController):
|
|||
verbose_name = _("Webalizer Content")
|
||||
model = 'websites.Content'
|
||||
default_route_match = "content.webapp.type == 'webalizer'"
|
||||
doc_settings = (settings,
|
||||
('WEBSITES_WEBALIZER_PATH',)
|
||||
)
|
||||
|
||||
def save(self, content):
|
||||
context = self.get_context(content)
|
||||
|
|
Loading…
Reference in New Issue