From 56c8e2741b94a0f4c47f7742dab148a098452475 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 22 May 2019 03:36:52 -0400 Subject: [PATCH] Intial commit of the forms manager app Signed-off-by: Roberto Rosario --- mayan/apps/forms/__init__.py | 3 + mayan/apps/forms/admin.py | 23 +++++ mayan/apps/forms/apps.py | 30 ++++++ mayan/apps/forms/classes.py | 28 ++++++ mayan/apps/forms/migrations/0001_initial.py | 61 +++++++++++++ .../forms/migrations/0002_forminstance.py | 23 +++++ mayan/apps/forms/migrations/__init__.py | 0 mayan/apps/forms/models.py | 91 +++++++++++++++++++ mayan/apps/forms/urls.py | 12 +++ mayan/apps/forms/views.py | 32 +++++++ mayan/settings/base.py | 1 + 11 files changed, 304 insertions(+) create mode 100644 mayan/apps/forms/__init__.py create mode 100644 mayan/apps/forms/admin.py create mode 100644 mayan/apps/forms/apps.py create mode 100644 mayan/apps/forms/classes.py create mode 100644 mayan/apps/forms/migrations/0001_initial.py create mode 100644 mayan/apps/forms/migrations/0002_forminstance.py create mode 100644 mayan/apps/forms/migrations/__init__.py create mode 100644 mayan/apps/forms/models.py create mode 100644 mayan/apps/forms/urls.py create mode 100644 mayan/apps/forms/views.py diff --git a/mayan/apps/forms/__init__.py b/mayan/apps/forms/__init__.py new file mode 100644 index 0000000000..80c986d6b3 --- /dev/null +++ b/mayan/apps/forms/__init__.py @@ -0,0 +1,3 @@ +from __future__ import unicode_literals + +default_app_config = 'mayan.apps.forms.apps.FormsApp' diff --git a/mayan/apps/forms/admin.py b/mayan/apps/forms/admin.py new file mode 100644 index 0000000000..d50ba39df7 --- /dev/null +++ b/mayan/apps/forms/admin.py @@ -0,0 +1,23 @@ +from __future__ import unicode_literals + +from django.contrib import admin + +from .models import FieldChoice, FormTemplate, FormTemplateField + + +@admin.register(FieldChoice) +class FieldChoiceAdmin(admin.ModelAdmin): + list_display = ('label', 'dotted_path') + + +class FormTemplateFieldInline(admin.StackedInline): + model = FormTemplateField + extra = 1 + classes = ('collapse-open',) + allow_add = True + + +@admin.register(FormTemplate) +class FormTemplateAdmin(admin.ModelAdmin): + inlines = (FormTemplateFieldInline,) + list_display = ('name', 'label', 'enabled') diff --git a/mayan/apps/forms/apps.py b/mayan/apps/forms/apps.py new file mode 100644 index 0000000000..f23bb4824e --- /dev/null +++ b/mayan/apps/forms/apps.py @@ -0,0 +1,30 @@ +from __future__ import unicode_literals + +from django.apps import apps +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.acls.classes import ModelPermission +from mayan.apps.acls.links import link_acl_list +from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view +from mayan.apps.common.apps import MayanAppConfig +from mayan.apps.common.html_widgets import TwoStateWidget +from mayan.apps.common.menus import ( + menu_list_facet, menu_multi_item, menu_object, menu_secondary, menu_setup, + menu_tools +) +from mayan.apps.events.classes import ModelEventType +from mayan.apps.events.links import ( + link_events_for_object, link_object_event_types_user_subcriptions_list +) +from mayan.apps.navigation.classes import SourceColumn + + +class FormsApp(MayanAppConfig): + app_namespace = 'forms' + app_url = 'forms' + #has_tests = True + name = 'mayan.apps.forms' + verbose_name = _('Forms') + + def ready(self): + super(FormsApp, self).ready() diff --git a/mayan/apps/forms/classes.py b/mayan/apps/forms/classes.py new file mode 100644 index 0000000000..7d92fea59d --- /dev/null +++ b/mayan/apps/forms/classes.py @@ -0,0 +1,28 @@ +from __future__ import unicode_literals + +import logging + +from django.apps import apps +from django.utils import six +from django.utils.encoding import force_text +from django.utils.translation import ugettext_lazy as _ + +logger = logging.getLogger(__name__) + + +class FieldEntry(object): + _registry = {} + + @classmethod + def all(cls): + return cls._registry.values() + + 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') diff --git a/mayan/apps/forms/migrations/0001_initial.py b/mayan/apps/forms/migrations/0001_initial.py new file mode 100644 index 0000000000..44e62131d6 --- /dev/null +++ b/mayan/apps/forms/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-22 07:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='FieldChoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.CharField(max_length=128, verbose_name='Label')), + ('dotted_path', models.CharField(max_length=128, verbose_name='Dotted path')), + ], + options={ + 'verbose_name': 'Field choice', + 'verbose_name_plural': 'Field choices', + 'ordering': ('label',), + }, + ), + migrations.CreateModel( + name='FormTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32, verbose_name='Name')), + ('label', models.CharField(max_length=128, verbose_name='Label')), + ('enabled', models.BooleanField(default=True, verbose_name='Enabled')), + ], + options={ + 'verbose_name': 'Form template', + 'verbose_name_plural': 'Form templates', + 'ordering': ('label',), + }, + ), + migrations.CreateModel( + name='FormTemplateField', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('arguments', models.TextField(blank=True, verbose_name='Arguments')), + ('widget', models.CharField(blank=True, max_length=128, verbose_name='Widget')), + ('widget_arguments', models.CharField(blank=True, max_length=128, verbose_name='Widget arguments')), + ('enabled', models.BooleanField(default=True, verbose_name='Enabled')), + ('choice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='forms', to='forms.FieldChoice', verbose_name='Choice')), + ('form_template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='forms.FormTemplate', verbose_name='Form template field')), + ], + options={ + 'verbose_name': 'Form template field', + 'verbose_name_plural': 'Form template fields', + }, + ), + ] diff --git a/mayan/apps/forms/migrations/0002_forminstance.py b/mayan/apps/forms/migrations/0002_forminstance.py new file mode 100644 index 0000000000..f76305f231 --- /dev/null +++ b/mayan/apps/forms/migrations/0002_forminstance.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-22 07:32 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('forms', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='FormInstance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('form_template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='forms.FormTemplate', verbose_name='Form template field')), + ], + ), + ] diff --git a/mayan/apps/forms/migrations/__init__.py b/mayan/apps/forms/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mayan/apps/forms/models.py b/mayan/apps/forms/models.py new file mode 100644 index 0000000000..440caa2985 --- /dev/null +++ b/mayan/apps/forms/models.py @@ -0,0 +1,91 @@ +from __future__ import unicode_literals + +import json +import logging + +from django.core import mail +from django.db import models, transaction +from django.template import Context, Template +from django.utils.encoding import force_text, python_2_unicode_compatible +from django.utils.html import strip_tags +from django.utils.module_loading import import_string +from django.utils.translation import ugettext_lazy as _ + +logger = logging.getLogger(__name__) + + +@python_2_unicode_compatible +class FieldChoice(models.Model): + label = models.CharField(max_length=128, verbose_name=_('Label')) + dotted_path = models.CharField( + max_length=128, verbose_name=_('Dotted path') + ) + + class Meta: + ordering = ('label',) + verbose_name = _('Field choice') + verbose_name_plural = _('Field choices') + + def __str__(self): + return self.label + + +class FormTemplate(models.Model): + name = models.CharField(max_length=32, verbose_name=_('Name')) + label = models.CharField(max_length=128, verbose_name=_('Label')) + enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) + + class Meta: + ordering = ('label',) + verbose_name = _('Form template') + verbose_name_plural = _('Form templates') + + def get_fields_dictionary(self): + result = {} + + for field in self.fields.all(): + result[field.name] = { + 'label': field.get_arguments().get('label', self.name), + 'class': field.choice.dotted_path, + 'kwargs': field.get_arguments(), + } + + return result + + +class FormTemplateField(models.Model): + form_template = models.ForeignKey( + on_delete=models.CASCADE, related_name='fields', to=FormTemplate, + verbose_name=_('Form template field') + ) + name = models.CharField( + max_length=128, verbose_name=_('Name') + ) + choice = models.ForeignKey( + on_delete=models.CASCADE, related_name='forms', to=FieldChoice, + verbose_name=_('Choice') + ) + arguments = models.TextField( + blank=True, verbose_name=_('Arguments') + ) + widget = models.CharField( + blank=True, max_length=128, verbose_name=_('Widget') + ) + widget_arguments = models.CharField( + blank=True, max_length=128, verbose_name=_('Widget arguments') + ) + enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) + + class Meta: + verbose_name = _('Form template field') + verbose_name_plural = _('Form template fields') + + def get_arguments(self): + return json.loads(self.arguments or '{}') + + +class FormInstance(models.Model): + form_template = models.ForeignKey( + on_delete=models.CASCADE, related_name='instances', to=FormTemplate, + verbose_name=_('Form template field') + ) diff --git a/mayan/apps/forms/urls.py b/mayan/apps/forms/urls.py new file mode 100644 index 0000000000..1409f3cc6a --- /dev/null +++ b/mayan/apps/forms/urls.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals + +from django.conf.urls import url + +from .views import FormInstanceCreateView + +urlpatterns = [ + url( + regex=r'^templates/(?P\d+)/instances/create/$', + view=FormInstanceCreateView.as_view(), name='form_instance_create' + ), +] diff --git a/mayan/apps/forms/views.py b/mayan/apps/forms/views.py new file mode 100644 index 0000000000..ff39b2a57d --- /dev/null +++ b/mayan/apps/forms/views.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import, unicode_literals + +from django.http import Http404, HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.template import RequestContext +from django.urls import reverse, reverse_lazy +from django.utils.translation import ungettext, ugettext_lazy as _ + +from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.generics import ( + DynamicFormView, FormView, MultipleObjectFormActionView, SingleObjectDeleteView, + SingleObjectDynamicFormCreateView, SingleObjectDynamicFormEditView, + SingleObjectListView +) +from mayan.apps.common.forms import DynamicForm, DynamicModelForm +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' + form_class = DynamicForm + + def get_form_schema(self): + result = { + 'fields': self.external_object.get_fields_dictionary(), + 'widgets': {} + } + + return result diff --git a/mayan/settings/base.py b/mayan/settings/base.py index 36b32c8162..ba410be070 100644 --- a/mayan/settings/base.py +++ b/mayan/settings/base.py @@ -118,6 +118,7 @@ INSTALLED_APPS = ( 'mayan.apps.document_states', 'mayan.apps.documents', 'mayan.apps.file_metadata', + 'mayan.apps.forms', 'mayan.apps.linking', 'mayan.apps.mailer', 'mayan.apps.mayan_statistics',