Compare commits

...

4 Commits

Author SHA1 Message Date
Roberto Rosario
757cf3d869 Update forms app
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-05-30 23:38:23 -04:00
Roberto Rosario
5ea0b02964 Put deletion policies units before periods
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-05-25 15:07:54 -04:00
Roberto Rosario
3346a88f81 Update use of collectstatic to preparestatic
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-05-24 23:16:37 -04:00
Roberto Rosario
56c8e2741b Intial commit of the forms manager app
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-05-22 03:37:14 -04:00
20 changed files with 562 additions and 5 deletions

View File

@@ -271,6 +271,8 @@
* Add the checkdependencies command.
* Add comment and make file target to generate all requirement
files.
* Place deletion policies units before periods for clarity.
3.1.11 (2019-04-XX)
===================

View File

@@ -1445,7 +1445,7 @@ sudo -u mayan \
dialog --infobox "Preparing static files" 3 70
sudo -u mayan \
MAYAN_MEDIA_ROOT=$MAYAN_MEDIA_ROOT \
$MAYAN_BIN collectstatic --noinput > /dev/null
$MAYAN_BIN preparestatic --noinput > /dev/null
# Create supervisor file for gunicorn (frontend), 3 background workers, and the scheduler for periodic tasks
cat > /etc/supervisor/conf.d/mayan.conf <<EOF

View File

@@ -161,7 +161,7 @@ priority = 998
EOF
echo -e "\n -> Collecting the static files \n"
mayan-edms.py collectstatic --noinput
mayan-edms.py preparestatic --noinput
echo -e "\n -> Making the installation directory readable and writable by the webserver user \n"
chown www-data:www-data ${INSTALLATION_DIRECTORY} -R

View File

@@ -705,6 +705,7 @@ Other changes
Change color scheme to match rest of project. Increase size of data points.
Improve responsive settings. Redirect to the current view after queueing.
- Split document type retention policies into it own view.
- Place deletion policies units before periods for clarity.
Removals
@@ -829,6 +830,10 @@ Backward incompatible changes
ImportError: No module named ocr.backends.pyocr
- To collect and compress the static media files, use the new ``preparestatic``
command instead of the traditional ``collectstatic``. Both work the same
way, but ``preparestatic`` has a default backlist to avoid collecting
test files.
Bugs fixed or issues closed
---------------------------

View File

@@ -148,7 +148,7 @@ Django's development server doesn't serve static files unless the DEBUG option
is set to True, this mode of operation should only be used for development or
testing. For production deployments the management command::
$ mayan-edms.py collectstatic
$ mayan-edms.py preparestatic
should be used and the resulting static folder served from a webserver.
For more information check the

View File

@@ -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

View File

@@ -111,8 +111,8 @@ class DocumentTypeDeleteView(SingleObjectDeleteView):
class DocumentTypeDeletionPoliciesEditView(SingleObjectEditView):
fields = (
'trash_time_period', 'trash_time_unit', 'delete_time_period',
'delete_time_unit'
'trash_time_unit', 'trash_time_period', 'delete_time_unit',
'delete_time_period'
)
model = DocumentType
object_permission = permission_document_type_edit

View File

@@ -0,0 +1,3 @@
from __future__ import unicode_literals
default_app_config = 'mayan.apps.forms.apps.FormsApp'

28
mayan/apps/forms/admin.py Normal file
View File

@@ -0,0 +1,28 @@
from __future__ import unicode_literals
from django.contrib import admin
from .models import FieldChoice, FormInstance, FormTemplate, FormTemplateField
@admin.register(FieldChoice)
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
classes = ('collapse-open',)
allow_add = True
@admin.register(FormTemplate)
class FormTemplateAdmin(admin.ModelAdmin):
inlines = (FormTemplateFieldInline,)
list_display = ('name', 'label', 'enabled')

33
mayan/apps/forms/apps.py Normal file
View File

@@ -0,0 +1,33 @@
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
from .classes import FieldClass
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()
FieldClass.initialize()

View File

@@ -0,0 +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 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
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

View File

@@ -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'))

82
mayan/apps/forms/forms.py Normal file
View File

@@ -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
"""

View File

@@ -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',
},
),
]

View File

@@ -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')),
],
),
]

View File

121
mayan/apps/forms/models.py Normal file
View File

@@ -0,0 +1,121 @@
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
@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'))
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
class Meta:
ordering = ('label',)
verbose_name = _('Form template')
verbose_name_plural = _('Form templates')
def __str__(self):
return self.label
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 '{}')
@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)

16
mayan/apps/forms/urls.py Normal file
View File

@@ -0,0 +1,16 @@
from __future__ import unicode_literals
from django.conf.urls import url
from .views import FormInstanceCreateView, FormInstanceEditView
urlpatterns = [
url(
regex=r'^templates/(?P<pk>\d+)/instances/create/$',
view=FormInstanceCreateView.as_view(), name='form_instance_create'
),
url(
regex=r'^instances/(?P<pk>\d+)/edit/$',
view=FormInstanceEditView.as_view(), name='form_instance_edit'
),
]

94
mayan/apps/forms/views.py Normal file
View File

@@ -0,0 +1,94 @@
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
"""
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

View File

@@ -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',