Merge all php webapps into one
This commit is contained in:
parent
e80f921601
commit
fd119f434d
9
TODO.md
9
TODO.md
|
@ -199,4 +199,11 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
|
|||
* Orchestra global search box on the header, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields
|
||||
|
||||
|
||||
* contain error on plugin missing key (plugin dissabled)
|
||||
* contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently
|
||||
|
||||
* contact.alternative_phone on a phone.tooltip, email:to
|
||||
|
||||
|
||||
* better validate options and directives (url locations, filesystem paths, etc..)
|
||||
* filter php deprecated options out based on version
|
||||
* Todo get php_version for fcgid wrapper
|
||||
|
|
|
@ -56,13 +56,13 @@ class MailSystemUserBackend(ServiceController):
|
|||
|
||||
def delete(self, mailbox):
|
||||
context = self.get_context(mailbox)
|
||||
self.append('mv %(home)s %(home)s.deleted' % context)
|
||||
self.append(textwrap.dedent("""
|
||||
{ sleep 2 && killall -u %(user)s -s KILL; } &
|
||||
killall -u %(user)s || true
|
||||
userdel %(user)s || true
|
||||
groupdel %(user)s || true""") % context
|
||||
)
|
||||
self.append('mv %(home)s %(home)s.deleted' % context)
|
||||
|
||||
def get_context(self, mailbox):
|
||||
context = {
|
||||
|
|
|
@ -25,8 +25,7 @@ STATE_COLORS = {
|
|||
|
||||
class RouteAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'id', 'backend', 'host', 'match', 'display_model', 'display_actions',
|
||||
'is_active'
|
||||
'backend', 'host', 'match', 'display_model', 'display_actions', 'is_active'
|
||||
]
|
||||
list_editable = ['host', 'match', 'is_active']
|
||||
list_filter = ['host', 'is_active', 'backend']
|
||||
|
@ -65,7 +64,7 @@ class RouteAdmin(admin.ModelAdmin):
|
|||
""" 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]
|
||||
form.base_fields['backend'].help_text = self.BACKEND_HELP_TEXT.get(obj.backend, '')
|
||||
return form
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from orchestra.utils.python import import_class
|
|||
from . import settings
|
||||
from .helpers import send_report
|
||||
from .models import BackendLog
|
||||
from .signals import pre_action, post_action
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -65,8 +66,17 @@ def execute(operations, async=False):
|
|||
else:
|
||||
scripts[key][1].append(operation)
|
||||
# Get and call backend action method
|
||||
method = getattr(scripts[key][0], operation.action)
|
||||
backend = scripts[key][0]
|
||||
method = getattr(backend, operation.action)
|
||||
kwargs = {
|
||||
'sender': backend.__class__,
|
||||
'backend': backend,
|
||||
'instance': operation.instance,
|
||||
'action': operation.action,
|
||||
}
|
||||
pre_action.send(**kwargs)
|
||||
method(operation.instance)
|
||||
post_action.send(**kwargs)
|
||||
# Execute scripts on each server
|
||||
threads = []
|
||||
executions = []
|
||||
|
|
|
@ -7,9 +7,9 @@ from django.http.response import HttpResponseServerError
|
|||
|
||||
from orchestra.utils.python import OrderedSet
|
||||
|
||||
from .manager import router
|
||||
from .backends import ServiceBackend
|
||||
from .helpers import message_user
|
||||
from .manager import router
|
||||
from .models import BackendLog
|
||||
from .models import BackendOperation as Operation
|
||||
|
||||
|
@ -103,7 +103,7 @@ class OperationsMiddleware(object):
|
|||
pass
|
||||
else:
|
||||
update_fields = kwargs.get('update_fields', None)
|
||||
if update_fields:
|
||||
if update_fields is not None:
|
||||
# "update_fileds=[]" is a convention for explicitly executing backend
|
||||
# i.e. account.disable()
|
||||
if update_fields != []:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import django.dispatch
|
||||
|
||||
|
||||
pre_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action'])
|
||||
|
||||
post_action = django.dispatch.Signal(providing_args=['backend', 'instance', 'action'])
|
|
@ -53,6 +53,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
|||
inlines = [WebAppOptionInline]
|
||||
readonly_fields = ('account_link',)
|
||||
change_readonly_fields = ('name', 'type')
|
||||
search_fuelds = ('name', 'account__username')
|
||||
list_prefetch_related = ('content_set__website',)
|
||||
plugin = AppType
|
||||
plugin_field = 'type'
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
import os
|
||||
import textwrap
|
||||
|
||||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.apps.orchestration import ServiceController
|
||||
|
||||
from . import WebAppServiceMixin
|
||||
from .. import settings
|
||||
|
||||
|
||||
class PHPBackend(WebAppServiceMixin, ServiceController):
|
||||
verbose_name = _("PHP FPM/FCGID")
|
||||
default_route_match = "webapp.type == 'php'"
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
if webapp.type_instance.is_fpm:
|
||||
self.save_fpm(webapp, context)
|
||||
self.delete_fcgid(webapp, context)
|
||||
elif webapp.type_instance.is_fcgid:
|
||||
self.save_fcgid(webapp, context)
|
||||
self.delete_fpm(webapp, context)
|
||||
|
||||
def save_fpm(self, webapp, context):
|
||||
self.create_webapp_dir(context)
|
||||
self.set_under_construction(context)
|
||||
self.append(textwrap.dedent("""\
|
||||
{
|
||||
echo -e '%(fpm_config)s' | diff -N -I'^\s*;;' %(fpm_path)s -
|
||||
} || {
|
||||
echo -e '%(fpm_config)s' > %(fpm_path)s
|
||||
UPDATEDFPM=1
|
||||
}""") % context
|
||||
)
|
||||
|
||||
def save_fcgid(self, webapp, context):
|
||||
self.create_webapp_dir(context)
|
||||
self.set_under_construction(context)
|
||||
self.append("mkdir -p %(wrapper_dir)s" % context)
|
||||
self.append(textwrap.dedent("""\
|
||||
{
|
||||
echo -e '%(wrapper)s' | diff -N -I'^\s*#' %(wrapper_path)s -
|
||||
} || {
|
||||
echo -e '%(wrapper)s' > %(wrapper_path)s; UPDATED_APACHE=1
|
||||
}""") % context
|
||||
)
|
||||
self.append("chmod +x %(wrapper_path)s" % context)
|
||||
self.append("chown -R %(user)s:%(group)s %(wrapper_dir)s" % context)
|
||||
if context['cmd_options']:
|
||||
self.append(textwrap.dedent("""
|
||||
{
|
||||
echo -e '%(cmd_options)s' | diff -N -I'^\s*#' %(cmd_options_path)s -
|
||||
} || {
|
||||
echo -e '%(cmd_options)s' > %(cmd_options_path)s; UPDATED_APACHE=1
|
||||
}""" ) % context
|
||||
)
|
||||
else:
|
||||
self.append("rm -f %(cmd_options_path)s" % context)
|
||||
|
||||
def delete(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
if webapp.type_instance.is_fpm:
|
||||
self.delete_fpm(webapp, context)
|
||||
elif webapp.type_instance.is_fcgid:
|
||||
self.delete_fcgid(webapp, context)
|
||||
self.delete_webapp_dir(context)
|
||||
|
||||
def delete_fpm(self, webapp, context):
|
||||
self.append("rm -f %(fpm_path)s" % context)
|
||||
|
||||
def delete_fcgid(self, webapp, context):
|
||||
self.append("rm -f %(wrapper_path)s" % context)
|
||||
self.append("rm -f %(cmd_options_path)s" % context)
|
||||
|
||||
def commit(self):
|
||||
if self.content:
|
||||
self.append(textwrap.dedent("""
|
||||
if [[ $UPDATEDFPM == 1 ]]; then
|
||||
service php5-fpm reload
|
||||
service php5-fpm start
|
||||
fi""")
|
||||
)
|
||||
self.append(textwrap.dedent("""\
|
||||
if [[ $UPDATED_APACHE == 1 ]]; then
|
||||
service apache2 reload
|
||||
fi""")
|
||||
)
|
||||
|
||||
def get_fpm_config(self, webapp, context):
|
||||
context.update({
|
||||
'init_vars': webapp.type_instance.get_php_init_vars(),
|
||||
'max_children': webapp.get_options().get('processes', False),
|
||||
'request_terminate_timeout': webapp.get_options().get('timeout', False),
|
||||
})
|
||||
context['fpm_listen'] = webapp.type_instance.FPM_LISTEN % context
|
||||
fpm_config = Template(textwrap.dedent("""\
|
||||
;; {{ banner }}
|
||||
[{{ user }}]
|
||||
user = {{ user }}
|
||||
group = {{ group }}
|
||||
|
||||
listen = {{ fpm_listen | safe }}
|
||||
listen.owner = {{ user }}
|
||||
listen.group = {{ group }}
|
||||
pm = ondemand
|
||||
{% if max_children %}pm.max_children = {{ max_children }}{% endif %}
|
||||
{% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
|
||||
{% for name, value in init_vars.iteritems %}
|
||||
php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
|
||||
"""
|
||||
))
|
||||
return fpm_config.render(Context(context))
|
||||
|
||||
def get_fcgid_wrapper(self, webapp, context):
|
||||
opt = webapp.type_instance
|
||||
# Format PHP init vars
|
||||
init_vars = opt.get_php_init_vars()
|
||||
if init_vars:
|
||||
init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.iteritems() ]
|
||||
init_vars = ', '.join(init_vars)
|
||||
context.update({
|
||||
'php_binary': os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context),
|
||||
'php_rc': os.path.normpath(settings.WEBAPPS_PHP_CGI_RC_DIR % context),
|
||||
'php_ini_scan': os.path.normpath(settings.WEBAPPS_PHP_CGI_INI_SCAN_DIR % context),
|
||||
'php_init_vars': init_vars,
|
||||
})
|
||||
return textwrap.dedent("""\
|
||||
#!/bin/sh
|
||||
# %(banner)s
|
||||
export PHPRC=%(php_rc)s
|
||||
export PHP_INI_SCAN_DIR=%(php_ini_scan)s
|
||||
exec %(php_binary)s %(php_init_vars)s""") % context
|
||||
|
||||
def get_fcgid_cmd_options(self, webapp, context):
|
||||
maps = {
|
||||
'MaxProcesses': webapp.get_options().get('processes', None),
|
||||
'IOTimeout': webapp.get_options().get('timeout', None),
|
||||
}
|
||||
cmd_options = []
|
||||
for directive, value in maps.iteritems():
|
||||
if value:
|
||||
cmd_options.append("%s %s" % (directive, value))
|
||||
if cmd_options:
|
||||
head = '# %(banner)s\nFcgidCmdOptions %(wrapper_path)s' % context
|
||||
cmd_options.insert(0, head)
|
||||
return ' \\\n '.join(cmd_options)
|
||||
|
||||
def update_fcgid_context(self, webapp, context):
|
||||
wrapper_path = webapp.type_instance.FCGID_WRAPPER_PATH % context
|
||||
context.update({
|
||||
'wrapper': self.get_fcgid_wrapper(webapp, context),
|
||||
'wrapper_path': wrapper_path,
|
||||
'wrapper_dir': os.path.dirname(wrapper_path),
|
||||
})
|
||||
context.update({
|
||||
'cmd_options': self.get_fcgid_cmd_options(webapp, context),
|
||||
'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
|
||||
})
|
||||
|
||||
def update_fpm_context(self, webapp, context):
|
||||
context.update({
|
||||
'fpm_config': self.get_fpm_config(webapp, context),
|
||||
'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
|
||||
})
|
||||
return context
|
||||
|
||||
def get_context(self, webapp):
|
||||
context = super(PHPBackend, self).get_context(webapp)
|
||||
context.update({
|
||||
'php_version': webapp.type_instance.get_php_version(),
|
||||
'php_version_number': webapp.type_instance.get_php_version_number(),
|
||||
})
|
||||
self.update_fcgid_context(webapp, context)
|
||||
self.update_fpm_context(webapp, context)
|
||||
return context
|
|
@ -1,97 +0,0 @@
|
|||
import os
|
||||
import textwrap
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.apps.orchestration import ServiceController
|
||||
|
||||
from . import WebAppServiceMixin
|
||||
from .. import settings
|
||||
|
||||
|
||||
class PHPFcgidBackend(WebAppServiceMixin, ServiceController):
|
||||
""" Per-webapp fcgid application """
|
||||
verbose_name = _("PHP-Fcgid")
|
||||
directive = 'fcgid'
|
||||
default_route_match = "webapp.type_class.php_execution == 'fcgid'"
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
self.create_webapp_dir(context)
|
||||
self.set_under_construction(context)
|
||||
self.append("mkdir -p %(wrapper_dir)s" % context)
|
||||
self.append(textwrap.dedent("""\
|
||||
{
|
||||
echo -e '%(wrapper)s' | diff -N -I'^\s*#' %(wrapper_path)s -
|
||||
} || {
|
||||
echo -e '%(wrapper)s' > %(wrapper_path)s; UPDATED_APACHE=1
|
||||
}""") % context
|
||||
)
|
||||
self.append("chmod +x %(wrapper_path)s" % context)
|
||||
self.append("chown -R %(user)s:%(group)s %(wrapper_dir)s" % context)
|
||||
if context['cmd_options']:
|
||||
self.append(textwrap.dedent("""
|
||||
{
|
||||
echo -e '%(cmd_options)s' | diff -N -I'^\s*#' %(cmd_options_path)s -
|
||||
} || {
|
||||
echo -e '%(cmd_options)s' > %(cmd_options_path)s; UPDATED_APACHE=1
|
||||
}""" ) % context
|
||||
)
|
||||
else:
|
||||
self.append("rm -f %(cmd_options_path)s" % context)
|
||||
|
||||
def delete(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
self.append("rm -f '%(wrapper_path)s'" % context)
|
||||
self.append("rm -f '%(cmd_options_path)s'" % context)
|
||||
self.delete_webapp_dir(context)
|
||||
|
||||
def commit(self):
|
||||
self.append('if [[ $UPDATED_APACHE == 1 ]]; then service apache2 reload; fi')
|
||||
|
||||
def get_fcgid_wrapper(self, webapp, context):
|
||||
opt = webapp.type_instance
|
||||
# Format PHP init vars
|
||||
init_vars = opt.get_php_init_vars()
|
||||
if init_vars:
|
||||
init_vars = [ '-d %s="%s"' % (k,v) for k,v in init_vars.iteritems() ]
|
||||
init_vars = ', '.join(init_vars)
|
||||
|
||||
context.update({
|
||||
'php_binary': opt.get_php_binary_path(),
|
||||
'php_rc': opt.get_php_rc_path(),
|
||||
'php_init_vars': init_vars,
|
||||
})
|
||||
return textwrap.dedent("""\
|
||||
#!/bin/sh
|
||||
# %(banner)s
|
||||
export PHPRC=%(php_rc)s
|
||||
exec %(php_binary)s %(php_init_vars)s""") % context
|
||||
|
||||
def get_fcgid_cmd_options(self, webapp, context):
|
||||
maps = {
|
||||
'MaxProcesses': webapp.get_options().get('processes', None),
|
||||
'IOTimeout': webapp.get_options().get('timeout', None),
|
||||
}
|
||||
cmd_options = []
|
||||
for directive, value in maps.iteritems():
|
||||
if value:
|
||||
cmd_options.append("%s %s" % (directive, value))
|
||||
if cmd_options:
|
||||
head = '# %(banner)s\nFcgidCmdOptions %(wrapper_path)s' % context
|
||||
cmd_options.insert(0, head)
|
||||
return ' \\\n '.join(cmd_options)
|
||||
|
||||
def get_context(self, webapp):
|
||||
context = super(PHPFcgidBackend, self).get_context(webapp)
|
||||
wrapper_path = settings.WEBAPPS_FCGID_WRAPPER_PATH % context
|
||||
context.update({
|
||||
'wrapper': self.get_fcgid_wrapper(webapp, context),
|
||||
'wrapper_path': wrapper_path,
|
||||
'wrapper_dir': os.path.dirname(wrapper_path),
|
||||
})
|
||||
context.update({
|
||||
'cmd_options': self.get_fcgid_cmd_options(webapp, context),
|
||||
'cmd_options_path': settings.WEBAPPS_FCGID_CMD_OPTIONS_PATH % context,
|
||||
})
|
||||
return context
|
|
@ -1,78 +0,0 @@
|
|||
import os
|
||||
import textwrap
|
||||
|
||||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.apps.orchestration import ServiceController
|
||||
|
||||
from . import WebAppServiceMixin
|
||||
from .. import settings
|
||||
|
||||
|
||||
class PHPFPMBackend(WebAppServiceMixin, ServiceController):
|
||||
""" Per-webapp php application """
|
||||
verbose_name = _("PHP-FPM")
|
||||
default_route_match = "webapp.type_class.php_execution == 'fpm'"
|
||||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
self.create_webapp_dir(context)
|
||||
self.set_under_construction(context)
|
||||
self.append(textwrap.dedent("""\
|
||||
{
|
||||
echo -e '%(fpm_config)s' | diff -N -I'^\s*;;' %(fpm_path)s -
|
||||
} || {
|
||||
echo -e '%(fpm_config)s' > %(fpm_path)s
|
||||
UPDATEDFPM=1
|
||||
}""") % context
|
||||
)
|
||||
|
||||
def delete(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
self.append("rm '%(fpm_path)s'" % context)
|
||||
self.delete_webapp_dir(context)
|
||||
|
||||
def commit(self):
|
||||
if not self.cmds:
|
||||
return
|
||||
super(PHPFPMBackend, self).commit()
|
||||
self.append(textwrap.dedent("""
|
||||
if [[ $UPDATEDFPM == 1 ]]; then
|
||||
service php5-fpm reload
|
||||
service php5-fpm start
|
||||
fi"""))
|
||||
|
||||
def get_fpm_config(self, webapp, context):
|
||||
context.update({
|
||||
'init_vars': webapp.type_instance.get_php_init_vars(),
|
||||
'fpm_port': webapp.get_fpm_port(),
|
||||
'max_children': webapp.get_options().get('processes', False),
|
||||
'request_terminate_timeout': webapp.get_options().get('timeout', False),
|
||||
})
|
||||
context['fpm_listen'] = settings.WEBAPPS_FPM_LISTEN % context
|
||||
fpm_config = Template(textwrap.dedent("""\
|
||||
;; {{ banner }}
|
||||
[{{ user }}]
|
||||
user = {{ user }}
|
||||
group = {{ group }}
|
||||
|
||||
listen = {{ fpm_listen | safe }}
|
||||
listen.owner = {{ user }}
|
||||
listen.group = {{ group }}
|
||||
pm = ondemand
|
||||
{% if max_children %}pm.max_children = {{ max_children }}{% endif %}
|
||||
{% if request_terminate_timeout %}request_terminate_timeout = {{ request_terminate_timeout }}{% endif %}
|
||||
{% for name, value in init_vars.iteritems %}
|
||||
php_admin_value[{{ name | safe }}] = {{ value | safe }}{% endfor %}
|
||||
"""
|
||||
))
|
||||
return fpm_config.render(Context(context))
|
||||
|
||||
def get_context(self, webapp):
|
||||
context = super(PHPFPMBackend, self).get_context(webapp)
|
||||
context.update({
|
||||
'fpm_config': self.get_fpm_config(webapp, context),
|
||||
'fpm_path': settings.WEBAPPS_PHPFPM_POOL_PATH % context,
|
||||
})
|
||||
return context
|
|
@ -27,50 +27,48 @@ WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PA
|
|||
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||
'')
|
||||
|
||||
|
||||
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', (
|
||||
'orchestra.apps.webapps.types.php.PHPFPMApp',
|
||||
'orchestra.apps.webapps.types.php.PHPFCGIDApp',
|
||||
'orchestra.apps.webapps.types.php.PHPApp',
|
||||
'orchestra.apps.webapps.types.misc.StaticApp',
|
||||
'orchestra.apps.webapps.types.misc.WebalizerApp',
|
||||
'orchestra.apps.webapps.types.saas.WordPressMuApp',
|
||||
'orchestra.apps.webapps.types.saas.DokuWikiMuApp',
|
||||
'orchestra.apps.webapps.types.saas.DrupalMuApp',
|
||||
'orchestra.apps.webapps.types.misc.SymbolicLinkApp',
|
||||
'orchestra.apps.webapps.types.wordpress.WordPressFPMApp',
|
||||
'orchestra.apps.webapps.types.wordpress.WordPressFCGIDApp',
|
||||
'orchestra.apps.webapps.types.wordpress.WordPressApp',
|
||||
))
|
||||
|
||||
|
||||
|
||||
WEBAPPS_PHP_FCGID_VERSIONS = getattr(settings, 'WEBAPPS_PHP_FCGID_VERSIONS', (
|
||||
('5.4', '5.4'),
|
||||
('5.3', '5.3'),
|
||||
('5.2', '5.2'),
|
||||
('4', '4'),
|
||||
WEBAPPS_PHP_VERSIONS = getattr(settings, 'WEBAPPS_PHP_VERSIONS', (
|
||||
# Execution modle choose by ending with -fpm or -cgi
|
||||
('php-5.4-fpm', 'PHP 5.4 FPM'),
|
||||
('php-5.4-cgi', 'PHP 5.4 FCGID'),
|
||||
('php-5.3-cgi', 'PHP 5.3 FCGID'),
|
||||
('php-5.2-cgi', 'PHP 5.2 FCGID'),
|
||||
('php-4-cgi', 'PHP 4 FCGID'),
|
||||
))
|
||||
|
||||
|
||||
WEBAPPS_PHP_FCGID_DEFAULT_VERSION = getattr(settings, 'WEBAPPS_PHP_FCGID_DEFAULT_VERSION',
|
||||
'5.4')
|
||||
WEBAPPS_DEFAULT_PHP_VERSION = getattr(settings, 'WEBAPPS_DEFAULT_PHP_VERSION',
|
||||
'5.4-cgi')
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_BINARY_PATH',
|
||||
# Path of the cgi binary used by fcgid
|
||||
'/usr/bin/php%(php_version)s-cgi')
|
||||
'/usr/bin/php%(php_version_number)s-cgi')
|
||||
|
||||
WEBAPPS_PHP_CGI_RC_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_RC_PATH',
|
||||
|
||||
WEBAPPS_PHP_CGI_RC_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_RC_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version)s/cgi/')
|
||||
'/etc/php%(php_version_number)s/cgi/')
|
||||
|
||||
|
||||
WEBAPPS_PHP_FPM_VERSIONS = getattr(settings, 'WEBAPPS_PHP_FPM_VERSIONS', (
|
||||
('5.4', '5.4'),
|
||||
))
|
||||
WEBAPPS_PHP_CGI_INI_SCAN_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version_number)s/cgi/conf.d')
|
||||
|
||||
|
||||
WEBAPPS_PHP_FPM_DEFAULT_VERSION = getattr(settings, 'WEBAPPS_PHP_DEFAULT_VERSION',
|
||||
'5.4')
|
||||
|
||||
|
||||
WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_PATH',
|
||||
# Server-side path where a under construction stock page is
|
||||
|
|
|
@ -20,7 +20,6 @@ class AppType(plugins.Plugin):
|
|||
unique_name = False
|
||||
option_groups = (AppOption.FILESYSTEM, AppOption.PROCESS, AppOption.PHP)
|
||||
# TODO generic name like 'execution' ?
|
||||
php_execution = None
|
||||
|
||||
@classmethod
|
||||
@cached
|
||||
|
|
|
@ -9,7 +9,7 @@ from orchestra.plugins.forms import PluginDataForm
|
|||
from ..options import AppOption
|
||||
|
||||
from . import AppType
|
||||
from .php import PHPAppType
|
||||
from .php import PHPApp
|
||||
|
||||
|
||||
class StaticApp(AppType):
|
||||
|
@ -48,7 +48,7 @@ class SymbolicLinkSerializer(serializers.Serializer):
|
|||
path = serializers.CharField(label=_("Path"))
|
||||
|
||||
|
||||
class SymbolicLinkApp(PHPAppType):
|
||||
class SymbolicLinkApp(PHPApp):
|
||||
name = 'symbolic-link'
|
||||
verbose_name = "Symbolic link"
|
||||
form = SymbolicLinkForm
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -12,12 +13,46 @@ from .. import settings
|
|||
from . import AppType
|
||||
|
||||
|
||||
class PHPAppType(AppType):
|
||||
FPM = 'fpm'
|
||||
FCGID = 'fcgid'
|
||||
help_message = _("Version of PHP used to execute this webapp. <br>"
|
||||
"Changing the PHP version may result in application malfunction, "
|
||||
"make sure that everything continue to work as expected.")
|
||||
|
||||
|
||||
class PHPAppForm(PluginDataForm):
|
||||
php_version = forms.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_VERSIONS,
|
||||
initial=settings.WEBAPPS_DEFAULT_PHP_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPAppSerializer(serializers.Serializer):
|
||||
php_version = serializers.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_VERSIONS,
|
||||
default=settings.WEBAPPS_DEFAULT_PHP_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPApp(AppType):
|
||||
name = 'php'
|
||||
verbose_name = "PHP"
|
||||
help_text = _("This creates a PHP application under ~/webapps/<app_name><br>")
|
||||
form = PHPAppForm
|
||||
serializer = PHPAppSerializer
|
||||
icon = 'orchestra/icons/apps/PHP.png'
|
||||
|
||||
php_version = 5.4
|
||||
fpm_listen = settings.WEBAPPS_FPM_LISTEN
|
||||
DEFAULT_PHP_VERSION = settings.WEBAPPS_DEFAULT_PHP_VERSION
|
||||
PHP_DISABLED_FUNCTIONS = settings.WEBAPPS_PHP_DISABLED_FUNCTIONS
|
||||
PHP_ERROR_LOG_PATH = settings.WEBAPPS_PHP_ERROR_LOG_PATH
|
||||
FPM_LISTEN = settings.WEBAPPS_FPM_LISTEN
|
||||
FCGID_WRAPPER_PATH = settings.WEBAPPS_FCGID_WRAPPER_PATH
|
||||
|
||||
@property
|
||||
def is_fpm(self):
|
||||
return self.get_php_version().endswith('-fpm')
|
||||
|
||||
@property
|
||||
def is_fcgid(self):
|
||||
return self.get_php_version().endswith('-cgi')
|
||||
|
||||
def get_context(self):
|
||||
""" context used to format settings """
|
||||
|
@ -46,95 +81,37 @@ class PHPAppType(AppType):
|
|||
enabled_functions += enabled_functions.get().value.split(',')
|
||||
if enabled_functions:
|
||||
disabled_functions = []
|
||||
for function in settings.WEBAPPS_PHP_DISABLED_FUNCTIONS:
|
||||
for function in self.PHP_DISABLED_FUNCTIONS:
|
||||
if function not in enabled_functions:
|
||||
disabled_functions.append(function)
|
||||
init_vars['dissabled_functions'] = ','.join(disabled_functions)
|
||||
if settings.WEBAPPS_PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
|
||||
if self.PHP_ERROR_LOG_PATH and 'error_log' not in init_vars:
|
||||
context = self.get_context()
|
||||
error_log_path = os.path.normpath(settings.WEBAPPS_PHP_ERROR_LOG_PATH % context)
|
||||
error_log_path = os.path.normpath(self.PHP_ERROR_LOG_PATH % context)
|
||||
init_vars['error_log'] = error_log_path
|
||||
return init_vars
|
||||
|
||||
|
||||
help_message = _("Version of PHP used to execute this webapp. <br>"
|
||||
"Changing the PHP version may result in application malfunction, "
|
||||
"make sure that everything continue to work as expected.")
|
||||
|
||||
|
||||
class PHPFPMAppForm(PluginDataForm):
|
||||
php_version = forms.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_FPM_VERSIONS,
|
||||
initial=settings.WEBAPPS_PHP_FPM_DEFAULT_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPFPMAppSerializer(serializers.Serializer):
|
||||
php_version = serializers.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_FPM_VERSIONS,
|
||||
default=settings.WEBAPPS_PHP_FPM_DEFAULT_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPFPMApp(PHPAppType):
|
||||
name = 'php-fpm'
|
||||
php_execution = PHPAppType.FPM
|
||||
verbose_name = "PHP FPM"
|
||||
help_text = _("This creates a PHP application under ~/webapps/<app_name><br>"
|
||||
"PHP-FPM will be used to execute PHP files.")
|
||||
icon = 'orchestra/icons/apps/PHPFPM.png'
|
||||
form = PHPFPMAppForm
|
||||
serializer = PHPFPMAppSerializer
|
||||
|
||||
def get_directive(self):
|
||||
context = self.get_directive_context()
|
||||
socket_type = 'unix'
|
||||
if ':' in self.fpm_listen:
|
||||
socket_type = 'tcp'
|
||||
socket = self.fpm_listen % context
|
||||
return ('fpm', socket_type, socket, self.instance.get_path())
|
||||
|
||||
|
||||
class PHPFCGIDAppForm(PluginDataForm):
|
||||
php_version = forms.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_FCGID_VERSIONS,
|
||||
initial=settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPFCGIDAppSerializer(serializers.Serializer):
|
||||
php_version = serializers.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_FCGID_VERSIONS,
|
||||
default=settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
class PHPFCGIDApp(PHPAppType):
|
||||
name = 'php-fcgid'
|
||||
php_execution = PHPAppType.FCGID
|
||||
verbose_name = "PHP FCGID"
|
||||
help_text = _("This creates a PHP application under ~/webapps/<app_name><br>"
|
||||
"Apache-mod-fcgid will be used to execute PHP files.")
|
||||
icon = 'orchestra/icons/apps/PHPFCGI.png'
|
||||
form = PHPFCGIDAppForm
|
||||
serializer = PHPFCGIDAppSerializer
|
||||
if self.is_fpm:
|
||||
socket_type = 'unix'
|
||||
if ':' in self.FPM_LISTEN:
|
||||
socket_type = 'tcp'
|
||||
socket = self.FPM_LISTEN % context
|
||||
return ('fpm', socket_type, socket, self.instance.get_path())
|
||||
elif self.is_fcgid:
|
||||
wrapper_path = os.path.normpath(self.FCGID_WRAPPER_PATH % context)
|
||||
return ('fcgid', self.instance.get_path(), wrapper_path)
|
||||
else:
|
||||
raise ValueError("Unknown directive for php version '%s'" % php_version)
|
||||
|
||||
def get_directive(self):
|
||||
context = self.get_directive_context()
|
||||
wrapper_path = os.path.normpath(settings.WEBAPPS_FCGID_PATH % context)
|
||||
return ('fcgid', self.instance.get_path(), wrapper_path)
|
||||
def get_php_version(self):
|
||||
default_version = self.DEFAULT_PHP_VERSION
|
||||
return self.instance.data.get('php_version', default_version)
|
||||
|
||||
def get_php_binary_path(self):
|
||||
default_version = settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION
|
||||
context = {
|
||||
'php_version': self.instance.data.get('php_version', default_version)
|
||||
}
|
||||
return os.path.normpath(settings.WEBAPPS_PHP_CGI_BINARY_PATH % context)
|
||||
|
||||
def get_php_rc_path(self):
|
||||
default_version = settings.WEBAPPS_PHP_FCGID_DEFAULT_VERSION
|
||||
context = {
|
||||
'php_version': self.instance.data.get('php_version', default_version)
|
||||
}
|
||||
return os.path.normpath(settings.WEBAPPS_PHP_CGI_RC_PATH % context)
|
||||
|
||||
def get_php_version_number(self):
|
||||
php_version = self.get_php_version()
|
||||
number = re.findall(r'[0-9]+\.?[0-9]+', php_version)
|
||||
if len(number) > 1:
|
||||
raise ValueError("Multiple version number matches for '%'" % php_version)
|
||||
return number[0]
|
||||
|
|
|
@ -9,11 +9,10 @@ from orchestra.utils.python import random_ascii
|
|||
|
||||
from .. import settings
|
||||
|
||||
from .php import (PHPAppType, PHPFCGIDApp, PHPFPMApp, PHPFCGIDAppForm, PHPFCGIDAppSerializer,
|
||||
PHPFPMAppForm, PHPFPMAppSerializer)
|
||||
from .php import PHPApp, PHPAppForm, PHPAppSerializer
|
||||
|
||||
|
||||
class WordPressAbstractAppForm(PluginDataForm):
|
||||
class WordPressAppForm(PHPAppForm):
|
||||
db_name = forms.CharField(label=_("Database name"),
|
||||
help_text=_("Database used for this webapp."))
|
||||
db_user = forms.CharField(label=_("Database user"),)
|
||||
|
@ -21,16 +20,20 @@ class WordPressAbstractAppForm(PluginDataForm):
|
|||
help_text=_("Initial database password."))
|
||||
|
||||
|
||||
class WordPressAbstractAppSerializer(serializers.Serializer):
|
||||
class WordPressAppSerializer(PHPAppSerializer):
|
||||
db_name = serializers.CharField(label=_("Database name"), required=False)
|
||||
db_user = serializers.CharField(label=_("Database user"), required=False)
|
||||
db_pass = serializers.CharField(label=_("Database user password"), required=False)
|
||||
|
||||
|
||||
class WordPressAbstractApp(object):
|
||||
icon = 'orchestra/icons/apps/WordPress.png'
|
||||
class WordPressApp(PHPApp):
|
||||
name = 'wordpress'
|
||||
verbose_name = "WordPress"
|
||||
serializer = WordPressAppSerializer
|
||||
change_form = WordPressAppForm
|
||||
change_readonly_fileds = ('db_name', 'db_user', 'db_pass',)
|
||||
help_text = _("Visit http://<domain.lan>/wp-admin/install.php to finish the installation.")
|
||||
icon = 'orchestra/icons/apps/WordPress.png'
|
||||
|
||||
def get_db_name(self):
|
||||
db_name = 'wp_%s_%s' % (self.instance.name, self.instance.account)
|
||||
|
@ -46,7 +49,7 @@ class WordPressAbstractApp(object):
|
|||
return random_ascii(10)
|
||||
|
||||
def validate(self):
|
||||
super(WordPressAbstractApp, self).validate()
|
||||
super(WordPressApp, self).validate()
|
||||
create = not self.instance.pk
|
||||
if create:
|
||||
db = Database(name=self.get_db_name(), account=self.instance.account)
|
||||
|
@ -79,7 +82,7 @@ class WordPressAbstractApp(object):
|
|||
else:
|
||||
# Trigger related backends
|
||||
for related in self.get_related():
|
||||
related.save()
|
||||
related.save(updated_fields=[])
|
||||
|
||||
def delete(self):
|
||||
for related in self.get_related():
|
||||
|
@ -101,23 +104,3 @@ class WordPressAbstractApp(object):
|
|||
else:
|
||||
related.append(db)
|
||||
return related
|
||||
|
||||
|
||||
class WordPressFPMApp(WordPressAbstractApp, PHPFPMApp):
|
||||
name = 'wordpress-fpm'
|
||||
php_execution = PHPAppType.FPM
|
||||
verbose_name = "WordPress (FPM)"
|
||||
serializer = type('WordPressFPMSerializer',
|
||||
(WordPressAbstractAppSerializer, PHPFPMAppSerializer), {})
|
||||
change_form = type('WordPressFPMForm',
|
||||
(WordPressAbstractAppForm, PHPFPMAppForm), {})
|
||||
|
||||
|
||||
class WordPressFCGIDApp(WordPressAbstractApp, PHPFCGIDApp):
|
||||
name = 'wordpress-fcgid'
|
||||
php_execution = PHPAppType.FCGID
|
||||
verbose_name = "WordPress (FCGID)"
|
||||
serializer = type('WordPressFCGIDSerializer',
|
||||
(WordPressAbstractAppSerializer, PHPFCGIDAppSerializer), {})
|
||||
change_form = type('WordPressFCGIDForm',
|
||||
(WordPressAbstractAppForm, PHPFCGIDAppForm), {})
|
||||
|
|
|
@ -164,11 +164,11 @@ class Apache2Backend(ServiceController):
|
|||
for rules in directives.get('sec_rule_remove', []):
|
||||
for rule in rules.value.split():
|
||||
config += "SecRuleRemoveById %i\n" % int(rule)
|
||||
for modsecurity in directives.get('sec_rule_off', []):
|
||||
for modsecurity in directives.get('sec_engine', []):
|
||||
config += textwrap.dedent("""\
|
||||
<Location %s>
|
||||
SecRuleEngine off
|
||||
</LocationMatch>
|
||||
</Location>
|
||||
""") % modsecurity
|
||||
return config
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from .. import settings
|
|||
class WebalizerBackend(ServiceController):
|
||||
verbose_name = _("Webalizer Content")
|
||||
model = 'websites.Content'
|
||||
default_route_match = "content.webapp.type == 'webalizer'"
|
||||
|
||||
def save(self, content):
|
||||
context = self.get_context(content)
|
||||
|
|
|
@ -149,5 +149,5 @@ class SecEngine(SiteDirective):
|
|||
name = 'sec_engine'
|
||||
verbose_name = _("Modsecurity engine")
|
||||
help_text = _("URL location for disabling modsecurity engine.")
|
||||
regex = r'^[^ ]+$'
|
||||
regex = r'^/[^ ]*$'
|
||||
group = SiteDirective.SEC
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -1,63 +1,203 @@
|
|||
<?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"
|
||||
width="48px"
|
||||
height="48px"
|
||||
id="svg3109"
|
||||
version="1.1"
|
||||
width="300"
|
||||
height="159"
|
||||
viewBox="0 0 300 160"
|
||||
id="svg2943">
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="PHP.svg"
|
||||
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/PHP.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs2945">
|
||||
id="defs3111">
|
||||
<linearGradient
|
||||
x1="150"
|
||||
y1="84"
|
||||
x2="299"
|
||||
y2="84"
|
||||
id="linearGradient3798"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4350"
|
||||
id="linearGradient2623"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.972701,0,0,0.925949,-23.47387,-13.25574)"
|
||||
x1="97.728783"
|
||||
y1="54.517036"
|
||||
x2="97.728783"
|
||||
y2="42.400635" />
|
||||
<linearGradient
|
||||
id="linearGradient4350"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3800"
|
||||
style="stop-color:#dddce9;stop-opacity:1"
|
||||
offset="0" />
|
||||
id="stop4352"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3802"
|
||||
style="stop-color:#5664a3;stop-opacity:1"
|
||||
offset="0.37" />
|
||||
id="stop4354"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4286"
|
||||
id="linearGradient2625"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-19,0.07465812)"
|
||||
x1="88.75"
|
||||
y1="22.673088"
|
||||
x2="87.8125"
|
||||
y2="45.579079" />
|
||||
<linearGradient
|
||||
id="linearGradient4286">
|
||||
<stop
|
||||
id="stop3804"
|
||||
style="stop-color:#000000;stop-opacity:1"
|
||||
offset="1" />
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4288" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.30508474;"
|
||||
offset="1"
|
||||
id="stop4290" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4350"
|
||||
id="linearGradient2627"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-26,-16.91315)"
|
||||
x1="95.03125"
|
||||
y1="57.906303"
|
||||
x2="95.03125"
|
||||
y2="44.592937" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4114"
|
||||
id="radialGradient17299"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(1.64399,0.608276)"
|
||||
cx="15.115514"
|
||||
cy="63.965389"
|
||||
fx="15.115514"
|
||||
fy="63.965389"
|
||||
r="12.289036" />
|
||||
<linearGradient
|
||||
id="linearGradient4114"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop4116"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop4118"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="77.914261"
|
||||
cy="-48.544521"
|
||||
r="146"
|
||||
fx="77.914261"
|
||||
fy="-48.544521"
|
||||
id="radialGradient3870"
|
||||
xlink:href="#linearGradient3798"
|
||||
r="12.289036"
|
||||
fy="63.965389"
|
||||
fx="15.115514"
|
||||
cy="63.965389"
|
||||
cx="15.115514"
|
||||
gradientTransform="scale(1.64399,0.608276)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
|
||||
id="radialGradient3107"
|
||||
xlink:href="#linearGradient4114"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<ellipse
|
||||
cx="150"
|
||||
cy="80"
|
||||
rx="146"
|
||||
ry="76"
|
||||
id="ellipse3860"
|
||||
style="fill:#6c7eb7;stroke:url(#radialGradient3870);stroke-width:5.5" />
|
||||
<path
|
||||
d="m 45,125 16,-81 37,0 c 16,1 24,9 24,23 0,24 -19,38 -36,37 l -18,0 -4,21 -19,0 z m 27,-36 5,-30 13,0 c 7,0 12,3 12,9 -1,17 -9,20 -18,21 l -12,0 z"
|
||||
id="p"
|
||||
style="fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linejoin:round" />
|
||||
<path
|
||||
d="m 116,104 16,-81 19,0 -4,21 18,0 c 16,1 22,9 20,19 l -7,41 -20,0 7,-37 c 1,-5 1,-8 -6,-8 l -15,0 -9,45 -19,0 z"
|
||||
id="h"
|
||||
style="stroke:#ffffff;stroke-width:2;stroke-linejoin:round" />
|
||||
<use
|
||||
transform="translate(134,0)"
|
||||
id="p2"
|
||||
xlink:href="#p" />
|
||||
</svg>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7"
|
||||
inkscape:cx="24"
|
||||
inkscape:cy="24"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1024"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3114">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
transform="matrix(0.99462358,0,0,1.2365616,-1.8004407,-14.308795)"
|
||||
d="m 45.052803,38.908627 a 20.203051,7.4751287 0 1 1 -40.4061012,0 20.203051,7.4751287 0 1 1 40.4061012,0 z"
|
||||
sodipodi:ry="7.4751287"
|
||||
sodipodi:rx="20.203051"
|
||||
sodipodi:cy="38.908627"
|
||||
sodipodi:cx="24.849752"
|
||||
id="path4112"
|
||||
style="opacity:0.83257919;fill:url(#radialGradient3107);fill-opacity:1;stroke:none;display:inline"
|
||||
sodipodi:type="arc" />
|
||||
<g
|
||||
id="g2615"
|
||||
transform="matrix(1.4117127,0,0,1.4117127,-76.234629,-24.714637)">
|
||||
<path
|
||||
transform="matrix(0.945073,0,0,0.905454,-21.13524,5.644298)"
|
||||
d="m 114.99324,33.06237 c 0,5.516156 -7.83542,9.987884 -17.500892,9.987884 -9.665476,0 -17.500893,-4.471728 -17.500893,-9.987884 0,-5.516155 7.835417,-9.987883 17.500893,-9.987883 9.665472,0 17.500892,4.471728 17.500892,9.987883 z"
|
||||
sodipodi:ry="9.9878836"
|
||||
sodipodi:rx="17.500893"
|
||||
sodipodi:cy="33.06237"
|
||||
sodipodi:cx="97.492348"
|
||||
id="path19716"
|
||||
style="fill:#5d65b4;fill-opacity:1;fill-rule:nonzero;stroke:#3d4384;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:none;stroke:#9ca1d2;stroke-width:1.17854095;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="path19718"
|
||||
sodipodi:cx="97.492348"
|
||||
sodipodi:cy="33.06237"
|
||||
sodipodi:rx="17.500893"
|
||||
sodipodi:ry="9.9878836"
|
||||
d="m 114.99324,33.06237 c 0,5.516156 -7.83542,9.987884 -17.500892,9.987884 -9.665476,0 -17.500893,-4.471728 -17.500893,-9.987884 0,-5.516155 7.835417,-9.987883 17.500893,-9.987883 9.665472,0 17.500892,4.471728 17.500892,9.987883 z"
|
||||
transform="matrix(0.889513,0,0,0.809391,-15.71799,8.823718)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cscsscsc"
|
||||
id="path19720"
|
||||
d="m 70.665432,27.000328 c -8.770642,0.06413 -15.490098,3.890307 -15.734387,8.371883 -0.05012,0.919501 0.159559,1.78858 0.687905,2.617543 3.996698,-3.732848 10.440129,-2.778994 15.745611,-2.170193 5.324581,0.610992 11.669454,0.972529 15.313098,-2.145164 0.0072,-0.0062 -0.02725,-0.039 -0.01953,-0.04456 -1.516063,-3.804519 -8.019325,-6.629508 -15.776011,-6.629508 -0.06938,0 -0.147511,-4.57e-4 -0.216684,0 z"
|
||||
style="opacity:0.55813952;fill:url(#linearGradient2623);fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path19724"
|
||||
d="m 69.156251,29.324658 -1,5 c 0.08862,-1.599006 -1.095832,-2.75 -2.28125,-2.75 l -4.125,0 -1.625,8.46875 2.03125,0.03125 0.4375,-2.1875 2.1875,0 c 1.191119,0 2.622044,-0.936537 3.1875,-2.625 l -0.5,2.625 2.03125,0 0.90625,-4.75 1.28125,0 c 0.255223,0.002 0.466944,0.03366 0.625,0.09375 0.02953,0.01215 0.06869,0.04716 0.09375,0.0625 0.0048,0.0032 0.02661,-0.0033 0.03125,0 0.0044,0.0035 0.02699,0.02763 0.03125,0.03125 0.0041,0.0038 0.02738,0.02734 0.03125,0.03125 0.0035,0.0042 0.02799,0.02688 0.03125,0.03125 0.0031,0.0045 -0.0029,0.02656 0,0.03125 0.0026,0.0048 0.02882,0.02623 0.03125,0.03125 0.0022,0.0052 -0.002,0.0259 0,0.03125 0.0054,0.01657 0.02789,0.04433 0.03125,0.0625 0.0018,0.01248 -8.72e-4,0.04928 0,0.0625 4.07e-4,0.0136 5.36e-4,0.04812 0,0.0625 -10e-4,0.01477 0.002,0.04693 0,0.0625 -0.0037,0.02398 -0.02529,0.06788 -0.03125,0.09375 l -0.75,4.0625 2.0625,0 0.75,-4.09375 c 0.152547,-0.791704 -0.0036,-1.314355 -0.3125,-1.65625 -0.469323,-0.499058 -1.284638,-0.613618 -1.9375,-0.625 l -1.6875,0 0.4375,-2.1875 -1.96875,0 z m 6.4375,2.25 -1.625,8.46875 2.03125,0.03125 0.40625,-2.1875 2.21875,0 c 1.270525,0 2.80633,-1.058815 3.28125,-2.96875 0.47492,-1.909935 -0.8565,-3.34375 -2.1875,-3.34375 l -4.125,0 z m -12.125,1.53125 1.25,0 c 0.294812,10e-7 0.560277,0.04782 0.75,0.125 0.0352,0.01535 0.09356,0.04457 0.125,0.0625 0.03813,0.0232 0.09271,0.06668 0.125,0.09375 0.01618,0.01498 0.04775,0.04638 0.0625,0.0625 0.0048,0.0055 0.02665,0.02563 0.03125,0.03125 0.0044,0.0057 0.02696,0.02538 0.03125,0.03125 0.04136,0.05994 0.06748,0.146827 0.09375,0.21875 0.005,0.01462 0.02687,0.04742 0.03125,0.0625 0.0838,0.313843 0.04097,0.733356 -0.09375,1.21875 -0.08568,0.308687 -0.200401,0.565156 -0.34375,0.75 -0.01212,0.01502 -0.05004,0.04823 -0.0625,0.0625 -0.02526,0.02781 -0.0672,0.06882 -0.09375,0.09375 -0.01285,0.01124 -0.04926,0.05182 -0.0625,0.0625 -0.402786,0.31197 -0.983107,0.375 -1.75,0.375 -1.268714,-10e-7 -0.71875,0 -0.71875,0 l 0.625,-3.25 z m 13.84375,0 1.25,0 c 0.449239,10e-7 0.793344,0.107982 1,0.28125 0.01618,0.01498 0.04775,0.04638 0.0625,0.0625 0.05235,0.06048 0.0912,0.143519 0.125,0.21875 0.0058,0.01391 0.026,0.04812 0.03125,0.0625 0.005,0.01462 0.02687,0.04742 0.03125,0.0625 0.0838,0.313843 0.04097,0.733356 -0.09375,1.21875 -0.09638,0.347273 -0.23803,0.619815 -0.40625,0.8125 -0.01263,0.0139 -0.04954,0.04932 -0.0625,0.0625 -0.0066,0.0064 -0.02461,0.02502 -0.03125,0.03125 -0.01899,0.01729 -0.04265,0.04649 -0.0625,0.0625 -0.402785,0.31197 -0.983106,0.375 -1.75,0.375 -1.268712,-10e-7 -0.71875,0 -0.71875,0 l 0.625,-3.25 z"
|
||||
style="opacity:0.69957084;fill:none;stroke:url(#linearGradient2625);stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path19732"
|
||||
d="m 69.156251,29.324658 -1,5 c 0.08862,-1.599006 -1.095832,-2.75 -2.28125,-2.75 l -4.125,0 -1.625,8.46875 2.03125,0.03125 0.4375,-2.1875 2.1875,0 c 1.191119,0 2.622044,-0.936537 3.1875,-2.625 l -0.5,2.625 2.03125,0 0.90625,-4.75 1.28125,0 c 0.255223,0.002 0.466944,0.03366 0.625,0.09375 0.02953,0.01215 0.06869,0.04716 0.09375,0.0625 0.0048,0.0032 0.02661,-0.0033 0.03125,0 0.0044,0.0035 0.02699,0.02763 0.03125,0.03125 0.0041,0.0038 0.02738,0.02734 0.03125,0.03125 0.0035,0.0042 0.02799,0.02688 0.03125,0.03125 0.0031,0.0045 -0.0029,0.02656 0,0.03125 0.0026,0.0048 0.02882,0.02623 0.03125,0.03125 0.0022,0.0052 -0.002,0.0259 0,0.03125 0.0054,0.01657 0.02789,0.04433 0.03125,0.0625 0.0018,0.01248 -8.72e-4,0.04928 0,0.0625 4.07e-4,0.0136 5.36e-4,0.04812 0,0.0625 -10e-4,0.01477 0.002,0.04693 0,0.0625 -0.0037,0.02398 -0.02529,0.06788 -0.03125,0.09375 l -0.75,4.0625 2.0625,0 0.75,-4.09375 c 0.152547,-0.791704 -0.0036,-1.314355 -0.3125,-1.65625 -0.469323,-0.499058 -1.284638,-0.613618 -1.9375,-0.625 l -1.6875,0 0.4375,-2.1875 -1.96875,0 z m 6.4375,2.25 -1.625,8.46875 2.03125,0.03125 0.40625,-2.1875 2.21875,0 c 1.270525,0 2.80633,-1.058815 3.28125,-2.96875 0.47492,-1.909935 -0.8565,-3.34375 -2.1875,-3.34375 l -4.125,0 z m -12.125,1.53125 1.25,0 c 0.294812,10e-7 0.560277,0.04782 0.75,0.125 0.0352,0.01535 0.09356,0.04457 0.125,0.0625 0.03813,0.0232 0.09271,0.06668 0.125,0.09375 0.01618,0.01498 0.04775,0.04638 0.0625,0.0625 0.0048,0.0055 0.02665,0.02563 0.03125,0.03125 0.0044,0.0057 0.02696,0.02538 0.03125,0.03125 0.04136,0.05994 0.06748,0.146827 0.09375,0.21875 0.005,0.01462 0.02687,0.04742 0.03125,0.0625 0.0838,0.313843 0.04097,0.733356 -0.09375,1.21875 -0.08568,0.308687 -0.200401,0.565156 -0.34375,0.75 -0.01212,0.01502 -0.05004,0.04823 -0.0625,0.0625 -0.02526,0.02781 -0.0672,0.06882 -0.09375,0.09375 -0.01285,0.01124 -0.04926,0.05182 -0.0625,0.0625 -0.402786,0.31197 -0.983107,0.375 -1.75,0.375 -1.268714,-10e-7 -0.71875,0 -0.71875,0 l 0.625,-3.25 z m 13.84375,0 1.25,0 c 0.449239,10e-7 0.793344,0.107982 1,0.28125 0.01618,0.01498 0.04775,0.04638 0.0625,0.0625 0.05235,0.06048 0.0912,0.143519 0.125,0.21875 0.0058,0.01391 0.026,0.04812 0.03125,0.0625 0.005,0.01462 0.02687,0.04742 0.03125,0.0625 0.0838,0.313843 0.04097,0.733356 -0.09375,1.21875 -0.09638,0.347273 -0.23803,0.619815 -0.40625,0.8125 -0.01263,0.0139 -0.04954,0.04932 -0.0625,0.0625 -0.0066,0.0064 -0.02461,0.02502 -0.03125,0.03125 -0.01899,0.01729 -0.04265,0.04649 -0.0625,0.0625 -0.402785,0.31197 -0.983106,0.375 -1.75,0.375 -1.268712,-10e-7 -0.71875,0 -0.71875,0 l 0.625,-3.25 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path19738"
|
||||
d="m 69.156249,29.336846 -1,4.90625 c 0.04837,-1.559124 -1.114354,-2.65625 -2.28125,-2.65625 l -4.125,0 -0.78125,4.125 c 0.665889,-0.113935 1.33972,-0.182287 2.03125,-0.21875 l 0.46875,-2.375 1.25,0 c 0.292618,-1e-6 0.555977,0.04984 0.75,0.125 0.02468,0.01021 0.07098,0.01981 0.09375,0.03125 0.02949,0.0158 0.0678,0.04454 0.09375,0.0625 0.011,0.0051 0.05173,0.02597 0.0625,0.03125 0.0062,0.0055 0.02536,0.02535 0.03125,0.03125 0.0056,0.0062 0.02594,0.02473 0.03125,0.03125 0.02035,0.02685 0.04389,0.06678 0.0625,0.09375 0.0055,0.01088 0.02595,0.0515 0.03125,0.0625 0.007,0.01296 0.02481,0.04906 0.03125,0.0625 0.0057,0.0054 0.0256,0.02581 0.03125,0.03125 0.0083,0.02123 0.02425,0.07148 0.03125,0.09375 0.09193,0.316681 0.04426,0.752767 -0.09375,1.25 -0.04909,0.176853 -0.118279,0.334053 -0.1875,0.46875 0.715226,0.03477 1.41411,0.08707 2.125,0.15625 0.04337,-0.09783 0.08799,-0.177253 0.125,-0.28125 l -0.0625,0.28125 c 0.672502,0.0661 1.343624,0.171135 2,0.25 l 0.53125,-2.75 1.28125,0 c 0.255223,0.002 0.466944,0.03366 0.625,0.09375 0.0028,-8.5e-5 0.02807,3.12e-4 0.03125,0 0.01111,0.0054 0.05211,0.02538 0.0625,0.03125 0.0056,0.0056 0.02571,0.02558 0.03125,0.03125 0.01109,0.01048 0.05219,0.05143 0.0625,0.0625 0.0056,0.0052 0.0259,0.02586 0.03125,0.03125 0.0084,0.01591 0.02626,0.05084 0.03125,0.0625 -2.37e-4,0.0032 7.3e-5,0.02848 0,0.03125 0.0058,0.0055 0.02551,0.02573 0.03125,0.03125 2e-6,0.01085 2.25e-4,0.05147 0,0.0625 -8e-5,0.0061 -1.02e-4,0.02785 0,0.03125 -9.3e-5,0.02098 0.0022,0.07099 0,0.09375 -9.26e-4,0.0035 0.0013,0.02729 0,0.03125 -0.0051,0.01302 -0.02785,0.05141 -0.03125,0.0625 l -0.4375,2.34375 c 0.670609,0.07685 1.341434,0.159629 2.03125,0.21875 l 0.46875,-2.5625 c 0.11067,-0.574373 0.06008,-1.019277 -0.09375,-1.34375 -0.04739,-0.09474 -0.12356,-0.204443 -0.1875,-0.28125 -0.02374,-0.02737 -0.06813,-0.06856 -0.09375,-0.09375 -0.0098,-0.0092 -0.04608,-0.04615 -0.0625,-0.0625 -0.0075,-0.0063 -0.02365,-0.02509 -0.03125,-0.03125 -0.478833,-0.372345 -1.198721,-0.458593 -1.78125,-0.46875 l -1.6875,0 0.4375,-2.1875 -1.96875,0 z m 6.4375,2.25 -0.9375,4.8125 c 2.24771,0.171748 4.53955,0.198619 6.656252,-0.125 0.251,-0.380091 0.4676,-0.836424 0.59375,-1.34375 0.47492,-1.909933 -0.8565,-3.34375 -2.187502,-3.34375 l -4.125,0 z m 1.71875,1.53125 1.25,0 c 0.28077,-1e-6 0.53335,0.02353 0.71875,0.09375 0.03546,0.01592 0.09667,0.04737 0.125,0.0625 0.02358,0.01345 0.07228,0.04764 0.09375,0.0625 0.011,0.0051 0.05173,0.02597 0.0625,0.03125 0.003,0.0035 0.0286,0.02736 0.03125,0.03125 0.0069,0.01272 0.02336,0.05102 0.03125,0.0625 0.0056,0.0055 0.02569,0.02573 0.03125,0.03125 0.0056,0.0055 0.02567,0.02575 0.03125,0.03125 0.0055,0.01088 0.02595,0.0515 0.03125,0.0625 0.007,0.01296 0.02481,0.04906 0.03125,0.0625 0.14748,0.328282 0.12647,0.806734 -0.03125,1.375 -0.07853,0.282964 -0.18499,0.509551 -0.3125,0.6875 -0.0331,0.0444 -0.08385,0.109473 -0.125,0.15625 -0.01624,0.01759 -0.04756,0.04847 -0.0625,0.0625 -0.0063,0.0058 -0.02483,0.02563 -0.03125,0.03125 -0.40372,0.339333 -0.98879,0.40625 -1.78125,0.40625 -1.26871,10e-7 -0.71875,0 -0.71875,0 l 0.625,-3.25 z"
|
||||
style="opacity:0.12217198;fill:url(#linearGradient2627);fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 16 KiB |
|
@ -64,7 +64,7 @@ def naturaldatetime(date, include_seconds=False):
|
|||
if days == 0:
|
||||
if hours == 0:
|
||||
if minutes > 0:
|
||||
minutes += float(seconds)/60
|
||||
minutes = float(seconds)/60
|
||||
return ungettext(
|
||||
_('{minutes:.1f} minute{ago}'),
|
||||
_('{minutes:.1f} minutes{ago}'), minutes
|
||||
|
@ -77,7 +77,7 @@ def naturaldatetime(date, include_seconds=False):
|
|||
).format(seconds=seconds, ago=ago)
|
||||
return _('just now')
|
||||
else:
|
||||
hours += float(minutes)/60
|
||||
hours = float(minutes)/60
|
||||
return ungettext(
|
||||
_('{hours:.1f} hour{ago}'),
|
||||
_('{hours:.1f} hours{ago}'), hours
|
||||
|
|
Loading…
Reference in New Issue