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.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import encapsulate
from common.views import ( from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView, AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectListView SingleObjectListView

View File

@@ -87,7 +87,7 @@
{% endif %} {% endif %}
{% if not hide_columns %} {% if not hide_columns %}
{% for column in object|get_source_columns %} {% for column in object|get_source_columns %}
<td>{{ object|object_property:column.attribute }}</td> <td>{% source_column_resolve column=column %}</td>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% for column in extra_columns %} {% 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 django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_object, menu_sidebar from common import MayanAppConfig, menu_object, menu_sidebar
from common.utils import encapsulate
from navigation import SourceColumn from navigation import SourceColumn
from .links import ( from .links import (
@@ -23,9 +22,7 @@ class ConverterApp(MayanAppConfig):
SourceColumn(source=Transformation, label=_('Order'), attribute='order') SourceColumn(source=Transformation, label=_('Order'), attribute='order')
SourceColumn( SourceColumn(
source=Transformation, label=_('Transformation'), source=Transformation, label=_('Transformation'),
attribute=encapsulate( func=lambda context: unicode(context['object'])
lambda transformation: unicode(transformation)
)
) )
SourceColumn( SourceColumn(
source=Transformation, label=_('Arguments'), attribute='arguments' 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 django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_object, menu_setup, menu_sidebar from common import MayanAppConfig, menu_object, menu_setup, menu_sidebar
from common.utils import encapsulate
from navigation import SourceColumn from navigation import SourceColumn
from .api import Key, KeyStub from .api import Key, KeyStub
@@ -23,34 +22,31 @@ class DjangoGPGApp(MayanAppConfig):
def ready(self): def ready(self):
super(DjangoGPGApp, self).ready() super(DjangoGPGApp, self).ready()
SourceColumn(source=Key, label='ID', attribute='key_id') SourceColumn(source=Key, label=_('ID'), attribute='key_id')
SourceColumn( SourceColumn(
source=Key, label='Owner', attribute=encapsulate( source=Key, label=_('Owner'),
lambda key: ', '.join(key.uids) func=lambda context: ', '.join(context['object'].uids)
)
) )
SourceColumn( SourceColumn(
source=KeyStub, label='ID', attribute=encapsulate( source=KeyStub, label=_('ID'),
lambda key: '...{0}'.format(key.key_id[-16:]) 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( SourceColumn(
source=KeyStub, label='Creation date', attribute=encapsulate( source=KeyStub, label=_('Creation date'),
lambda key: datetime.fromtimestamp(int(key.date)) func=lambda context: datetime.fromtimestamp(
int(context['object'].date)
) )
) )
SourceColumn( SourceColumn(
source=KeyStub, label='Expiration date', attribute=encapsulate( source=KeyStub, label=_('Expiration date'),
lambda key: datetime.fromtimestamp(int(key.expires)) if key.expires else _('No expiration') 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( SourceColumn(
source=KeyStub, label='Identities', attribute=encapsulate( source=KeyStub, label=_('Identities'),
lambda key: ', '.join(key.uids) func=lambda context: ', '.join(context['object'].uids)
)
) )
menu_object.bind_links(links=(link_key_delete,), sources=(Key,)) 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 django.utils.translation import ugettext_lazy as _
from common.generics import SimpleView, SingleObjectListView from common.generics import SimpleView, SingleObjectListView
from common.utils import encapsulate
from permissions import Permission from permissions import Permission
from .api import Key from .api import Key

View File

@@ -4,7 +4,6 @@ from django.utils.translation import ugettext_lazy as _
from acls import ModelPermission from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_object, menu_sidebar from common import MayanAppConfig, menu_facet, menu_object, menu_sidebar
from common.utils import encapsulate
from documents.models import Document from documents.models import Document
from navigation import SourceColumn from navigation import SourceColumn
@@ -37,9 +36,7 @@ class DocumentCommentsApp(MayanAppConfig):
SourceColumn(source=Comment, label=_('Date'), attribute='submit_date') SourceColumn(source=Comment, label=_('Date'), attribute='submit_date')
SourceColumn( SourceColumn(
source=Comment, label=_('User'), source=Comment, label=_('User'),
attribute=encapsulate( func=lambda context: context['object'].user.get_full_name() if context['object'].user.get_full_name() else context['object'].user
lambda x: x.user.get_full_name() if x.user.get_full_name() else x.user
)
) )
SourceColumn(source=Comment, label=_('Comment'), attribute='comment') 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, MayanAppConfig, menu_facet, menu_main, menu_object, menu_secondary,
menu_setup, menu_tools menu_setup, menu_tools
) )
from common.widgets import two_state_template
from documents.models import Document from documents.models import Document
from documents.signals import post_document_created from documents.signals import post_document_created
from mayan.celery import app from mayan.celery import app
from metadata.models import DocumentMetadata from metadata.models import DocumentMetadata
from navigation import SourceColumn
from rest_api.classes import APIEndPoint from rest_api.classes import APIEndPoint
from .handlers import ( from .handlers import (
@@ -27,7 +29,11 @@ from .links import (
link_template_node_create, link_template_node_delete, link_template_node_create, link_template_node_delete,
link_template_node_edit 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): class DocumentIndexingApp(MayanAppConfig):
@@ -41,6 +47,60 @@ class DocumentIndexingApp(MayanAppConfig):
APIEndPoint(app=self, version_string='1') 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( app.conf.CELERY_QUEUES.append(
Queue('indexing', Exchange('indexing'), routing_key='indexing'), 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.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible 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.fields import TreeForeignKey
from mptt.models import MPTTModel from mptt.models import MPTTModel
from acls.models import AccessControlList
from documents.models import Document, DocumentType from documents.models import Document, DocumentType
from documents.permissions import permission_document_view
from permissions import Permission
from .managers import IndexManager, IndexInstanceNodeManager from .managers import IndexManager, IndexInstanceNodeManager
@@ -58,7 +62,10 @@ class Index(models.Model):
return '#' return '#'
def save(self, *args, **kwargs): 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) super(Index, self).save(*args, **kwargs)
IndexTemplateNode.objects.get_or_create(parent=None, index=self) 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'] [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): def get_instance_node_count(self):
try: try:
return self.instance_root.get_descendant_count() return self.instance_root.get_descendant_count()
except IndexInstanceNode.DoesNotExist: except IndexInstanceNode.DoesNotExist:
return 0 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: class Meta:
verbose_name = _('Index') proxy = True
verbose_name_plural = _('Indexes') verbose_name = _('Index instance')
verbose_name_plural = _('Index instances')
@python_2_unicode_compatible @python_2_unicode_compatible
@@ -140,9 +160,6 @@ class IndexInstanceNode(MPTTModel):
def __str__(self): def __str__(self):
return self.value return self.value
def index(self):
return self.index_template_node.index
def get_absolute_url(self): def get_absolute_url(self):
return reverse('indexing:index_instance_node_view', args=[self.pk]) return reverse('indexing:index_instance_node_view', args=[self.pk])
@@ -151,6 +168,38 @@ class IndexInstanceNode(MPTTModel):
# Convenience method for serializer # Convenience method for serializer
return self.get_children() 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: class Meta:
verbose_name = _('Index node instance') verbose_name = _('Index node instance')
verbose_name_plural = _('Indexes node instances') 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 django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import ( from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView, AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectEditView, SingleObjectListView SingleObjectEditView, SingleObjectListView
@@ -23,7 +22,10 @@ from documents.views import DocumentListView
from permissions import Permission from permissions import Permission
from .forms import IndexTemplateNodeForm from .forms import IndexTemplateNodeForm
from .models import Index, IndexInstanceNode, IndexTemplateNode from .models import (
DocumentIndexInstanceNode, Index, IndexInstance, IndexInstanceNode,
IndexTemplateNode
)
from .permissions import ( from .permissions import (
permission_document_indexing_create, permission_document_indexing_delete, permission_document_indexing_create, permission_document_indexing_delete,
permission_document_indexing_edit, permission_document_indexing_edit,
@@ -49,17 +51,8 @@ class SetupIndexListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'title': _('Indexes'),
'hide_object': True, 'hide_object': True,
'extra_columns': [ 'title': _('Indexes'),
{'name': _('Label'), 'attribute': 'label'},
{'name': _('Slug'), 'attribute': 'slug'},
{
'name': _('Enabled'), 'attribute': encapsulate(
lambda x: two_state_template(x.enabled)
)
},
]
} }
@@ -71,8 +64,8 @@ class SetupIndexEditView(SingleObjectEditView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'title': _('Edit index: %s') % self.get_object(),
'object': 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): def get_extra_context(self):
return { return {
'title': _('Delete the index: %s?') % self.get_object(),
'object': 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): def get_extra_context(self):
return { 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, 'hide_object': True,
'index': self.get_index(), 'index': self.get_index(),
'navigation_object_list': ('index',), 'navigation_object_list': ('index',),
@@ -276,54 +252,17 @@ def template_node_delete(request, node_pk):
class IndexListView(SingleObjectListView): 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 object_permission = permission_document_indexing_view
queryset = IndexInstance.objects.filter(enabled=True)
def get_extra_context(self): def get_extra_context(self):
return { return {
'title': _('Indexes'),
'hide_links': True, 'hide_links': True,
'extra_columns': [ 'title': _('Indexes'),
{
'name': _('Items'), 'attribute': encapsulate(
lambda instance: IndexListView.get_items_count(instance)
)
},
{
'name': _('Document types'),
'attribute': 'get_document_types_names'
},
],
} }
class IndexInstanceNodeView(DocumentListView, SingleObjectListView): 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): def dispatch(self, request, *args, **kwargs):
self.index_instance = get_object_or_404( self.index_instance = get_object_or_404(
IndexInstanceNode, pk=self.kwargs['pk'] 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: if self.index_instance and not self.index_instance.index_template_node.link_documents:
context.update( context.update({'hide_object': True})
{
'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,
}
)
return context return context
@@ -446,16 +367,6 @@ class DocumentIndexNodeListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'extra_columns': (
{
'name': _('Node'),
'attribute': encapsulate(
lambda node: get_breadcrumbs(
index_instance_node=node, single_link=True, include_count=True
)
)
},
),
'hide_object': True, 'hide_object': True,
'object': self.get_document(), 'object': self.get_document(),
'title': _( 'title': _(
@@ -464,4 +375,4 @@ class DocumentIndexNodeListView(SingleObjectListView):
} }
def get_queryset(self): 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 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): def get_instance_link(index_instance_node, text=None, simple=False):
""" """
Return an HTML anchor to an index instance 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)) 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): def node_level(node):
""" """
Render an indented tree like output for a specific 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, MayanAppConfig, menu_facet, menu_object, menu_secondary, menu_setup,
menu_sidebar menu_sidebar
) )
from common.utils import encapsulate
from common.widgets import two_state_template from common.widgets import two_state_template
from documents.models import Document from documents.models import Document
from navigation import SourceColumn from navigation import SourceColumn
@@ -39,9 +38,7 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=Workflow, label=_('Initial state'), source=Workflow, label=_('Initial state'),
attribute=encapsulate( func=lambda context: context['object'].get_initial_state() or _('None')
lambda workflow: workflow.get_initial_state() or _('None')
)
) )
SourceColumn( SourceColumn(
@@ -50,10 +47,8 @@ class DocumentStatesApp(MayanAppConfig):
) )
SourceColumn( SourceColumn(
source=WorkflowInstance, label=_('User'), source=WorkflowInstance, label=_('User'),
attribute=encapsulate( func=lambda context: getattr(
lambda workflow: getattr( context['object'].get_last_log_entry(), 'user', _('None')
workflow.get_last_log_entry(), 'user', _('None')
)
) )
) )
SourceColumn( SourceColumn(
@@ -62,16 +57,14 @@ class DocumentStatesApp(MayanAppConfig):
) )
SourceColumn( SourceColumn(
source=WorkflowInstance, label=_('Date and time'), source=WorkflowInstance, label=_('Date and time'),
attribute=encapsulate( func=lambda context: getattr(
lambda workflow: getattr( context['object'].get_last_log_entry(), 'datetime', _('None')
workflow.get_last_log_entry(), 'datetime', _('None')
)
) )
) )
SourceColumn( SourceColumn(
source=WorkflowInstance, label=_('Completion'), source=WorkflowInstance, label=_('Completion'),
attribute=encapsulate(lambda workflow: getattr( func=lambda context: getattr(
workflow.get_current_state(), 'completion', _('None')) context['object'].get_current_state(), 'completion', _('None')
) )
) )
@@ -93,9 +86,7 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=WorkflowState, label=_('Is initial state?'), source=WorkflowState, label=_('Is initial state?'),
attribute=encapsulate( func=lambda context: two_state_template(context['object'].initial)
lambda state: two_state_template(state.initial)
)
) )
SourceColumn( SourceColumn(
source=WorkflowState, label=_('Completion'), attribute='completion' source=WorkflowState, label=_('Completion'), attribute='completion'

View File

@@ -17,7 +17,7 @@ from common import (
) )
from common.classes import ModelAttribute from common.classes import ModelAttribute
from common.signals import post_initial_setup 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.links import link_transformation_list
from converter.permissions import ( from converter.permissions import (
permission_transformation_create, permission_transformation_create,
@@ -125,17 +125,27 @@ class DocumentsApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=Document, label=_('Thumbnail'), source=Document, label=_('Thumbnail'),
attribute=encapsulate( func=lambda context: document_thumbnail(
lambda document: document_thumbnail( context['object'], gallery_name='documents:document_list',
document, gallery_name='documents:document_list',
size=setting_thumbnail_size.value, size=setting_thumbnail_size.value,
title=getattr(document, 'label', None), title=getattr(context['object'], 'label', None),
)
) )
) )
SourceColumn( SourceColumn(
source=Document, label=_('Type'), attribute='document_type' 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( SourceColumn(
source=DeletedDocument, label=_('Type'), attribute='document_type' source=DeletedDocument, label=_('Type'), attribute='document_type'
) )
@@ -144,6 +154,23 @@ class DocumentsApp(MayanAppConfig):
attribute='deleted_date_time' 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( app.conf.CELERYBEAT_SCHEDULE.update(
{ {
'task_check_delete_periods': { 'task_check_delete_periods': {

View File

@@ -954,9 +954,6 @@ class DocumentTypeListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'extra_columns': [
{'name': _('Documents'), 'attribute': encapsulate(lambda document_type: document_type.documents.count())}
],
'hide_link': True, 'hide_link': True,
'title': _('Document types'), 'title': _('Document types'),
} }
@@ -1010,12 +1007,6 @@ class DocumentTypeFilenameListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'document_type': self.get_document_type(), 'document_type': self.get_document_type(),
'extra_columns': (
{
'name': _('Enabled'),
'attribute': encapsulate(lambda filename: two_state_template(filename.enabled)),
},
),
'hide_link': True, 'hide_link': True,
'navigation_object_list': ('document_type',), 'navigation_object_list': ('document_type',),
'title': _('Filenames for document type: %s') % self.get_document_type(), 'title': _('Filenames for document type: %s') % self.get_document_type(),
@@ -1148,12 +1139,7 @@ class DocumentVersionListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'extra_columns': ( 'hide_object': True, 'object': self.get_document(),
{'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(),
'title': _('Versions of document: %s') % 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 actstream.models import Action
from common import MayanAppConfig, menu_tools from common import MayanAppConfig, menu_tools
from common.utils import encapsulate
from navigation import SourceColumn from navigation import SourceColumn
from .links import link_events_list from .links import link_events_list
@@ -21,8 +20,8 @@ class EventsApp(MayanAppConfig):
SourceColumn(source=Action, label=_('Timestamp'), attribute='timestamp') SourceColumn(source=Action, label=_('Timestamp'), attribute='timestamp')
SourceColumn(source=Action, label=_('Actor'), attribute='actor') SourceColumn(source=Action, label=_('Actor'), attribute='actor')
SourceColumn(source=Action, label=_('Verb'), attribute=encapsulate( SourceColumn(source=Action, label=_('Verb'),
lambda entry: event_type_link(entry)) func=lambda context: event_type_link(context['object'])
) )
menu_tools.bind_links(links=[link_events_list]) menu_tools.bind_links(links=[link_events_list])

View File

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

View File

@@ -56,6 +56,12 @@ class FoldersApp(MayanAppConfig):
source=Folder, label=_('Created'), attribute='datetime_created' source=Folder, label=_('Created'), attribute='datetime_created'
) )
SourceColumn(source=Folder, label=_('User'), attribute='user') 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( menu_facet.bind_links(
links=(link_document_folder_list,), sources=(Document,) 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.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList
from documents.models import Document from documents.models import Document
from documents.permissions import permission_document_view
from permissions import Permission
@python_2_unicode_compatible @python_2_unicode_compatible
@@ -28,6 +32,18 @@ class Folder(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('folders:folder_view', args=[self.pk]) 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: class Meta:
ordering = ('label',) ordering = ('label',)
unique_together = ('label', 'user') 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 django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import ( from common.views import (
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
) )
@@ -48,31 +47,8 @@ class FolderEditView(SingleObjectEditView):
class FolderListView(SingleObjectListView): class FolderListView(SingleObjectListView):
object_permission = permission_folder_view 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): def get_extra_context(self):
return { return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: FolderListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True, 'hide_link': True,
'title': _('Folders'), 'title': _('Folders'),
} }
@@ -253,16 +229,6 @@ class DocumentFolderListView(FolderListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: FolderListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True, 'hide_link': True,
'object': self.document, 'object': self.document,
'title': _('Folders containing document: %s') % 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 django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_tools, menu_object, menu_secondary from common import MayanAppConfig, menu_tools, menu_object, menu_secondary
from common.utils import encapsulate
from navigation import SourceColumn from navigation import SourceColumn
from .classes import Property, PropertyNamespace, PIPNotFound, VirtualEnv from .classes import Property, PropertyNamespace, PIPNotFound, VirtualEnv
@@ -24,7 +23,7 @@ class InstallationApp(MayanAppConfig):
) )
SourceColumn( SourceColumn(
source=PropertyNamespace, label=_('Items'), 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') 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, MayanAppConfig, menu_facet, menu_object, menu_secondary, menu_setup,
menu_sidebar menu_sidebar
) )
from common.widgets import two_state_template
from documents.models import Document from documents.models import Document
from navigation import SourceColumn
from .links import ( from .links import (
link_smart_link_create, link_smart_link_condition_create, 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( menu_facet.bind_links(
links=(link_smart_link_instances_for_document,), links=(link_smart_link_instances_for_document,),
sources=(Document,) sources=(Document,)

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ from common import (
menu_setup, menu_sidebar, menu_tools menu_setup, menu_sidebar, menu_tools
) )
from common.classes import ModelAttribute 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.models import Document, DocumentType
from documents.search import document_search from documents.search import document_search
from documents.signals import post_document_type_change 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_setup_metadata_type_edit, link_setup_metadata_type_list,
link_documents_missing_required_metadata link_documents_missing_required_metadata
) )
from .models import DocumentTypeMetadataType, MetadataType from .models import DocumentMetadata, DocumentTypeMetadataType, MetadataType
from .permissions import ( from .permissions import (
permission_metadata_document_add, permission_metadata_document_edit, permission_metadata_document_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view permission_metadata_document_remove, permission_metadata_document_view
@@ -94,9 +94,16 @@ class MetadataApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=Document, label=_('Metadata'), source=Document, label=_('Metadata'),
attribute=encapsulate( func=lambda context: get_metadata_string(context['object'])
lambda document: get_metadata_string(document)
) )
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( app.conf.CELERY_QUEUES.append(

View File

@@ -103,6 +103,10 @@ class DocumentMetadata(models.Model):
return super(DocumentMetadata, self).delete(*args, **kwargs) 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: class Meta:
unique_together = ('document', 'metadata_type') unique_together = ('document', 'metadata_type')
verbose_name = _('Document metadata') 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 django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList from acls.models import AccessControlList
from common.utils import encapsulate
from common.generics import ( from common.generics import (
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView, AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectEditView, SingleObjectListView SingleObjectEditView, SingleObjectListView
@@ -495,18 +494,9 @@ class DocumentMetadataListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
document = self.get_document() document = self.get_document()
return { 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, 'hide_link': True,
'object': document, 'object': document,
'title': _('Metadata for document: %s') % document,
} }
def get_queryset(self): def get_queryset(self):

View File

@@ -281,11 +281,13 @@ class SourceColumn(object):
# unhashable type: list # unhashable type: list
return () 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.setdefault(source, [])
self.__class__._registry[source].append( self.__class__._registry[source].append(self)
{'label': label, 'attribute': attribute}
)
class CombinedSource(object): class CombinedSource(object):

View File

@@ -2,6 +2,8 @@ from __future__ import unicode_literals
from django.template import Library from django.template import Library
from common.utils import return_attrib
from ..classes import Menu, SourceColumn from ..classes import Menu, SourceColumn
from ..forms import MultiItemForm from ..forms import MultiItemForm
@@ -58,3 +60,11 @@ def get_source_columns(source):
pass pass
return SourceColumn.get_for_source(source) 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, MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_tools menu_tools
) )
from common.utils import encapsulate
from documents.models import Document, DocumentType, DocumentVersion from documents.models import Document, DocumentType, DocumentVersion
from documents.search import document_search from documents.search import document_search
from documents.signals import post_version_upload from documents.signals import post_version_upload
@@ -70,9 +69,7 @@ class OCRApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=DocumentVersionOCRError, label=_('Document'), source=DocumentVersionOCRError, label=_('Document'),
attribute=encapsulate( func=lambda context: document_link(context['object'].document_version.document)
lambda entry: document_link(entry.document_version.document)
)
) )
SourceColumn( SourceColumn(
source=DocumentVersionOCRError, label=_('Added'), source=DocumentVersionOCRError, label=_('Added'),

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,6 @@ from common import (
MayanAppConfig, menu_facet, menu_secondary, menu_object, menu_main, MayanAppConfig, menu_facet, menu_secondary, menu_object, menu_main,
menu_multi_item, menu_sidebar menu_multi_item, menu_sidebar
) )
from common.utils import encapsulate
from documents.models import Document from documents.models import Document
from documents.search import document_search from documents.search import document_search
from navigation import CombinedSource, SourceColumn from navigation import CombinedSource, SourceColumn
@@ -55,14 +54,18 @@ class TagsApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=Document, label=_('Tags'), source=Document, label=_('Tags'),
attribute=encapsulate( func=lambda context: widget_inline_tags(context['object'])
lambda document: widget_inline_tags(document)
)
) )
SourceColumn( SourceColumn(
source=Tag, label=_('Preview'), 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')) 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.db import models
from django.core.exceptions import PermissionDenied
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from colorful.fields import RGBColorField from colorful.fields import RGBColorField
from acls.models import AccessControlList
from documents.models import Document from documents.models import Document
from documents.permissions import permission_document_view
from permissions import Permission
@python_2_unicode_compatible @python_2_unicode_compatible
@@ -25,3 +29,15 @@ class Tag(models.Model):
def __str__(self): def __str__(self):
return self.label 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 django.utils.translation import ugettext_lazy as _, ungettext
from acls.models import AccessControlList from acls.models import AccessControlList
from common.utils import encapsulate
from common.views import ( from common.views import (
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
) )
@@ -126,33 +125,9 @@ def tag_multiple_attach(request):
class TagListView(SingleObjectListView): class TagListView(SingleObjectListView):
object_permission = permission_tag_view 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): def get_extra_context(self):
return { return {
'extra_columns': (
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: TagListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
),
'hide_link': True, 'hide_link': True,
'title': _('Tags'), 'title': _('Tags'),
} }
@@ -282,16 +257,6 @@ class DocumentTagListView(TagListView):
def get_extra_context(self): def get_extra_context(self):
return { return {
'extra_columns': [
{
'name': _('Documents'),
'attribute': encapsulate(
lambda instance: TagListView.get_document_count(
instance=instance, user=self.request.user
)
)
},
],
'hide_link': True, 'hide_link': True,
'object': self.document, 'object': self.document,
'title': _('Tags for document: %s') % 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 import menu_multi_item, menu_object, menu_secondary, menu_setup
from common.apps import MayanAppConfig from common.apps import MayanAppConfig
from common.widgets import two_state_template
from metadata import MetadataLookup from metadata import MetadataLookup
from navigation import SourceColumn
from rest_api.classes import APIEndPoint from rest_api.classes import APIEndPoint
from .links import ( 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 groups.'), name='group', value=Group.objects.all())
MetadataLookup(description=_('All the users.'), name='users', value=get_user_model().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( menu_multi_item.bind_links(
links=(link_group_multiple_delete,), links=(link_group_multiple_delete,),
sources=('user_management:group_list',) 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.template import RequestContext
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import encapsulate
from common.views import ( from common.views import (
AssignRemoveView, SingleObjectCreateView, SingleObjectEditView, AssignRemoveView, SingleObjectCreateView, SingleObjectEditView,
SingleObjectListView SingleObjectListView
@@ -31,30 +30,6 @@ class UserListView(SingleObjectListView):
def get_extra_context(self): def get_extra_context(self):
return { 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, 'hide_link': True,
'title': _('Users'), 'title': _('Users'),
} }
@@ -301,8 +276,8 @@ class GroupEditView(SingleObjectEditView):
class GroupListView(SingleObjectListView): class GroupListView(SingleObjectListView):
extra_context = { extra_context = {
'title': _('Groups'),
'hide_link': True, 'hide_link': True,
'title': _('Groups'),
'extra_columns': [ 'extra_columns': [
{ {
'name': _('Members'), 'name': _('Members'),