diff --git a/mayan/apps/appearance/templates/appearance/generic_form_instance.html b/mayan/apps/appearance/templates/appearance/generic_form_instance.html index 72e631d7cf..9eeef9aa9b 100644 --- a/mayan/apps/appearance/templates/appearance/generic_form_instance.html +++ b/mayan/apps/appearance/templates/appearance/generic_form_instance.html @@ -63,6 +63,12 @@
{% render_field field %}
+ {% elif field|widget_type == 'checkboxselectmultiple' %} + {% for option in field %} +
+ {{ option.render }} +
+ {% endfor %} {% else %} {% render_field field class+="form-control" %} {% endif %} diff --git a/mayan/apps/sources/literals.py b/mayan/apps/sources/literals.py index 0c10517073..6605994b2c 100644 --- a/mayan/apps/sources/literals.py +++ b/mayan/apps/sources/literals.py @@ -36,3 +36,8 @@ DEFAULT_METADATA_ATTACHMENT_NAME = 'metadata.yaml' DEFAULT_POP3_TIMEOUT = 60 DEFAULT_IMAP_MAILBOX = 'INBOX' DEFAULT_SOURCE_TASK_RETRY_DELAY = 10 + +# Upload wizard steps +STEP_DOCUMENT_TYPE = '0' +STEP_METADATA = '1' +STEP_TAGS = '2' diff --git a/mayan/apps/sources/models.py b/mayan/apps/sources/models.py index 1df74b26e1..22ad807261 100644 --- a/mayan/apps/sources/models.py +++ b/mayan/apps/sources/models.py @@ -28,6 +28,7 @@ from documents.models import Document, DocumentType from documents.settings import setting_language from metadata.api import save_metadata_list, set_bulk_metadata from metadata.models import MetadataType +from tags.models import Tag from .classes import Attachment, SourceUploadedFile, StagingFile from .literals import ( @@ -59,7 +60,7 @@ class Source(models.Model): def fullname(self): return ' '.join([self.class_fullname(), '"%s"' % self.label]) - def upload_document(self, file_object, document_type, description=None, label=None, language=None, metadata_dict_list=None, metadata_dictionary=None, user=None): + def upload_document(self, file_object, document_type, description=None, label=None, language=None, metadata_dict_list=None, metadata_dictionary=None, tag_ids=None, user=None): try: with transaction.atomic(): document = Document.objects.create( @@ -88,6 +89,10 @@ class Source(models.Model): metadata_dictionary=metadata_dictionary ) + if tag_ids: + for tag in Tag.objects.filter(pk__in=tag_ids): + tag.documents.add(document) + except Exception as exception: logger.critical( 'Unexpected exception while trying to create new document ' diff --git a/mayan/apps/sources/tasks.py b/mayan/apps/sources/tasks.py index 5d75516dec..6d26b7fa8d 100644 --- a/mayan/apps/sources/tasks.py +++ b/mayan/apps/sources/tasks.py @@ -35,7 +35,7 @@ def task_check_interval_source(source_id): @app.task(bind=True, default_retry_delay=DEFAULT_SOURCE_TASK_RETRY_DELAY, ignore_result=True) -def task_upload_document(self, source_id, document_type_id, shared_uploaded_file_id, description=None, label=None, language=None, metadata_dict_list=None, user_id=None): +def task_upload_document(self, source_id, document_type_id, shared_uploaded_file_id, description=None, label=None, language=None, metadata_dict_list=None, tag_ids=None, user_id=None): SharedUploadedFile = apps.get_model( app_label='common', model_name='SharedUploadedFile' ) @@ -64,7 +64,8 @@ def task_upload_document(self, source_id, document_type_id, shared_uploaded_file source.upload_document( file_object=file_object, document_type=document_type, description=description, label=label, language=language, - metadata_dict_list=metadata_dict_list, user=user + metadata_dict_list=metadata_dict_list, user=user, + tag_ids=tag_ids ) except OperationalError as exception: @@ -85,7 +86,7 @@ def task_upload_document(self, source_id, document_type_id, shared_uploaded_file @app.task(bind=True, default_retry_delay=DEFAULT_SOURCE_TASK_RETRY_DELAY, ignore_result=True) -def task_source_handle_upload(self, document_type_id, shared_uploaded_file_id, source_id, description=None, expand=False, label=None, language=None, metadata_dict_list=None, skip_list=None, user_id=None): +def task_source_handle_upload(self, document_type_id, shared_uploaded_file_id, source_id, description=None, expand=False, label=None, language=None, metadata_dict_list=None, skip_list=None, tag_ids=None, user_id=None): SharedUploadedFile = apps.get_model( app_label='common', model_name='SharedUploadedFile' ) @@ -114,7 +115,7 @@ def task_source_handle_upload(self, document_type_id, shared_uploaded_file_id, s 'description': description, 'document_type_id': document_type.pk, 'label': label, 'language': language, 'metadata_dict_list': metadata_dict_list, - 'source_id': source_id, 'user_id': user_id + 'source_id': source_id, 'tag_ids': tag_ids, 'user_id': user_id } if not skip_list: @@ -147,7 +148,8 @@ def task_source_handle_upload(self, document_type_id, shared_uploaded_file_id, s expand=expand, label=label, language=language, metadata_dict_list=metadata_dict_list, - skip_list=skip_list, user_id=user_id + skip_list=skip_list, tag_ids=tag_ids, + user_id=user_id ) return else: diff --git a/mayan/apps/sources/views.py b/mayan/apps/sources/views.py index 5d5afdbaa1..ef75f158a7 100644 --- a/mayan/apps/sources/views.py +++ b/mayan/apps/sources/views.py @@ -264,6 +264,7 @@ class UploadInteractiveView(UploadBaseView): metadata_dict_list=decode_metadata_from_url(self.request.GET), shared_uploaded_file_id=shared_uploaded_file.pk, source_id=self.source.pk, + tag_ids=self.request.GET.getlist('tags'), user_id=user_id, )) messages.success( diff --git a/mayan/apps/sources/wizards.py b/mayan/apps/sources/wizards.py index 7f510f60d2..97f00befda 100644 --- a/mayan/apps/sources/wizards.py +++ b/mayan/apps/sources/wizards.py @@ -12,26 +12,39 @@ from common.mixins import ViewPermissionCheckMixin from documents.forms import DocumentTypeSelectForm from documents.permissions import permission_document_create from metadata.forms import MetadataFormSet +from tags.forms import TagMultipleSelectionForm +from .literals import STEP_DOCUMENT_TYPE, STEP_METADATA, STEP_TAGS from .models import InteractiveSource +def has_metadata_types(wizard): + """ + Skip the 2nd step if document type has no associated metadata + """ + + cleaned_data = wizard.get_cleaned_data_for_step(STEP_DOCUMENT_TYPE) or {} + + document_type = cleaned_data.get('document_type') + + if document_type: + return document_type.metadata.exists() + + class DocumentCreateWizard(ViewPermissionCheckMixin, SessionWizardView): - form_list = (DocumentTypeSelectForm, MetadataFormSet) - template_name = 'appearance/generic_wizard.html' + condition_dict = {STEP_METADATA: has_metadata_types} extra_context = {} + form_list = (DocumentTypeSelectForm, MetadataFormSet, TagMultipleSelectionForm) + form_titles = { + DocumentTypeSelectForm: _('Step 1 of 3: Select document type'), + MetadataFormSet: _('Step 2 of 3: Enter document metadata'), + TagMultipleSelectionForm: _('Step 3 of 3: Select tags'), + } + template_name = 'appearance/generic_wizard.html' view_permission = permission_document_create - @staticmethod - def _has_metadata_types(wizard): - # Skip the 2nd step if document type has no associated metadata - try: - return wizard.get_cleaned_data_for_step('0')['document_type'].metadata.all().count() - except TypeError: - return False - def dispatch(self, request, *args, **kwargs): - if InteractiveSource.objects.filter(enabled=True).count() == 0: + if not InteractiveSource.objects.filter(enabled=True).exists(): messages.error( request, _( @@ -40,59 +53,67 @@ class DocumentCreateWizard(ViewPermissionCheckMixin, SessionWizardView): ) ) return HttpResponseRedirect(reverse('sources:setup_source_list')) + return super( DocumentCreateWizard, self ).dispatch(request, *args, **kwargs) - def __init__(self, *args, **kwargs): - super(DocumentCreateWizard, self).__init__(*args, **kwargs) - - self.condition_dict = {'1': DocumentCreateWizard._has_metadata_types} - - self.step_titles = [ - _('Step 1 of 2: Select document type'), - _('Step 2 of 2: Enter document metadata'), - ] - - def get_form_initial(self, step): - if step == '1': - initial = [] - - for document_type_metadata_type in self.get_cleaned_data_for_step('0')['document_type'].metadata.all(): - initial.append({ - 'document_type': self.get_cleaned_data_for_step('0')['document_type'], - 'metadata_type': document_type_metadata_type.metadata_type, - }) - - return initial - 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], + 'step_title': self.form_titles[form.__class__], 'submit_label': _('Next step'), 'submit_icon': 'fa fa-arrow-right', 'title': _('Document upload wizard'), }) return context + def get_form_initial(self, step): + if step == STEP_METADATA: + initial = [] + + for document_type_metadata_type in self.get_cleaned_data_for_step(STEP_DOCUMENT_TYPE)['document_type'].metadata.all(): + initial.append( + { + 'document_type': self.get_cleaned_data_for_step(STEP_DOCUMENT_TYPE)['document_type'], + 'metadata_type': document_type_metadata_type.metadata_type, + } + ) + + return initial + return self.initial_dict.get(step, {}) + + def get_form_kwargs(self, step): + # Tags form needs the user instance to determine which tags to + # display + if step == STEP_TAGS: + return {'user': self.request.user} + + return {} + def done(self, *args, **kwargs): query_dict = {} + try: - query_dict['document_type_id'] = self.get_cleaned_data_for_step('0')['document_type'].pk + query_dict['document_type_id'] = self.get_cleaned_data_for_step(STEP_DOCUMENT_TYPE)['document_type'].pk except AttributeError: pass try: - for identifier, metadata in enumerate(self.get_cleaned_data_for_step('1')): + for identifier, metadata in enumerate(self.get_cleaned_data_for_step(STEP_METADATA)): query_dict['metadata%s_id' % identifier] = metadata['id'] query_dict['metadata%s_value' % identifier] = metadata['value'] except TypeError: pass + try: + query_dict['tags'] = self.get_cleaned_data_for_step(STEP_TAGS)['tags'] + except AttributeError: + pass + url = '?'.join( [ reverse('sources:upload_interactive'), diff --git a/mayan/apps/tags/forms.py b/mayan/apps/tags/forms.py index e8b70b496f..711b29620b 100644 --- a/mayan/apps/tags/forms.py +++ b/mayan/apps/tags/forms.py @@ -32,3 +32,24 @@ class TagListForm(forms.Form): self.fields['tag'] = forms.ModelChoiceField( queryset=queryset, label=_('Tags')) + + +class TagMultipleSelectionForm(forms.Form): + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + logger.debug('user: %s', user) + super(TagMultipleSelectionForm, self).__init__(*args, **kwargs) + + queryset = Tag.objects.all() + try: + Permission.check_permissions(user, (permission_tag_view,)) + except PermissionDenied: + queryset = AccessControlList.objects.filter_by_access( + permission_tag_view, user, queryset + ) + + self.fields['tags'] = forms.MultipleChoiceField( + label=_('Tags'), choices=queryset.values_list('id', 'label'), + help_text=_('Tags to attach to the document.'),required=False, + widget=forms.CheckboxSelectMultiple + )