Plugins logic inot a plugins app
This commit is contained in:
parent
49b7be33e3
commit
971b1b6874
23
TODO.md
23
TODO.md
|
@ -169,17 +169,17 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
|
|
||||||
* validate address.forward: if mailbox in account.mailboxes then: _("Please use mailboxes field") or consider removing mailbox support on forward (user@pangea.org instead)
|
* validate address.forward: if mailbox in account.mailboxes then: _("Please use mailboxes field") or consider removing mailbox support on forward (user@pangea.org instead)
|
||||||
|
|
||||||
* remove order in account admin and others admininlines
|
* remove ordering in account admin and others admininlines
|
||||||
|
|
||||||
* Databases.User add reverse M2M databases widget (like mailbox.addresses)
|
* Databases.User add reverse M2M databases widget (like mailbox.addresses)
|
||||||
|
|
||||||
* Change permissions periodically on the web server, to ensure security
|
* Change (correct) permissions periodically on the web server, to ensure security ?
|
||||||
|
|
||||||
* Root owned logs on user's home ?
|
* Root owned logs on user's home ? yes
|
||||||
|
|
||||||
* reconsider binding webapps to systemusers (pangea multiple users wordpress-ftp, moodle-pangea, etc)
|
* reconsider binding webapps to systemusers (pangea multiple users wordpress-ftp, moodle-pangea, etc)
|
||||||
* Secondary user home in /home/secondaryuser and simlink to /home/main/webapps/app so it can have private storage?
|
* Secondary user home in /home/secondaryuser and simlink to /home/main/webapps/app so it can have private storage?
|
||||||
* Grant permissions like in webfaction
|
* Grant permissions to systemusers, the problem of creating a related permission model is out of sync with the server-side. evaluate tradeoff
|
||||||
|
|
||||||
* Secondaryusers home should be under mainuser home. i.e. /home/mainuser/webapps/seconduser_webapp/
|
* Secondaryusers home should be under mainuser home. i.e. /home/mainuser/webapps/seconduser_webapp/
|
||||||
* Make one dedicated CGI user for each account only for CGI execution (fpm/fcgid). Different from the files owner, and without W permissions, so attackers can not inject backdors and malware.
|
* Make one dedicated CGI user for each account only for CGI execution (fpm/fcgid). Different from the files owner, and without W permissions, so attackers can not inject backdors and malware.
|
||||||
|
@ -195,24 +195,13 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
|
|
||||||
* domain validation parse named-checzone output to assign errors to fields
|
* domain validation parse named-checzone output to assign errors to fields
|
||||||
|
|
||||||
|
|
||||||
* Directory Protection on webapp and use webapp path as base path (validate)
|
* Directory Protection on webapp and use webapp path as base path (validate)
|
||||||
* User [Group] webapp/website option (validation) which overrides default mainsystemuser
|
* User [Group] webapp/website option (validation) which overrides default mainsystemuser
|
||||||
|
|
||||||
* validate systemuser.home
|
* validate systemuser.home
|
||||||
|
|
||||||
* Create plugin app
|
|
||||||
|
|
||||||
* Create options widget
|
|
||||||
|
|
||||||
* generic options fpm/fcgid/uwsgi webapps (num procs, idle io timeout)
|
|
||||||
* webapp backend option compatibility check?
|
* webapp backend option compatibility check?
|
||||||
|
|
||||||
|
|
||||||
* Route help text with model name when selecting backend
|
|
||||||
* Service instance name when selecting content_type
|
|
||||||
|
|
||||||
* Address.forward mailbbox validate not available on mailboxes
|
|
||||||
|
|
||||||
|
|
||||||
* Miscellaneous service construct form for specific data, fields, validation, uniquenes.. etc (domain usecase)
|
* Miscellaneous service construct form for specific data, fields, validation, uniquenes.. etc (domain usecase)
|
||||||
|
|
||||||
|
* miscellaneous.indentifier.endswith(('.org', '.es', '.cat'))
|
||||||
|
|
|
@ -163,75 +163,6 @@ class ExtendedModelAdmin(ChangeViewActionsMixin, ChangeAddFieldsMixin, admin.Mod
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class SelectPluginAdminMixin(object):
|
|
||||||
plugin = None
|
|
||||||
plugin_field = None
|
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
|
||||||
if obj:
|
|
||||||
self.form = getattr(obj, '%s_class' % self.plugin_field)().get_form()
|
|
||||||
else:
|
|
||||||
self.form = self.plugin.get_plugin(self.plugin_value)().get_form()
|
|
||||||
return super(SelectPluginAdminMixin, self).get_form(request, obj=obj, **kwargs)
|
|
||||||
|
|
||||||
def get_urls(self):
|
|
||||||
""" Hooks select account url """
|
|
||||||
urls = super(SelectPluginAdminMixin, self).get_urls()
|
|
||||||
opts = self.model._meta
|
|
||||||
info = opts.app_label, opts.model_name
|
|
||||||
select_urls = patterns("",
|
|
||||||
url("/select-plugin/$",
|
|
||||||
wrap_admin_view(self, self.select_plugin_view),
|
|
||||||
name='%s_%s_select_plugin' % info),
|
|
||||||
)
|
|
||||||
return select_urls + urls
|
|
||||||
|
|
||||||
def select_plugin_view(self, request):
|
|
||||||
opts = self.model._meta
|
|
||||||
context = {
|
|
||||||
'opts': opts,
|
|
||||||
'app_label': opts.app_label,
|
|
||||||
'field': self.plugin_field,
|
|
||||||
'field_name': opts.get_field_by_name(self.plugin_field)[0].verbose_name,
|
|
||||||
'plugin': self.plugin,
|
|
||||||
'plugins': self.plugin.get_plugins(),
|
|
||||||
}
|
|
||||||
template = 'admin/orchestra/select_plugin.html'
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
def add_view(self, request, form_url='', extra_context=None):
|
|
||||||
""" Redirects to select account view if required """
|
|
||||||
if request.user.is_superuser:
|
|
||||||
plugin_value = request.GET.get(self.plugin_field) or request.POST.get(self.plugin_field)
|
|
||||||
if plugin_value or len(self.plugin.get_plugins()) == 1:
|
|
||||||
self.plugin_value = plugin_value
|
|
||||||
if not plugin_value:
|
|
||||||
self.plugin_value = self.plugin.get_plugins()[0].get_plugin_name()
|
|
||||||
b = self.plugin_value
|
|
||||||
context = {
|
|
||||||
'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value),
|
|
||||||
}
|
|
||||||
context.update(extra_context or {})
|
|
||||||
return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url,
|
|
||||||
extra_context=context)
|
|
||||||
return redirect('./select-plugin/?%s' % request.META['QUERY_STRING'])
|
|
||||||
|
|
||||||
def change_view(self, request, object_id, form_url='', extra_context=None):
|
|
||||||
obj = self.get_object(request, unquote(object_id))
|
|
||||||
plugin_value = getattr(obj, self.plugin_field)
|
|
||||||
context = {
|
|
||||||
'title': _("Change %s") % camel_case_to_spaces(plugin_value),
|
|
||||||
}
|
|
||||||
context.update(extra_context or {})
|
|
||||||
return super(SelectPluginAdminMixin, self).change_view(request, object_id,
|
|
||||||
form_url=form_url, extra_context=context)
|
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
|
||||||
if not change:
|
|
||||||
setattr(obj, self.plugin_field, self.plugin_value)
|
|
||||||
obj.save()
|
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordAdminMixin(object):
|
class ChangePasswordAdminMixin(object):
|
||||||
change_password_form = AdminPasswordChangeForm
|
change_password_form = AdminPasswordChangeForm
|
||||||
change_user_password_template = 'admin/orchestra/change_password.html'
|
change_user_password_template = 'admin/orchestra/change_password.html'
|
||||||
|
|
|
@ -10,6 +10,14 @@ from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from .models import MiscService, Miscellaneous
|
from .models import MiscService, Miscellaneous
|
||||||
|
|
||||||
|
|
||||||
|
from orchestra.apps.plugins.admin import SelectPluginAdminMixin, PluginAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class MiscServicePlugin(PluginAdapter):
|
||||||
|
model = MiscService
|
||||||
|
name_field = 'name'
|
||||||
|
|
||||||
|
|
||||||
class MiscServiceAdmin(ExtendedModelAdmin):
|
class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
list_display = ('name', 'verbose_name', 'num_instances', 'has_amount', 'is_active')
|
list_display = ('name', 'verbose_name', 'num_instances', 'has_amount', 'is_active')
|
||||||
list_editable = ('has_amount', 'is_active')
|
list_editable = ('has_amount', 'is_active')
|
||||||
|
@ -32,15 +40,38 @@ class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
return qs.annotate(models.Count('instances', distinct=True))
|
return qs.annotate(models.Count('instances', distinct=True))
|
||||||
|
|
||||||
|
|
||||||
class MiscellaneousAdmin(AccountAdminMixin, admin.ModelAdmin):
|
class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin):
|
||||||
list_display = ('service', 'amount', 'active', 'account_link')
|
list_display = ('service', 'amount', 'active', 'account_link')
|
||||||
|
plugin_field = 'service'
|
||||||
|
plugin = MiscServicePlugin
|
||||||
|
|
||||||
|
def get_service(self, obj):
|
||||||
|
if obj is None:
|
||||||
|
return self.plugin.get_plugin(self.plugin_value)().instance
|
||||||
|
else:
|
||||||
|
return obj.service
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
def get_fields(self, request, obj=None):
|
||||||
if obj is None:
|
fields = ['account', 'description', 'is_active']
|
||||||
return ('service', 'account', 'description', 'amount', 'is_active')
|
if obj is not None:
|
||||||
elif not obj.service.has_amount:
|
fields = ['account_link', 'description', 'is_active']
|
||||||
return ('service', 'account_link', 'description', 'is_active')
|
service = self.get_service(obj)
|
||||||
return ('service', 'account_link', 'description', 'amount', 'is_active')
|
if service.has_amount:
|
||||||
|
fields.insert(-1, 'amount')
|
||||||
|
# if service.has_identifier:
|
||||||
|
# fields.insert(1, 'identifier')
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
form = super(SelectPluginAdminMixin, self).get_form(request, obj=obj, **kwargs)
|
||||||
|
service = self.get_service(obj)
|
||||||
|
def clean_identifier(self, service=service):
|
||||||
|
validator = settings.MISCELLANEOUS_IDENTIFIER_VALIDATORS.get(service.name, None)
|
||||||
|
if validator:
|
||||||
|
validator(self.cleaned_data['identifier'])
|
||||||
|
form.clean_identifier = clean_identifier
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(MiscService, MiscServiceAdmin)
|
admin.site.register(MiscService, MiscServiceAdmin)
|
||||||
|
|
|
@ -14,9 +14,9 @@ class MiscService(models.Model):
|
||||||
help_text=_("Human readable name"))
|
help_text=_("Human readable name"))
|
||||||
description = models.TextField(_("description"), blank=True,
|
description = models.TextField(_("description"), blank=True,
|
||||||
help_text=_("Optional description"))
|
help_text=_("Optional description"))
|
||||||
has_identifier = models.BooleanField(_("has identifier"), default=True,
|
# has_identifier = models.BooleanField(_("has identifier"), default=True,
|
||||||
help_text=_("Designates if this service has a <b>unique text</b> field that "
|
# help_text=_("Designates if this service has a <b>unique text</b> field that "
|
||||||
"identifies it or not."))
|
# "identifies it or not."))
|
||||||
has_amount = models.BooleanField(_("has amount"), default=False,
|
has_amount = models.BooleanField(_("has amount"), default=False,
|
||||||
help_text=_("Designates whether this service has <tt>amount</tt> "
|
help_text=_("Designates whether this service has <tt>amount</tt> "
|
||||||
"property or not."))
|
"property or not."))
|
||||||
|
@ -39,8 +39,8 @@ class Miscellaneous(models.Model):
|
||||||
related_name='instances')
|
related_name='instances')
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='miscellaneous')
|
related_name='miscellaneous')
|
||||||
identifier = NullableCharField(_("identifier"), max_length=256, null=True, unique=True,
|
# identifier = NullableCharField(_("identifier"), max_length=256, null=True, unique=True,
|
||||||
blank=True, help_text=_("A unique identifier for this service."))
|
# help_text=_("A unique identifier for this service."))
|
||||||
description = models.TextField(_("description"), blank=True)
|
description = models.TextField(_("description"), blank=True)
|
||||||
amount = models.PositiveIntegerField(_("amount"), default=1)
|
amount = models.PositiveIntegerField(_("amount"), default=1)
|
||||||
is_active = models.BooleanField(_("active"), default=True,
|
is_active = models.BooleanField(_("active"), default=True,
|
||||||
|
@ -51,6 +51,7 @@ class Miscellaneous(models.Model):
|
||||||
verbose_name_plural = _("miscellaneous")
|
verbose_name_plural = _("miscellaneous")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
# return self.identifier or str(self.service)
|
||||||
return "{0}-{1}".format(str(self.service), str(self.account))
|
return "{0}-{1}".format(str(self.service), str(self.account))
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -61,8 +62,8 @@ class Miscellaneous(models.Model):
|
||||||
return self.is_active
|
return self.is_active
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.identifier:
|
# if self.identifier:
|
||||||
self.identifier = self.identifier.strip()
|
# self.identifier = self.identifier.strip()
|
||||||
self.description = self.description.strip()
|
self.description = self.description.strip()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from functools import partial
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.utils import plugins
|
from orchestra.apps import plugins
|
||||||
|
|
||||||
from . import methods
|
from . import methods
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@ from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ChangeViewActionsMixin, SelectPluginAdminMixin, ExtendedModelAdmin
|
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import admin_colored, admin_link
|
from orchestra.admin.utils import admin_colored, admin_link
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
||||||
|
from orchestra.apps.plugins.admin import SelectPluginAdminMixin
|
||||||
|
|
||||||
from . import actions
|
from . import actions
|
||||||
from .methods import PaymentMethod
|
from .methods import PaymentMethod
|
||||||
|
|
|
@ -2,7 +2,7 @@ from dateutil import relativedelta
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from orchestra.utils import plugins
|
from orchestra.apps import plugins
|
||||||
from orchestra.utils.functional import cached
|
from orchestra.utils.functional import cached
|
||||||
from orchestra.utils.python import import_class
|
from orchestra.utils.python import import_class
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django_iban.forms import IBANFormField
|
||||||
from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH
|
from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .. import settings
|
from .. import settings
|
||||||
from .options import PaymentMethod
|
from .options import PaymentMethod
|
||||||
|
|
|
@ -31,7 +31,7 @@ class PaymentSourceSerializer(AccountSerializerMixin, serializers.HyperlinkedMod
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
meta = super(PaymentSourceSerializer, self).metadata()
|
meta = super(PaymentSourceSerializer, self).metadata()
|
||||||
meta['data'] = {
|
meta['data'] = {
|
||||||
method.get_plugin_name(): method().get_serializer()().metadata()
|
method.get_name(): method().get_serializer()().metadata()
|
||||||
for method in PaymentMethod.get_plugins()
|
for method in PaymentMethod.get_plugins()
|
||||||
}
|
}
|
||||||
return meta
|
return meta
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from .options import *
|
|
@ -0,0 +1,108 @@
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
from django.contrib.admin.utils import unquote
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.utils.text import camel_case_to_spaces
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin.utils import wrap_admin_view
|
||||||
|
from orchestra.utils.functional import cached
|
||||||
|
|
||||||
|
|
||||||
|
class SelectPluginAdminMixin(object):
|
||||||
|
plugin = None
|
||||||
|
plugin_field = None
|
||||||
|
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
if obj:
|
||||||
|
self.form = getattr(obj, '%s_class' % self.plugin_field)().get_form()
|
||||||
|
else:
|
||||||
|
self.form = self.plugin.get_plugin(self.plugin_value)().get_form()
|
||||||
|
return super(SelectPluginAdminMixin, self).get_form(request, obj=obj, **kwargs)
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
""" Hooks select account url """
|
||||||
|
urls = super(SelectPluginAdminMixin, self).get_urls()
|
||||||
|
opts = self.model._meta
|
||||||
|
info = opts.app_label, opts.model_name
|
||||||
|
select_urls = patterns("",
|
||||||
|
url("/select-plugin/$",
|
||||||
|
wrap_admin_view(self, self.select_plugin_view),
|
||||||
|
name='%s_%s_select_plugin' % info),
|
||||||
|
)
|
||||||
|
return select_urls + urls
|
||||||
|
|
||||||
|
def select_plugin_view(self, request):
|
||||||
|
opts = self.model._meta
|
||||||
|
context = {
|
||||||
|
'opts': opts,
|
||||||
|
'app_label': opts.app_label,
|
||||||
|
'field': self.plugin_field,
|
||||||
|
'field_name': opts.get_field_by_name(self.plugin_field)[0].verbose_name,
|
||||||
|
'plugin': self.plugin,
|
||||||
|
'plugins': self.plugin.get_plugins(),
|
||||||
|
}
|
||||||
|
template = 'admin/plugins/select_plugin.html'
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
def add_view(self, request, form_url='', extra_context=None):
|
||||||
|
""" Redirects to select account view if required """
|
||||||
|
if request.user.is_superuser:
|
||||||
|
plugin_value = request.GET.get(self.plugin_field) or request.POST.get(self.plugin_field)
|
||||||
|
if plugin_value or len(self.plugin.get_plugins()) == 1:
|
||||||
|
self.plugin_value = plugin_value
|
||||||
|
if not plugin_value:
|
||||||
|
self.plugin_value = self.plugin.get_plugins()[0].get_name()
|
||||||
|
context = {
|
||||||
|
'title': _("Add new %s") % camel_case_to_spaces(self.plugin_value),
|
||||||
|
}
|
||||||
|
context.update(extra_context or {})
|
||||||
|
return super(SelectPluginAdminMixin, self).add_view(request, form_url=form_url,
|
||||||
|
extra_context=context)
|
||||||
|
return redirect('./select-plugin/?%s' % request.META['QUERY_STRING'])
|
||||||
|
|
||||||
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
||||||
|
obj = self.get_object(request, unquote(object_id))
|
||||||
|
plugin_value = getattr(obj, self.plugin_field)
|
||||||
|
context = {
|
||||||
|
'title': _("Change %s") % camel_case_to_spaces(str(plugin_value)),
|
||||||
|
}
|
||||||
|
context.update(extra_context or {})
|
||||||
|
return super(SelectPluginAdminMixin, self).change_view(request, object_id,
|
||||||
|
form_url=form_url, extra_context=context)
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
if not change:
|
||||||
|
setattr(obj, self.plugin_field, self.plugin_value)
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
|
||||||
|
class PluginAdapter(object):
|
||||||
|
""" Adapter class for using model classes as plugins """
|
||||||
|
|
||||||
|
model = None
|
||||||
|
name_field = None
|
||||||
|
|
||||||
|
def __init__(self, instance):
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cached
|
||||||
|
def get_plugins(cls):
|
||||||
|
plugins = []
|
||||||
|
for instance in cls.model.objects.filter(is_active=True):
|
||||||
|
plugins.append(cls(instance))
|
||||||
|
return plugins
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_plugin(cls, name):
|
||||||
|
return cls(cls.model.objects.get(**{cls.name_field:name}))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verbose_name(self):
|
||||||
|
return self.instance.verbose_name or str(getattr(self.instance, self.name_field))
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return getattr(self.instance, self.name_field)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return self
|
|
@ -0,0 +1,27 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class PluginDataForm(forms.ModelForm):
|
||||||
|
data = forms.CharField(widget=forms.HiddenInput, required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PluginDataForm, self).__init__(*args, **kwargs)
|
||||||
|
# TODO remove it well
|
||||||
|
try:
|
||||||
|
self.fields[self.plugin_field].widget = forms.HiddenInput()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
instance = kwargs.get('instance')
|
||||||
|
if instance:
|
||||||
|
for field in self.declared_fields:
|
||||||
|
initial = self.fields[field].initial
|
||||||
|
self.fields[field].initial = instance.data.get(field, initial)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
data = {}
|
||||||
|
for field in self.declared_fields:
|
||||||
|
try:
|
||||||
|
data[field] = self.cleaned_data[field]
|
||||||
|
except KeyError:
|
||||||
|
data[field] = self.data[field]
|
||||||
|
self.cleaned_data['data'] = data
|
|
@ -1,4 +1,4 @@
|
||||||
from .functional import cached
|
from orchestra.utils.functional import cached
|
||||||
|
|
||||||
|
|
||||||
class Plugin(object):
|
class Plugin(object):
|
||||||
|
@ -8,7 +8,7 @@ class Plugin(object):
|
||||||
icon = None
|
icon = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_name(cls):
|
def get_name(cls):
|
||||||
return cls.__name__
|
return cls.__name__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -19,7 +19,7 @@ class Plugin(object):
|
||||||
@cached
|
@cached
|
||||||
def get_plugin(cls, name):
|
def get_plugin(cls, name):
|
||||||
for plugin in cls.get_plugins():
|
for plugin in cls.get_plugins():
|
||||||
if plugin.get_plugin_name() == name:
|
if plugin.get_name() == name:
|
||||||
return plugin
|
return plugin
|
||||||
raise KeyError('This plugin is not registered')
|
raise KeyError('This plugin is not registered')
|
||||||
|
|
||||||
|
@ -30,14 +30,14 @@ class Plugin(object):
|
||||||
if verbose[0]:
|
if verbose[0]:
|
||||||
return cls.verbose_name
|
return cls.verbose_name
|
||||||
else:
|
else:
|
||||||
return cls.get_plugin_name()
|
return cls.get_name()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_choices(cls):
|
def get_plugin_choices(cls):
|
||||||
choices = []
|
choices = []
|
||||||
for plugin in cls.get_plugins():
|
for plugin in cls.get_plugins():
|
||||||
verbose = plugin.get_verbose_name()
|
verbose = plugin.get_verbose_name()
|
||||||
choices.append((plugin.get_plugin_name(), verbose))
|
choices.append((plugin.get_name(), verbose))
|
||||||
return sorted(choices, key=lambda e: e[1])
|
return sorted(choices, key=lambda e: e[1])
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
<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 %}
|
||||||
<li><a class="fluent-dashboard-icon" href="../?{{ field }}={{ plugin.get_plugin_name }}&{{ request.META.QUERY_STRING }}">
|
<li><a class="fluent-dashboard-icon" href="../?{{ field }}={{ plugin.get_name }}&{{ request.META.QUERY_STRING }}">
|
||||||
<img src="{% static plugin.icon %}" width="48" height="48" alt="{{ plugin.get_name }}"></a>
|
<img src="{% static plugin.icon %}" width="48" height="48" alt="{{ plugin.get_name }}"></a>
|
||||||
<a class="fluent-dashboard-icon-caption" href="../?{{ field }}={{ plugin.get_plugin_name }}&{{ request.META.QUERY_STRING }}">{{ plugin.verbose_name }}</a></li>
|
<a class="fluent-dashboard-icon-caption" href="../?{{ field }}={{ plugin.get_name }}&{{ request.META.QUERY_STRING }}">{{ plugin.verbose_name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for plugin in plugins %}
|
{% for plugin in plugins %}
|
||||||
<li><a style="font-size:small;" href="../?{{ field }}={{ plugin.get_plugin_name }}&{{ request.META.QUERY_STRING }}">{{ plugin.verbose_name }}</<a></li>
|
<li><a style="font-size:small;" href="../?{{ field }}={{ plugin.get_name }}&{{ request.META.QUERY_STRING }}">{{ plugin.verbose_name }}</<a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,7 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from orchestra.admin import SelectPluginAdminMixin
|
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
|
from orchestra.apps.plugins.admin import SelectPluginAdminMixin
|
||||||
|
|
||||||
from .models import SaaS
|
from .models import SaaS
|
||||||
from .services import SoftwareService
|
from .services import SoftwareService
|
||||||
|
|
|
@ -3,8 +3,8 @@ from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
from orchestra.core import validators
|
from orchestra.core import validators
|
||||||
from orchestra.forms import PluginDataForm
|
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.utils import plugins
|
from orchestra.apps import plugins
|
||||||
from orchestra.utils.functional import cached
|
from orchestra.utils.functional import cached
|
||||||
from orchestra.utils.python import import_class
|
from orchestra.utils.python import import_class
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.forms import PluginDataForm
|
from orchestra.apps.plugins.forms import PluginDataForm
|
||||||
|
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.utils import plugins
|
from orchestra.apps import plugins
|
||||||
from orchestra.utils.humanize import text2int
|
from orchestra.utils.humanize import text2int
|
||||||
from orchestra.utils.python import AttrDict
|
from orchestra.utils.python import AttrDict
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ INSTALLED_APPS = (
|
||||||
'orchestra.apps.miscellaneous',
|
'orchestra.apps.miscellaneous',
|
||||||
'orchestra.apps.bills',
|
'orchestra.apps.bills',
|
||||||
'orchestra.apps.payments',
|
'orchestra.apps.payments',
|
||||||
|
'orchestra.apps.plugins',
|
||||||
|
|
||||||
# Third-party apps
|
# Third-party apps
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
|
|
|
@ -6,32 +6,6 @@ from .. import settings
|
||||||
from ..core.validators import validate_password
|
from ..core.validators import validate_password
|
||||||
|
|
||||||
|
|
||||||
class PluginDataForm(forms.ModelForm):
|
|
||||||
data = forms.CharField(widget=forms.HiddenInput, required=False)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(PluginDataForm, self).__init__(*args, **kwargs)
|
|
||||||
# TODO remove it well
|
|
||||||
try:
|
|
||||||
self.fields[self.plugin_field].widget = forms.HiddenInput()
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
instance = kwargs.get('instance')
|
|
||||||
if instance:
|
|
||||||
for field in self.declared_fields:
|
|
||||||
initial = self.fields[field].initial
|
|
||||||
self.fields[field].initial = instance.data.get(field, initial)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
data = {}
|
|
||||||
for field in self.declared_fields:
|
|
||||||
try:
|
|
||||||
data[field] = self.cleaned_data[field]
|
|
||||||
except KeyError:
|
|
||||||
data[field] = self.data[field]
|
|
||||||
self.cleaned_data['data'] = data
|
|
||||||
|
|
||||||
|
|
||||||
class UserCreationForm(forms.ModelForm):
|
class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
A form that creates a user, with no privileges, from the given username and
|
A form that creates a user, with no privileges, from the given username and
|
||||||
|
|
Loading…
Reference in New Issue