Add widget support to SourceColumn

Allow passing a widget class to SourceColumn. This makes
using lambdas to render model column unnecesary and are
mostly removed too.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2018-12-22 05:35:31 -04:00
parent 360e756093
commit 64e1c6bb67
59 changed files with 450 additions and 402 deletions

View File

@@ -211,6 +211,9 @@
clicking on the title of the body of the card, not just on
the checkbox next to the title.
- Event handler to highlight panels when selected.
- Improve duplicated document display.
- Filter document duplicted count by access.
3.1.9 (2018-11-01)
==================

View File

@@ -76,7 +76,7 @@
{{ object }}
{% endif %}
{% else %}
{% source_column_resolve column=object|get_source_columns:"only_identifier" %}
{% source_column_resolve column=object|get_source_columns:"only_identifier" as column_value %}{{ column_value }}
{% endif %}
</span>
</label>

View File

@@ -98,7 +98,7 @@
{% endif %}
{% if not hide_columns %}
{% for column in object|get_source_columns %}
<td>{% source_column_resolve column=column %}{{ column_result }}</td>
<td>{% source_column_resolve column=column as column_value %}{{ column_value }}</td>{# Use explicit 'as column_value ' to force date rendering #}
{% endfor %}
{% endif %}
{% for column in extra_columns %}

View File

@@ -23,6 +23,10 @@ from pure_pagination.mixins import PaginationMixin
from .forms import ChoiceForm
from .icons import icon_assign_remove_add, icon_assign_remove_remove
from .literals import (
TEXT_CHOICE_ITEMS, TEXT_CHOICE_LIST, TEXT_LIST_AS_ITEMS,
TEXT_LIST_AS_ITEMS_PARAMETER
)
from .mixins import (
DeleteExtraDataMixin, DynamicFormViewMixin, ExtraContextMixin,
FormExtraKwargsMixin, MultipleObjectMixin, ObjectActionMixin,
@@ -500,14 +504,17 @@ class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectList
def get_context_data(self, **kwargs):
context = super(SingleObjectListView, self).get_context_data(**kwargs)
if context.get('list_as_items'):
default_mode = 'items'
if context.get(TEXT_LIST_AS_ITEMS):
default_mode = TEXT_CHOICE_ITEMS
else:
default_mode = 'list'
default_mode = TEXT_CHOICE_LIST
list_mode = self.request.GET.get('_list_mode', default_mode)
list_mode = self.request.GET.get(
TEXT_LIST_AS_ITEMS_PARAMETER, default_mode
)
context.update({'list_as_items': list_mode=='items'})
context.update({TEXT_LIST_AS_ITEMS: list_mode == TEXT_CHOICE_ITEMS})
return context
def get_paginate_by(self, queryset):

View File

@@ -30,8 +30,8 @@ icon_menu_about = Icon(
icon_menu_user = Icon(
driver_name='fontawesome', symbol='user-circle'
)
icon_object_error_list_with_icon = Icon(
driver_name='fontawesome', symbol='lock'
icon_object_error_list = Icon(
driver_name='fontawesome', symbol='exclamation-triangle'
)
icon_ok = Icon(
driver_name='fontawesome', symbol='check'

View File

@@ -10,7 +10,7 @@ from .icons import (
icon_about, icon_check_version, icon_current_user_details,
icon_current_user_edit, icon_current_user_locale_profile_details,
icon_current_user_locale_profile_edit, icon_documentation, icon_forum,
icon_license, icon_object_error_list_with_icon, icon_packages_licenses,
icon_license, icon_object_error_list, icon_packages_licenses,
icon_setup, icon_source_code, icon_support, icon_tools
)
from .permissions_runtime import permission_error_log_view
@@ -65,6 +65,7 @@ link_documentation = Link(
text=_('Documentation'), url='https://docs.mayan-edms.com'
)
link_object_error_list = Link(
icon_class=icon_object_error_list,
kwargs=get_kwargs_factory('resolved_object'),
permissions=(permission_error_log_view,), text=_('Errors'),
view='common:object_error_list',
@@ -74,12 +75,6 @@ link_object_error_list_clear = Link(
permissions=(permission_error_log_view,), text=_('Clear all'),
view='common:object_error_list_clear',
)
link_object_error_list_with_icon = Link(
kwargs=get_kwargs_factory('resolved_object'),
icon_class=icon_object_error_list_with_icon,
permissions=(permission_error_log_view,), text=_('Errors'),
view='common:error_list',
)
link_forum = Link(
icon_class=icon_forum, tags='new_window', text=_('Forum'),
url='https://forum.mayan-edms.com'

View File

@@ -11,6 +11,12 @@ MESSAGE_SQLITE_WARNING = _(
'for development and testing, not for production.'
)
PYPI_URL = 'https://pypi.python.org/pypi'
TEXT_LIST_AS_ITEMS_PARAMETER = '_list_mode'
TEXT_LIST_AS_ITEMS = 'list_as_items'
TEXT_CHOICE_ITEMS = 'items'
TEXT_CHOICE_LIST = 'list'
TIME_DELTA_UNIT_DAYS = 'days'
TIME_DELTA_UNIT_HOURS = 'hours'
TIME_DELTA_UNIT_MINUTES = 'minutes'

View File

@@ -4,7 +4,6 @@ import logging
import os
import shutil
import tempfile
import types
from django.conf import settings
from django.db.models.constants import LOOKUP_SEP
@@ -18,7 +17,6 @@ from django.utils.six.moves import xmlrpc_client
from django.utils.translation import ugettext_lazy as _
import mayan
from mayan.apps.common.compat import dict_type, dictionary_type
from .exceptions import NotLatestVersion, UnknownLatestVersion
from .literals import DJANGO_SQLITE_BACKEND, MAYAN_PYPI_NAME, PYPI_URL
@@ -155,9 +153,9 @@ def resolve_attribute(obj, attribute, kwargs=None):
except AttributeError:
# Try as a related model field
if LOOKUP_SEP in attribute:
attrib = attribute.replace(LOOKUP_SEP, '.')
attribute_replaced = attribute.replace(LOOKUP_SEP, '.')
return resolve_attribute(
obj=obj, attribute=attribute, kwargs=kwargs
obj=obj, attribute=attribute_replaced, kwargs=kwargs
)
else:
raise

View File

@@ -28,7 +28,7 @@ from .generics import ( # NOQA
SingleObjectDownloadView, SingleObjectDynamicFormCreateView,
SingleObjectDynamicFormEditView, SingleObjectEditView, SingleObjectListView
)
from .icons import icon_setup
from .icons import icon_object_error_list, icon_setup
from .menus import menu_setup, menu_tools
from .permissions_runtime import permission_error_log_view
from .settings import setting_home_view
@@ -211,6 +211,15 @@ class ObjectErrorLogEntryListView(SingleObjectListView):
{'name': _('Result'), 'attribute': 'result'},
),
'hide_object': True,
'no_results_icon': icon_object_error_list,
'no_results_text': _(
'Errors during the operation of some objects are recorded '
'in this view. Some objects automatically clear their '
'errors on subsequent successful operation.'
),
'no_results_title': _(
'There are no errors for this object'
),
'object': self.get_object(),
'title': _('Error log entries for: %s' % self.get_object()),
}

View File

@@ -89,18 +89,17 @@ class TextAreaDiv(forms.widgets.Widget):
class TwoStateWidget(object):
def __init__(self, state, center=False, icon_ok=None, icon_fail=None):
self.state = state
def __init__(self, center=False, icon_ok=None, icon_fail=None):
self.icon_ok = icon_ok or default_icon_ok
self.icon_fail = icon_fail or default_icon_fail
self.center = center
def render(self):
def render(self, name=None, value=None):
center_class = ''
if self.center:
center_class = 'text-center'
if self.state:
if value:
return mark_safe(
'<div class="{} text-success">{}</div>'.format(
center_class, self.icon_ok.render()

View File

@@ -1,6 +1,5 @@
from __future__ import unicode_literals
from django.apps import apps
from django.utils.translation import ugettext_lazy as _
from mayan.apps.common import MayanAppConfig

View File

@@ -1,12 +1,6 @@
from __future__ import unicode_literals
from django.apps import apps
from django.db import models
from django.template import loader
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext
class Dashboard(object):

View File

@@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.

View File

@@ -1,6 +1,6 @@
from __future__ import unicode_literals
from django.template import Context, Library
from django.template import Library
from ..classes import Dashboard

View File

@@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
# Create your views here.

View File

@@ -44,22 +44,24 @@ class DjangoGPGApp(MayanAppConfig):
)
)
SourceColumn(source=Key, label=_('Key ID'), attribute='key_id')
SourceColumn(source=Key, label=_('User ID'), attribute='user_id')
SourceColumn(attribute='key_id', is_identifier=True, source=Key)
SourceColumn(attribute='user_id', source=Key)
SourceColumn(source=KeyStub, label=_('Key ID'), attribute='key_id')
SourceColumn(source=KeyStub, label=_('Type'), attribute='key_type')
SourceColumn(
source=KeyStub, label=_('Creation date'), attribute='date'
attribute='key_id.fget', is_identifier=True, source=KeyStub
)
SourceColumn(attribute='key_type', label=_('Type'), source=KeyStub)
SourceColumn(
attribute='date', label=_('Creation date'), source=KeyStub
)
SourceColumn(
source=KeyStub, label=_('Expiration date'),
func=lambda context: context['object'].expires or _('No expiration')
attribute='expires', empty_value=_('No expiration'),
label=_('Expiration date'), source=KeyStub
)
SourceColumn(source=KeyStub, label=_('Length'), attribute='length')
SourceColumn(attribute='length', label=_('Length'), source=KeyStub)
SourceColumn(
source=KeyStub, label=_('User ID'),
func=lambda context: ', '.join(context['object'].user_id)
func=lambda context: ', '.join(context['object'].user_id),
label=_('User ID'), source=KeyStub
)
menu_object.bind_links(links=(link_key_detail,), sources=(Key,))

View File

@@ -6,6 +6,8 @@ import shutil
import gnupg
from django.utils.translation import ugettext_lazy as _
from mayan.apps.common.utils import mkdtemp
@@ -149,6 +151,7 @@ class KeyStub(object):
@property
def key_id(self):
return self.fingerprint[-8:]
key_id.fget.short_description = _('Key ID')
class SignatureVerification(object):

View File

@@ -85,6 +85,7 @@ class Key(models.Model):
Short form key ID (using the first 8 characters).
"""
return self.fingerprint[-8:]
key_id.fget.short_description = _('Key ID')
def save(self, *args, **kwargs):
import_results, key_info = gpg_backend.import_and_list_keys(

View File

@@ -87,9 +87,7 @@ class DocumentIndexingApp(MayanAppConfig):
SourceColumn(attribute='label', is_identifier=True, source=Index)
SourceColumn(attribute='slug', source=Index)
SourceColumn(
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render(), label=_('Enabled'), source=Index
attribute='enabled', source=Index, widget=TwoStateWidget
)
SourceColumn(
@@ -111,15 +109,12 @@ class DocumentIndexingApp(MayanAppConfig):
label=_('Level'), source=IndexTemplateNode
)
SourceColumn(
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render(), label=_('Enabled'), source=IndexTemplateNode
attribute='enabled', source=IndexTemplateNode,
widget=TwoStateWidget
)
SourceColumn(
func=lambda context: TwoStateWidget(
state=context['object'].link_documents
).render(), label=_('Has document links?'),
source=IndexTemplateNode,
attribute='link_documents', source=IndexTemplateNode,
widget=TwoStateWidget
)
SourceColumn(

View File

@@ -1,9 +1,6 @@
from __future__ import unicode_literals
from .tasks import (
task_unverify_key_signatures, task_verify_key_signatures,
task_verify_missing_embedded_signature
)
from .tasks import task_unverify_key_signatures, task_verify_key_signatures
def handler_unverify_key_signatures(sender, **kwargs):

View File

@@ -135,16 +135,14 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn(
attribute='label', is_identifier=True, source=Workflow
)
SourceColumn(
attribute='internal_name', source=Workflow
)
SourceColumn(
attribute='get_initial_state', source=Workflow,
)
SourceColumn(attribute='internal_name', source=Workflow)
SourceColumn(attribute='get_initial_state', source=Workflow)
SourceColumn(
attribute='get_current_state', source=WorkflowInstance
attribute='workflow', is_identifier=True,
source=WorkflowInstance
)
SourceColumn(attribute='get_current_state', source=WorkflowInstance)
SourceColumn(
attribute='get_last_transition_user', source=WorkflowInstance
)
@@ -163,57 +161,40 @@ class DocumentStatesApp(MayanAppConfig):
SourceColumn(
attribute='get_rendered_datetime', source=WorkflowInstanceLogEntry
)
SourceColumn(
attribute='user', source=WorkflowInstanceLogEntry
)
SourceColumn(
attribute='transition', source=WorkflowInstanceLogEntry
)
SourceColumn(
attribute='comment', source=WorkflowInstanceLogEntry
)
SourceColumn(attribute='user', source=WorkflowInstanceLogEntry)
SourceColumn(attribute='transition', source=WorkflowInstanceLogEntry)
SourceColumn(attribute='comment', source=WorkflowInstanceLogEntry)
SourceColumn(
source=WorkflowState, label=_('Is initial state?'),
func=lambda context: TwoStateWidget(
state=context['object'].initial
).render()
attribute='label', is_identifier=True, source=WorkflowState
)
SourceColumn(
source=WorkflowState, label=_('Completion'), attribute='completion'
attribute='initial', source=WorkflowState, widget=TwoStateWidget
)
SourceColumn(attribute='completion', source=WorkflowState)
SourceColumn(
source=WorkflowStateAction, label=_('Label'), attribute='label'
attribute='label', is_identifier=True, source=WorkflowStateAction
)
SourceColumn(
source=WorkflowStateAction, label=_('Enabled?'),
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render()
attribute='enabled', source=WorkflowStateAction,
widget=TwoStateWidget
)
SourceColumn(
source=WorkflowStateAction, label=_('When?'),
attribute='get_when_display'
)
SourceColumn(
source=WorkflowStateAction, label=_('Action type'),
attribute='get_class_label'
attribute='get_when_display', label=_('When?'),
source=WorkflowStateAction
)
SourceColumn(attribute='get_class_label', source=WorkflowStateAction)
SourceColumn(
source=WorkflowTransition, label=_('Origin state'),
attribute='origin_state'
attribute='label', is_identifier=True, source=WorkflowTransition
)
SourceColumn(attribute='origin_state', source=WorkflowTransition)
SourceColumn(attribute='destination_state', source=WorkflowTransition)
SourceColumn(
source=WorkflowTransition, label=_('Destination state'),
attribute='destination_state'
)
SourceColumn(
source=WorkflowTransition, label=_('Triggers'),
func=lambda context: widget_transition_events(
transition=context['object']
)
), label=_('Triggers'), source=WorkflowTransition
)
app.conf.task_queues.extend(

View File

@@ -318,6 +318,7 @@ class WorkflowStateAction(models.Model):
def get_class_label(self):
return self.get_class().label
get_class_label.short_description = _('Action type')
def loads(self):
return json.loads(self.action_data)

View File

@@ -62,7 +62,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_workflow_list,
'no_results_text': _(
'Assign workflows to the document type of this document '
@@ -96,6 +96,13 @@ class WorkflowInstanceDetailView(SingleObjectListView):
return {
'hide_object': True,
'navigation_object_list': ('object', 'workflow_instance'),
'no_results_text': _(
'This view will show the states changed as a workflow '
'instance is transitioned.'
),
'no_results_title': _(
'There are no details for this workflow instance'
),
'object': self.get_workflow_instance().document,
'title': _('Detail of workflow: %(workflow)s') % {
'workflow': self.get_workflow_instance()
@@ -509,7 +516,7 @@ class SetupWorkflowStateListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_workflow_state,
'no_results_main_link': link_setup_workflow_state_create.resolve(
context=RequestContext(
@@ -624,7 +631,7 @@ class SetupWorkflowTransitionListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_workflow_transition,
'no_results_main_link': link_setup_workflow_transition_create.resolve(
context=RequestContext(

View File

@@ -104,10 +104,7 @@ from .queues import * # NOQA
from .search import document_page_search, document_search # NOQA
from .signals import post_version_upload
from .statistics import * # NOQA
from .widgets import (
DocumentPageThumbnailWidget, widget_document_page_number,
widget_document_version_page_number
)
from .widgets import DocumentPageThumbnailWidget
class DocumentsApp(MayanAppConfig):
@@ -129,7 +126,7 @@ class DocumentsApp(MayanAppConfig):
DocumentType = self.get_model('DocumentType')
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
DocumentVersion = self.get_model('DocumentVersion')
DuplicatedDocument = self.get_model('DuplicatedDocument')
DuplicatedDocumentProxy = self.get_model('DuplicatedDocumentProxy')
DynamicSerializerField.add_serializer(
klass=Document,
@@ -244,9 +241,7 @@ class DocumentsApp(MayanAppConfig):
SourceColumn(
attribute='document_type', label=_('Type'), source=Document
)
SourceColumn(
func=widget_document_page_number, label=_('Pages'), source=Document
)
SourceColumn(attribute='get_page_count', source=Document)
# DocumentPage
SourceColumn(
@@ -274,9 +269,8 @@ class DocumentsApp(MayanAppConfig):
)
SourceColumn(
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render(), label=_('Enabled'), source=DocumentTypeFilename
attribute='enabled', source=DocumentTypeFilename,
widget=TwoStateWidget
)
# DeletedDocument
@@ -290,41 +284,34 @@ class DocumentsApp(MayanAppConfig):
attribute='document_type', source=DeletedDocument
)
SourceColumn(
attribute='deleted_date_time', source=DeletedDocument
attribute='get_rendered_deleted_date_time', source=DeletedDocument
)
# DocumentVersion
SourceColumn(
attribute='get_rendered_timestamp', is_identifier=True,
source=DocumentVersion
)
SourceColumn(
func=lambda context: document_page_thumbnail_widget.render(
instance=context['object']
), label=_('Thumbnail'), source=DocumentVersion
)
SourceColumn(
attribute='timestamp', source=DocumentVersion
)
SourceColumn(
func=widget_document_version_page_number, label=_('Pages'),
source=DocumentVersion
)
SourceColumn(
attribute='mimetype', source=DocumentVersion
)
SourceColumn(
attribute='encoding', source=DocumentVersion
)
SourceColumn(
attribute='comment', source=DocumentVersion
)
SourceColumn(attribute='get_page_count', source=DocumentVersion)
SourceColumn(attribute='mimetype', source=DocumentVersion)
SourceColumn(attribute='encoding', source=DocumentVersion)
SourceColumn(attribute='comment', source=DocumentVersion)
# DuplicatedDocument
SourceColumn(
func=lambda context: document_page_thumbnail_widget.render(
instance=context['object'].document
), label=_('Thumbnail'), source=DuplicatedDocument
instance=context['object']
), label=_('Thumbnail'), source=DuplicatedDocumentProxy
)
SourceColumn(
func=lambda context: context['object'].documents.count(),
label=_('Duplicates'), source=DuplicatedDocument
func=lambda context: context['object'].get_duplicate_count(
user=context['request'].user,
), label=_('Duplicates'), source=DuplicatedDocumentProxy
)
app.conf.beat_schedule.update(

View File

@@ -128,10 +128,10 @@ class DuplicatedDocumentManager(models.Manager):
self.filter(documents=None).delete()
def get_duplicated_documents(self):
Document = apps.get_model(
app_label='documents', model_name='Document'
DuplicatedDocumentProxy = apps.get_model(
app_label='documents', model_name='DuplicatedDocumentProxy'
)
return Document.objects.filter(
return DuplicatedDocumentProxy.objects.filter(
pk__in=self.filter(documents__isnull=False).values_list(
'document_id', flat=True
)

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-22 09:30
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('documents', '0049_auto_20181211_0011'),
]
operations = [
migrations.CreateModel(
name='DuplicatedDocumentProxy',
fields=[
],
options={
'verbose_name': 'Duplicated document',
'proxy': True,
'verbose_name_plural': 'Duplicated documents',
'indexes': [],
},
bases=('documents.document',),
),
]

View File

@@ -7,12 +7,15 @@ from django.apps import apps
from django.conf import settings
from django.core.files import File
from django.db import models
from django.template import Context, Template
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.timezone import now
from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList
from ..events import (
event_document_create, event_document_properties_edit,
event_document_type_change,
@@ -21,6 +24,7 @@ from ..managers import (
DocumentManager, DuplicatedDocumentManager, FavoriteDocumentManager,
PassthroughManager, RecentDocumentManager, TrashCanManager
)
from ..permissions import permission_document_view
from ..settings import setting_language
from ..signals import post_document_type_change
@@ -77,7 +81,6 @@ class Document(models.Model):
'Whether or not this document is in the trash.'
), editable=False, verbose_name=_('In trash?')
)
# TODO: set editable to False
deleted_date_time = models.DateTimeField(
blank=True, editable=True, help_text=_(
'The server date and time when the document was moved to the '
@@ -139,6 +142,24 @@ class Document(models.Model):
if latest_version:
return latest_version.get_api_image_url(*args, **kwargs)
def get_duplicates(self):
try:
return DuplicatedDocument.objects.get(document=self).documents.all()
except DuplicatedDocument.DoesNotExist:
return Document.objects.none()
def get_page_count(self):
return self.pages.count()
get_page_count.short_description = _('Pages')
def get_rendered_deleted_date_time(self):
return Template('{{ instance.deleted_date_time }}').render(
context=Context({'instance': self})
)
get_rendered_deleted_date_time.short_description = _(
'Date and time trashed'
)
def invalidate_cache(self):
for document_version in self.versions.all():
document_version.invalidate_cache()
@@ -285,6 +306,20 @@ class DuplicatedDocument(models.Model):
return force_text(self.document)
class DuplicatedDocumentProxy(Document):
class Meta:
proxy = True
verbose_name = _('Duplicated document')
verbose_name_plural = _('Duplicated documents')
def get_duplicate_count(self, user):
queryset = AccessControlList.objects.filter_by_access(
permission=permission_document_view, user=user,
queryset=self.get_duplicates()
)
return queryset.count()
@python_2_unicode_compatible
class FavoriteDocument(models.Model):
"""

View File

@@ -87,10 +87,11 @@ class DocumentType(models.Model):
def get_document_count(self, user):
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, user, queryset=self.documents
permission=permission_document_view, user=user,
queryset=self.documents
)
return queryset.count()
get_document_count.short_description = _('Documents')
def natural_key(self):
return (self.label,)

View File

@@ -181,6 +181,10 @@ class DocumentVersion(models.Model):
)
raise
def get_page_count(self):
return self.pages.count()
get_page_count.short_description = _('Pages')
def get_rendered_string(self, preserve_extension=False):
if preserve_extension:
filename, extension = os.path.splitext(self.document.label)
@@ -196,6 +200,7 @@ class DocumentVersion(models.Model):
return Template('{{ instance.timestamp }}').render(
context=Context({'instance': self})
)
get_rendered_timestamp.short_description = _('Date and time')
def natural_key(self):
return (self.checksum, self.document.natural_key())

View File

@@ -20,7 +20,6 @@ from mayan.apps.common.generics import (
SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView
)
from mayan.apps.common.mixins import MultipleInstanceActionMixin
from mayan.apps.common.utils import encapsulate
from mayan.apps.converter.models import Transformation
from mayan.apps.converter.permissions import (
permission_transformation_delete, permission_transformation_edit
@@ -259,12 +258,7 @@ class DocumentDuplicatesListView(DocumentListView):
return context
def get_object_list(self):
try:
return DuplicatedDocument.objects.get(
document=self.get_document()
).documents.all()
except DuplicatedDocument.DoesNotExist:
return Document.objects.none()
return self.get_document().get_duplicates()
class DocumentEditView(SingleObjectEditView):
@@ -842,16 +836,6 @@ class DuplicatedDocumentListView(DocumentListView):
context = super(DuplicatedDocumentListView, self).get_extra_context()
context.update(
{
'extra_columns': (
{
'name': _('Duplicates'),
'attribute': encapsulate(
lambda document: DuplicatedDocument.objects.get(
document=document
).documents.count()
)
},
),
'no_results_icon': icon_duplicated_document_list,
'no_results_text': _(
'Duplicates are documents that are composed of the exact '

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django import forms
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from .settings import (
setting_display_height, setting_display_width, setting_preview_height,
@@ -75,11 +74,3 @@ def document_link(document):
return mark_safe('<a href="%s">%s</a>' % (
document.get_absolute_url(), document)
)
def widget_document_page_number(context):
return context['object'].pages.count()
def widget_document_version_page_number(context):
return context['object'].pages.count()

View File

@@ -18,7 +18,9 @@ from .links import (
link_notification_mark_read_all, link_user_events,
link_user_notifications_list
)
from .widgets import event_object_link, event_type_link, event_user_link
from .widgets import (
widget_event_object_link, widget_event_type_link, widget_event_user_link
)
class EventsApp(MayanAppConfig):
@@ -37,21 +39,22 @@ class EventsApp(MayanAppConfig):
User = get_user_model()
SourceColumn(
source=Action, label=_('Timestamp'), attribute='timestamp'
attribute='timestamp', is_identifier=True,
label=_('Date and time'), source=Action
)
SourceColumn(
source=Action, label=_('Actor'),
func=lambda context: event_user_link(context['object'])
func=widget_event_user_link, label=_('Actor'), source=Action
)
SourceColumn(
source=Action, label=_('Event'),
func=lambda context: event_type_link(context['object'])
func=widget_event_type_link, label=_('Event'), source=Action
)
SourceColumn(
source=Action, label=_('Action object'),
func=lambda context: event_object_link(
entry=context['object'], attribute='action_object'
func=widget_event_object_link, kwargs={
'attribute': 'action_object'
}, label=_('Action object'), source=Action
)
SourceColumn(
func=widget_event_object_link, label=_('Target'), source=Action
)
SourceColumn(
@@ -62,25 +65,27 @@ class EventsApp(MayanAppConfig):
)
SourceColumn(
source=Notification, label=_('Timestamp'),
attribute='action.timestamp'
attribute='action.timestamp', is_identifier=True,
label=_('Date and time'), source=Notification
)
SourceColumn(
source=Notification, label=_('Actor'), attribute='action.actor'
func=widget_event_user_link, kwargs={'attribute': 'action'},
label=_('Actor'), source=Notification
)
SourceColumn(
source=Notification, label=_('Event'),
func=lambda context: event_type_link(context['object'].action)
func=widget_event_type_link, kwargs={'attribute': 'action'},
label=_('Event'), source=Notification
)
SourceColumn(
func=widget_event_object_link, kwargs={
'attribute': 'action.target'
}, label=_('Target'), source=Notification
)
SourceColumn(
source=Notification, label=_('Target'),
func=lambda context: event_object_link(context['object'].action)
)
SourceColumn(
source=Notification, label=_('Seen'),
func=lambda context: TwoStateWidget(
state=context['object'].read
).render()
attribute='read', label=_('Seen'), source=Notification,
widget=TwoStateWidget
)
menu_list_facet.bind_links(

View File

@@ -32,10 +32,6 @@ def get_kwargs_factory(variable_name):
return get_kwargs
def get_notification_count(context):
return context['request'].user.notifications.filter(read=False).count()
link_current_user_events = Link(
icon_class=icon_events_user_list, text=_('My events'),
view='events:current_user_events'

View File

@@ -13,7 +13,6 @@ from actstream.models import Action, any_stream
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.generics import FormView, SimpleView
from mayan.apps.common.utils import encapsulate
from mayan.apps.common.views import SingleObjectListView
from .classes import EventType, ModelEventType
@@ -26,7 +25,6 @@ from .icons import (
from .links import link_event_types_subscriptions_list
from .models import StoredEventType
from .permissions import permission_events_view
from .widgets import event_object_link
class EventListView(SingleObjectListView):
@@ -34,14 +32,6 @@ class EventListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Target'),
'attribute': encapsulate(
lambda entry: event_object_link(entry)
)
},
),
'hide_object': True,
'title': _('Events'),
}
@@ -270,14 +260,6 @@ class UserEventListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Target'),
'attribute': encapsulate(
lambda entry: event_object_link(entry)
)
},
),
'hide_object': True,
'no_results_icon': icon_events_user_list,
'no_results_text': _(
@@ -308,14 +290,6 @@ class CurrentUserEventListView(UserEventListView):
class VerbEventListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Target'),
'attribute': encapsulate(
lambda entry: event_object_link(entry)
)
},
),
'hide_object': True,
'title': _(
'Events of type: %s'

View File

@@ -5,15 +5,18 @@ from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from mayan.apps.common.utils import resolve_attribute
from .classes import EventType
def event_object_link(entry, attribute='target'):
def widget_event_object_link(context, attribute='target'):
entry = context['object']
label = ''
url = '#'
obj_type = ''
obj = getattr(entry, attribute)
obj = resolve_attribute(obj=entry, attribute=attribute)
if obj:
obj_type = '{}: '.format(obj._meta.verbose_name)
@@ -28,7 +31,12 @@ def event_object_link(entry, attribute='target'):
)
def event_type_link(entry):
def widget_event_type_link(context, attribute=None):
entry = context['object']
if attribute:
entry = getattr(entry, attribute)
return mark_safe(
'<a href="%(url)s">%(label)s</a>' % {
'url': reverse('events:events_by_verb', kwargs={'verb': entry.verb}),
@@ -37,7 +45,12 @@ def event_type_link(entry):
)
def event_user_link(entry):
def widget_event_user_link(context, attribute=None):
entry = context['object']
if attribute:
entry = getattr(entry, attribute)
if entry.actor == entry.target:
return _('System')
else:

View File

@@ -121,16 +121,17 @@ class FileMetadataApp(MayanAppConfig):
model=DocumentTypeSettings, related='document_type',
)
SourceColumn(source=FileMetadataEntry, attribute='key')
SourceColumn(source=FileMetadataEntry, attribute='value')
SourceColumn(attribute='key', source=FileMetadataEntry)
SourceColumn(attribute='value', source=FileMetadataEntry)
SourceColumn(
source=DocumentVersionDriverEntry, attribute='driver'
attribute='driver', source=DocumentVersionDriverEntry
)
SourceColumn(
source=DocumentVersionDriverEntry, attribute='driver__internal_name'
attribute='driver__internal_name',
source=DocumentVersionDriverEntry
)
SourceColumn(
source=DocumentVersionDriverEntry, attribute='get_attribute_count'
attribute='get_attribute_count', source=DocumentVersionDriverEntry
)
app.conf.task_queues.append(

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-22 09:31
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('file_metadata', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='filemetadataentry',
name='key',
field=models.CharField(db_index=True, help_text='Name of the file metadata entry.', max_length=255, verbose_name='Key'),
),
migrations.AlterField(
model_name='filemetadataentry',
name='value',
field=models.CharField(db_index=True, help_text='Value of the file metadata entry.', max_length=255, verbose_name='Value'),
),
]

View File

@@ -57,28 +57,22 @@ class LinkingApp(MayanAppConfig):
)
SourceColumn(
source=ResolvedSmartLink, label=_('Label'),
func=lambda context: context['object'].get_label_for(
document=context['document']
)
), label=_('Label'), source=ResolvedSmartLink
)
SourceColumn(
source=SmartLink, label=_('Dynamic label'),
attribute='dynamic_label'
attribute='label', is_identifier=True, source=SmartLink
)
SourceColumn(attribute='dynamic_label', source=SmartLink)
SourceColumn(
source=SmartLink, label=_('Enabled'),
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render()
attribute='enabled', source=SmartLink, widget=TwoStateWidget
)
SourceColumn(
source=SmartLinkCondition, label=_('Enabled'),
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render()
attribute='enabled', source=SmartLinkCondition,
widget=TwoStateWidget
)
menu_facet.bind_links(

View File

@@ -139,7 +139,7 @@ class SmartLinkListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_smart_link_setup,
'no_results_main_link': link_smart_link_create.resolve(
context=RequestContext(request=self.request)

View File

@@ -53,30 +53,16 @@ class MailerApp(MayanAppConfig):
MailerBackend.initialize()
SourceColumn(attribute='datetime', source=LogEntry)
SourceColumn(attribute='message', source=LogEntry)
SourceColumn(attribute='label', is_identifier=True, source=UserMailer)
SourceColumn(
source=LogEntry, label=_('Date and time'), attribute='datetime'
attribute='default', source=UserMailer, widget=TwoStateWidget
)
SourceColumn(
source=LogEntry, label=_('Message'), attribute='message'
)
SourceColumn(
source=UserMailer, label=_('Label'), attribute='label'
)
SourceColumn(
source=UserMailer, label=_('Default?'),
func=lambda context: TwoStateWidget(
state=context['object'].default
).render()
)
SourceColumn(
source=UserMailer, label=_('Enabled?'),
func=lambda context: TwoStateWidget(
state=context['object'].enabled
).render()
)
SourceColumn(
source=UserMailer, label=_('Label'), attribute='backend_label'
attribute='enabled', source=UserMailer, widget=TwoStateWidget
)
SourceColumn(attribute='backend_label', source=UserMailer)
ModelPermission.register(
model=Document, permissions=(

View File

@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
class LogEntry(models.Model):
datetime = models.DateTimeField(
auto_now_add=True, editable=False, verbose_name=_('Date time')
auto_now_add=True, editable=False, verbose_name=_('Date and time')
)
message = models.TextField(
blank=True, editable=False, verbose_name=_('Message')
@@ -76,6 +76,7 @@ class UserMailer(models.Model):
property.
"""
return self.get_backend().label
backend_label.short_description = _('Backend label')
def dumps(self, data):
"""

View File

@@ -35,14 +35,40 @@ def decode_metadata_from_querystring(querystring=None):
return metadata_list
def save_metadata_list(metadata_list, document, create=False, _user=None):
def metadata_repr(metadata_list):
"""
Take a list of metadata dictionaries and associate them to a
document
Return a printable representation of a metadata list
"""
for item in metadata_list:
save_metadata(
metadata_dict=item, document=document, create=create, _user=_user
return ', '.join(metadata_repr_as_list(metadata_list))
def metadata_repr_as_list(metadata_list):
"""
Turn a list of metadata into a list of printable representations
"""
output = []
for metadata_dict in metadata_list:
try:
output.append('%s - %s' % (MetadataType.objects.get(
pk=metadata_dict['id']), metadata_dict.get('value', '')))
except Exception:
pass
return output
def set_bulk_metadata(document, metadata_dictionary):
document_type = document.document_type
document_type_metadata_types = [
document_type_metadata_type.metadata_type for document_type_metadata_type in document_type.metadata.all()
]
for metadata_type_name, value in metadata_dictionary.items():
metadata_type = MetadataType.objects.get(name=metadata_type_name)
if metadata_type in document_type_metadata_types:
DocumentMetadata.objects.get_or_create(
document=document, metadata_type=metadata_type, value=value
)
@@ -84,38 +110,12 @@ def save_metadata(metadata_dict, document, create=False, _user=None):
document_metadata.save(_user=_user)
def metadata_repr(metadata_list):
def save_metadata_list(metadata_list, document, create=False, _user=None):
"""
Return a printable representation of a metadata list
Take a list of metadata dictionaries and associate them to a
document
"""
return ', '.join(metadata_repr_as_list(metadata_list))
def metadata_repr_as_list(metadata_list):
"""
Turn a list of metadata into a list of printable representations
"""
output = []
for metadata_dict in metadata_list:
try:
output.append('%s - %s' % (MetadataType.objects.get(
pk=metadata_dict['id']), metadata_dict.get('value', '')))
except Exception:
pass
return output
def set_bulk_metadata(document, metadata_dictionary):
document_type = document.document_type
document_type_metadata_types = [
document_type_metadata_type.metadata_type for document_type_metadata_type in document_type.metadata.all()
]
for metadata_type_name, value in metadata_dictionary.items():
metadata_type = MetadataType.objects.get(name=metadata_type_name)
if metadata_type in document_type_metadata_types:
DocumentMetadata.objects.get_or_create(
document=document, metadata_type=metadata_type, value=value
for item in metadata_list:
save_metadata(
metadata_dict=item, document=document, create=create, _user=_user
)

View File

@@ -54,7 +54,7 @@ from .permissions import (
)
from .queues import * # NOQA
from .search import metadata_type_search # NOQA
from .widgets import get_metadata_string
from .widgets import widget_get_metadata_string
logger = logging.getLogger(__name__)
@@ -144,27 +144,27 @@ class MetadataApp(MayanAppConfig):
)
SourceColumn(
source=Document, label=_('Metadata'),
func=lambda context: get_metadata_string(context['object'])
func=widget_get_metadata_string, source=Document
)
SourceColumn(
source=DocumentPageSearchResult, label=_('Metadata'),
func=lambda context: get_metadata_string(
context['object'].document
)
func=widget_get_metadata_string, kwargs={'attribute': 'document'},
source=DocumentPageSearchResult,
)
SourceColumn(
source=DocumentMetadata, label=_('Value'),
attribute='value'
attribute='metadata_type', is_identifier=True,
source=DocumentMetadata
)
SourceColumn(attribute='value', source=DocumentMetadata)
SourceColumn(
attribute='is_required', source=DocumentMetadata,
widget=TwoStateWidget
)
SourceColumn(
source=DocumentMetadata, label=_('Required'),
func=lambda context: TwoStateWidget(
state=context['object'].is_required
).render()
attribute='label', is_identifier=True, source=MetadataType
)
SourceColumn(attribute='name', source=MetadataType)
app.conf.task_queues.append(
Queue('metadata', Exchange('metadata'), routing_key='metadata'),

View File

@@ -265,6 +265,7 @@ class DocumentMetadata(models.Model):
return self.metadata_type.get_required_for(
document_type=self.document.document_type
)
is_required.fget.short_description = _('Required')
def save(self, *args, **kwargs):
if self.metadata_type.pk not in self.document.document_type.metadata.values_list('metadata_type', flat=True):

View File

@@ -402,7 +402,7 @@ class DocumentMetadataListView(SingleObjectListView):
def get_extra_context(self):
document = self.get_document()
return {
'hide_link': True,
'hide_object': True,
'object': document,
'no_results_icon': icon_metadata,
'no_results_main_link': link_metadata_add.resolve(
@@ -618,13 +618,7 @@ class MetadataTypeListView(SingleObjectListView):
def get_extra_context(self):
return {
'extra_columns': (
{
'name': _('Internal name'),
'attribute': 'name',
},
),
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_metadata,
'no_results_main_link': link_setup_metadata_type_create.resolve(
context=RequestContext(request=self.request)

View File

@@ -1,17 +1,26 @@
from __future__ import unicode_literals
from django.utils.html import format_html_join
from django.utils.translation import ugettext_lazy as _
def get_metadata_string(document):
def widget_get_metadata_string(context, attribute=None):
"""
Return a formated representation of a document's metadata values
"""
obj = context['object']
if attribute:
obj = getattr(context['object'], attribute)
return format_html_join(
'\n', '<div class="metadata-display"><b>{}: </b><span data-metadata-type="{}" data-pk="{}">{}</span></div>',
(
(
document_metadata.metadata_type, document_metadata.metadata_type_id, document_metadata.id, document_metadata.value
) for document_metadata in document.metadata.all()
) for document_metadata in obj.metadata.all()
)
)
widget_get_metadata_string.short_description = _('Metadata')

View File

@@ -12,6 +12,7 @@ from mayan.apps.acls.permissions import (
from mayan.apps.common import (
MayanAppConfig, menu_list_facet, menu_object, menu_secondary, menu_setup
)
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.navigation import SourceColumn
from .links import (
@@ -45,16 +46,18 @@ class MOTDApp(MayanAppConfig):
permission_message_view
)
)
SourceColumn(
source=Message, label=_('Enabled'), attribute='enabled'
attribute='label', is_identifier=True, source=Message
)
SourceColumn(
source=Message, label=_('Start date time'),
func=lambda context: context['object'].start_datetime or _('None')
attribute='enabled', source=Message, widget=TwoStateWidget
)
SourceColumn(
source=Message, label=_('End date time'),
func=lambda context: context['object'].end_datetime or _('None')
attribute='start_datetime', empty_value=_('None'), source=Message
)
SourceColumn(
attribute='end_datetime', empty_value=_('None'), source=Message
)
menu_list_facet.bind_links(

View File

@@ -65,7 +65,7 @@ class MessageListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_message_list,
'no_results_main_link': link_message_create.resolve(
context=RequestContext(request=self.request)

View File

@@ -15,10 +15,13 @@ from django.template import VariableDoesNotExist, Variable
from django.template.defaulttags import URLNode
from django.urls import Resolver404, resolve
from django.utils.encoding import force_str, force_text
from django.utils.translation import ugettext_lazy as _
from mayan.apps.common.utils import resolve_attribute
from mayan.apps.permissions import Permission
from .exceptions import NavigationError
logger = logging.getLogger(__name__)
@@ -527,10 +530,11 @@ class SourceColumn(object):
else:
return result
def __init__(self, source, label=None, attribute=None, func=None, kwargs=None, order=None, is_identifier=False):
def __init__(self, source, attribute=None, empty_value=None, func=None, is_identifier=False, kwargs=None, label=None, order=None, widget=None):
self.source = source
self._label = label
self.attribute = attribute
self.empty_value = empty_value
self.func = func
self.kwargs = kwargs or {}
self.order = order or 0
@@ -538,12 +542,21 @@ class SourceColumn(object):
self.__class__._registry.setdefault(source, [])
self.__class__._registry[source].append(self)
self.label = None
self.widget = widget
if not attribute and not func:
raise NavigationError(
'Must provide either an attribute or a function'
)
self._calculate_label()
def _calculate_label(self):
if not self._label:
if self.attribute:
try:
attribute = resolve_attribute(obj=self.source, attribute=self.attribute)
self._label = getattr(attribute, 'short_description')
except AttributeError:
name, model = SourceColumn.get_attribute_recursive(
attribute=self.attribute, model=self.source._meta.model
)
@@ -551,7 +564,7 @@ class SourceColumn(object):
name=name, model=model
)
else:
self._label = 'Function'
self._label = getattr(self.func, 'short_description', _('Unnamed function'))
self.label = self._label
@@ -564,6 +577,16 @@ class SourceColumn(object):
elif self.func:
result = self.func(context=context, **self.kwargs)
if self.widget:
widget_instance = self.widget()
return widget_instance.render(name='asd', value=result)
if not result:
if self.empty_value:
return self.empty_value
else:
return result
else:
return result

View File

@@ -0,0 +1,5 @@
from __future__ import unicode_literals
class NavigationError(Exception):
"""Base navigation app exception"""

View File

@@ -110,16 +110,16 @@ class OCRApp(MayanAppConfig):
)
SourceColumn(
source=DocumentVersionOCRError, label=_('Document'),
func=lambda context: document_link(context['object'].document_version.document)
func=lambda context: document_link(
context['object'].document_version.document
), label=_('Document'), source=DocumentVersionOCRError
)
SourceColumn(
source=DocumentVersionOCRError, label=_('Added'),
attribute='datetime_submitted'
attribute='datetime_submitted', label=_('Date and time'),
source=DocumentVersionOCRError
)
SourceColumn(
source=DocumentVersionOCRError, label=_('Result'),
attribute='result'
attribute='result', source=DocumentVersionOCRError
)
app.conf.task_queues.append(

View File

@@ -11,6 +11,7 @@ from mayan.apps.common import (
menu_secondary, menu_setup
)
from mayan.apps.common.signals import perform_upgrade
from mayan.apps.navigation import SourceColumn
from .handlers import handler_purge_permissions
from .links import (
@@ -48,6 +49,8 @@ class PermissionsApp(MayanAppConfig):
)
)
SourceColumn(attribute='label', is_identifier=True, source=Role)
menu_list_facet.bind_links(
links=(
link_acl_list, link_role_groups, link_role_permissions,

View File

@@ -199,7 +199,7 @@ class RoleListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_role_list,
'no_results_main_link': link_role_create.resolve(
context=RequestContext(request=self.request)

View File

@@ -37,4 +37,3 @@ class ClassesTestCase(BaseTestCase):
setting_value = setting_paginate_by.value
os.environ.pop('MAYAN_{}'.format(TEST_SETTING_NAME))
self.assertEqual(setting_value, TEST_SETTING_VALUE)

View File

@@ -101,7 +101,7 @@ class TagsApp(MayanAppConfig):
)
SourceColumn(
attribute='label', source=DocumentTag,
attribute='label', is_identifier=True, source=DocumentTag,
)
SourceColumn(
attribute='get_preview_widget', source=DocumentTag

View File

@@ -33,16 +33,12 @@ class TaskManagerApp(MayanAppConfig):
source=CeleryQueue, label=_('Name'), attribute='name'
)
SourceColumn(
source=CeleryQueue, label=_('Default queue?'),
func=lambda context: TwoStateWidget(
state=context['object'].is_default_queue
).render()
attribute='is_default_queue', label=_('Default queue?'),
source=CeleryQueue, widget=TwoStateWidget
)
SourceColumn(
source=CeleryQueue, label=_('Is transient?'),
func=lambda context: TwoStateWidget(
state=context['object'].is_transient
).render()
attribute='is_transient', label=_('Is transient?'),
source=CeleryQueue, widget=TwoStateWidget
)
SourceColumn(
source=Task, label=_('Type'), attribute='task_type'
@@ -54,12 +50,12 @@ class TaskManagerApp(MayanAppConfig):
source=Task, label=_('Host'),
func=lambda context: context['object'].kwargs['hostname']
)
SourceColumn(
source=Task, label=_('Acknowledged'),
func=lambda context: TwoStateWidget(
state=context['object'].kwargs['acknowledged']
).render()
)
#SourceColumn(
# source=Task, label=_('Acknowledged'),
# func=lambda context: TwoStateWidget(
# state=context['object'].kwargs['acknowledged']
# ).render()
#)
SourceColumn(
source=Task, label=_('Arguments'),
func=lambda context: context['object'].kwargs['args']

View File

@@ -31,20 +31,7 @@ from .permissions import (
permission_user_view
)
from .search import * # NOQA
def get_groups():
Group = apps.get_model(app_label='auth', model_name='Group')
return ','.join([group.name for group in Group.objects.all()])
def get_users():
return ','.join(
[
user.get_full_name() or user.username
for user in get_user_model().objects.all()
]
)
from .utils import get_groups, get_users
class UserManagementApp(MayanAppConfig):
@@ -89,27 +76,27 @@ class UserManagementApp(MayanAppConfig):
permission_user_view
)
)
SourceColumn(attribute='name', is_identifier=True, source=Group)
SourceColumn(
source=Group, label=_('Users'), attribute='user_set.count'
attribute='user_set.count', label=_('Users'), source=Group
)
SourceColumn(attribute='username', is_identifier=True, source=User)
SourceColumn(
source=User, label=_('Full name'), attribute='get_full_name'
attribute='get_full_name', label=_('Full name'), source=User
)
SourceColumn(attribute='email', label=_('Email'), source=User)
SourceColumn(
attribute='is_active', label=_('Active'), source=User,
widget=TwoStateWidget
)
SourceColumn(
source=User, label=_('Email'), attribute='email'
attribute='has_usable_password', source=User, widget=TwoStateWidget
)
SourceColumn(
source=User, label=_('Active'),
func=lambda context: TwoStateWidget(
state=context['object'].is_active
).render()
)
SourceColumn(
source=User, label=_('Has usable password?'),
func=lambda context: TwoStateWidget(
state=context['object'].has_usable_password()
).render()
attribute='has_usable_password', label=_('Has usable password?'),
source=User, widget=TwoStateWidget
)
menu_list_facet.bind_links(

View File

@@ -0,0 +1,18 @@
from __future__ import unicode_literals
from django.apps import apps
from django.contrib.auth import get_user_model
def get_groups():
Group = apps.get_model(app_label='auth', model_name='Group')
return ','.join([group.name for group in Group.objects.all()])
def get_users():
return ','.join(
[
user.get_full_name() or user.username
for user in get_user_model().objects.all()
]
)

View File

@@ -55,7 +55,7 @@ class GroupListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_group_setup,
'no_results_main_link': link_group_create.resolve(
context=RequestContext(request=self.request)
@@ -261,7 +261,7 @@ class UserListView(SingleObjectListView):
def get_extra_context(self):
return {
'hide_link': True,
'hide_object': True,
'no_results_icon': icon_user_setup,
'no_results_main_link': link_user_create.resolve(
context=RequestContext(request=self.request)