Files
mayan-edms/mayan/apps/metadata/views.py
Roberto Rosario 8e69178e07 Project: Switch to full app paths
Instead of inserting the path of the apps into the Python app,
the apps are now referenced by their full import path.

This app name claves with external or native Python libraries.
Example: Mayan statistics app vs. Python new statistics library.

Every app reference is now prepended with 'mayan.apps'.

Existing config.yml files need to be updated manually.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2018-12-05 02:04:20 -04:00

755 lines
25 KiB
Python

from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.urls import reverse, reverse_lazy
from django.utils.encoding import force_text
from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _, ungettext
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.generics import (
FormView, MultipleObjectFormActionView, SingleObjectCreateView,
SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView
)
from mayan.apps.documents.models import Document, DocumentType
from mayan.apps.documents.permissions import (
permission_document_type_edit
)
from .api import save_metadata_list
from .forms import (
DocumentAddMetadataForm, DocumentMetadataFormSet,
DocumentMetadataRemoveFormSet,
DocumentTypeMetadataTypeRelationshipFormSet, MetadataTypeForm
)
from .icons import (
icon_document_metadata_add, icon_document_metadata_edit,
icon_document_metadata_remove, icon_metadata
)
from .links import (
link_metadata_add, link_metadata_multiple_add,
link_setup_metadata_type_create
)
from .models import DocumentMetadata, MetadataType
from .permissions import (
permission_metadata_document_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view,
permission_metadata_type_create, permission_metadata_type_delete,
permission_metadata_type_edit, permission_metadata_type_view
)
class DocumentMetadataAddView(MultipleObjectFormActionView):
form_class = DocumentAddMetadataForm
model = Document
object_permission = permission_metadata_document_add
success_message = _('Metadata add request performed on %(count)d document')
success_message_plural = _(
'Metadata add request performed on %(count)d documents'
)
def dispatch(self, request, *args, **kwargs):
result = super(
DocumentMetadataAddView, self
).dispatch(request, *args, **kwargs)
queryset = self.get_queryset()
for document in queryset:
document.add_as_recent_document_for_user(request.user)
if len(set([document.document_type.pk for document in queryset])) > 1:
messages.error(
request, _('Selected documents must be of the same type.')
)
return HttpResponseRedirect(self.previous_url)
return result
def form_valid(self, form):
result = super(DocumentMetadataAddView, self).form_valid(form=form)
queryset = self.get_queryset()
if self.action_count == 1:
return HttpResponseRedirect(
reverse('metadata:metadata_edit', args=(queryset.first().pk,)),
)
elif self.action_count > 1:
return HttpResponseRedirect(
'%s?%s' % (
reverse('metadata:metadata_multiple_edit'),
urlencode(
{
'id_list': ','.join(
map(
force_text,
queryset.values_list('pk', flat=True)
)
)
}
)
)
)
return result
def get_extra_context(self):
queryset = self.get_queryset()
result = {
'submit_icon_class': icon_document_metadata_add,
'submit_label': _('Add'),
'title': ungettext(
'Add metadata types to document',
'Add metadata types to documents',
queryset.count()
)
}
if queryset.count() == 1:
result.update(
{
'object': queryset.first(),
'title': _(
'Add metadata types to document: %s'
) % queryset.first()
}
)
return result
def get_form_extra_kwargs(self):
queryset = self.get_queryset()
result = {}
if queryset.count():
result.update(
{
'document_type': queryset.first().document_type,
}
)
if queryset.count() == 1:
result.update(
{
'queryset': MetadataType.objects.get_for_document_type(
document_type=queryset.first().document_type
).exclude(
pk__in=MetadataType.objects.get_for_document(
document=queryset.first()
)
)
}
)
return result
def object_action(self, form, instance):
for metadata_type in form.cleaned_data['metadata_type']:
try:
created = False
try:
DocumentMetadata.objects.get(
document=instance,
metadata_type=metadata_type,
)
except DocumentMetadata.DoesNotExist:
document_metadata = DocumentMetadata(
document=instance,
metadata_type=metadata_type,
)
document_metadata.save(_user=self.request.user)
created = True
except Exception as exception:
messages.error(
self.request,
_(
'Error adding metadata type '
'"%(metadata_type)s" to document: '
'%(document)s; %(exception)s'
) % {
'metadata_type': metadata_type,
'document': instance,
'exception': ', '.join(
getattr(exception, 'messages', exception)
)
}
)
else:
if created:
messages.success(
self.request,
_(
'Metadata type: %(metadata_type)s '
'successfully added to document %(document)s.'
) % {
'metadata_type': metadata_type,
'document': instance
}
)
else:
messages.warning(
self.request, _(
'Metadata type: %(metadata_type)s already '
'present in document %(document)s.'
) % {
'metadata_type': metadata_type,
'document': instance
}
)
class DocumentMetadataEditView(MultipleObjectFormActionView):
form_class = DocumentMetadataFormSet
model = Document
object_permission = permission_metadata_document_edit
success_message = _(
'Metadata edit request performed on %(count)d document'
)
success_message_plural = _(
'Metadata edit request performed on %(count)d documents'
)
def dispatch(self, request, *args, **kwargs):
result = super(
DocumentMetadataEditView, self
).dispatch(request, *args, **kwargs)
queryset = self.get_queryset()
for document in queryset:
document.add_as_recent_document_for_user(request.user)
if len(set([document.document_type.pk for document in queryset])) > 1:
messages.error(
request, _('Selected documents must be of the same type.')
)
return HttpResponseRedirect(self.previous_url)
return result
def form_valid(self, form):
result = super(DocumentMetadataEditView, self).form_valid(form=form)
queryset = self.get_queryset()
if self.action_count == 1:
return HttpResponseRedirect(
reverse('metadata:metadata_edit', args=(queryset.first().pk,)),
)
elif self.action_count > 1:
return HttpResponseRedirect(
'%s?%s' % (
reverse('metadata:metadata_multiple_edit'),
urlencode(
{
'id_list': ','.join(
map(
force_text, queryset.values_list(
'pk', flat=True
)
)
)
}
)
)
)
return result
def get_extra_context(self):
queryset = self.get_queryset()
id_list = ','.join(
map(
force_text,
queryset.values_list('pk', flat=True)
)
)
if queryset.count() == 1:
no_results_main_link = link_metadata_add.resolve(
context=RequestContext(
request=self.request, dict_={'object': queryset.first()}
)
)
else:
no_results_main_link = link_metadata_multiple_add.resolve(
context=RequestContext(request=self.request)
)
no_results_main_link.url = '{}?id_list={}'.format(
no_results_main_link.url, id_list
)
result = {
'form_display_mode_table': True,
'no_results_icon': icon_metadata,
'no_results_main_link': no_results_main_link,
'no_results_text': _(
'Add metadata types available for this document\'s type '
'and assign them corresponding values.'
),
'no_results_title': _('There is no metadata to edit'),
'submit_icon_class': icon_document_metadata_edit,
'submit_label': _('Edit'),
'title': ungettext(
'Edit document metadata',
'Edit documents metadata',
queryset.count()
)
}
if queryset.count() == 1:
result.update(
{
'object': queryset.first(),
'title': _(
'Edit metadata for document: %s'
) % queryset.first()
}
)
return result
def get_initial(self):
queryset = self.get_queryset()
metadata_dict = {}
initial = []
for document in queryset:
document.add_as_recent_document_for_user(self.request.user)
for document_metadata in document.metadata.all():
metadata_dict.setdefault(
document_metadata.metadata_type, set()
)
if document_metadata.value:
metadata_dict[
document_metadata.metadata_type
].add(document_metadata.value)
for key, value in metadata_dict.items():
initial.append({
'document_type': document.document_type,
'metadata_type': key,
'value': ', '.join(value) if value else '',
})
return initial
def object_action(self, form, instance):
errors = []
for form in form.forms:
if form.cleaned_data['update']:
try:
save_metadata_list(
metadata_list=[form.cleaned_data], document=instance,
_user=self.request.user
)
except Exception as exception:
errors.append(exception)
for error in errors:
if settings.DEBUG:
raise
else:
if isinstance(error, ValidationError):
exception_message = ', '.join(error.messages)
else:
exception_message = force_text(error)
messages.error(
self.request, _(
'Error editing metadata for document: '
'%(document)s; %(exception)s.'
) % {
'document': instance,
'exception': exception_message
}
)
else:
messages.success(
self.request,
_(
'Metadata for document %s edited successfully.'
) % instance
)
class DocumentMetadataListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access(
permissions=permission_metadata_document_view,
user=self.request.user, obj=self.get_document()
)
return super(DocumentMetadataListView, self).dispatch(
request, *args, **kwargs
)
def get_document(self):
return get_object_or_404(Document, pk=self.kwargs['pk'])
def get_extra_context(self):
document = self.get_document()
return {
'hide_link': True,
'object': document,
'no_results_icon': icon_metadata,
'no_results_main_link': link_metadata_add.resolve(
context=RequestContext(
request=self.request, dict_={'object': document}
)
),
'no_results_text': _(
'Add metadata types this document\'s type '
'to be able to add them to individual documents. '
'Once added to individual document, you can then edit their '
'values.'
),
'no_results_title': _('This document doesn\'t have any metadata'),
'title': _('Metadata for document: %s') % document,
}
def get_object_list(self):
return self.get_document().metadata.all()
class DocumentMetadataRemoveView(MultipleObjectFormActionView):
form_class = DocumentMetadataRemoveFormSet
model = Document
object_permission = permission_metadata_document_remove
success_message = _(
'Metadata remove request performed on %(count)d document'
)
success_message_plural = _(
'Metadata remove request performed on %(count)d documents'
)
def dispatch(self, request, *args, **kwargs):
result = super(
DocumentMetadataRemoveView, self
).dispatch(request, *args, **kwargs)
queryset = self.get_queryset()
for document in queryset:
document.add_as_recent_document_for_user(request.user)
if len(set([document.document_type.pk for document in queryset])) > 1:
messages.error(
request, _('Selected documents must be of the same type.')
)
return HttpResponseRedirect(self.previous_url)
return result
def form_valid(self, form):
result = super(DocumentMetadataRemoveView, self).form_valid(form=form)
queryset = self.get_queryset()
if self.action_count == 1:
return HttpResponseRedirect(
reverse('metadata:metadata_edit', args=(queryset.first().pk,)),
)
elif self.action_count > 1:
return HttpResponseRedirect(
'%s?%s' % (
reverse('metadata:metadata_multiple_edit'),
urlencode(
{
'id_list': ','.join(
map(
force_text,
queryset.values_list('pk', flat=True)
)
)
}
)
)
)
return result
def get_extra_context(self):
queryset = self.get_queryset()
result = {
'form_display_mode_table': True,
'submit_icon_class': icon_document_metadata_remove,
'submit_label': _('Remove'),
'title': ungettext(
'Remove metadata types from the document',
'Remove metadata types from the documents',
queryset.count()
)
}
if queryset.count() == 1:
result.update(
{
'object': queryset.first(),
'title': _(
'Remove metadata types from the document: %s'
) % queryset.first()
}
)
return result
def get_initial(self):
queryset = self.get_queryset()
metadata = {}
for document in queryset:
document.add_as_recent_document_for_user(self.request.user)
for document_metadata in document.metadata.all():
# Metadata value cannot be None here, fallback to an empty
# string
value = document_metadata.value or ''
if document_metadata.metadata_type in metadata:
if value not in metadata[document_metadata.metadata_type]:
metadata[document_metadata.metadata_type].append(value)
else:
metadata[document_metadata.metadata_type] = [value] if value else ''
initial = []
for key, value in metadata.items():
initial.append(
{
'document_type': queryset.first().document_type,
'metadata_type': key,
'value': ', '.join(value)
}
)
return initial
def object_action(self, form, instance):
for form in form.forms:
if form.cleaned_data['update']:
metadata_type = get_object_or_404(
MetadataType, pk=form.cleaned_data['id']
)
try:
document_metadata = DocumentMetadata.objects.get(
document=instance, metadata_type=metadata_type
)
document_metadata.delete(_user=self.request.user)
messages.success(
self.request,
_(
'Successfully remove metadata type "%(metadata_type)s" from document: %(document)s.'
) % {
'metadata_type': metadata_type,
'document': instance
}
)
except Exception as exception:
messages.error(
self.request,
_(
'Error removing metadata type "%(metadata_type)s" from document: %(document)s; %(exception)s'
) % {
'metadata_type': metadata_type,
'document': instance,
'exception': ', '.join(exception.messages)
}
)
# Setup views
class MetadataTypeCreateView(SingleObjectCreateView):
extra_context = {'title': _('Create metadata type')}
form_class = MetadataTypeForm
model = MetadataType
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
view_permission = permission_metadata_type_create
def get_save_extra_data(self):
return {
'_user': self.request.user,
}
class MetadataTypeDeleteView(SingleObjectDeleteView):
model = MetadataType
object_permission = permission_metadata_type_delete
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
def get_extra_context(self):
return {
'delete_view': True,
'object': self.get_object(),
'title': _('Delete the metadata type: %s?') % self.get_object(),
}
class MetadataTypeEditView(SingleObjectEditView):
form_class = MetadataTypeForm
model = MetadataType
object_permission = permission_metadata_type_edit
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
def get_extra_context(self):
return {
'object': self.get_object(),
'title': _('Edit metadata type: %s') % self.get_object(),
}
def get_save_extra_data(self):
return {
'_user': self.request.user,
}
class MetadataTypeListView(SingleObjectListView):
object_permission = permission_metadata_type_view
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Internal name'),
'attribute': 'name',
},
),
'hide_link': True,
'no_results_icon': icon_metadata,
'no_results_main_link': link_setup_metadata_type_create.resolve(
context=RequestContext(request=self.request)
),
'no_results_text': _(
'Metadata types are users defined properties that can be '
'assigned values. Once created they must be associated to '
'document types, either as optional or required, for each. '
'Setting a metadata type as required for a document type '
'will block the upload of documents of that type until a '
'metadata value is provided.'
),
'no_results_title': _('There are no metadata types'),
'title': _('Metadata types'),
}
def get_object_list(self):
return MetadataType.objects.all()
class SetupDocumentTypeMetadataTypes(FormView):
form_class = DocumentTypeMetadataTypeRelationshipFormSet
main_model = 'document_type'
model = DocumentType
submodel = MetadataType
def form_valid(self, form):
try:
for instance in form:
instance.save()
except Exception as exception:
messages.error(
self.request,
_('Error updating relationship; %s') % exception
)
else:
messages.success(
self.request, _('Relationships updated successfully')
)
return super(
SetupDocumentTypeMetadataTypes, self
).form_valid(form=form)
def get_extra_context(self):
return {
'form_display_mode_table': True,
'no_results_icon': icon_metadata,
'no_results_main_link': link_setup_metadata_type_create.resolve(
context=RequestContext(request=self.request)
),
'no_results_text': _(
'Create metadata types to be able to associate them '
'to this document type.'
),
'no_results_title': _('There are no metadata types available'),
'object': self.get_object(),
'title': _(
'Metadata types for document type: %s'
) % self.get_object()
}
def get_form_extra_kwargs(self):
return {
'_user': self.request.user,
}
def get_initial(self):
obj = self.get_object()
initial = []
for element in self.get_queryset():
initial.append({
'document_type': obj,
'main_model': self.main_model,
'metadata_type': element,
})
return initial
def get_object(self):
obj = get_object_or_404(self.model, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=(permission_metadata_type_edit,),
user=self.request.user, obj=obj
)
return obj
def get_post_action_redirect(self):
return reverse('documents:document_type_list')
def get_queryset(self):
queryset = self.submodel.objects.all()
return AccessControlList.objects.filter_by_access(
permission=permission_document_type_edit,
user=self.request.user, queryset=queryset
)
class SetupMetadataTypesDocumentTypes(SetupDocumentTypeMetadataTypes):
main_model = 'metadata_type'
model = MetadataType
submodel = DocumentType
def get_extra_context(self):
return {
'form_display_mode_table': True,
'object': self.get_object(),
'title': _(
'Document types for metadata type: %s'
) % self.get_object()
}
def get_initial(self):
obj = self.get_object()
initial = []
for element in self.get_queryset():
initial.append({
'document_type': element,
'main_model': self.main_model,
'metadata_type': obj,
})
return initial
def get_post_action_redirect(self):
return reverse('metadata:setup_metadata_type_list')