Added dynamic help text to backend route

This commit is contained in:
Marc Aymerich 2014-11-11 12:05:47 +00:00
parent 2eb2fb5a5c
commit d422e109e9
10 changed files with 99 additions and 62 deletions

15
TODO.md
View file

@ -163,7 +163,7 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* Names: lower andupper case allow or disallow ? webapps/account.username etc
* Split plans into a separate app (plans and rates / services ) ?
* Split plans into a separate app (plans and rates / services ) ?
* sync() ServiceController method that synchronizes orchestra and servers (delete or import)
@ -200,3 +200,16 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* User [Group] webapp/website option (validation) which overrides default mainsystemuser
* validate systemuser.home
* Create plugin app
* Create options widget
* generic options fpm/fcgid/uwsgi webapps (num procs, idle io timeout)
* webapp backend option compatibility check?
* Route help text with model name when selecting backend
* Service instance name when selecting content_type
* Address.forward mailbbox validate not available on mailboxes

View file

@ -203,10 +203,11 @@ class SelectPluginAdminMixin(object):
""" Redirects to select account view if required """
if request.user.is_superuser:
plugin_value = request.GET.get(self.plugin_field) or request.POST.get(self.plugin_field)
if plugin_value or self.plugin.get_plugins() == 1:
if plugin_value or len(self.plugin.get_plugins()) == 1:
self.plugin_value = plugin_value
if not plugin_value:
self.plugin_value = self.plugin.get_plugins()[0]
self.plugin_value = self.plugin.get_plugins()[0].get_plugin_name()
b = self.plugin_value
context = {
'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value),
}

View file

@ -4,6 +4,7 @@ from functools import wraps
from django.conf import settings
from django.contrib import admin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db import models
from django.shortcuts import redirect
@ -99,7 +100,10 @@ def admin_link(*args, **kwargs):
if kwargs['field'] in ['id', 'pk', '__unicode__']:
obj = instance
else:
obj = get_field_value(instance, kwargs['field'])
try:
obj = get_field_value(instance, kwargs['field'])
except ObjectDoesNotExist:
return '---'
if not getattr(obj, 'pk', None):
return '---'
url = change_url(obj)

View file

@ -20,13 +20,12 @@ class BillContact(models.Model):
account = models.OneToOneField('accounts.Account', verbose_name=_("account"),
related_name='billcontact')
name = models.CharField(_("name"), max_length=256, blank=True,
help_text=_("Account full name will be used when not provided"))
help_text=_("Account full name will be used when left blank."))
address = models.TextField(_("address"))
city = models.CharField(_("city"), max_length=128,
default=settings.BILLS_CONTACT_DEFAULT_CITY)
zipcode = models.CharField(_("zip code"), max_length=10,
validators=[RegexValidator(r'^[0-9A-Z]{3,10}$',
_("Enter a valid zipcode."), 'invalid')])
validators=[RegexValidator(r'^[0-9A-Z]{3,10}$', _("Enter a valid zipcode."))])
country = models.CharField(_("country"), max_length=20,
choices=settings.BILLS_CONTACT_COUNTRIES,
default=settings.BILLS_CONTACT_DEFAULT_COUNTRY)

View file

@ -1,10 +1,13 @@
from django import forms
from django.contrib import admin
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.admin.html import monospace_format
from orchestra.admin.utils import admin_link, admin_date, admin_colored
from .backends import ServiceBackend
from .models import Server, Route, BackendLog, BackendOperation
@ -27,6 +30,11 @@ class RouteAdmin(admin.ModelAdmin):
list_editable = ['backend', 'host', 'match', 'is_active']
list_filter = ['host', 'is_active', 'backend']
BACKEND_HELP_TEXT = {
backend: "This backend operates over '%s'" % ServiceBackend.get_backend(backend).model
for backend, __ in ServiceBackend.get_plugin_choices()
}
def display_model(self, route):
try:
return escape(route.backend_class().model)
@ -42,6 +50,19 @@ class RouteAdmin(admin.ModelAdmin):
return "<span style='color: red;'>NOT AVAILABLE</span>"
display_actions.short_description = _("actions")
display_actions.allow_tags = True
def formfield_for_dbfield(self, db_field, **kwargs):
""" Provides dynamic help text on backend form field """
if db_field.name == 'backend':
kwargs['widget'] = DynamicHelpTextSelect('this.id', self.BACKEND_HELP_TEXT)
return super(RouteAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def get_form(self, request, obj=None, **kwargs):
""" Include dynamic help text for existing objects """
form = super(RouteAdmin, self).get_form(request, obj=obj, **kwargs)
if obj:
form.base_fields['backend'].help_text = self.BACKEND_HELP_TEXT[obj.backend]
return form
class BackendOperationInline(admin.TabularInline):

View file

@ -6,6 +6,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import change_url
from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect
from . import settings
from .models import WebApp, WebAppOption
@ -26,21 +27,13 @@ class WebAppOptionInline(admin.TabularInline):
}
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
if db_field.name == 'value':
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
if db_field.name == 'name':
# Help text based on select widget
kwargs['widget'] = forms.Select(attrs={
'onChange': """
siteoptions = %s;
valueelement = $("#"+this.id.replace("name", "value"));
valueelement.parent().find('p').remove();
valueelement.parent().append(
"<p class='help'>" + siteoptions[this.options[this.selectedIndex].value] + "</p>"
);
""" % str(self.OPTIONS_HELP_TEXT),
})
kwargs['widget'] = DynamicHelpTextSelect(
'this.id.replace("name", "value")', self.OPTIONS_HELP_TEXT
)
return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs)
@ -67,7 +60,7 @@ class WebAppAdmin(AccountAdminMixin, ExtendedModelAdmin):
name = "%s on %s" % (website.name, content.path)
websites.append('<a href="%s">%s</a>' % (url, name))
add_url = reverse('admin:websites_website_add')
# TODO support for preselecting related we app on website
# TODO support for preselecting related web app on website
add_url += '?account=%s' % webapp.account_id
plus = '<strong style="color:green; font-size:12px">+</strong>'
websites.append('<a href="%s">%s%s</a>' % (add_url, plus, ugettext("Add website")))
@ -80,7 +73,7 @@ class WebAppAdmin(AccountAdminMixin, ExtendedModelAdmin):
if db_field.name == 'type':
# Help text based on select widget
kwargs['widget'] = forms.Select(attrs={
'onChange': """
'onClick': """
siteoptions = %s;
valueelement = $("#"+this.id);
valueelement.parent().find('p').remove();

View file

@ -29,20 +29,20 @@ WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH',
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', {
'php5.5': {
'verbose_name': "PHP 5.5",
'verbose_name': "PHP 5.5 fpm",
# 'fpm', ('unix:/var/run/%(user)s-%(app_name)s.sock|fcgi://127.0.0.1%(app_path)s',),
'directive': ('fpm', 'fcgi://{}%(app_path)s'.format(WEBAPPS_FPM_LISTEN)),
'help_text': _("This creates a PHP5.5 application under ~/webapps/&lt;app_name&gt;<br>"
"PHP-FPM will be used to execute PHP files.")
},
'php5.2': {
'verbose_name': "PHP 5.2",
'verbose_name': "PHP 5.2 fcgi",
'directive': ('fcgi', WEBAPPS_FCGID_PATH),
'help_text': _("This creates a PHP5.2 application under ~/webapps/&lt;app_name&gt;<br>"
"Apache-mod-fcgid will be used to execute PHP files.")
},
'php4': {
'verbose_name': "PHP 4",
'verbose_name': "PHP 4 fcgi",
'directive': ('fcgi', WEBAPPS_FCGID_PATH,),
'help_text': _("This creates a PHP4 application under ~/webapps/&lt;app_name&gt;<br>"
"Apache-mod-fcgid will be used to execute PHP files.")
@ -104,8 +104,24 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTIO
WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', {
# { name: ( verbose_name, [help_text], validation_regex ) }
# Processes
'timeout': (
_("Process timeout"),
_("Maximum time in seconds allowed for a request to complete (a number between 0 and 999)."),
# FCGID FcgidIOTimeout
# FPM pm.request_terminate_timeout
# PHP max_execution_time ini
r'^[0-9]{1,3}$',
),
'processes': (
_("Number of processes"),
_("Maximum number of children that can be alive at the same time (a number between 0 and 9)."),
# FCGID MaxProcesses
# FPM pm.max_children
r'^[0-9]$',
),
# PHP
'enabled_functions': (
'php-enabled_functions': (
_("PHP - Enabled functions"),
' '.join(WEBAPPS_PHP_DISABLED_FUNCTIONS),
r'^[\w\.,-]+$'
@ -249,30 +265,4 @@ WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', {
_("PHP - zend_extension"),
r'^[^ ]+$'
),
# FCGID
'FcgidIdleTimeout': (
_("FCGI - Idle timeout"),
_("Number between 0 and 999."),
r'^[0-9]{1,3}$'
),
'FcgidBusyTimeout': (
_("FCGI - Busy timeout"),
_("Number between 0 and 999."),
r'^[0-9]{1,3}$'
),
'FcgidConnectTimeout': (
_("FCGI - Connection timeout"),
_("Number of seconds between 0 and 999."),
r'^[0-9]{1,3}$'
),
'FcgidIOTimeout': (
_("FCGI - IO timeout"),
_("Number of seconds between 0 and 999."),
r'^[0-9]{1,3}$'
),
'FcgidProcessLifeTime': (
_("FCGI - IO timeout"),
_("Numbe of secondsr between 0 and 9999."),
r'^[0-9]{1,4}$'
),
})

View file

@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, change_url
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect
from . import settings
from .models import Content, Website, WebsiteOption
@ -25,21 +26,13 @@ class WebsiteOptionInline(admin.TabularInline):
# }
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
if db_field.name == 'value':
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
if db_field.name == 'name':
# Help text based on select widget
kwargs['widget'] = forms.Select(attrs={
'onChange': """
siteoptions = %s;
valueelement = $("#"+this.id.replace("name", "value"));
valueelement.parent().find('p').remove();
valueelement.parent().append(
"<p class='help'>" + siteoptions[this.options[this.selectedIndex].value] + "</p>"
);
""" % str(self.OPTIONS_HELP_TEXT),
})
kwargs['widget'] = DynamicHelpTextSelect(
'this.id.replace("name", "value")', self.OPTIONS_HELP_TEXT
)
return super(WebsiteOptionInline, self).formfield_for_dbfield(db_field, **kwargs)

View file

@ -1,4 +1,5 @@
import re
import textwrap
from django import forms
from django.utils.safestring import mark_safe
@ -63,3 +64,25 @@ def paddingCheckboxSelectMultiple(padding):
return mark_safe(value)
widget.render = render
return widget
class DynamicHelpTextSelect(forms.Select):
def __init__(self, target, help_text, *args, **kwargs):
help_text = self.get_dynamic_help_text(target, help_text)
attrs = {
'onClick': help_text,
'onChange': help_text,
}
attrs.update(kwargs.get('attrs', {}))
kwargs['attrs'] = attrs
super(DynamicHelpTextSelect, self).__init__(*args, **kwargs)
def get_dynamic_help_text(self, target, help_text):
return textwrap.dedent("""\
siteoptions = {help_text};
valueelement = $("#" + {target});
valueelement.parent().find('p').remove();
valueelement.parent().append(
"<p class='help'>" + siteoptions[this.options[this.selectedIndex].value] + "</p>"
);""".format(target=target, help_text=str(help_text))
)

View file

@ -56,6 +56,6 @@ def dict_setting_to_choices(choices):
def tuple_setting_to_choices(choices):
return sorted(
[ (name, opt[0]) for name,opt in choices.iteritems() ],
tuple((name, opt[0]) for name, opt in choices.iteritems()),
key=lambda e: e[0]
)