diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index 791f2f8f9d..f1a3c59de4 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -172,6 +172,22 @@ class DynamicFormMixin(object): kwargs.update(field_data.get('kwargs', {})) self.fields[field_name] = field_class(**kwargs) + def get_dynamic_values(self): + # Consolidate the dynamic fields into a single dictionary. + cleaned_data = super(DynamicFormMixin, self).clean() + result = {} + + for field_name, field_data in self.schema['fields'].items(): + result[field_name] = cleaned_data.pop( + field_name, field_data.get('default', None) + ) + + return result + + def set_dynamic_values(self, values): + for key in self.schema['fields']: + self.fields[key].initial = values.get(key, self.fields[key].initial) + class DynamicForm(DynamicFormMixin, forms.Form): pass diff --git a/mayan/apps/forms/admin.py b/mayan/apps/forms/admin.py index d50ba39df7..41031f09f9 100644 --- a/mayan/apps/forms/admin.py +++ b/mayan/apps/forms/admin.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.contrib import admin -from .models import FieldChoice, FormTemplate, FormTemplateField +from .models import FieldChoice, FormInstance, FormTemplate, FormTemplateField @admin.register(FieldChoice) @@ -10,6 +10,11 @@ class FieldChoiceAdmin(admin.ModelAdmin): list_display = ('label', 'dotted_path') +@admin.register(FormInstance) +class FormInstanceAdmin(admin.ModelAdmin): + list_display = ('form_template', 'data') + + class FormTemplateFieldInline(admin.StackedInline): model = FormTemplateField extra = 1 diff --git a/mayan/apps/forms/apps.py b/mayan/apps/forms/apps.py index f23bb4824e..1f3e2c80fd 100644 --- a/mayan/apps/forms/apps.py +++ b/mayan/apps/forms/apps.py @@ -18,6 +18,8 @@ from mayan.apps.events.links import ( ) from mayan.apps.navigation.classes import SourceColumn +from .classes import FieldClass + class FormsApp(MayanAppConfig): app_namespace = 'forms' @@ -28,3 +30,4 @@ class FormsApp(MayanAppConfig): def ready(self): super(FormsApp, self).ready() + FieldClass.initialize() diff --git a/mayan/apps/forms/classes.py b/mayan/apps/forms/classes.py index 7d92fea59d..ba1bd93372 100644 --- a/mayan/apps/forms/classes.py +++ b/mayan/apps/forms/classes.py @@ -1,28 +1,64 @@ from __future__ import unicode_literals +from importlib import import_module import logging from django.apps import apps from django.utils import six from django.utils.encoding import force_text +from django.utils.module_loading import import_string from django.utils.translation import ugettext_lazy as _ logger = logging.getLogger(__name__) -class FieldEntry(object): +class FieldClass(object): _registry = {} + @staticmethod + def initialize(): + for app in apps.get_app_configs(): + try: + import_module('{}.form_fields'.format(app.name)) + except ImportError as exception: + if force_text(exception) not in ('No module named form_fields', 'No module named \'{}.form_fields\''.format(app.name)): + logger.error( + 'Error importing %s queues.py file; %s', app.name, + exception + ) + raise + + FieldClass.update() + @classmethod def all(cls): return cls._registry.values() + @classmethod + def update(cls): + FieldChoice = apps.get_model( + app_label='forms', model_name='FieldChoice' + ) + + for field in cls.all(): + FieldChoice.objects.update_or_create( + dotted_path=field.dotted_path, defaults={ + 'label': field.label + } + ) + def __init__(self, dotted_path, label): self.dotted_path = dotted_path self.label = label self.__class__._registry[self.dotted_path] = self - - -FieldEntry(dotted_path='django.forms.CharField', label='Character field') + def validate(self): + try: + import_string(dotted_path=self.dotted_path) + except Exception as exception: + logger.critical( + 'Exception validating field entry %s; %s', self.label, exception, + exc_info=True + ) + raise diff --git a/mayan/apps/forms/form_fields.py b/mayan/apps/forms/form_fields.py new file mode 100644 index 0000000000..027e9e6505 --- /dev/null +++ b/mayan/apps/forms/form_fields.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals + +from django.utils.translation import ugettext_lazy as _ + +from .classes import FieldClass + + +FieldClass(dotted_path='django.forms.CharField', label=_('Character field')) diff --git a/mayan/apps/forms/forms.py b/mayan/apps/forms/forms.py new file mode 100644 index 0000000000..c76f127e8a --- /dev/null +++ b/mayan/apps/forms/forms.py @@ -0,0 +1,82 @@ +from __future__ import unicode_literals + +import json + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.forms import DynamicModelForm +#from mayan.apps.common.settings import setting_project_title, setting_project_url + +#from .classes import MailerBackend +#from .models import UserMailer +#from .permissions import permission_user_mailer_use +#from .settings import ( +# setting_document_body_template, setting_document_subject_template, +# setting_link_body_template, setting_link_subject_template +#) +#from .validators import validate_email_multiple + +from .models import FormInstance + + +class FormInstanceDynamicForm(DynamicModelForm): + class Meta: + #fields = ('label', 'default', 'enabled', 'backend_data') + fields = ('data',) + model = FormInstance + widgets = {'data': forms.widgets.HiddenInput} + + def __init__(self, *args, **kwargs): + result = super(FormInstanceDynamicForm, self).__init__(*args, **kwargs) + + if self.instance: + #print("!!!!!!!!!!! instace data", self.instance.get_data()) + ##print("!!!!!!!!!!! instace data", type(self.instance.get_data())) + print("!!!!!!!!!!! instace data", self.instance.data) + print("!!!!!!!!!!! instace data", type(self.instance.data)) + self.set_dynamic_values(values=self.instance.data) + + """ + + instance_data = self.instance.get_data() + + if instance_data: + #print("@@@@@@@@@!!!!!!!!!!!! self.instance.data", self.instance.data) + #print("@@@@@@@@@!!!!!!!!!!!! data", data) + #print("@@@@@@@@@!!!!!!!!!!!! type: data", type(data)) + #print("@@@@@@@@@!!!!!!!!!!!! self.fields", self.fields) + #print("@@@@@@@@@!!!!!!!!!!!! self.fields", self.fields.keys()) + for key in self.instance.form_template.get_fields_dictionary(): + #print("@@@@@@@@@@@key", key) + #print("@@@@@@@@@@@key", type(data)) + #print("@@@@@@@@@@@key", data[key]) + self.fields[key].initial = instance_data[key] + + return result + """ + + def clean(self): + cleaned_data = super(FormInstanceDynamicForm, self).clean() + cleaned_data['data'] = json.dumps(self.get_dynamic_values()) + return cleaned_data + + """ + + cleaned_data = super(FormInstanceDynamicForm, self).clean() + + # Consolidate the dynamic fields into a single JSON field called + # 'backend_data'. + form_instance_data = {} + + for field_name, field_data in self.schema['fields'].items(): + form_instance_data[field_name] = cleaned_data.pop( + field_name, field_data.get('default', None) + ) + + print("!!!!!!!!!!!!!!!!!! form_instance_data", form_instance_data) + #cleaned_data['data'] = json.dumps(form_instance_data) + cleaned_data['data'] = form_instance_data + return cleaned_data + """ diff --git a/mayan/apps/forms/models.py b/mayan/apps/forms/models.py index 440caa2985..c3743f7da9 100644 --- a/mayan/apps/forms/models.py +++ b/mayan/apps/forms/models.py @@ -30,6 +30,7 @@ class FieldChoice(models.Model): return self.label +@python_2_unicode_compatible class FormTemplate(models.Model): name = models.CharField(max_length=32, verbose_name=_('Name')) label = models.CharField(max_length=128, verbose_name=_('Label')) @@ -40,6 +41,9 @@ class FormTemplate(models.Model): verbose_name = _('Form template') verbose_name_plural = _('Form templates') + def __str__(self): + return self.label + def get_fields_dictionary(self): result = {} @@ -84,8 +88,34 @@ class FormTemplateField(models.Model): return json.loads(self.arguments or '{}') +@python_2_unicode_compatible class FormInstance(models.Model): form_template = models.ForeignKey( on_delete=models.CASCADE, related_name='instances', to=FormTemplate, verbose_name=_('Form template field') ) + data = models.TextField( + blank=True, verbose_name=_('Data') + ) + + def __init__(self, *args, **kwargs): + super(FormInstance, self).__init__(*args, **kwargs) + self.deserialize_data() + + def __str__(self): + return force_text(self.form_template) + + class Meta: + verbose_name = _('Form instance') + verbose_name_plural = _('Form instances') + + def deserialize_data(self): + self.data = json.loads(self.data or '{}') + + def serialize_data(self): + self.data = json.dumps(self.data or {}) + + def save(self, *args, **kwargs): + #if not self.pk: + self.serialize_data() + return super(FormInstance, self).save(*args, **kwargs) diff --git a/mayan/apps/forms/urls.py b/mayan/apps/forms/urls.py index 1409f3cc6a..2be759bb5c 100644 --- a/mayan/apps/forms/urls.py +++ b/mayan/apps/forms/urls.py @@ -2,11 +2,15 @@ from __future__ import unicode_literals from django.conf.urls import url -from .views import FormInstanceCreateView +from .views import FormInstanceCreateView, FormInstanceEditView urlpatterns = [ url( regex=r'^templates/(?P\d+)/instances/create/$', view=FormInstanceCreateView.as_view(), name='form_instance_create' ), + url( + regex=r'^instances/(?P\d+)/edit/$', + view=FormInstanceEditView.as_view(), name='form_instance_edit' + ), ] diff --git a/mayan/apps/forms/views.py b/mayan/apps/forms/views.py index ff39b2a57d..e4318d8f88 100644 --- a/mayan/apps/forms/views.py +++ b/mayan/apps/forms/views.py @@ -17,7 +17,7 @@ from mayan.apps.common.mixins import ExternalObjectMixin from .models import FormTemplate, FormInstance - +""" class FormInstanceCreateView(ExternalObjectMixin, DynamicFormView): external_object_class = FormTemplate external_object_pk_url_kwarg = 'pk' @@ -29,4 +29,66 @@ class FormInstanceCreateView(ExternalObjectMixin, DynamicFormView): 'widgets': {} } + return result +""" + + +from .forms import FormInstanceDynamicForm + +class FormInstanceCreateView(ExternalObjectMixin, SingleObjectDynamicFormCreateView): + external_object_class = FormTemplate + external_object_pk_url_kwarg = 'pk' + form_class = FormInstanceDynamicForm + #post_action_redirect = reverse_lazy(viewname='mailer:user_mailer_list') + #view_permission = permission_user_mailer_create + + #def get_backend(self): + # try: + # return MailerBackend.get(name=self.kwargs['class_path']) + # except KeyError: + # raise Http404( + # '{} class not found'.format(self.kwargs['class_path']) + # ) + + #def get_extra_context(self): + # return { + # 'title': _( + # 'Create a "%s" mailing profile' + # ) % self.get_backend().label, + # } + + def get_form_schema(self): + result = { + 'fields': self.external_object.get_fields_dictionary(), + 'widgets': {} + } + + return result + + + #def get_form_schema(self): + # backend = self.get_backend() + # result = { + # 'fields': backend.fields, + # 'widgets': getattr(backend, 'widgets', {}) + # } + # if hasattr(backend, 'field_order'): + # result['field_order'] = backend.field_order + + # return result + + def get_instance_extra_data(self): + return {'form_template': self.external_object} + + +class FormInstanceEditView(SingleObjectDynamicFormEditView): + model = FormInstance + form_class = FormInstanceDynamicForm + + def get_form_schema(self): + result = { + 'fields': self.object.form_template.get_fields_dictionary(), + 'widgets': {} + } + return result