diff --git a/mayan/apps/common/views.py b/mayan/apps/common/views.py index 799ccf1a56..bf25f91c47 100644 --- a/mayan/apps/common/views.py +++ b/mayan/apps/common/views.py @@ -1,23 +1,23 @@ from __future__ import absolute_import -from django.shortcuts import redirect -from django.utils.translation import ugettext_lazy as _ -from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response -from django.template import RequestContext from django.contrib import messages -from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse -from django.utils.http import urlencode -from django.contrib.auth.views import login -from django.utils.simplejson import dumps, loads -from django.contrib.auth.views import password_change from django.contrib.auth.models import User +from django.contrib.auth.views import login, password_change +from django.contrib.contenttypes.models import ContentType from django.conf import settings +from django.core.urlresolvers import reverse +from django.http import HttpResponseRedirect +from django.shortcuts import redirect, render_to_response +from django.template import RequestContext +from django.utils.http import urlencode +from django.utils.simplejson import dumps, loads +from django.utils.translation import ugettext_lazy as _ +from permissions.models import Permission + +from .conf.settings import LOGIN_METHOD from .forms import (ChoiceForm, UserForm, UserForm_view, LicenseForm, EmailAuthenticationForm) -from .conf.settings import LOGIN_METHOD def multi_object_action_view(request): @@ -237,7 +237,7 @@ def password_change_view(request): Password change wrapper for better control """ context={'title': _(u'Current user password change')} - + return password_change( request, extra_context=context, @@ -253,3 +253,13 @@ def password_change_done(request): messages.success(request, _(u'Your password has been successfully changed.')) return redirect('current_user_details') + + +class MayanPermissionCheckMixin(object): + permissions_required = None + + def dispatch(self, request, *args, **kwargs): + if self.permissions_required: + Permission.objects.check_permissions(self.request.user, self.permissions_required) + + return super(MayanPermissionCheckMixin, self).dispatch(request, *args, **kwargs) diff --git a/mayan/apps/common/wizard.py b/mayan/apps/common/wizard.py deleted file mode 100644 index 5454277135..0000000000 --- a/mayan/apps/common/wizard.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Common abstract classes for forms.""" -try: - import cPickle as pickle -except ImportError: - import pickle - -from django import forms -from django.conf import settings -from django.contrib.formtools.wizard import FormWizard -from django.forms.forms import BoundField -from django.forms.formsets import BaseFormSet -from django.utils.hashcompat import md5_constructor - -__all__ = ('security_hash', 'BoundFormWizard') - - -def security_hash_new(form, exclude=None, *args): - """ - Calculates a security hash for the given Form/FormSet instance. - - This creates a list of the form field names/values in a deterministic - order, pickles the result with the SECRET_KEY setting, then takes an md5 - hash of that. - """ - - data = [] - if exclude is None: - exclude = () - if isinstance(form, BaseFormSet): - for _form in form.forms + [form.management_form]: - for bf in _form: - value = bf.field.clean(bf.data) or '' - if isinstance(value, basestring): - value = value.strip() - data.append((bf.name, value)) - else: - for bf in form: - if bf.name in exclude: - continue - value = bf.field.clean(bf.data) or '' - if isinstance(value, basestring): - value = value.strip() - data.append((bf.name, value)) - data.extend(args) - data.append(settings.SECRET_KEY) - - # Use HIGHEST_PROTOCOL because it's the most efficient. It requires - # Python 2.3, but Django requires 2.3 anyway, so that's OK. - pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) - - return md5_constructor(pickled).hexdigest() - - -class BoundFormWizard(FormWizard): - """ - Render prev_fields as a list of bound form fields in the template - context rather than raw html. - """ - - def security_hash(self, request, form): - """ - Calculates the security hash for the given HttpRequest and - Form/FormSet instances. - - Subclasses may want to take into account request-specific information, - such as the IP address. - """ - - return security_hash_new(form) - - def render(self, form, request, step, context=None): - 'Renders the given Form object, returning an HttpResponse.' - old_data = request.POST - prev_fields = [] - if old_data: - for i in range(step): - old_form = self.get_form(i, old_data) - hash_name = 'hash_%s' % i - if isinstance(old_form, BaseFormSet): - for _form in old_form.forms + [old_form.management_form]: - prev_fields.extend([bf for bf in _form]) - else: - prev_fields.extend([bf for bf in old_form]) - hash_field = forms.Field(initial=old_data.get(hash_name, - self.security_hash(request, old_form))) - bf = BoundField(forms.Form(), hash_field, hash_name) - prev_fields.append(bf) - return self.render_template(request, form, prev_fields, step, context) diff --git a/mayan/apps/sources/urls.py b/mayan/apps/sources/urls.py index d7791f3dfc..2936597e10 100644 --- a/mayan/apps/sources/urls.py +++ b/mayan/apps/sources/urls.py @@ -1,9 +1,10 @@ from __future__ import absolute_import -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url from .literals import (SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING, SOURCE_CHOICE_WATCH) +from .wizards import DocumentCreateWizard urlpatterns = patterns('sources.views', url(r'^staging_file/type/(?P\w+)/(?P\d+)/(?P\w+)/preview/$', 'staging_file_preview', (), 'staging_file_preview'), @@ -34,6 +35,6 @@ urlpatterns = patterns('sources.views', # Document create views - url(r'^create/from/local/multiple/$', 'document_create', (), 'document_create_multiple'), + url(r'^create/from/local/multiple/$', DocumentCreateWizard.as_view(), name='document_create_multiple'), url(r'^(?P\d+)/create/siblings/$', 'document_create_siblings', (), 'document_create_siblings'), ) diff --git a/mayan/apps/sources/views.py b/mayan/apps/sources/views.py index 16482a8bc0..22d98ec0db 100644 --- a/mayan/apps/sources/views.py +++ b/mayan/apps/sources/views.py @@ -40,14 +40,6 @@ from .staging import create_staging_file_class from .wizards import DocumentCreateWizard -def document_create(request): - Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) - - wizard = DocumentCreateWizard(form_list=[DocumentTypeSelectForm, MetadataSelectionForm, MetadataFormSet]) - - return wizard(request) - - def document_create_siblings(request, document_id): Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) diff --git a/mayan/apps/sources/wizards.py b/mayan/apps/sources/wizards.py index c71d204bd5..5191eca799 100644 --- a/mayan/apps/sources/wizards.py +++ b/mayan/apps/sources/wizards.py @@ -1,25 +1,40 @@ -# DEPRECATION: This module is scheduled to be delete once the update to Django 1.6.X is complete from __future__ import absolute_import +from django.contrib.formtools.wizard.views import SessionWizardView from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ -from common.wizard import BoundFormWizard +from common.views import MayanPermissionCheckMixin from documents.forms import DocumentTypeSelectForm +from documents.permissions import PERMISSION_DOCUMENT_CREATE from metadata.forms import MetadataSelectionForm, MetadataFormSet -class DocumentCreateWizard(BoundFormWizard): +class DocumentCreateWizard(MayanPermissionCheckMixin, SessionWizardView): + form_list = [DocumentTypeSelectForm, MetadataSelectionForm, MetadataFormSet] + template_name = 'generic_wizard.html' + extra_context = {} + permissions_required = [PERMISSION_DOCUMENT_CREATE] + + @staticmethod + def has_metadata_types(wizard): + # Skip the 3rd step if no metadata types or sets are selected + try: + return wizard.get_cleaned_data_for_step('1')['metadata_sets'] or wizard.get_cleaned_data_for_step('1')['metadata_types'] + except TypeError: + return False + def generate_metadata_initial_values(self): initial = [] - for metadata_type in self.metadata_types: + + for metadata_type in self.get_cleaned_data_for_step('1')['metadata_types']: initial.append({ 'metadata_type': metadata_type, }) - for metadata_set in self.metadata_sets: + for metadata_set in self.get_cleaned_data_for_step('1')['metadata_sets']: for metadata_set_item in metadata_set.metadatasetitem_set.all(): data = { 'metadata_type': metadata_set_item.metadata_type, @@ -30,53 +45,49 @@ class DocumentCreateWizard(BoundFormWizard): return initial def __init__(self, *args, **kwargs): - self.query_dict = {} - self.step_titles = kwargs.pop('step_titles', [ + super(DocumentCreateWizard, self).__init__(*args, **kwargs) + + self.condition_dict = {'2': DocumentCreateWizard.has_metadata_types} + + self.step_titles = [ _(u'step 1 of 3: Document type'), _(u'step 2 of 3: Metadata selection'), _(u'step 3 of 3: Document metadata'), - ]) + ] - super(DocumentCreateWizard, self).__init__(*args, **kwargs) + def get_form_initial(self, step): + if step == '1': + try: + return {'document_type': self.get_cleaned_data_for_step('0')['document_type']} + except TypeError: + return {} + elif step == '2': + return self.generate_metadata_initial_values() - def render_template(self, request, form, previous_fields, step, context=None): - context = { - 'step_title': self.extra_context['step_titles'][step], + return self.initial_dict.get(step, {}) + + def get_context_data(self, form, **kwargs): + context = super(DocumentCreateWizard, self).get_context_data(form=form, **kwargs) + context.update({ + 'step_title': self.step_titles[self.steps.step0], 'submit_label': _(u'Next step'), 'submit_icon_famfam': 'arrow_right', - } - return super(DocumentCreateWizard, self).render_template( - request, form, previous_fields, step, context - ) + }) + return context - def parse_params(self, request, *args, **kwargs): - self.extra_context = {'step_titles': self.step_titles} + def done(self, form_list): + query_dict = {} + try: + query_dict['document_type_id'] = self.get_cleaned_data_for_step('0')['document_type'].pk + except AttributeError: + pass - def process_step(self, request, form, step): - if isinstance(form, DocumentTypeSelectForm): - self.document_type = form.cleaned_data['document_type'] - self.initial = {1: {'document_type': self.document_type}} + try: + for identifier, metadata in enumerate(self.get_cleaned_data_for_step('2')): + query_dict['metadata%s_id' % identifier] = metadata['id'] + query_dict['metadata%s_value' % identifier] = metadata['value'] + except TypeError: + pass - if isinstance(form, MetadataSelectionForm): - self.metadata_sets = form.cleaned_data['metadata_sets'] - self.metadata_types = form.cleaned_data['metadata_types'] - initial_data = self.generate_metadata_initial_values() - self.initial = {2: initial_data} - if not initial_data: - # If there is no metadata selected, finish wizard - self.form_list = [DocumentTypeSelectForm, MetadataSelectionForm] - - if isinstance(form, MetadataFormSet): - for identifier, metadata in enumerate(form.cleaned_data): - self.query_dict['metadata%s_id' % identifier] = metadata['id'] - self.query_dict['metadata%s_value' % identifier] = metadata['value'] - - def get_template(self, step): - return 'generic_wizard.html' - - def done(self, request, form_list): - if self.document_type: - self.query_dict['document_type_id'] = self.document_type.pk - - url = '?'.join([reverse('upload_interactive'), urlencode(self.query_dict, doseq=True)]) + url = '?'.join([reverse('upload_interactive'), urlencode(query_dict, doseq=True)]) return HttpResponseRedirect(url)