Webapps massive refactoring

This commit is contained in:
Marc Aymerich 2015-03-04 21:06:16 +00:00
parent 7c5165f753
commit 12910bf072
82 changed files with 5118 additions and 487 deletions

View File

@ -190,7 +190,6 @@ Multi-tenant WebApps
* forms autocomplete="off", doesn't work in chrome * forms autocomplete="off", doesn't work in chrome
ln -s /proc/self/fd /dev/fd ln -s /proc/self/fd /dev/fd
@ -204,3 +203,6 @@ POST INSTALL
ssh-keygen ssh-keygen
ssh-copy-id root@<server-address> ssh-copy-id root@<server-address>
* symbolicLink webapp (link stuff from other places)

View File

@ -161,6 +161,22 @@ class AccountAdminMixin(object):
account_link.allow_tags = True account_link.allow_tags = True
account_link.admin_order_field = 'account__username' account_link.admin_order_field = 'account__username'
def get_fields(self, request, obj=None):
""" remove account or account_link depending on the case """
fields = super(AccountAdminMixin, self).get_fields(request, obj)
fields = list(fields)
if obj is not None or getattr(self, 'account_id', None):
try:
fields.remove('account')
except ValueError:
pass
else:
try:
fields.remove('account_link')
except ValueError:
pass
return fields
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
""" provide account for filter_by_account_fields """ """ provide account for filter_by_account_fields """
if obj: if obj:

View File

@ -11,6 +11,7 @@ from . import settings
class MySQLBackend(ServiceController): class MySQLBackend(ServiceController):
verbose_name = "MySQL database" verbose_name = "MySQL database"
model = 'databases.Database' model = 'databases.Database'
default_route_match = "database.type == 'mysql'"
def save(self, database): def save(self, database):
if database.type != database.MYSQL: if database.type != database.MYSQL:
@ -53,6 +54,7 @@ class MySQLBackend(ServiceController):
class MySQLUserBackend(ServiceController): class MySQLUserBackend(ServiceController):
verbose_name = "MySQL user" verbose_name = "MySQL user"
model = 'databases.DatabaseUser' model = 'databases.DatabaseUser'
default_route_match = "databaseuser.type == 'mysql'"
def save(self, user): def save(self, user):
if user.type != user.MYSQL: if user.type != user.MYSQL:

View File

@ -37,7 +37,9 @@ class Database(models.Model):
return users.order_by('id').first().databaseuser return users.order_by('id').first().databaseuser
Database.users.through._meta.unique_together = (('database', 'databaseuser'),) Database.users.through._meta.unique_together = (
('database', 'databaseuser'),
)
class DatabaseUser(models.Model): class DatabaseUser(models.Model):

View File

@ -4,6 +4,7 @@ import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.apps.orchestration import ServiceController from orchestra.apps.orchestration import ServiceController
from orchestra.apps.orchestration.models import BackendOperation as Operation
from orchestra.utils.python import AttrDict from orchestra.utils.python import AttrDict
from . import settings from . import settings
@ -75,10 +76,11 @@ class Bind9MasterDomainBackend(ServiceController):
self.append('[[ $UPDATED == 1 ]] && service bind9 reload') self.append('[[ $UPDATED == 1 ]] && service bind9 reload')
def get_servers(self, domain, backend): def get_servers(self, domain, backend):
from orchestra.apps.orchestration.models import Route """ Get related server IPs from registered backend routes """
operation = AttrDict(backend=backend, action='save', instance=domain) from orchestra.apps.orchestration.manager import router
operation = Operation.create(backend_cls=backend, action=Operation.SAVE, instance=domain)
servers = [] servers = []
for server in Route.get_servers(operation): for server in router.get_servers(operation):
servers.append(server.get_ip()) servers.append(server.get_ip())
return servers return servers

View File

@ -96,7 +96,14 @@ class Domain(models.Model):
def render_zone(self): def render_zone(self):
origin = self.origin origin = self.origin
zone = origin.render_records() zone = origin.render_records()
tail = []
for subdomain in origin.get_subdomains(): for subdomain in origin.get_subdomains():
if subdomain.name.startswith('*'):
# This subdomains needs to be rendered last in order to avoid undesired matches
tail.append(subdomain)
else:
zone += subdomain.render_records()
for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True):
zone += subdomain.render_records() zone += subdomain.render_records()
return zone return zone

View File

@ -3,13 +3,12 @@ from django.contrib import admin
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.admin.html import monospace_format from orchestra.admin.html import monospace_format
from orchestra.admin.utils import admin_link, admin_date, admin_colored from orchestra.admin.utils import admin_link, admin_date, admin_colored
from .backends import ServiceBackend from .backends import ServiceBackend
from .models import Server, Route, BackendLog, BackendOperation from .models import Server, Route, BackendLog, BackendOperation
from .widgets import RouteBackendSelect
STATE_COLORS = { STATE_COLORS = {
BackendLog.RECEIVED: 'darkorange', BackendLog.RECEIVED: 'darkorange',
@ -22,18 +21,22 @@ STATE_COLORS = {
} }
class RouteAdmin(admin.ModelAdmin): class RouteAdmin(admin.ModelAdmin):
list_display = [ list_display = [
'id', 'backend', 'host', 'match', 'display_model', 'display_actions', 'id', 'backend', 'host', 'match', 'display_model', 'display_actions',
'is_active' 'is_active'
] ]
list_editable = ['backend', 'host', 'match', 'is_active'] list_editable = ['host', 'match', 'is_active']
list_filter = ['host', 'is_active', 'backend'] list_filter = ['host', 'is_active', 'backend']
BACKEND_HELP_TEXT = { BACKEND_HELP_TEXT = {
backend: "This backend operates over '%s'" % ServiceBackend.get_backend(backend).model backend: "This backend operates over '%s'" % ServiceBackend.get_backend(backend).model
for backend, __ in ServiceBackend.get_plugin_choices() for backend, __ in ServiceBackend.get_plugin_choices()
} }
DEFAULT_MATCH = {
backend.get_name(): backend.default_route_match for backend in ServiceBackend.get_backends(active=False)
}
def display_model(self, route): def display_model(self, route):
try: try:
@ -54,7 +57,7 @@ class RouteAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
""" Provides dynamic help text on backend form field """ """ Provides dynamic help text on backend form field """
if db_field.name == 'backend': if db_field.name == 'backend':
kwargs['widget'] = DynamicHelpTextSelect('this.id', self.BACKEND_HELP_TEXT) kwargs['widget'] = RouteBackendSelect('this.id', self.BACKEND_HELP_TEXT, self.DEFAULT_MATCH)
return super(RouteAdmin, self).formfield_for_dbfield(db_field, **kwargs) return super(RouteAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):

View File

@ -34,6 +34,7 @@ class ServiceBackend(plugins.Plugin):
type = 'task' # 'sync' type = 'task' # 'sync'
ignore_fields = [] ignore_fields = []
actions = [] actions = []
default_route_match = 'True'
__metaclass__ = ServiceMount __metaclass__ = ServiceMount

View File

@ -10,6 +10,7 @@ from .helpers import send_report
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = import_class(settings.ORCHESTRATION_ROUTER)
def as_task(execute): def as_task(execute):
@ -41,17 +42,17 @@ def close_connection(execute):
def execute(operations, async=False): def execute(operations, async=False):
""" generates and executes the operations on the servers """ """ generates and executes the operations on the servers """
router = import_class(settings.ORCHESTRATION_ROUTER)
scripts = {} scripts = {}
cache = {} cache = {}
# Generate scripts per server+backend # Generate scripts per server+backend
for operation in operations: for operation in operations:
logger.debug("Queued %s" % str(operation)) logger.debug("Queued %s" % str(operation))
servers = router.get_servers(operation, cache=cache) if operation.servers is None:
for server in servers: operation.servers = router.get_servers(operation, cache=cache)
for server in operation.servers:
key = (server, operation.backend) key = (server, operation.backend)
if key not in scripts: if key not in scripts:
scripts[key] = (operation.backend(), [operation]) scripts[key] = (operation.backend, [operation])
scripts[key][0].prepare() scripts[key][0].prepare()
else: else:
scripts[key][1].append(operation) scripts[key][1].append(operation)

View File

@ -7,6 +7,7 @@ from django.http.response import HttpResponseServerError
from orchestra.utils.python import OrderedSet from orchestra.utils.python import OrderedSet
from .manager import router
from .backends import ServiceBackend from .backends import ServiceBackend
from .helpers import message_user from .helpers import message_user
from .models import BackendLog from .models import BackendLog
@ -51,6 +52,16 @@ class OperationsMiddleware(object):
return request.pending_operations return request.pending_operations
return set() return set()
@classmethod
def get_route_cache(cls):
""" chache the routes to save sql queries """
if hasattr(cls.thread_locals, 'request'):
request = cls.thread_locals.request
if not hasattr(request, 'route_cache'):
request.route_cache = {}
return request.route_cache
return {}
@classmethod @classmethod
def collect(cls, action, **kwargs): def collect(cls, action, **kwargs):
""" Collects all pending operations derived from model signals """ """ Collects all pending operations derived from model signals """
@ -58,13 +69,14 @@ class OperationsMiddleware(object):
if request is None: if request is None:
return return
pending_operations = cls.get_pending_operations() pending_operations = cls.get_pending_operations()
for backend in ServiceBackend.get_backends(): route_cache = cls.get_route_cache()
for backend_cls in ServiceBackend.get_backends():
# Check if there exists a related instance to be executed for this backend # Check if there exists a related instance to be executed for this backend
instances = [] instances = []
if backend.is_main(kwargs['instance']): if backend_cls.is_main(kwargs['instance']):
instances = [(kwargs['instance'], action)] instances = [(kwargs['instance'], action)]
else: else:
candidate = backend.get_related(kwargs['instance']) candidate = backend_cls.get_related(kwargs['instance'])
if candidate: if candidate:
if candidate.__class__.__name__ == 'ManyRelatedManager': if candidate.__class__.__name__ == 'ManyRelatedManager':
if 'pk_set' in kwargs: if 'pk_set' in kwargs:
@ -76,7 +88,7 @@ class OperationsMiddleware(object):
candidates = [candidate] candidates = [candidate]
for candidate in candidates: for candidate in candidates:
# Check if a delete for candidate is in pending_operations # Check if a delete for candidate is in pending_operations
delete_mock = Operation.create(backend, candidate, Operation.DELETE) delete_mock = Operation.create(backend_cls, candidate, Operation.DELETE)
if delete_mock not in pending_operations: if delete_mock not in pending_operations:
# related objects with backend.model trigger save() # related objects with backend.model trigger save()
instances.append((candidate, Operation.SAVE)) instances.append((candidate, Operation.SAVE))
@ -84,7 +96,7 @@ class OperationsMiddleware(object):
# Maintain consistent state of pending_operations based on save/delete behaviour # Maintain consistent state of pending_operations based on save/delete behaviour
# Prevent creating a deleted instance by deleting existing saves # Prevent creating a deleted instance by deleting existing saves
if iaction == Operation.DELETE: if iaction == Operation.DELETE:
save_mock = Operation.create(backend, instance, Operation.SAVE) save_mock = Operation.create(backend_cls, instance, Operation.SAVE)
try: try:
pending_operations.remove(save_mock) pending_operations.remove(save_mock)
except KeyError: except KeyError:
@ -97,16 +109,22 @@ class OperationsMiddleware(object):
if update_fields != []: if update_fields != []:
execute = False execute = False
for field in update_fields: for field in update_fields:
if field not in backend.ignore_fields: if field not in backend_cls.ignore_fields:
execute = True execute = True
break break
if not execute: if not execute:
continue continue
operation = Operation.create(backend, instance, iaction) operation = Operation.create(backend_cls, instance, iaction)
# Only schedule operations if the router gives servers to execute into
servers = router.get_servers(operation, cache=route_cache)
if servers:
operation.servers = servers
if iaction != Operation.DELETE: if iaction != Operation.DELETE:
# usually we expect to be using last object state, # usually we expect to be using last object state,
# except when we are deleting it # except when we are deleting it
pending_operations.discard(operation) pending_operations.discard(operation)
elif iaction == Operation.DELETE:
operation.preload_context()
pending_operations.add(operation) pending_operations.add(operation)
def process_request(self, request): def process_request(self, request):

View File

@ -11,7 +11,7 @@ from orchestra.core.validators import validate_ip_address, ValidationError
from orchestra.models.fields import NullableCharField from orchestra.models.fields import NullableCharField
#from orchestra.utils.apps import autodiscover #from orchestra.utils.apps import autodiscover
from . import settings, manager from . import settings
from .backends import ServiceBackend from .backends import ServiceBackend
@ -119,36 +119,43 @@ class BackendOperation(models.Model):
def __hash__(self): def __hash__(self):
""" set() """ """ set() """
backend = getattr(self, 'backend', self.backend) backend_cls = type(self.backend)
return hash(backend) + hash(self.instance) + hash(self.action) return hash(backend_cls) + hash(self.instance) + hash(self.action)
def __eq__(self, operation): def __eq__(self, operation):
""" set() """ """ set() """
return hash(self) == hash(operation) return hash(self) == hash(operation)
@classmethod @classmethod
def create(cls, backend, instance, action): def create(cls, backend_cls, instance, action, servers=None):
op = cls(backend=backend.get_name(), instance=instance, action=action) op = cls(backend=backend_cls.get_name(), instance=instance, action=action)
op.backend = backend op.backend = backend_cls()
# instance should maintain any dynamic attribute until backend execution # instance should maintain any dynamic attribute until backend execution
# deep copy is prefered over copy otherwise objects will share same atributes (queryset cache) # deep copy is prefered over copy otherwise objects will share same atributes (queryset cache)
op.instance = copy.deepcopy(instance) op.instance = copy.deepcopy(instance)
if action == cls.DELETE: op.servers = servers
# Heuristic, running get_context will prevent most of related objects do not exist errors
if hasattr(backend, 'get_context'):
backend().get_context(op.instance)
return op return op
@classmethod @classmethod
def execute(cls, operations, async=False): def execute(cls, operations, async=False):
from . import manager
return manager.execute(operations, async=async) return manager.execute(operations, async=async)
@classmethod @classmethod
def execute_action(cls, instance, action): def execute_action(cls, instance, action):
backends = ServiceBackend.get_backends(instance=instance, action=action) backends = ServiceBackend.get_backends(instance=instance, action=action)
operations = [cls.create(backend, instance, action) for backend in backends] operations = [cls.create(backend_cls, instance, action) for backend_cls in backends]
return cls.execute(operations) return cls.execute(operations)
def preload_context(self):
"""
Heuristic
Running get_context will prevent most of related objects do not exist errors
"""
if self.action == self.DELETE:
if hasattr(self.backend, 'get_context'):
self.backend.get_context(op.instance)
def backend_class(self): def backend_class(self):
return ServiceBackend.get_backend(self.backend) return ServiceBackend.get_backend(self.backend)
@ -187,14 +194,14 @@ class Route(models.Model):
def get_servers(cls, operation, **kwargs): def get_servers(cls, operation, **kwargs):
cache = kwargs.get('cache', {}) cache = kwargs.get('cache', {})
servers = [] servers = []
backend = operation.backend backend_cls = type(operation.backend)
key = (backend.get_name(), operation.action) key = (backend_cls.get_name(), operation.action)
try: try:
routes = cache[key] routes = cache[key]
except KeyError: except KeyError:
cache[key] = [] cache[key] = []
for route in cls.objects.filter(is_active=True, backend=backend.get_name()): for route in cls.objects.filter(is_active=True, backend=backend_cls.get_name()):
for action in backend.get_actions(): for action in backend_cls.get_actions():
_key = (route.backend, action) _key = (route.backend, action)
try: try:
cache[_key].append(route) cache[_key].append(route)

View File

@ -0,0 +1,24 @@
import textwrap
from orchestra.forms.widgets import DynamicHelpTextSelect
class RouteBackendSelect(DynamicHelpTextSelect):
""" Updates matches input field based on selected backend """
def __init__(self, target, help_text, route_matches, *args, **kwargs):
kwargs['attrs'] = {
'onfocus': "this.oldvalue = this.value;",
}
self.route_matches = route_matches
super(RouteBackendSelect, self).__init__(target, help_text, *args, **kwargs)
def get_dynamic_help_text(self, target, help_text):
help_text = super(RouteBackendSelect, self).get_dynamic_help_text(target, help_text)
return help_text + textwrap.dedent("""\
routematches = {route_matches};
match = $("#id_match");
if ( this.oldvalue == "" || match.value == routematches[this.oldvalue])
match.value = routematches[this.options[this.selectedIndex].value];
this.oldvalue = this.value;
""".format(route_matches=self.route_matches)
)

View File

@ -33,25 +33,30 @@ class PaymentSource(models.Model):
def method_class(self): def method_class(self):
return PaymentMethod.get_plugin(self.method) return PaymentMethod.get_plugin(self.method)
@cached_property
def service_instance(self):
""" Per request lived method_instance """
return self.method_class()
@cached_property @cached_property
def label(self): def label(self):
return self.method_class().get_label(self.data) return self.method_instance.get_label(self.data)
@cached_property @cached_property
def number(self): def number(self):
return self.method_class().get_number(self.data) return self.method_instance.get_number(self.data)
def get_bill_context(self): def get_bill_context(self):
method = self.method_class() method = self.method_instance
return { return {
'message': method.get_bill_message(self), 'message': method.get_bill_message(self),
} }
def get_due_delta(self): def get_due_delta(self):
return self.method_class().due_delta return self.method_instance.due_delta
def clean(self): def clean(self):
self.data = self.method_class().clean_data(self.data) self.data = self.method_instance.clean_data(self.data)
class TransactionQuerySet(models.QuerySet): class TransactionQuerySet(models.QuerySet):

View File

@ -13,6 +13,7 @@ class SaaSAdmin(SelectPluginAdminMixin, AccountAdminMixin, admin.ModelAdmin):
list_filter = ('service',) list_filter = ('service',)
plugin = SoftwareService plugin = SoftwareService
plugin_field = 'service' plugin_field = 'service'
plugin_title = 'Software as a Service'
def display_site_name(self, saas): def display_site_name(self, saas):
site_name = saas.get_site_name() site_name = saas.get_site_name()
@ -21,14 +22,5 @@ class SaaSAdmin(SelectPluginAdminMixin, AccountAdminMixin, admin.ModelAdmin):
display_site_name.allow_tags = True display_site_name.allow_tags = True
display_site_name.admin_order_field = 'site_name' display_site_name.admin_order_field = 'site_name'
def get_fields(self, request, obj=None):
fields = super(SaaSAdmin, self).get_fields(request, obj)
fields = list(fields)
# TODO do it in AccountAdminMixin?
if obj is not None:
fields.remove('account')
else:
fields.remove('account_link')
return fields
admin.site.register(SaaS, SaaSAdmin) admin.site.register(SaaS, SaaSAdmin)

View File

@ -35,11 +35,16 @@ class SaaS(models.Model):
def service_class(self): def service_class(self):
return SoftwareService.get_plugin(self.service) return SoftwareService.get_plugin(self.service)
@cached_property
def service_instance(self):
""" Per request lived service_instance """
return self.service_class()
def get_site_name(self): def get_site_name(self):
return self.service_class().get_site_name(self) return self.service_instance.get_site_name(self)
def clean(self): def clean(self):
self.data = self.service_class().clean_data(self) self.data = self.service_instance.clean_data(self)
def set_password(self, password): def set_password(self, password):
self.password = password self.password = password

View File

@ -22,7 +22,7 @@ class BSCWService(SoftwareService):
verbose_name = "BSCW" verbose_name = "BSCW"
form = BSCWForm form = BSCWForm
serializer = BSCWDataSerializer serializer = BSCWDataSerializer
icon = 'saas/icons/BSCW.png' icon = 'orchestra/icons/apps/BSCW.png'
# TODO override from settings # TODO override from settings
site_name = 'bascw.orchestra.lan' site_name = 'bascw.orchestra.lan'
change_readonly_fileds = ('email',) change_readonly_fileds = ('email',)

View File

@ -3,4 +3,4 @@ from .options import SoftwareService
class GitLabService(SoftwareService): class GitLabService(SoftwareService):
verbose_name = "GitLab" verbose_name = "GitLab"
icon = 'saas/icons/gitlab.png' icon = 'orchestra/icons/apps/gitlab.png'

View File

@ -17,4 +17,4 @@ class MoodleService(SoftwareService):
verbose_name = "Moodle" verbose_name = "Moodle"
form = MoodleForm form = MoodleForm
description_field = 'site_name' description_field = 'site_name'
icon = 'saas/icons/Moodle.png' icon = 'orchestra/icons/apps/Moodle.png'

View File

@ -29,12 +29,6 @@ class SoftwareServiceForm(PluginDataForm):
super(SoftwareServiceForm, self).__init__(*args, **kwargs) super(SoftwareServiceForm, self).__init__(*args, **kwargs)
self.is_change = bool(self.instance and self.instance.pk) self.is_change = bool(self.instance and self.instance.pk)
if self.is_change: if self.is_change:
for field in self.plugin.change_readonly_fileds + ('username',):
value = getattr(self.instance, field, None) or self.instance.data[field]
self.fields[field].required = False
self.fields[field].widget = widgets.ReadOnlyWidget(value)
self.fields[field].help_text = None
site_name = self.instance.get_site_name() site_name = self.instance.get_site_name()
self.fields['password1'].required = False self.fields['password1'].required = False
self.fields['password1'].widget = forms.HiddenInput() self.fields['password1'].widget = forms.HiddenInput()
@ -79,7 +73,7 @@ class SoftwareService(plugins.Plugin):
site_name = None site_name = None
site_name_base_domain = 'orchestra.lan' site_name_base_domain = 'orchestra.lan'
icon = 'orchestra/icons/apps.png' icon = 'orchestra/icons/apps.png'
change_readonly_fileds = () change_readonly_fileds = ('username',)
class_verbose_name = _("Software as a Service") class_verbose_name = _("Software as a Service")
@classmethod @classmethod
@ -98,6 +92,10 @@ class SoftwareService(plugins.Plugin):
raise ValidationError(serializer.errors) raise ValidationError(serializer.errors)
return serializer.data return serializer.data
@classmethod
def get_change_readonly_fileds(cls):
return cls.change_readonly_fileds + ('username',)
def get_site_name(self, saas): def get_site_name(self, saas):
return self.site_name or '.'.join((saas.site_name, self.site_name_base_domain)) return self.site_name or '.'.join((saas.site_name, self.site_name_base_domain))

View File

@ -11,4 +11,4 @@ class PHPListForm(SoftwareServiceForm):
class PHPListService(SoftwareService): class PHPListService(SoftwareService):
verbose_name = "phpList" verbose_name = "phpList"
form = PHPListForm form = PHPListForm
icon = 'saas/icons/Phplist.png' icon = 'orchestra/icons/apps/Phplist.png'

View File

@ -115,7 +115,7 @@ class FTPTraffic(ServiceMonitor):
USERNAME="$3" USERNAME="$3"
LOG_FILE="$4" LOG_FILE="$4"
{ {
grep "UPLOAD\|DOWNLOAD" ${LOG_FILE} \\ grep " bytes, " ${LOG_FILE} \\
| grep " \\[${USERNAME}\\] " \\ | grep " \\[${USERNAME}\\] " \\
| awk -v ini="${INI_DATE}" -v end="${END_DATE}" ' | awk -v ini="${INI_DATE}" -v end="${END_DATE}" '
BEGIN { BEGIN {
@ -135,7 +135,7 @@ class FTPTraffic(ServiceMonitor):
} { } {
# Fri Jul 1 13:23:17 2014 # Fri Jul 1 13:23:17 2014
split($4, time, ":") split($4, time, ":")
day = sprintf("%02d", $3) day = sprintf("%%02d", $3)
# line_date = year month day hour minute second # line_date = year month day hour minute second
line_date = $5 months[$2] day time[1] time[2] time[3] line_date = $5 months[$2] day time[1] time[2] time[3]
if ( line_date > ini && line_date < end) { if ( line_date > ini && line_date < end) {

View File

@ -7,6 +7,7 @@ from orchestra.forms import UserCreationForm, UserChangeForm
from . import settings from . import settings
from .models import SystemUser from .models import SystemUser
class SystemUserFormMixin(object): class SystemUserFormMixin(object):
MOCK_USERNAME = '<username>' MOCK_USERNAME = '<username>'

View File

@ -7,8 +7,10 @@ from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
from orchestra.apps.accounts.admin import AccountAdminMixin from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.plugins.admin import SelectPluginAdminMixin
from . import settings from . import settings, options
from .applications import App
from .models import WebApp, WebAppOption from .models import WebApp, WebAppOption
@ -17,8 +19,7 @@ class WebAppOptionInline(admin.TabularInline):
extra = 1 extra = 1
OPTIONS_HELP_TEXT = { OPTIONS_HELP_TEXT = {
k: str(unicode(v[1])) if len(v) == 3 else '' op.name: str(unicode(op.help_text)) for op in options.get_enabled().values()
for k, v in settings.WEBAPPS_OPTIONS.iteritems()
} }
class Media: class Media:
@ -30,6 +31,12 @@ class WebAppOptionInline(admin.TabularInline):
if db_field.name == 'value': if db_field.name == 'value':
kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
if db_field.name == 'name': if db_field.name == 'name':
if self.parent_object:
plugin = self.parent_object.type_class
else:
request = kwargs['request']
plugin = App.get_plugin(request.GET['type'])
kwargs['choices'] = plugin.get_options_choices()
# Help text based on select widget # Help text based on select widget
kwargs['widget'] = DynamicHelpTextSelect( kwargs['widget'] = DynamicHelpTextSelect(
'this.id.replace("name", "value")', self.OPTIONS_HELP_TEXT 'this.id.replace("name", "value")', self.OPTIONS_HELP_TEXT
@ -37,20 +44,22 @@ class WebAppOptionInline(admin.TabularInline):
return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs) return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs)
class WebAppAdmin(AccountAdminMixin, ExtendedModelAdmin): class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'type', 'display_websites', 'account_link') list_display = ('name', 'type', 'display_websites', 'account_link')
list_filter = ('type',) list_filter = ('type',)
add_fields = ('account', 'name', 'type') # add_fields = ('account', 'name', 'type')
fields = ('account_link', 'name', 'type') # fields = ('account_link', 'name', 'type')
inlines = [WebAppOptionInline] inlines = [WebAppOptionInline]
readonly_fields = ('account_link',) readonly_fields = ('account_link',)
change_readonly_fields = ('name', 'type') change_readonly_fields = ('name', 'type')
list_prefetch_related = ('content_set__website',) list_prefetch_related = ('content_set__website',)
plugin = App
plugin_field = 'type'
plugin_title = _("Web application type")
TYPE_HELP_TEXT = { # TYPE_HELP_TEXT = {
k: str(unicode(v.get('help_text', ''))) # app.get_name(): str(unicode(app.help_text)) for app in App.get_plugins()
for k, v in settings.WEBAPPS_TYPES.iteritems() # }
}
def display_websites(self, webapp): def display_websites(self, webapp):
websites = [] websites = []
@ -68,14 +77,14 @@ class WebAppAdmin(AccountAdminMixin, ExtendedModelAdmin):
display_websites.short_description = _("web sites") display_websites.short_description = _("web sites")
display_websites.allow_tags = True display_websites.allow_tags = True
def formfield_for_dbfield(self, db_field, **kwargs): # def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """ # """ Make value input widget bigger """
if db_field.name == 'type': # if db_field.name == 'type':
# Help text based on select widget # # Help text based on select widget
kwargs['widget'] = DynamicHelpTextSelect( # kwargs['widget'] = DynamicHelpTextSelect(
'this.id.replace("name", "value")', self.TYPE_HELP_TEXT # 'this.id.replace("name", "value")', self.TYPE_HELP_TEXT
) # )
kwargs['help_text'] = self.TYPE_HELP_TEXT.get(db_field.default, '') # kwargs['help_text'] = self.TYPE_HELP_TEXT.get(db_field.default, '')
return super(WebAppAdmin, self).formfield_for_dbfield(db_field, **kwargs) # return super(WebAppAdmin, self).formfield_for_dbfield(db_field, **kwargs)
admin.site.register(WebApp, WebAppAdmin) admin.site.register(WebApp, WebAppAdmin)

View File

@ -12,16 +12,8 @@ class WebAppServiceMixin(object):
return settings.WEBAPPS_TYPES[webapp.type]['directive'][0] == self.directive return settings.WEBAPPS_TYPES[webapp.type]['directive'][0] == self.directive
def create_webapp_dir(self, context): def create_webapp_dir(self, context):
self.append(textwrap.dedent(""" self.append("mkdir -p %(app_path)s" % context)
path="" self.append("chown %(user)s:%(group)s %(app_path)s" % context)
for dir in $(echo %(app_path)s | tr "/" "\n"); do
path="${path}/${dir}"
[ -d $path ] || {
mkdir "${path}"
chown %(user)s:%(group)s "${path}"
}
done
""" % context))
def get_php_init_vars(self, webapp, per_account=False): def get_php_init_vars(self, webapp, per_account=False):
""" """
@ -54,7 +46,7 @@ class WebAppServiceMixin(object):
return { return {
'user': webapp.get_username(), 'user': webapp.get_username(),
'group': webapp.get_groupname(), 'group': webapp.get_groupname(),
'app_name': webapp.get_name(), 'app_name': webapp.name,
'type': webapp.type, 'type': webapp.type,
'app_path': webapp.get_path().rstrip('/'), 'app_path': webapp.get_path().rstrip('/'),
'banner': self.get_banner(), 'banner': self.get_banner(),

View File

@ -13,6 +13,7 @@ class PHPFcgidBackend(WebAppServiceMixin, ServiceController):
""" Per-webapp fcgid application """ """ Per-webapp fcgid application """
verbose_name = _("PHP-Fcgid") verbose_name = _("PHP-Fcgid")
directive = 'fcgi' directive = 'fcgi'
default_route_match = "webapp.type.endswith('-fcgi')"
def save(self, webapp): def save(self, webapp):
if not self.valid_directive(webapp): if not self.valid_directive(webapp):

View File

@ -14,6 +14,7 @@ class PHPFPMBackend(WebAppServiceMixin, ServiceController):
""" Per-webapp php application """ """ Per-webapp php application """
verbose_name = _("PHP-FPM") verbose_name = _("PHP-FPM")
directive = 'fpm' directive = 'fpm'
default_route_match = "webapp.type.endswith('-fpm')"
def save(self, webapp): def save(self, webapp):
if not self.valid_directive(webapp): if not self.valid_directive(webapp):
@ -26,7 +27,8 @@ class PHPFPMBackend(WebAppServiceMixin, ServiceController):
} || { } || {
echo -e '%(fpm_config)s' > %(fpm_path)s echo -e '%(fpm_config)s' > %(fpm_path)s
UPDATEDFPM=1 UPDATEDFPM=1
}""" % context)) }""" % context
))
def delete(self, webapp): def delete(self, webapp):
if not self.valid_directive(webapp): if not self.valid_directive(webapp):

View File

@ -8,6 +8,7 @@ from . import WebAppServiceMixin
class StaticBackend(WebAppServiceMixin, ServiceController): class StaticBackend(WebAppServiceMixin, ServiceController):
verbose_name = _("Static") verbose_name = _("Static")
directive = 'static' directive = 'static'
default_route_match = "webapp.type == 'static'"
def save(self, webapp): def save(self, webapp):
if not self.valid_directive(webapp): if not self.valid_directive(webapp):

View File

@ -0,0 +1,27 @@
from django.utils.translation import ugettext_lazy as _
from orchestra.apps.orchestration import ServiceController
from . import WebAppServiceMixin
class SymbolicLinkBackend(WebAppServiceMixin, ServiceController):
verbose_name = _("Symbolic link webapp")
model = 'webapps.WebApp'
default_route_match = "webapp.type == 'symbolic-link'"
def save(self, webapp):
context = self.get_context(webapp)
self.append("ln -s '%(link_path)s' %(app_path)s" % context)
self.append("chown -h %(user)s:%(group)s %(app_path)s" % context)
def delete(self, webapp):
context = self.get_context(webapp)
self.delete_webapp_dir(context)
def get_context(self, webapp):
context = super(SymbolicLinkBackend, self).get_context(webapp)
context.update({
'link_path': webapp.data['path'],
})
return context

View File

@ -0,0 +1,48 @@
import textwrap
from django.utils.translation import ugettext_lazy as _
from orchestra.apps.orchestration import ServiceController
from .. import settings
from . import WebAppServiceMixin
class WordPressBackend(WebAppServiceMixin, ServiceController):
verbose_name = _("Wordpress")
model = 'webapps.WebApp'
default_route_match = "webapp.type == 'wordpress'"
def save(self, webapp):
context = self.get_context(webapp)
self.create_webapp_dir(context)
self.append(textwrap.dedent("""\
# Check if directory is empty befor doing anything
if [[ ! $(ls -A %(app_path)s) ]]; then
wget http://wordpress.org/latest.tar.gz -O - --no-check-certificate \\
| tar -xzvf - -C %(app_path)s --strip-components=1
cp %(app_path)s/wp-config-sample.php %(app_path)s/wp-config.php
sed -i "s/database_name_here/%(db_name)s/" %(app_path)s/wp-config.php
sed -i "s/username_here/%(db_user)s/" %(app_path)s/wp-config.php
sed -i "s/password_here/%(db_pass)s/" %(app_path)s/wp-config.php
sed -i "s/localhost/%(db_host)s/" %(app_path)s/wp-config.php
mkdir %(app_path)s/wp-content/uploads
chmod 750 %(app_path)s/wp-content/uploads
chown -R %(user)s:%(group)s %(app_path)s
fi""" % context
))
def delete(self, webapp):
context = self.get_context(webapp)
self.delete_webapp_dir(context)
def get_context(self, webapp):
context = super(WordPressBackend, self).get_context(webapp)
context.update({
'db_name': webapp.data['db_name'],
'db_user': webapp.data['db_user'],
'db_pass': webapp.data['db_pass'],
'db_host': settings.WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST,
})
return context

View File

@ -11,6 +11,7 @@ from .. import settings
class WordpressMuBackend(ServiceController): class WordpressMuBackend(ServiceController):
verbose_name = _("Wordpress multisite") verbose_name = _("Wordpress multisite")
model = 'webapps.WebApp' model = 'webapps.WebApp'
default_route_match = "webapp.type == 'wordpress-mu'"
@property @property
def script(self): def script(self):
@ -85,7 +86,7 @@ class WordpressMuBackend(ServiceController):
self.validate_response(response) self.validate_response(response)
def delete_blog(self, webapp, server): def delete_blog(self, webapp, server):
# OH, I've enjoied so much coding this methods that I want to thanks # OH, I've enjoied so much coding this methods that I want to thank
# the wordpress team for the excellent software they are producing # the wordpress team for the excellent software they are producing
session = requests.Session() session = requests.Session()
self.login(session) self.login(session)

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import orchestra.core.validators
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='WebApp',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=128, verbose_name='name', validators=[orchestra.core.validators.validate_name])),
('type', models.CharField(max_length=32, verbose_name='type', choices=[(b'dokuwiki-mu', b'DokuWiki (SaaS)'), (b'drupal-mu', b'Drupdal (SaaS)'), (b'php4-fcgi', b'PHP 4 FCGI'), (b'php5.2-fcgi', b'PHP 5.2 FCGI'), (b'php5.5-fpm', b'PHP 5.5 FPM'), (b'static', b'Static'), (b'symlink', b'Symbolic link'), (b'webalizer', b'Webalizer'), (b'wordpress', b'WordPress'), (b'wordpress-mu', b'WordPress (SaaS)')])),
('account', models.ForeignKey(related_name='webapps', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Web App',
'verbose_name_plural': 'Web Apps',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='WebAppOption',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=128, verbose_name='name', choices=[(b'PHP-allow_url_fopen', 'PHP - allow_url_fopen'), (b'PHP-allow_url_include', 'PHP - Allow URL include'), (b'PHP-auto_append_file', 'PHP - Auto append file'), (b'PHP-auto_prepend_file', 'PHP - Auto prepend file'), (b'PHP-date.timezone', 'PHP - date.timezone'), (b'PHP-default_socket_timeout', 'PHP - Default socket timeout'), (b'PHP-display_errors', 'PHP - Display errors'), (b'PHP-extension', 'PHP - Extension'), (b'PHP-magic_quotes_gpc', 'PHP - Magic quotes GPC'), (b'PHP-magic_quotes_runtime', 'PHP - Magic quotes runtime'), (b'PHP-magic_quotes_sybase', 'PHP - Magic quotes sybase'), (b'PHP-max_execution_time', 'PHP - Max execution time'), (b'PHP-max_input_time', 'PHP - Max input time'), (b'PHP-max_input_vars', 'PHP - Max input vars'), (b'PHP-memory_limit', 'PHP - Memory limit'), (b'PHP-mysql.connect_timeout', 'PHP - Mysql connect timeout'), (b'PHP-output_buffering', 'PHP - output_buffering'), (b'PHP-post_max_size', 'PHP - Post max size'), (b'PHP-register_globals', 'PHP - Register globals'), (b'PHP-safe_mode', 'PHP - Safe mode'), (b'PHP-sendmail_path', 'PHP - sendmail_path'), (b'PHP-session.auto_start', 'PHP - session.auto_start'), (b'PHP-session.bug_compat_warn', 'PHP - session.bug_compat_warn'), (b'PHP-suhosin.executor.include.whitelist', 'PHP - suhosin.executor.include.whitelist'), (b'PHP-suhosin.get.max_vars', 'PHP - Suhosin GET max vars'), (b'PHP-suhosin.post.max_vars', 'PHP - Suhosin POST max vars'), (b'PHP-suhosin.request.max_vars', 'PHP - Suhosin request max vars'), (b'PHP-suhosin.session.encrypt', 'PHP - suhosin.session.encrypt'), (b'PHP-suhosin.simulation', 'PHP - Suhosin simulation'), (b'PHP-upload_max_filesize', 'PHP - upload_max_filesize'), (b'PHP-zend_extension', 'PHP - zend_extension'), (b'php-enabled_functions', 'PHP - Enabled functions'), (b'processes', 'Number of processes'), (b'public-root', 'Public root'), (b'timeout', 'Process timeout')])),
('value', models.CharField(max_length=256, verbose_name='value')),
('webapp', models.ForeignKey(related_name='options', verbose_name='Web application', to='webapps.WebApp')),
],
options={
'verbose_name': 'option',
'verbose_name_plural': 'options',
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='webappoption',
unique_together=set([('webapp', 'name')]),
),
migrations.AlterUniqueTogether(
name='webapp',
unique_together=set([('name', 'account')]),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('webapps', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='webapp',
name='data',
field=jsonfield.fields.JSONField(default={}, help_text='Extra information dependent of each service.', verbose_name='data'),
preserve_default=False,
),
]

View File

@ -2,23 +2,25 @@ import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from jsonfield import JSONField
from orchestra.core import validators, services from orchestra.core import validators, services
from orchestra.utils import tuple_setting_to_choices, dict_setting_to_choices
from orchestra.utils.functional import cached from orchestra.utils.functional import cached
from . import settings from . import settings, options
from .types import AppType
class WebApp(models.Model): class WebApp(models.Model):
""" Represents a web application """ """ Represents a web application """
name = models.CharField(_("name"), max_length=128, validators=[validators.validate_name]) name = models.CharField(_("name"), max_length=128, validators=[validators.validate_name])
type = models.CharField(_("type"), max_length=32, type = models.CharField(_("type"), max_length=32,
choices=dict_setting_to_choices(settings.WEBAPPS_TYPES), choices=AppType.get_plugin_choices())
default=settings.WEBAPPS_DEFAULT_TYPE)
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='webapps') related_name='webapps')
data = JSONField(_("data"), help_text=_("Extra information dependent of each service."))
class Meta: class Meta:
unique_together = ('name', 'account') unique_together = ('name', 'account')
@ -31,21 +33,25 @@ class WebApp(models.Model):
def get_description(self): def get_description(self):
return self.get_type_display() return self.get_type_display()
@cached_property
def type_class(self):
return AppType.get_plugin(self.type)
@cached_property
def type_instance(self):
""" Per request lived type_instance """
return self.type_class()
def clean(self): def clean(self):
# Validate unique webapp names apptype = self.type_instance
if self.app_type.get('unique_name', False): apptype.validate(self)
try: self.data = apptype.clean_data(self)
webapp = WebApp.objects.exclude(id=self.pk).get(name=self.name, type=self.type)
except WebApp.DoesNotExist:
pass
else:
raise ValidationError({
'name': _("A webapp with this name already exists."),
})
@cached @cached
def get_options(self): def get_options(self):
return { opt.name: opt.value for opt in self.options.all() } return {
opt.name: opt.value for opt in self.options.all()
}
@property @property
def app_type(self): def app_type(self):
@ -81,7 +87,7 @@ class WebAppOption(models.Model):
webapp = models.ForeignKey(WebApp, verbose_name=_("Web application"), webapp = models.ForeignKey(WebApp, verbose_name=_("Web application"),
related_name='options') related_name='options')
name = models.CharField(_("name"), max_length=128, name = models.CharField(_("name"), max_length=128,
choices=tuple_setting_to_choices(settings.WEBAPPS_OPTIONS)) choices=((op.name, op.verbose_name) for op in options.get_enabled().values()))
value = models.CharField(_("value"), max_length=256) value = models.CharField(_("value"), max_length=256)
class Meta: class Meta:
@ -93,16 +99,24 @@ class WebAppOption(models.Model):
return self.name return self.name
def clean(self): def clean(self):
""" validates name and value according to WEBAPPS_OPTIONS """ option = options.get_enabled()[self.name]
regex = settings.WEBAPPS_OPTIONS[self.name][-1] option.validate(self)
if not re.match(regex, self.value):
raise ValidationError({
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
params={
'value': self.value,
'regex': regex
}),
})
services.register(WebApp) services.register(WebApp)
# Admin bulk deletion doesn't call model.delete(), we use signals instead of model method overriding
from django.db.models.signals import pre_save, pre_delete
from django.dispatch import receiver
@receiver(pre_save, sender=WebApp, dispatch_uid='webapps.type.save')
def type_save(sender, *args, **kwargs):
instance = kwargs['instance']
instance.type_instance.save(instance)
@receiver(pre_delete, sender=WebApp, dispatch_uid='webapps.type.delete')
def type_delete(sender, *args, **kwargs):
instance = kwargs['instance']
instance.type_instance.delete(instance)

View File

@ -0,0 +1,300 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from orchestra.utils.python import import_class
from . import settings
class AppOption(object):
def __init__(self, name, *args, **kwargs):
self.name = name
self.verbose_name = kwargs.pop('verbose_name', name)
self.help_text = kwargs.pop('help_text', '')
for k,v in kwargs.iteritems():
setattr(self, k, v)
def validate(self, webapp):
if self.regex and not re.match(self.regex, webapp.value):
raise ValidationError({
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
params={
'value': webapp.value,
'regex': self.regex
}),
})
public_root = AppOption('public-root',
verbose_name=_("Public root"),
help_text=_("Document root relative to webapps/&lt;webapp&gt;/"),
regex=r'[^ ]+'
)
timeout = AppOption('timeout',
# FCGID FcgidIOTimeout
# FPM pm.request_terminate_timeout
# PHP max_execution_time ini
verbose_name=_("Process timeout"),
help_text=_("Maximum time in seconds allowed for a request to complete (a number between 0 and 999)."),
regex=r'^[0-9]{1,3}$',
)
processes = AppOption('processes',
# FCGID MaxProcesses
# FPM pm.max_children
verbose_name=_("Number of processes"),
help_text=_("Maximum number of children that can be alive at the same time (a number between 0 and 9)."),
regex=r'^[0-9]$',
)
php_enabled_functions = AppOption('php-enabled_functions',
verbose_name=_("Enabled functions"),
help_text = ' '.join(settings.WEBAPPS_PHP_DISABLED_FUNCTIONS),
regex=r'^[\w\.,-]+$'
)
php_allow_url_include = AppOption('PHP-allow_url_include',
verbose_name=_("Allow URL include"),
help_text=_("Allows the use of URL-aware fopen wrappers with include, include_once, require, "
"require_once (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_allow_url_fopen = AppOption('PHP-allow_url_fopen',
verbose_name=_("Allow URL fopen"),
help_text=_("Enables the URL-aware fopen wrappers that enable accessing URL object like files (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_auto_append_file = AppOption('PHP-auto_append_file',
verbose_name=_("Auto append file"),
help_text=_("Specifies the name of a file that is automatically parsed after the main file."),
regex=r'^[\w\.,-/]+$'
)
php_auto_prepend_file = AppOption('PHP-auto_prepend_file',
verbose_name=_("Auto prepend file"),
help_text=_("Specifies the name of a file that is automatically parsed before the main file."),
regex=r'^[\w\.,-/]+$'
)
php_date_timezone = AppOption('PHP-date.timezone',
verbose_name=_("date.timezone"),
help_text=_("Sets the default timezone used by all date/time functions (Timezone string 'Europe/London')."),
regex=r'^\w+/\w+$'
)
php_default_socket_timeout = AppOption('PHP-default_socket_timeout',
verbose_name=_("Default socket timeout"),
help_text=_("Number between 0 and 999."),
regex=r'^[0-9]{1,3}$'
)
php_display_errors = AppOption('PHP-display_errors',
verbose_name=_("Display errors"),
help_text=_("Determines whether errors should be printed to the screen as part of the output or "
"if they should be hidden from the user (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_extension = AppOption('PHP-extension',
verbose_name=_("Extension"),
regex=r'^[^ ]+$'
)
php_magic_quotes_gpc = AppOption('PHP-magic_quotes_gpc',
verbose_name=_("Magic quotes GPC"),
help_text=_("Sets the magic_quotes state for GPC (Get/Post/Cookie) operations (On or Off) "
"<b>DEPRECATED as of PHP 5.3.0</b>."),
regex=r'^(On|Off|on|off)$',
deprecated=5.3
)
php_magic_quotes_runtime = AppOption('PHP-magic_quotes_runtime',
verbose_name=_("Magic quotes runtime"),
help_text=_("Functions that return data from any sort of external source will have quotes escaped "
"with a backslash (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>."),
regex=r'^(On|Off|on|off)$',
deprecated=5.3
)
php_magic_quotes_sybase = AppOption('PHP-magic_quotes_sybase',
verbose_name=_("Magic quotes sybase"),
help_text=_("Single-quote is escaped with a single-quote instead of a backslash (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_max_execution_time = AppOption('PHP-max_execution_time',
verbose_name=_("Max execution time"),
help_text=_("Maximum time in seconds a script is allowed to run before it is terminated by "
"the parser (Integer between 0 and 999)."),
regex=r'^[0-9]{1,3}$'
)
php_max_input_time = AppOption('PHP-max_input_time',
verbose_name=_("Max input time"),
help_text=_("Maximum time in seconds a script is allowed to parse input data, like POST and GET "
"(Integer between 0 and 999)."),
regex=r'^[0-9]{1,3}$'
)
php_max_input_vars = AppOption('PHP-max_input_vars',
verbose_name=_("Max input vars"),
help_text=_("How many input variables may be accepted (limit is applied to $_GET, $_POST "
"and $_COOKIE superglobal separately) (Integer between 0 and 9999)."),
regex=r'^[0-9]{1,4}$'
)
php_memory_limit = AppOption('PHP-memory_limit',
verbose_name=_("Memory limit"),
help_text=_("This sets the maximum amount of memory in bytes that a script is allowed to allocate "
"(Value between 0M and 999M)."),
regex=r'^[0-9]{1,3}M$'
)
php_mysql_connect_timeout = AppOption('PHP-mysql.connect_timeout',
verbose_name=_("Mysql connect timeout"),
help_text=_("Number between 0 and 999."),
regex=r'^([0-9]){1,3}$'
)
php_output_buffering = AppOption('PHP-output_buffering',
verbose_name=_("Output buffering"),
help_text=_("Turn on output buffering (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_register_globals = AppOption('PHP-register_globals',
verbose_name=_("Register globals"),
help_text=_("Whether or not to register the EGPCS (Environment, GET, POST, Cookie, Server) "
"variables as global variables (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_post_max_size = AppOption('PHP-post_max_size',
verbose_name=_("Post max size"),
help_text=_("Sets max size of post data allowed (Value between 0M and 999M)."),
regex=r'^[0-9]{1,3}M$'
)
php_sendmail_path = AppOption('PHP-sendmail_path',
verbose_name=_("sendmail_path"),
help_text=_("Where the sendmail program can be found."),
regex=r'^[^ ]+$'
)
php_session_bug_compat_warn = AppOption('PHP-session.bug_compat_warn',
verbose_name=_("session.bug_compat_warn"),
help_text=_("Enables an PHP bug on session initialization for legacy behaviour (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_session_auto_start = AppOption('PHP-session.auto_start',
verbose_name=_("session.auto_start"),
help_text=_("Specifies whether the session module starts a session automatically on request "
"startup (On or Off)."),
regex=r'^(On|Off|on|off)$'
)
php_safe_mode = AppOption('PHP-safe_mode',
verbose_name=_("Safe mode"),
help_text=_("Whether to enable PHP's safe mode (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>"),
regex=r'^(On|Off|on|off)$',
deprecated=5.3
)
php_suhosin_post_max_vars = AppOption('PHP-suhosin.post.max_vars',
verbose_name=_("Suhosin POST max vars"),
help_text=_("Number between 0 and 9999."),
regex=r'^[0-9]{1,4}$'
)
php_suhosin_get_max_vars = AppOption('PHP-suhosin.get.max_vars',
verbose_name=_("Suhosin GET max vars"),
help_text=_("Number between 0 and 9999."),
regex=r'^[0-9]{1,4}$'
)
php_suhosin_request_max_vars = AppOption('PHP-suhosin.request.max_vars',
verbose_name=_("Suhosin request max vars"),
help_text=_("Number between 0 and 9999."),
regex=r'^[0-9]{1,4}$'
)
php_suhosin_session_encrypt = AppOption('PHP-suhosin.session.encrypt',
verbose_name=_("suhosin.session.encrypt"),
help_text=_("On or Off"),
regex=r'^(On|Off|on|off)$'
)
php_suhosin_simulation = AppOption('PHP-suhosin.simulation',
verbose_name=_("Suhosin simulation"),
help_text=_("On or Off"),
regex=r'^(On|Off|on|off)$'
)
php_suhosin_executor_include_whitelist = AppOption('PHP-suhosin.executor.include.whitelist',
verbose_name=_("suhosin.executor.include.whitelist"),
regex=r'.*$'
)
php_upload_max_filesize = AppOption('PHP-upload_max_filesize',
verbose_name=_("upload_max_filesize"),
help_text=_("Value between 0M and 999M."),
regex=r'^[0-9]{1,3}M$'
)
php_zend_extension = AppOption('PHP-post_max_size',
verbose_name=_("zend_extension"),
regex=r'^[^ ]+$'
)
filesystem = [
public_root,
]
process = [
timeout,
processes,
]
php = [
php_enabled_functions,
php_allow_url_include,
php_allow_url_fopen,
php_auto_append_file,
php_auto_prepend_file,
php_date_timezone,
php_default_socket_timeout,
php_display_errors,
php_extension,
php_magic_quotes_gpc,
php_magic_quotes_runtime,
php_magic_quotes_sybase,
php_max_execution_time,
php_max_input_time,
php_max_input_vars,
php_memory_limit,
php_mysql_connect_timeout,
php_output_buffering,
php_register_globals,
php_post_max_size,
php_sendmail_path,
php_session_bug_compat_warn,
php_session_auto_start,
php_safe_mode,
php_suhosin_post_max_vars,
php_suhosin_get_max_vars,
php_suhosin_request_max_vars,
php_suhosin_session_encrypt,
php_suhosin_simulation,
php_suhosin_executor_include_whitelist,
php_upload_max_filesize,
php_zend_extension,
]
_enabled = None
def get_enabled():
global _enabled
if _enabled is None:
from . import settings
_enabled = {}
for op in settings.WEBAPPS_ENABLED_OPTIONS:
op = import_class(op)
_enabled[op.name] = op
return _enabled

View File

@ -21,67 +21,26 @@ WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH',
'/home/httpd/fcgid/%(user)s/%(app_name)s-wrapper') '/home/httpd/fcgid/%(user)s/%(app_name)s-wrapper')
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', { WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', (
'php5.5': { 'orchestra.apps.webapps.types.Php55App',
'verbose_name': "PHP 5.5 fpm", 'orchestra.apps.webapps.types.Php52App',
# 'fpm', ('unix:/var/run/%(user)s-%(app_name)s.sock|fcgi://127.0.0.1%(app_path)s',), 'orchestra.apps.webapps.types.Php4App',
'directive': ('fpm', 'fcgi://{}%(app_path)s'.format(WEBAPPS_FPM_LISTEN)), 'orchestra.apps.webapps.types.StaticApp',
'help_text': _("This creates a PHP5.5 application under ~/webapps/&lt;app_name&gt;<br>" 'orchestra.apps.webapps.types.WebalizerApp',
"PHP-FPM will be used to execute PHP files.") 'orchestra.apps.webapps.types.WordPressMuApp',
}, 'orchestra.apps.webapps.types.DokuWikiMuApp',
'php5.2': { 'orchestra.apps.webapps.types.DrupalMuApp',
'verbose_name': "PHP 5.2 fcgi", 'orchestra.apps.webapps.types.SymbolicLinkApp',
'directive': ('fcgi', WEBAPPS_FCGID_PATH), 'orchestra.apps.webapps.types.WordPressApp',
'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 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.")
},
'static': {
'verbose_name': _("Static"),
'directive': ('static',),
'help_text': _("This creates a Static application under ~/webapps/&lt;app_name&gt;<br>"
"Apache2 will be used to serve static content and execute CGI files.")
},
'webalizer': {
'verbose_name': "Webalizer",
'directive': ('static', '%(app_path)s%(site_name)s'),
'help_text': _("This creates a Webalizer application under "
"~/webapps/&lt;app_name&gt;-&lt;site_name&gt;")
},
'wordpress-mu': {
'verbose_name': _("Wordpress (SaaS)"),
'directive': ('fpm', 'fcgi://127.0.0.1:8990/home/httpd/wordpress-mu/'),
'help_text': _("This creates a Wordpress site on a multi-tenant Wordpress server.<br>"
"By default this blog is accessible via &lt;app_name&gt;.blogs.orchestra.lan")
},
'dokuwiki-mu': {
'verbose_name': _("DokuWiki (SaaS)"),
'directive': ('alias', '/home/httpd/wikifarm/farm/'),
'help_text': _("This create a Dokuwiki wiki into a shared Dokuwiki server.<br>"
"By default this wiki is accessible via &lt;app_name&gt;.wikis.orchestra.lan")
},
'drupal-mu': {
'verbose_name': _("Drupdal (SaaS)"),
'directive': ('fpm', 'fcgi://127.0.0.1:8991/home/httpd/drupal-mu/'),
'help_text': _("This creates a Drupal site into a multi-tenant Drupal server.<br>"
"The installation will be completed after visiting "
"http://&lt;app_name&gt;.drupal.orchestra.lan/install.php?profile=standard<br>"
"By default this site will be accessible via &lt;app_name&gt;.drupal.orchestra.lan")
}
})
WEBAPPS_TYPES_OVERRIDE = getattr(settings, 'WEBAPPS_TYPES_OVERRIDE', {}) #WEBAPPS_TYPES_OVERRIDE = getattr(settings, 'WEBAPPS_TYPES_OVERRIDE', {})
for webapp_type, value in WEBAPPS_TYPES_OVERRIDE.iteritems(): #for webapp_type, value in WEBAPPS_TYPES_OVERRIDE.iteritems():
if value is None: # if value is None:
WEBAPPS_TYPES.pop(webapp_type, None) # WEBAPPS_TYPES.pop(webapp_type, None)
else: # else:
WEBAPPS_TYPES[webapp_type] = value # WEBAPPS_TYPES[webapp_type] = value
WEBAPPS_DEFAULT_TYPE = getattr(settings, 'WEBAPPS_DEFAULT_TYPE', 'php5.5') WEBAPPS_DEFAULT_TYPE = getattr(settings, 'WEBAPPS_DEFAULT_TYPE', 'php5.5')
@ -116,204 +75,43 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTIO
]) ])
WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', { WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', (
# { name: ( verbose_name, [help_text], validation_regex ) } 'orchestra.apps.webapps.options.public_root',
# Filesystem 'orchestra.apps.webapps.options.timeout',
'public-root': ( 'orchestra.apps.webapps.options.processes',
_("Public root"), 'orchestra.apps.webapps.options.php_enabled_functions',
_("Document root relative to webapps/&lt;webapp&gt;/"), 'orchestra.apps.webapps.options.php_allow_url_include',
r'[^ ]+', 'orchestra.apps.webapps.options.php_allow_url_fopen',
), 'orchestra.apps.webapps.options.php_auto_append_file',
# Processes 'orchestra.apps.webapps.options.php_auto_prepend_file',
'timeout': ( 'orchestra.apps.webapps.options.php_date_timezone',
_("Process timeout"), 'orchestra.apps.webapps.options.php_default_socket_timeout',
_("Maximum time in seconds allowed for a request to complete " 'orchestra.apps.webapps.options.php_display_errors',
"(a number between 0 and 999)."), 'orchestra.apps.webapps.options.php_extension',
# FCGID FcgidIOTimeout 'orchestra.apps.webapps.options.php_magic_quotes_gpc',
# FPM pm.request_terminate_timeout 'orchestra.apps.webapps.options.php_magic_quotes_runtime',
# PHP max_execution_time ini 'orchestra.apps.webapps.options.php_magic_quotes_sybase',
r'^[0-9]{1,3}$', 'orchestra.apps.webapps.options.php_max_execution_time',
), 'orchestra.apps.webapps.options.php_max_input_time',
'processes': ( 'orchestra.apps.webapps.options.php_max_input_vars',
_("Number of processes"), 'orchestra.apps.webapps.options.php_memory_limit',
_("Maximum number of children that can be alive at the same time " 'orchestra.apps.webapps.options.php_mysql_connect_timeout',
"(a number between 0 and 9)."), 'orchestra.apps.webapps.options.php_output_buffering',
# FCGID MaxProcesses 'orchestra.apps.webapps.options.php_register_globals',
# FPM pm.max_children 'orchestra.apps.webapps.options.php_post_max_size',
r'^[0-9]$', 'orchestra.apps.webapps.options.php_sendmail_path',
), 'orchestra.apps.webapps.options.php_session_bug_compat_warn',
# PHP 'orchestra.apps.webapps.options.php_session_auto_start',
'php-enabled_functions': ( 'orchestra.apps.webapps.options.php_safe_mode',
_("PHP - Enabled functions"), 'orchestra.apps.webapps.options.php_suhosin_post_max_vars',
' '.join(WEBAPPS_PHP_DISABLED_FUNCTIONS), 'orchestra.apps.webapps.options.php_suhosin_get_max_vars',
r'^[\w\.,-]+$' 'orchestra.apps.webapps.options.php_suhosin_request_max_vars',
), 'orchestra.apps.webapps.options.php_suhosin_session_encrypt',
'PHP-allow_url_include': ( 'orchestra.apps.webapps.options.php_suhosin_simulation',
_("PHP - Allow URL include"), 'orchestra.apps.webapps.options.php_suhosin_executor_include_whitelist',
_("Allows the use of URL-aware fopen wrappers with include, include_once, require, " 'orchestra.apps.webapps.options.php_upload_max_filesize',
"require_once (On or Off)."), 'orchestra.apps.webapps.options.php_zend_extension',
r'^(On|Off|on|off)$' ))
),
'PHP-allow_url_fopen': (
_("PHP - allow_url_fopen"),
_("Enables the URL-aware fopen wrappers that enable accessing URL object like files "
"(On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-auto_append_file': (
_("PHP - Auto append file"),
_("Specifies the name of a file that is automatically parsed after the main file."),
r'^[\w\.,-/]+$'
),
'PHP-auto_prepend_file': (
_("PHP - Auto prepend file"),
_("Specifies the name of a file that is automatically parsed before the main file."),
r'^[\w\.,-/]+$'
),
'PHP-date.timezone': (
_("PHP - date.timezone"),
_("Sets the default timezone used by all date/time functions "
"(Timezone string 'Europe/London')."),
r'^\w+/\w+$'
),
'PHP-default_socket_timeout': (
_("PHP - Default socket timeout"),
_("Number between 0 and 999."),
r'^[0-9]{1,3}$'
),
'PHP-display_errors': (
_("PHP - Display errors"),
_("determines whether errors should be printed to the screen as part of the output or "
"if they should be hidden from the user (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-extension': (
_("PHP - Extension"),
r'^[^ ]+$'
),
'PHP-magic_quotes_gpc': (
_("PHP - Magic quotes GPC"),
_("Sets the magic_quotes state for GPC (Get/Post/Cookie) operations (On or Off) "
"<b>DEPRECATED as of PHP 5.3.0</b>."),
r'^(On|Off|on|off)$'
),
'PHP-magic_quotes_runtime': (
_("PHP - Magic quotes runtime"),
_("Functions that return data from any sort of external source will have quotes escaped "
"with a backslash (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>."),
r'^(On|Off|on|off)$'
),
'PHP-magic_quotes_sybase': (
_("PHP - Magic quotes sybase"),
_("Single-quote is escaped with a single-quote instead of a backslash (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-max_execution_time': (
_("PHP - Max execution time"),
_("Maximum time in seconds a script is allowed to run before it is terminated by "
"the parser (Integer between 0 and 999)."),
r'^[0-9]{1,3}$'
),
'PHP-max_input_time': (
_("PHP - Max input time"),
_("Maximum time in seconds a script is allowed to parse input data, like POST and GET "
"(Integer between 0 and 999)."),
r'^[0-9]{1,3}$'
),
'PHP-max_input_vars': (
_("PHP - Max input vars"),
_("How many input variables may be accepted (limit is applied to $_GET, $_POST and $_COOKIE superglobal separately) "
"(Integer between 0 and 9999)."),
r'^[0-9]{1,4}$'
),
'PHP-memory_limit': (
_("PHP - Memory limit"),
_("This sets the maximum amount of memory in bytes that a script is allowed to allocate "
"(Value between 0M and 999M)."),
r'^[0-9]{1,3}M$'
),
'PHP-mysql.connect_timeout': (
_("PHP - Mysql connect timeout"),
_("Number between 0 and 999."),
r'^([0-9]){1,3}$'
),
'PHP-output_buffering': (
_("PHP - output_buffering"),
_("Turn on output buffering (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-register_globals': (
_("PHP - Register globals"),
_("Whether or not to register the EGPCS (Environment, GET, POST, Cookie, Server) "
"variables as global variables (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-post_max_size': (
_("PHP - Post max size"),
_("Sets max size of post data allowed (Value between 0M and 999M)."),
r'^[0-9]{1,3}M$'
),
'PHP-sendmail_path': (
_("PHP - sendmail_path"),
_("Where the sendmail program can be found."),
r'^[^ ]+$'
),
'PHP-session.bug_compat_warn': (
_("PHP - session.bug_compat_warn"),
_("Enables an PHP bug on session initialization for legacy behaviour (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-session.auto_start': (
_("PHP - session.auto_start"),
_("Specifies whether the session module starts a session automatically on request "
"startup (On or Off)."),
r'^(On|Off|on|off)$'
),
'PHP-safe_mode': (
_("PHP - Safe mode"),
_("Whether to enable PHP's safe mode (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>"),
r'^(On|Off|on|off)$'
),
'PHP-suhosin.post.max_vars': (
_("PHP - Suhosin POST max vars"),
_("Number between 0 and 9999."),
r'^[0-9]{1,4}$'
),
'PHP-suhosin.get.max_vars': (
_("PHP - Suhosin GET max vars"),
_("Number between 0 and 9999."),
r'^[0-9]{1,4}$'
),
'PHP-suhosin.request.max_vars': (
_("PHP - Suhosin request max vars"),
_("Number between 0 and 9999."),
r'^[0-9]{1,4}$'
),
'PHP-suhosin.session.encrypt': (
_("PHP - suhosin.session.encrypt"),
_("On or Off"),
r'^(On|Off|on|off)$'
),
'PHP-suhosin.simulation': (
_("PHP - Suhosin simulation"),
_("On or Off"),
r'^(On|Off|on|off)$'
),
'PHP-suhosin.executor.include.whitelist': (
_("PHP - suhosin.executor.include.whitelist"),
r'.*$'
),
'PHP-upload_max_filesize': (
_("PHP - upload_max_filesize"),
_("Value between 0M and 999M."),
r'^[0-9]{1,3}M$'
),
'PHP-zend_extension': (
_("PHP - zend_extension"),
r'^[^ ]+$'
),
})
WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD = getattr(settings, 'WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD', WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD = getattr(settings, 'WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD',
@ -332,3 +130,7 @@ WEBAPPS_DOKUWIKIMU_FARM_PATH = getattr(settings, 'WEBAPPS_DOKUWIKIMU_FARM_PATH',
WEBAPPS_DRUPAL_SITES_PATH = getattr(settings, 'WEBAPPS_DRUPAL_SITES_PATH', WEBAPPS_DRUPAL_SITES_PATH = getattr(settings, 'WEBAPPS_DRUPAL_SITES_PATH',
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s') '/home/httpd/htdocs/drupal-mu/sites/%(site_name)s')
WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = getattr(settings, 'WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',
'mysql.orchestra.lan')

View File

@ -0,0 +1,309 @@
from django import forms
from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra import plugins
from orchestra.plugins.forms import PluginDataForm
from orchestra.core import validators
from orchestra.forms import widgets
from orchestra.utils.functional import cached
from orchestra.utils.python import import_class
from . import options, settings
class AppType(plugins.Plugin):
name = None
verbose_name = ""
help_text= ""
form = PluginDataForm
change_form = None
serializer = None
icon = 'orchestra/icons/apps.png'
unique_name = False
options = (
('Process', options.process),
('PHP', options.php),
('File system', options.filesystem),
)
@classmethod
@cached
def get_plugins(cls):
plugins = []
for cls in settings.WEBAPPS_TYPES:
plugins.append(import_class(cls))
return plugins
@classmethod
def clean_data(cls, webapp):
""" model clean, uses cls.serizlier by default """
if cls.serializer:
serializer = cls.serializer(data=webapp.data)
if not serializer.is_valid():
raise ValidationError(serializer.errors)
return serializer.data
return {}
def get_form(self):
self.form.plugin = self
self.form.plugin_field = 'type'
return self.form
def get_change_form(self):
form = self.change_form or self.form
form.plugin = self
form.plugin_field = 'type'
return form
def get_serializer(self):
self.serializer.plugin = self
return self.serializer
def validate(self, instance):
""" Unique name validation """
if self.unique_name:
if not instance.pk and Webapp.objects.filter(name=instance.name, type=instance.type).exists():
raise ValidationError({
'name': _("A WordPress blog with this name already exists."),
})
def get_options(self):
pass
@classmethod
def get_options_choices(cls):
enabled = options.get_enabled().values()
yield (None, '-------')
for option in cls.options:
if hasattr(option, '__iter__'):
yield (option[0], [(op.name, op.verbose_name) for op in option[1] if op in enabled])
elif option in enabled:
yield (option.name, option.verbose_name)
def save(self, instance):
pass
def delete(self, instance):
pass
def get_related_objects(self, instance):
pass
class Php55App(AppType):
name = 'php5.5-fpm'
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(settings.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.")
options = (
('Process', options.process),
('PHP', [op for op in options.php if getattr(op, 'deprecated', 99) > 5.5]),
('File system', options.filesystem),
)
icon = 'orchestra/icons/apps/PHPFPM.png'
class Php52App(AppType):
name = 'php5.2-fcgi'
verbose_name = "PHP 5.2 FCGI"
directive = ('fcgi', settings.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.")
icon = 'orchestra/icons/apps/PHPFCGI.png'
class Php4App(AppType):
name = 'php4-fcgi'
verbose_name = "PHP 4 FCGI"
directive = ('fcgi', settings.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.")
icon = 'orchestra/icons/apps/PHPFCGI.png'
class StaticApp(AppType):
name = 'static'
verbose_name = "Static"
directive = ('static',)
help_text = _("This creates a Static application under ~/webapps/&lt;app_name&gt;<br>"
"Apache2 will be used to serve static content and execute CGI files.")
icon = 'orchestra/icons/apps/Static.png'
options = (
('File system', options.filesystem),
)
class WebalizerApp(AppType):
name = 'webalizer'
verbose_name = "Webalizer"
directive = ('static', '%(app_path)s%(site_name)s')
help_text = _("This creates a Webalizer application under "
"~/webapps/&lt;app_name&gt;-&lt;site_name&gt;")
icon = 'orchestra/icons/apps/Stats.png'
options = ()
class WordPressMuApp(AppType):
name = 'wordpress-mu'
verbose_name = "WordPress (SaaS)"
directive = ('fpm', 'fcgi://127.0.0.1:8990/home/httpd/wordpress-mu/')
help_text = _("This creates a WordPress site on a multi-tenant WordPress server.<br>"
"By default this blog is accessible via &lt;app_name&gt;.blogs.orchestra.lan")
icon = 'orchestra/icons/apps/WordPressMu.png'
unique_name = True
options = ()
class DokuWikiMuApp(AppType):
name = 'dokuwiki-mu'
verbose_name = "DokuWiki (SaaS)"
directive = ('alias', '/home/httpd/wikifarm/farm/')
help_text = _("This create a DokuWiki wiki into a shared DokuWiki server.<br>"
"By default this wiki is accessible via &lt;app_name&gt;.wikis.orchestra.lan")
icon = 'orchestra/icons/apps/DokuWikiMu.png'
unique_name = True
options = ()
class MoodleMuApp(AppType):
name = 'moodle-mu'
verbose_name = "Moodle (SaaS)"
directive = ('alias', '/home/httpd/wikifarm/farm/')
help_text = _("This create a Moodle site into a shared Moodle server.<br>"
"By default this wiki is accessible via &lt;app_name&gt;.moodle.orchestra.lan")
icon = 'orchestra/icons/apps/MoodleMu.png'
unique_name = True
options = ()
class DrupalMuApp(AppType):
name = 'drupal-mu'
verbose_name = "Drupdal (SaaS)"
directive = ('fpm', 'fcgi://127.0.0.1:8991/home/httpd/drupal-mu/')
help_text = _("This creates a Drupal site into a multi-tenant Drupal server.<br>"
"The installation will be completed after visiting "
"http://&lt;app_name&gt;.drupal.orchestra.lan/install.php?profile=standard<br>"
"By default this site will be accessible via &lt;app_name&gt;.drupal.orchestra.lan")
icon = 'orchestra/icons/apps/DrupalMu.png'
unique_name = True
options = ()
from rest_framework import serializers
from orchestra.forms import widgets
class SymbolicLinkForm(PluginDataForm):
path = forms.CharField(label=_("Path"), widget=forms.TextInput(attrs={'size':'100'}),
help_text=_("Path for the origin of the symbolic link."))
class SymbolicLinkSerializer(serializers.Serializer):
path = serializers.CharField(label=_("Path"))
class SymbolicLinkApp(AppType):
name = 'symbolic-link'
verbose_name = "Symbolic link"
form = SymbolicLinkForm
serializer = SymbolicLinkSerializer
icon = 'orchestra/icons/apps/SymbolicLink.png'
change_readonly_fileds = ('path',)
class WordPressForm(PluginDataForm):
db_name = forms.CharField(label=_("Database name"),
help_text=_("Database used for this webapp."))
db_user = forms.CharField(label=_("Database user"),)
db_pass = forms.CharField(label=_("Database user password"),
help_text=_("Initial database password."))
class WordPressSerializer(serializers.Serializer):
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)
from orchestra.apps.databases.models import Database, DatabaseUser
from orchestra.utils.python import random_ascii
class WordPressApp(AppType):
name = 'wordpress'
verbose_name = "WordPress"
icon = 'orchestra/icons/apps/WordPress.png'
change_form = WordPressForm
serializer = WordPressSerializer
change_readonly_fileds = ('db_name', 'db_user', 'db_pass',)
help_text = _("Visit http://&lt;domain.lan&gt;/wp-admin/install.php to finish the installation.")
def get_db_name(self, webapp):
db_name = 'wp_%s_%s' % (webapp.name, webapp.account)
# Limit for mysql database names
return db_name[:65]
def get_db_user(self, webapp):
db_name = self.get_db_name(webapp)
# Limit for mysql user names
return db_name[:17]
def get_db_pass(self):
return random_ascii(10)
def validate(self, webapp):
create = not webapp.pk
if create:
db = Database(name=self.get_db_name(webapp), account=webapp.account)
user = DatabaseUser(username=self.get_db_user(webapp), password=self.get_db_pass(),
account=webapp.account)
for obj in (db, user):
try:
obj.full_clean()
except ValidationError, e:
raise ValidationError({
'name': e.messages,
})
def save(self, webapp):
create = not webapp.pk
if create:
db_name = self.get_db_name(webapp)
db_user = self.get_db_user(webapp)
db_pass = self.get_db_pass()
db = Database.objects.create(name=db_name, account=webapp.account)
user = DatabaseUser(username=db_user, account=webapp.account)
user.set_password(db_pass)
user.save()
db.users.add(user)
webapp.data = {
'db_name': db_name,
'db_user': db_user,
'db_pass': db_pass,
}
else:
# Trigger related backends
for related in self.get_related(webapp):
related.save()
def delete(self, webapp):
for related in self.get_related(webapp):
related.delete()
def get_related(self, webapp):
related = []
try:
db_user = DatabaseUser.objects.get(username=webapp.data.get('db_user'))
except DatabaseUser.DoesNotExist:
pass
else:
related.append(db_user)
try:
db = Database.objects.get(name=webapp.data.get('db_name'))
except Database.DoesNotExist:
pass
else:
related.append(db)
return related

View File

@ -10,7 +10,7 @@ from orchestra.admin.utils import admin_link, change_url
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect from orchestra.forms.widgets import DynamicHelpTextSelect
from . import settings from . import settings, options
from .forms import WebsiteAdminForm from .forms import WebsiteAdminForm
from .models import Content, Website, WebsiteOption from .models import Content, Website, WebsiteOption
@ -20,8 +20,7 @@ class WebsiteOptionInline(admin.TabularInline):
extra = 1 extra = 1
OPTIONS_HELP_TEXT = { OPTIONS_HELP_TEXT = {
k: str(unicode(v[1])) if len(v) == 3 else '' op.name: str(unicode(op.help_text)) for op in options.get_enabled().values()
for k, v in settings.WEBSITES_OPTIONS.iteritems()
} }
# class Media: # class Media:

View File

@ -5,10 +5,9 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.core import validators, services from orchestra.core import validators, services
from orchestra.utils import tuple_setting_to_choices
from orchestra.utils.functional import cached from orchestra.utils.functional import cached
from . import settings from . import settings, options
class Website(models.Model): class Website(models.Model):
@ -78,15 +77,16 @@ class Website(models.Model):
path = settings.WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH % context path = settings.WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH % context
return path.replace('//', '/') return path.replace('//', '/')
class WebsiteOption(models.Model): class WebsiteOption(models.Model):
website = models.ForeignKey(Website, verbose_name=_("web site"), website = models.ForeignKey(Website, verbose_name=_("web site"),
related_name='options') related_name='options')
name = models.CharField(_("name"), max_length=128, name = models.CharField(_("name"), max_length=128,
choices=tuple_setting_to_choices(settings.WEBSITES_OPTIONS)) choices=((op.name, op.verbose_name) for op in options.get_enabled().values()))
value = models.CharField(_("value"), max_length=256) value = models.CharField(_("value"), max_length=256)
class Meta: class Meta:
unique_together = ('website', 'name') # unique_together = ('website', 'name')
verbose_name = _("option") verbose_name = _("option")
verbose_name_plural = _("options") verbose_name_plural = _("options")
@ -94,16 +94,8 @@ class WebsiteOption(models.Model):
return self.name return self.name
def clean(self): def clean(self):
""" validates name and value according to WEBSITES_WEBSITEOPTIONS """ option = options.get_enabled()[self.name]
regex = settings.WEBSITES_OPTIONS[self.name][-1] option.validate(self)
if not re.match(regex, self.value):
raise ValidationError({
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
params={
'value': self.value,
'regex': regex
}),
})
class Content(models.Model): class Content(models.Model):

View File

@ -0,0 +1,127 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from orchestra.utils.python import import_class
from . import settings
# TODO multiple and unique validation support in the formset
class SiteOption(object):
unique = True
def __init__(self, name, *args, **kwargs):
self.name = name
self.verbose_name = kwargs.pop('verbose_name', name)
self.help_text = kwargs.pop('help_text', '')
for k,v in kwargs.iteritems():
setattr(self, k, v)
def validate(self, website):
if self.regex and not re.match(self.regex, website.value):
raise ValidationError({
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
params={
'value': website.value,
'regex': self.regex
}),
})
directory_protection = SiteOption('directory_protection',
verbose_name=_("Directory protection"),
help_text=_("Space separated ..."),
regex=r'^([\w/_]+)\s+(\".*\")\s+([\w/_\.]+)$',
)
redirect = SiteOption('redirect',
verbose_name=_("Redirection"),
help_text=_("<tt>&lt;website path&gt; &lt;destination URL&gt;</tt>"),
regex=r'^[^ ]+\s[^ ]+$',
)
proxy = SiteOption('proxy',
verbose_name=_("Proxy"),
help_text=_("<tt>&lt;website path&gt; &lt;target URL&gt;</tt>"),
regex=r'^[^ ]+\shttp[^ ]+(timeout=[0-9]{1,3}|retry=[0-9]|\s)*$',
)
ssl_ca = SiteOption('ssl_ca',
verbose_name=_("SSL CA"),
help_text=_("Filesystem path of the CA certificate file."),
regex=r'^[^ ]+$'
)
ssl_cert = SiteOption('ssl_cert',
verbose_name=_("SSL cert"),
help_text=_("Filesystem path of the certificate file."),
regex=r'^[^ ]+$',
)
ssl_key = SiteOption('ssl_key',
verbose_name=_("SSL key"),
help_text=_("Filesystem path of the key file."),
regex=r'^[^ ]+$',
)
sec_rule_remove = SiteOption('sec_rule_remove',
verbose_name=_("SecRuleRemoveById"),
help_text=_("Space separated ModSecurity rule IDs."),
regex=r'^[0-9\s]+$',
)
sec_engine = SiteOption('sec_engine',
verbose_name=_("Modsecurity engine"),
help_text=_("<tt>On</tt> or <tt>Off</tt>, defaults to On"),
regex=r'^(On|Off)$',
)
user_group = SiteOption('user_group',
verbose_name=_("SuexecUserGroup"),
help_text=_("<tt>user [group]</tt>, username and optional groupname."),
# TODO validate existing user/group
regex=r'^[\w/_]+(\s[\w/_]+)*$',
)
error_document = SiteOption('error_document',
verbose_name=_("ErrorDocumentRoot"),
help_text=_("&lt;error code&gt; &lt;URL/path/message&gt;<br>"
"<tt>&nbsp;500 http://foo.example.com/cgi-bin/tester</tt><br>"
"<tt>&nbsp;404 /cgi-bin/bad_urls.pl</tt><br>"
"<tt>&nbsp;401 /subscription_info.html</tt><br>"
"<tt>&nbsp;403 \"Sorry can't allow you access today\"</tt>"),
regex=r'[45]0[0-9]\s.*',
)
ssl = [
ssl_ca,
ssl_cert,
ssl_key,
]
sec = [
sec_rule_remove,
sec_engine,
]
httpd = [
directory_protection,
redirect,
proxy,
user_group,
error_document,
]
_enabled = None
def get_enabled():
global _enabled
if _enabled is None:
from . import settings
_enabled = {}
for op in settings.WEBSITES_ENABLED_OPTIONS:
op = import_class(op)
_enabled[op.name] = op
return _enabled

View File

@ -6,12 +6,20 @@ WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT',
'%(account)s-%(name)s') '%(account)s-%(name)s')
# TODO 'http', 'https', 'https-only', 'http and https' and rename to PROTOCOL
WEBSITES_PORT_CHOICES = getattr(settings, 'WEBSITES_PORT_CHOICES', ( WEBSITES_PORT_CHOICES = getattr(settings, 'WEBSITES_PORT_CHOICES', (
(80, 'HTTP'), (80, 'HTTP'),
(443, 'HTTPS'), (443, 'HTTPS'),
)) ))
WEBSITES_PROTOCOL_CHOICES = getattr(settings, 'WEBSITES_PROTOCOL_CHOICES', (
('http', 'HTTP'),
('https', 'HTTPS'),
('http-https', 'HTTP and HTTPS),
('https-only', 'HTTPS only'),
))
WEBSITES_DEFAULT_PORT = getattr(settings, 'WEBSITES_DEFAULT_PORT', 80) WEBSITES_DEFAULT_PORT = getattr(settings, 'WEBSITES_DEFAULT_PORT', 80)
@ -21,65 +29,18 @@ WEBSITES_DEFAULT_IP = getattr(settings, 'WEBSITES_DEFAULT_IP', '*')
WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL', 'domains.Domain') WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL', 'domains.Domain')
WEBSITES_OPTIONS = getattr(settings, 'WEBSITES_OPTIONS', { WEBSITES_ENABLED_OPTIONS = getattr(settings, 'WEBSITES_ENABLED_OPTIONS', (
# { name: ( verbose_name, [help_text], validation_regex ) } 'orchestra.apps.websites.options.directory_protection',
'directory_protection': ( 'orchestra.apps.websites.options.redirect',
_("HTTPD - Directory protection"), 'orchestra.apps.websites.options.proxy',
_("Space separated ..."), 'orchestra.apps.websites.options.ssl_ca',
r'^([\w/_]+)\s+(\".*\")\s+([\w/_\.]+)$', 'orchestra.apps.websites.options.ssl_cert',
), 'orchestra.apps.websites.options.ssl_key',
'redirect': ( 'orchestra.apps.websites.options.sec_rule_remove',
_("HTTPD - Redirection"), 'orchestra.apps.websites.options.sec_engine',
_("<tt>&lt;website path&gt; &lt;destination URL&gt;</tt>"), 'orchestra.apps.websites.options.user_group',
r'^[^ ]+\s[^ ]+$', 'orchestra.apps.websites.options.error_document',
), ))
'proxy': (
_("HTTPD - Proxy"),
_("<tt>&lt;website path&gt; &lt;target URL&gt;</tt>"),
r'^[^ ]+\shttp[^ ]+(timeout=[0-9]{1,3}|retry=[0-9]|\s)*$',
),
'ssl_ca': (
"HTTPD - SSL CA",
_("Filesystem path of the CA certificate file."),
r'^[^ ]+$'
),
'ssl_cert': (
_("HTTPD - SSL cert"),
_("Filesystem path of the certificate file."),
r'^[^ ]+$'
),
'ssl_key': (
_("HTTPD - SSL key"),
_("Filesystem path of the key file."),
r'^[^ ]+$',
),
'sec_rule_remove': (
"HTTPD - SecRuleRemoveById",
_("Space separated ModSecurity rule IDs."),
r'^[0-9\s]+$',
),
'sec_engine': (
"HTTPD - Modsecurity engine",
_("<tt>On</tt> or <tt>Off</tt>, defaults to On"),
r'^(On|Off)$',
),
'user_group': (
"HTTPD - SuexecUserGroup",
_("<tt>user [group]</tt>, username and optional groupname."),
# TODO validate existing user/group
r'^[\w/_]+(\s[\w/_]+)*$',
),
# TODO backend support
'error_document': (
"HTTPD - ErrorDocumentRoot",
_("&lt;error code&gt; &lt;URL/path/message&gt;<br>"
"<tt>&nbsp;500 http://foo.example.com/cgi-bin/tester</tt><br>"
"<tt>&nbsp;404 /cgi-bin/bad_urls.pl</tt><br>"
"<tt>&nbsp;401 /subscription_info.html</tt><br>"
"<tt>&nbsp;403 \"Sorry can't allow you access today\"</tt>"),
r'[45]0[0-9]\s.*',
)
})
WEBSITES_BASE_APACHE_CONF = getattr(settings, 'WEBSITES_BASE_APACHE_CONF', WEBSITES_BASE_APACHE_CONF = getattr(settings, 'WEBSITES_BASE_APACHE_CONF',

View File

@ -69,10 +69,11 @@ def paddingCheckboxSelectMultiple(padding):
class DynamicHelpTextSelect(forms.Select): class DynamicHelpTextSelect(forms.Select):
def __init__(self, target, help_text, *args, **kwargs): def __init__(self, target, help_text, *args, **kwargs):
help_text = self.get_dynamic_help_text(target, help_text) help_text = self.get_dynamic_help_text(target, help_text)
attrs = { attrs = kwargs.get('attrs', {})
attrs.update({
'onClick': help_text, 'onClick': help_text,
'onChange': help_text, 'onChange': help_text,
} })
attrs.update(kwargs.get('attrs', {})) attrs.update(kwargs.get('attrs', {}))
kwargs['attrs'] = attrs kwargs['attrs'] = attrs
super(DynamicHelpTextSelect, self).__init__(*args, **kwargs) super(DynamicHelpTextSelect, self).__init__(*args, **kwargs)
@ -81,8 +82,8 @@ class DynamicHelpTextSelect(forms.Select):
return textwrap.dedent("""\ return textwrap.dedent("""\
siteoptions = {help_text}; siteoptions = {help_text};
valueelement = $("#" + {target}); valueelement = $("#" + {target});
help_text = siteoptions[this.options[this.selectedIndex].value] || ""
valueelement.parent().find('p').remove(); valueelement.parent().find('p').remove();
valueelement.parent().append( valueelement.parent().append("<p class='help'>" + help_text + "</p>");\
"<p class='help'>" + siteoptions[this.options[this.selectedIndex].value] + "</p>" """.format(target=target, help_text=str(help_text))
);""".format(target=target, help_text=str(help_text))
) )

View File

@ -1,7 +1,6 @@
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils.text import camel_case_to_spaces
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.admin.utils import wrap_admin_view from orchestra.admin.utils import wrap_admin_view
@ -11,14 +10,29 @@ from orchestra.utils.functional import cached
class SelectPluginAdminMixin(object): class SelectPluginAdminMixin(object):
plugin = None plugin = None
plugin_field = None plugin_field = None
plugin_title = None
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
if obj: if obj:
self.form = getattr(obj, '%s_class' % self.plugin_field)().get_form() plugin = getattr(obj, '%s_instance' % self.plugin_field)
self.form = getattr(plugin, 'get_change_form', plugin.get_form)()
else: else:
self.form = self.plugin.get_plugin(self.plugin_value)().get_form() plugin = self.plugin.get_plugin(self.plugin_value)()
self.form = plugin.get_form()
return super(SelectPluginAdminMixin, self).get_form(request, obj=obj, **kwargs) return super(SelectPluginAdminMixin, self).get_form(request, obj=obj, **kwargs)
def get_fields(self, request, obj=None):
""" Try to maintain original field ordering """
fields = super(SelectPluginAdminMixin, self).get_fields(request, obj=obj)
head_fields = list(self.get_readonly_fields(request, obj))
head, tail = [], []
for field in fields:
if field in head_fields:
head.append(field)
else:
tail.append(field)
return head + tail
def get_urls(self): def get_urls(self):
""" Hooks select account url """ """ Hooks select account url """
urls = super(SelectPluginAdminMixin, self).get_urls() urls = super(SelectPluginAdminMixin, self).get_urls()
@ -34,6 +48,7 @@ class SelectPluginAdminMixin(object):
def select_plugin_view(self, request): def select_plugin_view(self, request):
opts = self.model._meta opts = self.model._meta
context = { context = {
'plugin_title': self.plugin_title or 'Plugins',
'opts': opts, 'opts': opts,
'app_label': opts.app_label, 'app_label': opts.app_label,
'field': self.plugin_field, 'field': self.plugin_field,
@ -52,8 +67,9 @@ class SelectPluginAdminMixin(object):
self.plugin_value = plugin_value self.plugin_value = plugin_value
if not plugin_value: if not plugin_value:
self.plugin_value = self.plugin.get_plugins()[0].get_name() self.plugin_value = self.plugin.get_plugins()[0].get_name()
plugin = self.plugin.get_plugin(self.plugin_value)
context = { context = {
'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value), 'title': _("Add new %s") % plugin.verbose_name,
} }
context.update(extra_context or {}) context.update(extra_context or {})
return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url, return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url,
@ -62,9 +78,9 @@ class SelectPluginAdminMixin(object):
def change_view(self, request, object_id, form_url='', extra_context=None): def change_view(self, request, object_id, form_url='', extra_context=None):
obj = self.get_object(request, unquote(object_id)) obj = self.get_object(request, unquote(object_id))
plugin_value = getattr(obj, self.plugin_field) plugin = getattr(obj, '%s_class' % self.plugin_field)
context = { context = {
'title': _("Change %s") % camel_case_to_spaces(str(plugin_value)), 'title': _("Change %s") % plugin.verbose_name,
} }
context.update(extra_context or {}) context.update(extra_context or {})
return super(SelectPluginAdminMixin, self).change_view(request, object_id, return super(SelectPluginAdminMixin, self).change_view(request, object_id,

View File

@ -1,23 +1,32 @@
from django import forms from django import forms
from orchestra.forms.widgets import ReadOnlyWidget
class PluginDataForm(forms.ModelForm): class PluginDataForm(forms.ModelForm):
data = forms.CharField(widget=forms.HiddenInput, required=False) data = forms.CharField(widget=forms.HiddenInput, required=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PluginDataForm, self).__init__(*args, **kwargs) super(PluginDataForm, self).__init__(*args, **kwargs)
# TODO remove it well if self.plugin_field in self.fields:
try: value = self.plugin.get_name()
self.fields[self.plugin_field].widget = forms.HiddenInput() display = '%s <a href=".">change</a>' % unicode(self.plugin.verbose_name)
except KeyError: self.fields[self.plugin_field].widget = ReadOnlyWidget(value, display)
pass self.fields[self.plugin_field].help_text = getattr(self.plugin, 'help_text', '')
instance = kwargs.get('instance') instance = kwargs.get('instance')
if instance: if instance:
for field in self.declared_fields: for field in self.declared_fields:
initial = self.fields[field].initial initial = self.fields[field].initial
self.fields[field].initial = instance.data.get(field, initial) self.fields[field].initial = instance.data.get(field, initial)
if self.instance.pk:
for field in self.plugin.get_change_readonly_fileds():
value = getattr(self.instance, field, None) or self.instance.data[field]
self.fields[field].required = False
self.fields[field].widget = ReadOnlyWidget(value)
# self.fields[field].help_text = None
def clean(self): def clean(self):
# TODO clean all filed within data???
data = {} data = {}
for field in self.declared_fields: for field in self.declared_fields:
try: try:

View File

@ -6,10 +6,11 @@ class Plugin(object):
# Used on select plugin view # Used on select plugin view
class_verbose_name = None class_verbose_name = None
icon = None icon = None
change_readonly_fileds = ()
@classmethod @classmethod
def get_name(cls): def get_name(cls):
return cls.__name__ return getattr(cls, 'name', cls.__name__)
@classmethod @classmethod
def get_plugins(cls): def get_plugins(cls):
@ -40,6 +41,10 @@ class Plugin(object):
choices.append((plugin.get_name(), verbose)) choices.append((plugin.get_name(), verbose))
return sorted(choices, key=lambda e: e[1]) return sorted(choices, key=lambda e: e[1])
@classmethod
def get_change_readonly_fileds(cls):
return cls.change_readonly_fileds
class PluginModelAdapter(Plugin): class PluginModelAdapter(Plugin):
""" Adapter class for using model classes as plugins """ """ Adapter class for using model classes as plugins """

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,630 @@
<?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"
id="svg3201"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="48"
height="48"
sodipodi:docname="DokuWikiMu.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/DokuWikiMu.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata3207">
<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>
<defs
id="defs3205">
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.89261,32.892968)"
gradientUnits="userSpaceOnUse"
id="radialGradient3610"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
<linearGradient
id="linearGradient8838-7">
<stop
id="stop8840-0"
style="stop-color:black;stop-opacity:1"
offset="0" />
<stop
id="stop8842-7"
style="stop-color:black;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.84341,35.950626)"
gradientUnits="userSpaceOnUse"
id="radialGradient3708"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
<radialGradient
r="8.6813803"
fy="212.80016"
fx="224.41418"
cy="212.80016"
cx="224.41418"
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
gradientUnits="userSpaceOnUse"
id="radialGradient2647"
xlink:href="#linearGradient2360"
inkscape:collect="always" />
<linearGradient
y2="189.01556"
x2="286.22665"
y1="189.01556"
x1="219.21262"
gradientUnits="userSpaceOnUse"
id="linearGradient2643"
xlink:href="#linearGradient2336"
inkscape:collect="always" />
<linearGradient
y2="192.73286"
x2="277.8761"
y1="192.73286"
x1="219.66267"
gradientUnits="userSpaceOnUse"
id="linearGradient2640"
xlink:href="#linearGradient2352"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="217.94008"
x2="255.68353"
y1="251.56442"
x1="275.71765"
id="linearGradient2630"
xlink:href="#linearGradient2624"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="213.12164"
x2="251.64362"
y1="234.52202"
x1="248.62152"
id="linearGradient2618"
xlink:href="#linearGradient2612"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="210.3558"
x2="206.06017"
y1="222.05145"
x1="202.41772"
id="linearGradient2606"
xlink:href="#linearGradient2600"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="307.52844"
x2="224.67441"
y1="241.52789"
x1="184.30582"
id="linearGradient2406"
xlink:href="#linearGradient2408"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="265.40363"
x2="244.79126"
y1="220.07191"
x1="213.96568"
id="linearGradient2395"
xlink:href="#linearGradient2389"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="172.32423"
x2="185.81258"
y1="262.28729"
x1="286.15598"
id="linearGradient2362"
xlink:href="#linearGradient2370"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="195.87151"
x2="136.14151"
y1="303.78967"
x1="140.15784"
id="linearGradient2354"
xlink:href="#linearGradient2348"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="289.50323"
x2="240.84924"
y1="184.99277"
x1="162.76369"
id="linearGradient2346"
xlink:href="#linearGradient2332"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="246.35907"
x2="201.40646"
y1="246.35907"
x1="184.07063"
id="linearGradient2325"
xlink:href="#linearGradient2360"
inkscape:collect="always" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,1.631384,0,-173.4045)"
r="7.1440549"
fy="274.64203"
fx="257.41144"
cy="274.64203"
cx="257.41144"
id="radialGradient2317"
xlink:href="#linearGradient2360"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="258.91571"
x2="255.6561"
y1="258.91571"
x1="191.75092"
id="linearGradient2247"
xlink:href="#linearGradient2229"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="262.25757"
x2="263.67093"
y1="262.25757"
x1="192.03938"
id="linearGradient2227"
xlink:href="#linearGradient2249"
inkscape:collect="always" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
r="8.6813803"
fy="212.80016"
fx="224.41418"
cy="212.80016"
cx="224.41418"
id="radialGradient2366"
xlink:href="#linearGradient2360"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="192.73286"
x2="277.8761"
y1="192.73286"
x1="219.66267"
id="linearGradient2350"
xlink:href="#linearGradient2352"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="189.01556"
x2="286.22665"
y1="189.01556"
x1="219.21262"
id="linearGradient2342"
xlink:href="#linearGradient2336"
inkscape:collect="always" />
<linearGradient
id="linearGradient2336">
<stop
id="stop2338"
offset="0"
style="stop-color:#8f2a15;stop-opacity:1;" />
<stop
id="stop2340"
offset="1"
style="stop-color:#c8381b;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2352">
<stop
style="stop-color:#ce411e;stop-opacity:1;"
offset="0"
id="stop2354" />
<stop
style="stop-color:#ecad8d;stop-opacity:1;"
offset="1"
id="stop2356" />
</linearGradient>
<linearGradient
id="linearGradient2360">
<stop
id="stop2362"
offset="0"
style="stop-color:#d69c00;stop-opacity:1;" />
<stop
id="stop2364"
offset="1"
style="stop-color:#ffe658;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2213">
<stop
id="stop2215"
offset="0"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop2217"
offset="1"
style="stop-color:#000000;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient2229">
<stop
style="stop-color:#00b62b;stop-opacity:1;"
offset="0"
id="stop2231" />
<stop
style="stop-color:#a1d784;stop-opacity:1;"
offset="1"
id="stop2233" />
</linearGradient>
<linearGradient
id="linearGradient2249">
<stop
id="stop2251"
offset="0"
style="stop-color:#00a423;stop-opacity:1;" />
<stop
id="stop2253"
offset="1"
style="stop-color:#00b427;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2332">
<stop
id="stop2334"
offset="0"
style="stop-color:#ede1ae;stop-opacity:1;" />
<stop
id="stop2336"
offset="1"
style="stop-color:#fefdfa;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2348">
<stop
id="stop2350"
offset="0"
style="stop-color:#fbf6f0;stop-opacity:1;" />
<stop
id="stop2352"
offset="1"
style="stop-color:#e9dac7;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2364">
<stop
style="stop-color:#fbf6f0;stop-opacity:1;"
offset="0"
id="stop2366" />
<stop
style="stop-color:#e9dac7;stop-opacity:1;"
offset="1"
id="stop2368" />
</linearGradient>
<linearGradient
id="linearGradient2370">
<stop
id="stop2372"
offset="0"
style="stop-color:#fbfaf9;stop-opacity:1;" />
<stop
id="stop2374"
offset="1"
style="stop-color:#e9dac7;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2389">
<stop
id="stop2391"
offset="0"
style="stop-color:#000000;stop-opacity:0.17346939;" />
<stop
id="stop2393"
offset="1"
style="stop-color:#c7cec2;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient2408">
<stop
style="stop-color:#000000;stop-opacity:0.17346939;"
offset="0"
id="stop2410" />
<stop
style="stop-color:#c7cec2;stop-opacity:0;"
offset="1"
id="stop2412" />
</linearGradient>
<marker
style="overflow:visible"
id="Arrow2Lstart"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lstart">
<path
inkscape:connector-curvature="0"
transform="matrix(1.1,0,0,1.1,-5.5,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
id="path2571" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutL"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutL">
<path
inkscape:connector-curvature="0"
transform="scale(0.8,0.8)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path2488" />
</marker>
<linearGradient
id="linearGradient2600">
<stop
id="stop2602"
offset="0"
style="stop-color:#e32525;stop-opacity:0.81632656;" />
<stop
id="stop2604"
offset="1"
style="stop-color:#e32525;stop-opacity:0.5714286;" />
</linearGradient>
<linearGradient
id="linearGradient2612">
<stop
id="stop2614"
offset="0"
style="stop-color:#25901b;stop-opacity:0.83673471;" />
<stop
id="stop2616"
offset="1"
style="stop-color:#25901b;stop-opacity:0.37755102;" />
</linearGradient>
<linearGradient
id="linearGradient2624">
<stop
id="stop2626"
offset="0"
style="stop-color:#3a9030;stop-opacity:0.83673471;" />
<stop
id="stop2628"
offset="1"
style="stop-color:#3d9c32;stop-opacity:0.79591835;" />
</linearGradient>
<inkscape:perspective
id="perspective100"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 526.18109 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1024"
id="namedview3203"
showgrid="false"
inkscape:zoom="7.7918606"
inkscape:cx="10.453259"
inkscape:cy="21.076505"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg3201" />
<path
inkscape:connector-curvature="0"
d="m 48.122094,39.37881 c 0,4.34954 -10.77808,7.87555 -24.0735,7.87555 -13.29542,0 -24.07349952,-3.52601 -24.07349952,-7.87555 0,-4.34955 10.77807952,-7.87556 24.07349952,-7.87556 13.29542,0 24.0735,3.52601 24.0735,7.87556 l 0,0 z"
id="path8836-9"
style="opacity:0.3;fill:url(#radialGradient3708);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible" />
<rect
style="fill:#eeeeec;stroke:#eeeeec"
id="rect3747"
width="31.308554"
height="23.776344"
x="8.3035679"
y="14.241219"
ry="11.888172" />
<path
style="fill:#6184a3;fill-opacity:1"
d="M 10.280293,40.441739 C 7.2051807,40.132054 4.8722076,39.068741 2.8990174,37.077537 -0.40380286,33.744562 -1.1067297,27.727678 1.1759893,23.645792 2.1196585,21.958353 3.8188126,20.299386 5.4807087,19.442893 l 0.7741857,-0.398997 0.011288,-1.226069 c 0.030962,-3.362392 1.3554573,-7.041803 3.9025052,-9.4279835 3.920374,-3.6727721 9.689551,-4.5169172 14.625477,-2.139999 2.531395,1.2190062 4.768749,3.4149696 5.824881,5.7171185 0.134346,0.29285 0.276817,0.532455 0.316601,0.532455 0.0398,0 0.324958,-0.170005 0.633723,-0.377784 2.080065,-1.399785 5.000273,-1.351753 7.212027,0.118622 2.543639,1.691007 3.424188,5.866753 1.989066,8.518585 -0.156214,0.28865 -0.263577,0.537672 -0.238588,0.553375 0.02499,0.0157 0.483839,0.173171 1.019662,0.349919 2.556281,0.843228 4.816568,3.003772 5.753516,5.499618 0.314345,0.837357 0.611974,2.379683 0.611974,3.171291 0,0.28762 -0.07623,1.867577 -0.169403,2.401863 -0.631568,3.621676 -3.342607,6.461999 -7.126882,7.466739 l -0.926404,0.245964 -14.513874,0.01651 c -7.982628,0.0092 -14.687708,-7.4e-4 -14.900171,-0.02236 z M 31.462046,14.76551 c -20.974697,22.156329 -10.487349,11.078164 0,0 z"
id="path3726"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscsssssssscsssssccscc" />
<g
transform="matrix(0.73043968,0,0,0.73043968,-109.25563,-166.72103)"
style="display:inline"
inkscape:label="paper"
id="layer3">
<g
transform="matrix(0.3463897,0,0,0.3463897,105.30454,185.54142)"
id="g1419">
<g
id="g2376">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="rect1422"
d="m 120.21543,196.43769 70.90655,-0.79226 -2.40261,109.05308 -71.71761,0.37344 3.21367,-108.63426 z"
style="fill:url(#linearGradient2354);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.7216621px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
transform="matrix(0.989976,-0.141236,0.201069,0.979577,0,0)" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="rect1425"
d="m 179.20033,182.08731 79.84173,-19.51687 26.61391,101.72428 -82.50312,21.58684 -23.95252,-103.79425 z"
style="fill:url(#linearGradient2362);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="rect1419"
d="m 159.01353,181.74387 85.58587,0.53396 0,110.47429 -84.53387,-2.5127 -1.052,-108.49555 z"
style="fill:url(#linearGradient2346);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00418305px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
transform="matrix(0.995676,-0.09289891,0.08102261,0.996712,0,0)" />
</g>
<path
inkscape:connector-curvature="0"
transform="matrix(0.995433,-0.09546066,0.09546066,0.995433,0,0)"
style="font-size:12.0000124px;font-style:normal;font-weight:normal;line-height:125%;fill:#6184a3;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
d="m 167.55116,214.00773 0,-20.1846 5.34962,0 0,2.37403 -2.48145,0 0,15.43654 2.48145,0 0,2.37403 -5.34962,0 m 7.34767,0 0,-20.1846 5.34961,0 0,2.37403 -2.48144,0 0,15.43654 2.48144,0 0,2.37403 -5.34961,0 m 7.36915,-20.1846 5.81153,0 c 1.31054,2e-5 2.30956,0.10028 2.99707,0.30078 0.92382,0.27216 1.71516,0.75555 2.37403,1.4502 0.65884,0.69468 1.16014,1.54689 1.50391,2.55664 0.34373,1.00262 0.51561,2.24155 0.51562,3.71681 -10e-6,1.29623 -0.16115,2.41342 -0.4834,3.35156 -0.39389,1.14584 -0.95607,2.07325 -1.68652,2.78223 -0.55145,0.53711 -1.29624,0.95606 -2.23438,1.25684 -0.70183,0.222 -1.63999,0.33301 -2.81446,0.33301 l -5.9834,0 0,-15.74807 m 3.17969,2.66407 0,10.43067 2.37402,0 c 0.88802,1e-5 1.52897,-0.0501 1.92286,-0.15039 0.51561,-0.1289 0.94172,-0.34732 1.27832,-0.65527 0.34374,-0.30794 0.62304,-0.81282 0.83789,-1.51465 0.21483,-0.70898 0.32226,-1.6722 0.32227,-2.88965 -1e-5,-1.21744 -0.10744,-2.15201 -0.32227,-2.80372 -0.21485,-0.65168 -0.51563,-1.16014 -0.90234,-1.52539 -0.38673,-0.36522 -0.87729,-0.61229 -1.47168,-0.74121 -0.44402,-0.10025 -1.31414,-0.15038 -2.61036,-0.15039 l -1.42871,0 m 14.96388,13.084 -3.75977,-15.74807 3.25489,0 2.37403,10.8174 2.87891,-10.8174 3.78125,0 2.76074,11.00002 2.417,-11.00002 3.20118,0 -3.82423,15.74807 -3.37305,0 -3.13672,-11.77345 -3.12598,11.77345 -3.44825,0 m 22.76272,-15.74807 0,20.1846 -5.34961,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34961,0 m 7.34767,0 0,20.1846 -5.34962,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34962,0"
id="text2382" />
<g
style="display:inline"
id="g2632">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssssccccccssssscc"
id="path2414"
d="m 174.75585,201.60224 c -6.04576,2.46667 -10.16789,4.4194 -12.88454,6.35064 -2.71665,1.93124 -3.19257,4.60007 -3.24631,6.26587 -0.0269,0.8329 0.0809,1.77774 0.63189,2.44014 0.55103,0.6624 1.80769,1.87421 2.75794,2.38558 1.90049,1.02274 7.5417,2.42901 10.51899,3.07308 11.90917,2.57627 26.80568,1.68117 26.80568,1.68117 1.69307,1.2452 2.83283,2.82434 3.269,4.26902 4.5766,-1.88674 11.81084,-6.58439 13.15657,-8.57706 -5.45142,-4.19955 -10.79692,-6.33346 -16.51317,-8.30847 -1.59867,-0.71918 -2.87956,-1.22649 -0.71773,2.55635 0.98506,2.47275 0.85786,5.05143 0.57176,7.41825 0,0 -16.52749,0.40678 -28.23838,-2.1266 -2.92772,-0.63334 -5.46627,-0.95523 -7.21875,-1.89832 -0.87624,-0.47154 -1.48296,-0.8208 -1.91578,-1.3411 -0.43282,-0.5203 -0.2196,-1.29055 -0.20128,-1.85858 0.0366,-1.13607 0.25336,-1.67063 2.86177,-3.52492 2.60841,-1.85429 5.65407,-3.36195 11.65936,-5.81211 -0.0877,-1.29125 -0.29025,-2.5059 -1.29702,-2.99294 z"
style="fill:url(#linearGradient2606);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker-end:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsssscccccssssc"
id="path2608"
d="m 269.62539,220.7482 c -1.43576,-0.13963 -2.58044,0.30288 -2.56084,1.50218 0.94391,0.85652 1.34942,2.43518 1.48562,3.14008 0.1362,0.7049 0.0359,1.21914 -0.48562,1.89004 -1.043,1.3418 -3.12498,1.56875 -6.5006,2.72063 -6.75124,2.30377 -16.89306,2.52561 -27.90689,3.84639 -22.02767,2.64157 -39.03164,3.76107 -39.03164,3.76107 1.98346,-4.64758 6.32828,-4.41197 6.34903,-8.20969 0.27376,-0.89755 -3.14597,-1.31638 -5.09943,-0.10731 -4.26694,3.70137 -7.59152,6.75353 -10.69418,10.51311 l 1.88795,3.08438 c 0,0 26.13006,-2.88973 48.19776,-5.5361 11.03385,-1.32318 20.95601,-1.99856 27.80968,-4.33728 3.42683,-1.16936 5.95975,-1.49022 7.6409,-3.51958 0.63172,-0.76256 1.35238,-3.04699 1.06804,-4.73369 -0.21951,-1.30213 -1.14979,-3.09774 -2.15978,-4.01423 z"
style="fill:url(#linearGradient2618);fill-opacity:1;fill-rule:evenodd;stroke:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscccccsscc"
id="path2620"
d="m 254.36185,220.33948 c -6.84997,3.24198 -7.15311,8.60912 -5.95953,12.79884 1.19358,4.18972 5.26293,8.75677 9.32121,12.40608 8.11656,7.29861 12.06046,9.33163 12.06046,9.33163 -3.71515,-0.10342 -7.89887,-1.41174 -8.13315,0.49304 -0.9483,2.97582 11.49137,3.47486 17.43787,2.70205 -1.39456,-7.57836 -3.79323,-13.21546 -7.73151,-14.90312 -1.68464,-0.14804 0.31242,4.72441 0.76985,9.39604 0,0 -3.62454,-1.73122 -11.60519,-8.90762 -3.99032,-3.5882 -7.37386,-7.3421 -8.47319,-11.20099 -1.09933,-3.85889 0.0776,-6.1205 4.95082,-9.53176 0.92816,-0.99528 -1.28985,-2.45913 -2.63764,-2.58419 z"
style="fill:url(#linearGradient2630);fill-opacity:1;fill-rule:evenodd;stroke:none" />
</g>
<path
inkscape:connector-curvature="0"
style="fill:url(#linearGradient2395);fill-opacity:1;stroke:none;display:inline"
d="m 213.96569,234.57806 2.18756,-14.42897 15.21982,6.08793 21.49387,29.94828 -20.40591,9.21832 -18.49534,-30.82556 z"
id="rect2386"
sodipodi:nodetypes="cccccc" />
<g
style="display:inline"
id="g2649">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path1443"
d="m 232.55816,219.5295 -15.92827,0.32199 3.08809,-15.15716 12.84018,14.83517 z"
style="fill:url(#radialGradient2647);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="path1452"
d="m 221.60041,219.29315 -4.41205,0.0782 0.85429,-3.98263 3.55776,3.90445 z"
style="fill:#812310;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
id="rect1437"
d="m 269.44172,159.27421 0.098,8.91471 8.0581,8.72344 7.75906,0.7992 -52.80669,41.84092 -6.66532,-3.30696 -5.08243,-5.618 -1.08987,-5.91194 49.72911,-45.44137 z"
style="fill:url(#linearGradient2643);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="rect1446"
d="m 268.94766,168.32844 8.3426,8.82719 -51.1007,38.68262 -4.9197,-5.4436 47.6778,-42.06621 z"
style="fill:url(#linearGradient2640);fill-opacity:1;fill-rule:evenodd;stroke:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
id="path1440"
d="m 285.33776,177.73216 -8.16219,-0.86619 -7.7518,-8.67862 0.0132,-9.14293 8.36213,0.75209 7.18862,9.57682 0.35007,8.35883 z"
style="fill:#ffe965;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;display:inline" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
id="path1449"
d="m 280.72049,168.46367 0.1644,4.05654 -3.81335,-0.71676 -2.87504,-3.18901 -0.28089,-3.53393 3.85447,-0.16637 2.95041,3.54953 z"
style="fill:#cb391c;fill-opacity:1;fill-rule:evenodd;stroke:none" />
</g>
<g
style="display:inline"
id="g2657">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
id="rect2397"
d="m 183.88617,256.82796 0.99991,-16.30721 17.2878,8.44012 26.05488,38.00946 -29.28095,-1.13363 -15.06164,-29.00874 z"
style="fill:url(#linearGradient2406);fill-opacity:1;stroke:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="rect2207"
d="m 200.90647,238.44836 -8.04601,15.77386 -7.05577,-13.57337 15.10178,-2.20049 z"
style="fill:url(#linearGradient2325);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
id="rect1328"
d="m 201.05389,238.55401 62.11704,24.91912 -7.88689,3.21429 -4.35152,9.30976 1.1716,9.96396 -59.31453,-31.72759 -0.49402,-7.36382 3.09592,-5.82826 5.6624,-2.48746 z"
style="fill:url(#linearGradient2227);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
id="rect2204"
d="m 255.27801,266.53504 7.9241,-3.04772 0.85337,10.24037 -3.9011,8.28983 -8.04601,3.77919 -1.341,-9.63083 4.51064,-9.63084 z"
style="fill:url(#radialGradient2317);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="rect2210"
d="m 195.7549,241.421 59.13059,24.7962 -4.5917,9.76614 -57.48995,-29.00967 2.95106,-5.55267 z"
style="fill:url(#linearGradient2247);fill-opacity:1;stroke:none;display:inline" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
id="rect2308"
d="m 255.02263,275.21029 2.08411,-4.1069 2.96459,-1.06995 0.69433,3.37197 -1.76759,3.85723 -3.15516,1.38315 -0.82028,-3.4355 z"
style="fill:#00b527;fill-opacity:1;stroke:none" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
id="rect2327"
d="m 186.56849,241.00362 3.54963,-0.47312 -2.02297,3.53926 -1.52666,-3.06614 z"
style="fill:#258209;fill-opacity:1;stroke:none;display:inline" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,160 @@
<?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"
id="svg3201"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="48"
height="48"
sodipodi:docname="DrupalMu.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/DrupalMu.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata3207">
<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>
<defs
id="defs3205">
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.89261,32.892968)"
gradientUnits="userSpaceOnUse"
id="radialGradient3610"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
<linearGradient
id="linearGradient8838-7">
<stop
id="stop8840-0"
style="stop-color:black;stop-opacity:1"
offset="0" />
<stop
id="stop8842-7"
style="stop-color:black;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.84341,35.950626)"
gradientUnits="userSpaceOnUse"
id="radialGradient3708"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1024"
id="namedview3203"
showgrid="false"
inkscape:zoom="3.8959303"
inkscape:cx="0.68424809"
inkscape:cy="43.440013"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg3201" />
<path
inkscape:connector-curvature="0"
d="m 48.122094,39.37881 c 0,4.34954 -10.77808,7.87555 -24.0735,7.87555 -13.29542,0 -24.07349952,-3.52601 -24.07349952,-7.87555 0,-4.34955 10.77807952,-7.87556 24.07349952,-7.87556 13.29542,0 24.0735,3.52601 24.0735,7.87556 l 0,0 z"
id="path8836-9"
style="opacity:0.3;fill:url(#radialGradient3708);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible" />
<rect
style="fill:#eeeeec;stroke:#eeeeec"
id="rect3747"
width="31.308554"
height="23.776344"
x="8.3035679"
y="14.241219"
ry="11.888172" />
<path
style="fill:#93c5e4;fill-opacity:1"
d="M 10.280293,40.441737 C 7.2051807,40.132052 4.8722076,39.06874 2.8990174,37.077535 -0.40380281,33.74456 -1.1067297,27.727676 1.1759893,23.645787 2.1196585,21.958348 3.8188126,20.299384 5.4807087,19.442889 l 0.7741857,-0.398994 0.011289,-1.226072 C 6.2971444,14.455434 7.6216401,10.77602 10.168688,8.3898403 14.089062,4.7170682 19.858239,3.8729229 24.794165,6.249841 c 2.531395,1.2190065 4.768749,3.4149697 5.824881,5.717118 0.134346,0.292851 0.276817,0.532457 0.316601,0.532457 0.0398,0 0.324958,-0.170005 0.633723,-0.377787 2.080065,-1.399783 5.000273,-1.351751 7.212027,0.118622 2.543639,1.69101 3.424188,5.866753 1.989066,8.518588 -0.156214,0.28865 -0.263577,0.53767 -0.238588,0.553375 0.02499,0.0157 0.483839,0.17317 1.019662,0.349919 2.556281,0.843227 4.816568,3.00377 5.753516,5.499616 0.314345,0.837359 0.611974,2.379682 0.611974,3.17129 0,0.28762 -0.07623,1.86758 -0.169403,2.401864 -0.631568,3.621676 -3.342607,6.462001 -7.126882,7.466741 l -0.926404,0.245964 -14.513874,0.01651 c -7.982628,0.0092 -14.687708,-9.84e-4 -14.900171,-0.02238 z"
id="path3726"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscsssssssscsssssccs" />
<g
id="g3003"
transform="matrix(0.64828664,0,0,0.64828664,7.5527918,8.5331199)"
inkscape:transform-center-x="-1.0664281"
inkscape:transform-center-y="-3.7324978">
<path
inkscape:connector-curvature="0"
id="path6"
d="M 34.090098,9.1797034 C 31.767567,7.7335672 29.576496,7.1638982 27.385409,5.7178032 26.026937,4.7975404 24.142593,2.6064525 22.565038,0.72214222 22.25828,3.7458125 21.33801,4.9728263 20.286308,5.8492663 18.05144,7.6020962 16.649147,8.1279792 14.721005,9.1797034 13.099587,10.012281 4.2914557,15.27086 4.2914557,26.576856 c 0,11.305933 9.5092553,19.632018 20.0702633,19.632018 10.560964,0 19.719685,-7.668772 19.719685,-19.281495 0,-11.612658 -8.589013,-16.871239 -9.991306,-17.7476756 z"
style="fill:#00598e" />
<path
inkscape:connector-curvature="0"
id="path8"
d="m 30.530787,36.388028 c 0.701163,0 1.44609,0.0438 1.97197,0.394389 0.525883,0.350543 0.832613,1.139364 1.007874,1.577551 0.175326,0.438212 0,0.701153 -0.350524,0.87644 -0.306789,0.175265 -0.35059,0.08764 -0.65735,-0.482051 -0.306753,-0.569651 -0.569681,-1.139366 -2.103434,-1.139366 -1.533754,0 -2.015805,0.525853 -2.760753,1.139366 -0.744944,0.613515 -1.007869,0.832642 -1.270808,0.482051 -0.262941,-0.350587 -0.175315,-0.701169 0.306737,-1.139365 0.482055,-0.4382 1.270828,-1.139365 2.015808,-1.44609 0.744953,-0.306791 1.139366,-0.262925 1.84048,-0.262925 l 0,0 z"
style="fill:#ffffff" />
<path
inkscape:connector-curvature="0"
id="path10"
d="m 23.256403,41.383671 c 0.876441,0.701124 2.191068,1.270827 4.995651,1.270827 2.804586,0 4.776586,-0.78882 5.652982,-1.446142 0.394412,-0.306731 0.569698,-0.04382 0.613499,0.131465 0.04386,0.175314 0.131464,0.43824 -0.175262,0.744974 -0.219127,0.21912 -2.234927,1.62141 -4.601264,1.840515 -2.366381,0.219093 -5.565349,0.350557 -7.493509,-1.402279 -0.306727,-0.306772 -0.219064,-0.74497 0,-0.920281 0.219126,-0.175272 0.394411,-0.306735 0.657352,-0.306735 0.262941,0 0.219087,0 0.350551,0.08764 l 0,0 z"
style="fill:#ffffff" />
<path
inkscape:connector-curvature="0"
id="path12"
d="m 12.520122,38.053242 c 3.33044,-0.04387 3.943947,-0.613513 6.879994,-1.928148 15.863396,-7.099116 18.799442,-13.584698 19.369108,-15.074612 0.569699,-1.489957 1.402279,-3.900151 0.525882,-6.573243 -0.169228,-0.516053 -0.29251,-0.930415 -0.381545,-1.263793 C 36.805518,10.856247 34.712656,9.5700024 34.080346,9.1748343 31.801615,7.728743 29.566731,7.159029 27.375628,5.7129375 26.017185,4.8365024 24.132844,2.6016154 22.555289,0.71728498 22.2485,3.7409777 21.372104,5.0118054 20.276556,5.8444006 18.041664,7.5972796 16.639386,8.1231321 14.711225,9.1748343 13.089836,10.051274 4.2816978,15.266054 4.2816978,26.571983 c 0,3.609719 0.9695098,6.915694 2.6329705,9.743944 l 0.4345376,-0.01556 c 0.920247,0.832595 2.3663601,1.796678 5.1709161,1.752879 z"
style="fill:#0073ba" />
<path
inkscape:connector-curvature="0"
id="path14"
d="M 34.080346,9.1748343 C 31.801615,7.728743 29.566731,7.159029 27.375628,5.7129375 26.017185,4.8365024 24.132844,2.6016154 22.555289,0.71728498 22.2485,3.7409777 21.372104,5.0118054 20.276556,5.8444006 18.041664,7.5972796 16.639386,8.1231321 14.711225,9.1748343 13.089836,10.051274 4.2816978,15.266054 4.2816978,26.571983 c 0,3.609719 0.9695098,6.915694 2.6329705,9.743944 3.5466277,6.029889 10.2482267,9.888084 17.4372997,9.888084 10.560956,0 19.719684,-7.668772 19.719684,-19.281437 0,-6.377263 -2.590255,-10.837861 -5.158091,-13.709128 -2.108043,-2.357199 -4.201049,-3.6434436 -4.833215,-4.0386117 z m 5.326588,5.1298647 c 2.877156,3.597128 4.336061,7.842377 4.336061,12.617875 0,2.771148 -0.527811,5.388863 -1.568747,7.780659 -0.987459,2.26883 -2.4094,4.278898 -4.226366,5.974547 -3.591569,3.351706 -8.41997,5.197573 -13.595914,5.197573 -2.560687,0 -5.073487,-0.489081 -7.468591,-1.453807 -2.352527,-0.94753 -4.472551,-2.3043 -6.30115,-4.032752 -3.8510202,-3.639797 -5.9718669,-8.546757 -5.9718669,-13.816811 0,-4.693944 1.5235887,-8.864822 4.5283833,-12.396626 2.2956266,-2.698273 4.7710076,-4.1936671 5.7287516,-4.7113412 0.46499,-0.2536462 0.901182,-0.4774924 1.323031,-0.6939519 1.32225,-0.678689 2.571148,-1.3196827 4.288889,-2.6669881 0.915988,-0.6961406 1.891737,-1.7987311 2.307277,-4.5984547 1.447515,1.7216416 3.127255,3.6563963 4.410821,4.484523 1.139353,0.7519791 2.307987,1.2788589 3.438159,1.7885041 1.069149,0.482029 2.174638,0.9804706 3.268631,1.674732 0.0018,0.00136 0.04099,0.025553 0.04099,0.025553 3.197924,1.9966268 4.901719,4.1267468 5.461639,4.8267578 z"
style="fill:#004975" />
<path
inkscape:connector-curvature="0"
id="path16"
d="m 22.774381,1.5937182 c 0.613483,1.7966831 0.525884,2.7169311 0.525884,3.1113198 0,0.394389 -0.219126,1.4461034 -0.920295,1.9719862 -0.306733,0.2190788 -0.394395,0.3944051 -0.394395,0.4382036 0,0.1752646 0.394395,0.3067268 0.394395,0.7011334 0,0.4820332 -0.21907,1.4461351 -2.541615,3.7686668 -2.322573,2.322534 -5.653013,4.382137 -8.238475,5.652966 C 9.0144231,18.508823 7.7874103,18.421161 7.4368549,17.807679 7.0862773,17.194197 7.5683179,15.835704 9.1896923,14.039023 10.811103,12.242345 15.93822,9.6568851 15.93822,9.6568851 L 22.336162,5.1870844 22.68675,3.4780508"
style="fill:#93c5e4" />
<path
inkscape:connector-curvature="0"
id="path18"
d="m 22.774381,1.5498967 c -0.394411,2.8922154 -1.270807,3.7686495 -2.454025,4.6888929 -1.971942,1.4899534 -3.900084,2.4101952 -4.338323,2.6293198 -1.139337,0.56965 -5.258593,2.8483826 -7.4058104,6.1350136 -0.6573465,1.007866 0,1.402277 0.1314641,1.489895 0.131463,0.08765 1.6213643,0.262925 4.8203393,-1.665218 3.198971,-1.928143 4.601261,-3.067507 6.397943,-4.951807 0.964103,-1.0078836 1.095567,-1.5775986 1.095567,-1.8405239 0,-0.3067261 -0.219128,-0.4381895 -0.569719,-0.5258525 -0.175261,-0.043802 -0.219059,-0.1314622 0,-0.2629256 0.219128,-0.1314632 1.139369,-0.5696668 1.358493,-0.744977 0.219126,-0.1752639 1.270829,-0.8764349 1.314629,-2.0157916 0.04386,-1.1393393 -0.04381,-1.9281403 -0.350558,-2.9360257 l 0,0 z"
style="fill:#ffffff" />
<path
inkscape:connector-curvature="0"
id="path20"
d="m 12.914535,33.408116 c 0.04385,-3.418034 3.242809,-6.617006 7.274359,-6.660868 5.127116,-0.04381 8.676675,5.083317 11.26213,5.039515 2.191071,-0.04387 6.397946,-4.338339 8.457552,-4.338339 2.191088,0 2.804583,2.278733 2.804583,3.637161 0,1.358492 -0.438189,3.812486 -1.489891,5.346241 -1.051765,1.533753 -1.709079,2.103413 -2.936046,2.015805 -1.577616,-0.131461 -4.732726,-5.039515 -6.748531,-5.127114 -2.541658,-0.08765 -8.063164,5.302379 -12.4015,5.302379 -2.629275,0 -3.418098,-0.394389 -4.294495,-0.964053 -1.31467,-0.920292 -1.971987,-2.322573 -1.928161,-4.250727 l 0,0 z"
style="fill:#ffffff" />
<path
inkscape:connector-curvature="0"
id="path22"
d="m 39.295106,14.477239 c 0.876397,2.673092 0.04383,5.083286 -0.525882,6.573243 -0.569666,1.489914 -3.505712,7.975496 -19.369108,15.074612 -2.936047,1.314635 -3.549554,1.884286 -6.879994,1.928148 -2.804556,0.04381 -4.2506691,-0.920282 -5.1709161,-1.752877 l -0.4345376,0.01555 C 10.461296,42.345805 17.162895,46.204 24.351968,46.204 c 10.560956,0 19.719684,-7.668772 19.719684,-19.281438 0,-6.377263 -2.590255,-10.83786 -5.158091,-13.709127 0.08902,0.333378 0.212317,0.74774 0.381545,1.263793 z"
style="fill:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="300"
height="159"
viewBox="0 0 300 160"
id="svg2943">
<defs
id="defs2945">
<linearGradient
x1="150"
y1="84"
x2="299"
y2="84"
id="linearGradient3798"
gradientUnits="userSpaceOnUse">
<stop
id="stop3800"
style="stop-color:#dddce9;stop-opacity:1"
offset="0" />
<stop
id="stop3802"
style="stop-color:#5664a3;stop-opacity:1"
offset="0.37" />
<stop
id="stop3804"
style="stop-color:#000000;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
cx="77.914261"
cy="-48.544521"
r="146"
fx="77.914261"
fy="-48.544521"
id="radialGradient3870"
xlink:href="#linearGradient3798"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
</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>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
version="1.1"
width="48"
height="48"
viewBox="0 0 48 48.301887"
id="svg2943"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="PHPFCGI.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/PHPFCGI.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata15">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1024"
id="namedview13"
showgrid="false"
inkscape:zoom="23.834212"
inkscape:cx="29.136432"
inkscape:cy="25.452333"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2943" />
<defs
id="defs2945">
<linearGradient
x1="150"
y1="84"
x2="299"
y2="84"
id="linearGradient3798"
gradientUnits="userSpaceOnUse">
<stop
id="stop3800"
style="stop-color:#dddce9;stop-opacity:1"
offset="0" />
<stop
id="stop3802"
style="stop-color:#5664a3;stop-opacity:1"
offset="0.37" />
<stop
id="stop3804"
style="stop-color:#000000;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
cx="77.914261"
cy="-48.544521"
r="146"
fx="77.914261"
fy="-48.544521"
id="radialGradient3870"
xlink:href="#linearGradient3798"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
<radialGradient
cx="77.914261"
cy="-48.544521"
r="146"
fx="77.914261"
fy="-48.544521"
id="radialGradient3870-6"
xlink:href="#linearGradient3798-8"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
<linearGradient
x1="150"
y1="84"
x2="299"
y2="84"
id="linearGradient3798-8"
gradientUnits="userSpaceOnUse">
<stop
id="stop3800-0"
style="stop-color:#dddce9;stop-opacity:1"
offset="0" />
<stop
id="stop3802-9"
style="stop-color:#5664a3;stop-opacity:1"
offset="0.37" />
<stop
id="stop3804-4"
style="stop-color:#000000;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
inkscape:collect="always"
id="filter3797"
x="-0.13393395"
width="1.2678679"
y="-0.25729417"
height="1.5145883">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="16.295298"
id="feGaussianBlur3799" />
</filter>
</defs>
<ellipse
cx="150"
cy="80"
rx="146"
ry="76"
id="ellipse3860-0"
style="fill:#555753;fill-opacity:1;stroke:#555753;stroke-width:5.5;filter:url(#filter3797)"
sodipodi:cx="150"
sodipodi:cy="80"
sodipodi:rx="146"
sodipodi:ry="76"
transform="matrix(0.13546531,0,0,0.05031395,5.6817942,36.572954)" />
<ellipse
cx="150"
cy="80"
rx="146"
ry="76"
id="ellipse3860"
style="fill:#6c7eb7;stroke:url(#radialGradient3870);stroke-width:5.5"
sodipodi:cx="150"
sodipodi:cy="80"
sodipodi:rx="146"
sodipodi:ry="76"
transform="matrix(0.1620589,0,0,0.25666252,-0.33382188,2.2009827)" />
<path
d="m 5.4618682,23.497045 2.59294,-13.126772 5.9961898,0 c 2.59294,0.162059 3.889411,1.458533 3.889411,3.727358 0,3.889414 -3.07915,6.158237 -5.834128,5.996179 l -2.9170559,0 -0.6482379,3.403235 -3.079119,0 z m 4.3755911,-5.834117 0.8102927,-4.861768 2.106768,0 c 1.13439,0 1.944715,0.486177 1.944715,1.45853 -0.162094,2.755001 -1.458553,3.241178 -2.917071,3.403238 l -1.9447047,0 z"
id="p"
style="fill-rule:evenodd;stroke:#ffffff;stroke-width:0.32411811;stroke-linejoin:round"
inkscape:connector-curvature="0" />
<path
d="m 18.305109,20.09381 2.592939,-13.1267725 3.079118,0 -0.648239,3.4032355 2.917073,0 c 2.592954,0.162059 3.56531,1.458533 3.241193,3.079124 l -1.134446,6.644413 -3.241174,0 1.134427,-5.996179 c 0.162043,-0.810294 0.162043,-1.296471 -0.972356,-1.296471 l -2.430892,0 -1.45854,7.29265 -3.079103,0 z"
id="h"
style="stroke:#ffffff;stroke-width:0.32411811;stroke-linejoin:round"
inkscape:connector-curvature="0" />
<use
transform="translate(23.728448,-1.7015835e-7)"
id="p2"
xlink:href="#p"
x="0"
y="0"
width="300"
height="160" />
<text
xml:space="preserve"
style="font-size:14.08805083999999930px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:128.99999619000001871%;letter-spacing:-0.96603775000000003px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono Bold Italic;stroke-width:0.30188679;stroke-miterlimit:4;stroke-dasharray:none"
x="7.2367697"
y="35.194664"
id="text2992"
sodipodi:linespacing="129%"><tspan
sodipodi:role="line"
id="tspan2994"
x="7.2367697"
y="35.194664"
style="font-size:14.08805083999999930px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-0.96603775000000003px;stroke:#ffffff;stroke-width:0.30188679;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:OpenSymbol;-inkscape-font-specification:OpenSymbol Bold Italic">FCGI</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
version="1.1"
width="48"
height="48"
viewBox="0 0 48 48.301887"
id="svg2943"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="PHPFPM.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/PHPFPM.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata15">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1024"
id="namedview13"
showgrid="false"
inkscape:zoom="8.4266665"
inkscape:cx="-4.8021953"
inkscape:cy="14.32354"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2943" />
<defs
id="defs2945">
<linearGradient
x1="150"
y1="84"
x2="299"
y2="84"
id="linearGradient3798"
gradientUnits="userSpaceOnUse">
<stop
id="stop3800"
style="stop-color:#dddce9;stop-opacity:1"
offset="0" />
<stop
id="stop3802"
style="stop-color:#5664a3;stop-opacity:1"
offset="0.37" />
<stop
id="stop3804"
style="stop-color:#000000;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
cx="77.914261"
cy="-48.544521"
r="146"
fx="77.914261"
fy="-48.544521"
id="radialGradient3870"
xlink:href="#linearGradient3798"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
<radialGradient
cx="77.914261"
cy="-48.544521"
r="146"
fx="77.914261"
fy="-48.544521"
id="radialGradient3870-6"
xlink:href="#linearGradient3798-8"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5089497,0,0,1.3582164,-39.028917,76.957747)" />
<linearGradient
x1="150"
y1="84"
x2="299"
y2="84"
id="linearGradient3798-8"
gradientUnits="userSpaceOnUse">
<stop
id="stop3800-0"
style="stop-color:#dddce9;stop-opacity:1"
offset="0" />
<stop
id="stop3802-9"
style="stop-color:#5664a3;stop-opacity:1"
offset="0.37" />
<stop
id="stop3804-4"
style="stop-color:#000000;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
inkscape:collect="always"
id="filter3797"
x="-0.13393395"
width="1.2678679"
y="-0.25729417"
height="1.5145883">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="16.295298"
id="feGaussianBlur3799" />
</filter>
</defs>
<ellipse
cx="150"
cy="80"
rx="146"
ry="76"
id="ellipse3860-0"
style="fill:#555753;fill-opacity:1;stroke:#555753;stroke-width:5.5;filter:url(#filter3797)"
sodipodi:cx="150"
sodipodi:cy="80"
sodipodi:rx="146"
sodipodi:ry="76"
transform="matrix(0.13546531,0,0,0.05031395,5.6817942,36.572954)" />
<ellipse
cx="150"
cy="80"
rx="146"
ry="76"
id="ellipse3860"
style="fill:#6c7eb7;stroke:url(#radialGradient3870);stroke-width:5.5"
sodipodi:cx="150"
sodipodi:cy="80"
sodipodi:rx="146"
sodipodi:ry="76"
transform="matrix(0.1620589,0,0,0.25666252,-0.33382188,2.2009827)" />
<path
d="m 5.4618682,23.497045 2.59294,-13.126772 5.9961898,0 c 2.59294,0.162059 3.889411,1.458533 3.889411,3.727358 0,3.889414 -3.07915,6.158237 -5.834128,5.996179 l -2.9170559,0 -0.6482379,3.403235 -3.079119,0 z m 4.3755911,-5.834117 0.8102927,-4.861768 2.106768,0 c 1.13439,0 1.944715,0.486177 1.944715,1.45853 -0.162094,2.755001 -1.458553,3.241178 -2.917071,3.403238 l -1.9447047,0 z"
id="p"
style="fill-rule:evenodd;stroke:#ffffff;stroke-width:0.32411811;stroke-linejoin:round"
inkscape:connector-curvature="0" />
<path
d="m 18.305109,20.09381 2.592939,-13.1267725 3.079118,0 -0.648239,3.4032355 2.917073,0 c 2.592954,0.162059 3.56531,1.458533 3.241193,3.079124 l -1.134446,6.644413 -3.241174,0 1.134427,-5.996179 c 0.162043,-0.810294 0.162043,-1.296471 -0.972356,-1.296471 l -2.430892,0 -1.45854,7.29265 -3.079103,0 z"
id="h"
style="stroke:#ffffff;stroke-width:0.32411811;stroke-linejoin:round"
inkscape:connector-curvature="0" />
<use
transform="translate(23.728448,-1.7015835e-7)"
id="p2"
xlink:href="#p"
x="0"
y="0"
width="300"
height="160" />
<text
xml:space="preserve"
style="font-size:14.08805084px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:128.99999619%;letter-spacing:-0.96603775px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans Mono;-inkscape-font-specification:Droid Sans Mono Bold Italic"
x="7.638485"
y="35.075245"
id="text2992"
sodipodi:linespacing="129%"><tspan
sodipodi:role="line"
id="tspan2994"
x="7.638485"
y="35.075245"
style="font-size:14.08805084px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-0.96603775px;stroke:#ffffff;stroke-width:0.3018868;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:OpenSymbol;-inkscape-font-specification:OpenSymbol Bold Italic">FPM</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48">
<defs>
<linearGradient id="lg1">
<stop stop-color="#3778ae" offset="0"/>
<stop stop-color="#366c99" offset="1"/>
</linearGradient>
<linearGradient id="lg2">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#fff" stop-opacity="0.165" offset="1"/>
</linearGradient>
<linearGradient id="lg3">
<stop stop-color="#ffe253" offset="0"/>
<stop stop-color="#ffca1c" offset="1"/>
</linearGradient>
<linearGradient id="lg4">
<stop stop-color="#000" offset="0"/>
<stop stop-color="#000" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient x1="94.693" y1="112.511" x2="94.693" y2="64.053" id="lg5" xlink:href="#lg2" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.353878,0,0,0.353878,-17.3795,-19.412)"/>
<linearGradient x1="59.728" y1="102" x2="142.62" y2="102" id="lg6" xlink:href="#lg1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.353878,0,0,0.353878,-17.3795,-19.412)"/>
<linearGradient x1="94.693" y1="112.511" x2="94.693" y2="64.053" id="lg7" xlink:href="#lg2" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.353878,0,0,0.353878,-6.29697,-7.94537)"/>
<linearGradient x1="119.191" y1="89.13" x2="116.965" y2="169.279" id="lg8" xlink:href="#lg3" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.353878,0,0,0.353878,-16.3897,-17.997)"/>
<radialGradient cx="15.115" cy="63.965" r="12.289" fx="15.115" fy="63.965" id="rg1" xlink:href="#lg4" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.6,0,0,0.551754,-0.183975,4.83575)"/>
</defs>
<path d="m 43.6618,40.1289 a 19.6618,6.7805 0 0 1 -39.3236,0 19.6618,6.7805 0 1 1 39.3236,0 z" fill="url(#rg1)"/>
<path d="m 34.0804,37.5795 c 0,2.7319 -2.371,4.1156 -4.7774,4.8057 -3.6202,1.0404 -6.5255,0.8811 -9.5511,0 -2.5268,-0.7362 -4.7774,-2.2402 -4.7774,-4.8057 l 0,-9.0132 c 0,-2.594 2.1621,-4.8092 4.7774,-4.8092 l 9.3984,-0.6111 c 3.1814,0 5.2606,-1.9858 5.2606,-5.245 l 0.8655,-4.3569 3.5813,0 c 2.7814,0 4.0909,2.0667 4.7774,4.8056 0.9555,3.8043 0.9978,6.6495 0,9.615 -0.9661,2.8805 -2,4.8056 -4.7774,4.8056 l -4.7773,0 -9.5512,0 0,-0.02 c 8.2424,0.02 9.5512,-3.3164 9.5512,4.8281 z" fill="url(#lg8)"/>
<path d="m 28.1104,38.1776 c 0,-0.9944 0.8033,-1.8013 1.7906,-1.8013 1,0 1.7906,0.8069 1.7906,1.8013 0,0.9979 -0.7997,1.8047 -1.7906,1.8047 -0.9873,0 -1.7906,-0.8068 -1.7906,-1.8047 z" fill="#fff"/>
<path d="m 35.2662,13.5436 0,4.2265 C 31.8551,23.767 25.375,23.7787 19.939,23.7787 l -2.017,0.2052 c -2.8342,2.4364 -2.9244,6.3463 -2.9223,9.7756 6.2502,-6.9742 12.9174,-7.5004 16.2436,-7.4854 6.6377,0.029 9.7638,-1.6029 12.9339,-5.1939 -0.016,-0.07 0.016,-0.132 0,-0.2037 -0.1222,-0.8 -0.3362,-1.6544 -0.5601,-2.546 -0.3218,-1.2839 -0.7428,-2.4141 -1.4257,-3.2589 -0.016,-0.016 -0.038,-0.034 -0.051,-0.05 0,0 -0.042,0 -0.05,0 -0.023,-0.028 -0.028,-0.075 -0.05,-0.1011 -0.047,-0.034 -0.052,-0.07 -0.101,-0.101 -0.016,-0.016 -0.039,0.016 -0.05,0 C 41.1653,14.3502 40.2858,14 39.3942,13.8519 38.5861,13.7183 37.775,13.6165 36.95,13.5464 l -0.5092,0 -1.1711,0 z" opacity="0.837" fill="url(#lg7)"/>
<path d="m 34.559,37.579 c 0,2.732 -2.8497,4.116 -5.256,4.806 -3.62,1.04 -6.525,0.881 -9.551,0 -2.527,-0.736 -4.801,-2.24 -4.805,-4.805 l -0.016,-9.258 c -0.01,-3.073 2.302,-5.01 4.849,-5 l 8.4667,0.015 c 3.25,0 6.41,-3.3 6.401,-6.55 l 0,-3.237 4.21,0 c 2.7814,0 4.09,2.067 4.777,4.806 0.956,3.804 1,6.649 0,9.615 -0.966,2.88 -2,5.1476 -4.777,5.148 l -4.3,0 -10.03,0 0,-0.02 10.03,0.034 z" fill="none" stroke="#bb9400" stroke-width="1.63"/>
<path d="m 35.4653,14.3584 0,2.3933 c 0,3.7245 -3.4965,7.3757 -7.4809,7.3757 l -8.2491,0 c -2.8506,0 -4.0184,2.1569 -4.0042,4.4379 l 0.056,9.0129 c 0.01,1.0446 0.4,1.8235 1.148,2.4951 0.748,0.6715 1.8726,1.183 3.0553,1.5277 2.9164,0.8492 5.576,1.0022 9.0638,0 1.1327,-0.3248 2.6394,-0.9401 3.3976,-1.5964 0.7581,-0.6565 1.3077,-1.2961 1.3077,-2.4264 l 0,-3.6298 -10.052,0 0,-1.6795 15.1743,0.05 c 1.2008,0 1.788,-0.4724 2.3844,-1.1633 0.5964,-0.691 1.1149,-2.0469 1.5874,-3.456 0.9456,-2.8096 0.9336,-5.4491 0,-9.1658 -0.3252,-1.2978 -0.7976,-2.3461 -1.4257,-3.0552 -0.628,-0.7091 -1.357,-1.1202 -2.5461,-1.1202 z m 1.6294,1.6294 1.7867,0 c 0.5202,0 1.1993,0.4194 1.324,0.5602 0.3625,0.4093 0.78,1.2386 1.0694,2.3932 0.8895,3.5416 0.8915,5.7515 0.05,8.2491 -0.4517,1.347 -0.9205,2.6142 -1.2729,3 -0.3184,0.3395 -1,0.5093 -1.1713,0.5093 l -4.7865,-0.051 -12.0173,0 0,4.9384 10.052,0 0,2 c 0,0.727 -0.9733,1.1789 -1.1408,1.3239 -0.4872,0.4219 -1.3508,0.8359 -2.3423,1.1202 -3.223,0.9264 -5.5,0.7859 -8.1984,0 -1.0211,-0.2975 -1.9061,-0.7339 -2.4069,-1.1595 -0.5008,-0.4256 -0.5316,-0.7741 -0.6238,-1.285 l -0.056,-9.0129 c -0.013,-2.0224 0.6457,-2.8084 2.3748,-2.8084 l 8.2491,0 c 4.9233,0 9.1103,-4.3502 9.1103,-9.0052 1e-4,-0.2574 -10e-5,-0.5149 -10e-5,-0.7723 z" opacity="0.384" fill="#fff"/>
<path d="m 13.9847,7.3199 c 0,-2.7319 0.7324,-4.2182 4.7772,-4.926 2.7462,-0.4813 6.2673,-0.5414 9.5513,0 2.5939,0.4282 4.7774,2.3604 4.7774,4.926 l 0,9.0134 c 0,2.6434 -2.1198,4.8091 -4.7774,4.8091 l -8.6621,0.4788 c -3.2414,0 -6.589,2.8321 -6.589,5.957 l -0.2736,3.777 -3.284,0 c -2.7779,0 -4.3951,-2.0029 -5.0745,-4.8056 -0.9166,-3.7651 -0.8776,-6.0088 0,-9.6147 0.7608,-3.1461 3.192,-4.8058 5.7,-4.8058 l 3.5848,0 c 0,0 8.9448,-0.035 0,-0.032 l 0,-4.7771 0,0 z" fill="url(#lg6)"/>
<path d="m 16.3733,6.7218 c 0,-1 0.8,-1.8048 1.7906,-1.8048 0.9873,0 1.7907,0.8068 1.7907,1.8048 0,0.9944 -0.8034,1.8012 -1.7907,1.8012 -0.9908,0 -1.7906,-0.8068 -1.7906,-1.8012 z" fill="#fff"/>
<path d="M 22.7173,1.9955 C 21.3039,2.0297 19.9469,2.1923 18.7455,2.4028 14.7007,3.1106 14.01,4.6102 14.01,7.3421 l -0.2952,4.787 -3.3203,-3e-4 c -2.7779,0 -5.1968,1.6406 -5.9576,4.7864 -0.5165,2.1225 -0.7504,3.7842 -0.6621,5.5504 6.2924,-7.1319 13.0442,-7.6531 16.3965,-7.638 6.6376,0.029 9.7637,-1.6029 12.9338,-5.1939 l 0,-2.2914 c 0,-2.5655 -2.1926,-4.511 -4.7865,-4.9392 C 26.4714,2.0983 24.5345,1.9515 22.7173,1.9955 z" opacity="0.377" fill="url(#lg5)"/>
<path d="m 18.696,2.6271 c 3.6201,-1.0404 6.5255,-0.8811 9.551,0 2.5268,0.7361 4.7787,2.24 4.7774,4.8057 l 0,9.7315 c 0,1.3662 -1.9746,4.5293 -4.7726,4.5293 l -8.8988,0 c -2.4983,0 -5.997,2.7217 -5.997,5.981 l 0,3.7937 -4.21,0 c -2.7815,0 -4.0909,-2.0667 -4.7774,-4.8058 -0.9555,-3.8041 -1,-6.6493 0,-9.6148 0.9661,-2.8806 2,-5.1477 4.7774,-5.1477 l 4.2985,0 10.03,0 0,0.019 -10.03,-0.034 0.019,-4.8733 c -0.026,-1.9965 2.4146,-3.5524 5.2323,-4.3845 z" fill="none" stroke="#274e70" stroke-width="1.63"/>
<path d="m 12.5337,30.6538 0,-2.796 c 0,-3.7246 3.3953,-7.0287 7.0461,-7.0287 l 8.6839,0 c 2.1549,0 4.024,-2.2411 4.0042,-4.3821 L 32.1845,7.434 C 32.1748,6.3895 31.8123,5.6105 31.0642,4.9389 30.3163,4.2673 29.1916,3.7558 28.009,3.4113 c -2.9166,-0.8494 -5.5762,-1.0023 -9.0639,0 -1.1326,0.3248 -2.6396,0.94 -3.3976,1.5964 -0.7582,0.6564 -1.3076,1.296 -1.3076,2.4263 l 0,3.6298 10.0519,0 0,1.6804 -15.1743,-0.051 c -1.2,0 -1.788,0.4724 -2.3844,1.1634 -0.5964,0.6908 -1.1149,2.0467 -1.5874,3.4559 -0.9456,2.8096 -0.9335,5.449 0,9.1657 0.3252,1.2978 0.7976,2.3461 1.4258,3.0552 0.628,0.7092 1.357,1.1203 2.546,1.1203 l 3.4162,0 z m -1.6295,-1.6295 -1.7867,0 c -0.52,0 -1.2,-0.4194 -1.3239,-0.56 C 7.431,28.0549 7.0135,27.2255 6.7241,26.0709 5.8346,22.5294 5.8327,20.3196 6.6736,17.8218 7.1253,16.475 7.5941,15.2077 7.9466,14.8319 8.265,14.4926 8.9375,14.3227 9.1178,14.3227 l 16.8038,0.051 0,-4.9394 -10.0519,0 0,-2 c 0,-0.727 0.9732,-1.1789 1.1407,-1.3239 0.4873,-0.4219 1.3509,-0.8359 2.3424,-1.12 3.2229,-0.9262 5.5,-0.7858 8.1983,0 1.0212,0.2975 1.9061,0.7339 2.3932,1.1712 0.1733,0.1352 0.2646,0.2973 0.4078,0.4582 0.1987,0.237 0.1552,0.55023 0.2011,0.8147 l 0,9.013 c 0,1.2357 -1.0565,2.7527 -2.2913,2.7527 l -8.6839,0 c -4.5897,0 -8.6756,4.0029 -8.6756,8.6581 0,0.3875 0,1.166 0,1.166 z" opacity="0.165" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,394 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
version="1.0"
width="48"
height="48"
id="svg13684"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Stats.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/Stats.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata77">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview75"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="24"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg13684" />
<defs
id="defs3">
<linearGradient
x1="-20.75"
y1="29"
x2="-19.5"
y2="18.75"
id="G1"
xlink:href="#G2"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="G2">
<stop
id="stop6689"
style="stop-color:#fff"
offset="0" />
<stop
id="stop6691"
style="stop-color:#fff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="-20.75"
y1="29"
x2="-19.5"
y2="18.75"
id="G3"
xlink:href="#G2"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="G4">
<stop
id="stop6721"
style="stop-color:#73d216"
offset="0" />
<stop
id="stop6723"
style="stop-color:#d5f7b3"
offset="1" />
</linearGradient>
<linearGradient
x1="-17.863041"
y1="30.827509"
x2="-20.821646"
y2="25.015009"
id="G0"
xlink:href="#G4"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="G5">
<stop
id="stop6713"
style="stop-color:#5b8ccb"
offset="0" />
<stop
id="stop6715"
style="stop-color:#3465a4"
offset="1" />
</linearGradient>
<linearGradient
x1="-16.387411"
y1="24.453547"
x2="-9.7352734"
y2="28.195539"
id="G6"
xlink:href="#G5"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="G7">
<stop
id="stop6701"
style="stop-color:#ff7171"
offset="0" />
<stop
id="stop6703"
style="stop-color:#cc0000"
offset="1" />
</linearGradient>
<linearGradient
x1="-22.976406"
y1="18.516047"
x2="-14.360273"
y2="33.016045"
id="G8"
xlink:href="#G7"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="G9">
<stop
id="stop4544"
style="stop-color:#000"
offset="0" />
<stop
id="stop4546"
style="stop-color:#000;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
cx="24.306795"
cy="42.07798"
r="15.821514"
fx="24.306795"
fy="42.07798"
id="R1"
xlink:href="#G9"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.284916,0,30.08928)" />
<linearGradient
id="G10">
<stop
id="stop2458"
style="stop-color:#000"
offset="0" />
<stop
id="stop2460"
style="stop-color:#000;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="G11">
<stop
id="stop270"
style="stop-color:#a3a3a3"
offset="0" />
<stop
id="stop271"
style="stop-color:#4c4c4c"
offset="1" />
</linearGradient>
<radialGradient
cx="8.824419"
cy="3.7561285"
r="37.751713"
fx="8.824419"
fy="3.7561285"
id="R2"
xlink:href="#G11"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.971646,0,0,1.034707,3.240408,0.495684)" />
<linearGradient
id="G12">
<stop
id="stop260"
style="stop-color:#fafafa"
offset="0" />
<stop
id="stop261"
style="stop-color:#bbbbbb"
offset="1" />
</linearGradient>
<radialGradient
cx="33.966679"
cy="35.736916"
r="86.70845"
fx="33.966679"
fy="35.736916"
id="R3"
xlink:href="#G12"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.963839,0,0,1.043088,-0.124825,-0.15197)" />
<linearGradient
id="G13">
<stop
id="stop15664"
style="stop-color:#fff"
offset="0" />
<stop
id="stop15666"
style="stop-color:#f8f8f8"
offset="1" />
</linearGradient>
<radialGradient
cx="8.1435566"
cy="7.2678967"
r="38.158695"
fx="8.1435566"
fy="7.2678967"
id="R4"
xlink:href="#G13"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.974777,0,0,1.034091,3.161873,0.559274)" />
<radialGradient
cx="28.283663"
cy="47.400623"
r="13.804391"
fx="28.283663"
fy="47.400623"
id="R5"
xlink:href="#G10"
gradientUnits="userSpaceOnUse"
gradientTransform="scale(1.164226,0.85894)" />
</defs>
<g
id="layer1"
style="display:inline">
<path
d="M 40.128309,42.07798 A 15.821514,4.5078058 0 1 1 8.485281,42.07798 A 15.821514,4.5078058 0 1 1 40.128309,42.07798 z"
transform="matrix(1.169294,0,0,0.776431,-4.394955,10.82935)"
id="path3667"
style="opacity:0.56725147;fill:url(#R1);fill-rule:evenodd" />
<rect
width="34.996506"
height="40.997345"
rx="1.1490482"
ry="1.1490481"
x="6.5017405"
y="3.5013213"
id="rect15391"
style="fill:url(#R3);fill-rule:nonzero;stroke:url(#R2);stroke-width:1;stroke-linecap:round;stroke-linejoin:round" />
<rect
width="32.996056"
height="38.996326"
rx="0.14904846"
ry="0.14904855"
x="7.5033512"
y="4.5018268"
id="rect15660"
style="fill:none;fill-rule:nonzero;stroke:url(#R4);stroke-width:1;stroke-linecap:round;stroke-linejoin:round" />
<path
d="M 10,6.002203 L 10,10.966297 L 10,41.002203 L 20,41 L 20,11 L 38,11 L 38,6.002203 L 10,6.002203 z"
id="rect13655"
style="fill:#000;fill-opacity:0.21052629;fill-rule:evenodd" />
<path
d="M 37.500415,10.502203 L 10.499583,10.502203"
id="path13660"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,16.5 L 10.49923,16.5"
id="path13662"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,22.5 L 10.49923,22.5"
id="path13664"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,28.5 L 10.49923,28.5"
id="path13666"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.501332,34.5 L 10.499823,34.5"
id="path13668"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,40.5 L 10.49923,40.5"
id="path13682"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.511644,13.5 L 10.5,13.5"
id="path2464"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,19.5 L 10.49923,19.5"
id="path2466"
style="fill:none;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,25.5 L 10.49923,25.5"
id="path2468"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,31.5 L 10.49923,31.5"
id="path2470"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.500755,37.5 L 10.49923,37.5"
id="path2472"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
<path
d="M 37.5,6.4997386 L 37.5,40.5003"
id="path2485"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.15789466" />
<path
d="M 31.5,6.4996867 L 31.5,40.500315"
id="path2487"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.15789466" />
<path
d="M 25.5,6.4996865 L 25.5,40.500314"
id="path2489"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.15789466" />
<path
d="M 19.5,6.4996867 L 19.5,40.500315"
id="path2491"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.15789466" />
<path
d="M 10.5,6.4996867 L 10.5,40.500315"
id="path2493"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.15789466" />
<path
d="M 37.500227,6.502203 L 10.49946,6.502203"
id="path2495"
style="fill:#000;fill-opacity:0.15789466;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.19298245" />
</g>
<g
id="layer2"
style="display:inline">
<path
d="M 49,40.714287 A 16.071428,11.857142 0 1 1 16.857141,40.714287 A 16.071428,11.857142 0 1 1 49,40.714287 z"
transform="matrix(0.92,0,0,0.92,2.777143,0.114286)"
id="path1693"
style="fill:url(#R5);fill-rule:evenodd" />
<g
transform="translate(51,9.12499)"
id="g6727">
<path
d="M -27.528542,18.092794 C -30.097576,19.791125 -31.511475,22.097833 -31.511475,24.590696 L -31.511475,26.659988 C -31.511475,29.150828 -30.097576,31.458211 -27.528542,33.156541 C -25.102053,34.760445 -21.895416,35.644009 -18.499359,35.644009 C -15.104587,35.644009 -11.89795,34.760445 -9.4701774,33.156541 C -6.9024275,31.458211 -5.4885277,29.151503 -5.4885277,26.659988 L -5.4885277,24.590696 C -5.4885277,22.097833 -6.9030696,19.79045 -9.4701774,18.092794 C -11.897308,16.490239 -15.103945,15.606001 -18.499359,15.606001 C -21.895416,15.606001 -25.102053,16.490239 -27.528542,18.092794 z"
id="path3931"
style="fill:#670000;fill-rule:nonzero" />
<path
d="M -7.0244261,24.453547 C -8.4030105,21.067003 -13.077484,18.573465 -18.636768,18.573465 C -24.196053,18.573465 -28.870526,21.067003 -30.249111,24.453547 L -30.663906,24.453547 L -30.663906,26.523514 C -30.663906,30.913678 -25.27863,34.472213 -18.636768,34.472213 C -11.994265,34.472213 -6.6102729,30.913678 -6.6102729,26.523514 L -6.6102729,24.453547 L -7.0244261,24.453547 L -7.0244261,24.453547 z"
id="path3933"
style="fill:#a40000;fill-rule:nonzero" />
<path
d="M -6.6102729,24.453547 C -6.6102729,28.844385 -11.994265,32.402921 -18.636768,32.402921 C -25.279272,32.402921 -30.663906,28.844385 -30.663906,24.453547 C -30.663906,20.062708 -25.27863,16.504173 -18.636768,16.504173 C -11.994265,16.504173 -6.6102729,20.062708 -6.6102729,24.453547 L -6.6102729,24.453547 z"
id="path3935"
style="fill:url(#G8);fill-rule:nonzero" />
<path
d="M -6.6102729,26.833098 L -6.6256833,24.666681 C -7.9297843,29.167459 -10.862246,31.074203 -14.549173,32.007678 L -14.549173,34.002103 C -10.558534,32.998483 -6.9871844,30.713358 -6.6102729,26.833098 L -6.6102729,26.833098 z"
id="path3937"
style="fill:#204a87;fill-rule:nonzero" />
<path
d="M -6.6102729,24.453547 C -6.8857329,29.533025 -13.16802,31.849176 -14.571646,31.937532 L -18.637411,24.453547 L -6.610915,24.453547 L -6.6102729,24.453547 z"
id="path3939"
style="fill:url(#G6);fill-rule:nonzero" />
<path
d="M -21.154435,34.298198 L -21.154435,32.228906 C -18.891039,32.523652 -16.814494,32.575587 -14.571646,32.015097 L -14.549173,34.002778 C -16.428593,34.619249 -19.36876,34.648251 -21.154435,34.298873 L -21.154435,34.298198 z"
id="path3941"
style="fill:#4e9a06;fill-rule:nonzero" />
<path
d="M -18.637411,24.454221 L -14.571646,31.937532 C -17.06106,32.88652 -21.154435,32.228906 -21.154435,32.228906 L -18.637411,24.454221 z"
id="path3943"
style="fill:url(#G0);fill-rule:nonzero" />
<path
d="M -6.875,24.375 A 11.75,7.75 0 1 1 -30.375,24.375 A 11.75,7.75 0 1 1 -6.875,24.375 z"
transform="matrix(0.979592,0,0,0.979592,-0.380102,0.497449)"
id="path5959"
style="opacity:0.31111115;fill:none;stroke:url(#G3);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter" />
<path
d="M -6.875,24.375 A 11.75,7.75 0 1 1 -30.375,24.375 A 11.75,7.75 0 1 1 -6.875,24.375 z"
transform="matrix(0.979592,0,0,0.979592,-0.380102,2.497449)"
id="path6707"
style="opacity:0.13333327;fill:none;stroke:url(#G1);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="90.000000"
inkscape:export-xdpi="90.000000"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/SymbolicLink.png"
width="48px"
height="48px"
id="svg11300"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="SymbolicLink.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs3">
<linearGradient
id="linearGradient11520">
<stop
id="stop11522"
offset="0.0000000"
style="stop-color:#ffffff;stop-opacity:1.0000000;" />
<stop
id="stop11524"
offset="1.0000000"
style="stop-color:#dcdcdc;stop-opacity:1.0000000;" />
</linearGradient>
<linearGradient
id="linearGradient11508"
inkscape:collect="always">
<stop
id="stop11510"
offset="0"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop11512"
offset="1"
style="stop-color:#000000;stop-opacity:0;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11508"
id="radialGradient1348"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,-1.435476e-15,29.48178)"
cx="30.203562"
cy="44.565483"
fx="30.203562"
fy="44.565483"
r="6.5659914" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11520"
id="radialGradient1366"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
cx="24.445690"
cy="35.878170"
fx="24.445690"
fy="35.878170"
r="20.530962" />
</defs>
<sodipodi:namedview
stroke="#ef2929"
fill="#888a85"
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="0.25490196"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="28.252398"
inkscape:cy="22.342711"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:showpageshadow="false"
inkscape:window-width="872"
inkscape:window-height="707"
inkscape:window-x="271"
inkscape:window-y="164"
inkscape:window-maximized="0" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>Jakub Steiner</dc:title>
</cc:Agent>
</dc:creator>
<dc:source>http://jimmac.musichall.cz</dc:source>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
<dc:title>Symbolic Link</dc:title>
<dc:subject>
<rdf:Bag>
<rdf:li>emblem</rdf:li>
<rdf:li>symbolic</rdf:li>
<rdf:li>link</rdf:li>
<rdf:li>pointer</rdf:li>
<rdf:li>io</rdf:li>
<rdf:li>file</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Attribution" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
sodipodi:type="arc"
style="opacity:0.40641710;color:#000000;fill:url(#radialGradient1348);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:2.8141584;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.000000;stroke-dasharray:none;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;visibility:visible;display:inline;overflow:visible"
id="path11530"
sodipodi:cx="30.203562"
sodipodi:cy="44.565483"
sodipodi:rx="6.5659914"
sodipodi:ry="2.2223356"
d="M 36.769553 44.565483 A 6.5659914 2.2223356 0 1 1 23.637570,44.565483 A 6.5659914 2.2223356 0 1 1 36.769553 44.565483 z"
transform="matrix(2.460049,0.000000,0.000000,2.460049,-49.40946,-67.96374)" />
<rect
ry="5.4548240"
rx="5.4548240"
y="3.5233452"
x="4.4147282"
height="40.061924"
width="40.061924"
id="rect11518"
style="opacity:1.0000000;color:#000000;fill:url(#radialGradient1366);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#9b9b9b;stroke-width:1.0000000;stroke-linecap:butt;stroke-linejoin:bevel;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.000000;stroke-dasharray:none;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;visibility:visible;display:inline;overflow:visible" />
<rect
style="opacity:1.0000000;color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99999976;stroke-linecap:butt;stroke-linejoin:bevel;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10.000000;stroke-dasharray:none;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;visibility:visible;display:inline;overflow:visible"
id="rect11528"
width="37.696587"
height="37.696587"
x="5.5973887"
y="4.7060070"
rx="4.2426391"
ry="4.2426391" />
<path
sodipodi:nodetypes="ccccccc"
id="path2177"
d="M 11.500037,31.436501 C 11.940474,20.09759 22.043105,11.32322 32.158766,21.979434 L 37.068811,17.246167 C 37.068811,17.246167 37.088388,32 37.088388,32 L 22.160133,31.978069 C 22.160133,31.978069 26.997745,27.140456 26.997745,27.140456 C 18.528582,18.264221 13.291696,25.230495 11.500037,31.436501 z "
style="opacity:0.69886361;color:#000000;fill:#888a85;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,127 @@
<?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"
id="svg3201"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="48"
height="48"
sodipodi:docname="WordPressMu.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/apps/WordPressMu.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata3207">
<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>
<defs
id="defs3205">
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.89261,32.892968)"
gradientUnits="userSpaceOnUse"
id="radialGradient3610"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
<linearGradient
id="linearGradient8838-7">
<stop
id="stop8840-0"
style="stop-color:black;stop-opacity:1"
offset="0" />
<stop
id="stop8842-7"
style="stop-color:black;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
r="10.625"
fy="4.625"
fx="62.625"
cy="4.625"
cx="62.625"
gradientTransform="matrix(2.2657407,0,0,0.74122745,-117.84341,35.950626)"
gradientUnits="userSpaceOnUse"
id="radialGradient3708"
xlink:href="#linearGradient8838-7"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1024"
id="namedview3203"
showgrid="false"
inkscape:zoom="5.5096774"
inkscape:cx="61.540584"
inkscape:cy="23.568076"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg3201" />
<path
inkscape:connector-curvature="0"
d="m 48.122094,39.37881 c 0,4.34954 -10.77808,7.87555 -24.0735,7.87555 -13.29542,0 -24.07349952,-3.52601 -24.07349952,-7.87555 0,-4.34955 10.77807952,-7.87556 24.07349952,-7.87556 13.29542,0 24.0735,3.52601 24.0735,7.87556 l 0,0 z"
id="path8836-9"
style="opacity:0.3;fill:url(#radialGradient3708);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible" />
<rect
style="fill:#eeeeec;stroke:#eeeeec"
id="rect3747"
width="31.308554"
height="23.776344"
x="8.3035679"
y="14.241219"
ry="11.888172" />
<path
style="fill:#464342;fill-opacity:1"
d="M 19.03125 4.90625 C 15.791437 4.896058 12.606484 6.0795174 10.15625 8.375 C 7.6092021 10.76118 6.312211 14.450111 6.28125 17.8125 L 6.25 19.03125 L 5.46875 19.4375 C 3.8068539 20.293995 2.1311692 21.968811 1.1875 23.65625 C -1.095219 27.738139 -0.39657021 33.729525 2.90625 37.0625 C 4.8794402 39.053705 7.2061377 40.127815 10.28125 40.4375 C 10.493713 40.4589 17.204872 40.47795 25.1875 40.46875 L 39.6875 40.4375 L 40.625 40.1875 C 44.409275 39.18276 47.118432 36.371676 47.75 32.75 C 47.84317 32.215716 47.90625 30.63137 47.90625 30.34375 C 47.90625 29.552142 47.626845 27.993609 47.3125 27.15625 C 46.375552 24.660404 44.118781 22.499477 41.5625 21.65625 C 41.026677 21.479501 40.55624 21.3282 40.53125 21.3125 C 40.50626 21.29679 40.62504 21.03865 40.78125 20.75 C 42.216372 18.098165 41.324889 13.94101 38.78125 12.25 C 36.569496 10.779627 33.642565 10.725217 31.5625 12.125 C 31.467146 12.189168 31.45817 12.1973 31.375 12.25 C 35.289322 15.015997 37.843598 19.572186 37.84375 24.71875 C 37.84375 33.135623 31.009377 39.96875 22.59375 39.96875 C 14.177874 39.96875 7.3125 33.135623 7.3125 24.71875 C 7.3125 16.302874 14.177874 9.4375 22.59375 9.4375 C 25.479095 9.4375 28.164756 10.25928 30.46875 11.65625 C 29.357794 9.4708026 27.196338 7.4129981 24.78125 6.25 C 22.930278 5.3586557 20.975138 4.9123651 19.03125 4.90625 z M 22.59375 10.15625 C 14.563551 10.15625 8.03125 16.6888 8.03125 24.71875 C 8.03125 32.749198 14.5638 39.28125 22.59375 39.28125 C 30.623451 39.28125 37.15625 32.749198 37.15625 24.71875 C 37.15625 16.6888 30.623451 10.15625 22.59375 10.15625 z M 21.75 11.625 C 22.031322 11.606943 22.307856 11.625 22.59375 11.625 C 26.002555 11.625 29.107992 12.928322 31.4375 15.0625 C 31.380944 15.059261 31.308051 15.03125 31.25 15.03125 C 29.964162 15.03125 29.0625 16.171379 29.0625 17.375 C 29.0625 18.45355 29.680027 19.35895 30.34375 20.4375 C 30.84229 21.309757 31.4375 22.413052 31.4375 24.03125 C 31.4375 25.152154 31.003558 26.470213 30.4375 28.28125 L 29.125 32.65625 L 24.40625 18.5625 C 25.194047 18.521142 25.875 18.4375 25.875 18.4375 C 26.58033 18.354036 26.518827 17.333393 25.8125 17.375 C 25.8125 17.375 23.681802 17.53125 22.3125 17.53125 C 21.025914 17.53125 18.875 17.375 18.875 17.375 C 18.169421 17.333393 18.075671 18.396142 18.78125 18.4375 C 18.78125 18.4375 19.451169 18.521142 20.15625 18.5625 L 22.1875 24.15625 L 19.34375 32.75 L 14.5625 18.5625 C 15.351792 18.521142 16.0625 18.4375 16.0625 18.4375 C 16.767581 18.354036 16.674578 17.333393 15.96875 17.375 C 15.96875 17.375 13.869551 17.53125 12.5 17.53125 C 12.254094 17.53125 11.963446 17.509218 11.65625 17.5 C 13.851141 14.167371 17.530176 11.895861 21.75 11.625 z M 34.0625 18.4375 C 35.085241 20.302601 35.687749 22.44231 35.6875 24.71875 C 35.6875 29.549177 33.048895 33.761288 29.15625 36.03125 L 33.15625 24.46875 C 33.903686 22.601158 34.15625 21.109941 34.15625 19.78125 C 34.15625 19.299652 34.118807 18.854569 34.0625 18.4375 z M 10.625 19.375 L 16.875 36.5 C 12.507484 34.377532 9.5 29.901469 9.5 24.71875 C 9.5 22.820513 9.8984926 21.003413 10.625 19.375 z M 22.8125 25.84375 L 26.84375 36.875 C 26.86991 36.939778 26.902371 37.006442 26.9375 37.0625 C 25.57642 37.541108 24.118519 37.8125 22.59375 37.8125 C 21.30841 37.8125 20.047976 37.626067 18.875 37.28125 L 22.8125 25.84375 z "
id="path3726" />
<g
id="g22"
transform="matrix(0.21649934,0,0,0.21649934,10.650797,12.186804)" />
<g
id="g3002"
transform="matrix(0.68558415,0,0,0.68558415,6.1370097,8.023588)"
style="fill:#5c3566">
<g
transform="translate(0,-74.523003)"
id="Layer_1"
style="fill:#5c3566" />
<g
transform="matrix(0.36340573,0,0,0.36340573,1.731598,2.0710297)"
id="g6"
style="fill:#5c3566">
<g
id="g22-5"
style="fill:#5c3566" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -16,7 +16,7 @@
width="48" width="48"
height="48" height="48"
sodipodi:docname="saas.svg" sodipodi:docname="saas.svg"
inkscape:export-filename="/home/glic3/orchestra/django-orchestra/orchestra/static/orchestra/icons/saas.png" inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/saas.png"
inkscape:export-xdpi="90" inkscape:export-xdpi="90"
inkscape:export-ydpi="90"> inkscape:export-ydpi="90">
<metadata <metadata

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -14,7 +14,7 @@
<div style="margin:20px;"> <div style="margin:20px;">
{% if plugin.icon %} {% if plugin.icon %}
<div id="module_2" class="dashboard-module" style="display: inline-block;"> <div id="module_2" class="dashboard-module" style="display: inline-block;">
<h2>Software as a Service</h2> <h2>{{ plugin_title }}</h2>
<div class="dashboard-module-content"> <div class="dashboard-module-content">
<ul class="fluent-dashboard-appiconlist clearfix" style="padding: 0"> <ul class="fluent-dashboard-appiconlist clearfix" style="padding: 0">
{% for plugin in plugins %} {% for plugin in plugins %}

View File

@ -174,7 +174,6 @@ UNITS_CONVERSIONS = {
} }
def unit_to_bytes(unit): def unit_to_bytes(unit):
unit = unit.upper()
for bytes, units in UNITS_CONVERSIONS.iteritems(): for bytes, units in UNITS_CONVERSIONS.iteritems():
if unit in units: if unit in units:
return bytes return bytes

View File

@ -1,4 +1,6 @@
import collections import collections
import random
import string
def import_class(cls): def import_class(cls):
@ -8,6 +10,10 @@ def import_class(cls):
return getattr(module, cls) return getattr(module, cls)
def random_ascii(length):
return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower()
class OrderedSet(collections.MutableSet): class OrderedSet(collections.MutableSet):
def __init__(self, iterable=None): def __init__(self, iterable=None):
self.end = end = [] self.end = end = []

View File

@ -1,7 +1,5 @@
import datetime import datetime
import os import os
import string
import random
from functools import wraps from functools import wraps
from django.conf import settings from django.conf import settings
@ -15,9 +13,8 @@ from xvfbwrapper import Xvfb
from orchestra.apps.accounts.models import Account from orchestra.apps.accounts.models import Account
from .python import random_ascii
def random_ascii(length):
return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower()
class AppDependencyMixin(object): class AppDependencyMixin(object):