diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index d6d1da3810..3515837b0b 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -15,7 +15,6 @@ from documents.conf.settings import ENABLE_SINGLE_DOCUMENT_UPLOAD PERMISSION_DOCUMENT_CREATE = 'document_create' PERMISSION_DOCUMENT_PROPERTIES_EDIT = 'document_properties_edit' PERMISSION_DOCUMENT_EDIT = 'document_edit' -PERMISSION_DOCUMENT_METADATA_EDIT = 'document_metadata_edit' PERMISSION_DOCUMENT_VIEW = 'document_view' PERMISSION_DOCUMENT_DELETE = 'document_delete' PERMISSION_DOCUMENT_DOWNLOAD = 'document_download' @@ -26,7 +25,6 @@ register_permissions('documents', [ {'name': PERMISSION_DOCUMENT_CREATE, 'label': _(u'Create document')}, {'name': PERMISSION_DOCUMENT_PROPERTIES_EDIT, 'label': _(u'Edit document properties')}, {'name': PERMISSION_DOCUMENT_EDIT, 'label': _(u'Edit document')}, - {'name': PERMISSION_DOCUMENT_METADATA_EDIT, 'label': _(u'Edit document metadata')}, {'name': PERMISSION_DOCUMENT_VIEW, 'label': _(u'View document')}, {'name': PERMISSION_DOCUMENT_DELETE, 'label': _(u'Delete document')}, {'name': PERMISSION_DOCUMENT_DOWNLOAD, 'label': _(u'Download document')}, @@ -44,8 +42,6 @@ document_view_simple = {'text': _(u'details (simple)'), 'view': 'document_view_s document_delete = {'text': _(u'delete'), 'view': 'document_delete', 'args': 'object.id', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_delete', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}} document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]}} -document_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_edit_metadata', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}} -document_multiple_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_multiple_edit_metadata', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}} document_preview = {'text': _(u'preview'), 'class': 'fancybox', 'view': 'document_preview', 'args': 'object.id', 'famfam': 'magnifier', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_download = {'text': _(u'download'), 'view': 'document_download', 'args': 'object.id', 'famfam': 'page_save', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}} document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_refresh', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} @@ -83,10 +79,10 @@ metadata_group_create_sibling = {'text': _(u'upload new document using same meta staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': 'object.id', 'famfam': 'drive_magnify'} staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': 'object.id', 'famfam': 'drive_delete'} -register_links(Document, [document_view_simple, document_view, document_edit, document_edit_metadata, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) +register_links(Document, [document_view_simple, document_view, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations]) register_links(Document, [document_create_sibling], menu_name='sidebar') -register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_edit_metadata, document_multiple_delete]) +register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete]) if ENABLE_SINGLE_DOCUMENT_UPLOAD: register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list_recent, document_list, document_create, document_create_multiple], menu_name='sidebar') diff --git a/apps/documents/admin.py b/apps/documents/admin.py index c1cb7d22e1..215923a88e 100644 --- a/apps/documents/admin.py +++ b/apps/documents/admin.py @@ -1,17 +1,14 @@ from django.contrib import admin -from documents.models import MetadataType, DocumentType, Document, \ - MetadataSet, MetadataSetItem, DocumentMetadata, \ +from metadata.admin import DocumentMetadataInline + +from documents.models import DocumentType, Document, \ DocumentTypeFilename, MetadataIndex, DocumentPage, DocumentGroup, \ DocumentGroupItem, DocumentPageTransformation, RecentDocument from filesystem_serving.admin import DocumentMetadataIndexInline -class MetadataTypeAdmin(admin.ModelAdmin): - list_display = ('name', 'title', 'default', 'lookup') - - class MetadataIndexInline(admin.StackedInline): model = MetadataIndex extra = 1 @@ -19,13 +16,6 @@ class MetadataIndexInline(admin.StackedInline): allow_add = True -class MetadataSetItemInline(admin.StackedInline): - model = MetadataSetItem - extra = 1 - classes = ('collapse-open',) - allow_add = True - - class DocumentTypeFilenameInline(admin.StackedInline): model = DocumentTypeFilename extra = 1 @@ -39,13 +29,6 @@ class DocumentTypeAdmin(admin.ModelAdmin): ] -class DocumentMetadataInline(admin.StackedInline): - model = DocumentMetadata - extra = 0 - classes = ('collapse-open',) - allow_add = False - - class DocumentPageTransformationAdmin(admin.ModelAdmin): model = DocumentPageTransformation @@ -85,15 +68,9 @@ class RecentDocumentAdmin(admin.ModelAdmin): date_hierarchy = 'datetime_accessed' -class MetadataSetAdmin(admin.ModelAdmin): - inlines = [MetadataSetItemInline] - - -admin.site.register(MetadataType, MetadataTypeAdmin) admin.site.register(DocumentType, DocumentTypeAdmin) admin.site.register(Document, DocumentAdmin) -admin.site.register(MetadataGroup, MetadataGroupAdmin) +admin.site.register(DocumentGroup, DocumentGroupAdmin) admin.site.register(DocumentPageTransformation, DocumentPageTransformationAdmin) admin.site.register(RecentDocument, RecentDocumentAdmin) -admin.site.register(MetadataSet, MetadataSetAdmin) diff --git a/apps/documents/conf/settings.py b/apps/documents/conf/settings.py index d504fef996..cae9ccc2c2 100644 --- a/apps/documents/conf/settings.py +++ b/apps/documents/conf/settings.py @@ -1,11 +1,9 @@ """Configuration options for the documents app""" -import datetime import hashlib import uuid from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User from common.utils import proper_name from storage.backends.filebasedstorage import FileBasedStorage @@ -21,14 +19,6 @@ def default_uuid(): """unicode(uuid.uuid4())""" return unicode(uuid.uuid4()) -default_available_functions = { - 'current_date': datetime.datetime.now().date, -} - -default_available_models = { - 'User': User -} - available_transformations = { 'rotate': {'label': _(u'Rotate [degrees]'), 'arguments': [{'name': 'degrees'}]} } @@ -42,8 +32,6 @@ register_settings( module=u'documents.conf.settings', settings=[ # Definition - {'name': u'AVAILABLE_FUNCTIONS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_FUNCTIONS', 'default': default_available_functions}, - {'name': u'AVAILABLE_MODELS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_MODELS', 'default': default_available_models}, {'name': u'AVAILABLE_INDEXING_FUNCTIONS', 'global_name': u'DOCUMENTS_INDEXING_AVAILABLE_FUNCTIONS', 'default': available_indexing_functions}, # Upload {'name': u'USE_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_USE_STAGING_DIRECTORY', 'default': False}, diff --git a/apps/documents/forms.py b/apps/documents/forms.py index a321f39cc6..94e3474a2c 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -16,12 +16,11 @@ from common.literals import PAGE_SIZE_CHOICES, PAGE_ORIENTATION_CHOICES from common.conf.settings import DEFAULT_PAPER_SIZE from common.conf.settings import DEFAULT_PAGE_ORIENTATION from common.utils import urlquote +from metadata.models import MetadataSet, MetadataType from documents.staging import StagingFile from documents.models import Document, DocumentType, \ - DocumentPage, DocumentPageTransformation, MetadataSet, MetadataType -from documents.conf.settings import AVAILABLE_FUNCTIONS -from documents.conf.settings import AVAILABLE_MODELS + DocumentPage, DocumentPageTransformation class DocumentPageTransformationForm(forms.ModelForm): @@ -249,54 +248,6 @@ class DocumentTypeSelectForm(forms.Form): document_type = forms.ModelChoiceField(queryset=DocumentType.objects.all(), label=(u'Document type'), required=False) -class MetadataForm(forms.Form): - def __init__(self, *args, **kwargs): - super(MetadataForm, self).__init__(*args, **kwargs) - - #Set form fields initial values - if 'initial' in kwargs: - self.metadata_type = kwargs['initial'].pop('metadata_type', None) - #self.document_type = kwargs['initial'].pop('document_type', None) - - # FIXME: - #required = self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required - required = False - required_string = u'' - if required: - self.fields['value'].required = True - required_string = ' (%s)' % ugettext(u'required') - else: - #TODO: FIXME: not working correctly - self.fields['value'].required = False - - self.fields['name'].initial = '%s%s' % ((self.metadata_type.title if self.metadata_type.title else self.metadata_type.name), required_string) - self.fields['id'].initial = self.metadata_type.id - if self.metadata_type.default: - try: - self.fields['value'].initial = eval(self.metadata_type.default, AVAILABLE_FUNCTIONS) - except Exception, err: - self.fields['value'].initial = err - - if self.metadata_type.lookup: - try: - choices = eval(self.metadata_type.lookup, AVAILABLE_MODELS) - self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label) - choices = zip(choices, choices) - if not required: - choices.insert(0, ('', '------')) - self.fields['value'].choices = choices - self.fields['value'].required = required - except Exception, err: - self.fields['value'].initial = err - self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'}) - - id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput) - name = forms.CharField(label=_(u'Name'), - required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) - value = forms.CharField(label=_(u'Value'), required=False) - -MetadataFormSet = formset_factory(MetadataForm, extra=0) - class DocumentCreateWizard(BoundFormWizard): def generate_metadata_initial_values(self): initial = [] diff --git a/apps/documents/managers.py b/apps/documents/managers.py new file mode 100644 index 0000000000..4888f7cd08 --- /dev/null +++ b/apps/documents/managers.py @@ -0,0 +1,52 @@ +from django.db import models + +#from documents.models import DocumentGroup +from metadata.classes import MetadataObject + + +class DocumentGroupManager(models.Manager): + def get_groups_for(self, document, group_obj=None): + errors = [] + metadata_groups = {} + metadata_dict = {} + for document_metadata in document.documentmetadata_set.all(): + metadata_dict[document_metadata.metadata_type.name] = document_metadata.value + eval_dict = {} + eval_dict['document'] = document + eval_dict['metadata'] = MetadataObject(metadata_dict) + + #if group_obj: + # groups_qs = DocumentGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True) & Q(pk=group_obj.pk)) + #else: + # groups_qs = DocumentGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True)) + groups_qs=[] + + for group in groups_qs: + total_query = Q() + for item in group.metadatagroupitem_set.filter(enabled=True): + try: + value_query = Q(**{'value__%s' % item.operator: eval(item.expression, eval_dict)}) + if item.negated: + query = (Q(metadata_type__id=item.metadata_type_id) & ~value_query) + else: + query = (Q(metadata_type__id=item.metadata_type_id) & value_query) + + if item.inclusion == INCLUSION_AND: + total_query &= query + elif item.inclusion == INCLUSION_OR: + total_query |= query + except Exception, e: + errors.append(e) + value_query = Q() + query = Q() + + if total_query: + document_id_list = DocumentMetadata.objects.filter(total_query).values_list('document', flat=True) + metadata_groups[group] = Document.objects.filter(Q(id__in=document_id_list)).order_by('file_filename') or [] + else: + metadata_groups[group] = [] + + if group_obj: + return metadata_groups[group_obj], errors + + return metadata_groups, errors diff --git a/apps/documents/models.py b/apps/documents/models.py index 8f023ff5f7..dbb39d752d 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -15,18 +15,16 @@ from taggit.managers import TaggableManager from dynamic_search.api import register from converter.api import get_page_count from converter import TRANFORMATION_CHOICES +from metadata.classes import MetadataObject from documents.conf.settings import AVAILABLE_INDEXING_FUNCTIONS -from documents.conf.settings import AVAILABLE_FUNCTIONS -from documents.conf.settings import AVAILABLE_MODELS from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION from documents.conf.settings import STORAGE_BACKEND from documents.conf.settings import AVAILABLE_TRANSFORMATIONS from documents.conf.settings import DEFAULT_TRANSFORMATIONS from documents.conf.settings import RECENT_COUNT -from documents.classes import MetadataObject - +from documents.managers import DocumentGroupManager def get_filename_from_uuid(instance, filename): filename, extension = os.path.splitext(filename) @@ -190,7 +188,7 @@ class Document(models.Model): return u', '.join([u'%s - %s' % (metadata.metadata_type, metadata.value) for metadata in self.documentmetadata_set.select_related('metadata_type', 'document').defer('document__document_type', 'document__file', 'document__description', 'document__file_filename', 'document__uuid', 'document__date_added', 'document__date_updated', 'document__file_mimetype', 'document__file_mime_encoding')]) def get_metadata_groups(self, group_obj=None): - return MetadataGroup.objects.get_groups_for(self, group_obj) + return DocumentGroup.objects.get_groups_for(self, group_obj) def apply_default_transformations(self): #Only apply default transformations on new documents @@ -207,60 +205,8 @@ class Document(models.Model): page_transformation.save() -available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else u'' -available_models_string = (_(u' Available models: %s') % u','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else u'' - - -class MetadataType(models.Model): - name = models.CharField(max_length=48, verbose_name=_(u'name'), help_text=_(u'Do not use python reserved words, or spaces.')) - title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True) - default = models.CharField(max_length=128, blank=True, null=True, - verbose_name=_(u'default'), - help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string) - lookup = models.CharField(max_length=128, blank=True, null=True, - verbose_name=_(u'lookup'), - help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string) - #TODO: datatype? - - def __unicode__(self): - return self.title if self.title else self.name - - class Meta: - verbose_name = _(u'metadata type') - verbose_name_plural = _(u'metadata types') - - -class MetadataSet(models.Model): - title = models.CharField(max_length=48, verbose_name=_(u'title')) - - def __unicode__(self): - return self.title if self.title else self.name - - class Meta: - verbose_name = _(u'metadata set') - verbose_name_plural = _(u'metadata set') - - -class MetadataSetItem(models.Model): - """ - Define the set of metadata that relates to a set or group of - metadata fields - """ - metadata_set = models.ForeignKey(MetadataSet, verbose_name=_(u'metadata set')) - metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type')) - #required = models.BooleanField(default=True, verbose_name=_(u'required')) - - def __unicode__(self): - return unicode(self.metadata_type) - - class Meta: - verbose_name = _(u'metadata set item') - verbose_name_plural = _(u'metadata set items') - - available_indexing_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_INDEXING_FUNCTIONS.items()])) if AVAILABLE_INDEXING_FUNCTIONS else u'' - class MetadataIndex(models.Model): document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) expression = models.CharField(max_length=128, @@ -276,23 +222,6 @@ class MetadataIndex(models.Model): verbose_name_plural = _(u'metadata indexes') -class DocumentMetadata(models.Model): - """ - Link a document to a specific instance of a metadata type with it's - current value - """ - document = models.ForeignKey(Document, verbose_name=_(u'document')) - metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type')) - value = models.TextField(blank=True, null=True, verbose_name=_(u'metadata value'), db_index=True) - - def __unicode__(self): - return unicode(self.metadata_type) - - class Meta: - verbose_name = _(u'document metadata') - verbose_name_plural = _(u'document metadata') - - class DocumentTypeFilename(models.Model): """ List of filenames available to a specific document type for the @@ -347,61 +276,13 @@ class DocumentPage(models.Model): return ' '.join(transformation_list), warnings -class MetadataGroupManager(models.Manager): - def get_groups_for(self, document, group_obj=None): - errors = [] - metadata_groups = {} - metadata_dict = {} - for document_metadata in document.documentmetadata_set.all(): - metadata_dict[document_metadata.metadata_type.name] = document_metadata.value - eval_dict = {} - eval_dict['document'] = document - eval_dict['metadata'] = MetadataObject(metadata_dict) - - if group_obj: - groups_qs = MetadataGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True) & Q(pk=group_obj.pk)) - else: - groups_qs = MetadataGroup.objects.filter((Q(document_type=document.document_type) | Q(document_type=None)) & Q(enabled=True)) - - for group in groups_qs: - total_query = Q() - for item in group.metadatagroupitem_set.filter(enabled=True): - try: - value_query = Q(**{'value__%s' % item.operator: eval(item.expression, eval_dict)}) - if item.negated: - query = (Q(metadata_type__id=item.metadata_type_id) & ~value_query) - else: - query = (Q(metadata_type__id=item.metadata_type_id) & value_query) - - if item.inclusion == INCLUSION_AND: - total_query &= query - elif item.inclusion == INCLUSION_OR: - total_query |= query - except Exception, e: - errors.append(e) - value_query = Q() - query = Q() - - if total_query: - document_id_list = DocumentMetadata.objects.filter(total_query).values_list('document', flat=True) - metadata_groups[group] = Document.objects.filter(Q(id__in=document_id_list)).order_by('file_filename') or [] - else: - metadata_groups[group] = [] - - if group_obj: - return metadata_groups[group_obj], errors - - return metadata_groups, errors - - class DocumentGroup(models.Model): document_type = models.ManyToManyField(DocumentType, null=True, blank=True, verbose_name=_(u'document type'), help_text=_(u'If left blank, all document types will be matched.')) - #name = models.CharField(max_length=32, verbose_name=_(u'name')) label = models.CharField(max_length=32, verbose_name=_(u'label')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) - objects = MetadataGroupManager() + objects = DocumentGroupManager() def __unicode__(self): return self.label if self.label else self.name @@ -443,10 +324,10 @@ OPERATOR_CHOICES = ( class DocumentGroupItem(models.Model): metadata_group = models.ForeignKey(DocumentGroup, verbose_name=_(u'metadata group')) inclusion = models.CharField(default=INCLUSION_AND, max_length=16, choices=INCLUSION_CHOICES, help_text=_(u'The inclusion is ignored for the first item.')) - foreign_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_foreign', verbose_name=_(u'foreign metadata'), help_text=_(u'This represents the metadata of all other documents.')) + #foreign_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_foreign', verbose_name=_(u'foreign metadata'), help_text=_(u'This represents the metadata of all other documents.')) operator = models.CharField(max_length=16, choices=OPERATOR_CHOICES) - local_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_local', verbose_name=_(u'local metadata'), help_text=_(u'This represents the metadata of the current document.')) + #local_metadata_type = models.ForeignKey(MetadataType, related_name='metadata_type_local', verbose_name=_(u'local metadata'), help_text=_(u'This represents the metadata of the current document.')) expression = models.TextField(verbose_name=_(u'expression'), help_text=_(u'This expression will be evaluated against the current selected document. The document metadata is available as variables `metadata` and document properties under the variable `document`.')) negated = models.BooleanField(default=False, verbose_name=_(u'negated'), help_text=_(u'Inverts the logic of the operator.')) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) @@ -458,8 +339,7 @@ class DocumentGroupItem(models.Model): verbose_name = _(u'group item') verbose_name_plural = _(u'group items') - -available_transformations = ([(name, data['label']) for name, data in AVAILABLE_TRANSFORMATIONS.items()]) if AVAILABLE_MODELS else [] +available_transformations = ([(name, data['label']) for name, data in AVAILABLE_TRANSFORMATIONS.items()]) class DocumentPageTransformation(models.Model): diff --git a/apps/documents/urls.py b/apps/documents/urls.py index e29ddd1b24..22308c3982 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -21,8 +21,6 @@ urlpatterns = patterns('documents.views', url(r'^document/(?P\d+)/delete/$', 'document_delete', (), 'document_delete'), url(r'^document/multiple/delete/$', 'document_multiple_delete', (), 'document_multiple_delete'), url(r'^document/(?P\d+)/edit/$', 'document_edit', (), 'document_edit'), - url(r'^document/(?P\d+)/edit/metadata/$', 'document_edit_metadata', (), 'document_edit_metadata'), - url(r'^document/multiple/edit/metadata/$', 'document_multiple_edit_metadata', (), 'document_multiple_edit_metadata'), url(r'^document/(?P\d+)/print/$', 'document_print', (), 'document_print'), url(r'^document/(?P\d+)/hard_copy/$', 'document_hard_copy', (), 'document_hard_copy'), diff --git a/apps/documents/views.py b/apps/documents/views.py index da29c7ee65..6c1915d593 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -31,6 +31,8 @@ from converter.api import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, \ from common.literals import PAGE_SIZE_DIMENSIONS, \ PAGE_ORIENTATION_PORTRAIT, PAGE_ORIENTATION_LANDSCAPE from common.conf.settings import DEFAULT_PAPER_SIZE +from metadata.api import save_metadata_list, \ + decode_metadata_from_url, metadata_repr_as_list from documents.conf.settings import DELETE_STAGING_FILE_AFTER_UPLOAD from documents.conf.settings import USE_STAGING_DIRECTORY @@ -48,7 +50,7 @@ from documents.conf.settings import PRINT_SIZE from documents import PERMISSION_DOCUMENT_CREATE, \ PERMISSION_DOCUMENT_PROPERTIES_EDIT, \ - PERMISSION_DOCUMENT_METADATA_EDIT, PERMISSION_DOCUMENT_VIEW, \ + PERMISSION_DOCUMENT_VIEW, \ PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \ PERMISSION_DOCUMENT_TRANSFORM, \ PERMISSION_DOCUMENT_EDIT @@ -56,14 +58,12 @@ from documents import PERMISSION_DOCUMENT_CREATE, \ from documents.forms import DocumentTypeSelectForm, DocumentCreateWizard, \ DocumentForm, DocumentForm_edit, DocumentForm_view, \ StagingDocumentForm, DocumentPreviewForm, \ - MetadataFormSet, DocumentPageForm, DocumentPageTransformationForm, \ + DocumentPageForm, DocumentPageTransformationForm, \ DocumentContentForm, DocumentPageForm_edit, MetaDataGroupForm, \ DocumentPageForm_text, PrintForm, MetadataSelectionForm -from documents.metadata import save_metadata_list, \ - decode_metadata_from_url, metadata_repr_as_list from documents.models import Document, DocumentType, DocumentPage, \ - DocumentPageTransformation, RecentDocument, MetadataGroup + DocumentPageTransformation, RecentDocument, DocumentGroup from documents.staging import StagingFile from documents import metadata_group_link from documents.literals import PICTURE_ERROR_SMALL, PICTURE_ERROR_MEDIUM, \ @@ -455,89 +455,6 @@ def document_edit(request, document_id): }, context_instance=RequestContext(request)) -def document_edit_metadata(request, document_id=None, document_id_list=None): - check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_METADATA_EDIT]) - - if document_id: - documents = [get_object_or_404(Document, pk=document_id)] - elif document_id_list: - documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] - if len(set([document.document_type for document in documents])) > 1: - messages.error(request, _(u'All documents must be from the same type.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - else: - messages.error(request, _(u'Must provide at least one document.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - - metadata = {} - for document in documents: - RecentDocument.objects.add_document_for_user(request.user, document) - - for item in document.documentmetadata_set.all(): - value = item.value - if item.metadata_type in metadata: - if value not in metadata[item.metadata_type]: - metadata[item.metadata_type].append(value) - else: - metadata[item.metadata_type] = [value] - - initial = [] - for key, value in metadata.items(): - initial.append({ - 'metadata_type': key, - 'value': u', '.join(value) - }) - - formset = MetadataFormSet(initial=initial) - if request.method == 'POST': - formset = MetadataFormSet(request.POST) - if formset.is_valid(): - for document in documents: - try: - document_delete_fs_links(document) - except Exception, e: - messages.error(request, _(u'Error deleting filesystem links for document: %(document)s; %(error)s') % { - 'document': document, 'error': e}) - - save_metadata_list(formset.cleaned_data, document) - - messages.success(request, _(u'Metadata for document %s edited successfully.') % document) - - try: - warnings = document_create_fs_links(document) - - if request.user.is_staff or request.user.is_superuser: - for warning in warnings: - messages.warning(request, warning) - - messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) - except Exception, e: - messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { - 'document': document, 'error': e}) - - if len(documents) == 1: - return HttpResponseRedirect(document.get_absolute_url()) - elif len(documents) > 1: - return HttpResponseRedirect(reverse('document_list')) - - context = { - 'form_display_mode_table': True, - 'form': formset, - } - if len(documents) == 1: - context['object'] = documents[0] - context['title'] = _(u'Edit metadata for document: %s') % ', '.join([unicode(d) for d in documents]) - elif len(documents) > 1: - context['title'] = _(u'Edit metadata for documents: %s') % ', '.join([unicode(d) for d in documents]) - - return render_to_response('generic_form.html', context, - context_instance=RequestContext(request)) - - -def document_multiple_edit_metadata(request): - return document_edit_metadata(request, document_id_list=request.GET.get('id_list', [])) - - def calculate_converter_arguments(document, *args, **kwargs): size = kwargs.pop('size', PREVIEW_SIZE) quality = kwargs.pop('quality', QUALITY_DEFAULT) diff --git a/apps/metadata/__init__.py b/apps/metadata/__init__.py new file mode 100644 index 0000000000..f3bfe8c528 --- /dev/null +++ b/apps/metadata/__init__.py @@ -0,0 +1,28 @@ +from django.utils.translation import ugettext_lazy as _ + +from navigation.api import register_links, register_menu, \ + register_model_list_columns, register_multi_item_links +from main.api import register_diagnostic, register_tool +from permissions.api import register_permissions + +from documents.models import Document + +PERMISSION_METADATA_DOCUMENT_EDIT = u'metadata_document_edit' +PERMISSION_METADATA_DOCUMENT_ADD = u'metadata_document_add' +PERMISSION_METADATA_DOCUMENT_REMOVE = u'metadata_document_remove' + +register_permissions('metadata', [ + {'name': PERMISSION_METADATA_DOCUMENT_EDIT, 'label': _(u'Edit a document\'s metadata')}, + {'name': PERMISSION_METADATA_DOCUMENT_ADD, 'label': _(u'Add metadata to a document')}, + {'name': PERMISSION_METADATA_DOCUMENT_REMOVE, 'label': _(u'Remove metadata from a document')}, +]) + +metadata_edit = {'text': _(u'edit metadata'), 'view': 'metadata_edit', 'args': 'object.id', 'famfam': 'xhtml_go', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]}} +metadata_multiple_edit = {'text': _(u'edit metadata'), 'view': 'metadata_multiple_edit', 'famfam': 'xhtml_go', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]}} +metadata_add = {'text': _(u'add metadata'), 'view': 'metadata_add', 'args': 'object.id', 'famfam': 'xhtml_add', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]}} +metadata_multiple_add = {'text': _(u'add metadata'), 'view': 'metadata_multiple_add', 'famfam': 'xhtml_add', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]}} +metadata_remove = {'text': _(u'remove metadata'), 'view': 'metadata_remove', 'args': 'object.id', 'famfam': 'xhtml_delete', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]}} +metadata_multiple_remove = {'text': _(u'remove metadata'), 'view': 'metadata_multiple_remove', 'famfam': 'xhtml_delete', 'permissions': {'namespace': 'metadata', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]}} + +register_links(Document, [metadata_add, metadata_edit, metadata_remove]) +register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [metadata_multiple_add, metadata_multiple_edit, metadata_multiple_remove]) diff --git a/apps/metadata/admin.py b/apps/metadata/admin.py new file mode 100644 index 0000000000..d28ea231a4 --- /dev/null +++ b/apps/metadata/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin + +from metadata.models import MetadataType, MetadataSet, MetadataSetItem, \ + DocumentMetadata + + +class MetadataTypeAdmin(admin.ModelAdmin): + list_display = ('name', 'title', 'default', 'lookup') + + +class MetadataSetItemInline(admin.StackedInline): + model = MetadataSetItem + extra = 1 + classes = ('collapse-open',) + allow_add = True + + +class DocumentMetadataInline(admin.StackedInline): + model = DocumentMetadata + extra = 0 + classes = ('collapse-open',) + allow_add = False + + +class MetadataSetAdmin(admin.ModelAdmin): + inlines = [MetadataSetItemInline] + + +admin.site.register(MetadataType, MetadataTypeAdmin) +admin.site.register(MetadataSet, MetadataSetAdmin) diff --git a/apps/metadata/api.py b/apps/metadata/api.py index 9b5aaf5b1a..7bfe0acff0 100644 --- a/apps/metadata/api.py +++ b/apps/metadata/api.py @@ -3,10 +3,9 @@ from urllib import unquote_plus from django.shortcuts import get_object_or_404 -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext_lazy as _ -from documents.models import DocumentMetadata, MetadataType +from metadata.models import DocumentMetadata, MetadataType def decode_metadata_from_url(url_dict): @@ -59,13 +58,22 @@ def save_metadata_list(metadata_list, document): def save_metadata(metadata_dict, document): """save metadata_dict""" # Use matched metadata now to create document metadata - document_metadata, created = DocumentMetadata.objects.get_or_create( + #document_metadata, created = DocumentMetadata.objects.get_or_create( + # document=document, + # metadata_type=get_object_or_404( + # MetadataType, + # pk=metadata_dict['id'] + # ), + #) + + document_metadata = DocumentMetadata.objects.get( document=document, metadata_type=get_object_or_404( MetadataType, pk=metadata_dict['id'] ), ) + # Handle 'plus sign as space' in the url # unquote_plus handles utf-8?!? diff --git a/apps/metadata/conf/__init__.py b/apps/metadata/conf/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/apps/metadata/conf/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/metadata/conf/settings.py b/apps/metadata/conf/settings.py new file mode 100644 index 0000000000..a2955cad2a --- /dev/null +++ b/apps/metadata/conf/settings.py @@ -0,0 +1,27 @@ +"""Configuration options for the metadata app""" + +import datetime + +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User + +from smart_settings.api import register_settings + + +default_available_functions = { + 'current_date': datetime.datetime.now().date, +} + +default_available_models = { + 'User': User +} + +register_settings( + namespace=u'metadata', + module=u'metadata.conf.settings', + settings=[ + # Definition + {'name': u'AVAILABLE_FUNCTIONS', 'global_name': u'METADATA_AVAILABLE_FUNCTIONS', 'default': default_available_functions}, + {'name': u'AVAILABLE_MODELS', 'global_name': u'METADATA_AVAILABLE_MODELS', 'default': default_available_models}, + ] +) diff --git a/apps/metadata/forms.py b/apps/metadata/forms.py new file mode 100644 index 0000000000..7bee8872e7 --- /dev/null +++ b/apps/metadata/forms.py @@ -0,0 +1,67 @@ +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.forms.formsets import formset_factory + +from metadata.conf.settings import AVAILABLE_MODELS +from metadata.conf.settings import AVAILABLE_FUNCTIONS +from metadata.models import MetadataType, DocumentMetadata + + +class MetadataForm(forms.Form): + def __init__(self, *args, **kwargs): + super(MetadataForm, self).__init__(*args, **kwargs) + + #Set form fields initial values + if 'initial' in kwargs: + self.metadata_type = kwargs['initial'].pop('metadata_type', None) + #self.document_type = kwargs['initial'].pop('document_type', None) + + # FIXME: + #required = self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required + required = False + required_string = u'' + if required: + self.fields['value'].required = True + required_string = ' (%s)' % _(u'required') + else: + #TODO: FIXME: not working correctly + self.fields['value'].required = False + + self.fields['name'].initial = '%s%s' % ((self.metadata_type.title if self.metadata_type.title else self.metadata_type.name), required_string) + self.fields['id'].initial = self.metadata_type.pk + if self.metadata_type.default: + try: + self.fields['value'].initial = eval(self.metadata_type.default, AVAILABLE_FUNCTIONS) + except Exception, err: + self.fields['value'].initial = err + + if self.metadata_type.lookup: + try: + choices = eval(self.metadata_type.lookup, AVAILABLE_MODELS) + self.fields['value'] = forms.ChoiceField(label=self.fields['value'].label) + choices = zip(choices, choices) + if not required: + choices.insert(0, ('', '------')) + self.fields['value'].choices = choices + self.fields['value'].required = required + except Exception, err: + self.fields['value'].initial = err + self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'}) + + id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput) + name = forms.CharField(label=_(u'Name'), + required=False, widget=forms.TextInput(attrs={'readonly': 'readonly'})) + value = forms.CharField(label=_(u'Value'), required=False) + update = forms.BooleanField(initial=True, label=_(u'Update'), required=False) + +MetadataFormSet = formset_factory(MetadataForm, extra=0) + + +class AddMetadataForm(forms.Form): + metadata_type = forms.ModelChoiceField(queryset=MetadataType.objects.all(), label=_(u'Metadata type')) + + +class MetadataRemoveForm(MetadataForm): + update = forms.BooleanField(initial=False, label=_(u'Remove'), required=False) + +MetadataRemoveFormSet = formset_factory(MetadataRemoveForm, extra=0) diff --git a/apps/metadata/models.py b/apps/metadata/models.py new file mode 100644 index 0000000000..c298f5c3a7 --- /dev/null +++ b/apps/metadata/models.py @@ -0,0 +1,74 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from documents.models import Document + +from metadata.conf.settings import AVAILABLE_MODELS +from metadata.conf.settings import AVAILABLE_FUNCTIONS + +available_models_string = (_(u' Available models: %s') % u','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else u'' +available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else u'' + + +class MetadataType(models.Model): + name = models.CharField(unique=True, max_length=48, verbose_name=_(u'name'), help_text=_(u'Do not use python reserved words, or spaces.')) + title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True) + default = models.CharField(max_length=128, blank=True, null=True, + verbose_name=_(u'default'), + help_text=_(u'Enter a string to be evaluated.%s') % available_functions_string) + lookup = models.CharField(max_length=128, blank=True, null=True, + verbose_name=_(u'lookup'), + help_text=_(u'Enter a string to be evaluated. Example: [user.get_full_name() for user in User.objects.all()].%s') % available_models_string) + #TODO: datatype? + + def __unicode__(self): + return self.title if self.title else self.name + + class Meta: + verbose_name = _(u'metadata type') + verbose_name_plural = _(u'metadata types') + + +class MetadataSet(models.Model): + title = models.CharField(max_length=48, verbose_name=_(u'title')) + + def __unicode__(self): + return self.title if self.title else self.name + + class Meta: + verbose_name = _(u'metadata set') + verbose_name_plural = _(u'metadata set') + + +class MetadataSetItem(models.Model): + """ + Define the set of metadata that relates to a set or group of + metadata fields + """ + metadata_set = models.ForeignKey(MetadataSet, verbose_name=_(u'metadata set')) + metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'metadata type')) + #required = models.BooleanField(default=True, verbose_name=_(u'required')) + + def __unicode__(self): + return unicode(self.metadata_type) + + class Meta: + verbose_name = _(u'metadata set item') + verbose_name_plural = _(u'metadata set items') + + +class DocumentMetadata(models.Model): + """ + Link a document to a specific instance of a metadata type with it's + current value + """ + document = models.ForeignKey(Document, verbose_name=_(u'document')) + metadata_type = models.ForeignKey(MetadataType, verbose_name=_(u'type')) + value = models.TextField(blank=True, null=True, verbose_name=_(u'value'), db_index=True) + + def __unicode__(self): + return unicode(self.metadata_type) + + class Meta: + verbose_name = _(u'document metadata') + verbose_name_plural = _(u'document metadata') diff --git a/apps/metadata/tests.py b/apps/metadata/tests.py new file mode 100644 index 0000000000..2247054b35 --- /dev/null +++ b/apps/metadata/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/apps/metadata/urls.py b/apps/metadata/urls.py new file mode 100644 index 0000000000..3e5e841a3c --- /dev/null +++ b/apps/metadata/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('metadata.views', + url(r'^(?P\d+)/edit/$', 'metadata_edit', (), 'metadata_edit'), + url(r'^multiple/edit/$', 'metadata_multiple_edit', (), 'metadata_multiple_edit'), + url(r'^(?P\d+)/add/$', 'metadata_add', (), 'metadata_add'), + url(r'^multiple/add/$', 'metadata_multiple_add', (), 'metadata_multiple_add'), + url(r'^(?P\d+)/remove/$', 'metadata_remove', (), 'metadata_remove'), + url(r'^multiple/remove/$', 'metadata_multiple_remove', (), 'metadata_multiple_remove'), +) diff --git a/apps/metadata/views.py b/apps/metadata/views.py new file mode 100644 index 0000000000..cd99f777ee --- /dev/null +++ b/apps/metadata/views.py @@ -0,0 +1,251 @@ +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ +from django.shortcuts import get_object_or_404 +from django.contrib import messages +from django.http import HttpResponseRedirect +from django.core.urlresolvers import reverse +from django.utils.http import urlencode + +from documents.models import Document, RecentDocument +from permissions.api import check_permissions +from filesystem_serving.api import document_create_fs_links, document_delete_fs_links + +from metadata import PERMISSION_METADATA_DOCUMENT_EDIT, \ + PERMISSION_METADATA_DOCUMENT_ADD, PERMISSION_METADATA_DOCUMENT_REMOVE +from metadata.forms import MetadataFormSet, AddMetadataForm, MetadataRemoveFormSet +from metadata.api import save_metadata_list +from metadata.models import DocumentMetadata, MetadataType + + +def metadata_edit(request, document_id=None, document_id_list=None): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_EDIT]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + if documents[0].documentmetadata_set.count() == 0: + messages.warning(request, _(u'The selected document doesn\'t have any metadata.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + for item in document.documentmetadata_set.all(): + value = item.value + if item.metadata_type in metadata: + if value not in metadata[item.metadata_type]: + metadata[item.metadata_type].append(value) + else: + metadata[item.metadata_type] = [value] if value else u'' + + initial = [] + for key, value in metadata.items(): + initial.append({ + 'metadata_type': key, + 'value': u', '.join(value) + }) + + formset = MetadataFormSet(initial=initial) + if request.method == 'POST': + formset = MetadataFormSet(request.POST) + if formset.is_valid(): + for document in documents: + try: + document_delete_fs_links(document) + except Exception, e: + messages.error(request, _(u'Error deleting filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + for form in formset.forms: + if form.cleaned_data['update']: + try: + save_metadata_list([form.cleaned_data], document) + messages.success(request, _(u'Metadata for document %s edited successfully.') % document) + except Exception, e: + messages.error(request, _(u'Error editing metadata for document %(document)s; %(error)s.') % { + 'document': document, 'error': e}) + + try: + warnings = document_create_fs_links(document) + + if request.user.is_staff or request.user.is_superuser: + for warning in warnings: + messages.warning(request, warning) + + messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) + except Exception, e: + messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + if len(documents) == 1: + return HttpResponseRedirect(document.get_absolute_url()) + elif len(documents) > 1: + return HttpResponseRedirect(reverse('document_list_recent')) + + context = { + 'form_display_mode_table': True, + 'form': formset, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Edit metadata for document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Edit metadata for documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_edit(request): + return metadata_edit(request, document_id_list=request.GET.get('id_list', [])) + + +def metadata_add(request, document_id=None, document_id_list=None): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_ADD]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + if request.method == 'POST': + form = AddMetadataForm(request.POST) + if form.is_valid(): + metadata_type = form.cleaned_data['metadata_type'] + for document in documents: + document_metadata, created = DocumentMetadata.objects.get_or_create(document=document, metadata_type=metadata_type, defaults={'value': u''}) + if created: + messages.success(request, _(u'Metadata type: %(metadata_type)s successfully added to document %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + else: + messages.warning(request, _(u'Metadata type: %(metadata_type)s already present in document %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + if len(documents) == 1: + return HttpResponseRedirect(reverse(metadata_edit, args=[document.pk])) + elif len(documents) > 1: + return HttpResponseRedirect(u'%s?%s' % (reverse('metadata_multiple_edit'), urlencode({'id_list': document_id_list}))) + + else: + form = AddMetadataForm() + + context = { + #'form_display_mode_table': True, + 'form': form, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Add metadata type to document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Add metadata type to documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_add(request): + return metadata_add(request, document_id_list=request.GET.get('id_list', [])) + + +def metadata_remove(request, document_id): + check_permissions(request.user, 'metadata', [PERMISSION_METADATA_DOCUMENT_REMOVE]) + + if document_id: + documents = [get_object_or_404(Document, pk=document_id)] + if documents[0].documentmetadata_set.count() == 0: + messages.warning(request, _(u'The selected document doesn\'t have any metadata.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + elif document_id_list: + documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + else: + messages.error(request, _(u'Must provide at least one document.')) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + + metadata = {} + for document in documents: + RecentDocument.objects.add_document_for_user(request.user, document) + + for item in document.documentmetadata_set.all(): + value = item.value + if item.metadata_type in metadata: + if value not in metadata[item.metadata_type]: + metadata[item.metadata_type].append(value) + else: + metadata[item.metadata_type] = [value] if value else u'' + + initial = [] + for key, value in metadata.items(): + initial.append({ + 'metadata_type': key, + 'value': u', '.join(value) + }) + + formset = MetadataRemoveFormSet(initial=initial) + if request.method == 'POST': + formset = MetadataRemoveFormSet(request.POST) + if formset.is_valid(): + for document in documents: + try: + document_delete_fs_links(document) + except Exception, e: + messages.error(request, _(u'Error deleting filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + for form in formset.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=document, metadata_type=metadata_type) + document_metadata.delete() + messages.success(request, _(u'Successfully remove metadata type: %(metadata_type)s from document: %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + except: + messages.error(request, _(u'Error removing metadata type: %(metadata_type)s from document: %(document)s.') % { + 'metadata_type': metadata_type, 'document': document}) + + try: + warnings = document_create_fs_links(document) + + if request.user.is_staff or request.user.is_superuser: + for warning in warnings: + messages.warning(request, warning) + + messages.success(request, _(u'Filesystem links updated successfully for document: %s.') % document) + except Exception, e: + messages.error(request, _('Error creating filesystem links for document: %(document)s; %(error)s') % { + 'document': document, 'error': e}) + + if len(documents) == 1: + return HttpResponseRedirect(document.get_absolute_url()) + elif len(documents) > 1: + return HttpResponseRedirect(reverse('document_list_recent')) + + context = { + 'form_display_mode_table': True, + 'form': formset, + } + if len(documents) == 1: + context['object'] = documents[0] + context['title'] = _(u'Remove metadata types to document: %s') % ', '.join([unicode(d) for d in documents]) + elif len(documents) > 1: + context['title'] = _(u'Remove metadata types to documents: %s') % ', '.join([unicode(d) for d in documents]) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def metadata_multiple_remove(request): + return metadata_remove(request, document_id_list=request.GET.get('id_list', [])) diff --git a/settings.py b/settings.py index 407fa95034..1591f996bd 100644 --- a/settings.py +++ b/settings.py @@ -125,7 +125,7 @@ INSTALLED_APPS = ( 'web_theme', 'main', 'common', - 'documents', + 'metadata', 'pagination', 'dynamic_search', 'filetransfers', @@ -145,6 +145,7 @@ INSTALLED_APPS = ( 'tags', 'document_comments', 'user_management', + 'documents', ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -160,7 +161,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( #--------- Pagination ------------------ #PAGINATION_DEFAULT_PAGINATION = 10 #--------- Web theme app --------------- -#WEB_THEME = 'default' +#WEB_THEME_THEME = 'default' #-------------- Main ----------------- #MAIN_SIDE_BAR_SEARCH = False #------------ Common -------------- @@ -176,10 +177,11 @@ TEMPLATE_CONTEXT_PROCESSORS = ( #STORAGE_GRIDFS_DATABASE_NAME = u'document_storage' # Filebased #STORAGE_FILESTORAGE_LOCATION = u'document_storage' +#---------- Metadata ----------------- +# METADATA_AVAILABLE_FUNCTIONS = {} +# METADATA_AVAILABLE_MODELS = {} #---------- Documents ------------------ # Definition -#DOCUMENTS_METADATA_AVAILABLE_FUNCTIONS = {} -#DOCUMENTS_METADATA_AVAILABLE_MODELS = {} #DOCUMENTS_INDEXING_AVAILABLE_FUNCTIONS = {} # Upload diff --git a/urls.py b/urls.py index b3520b487f..afc451f855 100644 --- a/urls.py +++ b/urls.py @@ -21,6 +21,7 @@ urlpatterns = patterns('', (r'^comments/', include('document_comments.urls')), (r'^user_management/', include('user_management.urls')), (r'^settings/', include('smart_settings.urls')), + (r'^metadata/', include('metadata.urls')), )