diff --git a/docs/releases/1.1.rst b/docs/releases/1.1.rst index ba5266406c..28009a0c9f 100644 --- a/docs/releases/1.1.rst +++ b/docs/releases/1.1.rst @@ -60,6 +60,7 @@ Migrate existing database schema with:: $ mayan-edms.py migrate dynamic_search 0001 --fake $ mayan-edms.py migrate history 0001 --fake + $ mayan-edms.py migrate linking 0001 --fake $ mayan-edms.py migrate lock_manager 0001 --fake $ mayan-edms.py migrate ocr 0001 --fake $ mayan-edms.py migrate tags 0001 --fake diff --git a/mayan/apps/linking/__init__.py b/mayan/apps/linking/__init__.py index 3d1c8d843a..41d9cc7c29 100644 --- a/mayan/apps/linking/__init__.py +++ b/mayan/apps/linking/__init__.py @@ -9,16 +9,16 @@ from project_setup.api import register_setup from .links import (smart_link_acl_list, smart_link_create, smart_link_condition_create, smart_link_condition_delete, smart_link_condition_edit, smart_link_condition_list, - smart_link_delete, smart_link_edit, - smart_link_instances_for_document, smart_link_list, - smart_link_setup) + smart_link_delete, smart_link_document_types, + smart_link_edit, smart_link_instances_for_document, + smart_link_list, smart_link_setup) from .models import SmartLink, SmartLinkCondition from .permissions import (PERMISSION_SMART_LINK_DELETE, PERMISSION_SMART_LINK_EDIT, PERMISSION_SMART_LINK_VIEW) register_links(Document, [smart_link_instances_for_document], menu_name='form_header') -register_links(SmartLink, [smart_link_edit, smart_link_condition_list, smart_link_acl_list, smart_link_delete]) +register_links(SmartLink, [smart_link_edit, smart_link_document_types, smart_link_condition_list, smart_link_acl_list, smart_link_delete]) register_links([SmartLink, 'linking:smart_link_list', 'linking:smart_link_create'], [smart_link_list, smart_link_create], menu_name='secondary_menu') register_links(SmartLinkCondition, [smart_link_condition_edit, smart_link_condition_delete]) register_links(['linking:smart_link_condition_list', 'linking:smart_link_condition_create', 'linking:smart_link_condition_edit', 'linking:smart_link_condition_delete'], [smart_link_condition_create], menu_name='sidebar') diff --git a/mayan/apps/linking/links.py b/mayan/apps/linking/links.py index 28e82854cc..a0b7bacb3f 100644 --- a/mayan/apps/linking/links.py +++ b/mayan/apps/linking/links.py @@ -17,6 +17,7 @@ smart_link_list = {'text': _(u'Smart links'), 'view': 'linking:smart_link_list', smart_link_create = {'text': _(u'Create new smart link'), 'view': 'linking:smart_link_create', 'famfam': 'link_add', 'permissions': [PERMISSION_SMART_LINK_CREATE]} smart_link_edit = {'text': _(u'Edit'), 'view': 'linking:smart_link_edit', 'args': 'object.pk', 'famfam': 'link_edit', 'permissions': [PERMISSION_SMART_LINK_EDIT]} smart_link_delete = {'text': _(u'Delete'), 'view': 'linking:smart_link_delete', 'args': 'object.pk', 'famfam': 'link_delete', 'permissions': [PERMISSION_SMART_LINK_DELETE]} +smart_link_document_types = {'text': _('Document types'), 'view': 'linking:smart_link_document_types', 'args': 'object.pk', 'famfam': 'layout', 'permissions': [PERMISSION_SMART_LINK_EDIT]} smart_link_condition_list = {'text': _(u'Conditions'), 'view': 'linking:smart_link_condition_list', 'args': 'object.pk', 'famfam': 'cog', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_CREATE]} smart_link_condition_create = {'text': _(u'Create condition'), 'view': 'linking:smart_link_condition_create', 'args': 'object.pk', 'famfam': 'cog_add', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]} diff --git a/mayan/apps/linking/managers.py b/mayan/apps/linking/managers.py index 940c34fc80..88d7eb17ac 100644 --- a/mayan/apps/linking/managers.py +++ b/mayan/apps/linking/managers.py @@ -20,10 +20,12 @@ class SmartLinkManager(models.Manager): eval_dict['document'] = document eval_dict['metadata'] = MetadataClass(metadata_dict) + smart_link_qs = self.model.objects.filter(enabled=True) + if smart_link_obj: - smart_link_qs = self.model.objects.filter(Q(enabled=True) & Q(pk=smart_link_obj.pk)) - else: - smart_link_qs = self.model.objects.filter(enabled=True) + smart_link_qs= smart_link_qs.filter(pk=smart_link_obj.pk) + + smart_link_qs = smart_link_qs.filter(document_types=document.document_type) for smart_link in smart_link_qs: total_query = Q() diff --git a/mayan/apps/linking/models.py b/mayan/apps/linking/models.py index d8dab1f8a9..0b2f3bc5fb 100644 --- a/mayan/apps/linking/models.py +++ b/mayan/apps/linking/models.py @@ -3,6 +3,8 @@ from __future__ import absolute_import from django.db import models from django.utils.translation import ugettext_lazy as _ +from documents.models import DocumentType + from .literals import INCLUSION_AND, INCLUSION_CHOICES, OPERATOR_CHOICES from .managers import SmartLinkManager @@ -11,9 +13,13 @@ class SmartLink(models.Model): title = models.CharField(max_length=96, verbose_name=_(u'Title')) dynamic_title = models.CharField(blank=True, max_length=96, verbose_name=_(u'Dynamic title'), 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`.')) enabled = models.BooleanField(default=True, verbose_name=_(u'Enabled')) + document_types = models.ManyToManyField(DocumentType, verbose_name=_(u'Document types')) objects = SmartLinkManager() + def get_document_types_not_selected(self): + return DocumentType.objects.exclude(pk__in=self.document_types.all()) + def __unicode__(self): return self.title @@ -25,9 +31,9 @@ class SmartLink(models.Model): class SmartLinkCondition(models.Model): smart_link = models.ForeignKey(SmartLink, verbose_name=_(u'Smart link')) inclusion = models.CharField(default=INCLUSION_AND, max_length=16, choices=INCLUSION_CHOICES, help_text=_(u'The inclusion is ignored for the first item.')) - foreign_document_data = models.CharField(max_length=32, verbose_name=_(u'Foreign document data'), help_text=_(u'This represents the metadata of all other documents. Available objects: `document.` and `metadata.`.')) + foreign_document_data = models.CharField(max_length=32, verbose_name=_(u'Foreign document attribute'), help_text=_(u'This represents the metadata of all other documents.')) operator = models.CharField(max_length=16, choices=OPERATOR_CHOICES) - 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`.')) + expression = models.TextField(verbose_name=_(u'Expression'), help_text=_(u'This expression will be evaluated against the current selected 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')) diff --git a/mayan/apps/linking/south_migrations/0001_initial.py b/mayan/apps/linking/south_migrations/0001_initial.py new file mode 100644 index 0000000000..eb981ad3f1 --- /dev/null +++ b/mayan/apps/linking/south_migrations/0001_initial.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'SmartLink' + db.create_table(u'linking_smartlink', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=96)), + ('dynamic_title', self.gf('django.db.models.fields.CharField')(max_length=96, blank=True)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal(u'linking', ['SmartLink']) + + # Adding model 'SmartLinkCondition' + db.create_table(u'linking_smartlinkcondition', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('smart_link', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['linking.SmartLink'])), + ('inclusion', self.gf('django.db.models.fields.CharField')(default=u'&', max_length=16)), + ('foreign_document_data', self.gf('django.db.models.fields.CharField')(max_length=32)), + ('operator', self.gf('django.db.models.fields.CharField')(max_length=16)), + ('expression', self.gf('django.db.models.fields.TextField')()), + ('negated', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal(u'linking', ['SmartLinkCondition']) + + + def backwards(self, orm): + # Deleting model 'SmartLink' + db.delete_table(u'linking_smartlink') + + # Deleting model 'SmartLinkCondition' + db.delete_table(u'linking_smartlinkcondition') + + + models = { + u'linking.smartlink': { + 'Meta': {'object_name': 'SmartLink'}, + 'dynamic_title': ('django.db.models.fields.CharField', [], {'max_length': '96', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '96'}) + }, + u'linking.smartlinkcondition': { + 'Meta': {'object_name': 'SmartLinkCondition'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'expression': ('django.db.models.fields.TextField', [], {}), + 'foreign_document_data': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'inclusion': ('django.db.models.fields.CharField', [], {'default': "u'&'", 'max_length': '16'}), + 'negated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'operator': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'smart_link': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['linking.SmartLink']"}) + } + } + + complete_apps = ['linking'] \ No newline at end of file diff --git a/mayan/apps/linking/south_migrations/0002_auto.py b/mayan/apps/linking/south_migrations/0002_auto.py new file mode 100644 index 0000000000..13d03540e1 --- /dev/null +++ b/mayan/apps/linking/south_migrations/0002_auto.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding M2M table for field document_types on 'SmartLink' + m2m_table_name = db.shorten_name(u'linking_smartlink_document_types') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('smartlink', models.ForeignKey(orm[u'linking.smartlink'], null=False)), + ('documenttype', models.ForeignKey(orm[u'documents.documenttype'], null=False)) + )) + db.create_unique(m2m_table_name, ['smartlink_id', 'documenttype_id']) + + + def backwards(self, orm): + # Removing M2M table for field document_types on 'SmartLink' + db.delete_table(db.shorten_name(u'linking_smartlink_document_types')) + + + models = { + u'documents.documenttype': { + 'Meta': {'ordering': "['name']", 'object_name': 'DocumentType'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'ocr': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'linking.smartlink': { + 'Meta': {'object_name': 'SmartLink'}, + 'document_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['documents.DocumentType']", 'symmetrical': 'False'}), + 'dynamic_title': ('django.db.models.fields.CharField', [], {'max_length': '96', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '96'}) + }, + u'linking.smartlinkcondition': { + 'Meta': {'object_name': 'SmartLinkCondition'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'expression': ('django.db.models.fields.TextField', [], {}), + 'foreign_document_data': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'inclusion': ('django.db.models.fields.CharField', [], {'default': "u'&'", 'max_length': '16'}), + 'negated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'operator': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'smart_link': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['linking.SmartLink']"}) + } + } + + complete_apps = ['linking'] \ No newline at end of file diff --git a/mayan/apps/linking/south_migrations/__init__.py b/mayan/apps/linking/south_migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mayan/apps/linking/urls.py b/mayan/apps/linking/urls.py index d1b367deb0..92aa3310de 100644 --- a/mayan/apps/linking/urls.py +++ b/mayan/apps/linking/urls.py @@ -9,6 +9,7 @@ urlpatterns = patterns('linking.views', url(r'^setup/create/$', 'smart_link_create', (), 'smart_link_create'), url(r'^setup/(?P\d+)/delete/$', 'smart_link_delete', (), 'smart_link_delete'), url(r'^setup/(?P\d+)/edit/$', 'smart_link_edit', (), 'smart_link_edit'), + url(r'^setup/(?P\d+)/document_types/$', 'smart_link_document_types', (), 'smart_link_document_types'), url(r'^setup/(?P\d+)/condition/list/$', 'smart_link_condition_list', (), 'smart_link_condition_list'), url(r'^setup/(?P\d+)/condition/create/$', 'smart_link_condition_create', (), 'smart_link_condition_create'), diff --git a/mayan/apps/linking/views.py b/mayan/apps/linking/views.py index fcb511ad5e..a9141514e0 100644 --- a/mayan/apps/linking/views.py +++ b/mayan/apps/linking/views.py @@ -13,7 +13,8 @@ from django.utils.translation import ugettext_lazy as _ from acls.models import AccessEntry from acls.utils import apply_default_acls from acls.views import acl_list_for -from common.utils import encapsulate +from common.utils import encapsulate, generate_choices_w_labels +from common.views import assign_remove from common.widgets import two_state_template from documents.models import Document from documents.permissions import PERMISSION_DOCUMENT_VIEW @@ -52,7 +53,7 @@ def smart_link_instance_view(request, document_id, smart_link_pk): except PermissionDenied: AccessEntry.objects.check_access(PERMISSION_SMART_LINK_VIEW, request.user, smart_link) - object_list, errors = SmartLink.objects.get_smart_link_instances_for(document, smart_link) + object_list, errors = SmartLink.objects.get_for(document, smart_link) return document_list( request, @@ -69,7 +70,7 @@ def smart_link_instance_view(request, document_id, smart_link_pk): def smart_link_instances_for_document(request, document_id): subtemplates_list = [] document = get_object_or_404(Document, pk=document_id) - smart_link_instances, errors = SmartLink.objects.get_smart_link_instances_for(document) + smart_link_instances, errors = SmartLink.objects.get_for(document) if (request.user.is_staff or request.user.is_superuser) and errors: for error in errors: messages.warning(request, _(u'Smart link query error: %s' % error)) @@ -215,6 +216,30 @@ def smart_link_delete(request, smart_link_pk): }, context_instance=RequestContext(request)) +def smart_link_document_types(request, smart_link_pk): + smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_SMART_LINK_EDIT, request.user, smart_link) + + return assign_remove( + request, + left_list=lambda: generate_choices_w_labels(smart_link.get_document_types_not_selected(), display_object_type=False), + right_list=lambda: generate_choices_w_labels(smart_link.document_types.all(), display_object_type=False), + add_method=lambda x: smart_link.document_types.add(x), + remove_method=lambda x: smart_link.document_types.remove(x), + #left_list_title=_(u'Document types not in index: %s') % smart_link, + #right_list_title=_(u'Document types for index: %s') % smart_link, + decode_content_type=True, + extra_context={ + 'main_title': _('Document type for which to enable smart link: %s') % smart_link, + 'object': smart_link, + } + ) + + def smart_link_condition_list(request, smart_link_pk): smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) @@ -244,7 +269,7 @@ def smart_link_condition_create(request, smart_link_pk): AccessEntry.objects.check_accesses([PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT], request.user, smart_link) if request.method == 'POST': - form = SmartLinkConditionForm(request.POST) + form = SmartLinkConditionForm(data=request.POST, smart_link=smart_link) if form.is_valid(): new_smart_link_condition = form.save(commit=False) new_smart_link_condition.smart_link = smart_link @@ -252,7 +277,7 @@ def smart_link_condition_create(request, smart_link_pk): messages.success(request, _(u'Smart link condition: "%s" created successfully.') % new_smart_link_condition) return HttpResponseRedirect(reverse('linking:smart_link_condition_list', args=[smart_link.pk])) else: - form = SmartLinkConditionForm(initial={'smart_link': smart_link}) + form = SmartLinkConditionForm(smart_link=smart_link) return render_to_response('main/generic_form.html', { 'form': form, @@ -273,15 +298,13 @@ def smart_link_condition_edit(request, smart_link_condition_pk): previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse('main:home')))) if request.method == 'POST': - form = SmartLinkConditionForm(request.POST, instance=smart_link_condition) + form = SmartLinkConditionForm(request.POST, smart_link=smart_link_condition.smart_link, instance=smart_link_condition) if form.is_valid(): - new_smart_link_condition = form.save(commit=False) - new_smart_link_condition.smart_link = smart_link_condition.smart_link - new_smart_link_condition.save() - messages.success(request, _(u'Smart link condition: "%s" edited successfully.') % new_smart_link_condition) + smart_link_condition = form.save() + messages.success(request, _(u'Smart link condition: "%s" edited successfully.') % smart_link_condition) return HttpResponseRedirect(next) else: - form = SmartLinkConditionForm(instance=smart_link_condition) + form = SmartLinkConditionForm(smart_link=smart_link_condition.smart_link, instance=smart_link_condition) return render_to_response('main/generic_form.html', { 'form': form,