Improve SourceColumn workflow by allowing explicit functions as columns. Remove usage of encapsulate. Move instances of 'extra_columns' to SourceColumn.

This commit is contained in:
Roberto Rosario
2015-08-16 14:26:41 -04:00
parent 938328bc14
commit 60bc327756
36 changed files with 366 additions and 389 deletions

View File

@@ -10,7 +10,6 @@ from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from common.utils import encapsulate
from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectListView

View File

@@ -87,7 +87,7 @@
{% endif %}
{% if not hide_columns %}
{% for column in object|get_source_columns %}
<td>{{ object|object_property:column.attribute }}</td>
<td>{% source_column_resolve column=column %}</td>
{% endfor %}
{% endif %}
{% for column in extra_columns %}

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_object, menu_sidebar
from common.utils import encapsulate
from navigation import SourceColumn
from .links import (
@@ -23,9 +22,7 @@ class ConverterApp(MayanAppConfig):
SourceColumn(source=Transformation, label=_('Order'), attribute='order')
SourceColumn(
source=Transformation, label=_('Transformation'),
attribute=encapsulate(
lambda transformation: unicode(transformation)
)
func=lambda context: unicode(context['object'])
)
SourceColumn(
source=Transformation, label=_('Arguments'), attribute='arguments'

View File

@@ -5,7 +5,6 @@ from datetime import datetime
from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_object, menu_setup, menu_sidebar
from common.utils import encapsulate
from navigation import SourceColumn
from .api import Key, KeyStub
@@ -23,34 +22,31 @@ class DjangoGPGApp(MayanAppConfig):
def ready(self):
super(DjangoGPGApp, self).ready()
SourceColumn(source=Key, label='ID', attribute='key_id')
SourceColumn(source=Key, label=_('ID'), attribute='key_id')
SourceColumn(
source=Key, label='Owner', attribute=encapsulate(
lambda key: ', '.join(key.uids)
)
source=Key, label=_('Owner'),
func=lambda context: ', '.join(context['object'].uids)
)
SourceColumn(
source=KeyStub, label='ID', attribute=encapsulate(
lambda key: '...{0}'.format(key.key_id[-16:])
source=KeyStub, label=_('ID'),
func=lambda context: '...{0}'.format(context['object'].key_id[-16:])
)
)
SourceColumn(source=KeyStub, label='Type', attribute='key_type')
SourceColumn(source=KeyStub, label=_('Type'), attribute='key_type')
SourceColumn(
source=KeyStub, label='Creation date', attribute=encapsulate(
lambda key: datetime.fromtimestamp(int(key.date))
source=KeyStub, label=_('Creation date'),
func=lambda context: datetime.fromtimestamp(
int(context['object'].date)
)
)
SourceColumn(
source=KeyStub, label='Expiration date', attribute=encapsulate(
lambda key: datetime.fromtimestamp(int(key.expires)) if key.expires else _('No expiration')
source=KeyStub, label=_('Expiration date'),
func=lambda context: datetime.fromtimestamp(int(context['object'].expires)) if context['object'].expires else _('No expiration')
)
)
SourceColumn(source=KeyStub, label='Length', attribute='length')
SourceColumn(source=KeyStub, label=_('Length'), attribute='length')
SourceColumn(
source=KeyStub, label='Identities', attribute=encapsulate(
lambda key: ', '.join(key.uids)
)
source=KeyStub, label=_('Identities'),
func=lambda context: ', '.join(context['object'].uids)
)
menu_object.bind_links(links=(link_key_delete,), sources=(Key,))

View File

@@ -12,7 +12,6 @@ from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from common.generics import SimpleView, SingleObjectListView
from common.utils import encapsulate
from permissions import Permission
from .api import Key

View File

@@ -4,7 +4,6 @@ from django.utils.translation import ugettext_lazy as _
from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_object, menu_sidebar
from common.utils import encapsulate
from documents.models import Document
from navigation import SourceColumn
@@ -37,9 +36,7 @@ class DocumentCommentsApp(MayanAppConfig):
SourceColumn(source=Comment, label=_('Date'), attribute='submit_date')
SourceColumn(
source=Comment, label=_('User'),
attribute=encapsulate(
lambda x: x.user.get_full_name() if x.user.get_full_name() else x.user
)
func=lambda context: context['object'].user.get_full_name() if context['object'].user.get_full_name() else context['object'].user
)
SourceColumn(source=Comment, label=_('Comment'), attribute='comment')

View File

@@ -9,10 +9,12 @@ from common import (
MayanAppConfig, menu_facet, menu_main, menu_object, menu_secondary,
menu_setup, menu_tools
)
from common.widgets import two_state_template
from documents.models import Document
from documents.signals import post_document_created
from mayan.celery import app
from metadata.models import DocumentMetadata
from navigation import SourceColumn
from rest_api.classes import APIEndPoint
from .handlers import (
@@ -27,7 +29,11 @@ from .links import (
link_template_node_create, link_template_node_delete,
link_template_node_edit
)
from .models import Index, IndexTemplateNode
from .models import (
DocumentIndexInstanceNode, Index, IndexInstance, IndexInstanceNode,
IndexTemplateNode
)
from .widgets import get_breadcrumbs, index_instance_item_link, node_level
class DocumentIndexingApp(MayanAppConfig):
@@ -41,6 +47,60 @@ class DocumentIndexingApp(MayanAppConfig):
APIEndPoint(app=self, version_string='1')
SourceColumn(source=Index, label=_('Label'), attribute='label')
SourceColumn(source=Index, label=_('Slug'), attribute='slug')
SourceColumn(
source=Index, label=_('Enabled'),
func=lambda context: two_state_template(context['object'].enabled)
)
SourceColumn(
source=IndexInstance, label=_('Items'),
func=lambda context: context['object'].get_items_count(
user=context['request'].user
)
)
SourceColumn(
source=IndexInstance, label=_('Document types'), attribute='get_document_types_names'
)
SourceColumn(
source=IndexTemplateNode, label=_('Level'),
func=lambda context: node_level(context['object'])
)
SourceColumn(
source=IndexTemplateNode, label=_('Enabled'),
func=lambda context: two_state_template(context['object'].enabled)
)
SourceColumn(
source=IndexTemplateNode, label=_('Has document links?'),
func=lambda context: two_state_template(context['object'].link_documents)
)
SourceColumn(
source=IndexInstanceNode, label=_('Node'),
func=lambda context: index_instance_item_link(context['object'])
)
SourceColumn(
source=IndexInstanceNode, label=_('Items'),
func=lambda context: context['object'].get_item_count(
user=context['request'].user
)
)
SourceColumn(
source=DocumentIndexInstanceNode, label=_('Node'),
func=lambda context: get_breadcrumbs(
index_instance_node=context['object'], single_link=True,
)
)
SourceColumn(
source=DocumentIndexInstanceNode, label=_('Items'),
func=lambda context: context['object'].get_item_count(
user=context['request'].user
)
)
app.conf.CELERY_QUEUES.append(
Queue('indexing', Exchange('indexing'), routing_key='indexing'),
)

View File

@@ -1,5 +1,6 @@
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@@ -8,7 +9,10 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
from acls.models import AccessControlList
from documents.models import Document, DocumentType
from documents.permissions import permission_document_view
from permissions import Permission
from .managers import IndexManager, IndexInstanceNodeManager
@@ -58,7 +62,10 @@ class Index(models.Model):
return '#'
def save(self, *args, **kwargs):
"""Automatically create the root index template node"""
"""
Automatically create the root index template node
"""
super(Index, self).save(*args, **kwargs)
IndexTemplateNode.objects.get_or_create(parent=None, index=self)
@@ -67,15 +74,28 @@ class Index(models.Model):
[unicode(document_type) for document_type in self.document_types.all()] or ['None']
)
class Meta:
verbose_name = _('Index')
verbose_name_plural = _('Indexes')
class IndexInstance(Index):
def get_instance_node_count(self):
try:
return self.instance_root.get_descendant_count()
except IndexInstanceNode.DoesNotExist:
return 0
def get_items_count(self, user):
try:
return self.instance_root.get_item_count(user=user)
except IndexInstanceNode.DoesNotExist:
return 0
class Meta:
verbose_name = _('Index')
verbose_name_plural = _('Indexes')
proxy = True
verbose_name = _('Index instance')
verbose_name_plural = _('Index instances')
@python_2_unicode_compatible
@@ -140,9 +160,6 @@ class IndexInstanceNode(MPTTModel):
def __str__(self):
return self.value
def index(self):
return self.index_template_node.index
def get_absolute_url(self):
return reverse('indexing:index_instance_node_view', args=[self.pk])
@@ -151,6 +168,38 @@ class IndexInstanceNode(MPTTModel):
# Convenience method for serializer
return self.get_children()
def index(self):
return self.index_template_node.index
def get_item_count(self, user):
if self.index_template_node.link_documents:
queryset = self.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()
else:
return self.get_children().count()
class Meta:
verbose_name = _('Index node instance')
verbose_name_plural = _('Indexes node instances')
class DocumentIndexInstanceNodeManager(models.Manager):
def get_for(self, document):
return self.filter(documents=document)
class DocumentIndexInstanceNode(IndexInstanceNode):
objects = DocumentIndexInstanceNodeManager()
class Meta:
proxy = True
verbose_name = _('Document index node instance')
verbose_name_plural = _('Document indexes node instances')

View File

@@ -11,7 +11,6 @@ from django.utils.html import mark_safe
from django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectEditView, SingleObjectListView
@@ -23,7 +22,10 @@ from documents.views import DocumentListView
from permissions import Permission
from .forms import IndexTemplateNodeForm
from .models import Index, IndexInstanceNode, IndexTemplateNode
from .models import (
DocumentIndexInstanceNode, Index, IndexInstance, IndexInstanceNode,
IndexTemplateNode
)
from .permissions import (
permission_document_indexing_create, permission_document_indexing_delete,
permission_document_indexing_edit,
@@ -49,17 +51,8 @@ class SetupIndexListView(SingleObjectListView):
def get_extra_context(self):
return {
'title': _('Indexes'),
'hide_object': True,
'extra_columns': [
{'name': _('Label'), 'attribute': 'label'},
{'name': _('Slug'), 'attribute': 'slug'},
{
'name': _('Enabled'), 'attribute': encapsulate(
lambda x: two_state_template(x.enabled)
)
},
]
'title': _('Indexes'),
}
@@ -71,8 +64,8 @@ class SetupIndexEditView(SingleObjectEditView):
def get_extra_context(self):
return {
'title': _('Edit index: %s') % self.get_object(),
'object': self.get_object(),
'title': _('Edit index: %s') % self.get_object(),
}
@@ -83,8 +76,8 @@ class SetupIndexDeleteView(SingleObjectDeleteView):
def get_extra_context(self):
return {
'title': _('Delete the index: %s?') % self.get_object(),
'object': self.get_object(),
'title': _('Delete the index: %s?') % self.get_object(),
}
@@ -99,23 +92,6 @@ class SetupIndexTreeTemplateListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Level'), 'attribute': encapsulate(
lambda node: node_level(node)
)
},
{
'name': _('Enabled'), 'attribute': encapsulate(
lambda node: two_state_template(node.enabled)
)
},
{
'name': _('Has document links?'), 'attribute': encapsulate(
lambda node: two_state_template(node.link_documents)
)
},
),
'hide_object': True,
'index': self.get_index(),
'navigation_object_list': ('index',),
@@ -276,54 +252,17 @@ def template_node_delete(request, node_pk):
class IndexListView(SingleObjectListView):
@staticmethod
def get_items_count(instance):
try:
if instance.template_root.link_documents:
return instance.instance_root.documents.count()
else:
return instance.instance_root.get_children().count()
except IndexInstanceNode.DoesNotExist:
return 0
queryset = Index.objects.filter(enabled=True)
object_permission = permission_document_indexing_view
queryset = IndexInstance.objects.filter(enabled=True)
def get_extra_context(self):
return {
'title': _('Indexes'),
'hide_links': True,
'extra_columns': [
{
'name': _('Items'), 'attribute': encapsulate(
lambda instance: IndexListView.get_items_count(instance)
)
},
{
'name': _('Document types'),
'attribute': 'get_document_types_names'
},
],
'title': _('Indexes'),
}
class IndexInstanceNodeView(DocumentListView, SingleObjectListView):
@staticmethod
def get_item_count(instance, user):
if instance.index_template_node.link_documents:
queryset = instance.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()
else:
return instance.get_children().count()
def dispatch(self, request, *args, **kwargs):
self.index_instance = get_object_or_404(
IndexInstanceNode, pk=self.kwargs['pk']
@@ -371,25 +310,7 @@ class IndexInstanceNodeView(DocumentListView, SingleObjectListView):
}
if self.index_instance and not self.index_instance.index_template_node.link_documents:
context.update(
{
'extra_columns': [
{
'name': _('Node'),
'attribute': encapsulate(
lambda x: index_instance_item_link(x)
)
},
{
'name': _('Items'),
'attribute': encapsulate(
lambda instance: IndexInstanceNodeView.get_item_count(instance=instance, user=self.request.user)
)
}
],
'hide_object': True,
}
)
context.update({'hide_object': True})
return context
@@ -446,16 +367,6 @@ class DocumentIndexNodeListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Node'),
'attribute': encapsulate(
lambda node: get_breadcrumbs(
index_instance_node=node, single_link=True, include_count=True
)
)
},
),
'hide_object': True,
'object': self.get_document(),
'title': _(
@@ -464,4 +375,4 @@ class DocumentIndexNodeListView(SingleObjectListView):
}
def get_queryset(self):
return self.get_document().node_instances.all()
return DocumentIndexInstanceNode.objects.get_for(self.get_document())

View File

@@ -7,22 +7,6 @@ from django.utils.translation import ugettext
from .models import IndexInstanceNode
def index_instance_item_link(index_instance_item):
if isinstance(index_instance_item, IndexInstanceNode):
if index_instance_item.index_template_node.link_documents:
icon_template = '<i class="fa fa-folder"></i>'
else:
icon_template = '<i class="fa fa-level-up fa-rotate-90"></i>'
else:
icon_template = ''
return mark_safe('%(icon_template)s&nbsp;<a href="%(url)s">%(text)s</a>' % {
'url': index_instance_item.get_absolute_url(),
'icon_template': icon_template,
'text': index_instance_item
})
def get_instance_link(index_instance_node, text=None, simple=False):
"""
Return an HTML anchor to an index instance
@@ -75,6 +59,22 @@ def get_breadcrumbs(index_instance_node, simple=False, single_link=False, includ
return mark_safe(' '.join(output))
def index_instance_item_link(index_instance_item):
if isinstance(index_instance_item, IndexInstanceNode):
if index_instance_item.index_template_node.link_documents:
icon_template = '<i class="fa fa-folder"></i>'
else:
icon_template = '<i class="fa fa-level-up fa-rotate-90"></i>'
else:
icon_template = ''
return mark_safe('%(icon_template)s&nbsp;<a href="%(url)s">%(text)s</a>' % {
'url': index_instance_item.get_absolute_url(),
'icon_template': icon_template,
'text': index_instance_item
})
def node_level(node):
"""
Render an indented tree like output for a specific node

View File

@@ -7,7 +7,6 @@ from common import (
MayanAppConfig, menu_facet, menu_object, menu_secondary, menu_setup,
menu_sidebar
)
from common.utils import encapsulate
from common.widgets import two_state_template
from documents.models import Document
from navigation import SourceColumn
@@ -39,9 +38,7 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn(
source=Workflow, label=_('Initial state'),
attribute=encapsulate(
lambda workflow: workflow.get_initial_state() or _('None')
)
func=lambda context: context['object'].get_initial_state() or _('None')
)
SourceColumn(
@@ -50,10 +47,8 @@ class DocumentStatesApp(MayanAppConfig):
)
SourceColumn(
source=WorkflowInstance, label=_('User'),
attribute=encapsulate(
lambda workflow: getattr(
workflow.get_last_log_entry(), 'user', _('None')
)
func=lambda context: getattr(
context['object'].get_last_log_entry(), 'user', _('None')
)
)
SourceColumn(
@@ -62,16 +57,14 @@ class DocumentStatesApp(MayanAppConfig):
)
SourceColumn(
source=WorkflowInstance, label=_('Date and time'),
attribute=encapsulate(
lambda workflow: getattr(
workflow.get_last_log_entry(), 'datetime', _('None')
)
func=lambda context: getattr(
context['object'].get_last_log_entry(), 'datetime', _('None')
)
)
SourceColumn(
source=WorkflowInstance, label=_('Completion'),
attribute=encapsulate(lambda workflow: getattr(
workflow.get_current_state(), 'completion', _('None'))
func=lambda context: getattr(
context['object'].get_current_state(), 'completion', _('None')
)
)
@@ -93,9 +86,7 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn(
source=WorkflowState, label=_('Is initial state?'),
attribute=encapsulate(
lambda state: two_state_template(state.initial)
)
func=lambda context: two_state_template(context['object'].initial)
)
SourceColumn(
source=WorkflowState, label=_('Completion'), attribute='completion'

View File

@@ -17,7 +17,7 @@ from common import (
)
from common.classes import ModelAttribute
from common.signals import post_initial_setup
from common.utils import encapsulate
from common.widgets import two_state_template
from converter.links import link_transformation_list
from converter.permissions import (
permission_transformation_create,
@@ -125,17 +125,27 @@ class DocumentsApp(MayanAppConfig):
SourceColumn(
source=Document, label=_('Thumbnail'),
attribute=encapsulate(
lambda document: document_thumbnail(
document, gallery_name='documents:document_list',
func=lambda context: document_thumbnail(
context['object'], gallery_name='documents:document_list',
size=setting_thumbnail_size.value,
title=getattr(document, 'label', None),
)
title=getattr(context['object'], 'label', None),
)
)
SourceColumn(
source=Document, label=_('Type'), attribute='document_type'
)
# TODO: make permission aware
SourceColumn(
source=DocumentType, label=_('Documents'),
func=lambda context: context['object'].documents.count()
)
SourceColumn(
source=DocumentTypeFilename, label=_('Enabled'),
func=lambda context: two_state_template(context['object'].enabled)
)
SourceColumn(
source=DeletedDocument, label=_('Type'), attribute='document_type'
)
@@ -144,6 +154,23 @@ class DocumentsApp(MayanAppConfig):
attribute='deleted_date_time'
)
SourceColumn(
source=DocumentVersion, label=_('Time and date'),
attribute='timestamp'
)
SourceColumn(
source=DocumentVersion, label=_('MIME type'),
attribute='mimetype'
)
SourceColumn(
source=DocumentVersion, label=_('Encoding'),
attribute='encoding'
)
SourceColumn(
source=DocumentVersion, label=_('Comment'),
attribute='comment'
)
app.conf.CELERYBEAT_SCHEDULE.update(
{
'task_check_delete_periods': {

View File

@@ -954,9 +954,6 @@ class DocumentTypeListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': [
{'name': _('Documents'), 'attribute': encapsulate(lambda document_type: document_type.documents.count())}
],
'hide_link': True,
'title': _('Document types'),
}
@@ -1010,12 +1007,6 @@ class DocumentTypeFilenameListView(SingleObjectListView):
def get_extra_context(self):
return {
'document_type': self.get_document_type(),
'extra_columns': (
{
'name': _('Enabled'),
'attribute': encapsulate(lambda filename: two_state_template(filename.enabled)),
},
),
'hide_link': True,
'navigation_object_list': ('document_type',),
'title': _('Filenames for document type: %s') % self.get_document_type(),
@@ -1148,12 +1139,7 @@ class DocumentVersionListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{'name': _('Time and date'), 'attribute': 'timestamp'},
{'name': _('MIME type'), 'attribute': 'mimetype'},
{'name': _('Encoding'), 'attribute': 'encoding'},
{'name': _('Comment'), 'attribute': 'comment'},
), 'hide_object': True, 'object': self.get_document(),
'hide_object': True, 'object': self.get_document(),
'title': _('Versions of document: %s') % self.get_document(),
}

View File

@@ -5,7 +5,6 @@ from django.utils.translation import ugettext_lazy as _
from actstream.models import Action
from common import MayanAppConfig, menu_tools
from common.utils import encapsulate
from navigation import SourceColumn
from .links import link_events_list
@@ -21,8 +20,8 @@ class EventsApp(MayanAppConfig):
SourceColumn(source=Action, label=_('Timestamp'), attribute='timestamp')
SourceColumn(source=Action, label=_('Actor'), attribute='actor')
SourceColumn(source=Action, label=_('Verb'), attribute=encapsulate(
lambda entry: event_type_link(entry))
SourceColumn(source=Action, label=_('Verb'),
func=lambda context: event_type_link(context['object'])
)
menu_tools.bind_links(links=[link_events_list])

View File

@@ -26,14 +26,14 @@ class EventListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': [
'extra_columns': (
{
'name': _('Target'),
'attribute': encapsulate(
lambda entry: event_object_link(entry)
)
}
],
},
),
'hide_object': True,
'title': _('Events'),
}
@@ -85,14 +85,14 @@ class VerbEventListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': [
'extra_columns': (
{
'name': _('Target'),
'attribute': encapsulate(
lambda entry: event_object_link(entry)
)
}
],
},
),
'hide_object': True,
'title': _(
'Events of type: %s'

View File

@@ -56,6 +56,12 @@ class FoldersApp(MayanAppConfig):
source=Folder, label=_('Created'), attribute='datetime_created'
)
SourceColumn(source=Folder, label=_('User'), attribute='user')
SourceColumn(
source=Folder, label=_('Documents'),
func=lambda context: context['object'].get_document_count(
user=context['request'].user
)
)
menu_facet.bind_links(
links=(link_document_folder_list,), sources=(Document,)

View File

@@ -1,12 +1,16 @@
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList
from documents.models import Document
from documents.permissions import permission_document_view
from permissions import Permission
@python_2_unicode_compatible
@@ -28,6 +32,18 @@ class Folder(models.Model):
def get_absolute_url(self):
return reverse('folders:folder_view', args=[self.pk])
def get_document_count(self, user):
queryset = self.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()
class Meta:
ordering = ('label',)
unique_together = ('label', 'user')

View File

@@ -12,7 +12,6 @@ from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import (
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
)
@@ -48,31 +47,8 @@ class FolderEditView(SingleObjectEditView):
class FolderListView(SingleObjectListView):
object_permission = permission_folder_view
@staticmethod
def get_document_count(instance, user):
queryset = instance.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()
def get_extra_context(self):
return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: FolderListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True,
'title': _('Folders'),
}
@@ -253,16 +229,6 @@ class DocumentFolderListView(FolderListView):
def get_extra_context(self):
return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: FolderListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True,
'object': self.document,
'title': _('Folders containing document: %s') % self.document,

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_tools, menu_object, menu_secondary
from common.utils import encapsulate
from navigation import SourceColumn
from .classes import Property, PropertyNamespace, PIPNotFound, VirtualEnv
@@ -24,7 +23,7 @@ class InstallationApp(MayanAppConfig):
)
SourceColumn(
source=PropertyNamespace, label=_('Items'),
attribute=encapsulate(lambda entry: len(entry.get_properties()))
func=lambda context: len(context['object'].get_properties())
)
SourceColumn(source=Property, label=_('Label'), attribute='label')

View File

@@ -9,7 +9,9 @@ from common import (
MayanAppConfig, menu_facet, menu_object, menu_secondary, menu_setup,
menu_sidebar
)
from common.widgets import two_state_template
from documents.models import Document
from navigation import SourceColumn
from .links import (
link_smart_link_create, link_smart_link_condition_create,
@@ -41,6 +43,26 @@ class LinkingApp(MayanAppConfig):
)
)
SourceColumn(
source=ResolvedSmartLink, label=_('Label'),
func=lambda context: context['object'].get_dynamic_label(
context['resolved_object']
)
)
SourceColumn(
source=SmartLink, label=_('Dynamic label'), attribute='dynamic_label'
)
SourceColumn(
source=SmartLink, label=_('Enabled'),
func=lambda context: two_state_template(context['object'].enabled)
)
SourceColumn(
source=SmartLinkCondition, label=_('Enabled'),
func=lambda context: two_state_template(context['object'].enabled)
)
menu_facet.bind_links(
links=(link_smart_link_instances_for_document,),
sources=(Document,)

View File

@@ -12,7 +12,6 @@ from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList
from common.utils import encapsulate
from common.generics import (
AssignRemoveView, SingleObjectCreateView, SingleObjectEditView,
SingleObjectListView
@@ -145,14 +144,6 @@ class SmartLinkListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': [
{'name': _('Dynamic label'), 'attribute': 'dynamic_label'},
{
'name': _('Enabled'), 'attribute': encapsulate(
lambda instance: two_state_template(instance.enabled)
)
},
],
'hide_link': True,
'title': _('Smart links'),
}
@@ -183,15 +174,6 @@ class DocumentSmartLinkListView(SmartLinkListView):
def get_extra_context(self):
return {
'document': self.document,
'extra_columns': (
{
'name': _('Label'), 'attribute': encapsulate(
lambda smart_link: smart_link.get_dynamic_label(
self.document
)
)
},
),
'hide_object': True,
'hide_link': True,
'object': self.document,
@@ -272,17 +254,9 @@ class SmartLinkConditionListView(SingleObjectListView):
def get_extra_context(self):
return {
'title': _('Conditions for smart link: %s') % self.get_smart_link(),
'extra_columns': (
{
'name': _('Enabled'),
'attribute': encapsulate(
lambda condition: two_state_template(condition.enabled)
)
},
),
'hide_link': True,
'object': self.get_smart_link(),
'title': _('Conditions for smart link: %s') % self.get_smart_link(),
}
def get_smart_link(self):

View File

@@ -28,15 +28,11 @@ class MailerApp(MayanAppConfig):
super(MailerApp, self).ready()
SourceColumn(
source=LogEntry,
label='Date and time',
attribute='datetime'
source=LogEntry, label=_('Date and time'), attribute='datetime'
)
SourceColumn(
source=LogEntry,
label='Message',
attribute='message'
source=LogEntry, label=_('Message'), attribute='message'
)
ModelPermission.register(

View File

@@ -13,7 +13,7 @@ from common import (
menu_setup, menu_sidebar, menu_tools
)
from common.classes import ModelAttribute
from common.utils import encapsulate
from common.widgets import two_state_template
from documents.models import Document, DocumentType
from documents.search import document_search
from documents.signals import post_document_type_change
@@ -38,7 +38,7 @@ from .links import (
link_setup_metadata_type_edit, link_setup_metadata_type_list,
link_documents_missing_required_metadata
)
from .models import DocumentTypeMetadataType, MetadataType
from .models import DocumentMetadata, DocumentTypeMetadataType, MetadataType
from .permissions import (
permission_metadata_document_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view
@@ -94,9 +94,16 @@ class MetadataApp(MayanAppConfig):
SourceColumn(
source=Document, label=_('Metadata'),
attribute=encapsulate(
lambda document: get_metadata_string(document)
func=lambda context: get_metadata_string(context['object'])
)
SourceColumn(
source=DocumentMetadata, label=_('Value'),
attribute='value'
)
SourceColumn(
source=DocumentMetadata, label=_('Required'),
func=lambda context: two_state_template(context['object'].is_required)
)
app.conf.CELERY_QUEUES.append(

View File

@@ -103,6 +103,10 @@ class DocumentMetadata(models.Model):
return super(DocumentMetadata, self).delete(*args, **kwargs)
@property
def is_required(self):
return self.metadata_type in self.document.document_type.metadata.filter(required=True)
class Meta:
unique_together = ('document', 'metadata_type')
verbose_name = _('Document metadata')

View File

@@ -11,7 +11,6 @@ from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList
from common.utils import encapsulate
from common.generics import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectEditView, SingleObjectListView
@@ -495,18 +494,9 @@ class DocumentMetadataListView(SingleObjectListView):
def get_extra_context(self):
document = self.get_document()
return {
'title': _('Metadata for document: %s') % document,
'extra_columns': (
{'name': _('Value'), 'attribute': 'value'},
{
'name': _('Required'),
'attribute': encapsulate(
lambda metadata: metadata.metadata_type in document.document_type.metadata.filter(required=True)
)
}
),
'hide_link': True,
'object': document,
'title': _('Metadata for document: %s') % document,
}
def get_queryset(self):

View File

@@ -281,11 +281,13 @@ class SourceColumn(object):
# unhashable type: list
return ()
def __init__(self, source, label, attribute):
def __init__(self, source, label, attribute=None, func=None):
self.source = source
self.label = label
self.attribute = attribute
self.func = func
self.__class__._registry.setdefault(source, [])
self.__class__._registry[source].append(
{'label': label, 'attribute': attribute}
)
self.__class__._registry[source].append(self)
class CombinedSource(object):

View File

@@ -2,6 +2,8 @@ from __future__ import unicode_literals
from django.template import Library
from common.utils import return_attrib
from ..classes import Menu, SourceColumn
from ..forms import MultiItemForm
@@ -58,3 +60,11 @@ def get_source_columns(source):
pass
return SourceColumn.get_for_source(source)
@register.simple_tag(takes_context=True)
def source_column_resolve(context, column):
if column.attribute:
return return_attrib(context['object'], column.attribute)
elif column.func:
return column.func(context=context)

View File

@@ -13,7 +13,6 @@ from common import (
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_tools
)
from common.utils import encapsulate
from documents.models import Document, DocumentType, DocumentVersion
from documents.search import document_search
from documents.signals import post_version_upload
@@ -70,9 +69,7 @@ class OCRApp(MayanAppConfig):
SourceColumn(
source=DocumentVersionOCRError, label=_('Document'),
attribute=encapsulate(
lambda entry: document_link(entry.document_version.document)
)
func=lambda context: document_link(context['object'].document_version.document)
)
SourceColumn(
source=DocumentVersionOCRError, label=_('Added'),

View File

@@ -7,7 +7,6 @@ from django.apps import apps
from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_setup, menu_object
from common.utils import encapsulate
from common.widgets import exists_widget
from navigation import SourceColumn
@@ -29,20 +28,20 @@ class SmartSettingsApp(MayanAppConfig):
SourceColumn(
source=Namespace, label=_('Setting count'),
attribute=encapsulate(lambda instance: len(instance.settings))
func=lambda context: len(context['object'].settings)
)
SourceColumn(
source=Setting, label=_('Name'),
attribute=encapsulate(lambda instance: setting_widget(instance))
func=lambda context: setting_widget(context['object'])
)
SourceColumn(
source=Setting, label=_('Value'), attribute='serialized_value'
)
SourceColumn(
source=Setting, label=_('Found in path'),
attribute=encapsulate(
lambda instance: exists_widget(instance.value) if instance.is_path else _('n/a')
)
func=lambda context: exists_widget(
context['object'].value
) if context['object'].is_path else _('n/a')
)
menu_object.bind_links(

View File

@@ -9,7 +9,6 @@ from common import (
menu_sidebar, menu_setup
)
from common.signals import post_initial_setup, post_upgrade
from common.utils import encapsulate
from converter.links import link_transformation_list
from documents.models import Document
from documents.signals import post_version_upload
@@ -32,8 +31,8 @@ from .links import (
link_upload_version
)
from .models import (
POP3Email, IMAPEmail, Source, StagingFolderSource, WatchFolderSource,
WebFormSource
POP3Email, IMAPEmail, Source, SourceLog, StagingFolderSource,
WatchFolderSource, WebFormSource
)
from .widgets import staging_file_thumbnail
@@ -61,13 +60,22 @@ class SourcesApp(MayanAppConfig):
SourceColumn(
source=StagingFile,
label=_('Thumbnail'),
attribute=encapsulate(
lambda staging_file: staging_file_thumbnail(
staging_file,
func=lambda context: staging_file_thumbnail(
context['object'],
gallery_name='sources:staging_list',
title=staging_file.filename, size='100'
title=context['object'].filename, size='100'
)
)
SourceColumn(
source=SourceLog,
label=_('Date time'),
func=lambda context: context['object'].datetime
)
SourceColumn(
source=SourceLog,
label=_('Message'),
func=lambda context: context['object'].message
)
app.conf.CELERY_QUEUES.extend(

View File

@@ -58,16 +58,6 @@ class SourceLogListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Date time'),
'attribute': encapsulate(lambda entry: entry.datetime)
},
{
'name': _('Message'),
'attribute': encapsulate(lambda entry: entry.message)
},
),
'hide_object': True,
'object': self.get_source(),
'title': _('Log entries for source: %s') % self.get_source(),
@@ -494,7 +484,7 @@ class SetupSourceListView(SingleObjectListView):
queryset = Source.objects.select_subclasses()
extra_context = {
'extra_columns': [
'extra_columns': (
{
'name': _('Type'),
'attribute': encapsulate(lambda entry: entry.class_fullname())
@@ -505,7 +495,7 @@ class SetupSourceListView(SingleObjectListView):
lambda entry: two_state_template(entry.enabled)
)
},
],
),
'hide_link': True,
'title': _('Sources'),
}

View File

@@ -9,7 +9,6 @@ from common import (
MayanAppConfig, menu_facet, menu_secondary, menu_object, menu_main,
menu_multi_item, menu_sidebar
)
from common.utils import encapsulate
from documents.models import Document
from documents.search import document_search
from navigation import CombinedSource, SourceColumn
@@ -55,14 +54,18 @@ class TagsApp(MayanAppConfig):
SourceColumn(
source=Document, label=_('Tags'),
attribute=encapsulate(
lambda document: widget_inline_tags(document)
)
func=lambda context: widget_inline_tags(context['object'])
)
SourceColumn(
source=Tag, label=_('Preview'),
attribute=encapsulate(lambda tag: widget_single_tag(tag))
func=lambda context: widget_single_tag(context['object'])
)
SourceColumn(
source=Tag, label=_('Documents'),
func=lambda context: context['object'].get_document_count(
user=context['request'].user
)
)
document_search.add_model_field(field='tags__label', label=_('Tags'))

View File

@@ -1,12 +1,16 @@
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
from django.db import models
from django.core.exceptions import PermissionDenied
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from colorful.fields import RGBColorField
from acls.models import AccessControlList
from documents.models import Document
from documents.permissions import permission_document_view
from permissions import Permission
@python_2_unicode_compatible
@@ -25,3 +29,15 @@ class Tag(models.Model):
def __str__(self):
return self.label
def get_document_count(self, user):
queryset = self.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()

View File

@@ -12,7 +12,6 @@ from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import (
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
)
@@ -126,33 +125,9 @@ def tag_multiple_attach(request):
class TagListView(SingleObjectListView):
object_permission = permission_tag_view
@staticmethod
def get_document_count(instance, user):
queryset = instance.documents
try:
Permission.check_permissions(user, (permission_document_view,))
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset
)
return queryset.count()
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: TagListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
),
'hide_link': True,
'title': _('Tags'),
}
@@ -282,16 +257,6 @@ class DocumentTagListView(TagListView):
def get_extra_context(self):
return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: TagListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True,
'object': self.document,
'title': _('Tags for document: %s') % self.document,

View File

@@ -8,7 +8,9 @@ from actstream import registry
from common import menu_multi_item, menu_object, menu_secondary, menu_setup
from common.apps import MayanAppConfig
from common.widgets import two_state_template
from metadata import MetadataLookup
from navigation import SourceColumn
from rest_api.classes import APIEndPoint
from .links import (
@@ -33,6 +35,25 @@ class UserManagementApp(MayanAppConfig):
MetadataLookup(description=_('All the groups.'), name='group', value=Group.objects.all())
MetadataLookup(description=_('All the users.'), name='users', value=get_user_model().objects.all())
SourceColumn(
source=Group, label=_('Members'), attribute='user_set.count'
)
SourceColumn(
source=User, label=_('Full name'), attribute='get_full_name'
)
SourceColumn(
source=User, label=_('Email'), attribute='email'
)
SourceColumn(
source=User, label=_('Active'),
func=lambda context: two_state_template(context['object'].is_active)
)
SourceColumn(
source=User, label=_('Has usable password?'),
func=lambda context: two_state_template(context['object'].has_usable_password())
)
menu_multi_item.bind_links(
links=(link_group_multiple_delete,),
sources=('user_management:group_list',)

View File

@@ -10,7 +10,6 @@ from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from common.utils import encapsulate
from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectEditView,
SingleObjectListView
@@ -31,30 +30,6 @@ class UserListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Full name'),
'attribute': 'get_full_name'
},
{
'name': _('Email'),
'attribute': 'email'
},
{
'name': _('Active'),
'attribute': encapsulate(
lambda user: two_state_template(user.is_active)
),
},
{
'name': _('Has usable password?'),
'attribute': encapsulate(
lambda user: two_state_template(
user.has_usable_password()
)
),
},
),
'hide_link': True,
'title': _('Users'),
}
@@ -301,8 +276,8 @@ class GroupEditView(SingleObjectEditView):
class GroupListView(SingleObjectListView):
extra_context = {
'title': _('Groups'),
'hide_link': True,
'title': _('Groups'),
'extra_columns': [
{
'name': _('Members'),