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:
@@ -211,6 +211,9 @@
|
|||||||
clicking on the title of the body of the card, not just on
|
clicking on the title of the body of the card, not just on
|
||||||
the checkbox next to the title.
|
the checkbox next to the title.
|
||||||
- Event handler to highlight panels when selected.
|
- Event handler to highlight panels when selected.
|
||||||
|
- Improve duplicated document display.
|
||||||
|
- Filter document duplicted count by access.
|
||||||
|
|
||||||
|
|
||||||
3.1.9 (2018-11-01)
|
3.1.9 (2018-11-01)
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
{{ object }}
|
{{ object }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -98,7 +98,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>{% 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 %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for column in extra_columns %}
|
{% for column in extra_columns %}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ from pure_pagination.mixins import PaginationMixin
|
|||||||
|
|
||||||
from .forms import ChoiceForm
|
from .forms import ChoiceForm
|
||||||
from .icons import icon_assign_remove_add, icon_assign_remove_remove
|
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 (
|
from .mixins import (
|
||||||
DeleteExtraDataMixin, DynamicFormViewMixin, ExtraContextMixin,
|
DeleteExtraDataMixin, DynamicFormViewMixin, ExtraContextMixin,
|
||||||
FormExtraKwargsMixin, MultipleObjectMixin, ObjectActionMixin,
|
FormExtraKwargsMixin, MultipleObjectMixin, ObjectActionMixin,
|
||||||
@@ -500,14 +504,17 @@ class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectList
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(SingleObjectListView, self).get_context_data(**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:
|
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
|
return context
|
||||||
|
|
||||||
def get_paginate_by(self, queryset):
|
def get_paginate_by(self, queryset):
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ icon_menu_about = Icon(
|
|||||||
icon_menu_user = Icon(
|
icon_menu_user = Icon(
|
||||||
driver_name='fontawesome', symbol='user-circle'
|
driver_name='fontawesome', symbol='user-circle'
|
||||||
)
|
)
|
||||||
icon_object_error_list_with_icon = Icon(
|
icon_object_error_list = Icon(
|
||||||
driver_name='fontawesome', symbol='lock'
|
driver_name='fontawesome', symbol='exclamation-triangle'
|
||||||
)
|
)
|
||||||
icon_ok = Icon(
|
icon_ok = Icon(
|
||||||
driver_name='fontawesome', symbol='check'
|
driver_name='fontawesome', symbol='check'
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from .icons import (
|
|||||||
icon_about, icon_check_version, icon_current_user_details,
|
icon_about, icon_check_version, icon_current_user_details,
|
||||||
icon_current_user_edit, icon_current_user_locale_profile_details,
|
icon_current_user_edit, icon_current_user_locale_profile_details,
|
||||||
icon_current_user_locale_profile_edit, icon_documentation, icon_forum,
|
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
|
icon_setup, icon_source_code, icon_support, icon_tools
|
||||||
)
|
)
|
||||||
from .permissions_runtime import permission_error_log_view
|
from .permissions_runtime import permission_error_log_view
|
||||||
@@ -65,6 +65,7 @@ link_documentation = Link(
|
|||||||
text=_('Documentation'), url='https://docs.mayan-edms.com'
|
text=_('Documentation'), url='https://docs.mayan-edms.com'
|
||||||
)
|
)
|
||||||
link_object_error_list = Link(
|
link_object_error_list = Link(
|
||||||
|
icon_class=icon_object_error_list,
|
||||||
kwargs=get_kwargs_factory('resolved_object'),
|
kwargs=get_kwargs_factory('resolved_object'),
|
||||||
permissions=(permission_error_log_view,), text=_('Errors'),
|
permissions=(permission_error_log_view,), text=_('Errors'),
|
||||||
view='common:object_error_list',
|
view='common:object_error_list',
|
||||||
@@ -74,12 +75,6 @@ link_object_error_list_clear = Link(
|
|||||||
permissions=(permission_error_log_view,), text=_('Clear all'),
|
permissions=(permission_error_log_view,), text=_('Clear all'),
|
||||||
view='common:object_error_list_clear',
|
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(
|
link_forum = Link(
|
||||||
icon_class=icon_forum, tags='new_window', text=_('Forum'),
|
icon_class=icon_forum, tags='new_window', text=_('Forum'),
|
||||||
url='https://forum.mayan-edms.com'
|
url='https://forum.mayan-edms.com'
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ MESSAGE_SQLITE_WARNING = _(
|
|||||||
'for development and testing, not for production.'
|
'for development and testing, not for production.'
|
||||||
)
|
)
|
||||||
PYPI_URL = 'https://pypi.python.org/pypi'
|
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_DAYS = 'days'
|
||||||
TIME_DELTA_UNIT_HOURS = 'hours'
|
TIME_DELTA_UNIT_HOURS = 'hours'
|
||||||
TIME_DELTA_UNIT_MINUTES = 'minutes'
|
TIME_DELTA_UNIT_MINUTES = 'minutes'
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import types
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
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 _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
import mayan
|
import mayan
|
||||||
from mayan.apps.common.compat import dict_type, dictionary_type
|
|
||||||
|
|
||||||
from .exceptions import NotLatestVersion, UnknownLatestVersion
|
from .exceptions import NotLatestVersion, UnknownLatestVersion
|
||||||
from .literals import DJANGO_SQLITE_BACKEND, MAYAN_PYPI_NAME, PYPI_URL
|
from .literals import DJANGO_SQLITE_BACKEND, MAYAN_PYPI_NAME, PYPI_URL
|
||||||
@@ -155,9 +153,9 @@ def resolve_attribute(obj, attribute, kwargs=None):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Try as a related model field
|
# Try as a related model field
|
||||||
if LOOKUP_SEP in attribute:
|
if LOOKUP_SEP in attribute:
|
||||||
attrib = attribute.replace(LOOKUP_SEP, '.')
|
attribute_replaced = attribute.replace(LOOKUP_SEP, '.')
|
||||||
return resolve_attribute(
|
return resolve_attribute(
|
||||||
obj=obj, attribute=attribute, kwargs=kwargs
|
obj=obj, attribute=attribute_replaced, kwargs=kwargs
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from .generics import ( # NOQA
|
|||||||
SingleObjectDownloadView, SingleObjectDynamicFormCreateView,
|
SingleObjectDownloadView, SingleObjectDynamicFormCreateView,
|
||||||
SingleObjectDynamicFormEditView, SingleObjectEditView, SingleObjectListView
|
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 .menus import menu_setup, menu_tools
|
||||||
from .permissions_runtime import permission_error_log_view
|
from .permissions_runtime import permission_error_log_view
|
||||||
from .settings import setting_home_view
|
from .settings import setting_home_view
|
||||||
@@ -211,6 +211,15 @@ class ObjectErrorLogEntryListView(SingleObjectListView):
|
|||||||
{'name': _('Result'), 'attribute': 'result'},
|
{'name': _('Result'), 'attribute': 'result'},
|
||||||
),
|
),
|
||||||
'hide_object': True,
|
'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(),
|
'object': self.get_object(),
|
||||||
'title': _('Error log entries for: %s' % self.get_object()),
|
'title': _('Error log entries for: %s' % self.get_object()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,18 +89,17 @@ class TextAreaDiv(forms.widgets.Widget):
|
|||||||
|
|
||||||
|
|
||||||
class TwoStateWidget(object):
|
class TwoStateWidget(object):
|
||||||
def __init__(self, state, center=False, icon_ok=None, icon_fail=None):
|
def __init__(self, center=False, icon_ok=None, icon_fail=None):
|
||||||
self.state = state
|
|
||||||
self.icon_ok = icon_ok or default_icon_ok
|
self.icon_ok = icon_ok or default_icon_ok
|
||||||
self.icon_fail = icon_fail or default_icon_fail
|
self.icon_fail = icon_fail or default_icon_fail
|
||||||
self.center = center
|
self.center = center
|
||||||
|
|
||||||
def render(self):
|
def render(self, name=None, value=None):
|
||||||
center_class = ''
|
center_class = ''
|
||||||
if self.center:
|
if self.center:
|
||||||
center_class = 'text-center'
|
center_class = 'text-center'
|
||||||
|
|
||||||
if self.state:
|
if value:
|
||||||
return mark_safe(
|
return mark_safe(
|
||||||
'<div class="{} text-success">{}</div>'.format(
|
'<div class="{} text-success">{}</div>'.format(
|
||||||
center_class, self.icon_ok.render()
|
center_class, self.icon_ok.render()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mayan.apps.common import MayanAppConfig
|
from mayan.apps.common import MayanAppConfig
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.db import models
|
|
||||||
from django.template import loader
|
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):
|
class Dashboard(object):
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.template import Context, Library
|
from django.template import Library
|
||||||
|
|
||||||
from ..classes import Dashboard
|
from ..classes import Dashboard
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
@@ -44,22 +44,24 @@ class DjangoGPGApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(source=Key, label=_('Key ID'), attribute='key_id')
|
SourceColumn(attribute='key_id', is_identifier=True, source=Key)
|
||||||
SourceColumn(source=Key, label=_('User ID'), attribute='user_id')
|
SourceColumn(attribute='user_id', source=Key)
|
||||||
|
|
||||||
SourceColumn(source=KeyStub, label=_('Key ID'), attribute='key_id')
|
|
||||||
SourceColumn(source=KeyStub, label=_('Type'), attribute='key_type')
|
|
||||||
SourceColumn(
|
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(
|
SourceColumn(
|
||||||
source=KeyStub, label=_('Expiration date'),
|
attribute='expires', empty_value=_('No expiration'),
|
||||||
func=lambda context: context['object'].expires or _('No expiration')
|
label=_('Expiration date'), source=KeyStub
|
||||||
)
|
)
|
||||||
SourceColumn(source=KeyStub, label=_('Length'), attribute='length')
|
SourceColumn(attribute='length', label=_('Length'), source=KeyStub)
|
||||||
SourceColumn(
|
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,))
|
menu_object.bind_links(links=(link_key_detail,), sources=(Key,))
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import shutil
|
|||||||
|
|
||||||
import gnupg
|
import gnupg
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mayan.apps.common.utils import mkdtemp
|
from mayan.apps.common.utils import mkdtemp
|
||||||
|
|
||||||
|
|
||||||
@@ -149,6 +151,7 @@ class KeyStub(object):
|
|||||||
@property
|
@property
|
||||||
def key_id(self):
|
def key_id(self):
|
||||||
return self.fingerprint[-8:]
|
return self.fingerprint[-8:]
|
||||||
|
key_id.fget.short_description = _('Key ID')
|
||||||
|
|
||||||
|
|
||||||
class SignatureVerification(object):
|
class SignatureVerification(object):
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class Key(models.Model):
|
|||||||
Short form key ID (using the first 8 characters).
|
Short form key ID (using the first 8 characters).
|
||||||
"""
|
"""
|
||||||
return self.fingerprint[-8:]
|
return self.fingerprint[-8:]
|
||||||
|
key_id.fget.short_description = _('Key ID')
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
import_results, key_info = gpg_backend.import_and_list_keys(
|
import_results, key_info = gpg_backend.import_and_list_keys(
|
||||||
|
|||||||
@@ -87,9 +87,7 @@ class DocumentIndexingApp(MayanAppConfig):
|
|||||||
SourceColumn(attribute='label', is_identifier=True, source=Index)
|
SourceColumn(attribute='label', is_identifier=True, source=Index)
|
||||||
SourceColumn(attribute='slug', source=Index)
|
SourceColumn(attribute='slug', source=Index)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: TwoStateWidget(
|
attribute='enabled', source=Index, widget=TwoStateWidget
|
||||||
state=context['object'].enabled
|
|
||||||
).render(), label=_('Enabled'), source=Index
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
@@ -111,15 +109,12 @@ class DocumentIndexingApp(MayanAppConfig):
|
|||||||
label=_('Level'), source=IndexTemplateNode
|
label=_('Level'), source=IndexTemplateNode
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: TwoStateWidget(
|
attribute='enabled', source=IndexTemplateNode,
|
||||||
state=context['object'].enabled
|
widget=TwoStateWidget
|
||||||
).render(), label=_('Enabled'), source=IndexTemplateNode
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: TwoStateWidget(
|
attribute='link_documents', source=IndexTemplateNode,
|
||||||
state=context['object'].link_documents
|
widget=TwoStateWidget
|
||||||
).render(), label=_('Has document links?'),
|
|
||||||
source=IndexTemplateNode,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .tasks import (
|
from .tasks import task_unverify_key_signatures, task_verify_key_signatures
|
||||||
task_unverify_key_signatures, task_verify_key_signatures,
|
|
||||||
task_verify_missing_embedded_signature
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handler_unverify_key_signatures(sender, **kwargs):
|
def handler_unverify_key_signatures(sender, **kwargs):
|
||||||
|
|||||||
@@ -135,16 +135,14 @@ class DocumentStatesApp(MayanAppConfig):
|
|||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='label', is_identifier=True, source=Workflow
|
attribute='label', is_identifier=True, source=Workflow
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(attribute='internal_name', source=Workflow)
|
||||||
attribute='internal_name', source=Workflow
|
SourceColumn(attribute='get_initial_state', source=Workflow)
|
||||||
)
|
|
||||||
SourceColumn(
|
|
||||||
attribute='get_initial_state', source=Workflow,
|
|
||||||
)
|
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='get_current_state', source=WorkflowInstance
|
attribute='workflow', is_identifier=True,
|
||||||
|
source=WorkflowInstance
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='get_current_state', source=WorkflowInstance)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='get_last_transition_user', source=WorkflowInstance
|
attribute='get_last_transition_user', source=WorkflowInstance
|
||||||
)
|
)
|
||||||
@@ -163,57 +161,40 @@ class DocumentStatesApp(MayanAppConfig):
|
|||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='get_rendered_datetime', source=WorkflowInstanceLogEntry
|
attribute='get_rendered_datetime', source=WorkflowInstanceLogEntry
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(attribute='user', source=WorkflowInstanceLogEntry)
|
||||||
attribute='user', source=WorkflowInstanceLogEntry
|
SourceColumn(attribute='transition', source=WorkflowInstanceLogEntry)
|
||||||
)
|
SourceColumn(attribute='comment', source=WorkflowInstanceLogEntry)
|
||||||
SourceColumn(
|
|
||||||
attribute='transition', source=WorkflowInstanceLogEntry
|
|
||||||
)
|
|
||||||
SourceColumn(
|
|
||||||
attribute='comment', source=WorkflowInstanceLogEntry
|
|
||||||
)
|
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowState, label=_('Is initial state?'),
|
attribute='label', is_identifier=True, source=WorkflowState
|
||||||
func=lambda context: TwoStateWidget(
|
|
||||||
state=context['object'].initial
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowState, label=_('Completion'), attribute='completion'
|
attribute='initial', source=WorkflowState, widget=TwoStateWidget
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='completion', source=WorkflowState)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowStateAction, label=_('Label'), attribute='label'
|
attribute='label', is_identifier=True, source=WorkflowStateAction
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowStateAction, label=_('Enabled?'),
|
attribute='enabled', source=WorkflowStateAction,
|
||||||
func=lambda context: TwoStateWidget(
|
widget=TwoStateWidget
|
||||||
state=context['object'].enabled
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowStateAction, label=_('When?'),
|
attribute='get_when_display', label=_('When?'),
|
||||||
attribute='get_when_display'
|
source=WorkflowStateAction
|
||||||
)
|
|
||||||
SourceColumn(
|
|
||||||
source=WorkflowStateAction, label=_('Action type'),
|
|
||||||
attribute='get_class_label'
|
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='get_class_label', source=WorkflowStateAction)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowTransition, label=_('Origin state'),
|
attribute='label', is_identifier=True, source=WorkflowTransition
|
||||||
attribute='origin_state'
|
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='origin_state', source=WorkflowTransition)
|
||||||
|
SourceColumn(attribute='destination_state', source=WorkflowTransition)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=WorkflowTransition, label=_('Destination state'),
|
|
||||||
attribute='destination_state'
|
|
||||||
)
|
|
||||||
SourceColumn(
|
|
||||||
source=WorkflowTransition, label=_('Triggers'),
|
|
||||||
func=lambda context: widget_transition_events(
|
func=lambda context: widget_transition_events(
|
||||||
transition=context['object']
|
transition=context['object']
|
||||||
)
|
), label=_('Triggers'), source=WorkflowTransition
|
||||||
)
|
)
|
||||||
|
|
||||||
app.conf.task_queues.extend(
|
app.conf.task_queues.extend(
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ class WorkflowStateAction(models.Model):
|
|||||||
|
|
||||||
def get_class_label(self):
|
def get_class_label(self):
|
||||||
return self.get_class().label
|
return self.get_class().label
|
||||||
|
get_class_label.short_description = _('Action type')
|
||||||
|
|
||||||
def loads(self):
|
def loads(self):
|
||||||
return json.loads(self.action_data)
|
return json.loads(self.action_data)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_workflow_list,
|
'no_results_icon': icon_workflow_list,
|
||||||
'no_results_text': _(
|
'no_results_text': _(
|
||||||
'Assign workflows to the document type of this document '
|
'Assign workflows to the document type of this document '
|
||||||
@@ -96,6 +96,13 @@ class WorkflowInstanceDetailView(SingleObjectListView):
|
|||||||
return {
|
return {
|
||||||
'hide_object': True,
|
'hide_object': True,
|
||||||
'navigation_object_list': ('object', 'workflow_instance'),
|
'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,
|
'object': self.get_workflow_instance().document,
|
||||||
'title': _('Detail of workflow: %(workflow)s') % {
|
'title': _('Detail of workflow: %(workflow)s') % {
|
||||||
'workflow': self.get_workflow_instance()
|
'workflow': self.get_workflow_instance()
|
||||||
@@ -509,7 +516,7 @@ class SetupWorkflowStateListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_workflow_state,
|
'no_results_icon': icon_workflow_state,
|
||||||
'no_results_main_link': link_setup_workflow_state_create.resolve(
|
'no_results_main_link': link_setup_workflow_state_create.resolve(
|
||||||
context=RequestContext(
|
context=RequestContext(
|
||||||
@@ -624,7 +631,7 @@ class SetupWorkflowTransitionListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_workflow_transition,
|
'no_results_icon': icon_workflow_transition,
|
||||||
'no_results_main_link': link_setup_workflow_transition_create.resolve(
|
'no_results_main_link': link_setup_workflow_transition_create.resolve(
|
||||||
context=RequestContext(
|
context=RequestContext(
|
||||||
|
|||||||
@@ -104,10 +104,7 @@ from .queues import * # NOQA
|
|||||||
from .search import document_page_search, document_search # NOQA
|
from .search import document_page_search, document_search # NOQA
|
||||||
from .signals import post_version_upload
|
from .signals import post_version_upload
|
||||||
from .statistics import * # NOQA
|
from .statistics import * # NOQA
|
||||||
from .widgets import (
|
from .widgets import DocumentPageThumbnailWidget
|
||||||
DocumentPageThumbnailWidget, widget_document_page_number,
|
|
||||||
widget_document_version_page_number
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentsApp(MayanAppConfig):
|
class DocumentsApp(MayanAppConfig):
|
||||||
@@ -129,7 +126,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
DocumentType = self.get_model('DocumentType')
|
DocumentType = self.get_model('DocumentType')
|
||||||
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
|
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
|
||||||
DocumentVersion = self.get_model('DocumentVersion')
|
DocumentVersion = self.get_model('DocumentVersion')
|
||||||
DuplicatedDocument = self.get_model('DuplicatedDocument')
|
DuplicatedDocumentProxy = self.get_model('DuplicatedDocumentProxy')
|
||||||
|
|
||||||
DynamicSerializerField.add_serializer(
|
DynamicSerializerField.add_serializer(
|
||||||
klass=Document,
|
klass=Document,
|
||||||
@@ -244,9 +241,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='document_type', label=_('Type'), source=Document
|
attribute='document_type', label=_('Type'), source=Document
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(attribute='get_page_count', source=Document)
|
||||||
func=widget_document_page_number, label=_('Pages'), source=Document
|
|
||||||
)
|
|
||||||
|
|
||||||
# DocumentPage
|
# DocumentPage
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
@@ -274,9 +269,8 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: TwoStateWidget(
|
attribute='enabled', source=DocumentTypeFilename,
|
||||||
state=context['object'].enabled
|
widget=TwoStateWidget
|
||||||
).render(), label=_('Enabled'), source=DocumentTypeFilename
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# DeletedDocument
|
# DeletedDocument
|
||||||
@@ -290,41 +284,34 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
attribute='document_type', source=DeletedDocument
|
attribute='document_type', source=DeletedDocument
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='deleted_date_time', source=DeletedDocument
|
attribute='get_rendered_deleted_date_time', source=DeletedDocument
|
||||||
)
|
)
|
||||||
|
|
||||||
# DocumentVersion
|
# DocumentVersion
|
||||||
|
SourceColumn(
|
||||||
|
attribute='get_rendered_timestamp', is_identifier=True,
|
||||||
|
source=DocumentVersion
|
||||||
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: document_page_thumbnail_widget.render(
|
func=lambda context: document_page_thumbnail_widget.render(
|
||||||
instance=context['object']
|
instance=context['object']
|
||||||
), label=_('Thumbnail'), source=DocumentVersion
|
), label=_('Thumbnail'), source=DocumentVersion
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(attribute='get_page_count', source=DocumentVersion)
|
||||||
attribute='timestamp', source=DocumentVersion
|
SourceColumn(attribute='mimetype', source=DocumentVersion)
|
||||||
)
|
SourceColumn(attribute='encoding', source=DocumentVersion)
|
||||||
SourceColumn(
|
SourceColumn(attribute='comment', source=DocumentVersion)
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
# DuplicatedDocument
|
# DuplicatedDocument
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: document_page_thumbnail_widget.render(
|
func=lambda context: document_page_thumbnail_widget.render(
|
||||||
instance=context['object'].document
|
instance=context['object']
|
||||||
), label=_('Thumbnail'), source=DuplicatedDocument
|
), label=_('Thumbnail'), source=DuplicatedDocumentProxy
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
func=lambda context: context['object'].documents.count(),
|
func=lambda context: context['object'].get_duplicate_count(
|
||||||
label=_('Duplicates'), source=DuplicatedDocument
|
user=context['request'].user,
|
||||||
|
), label=_('Duplicates'), source=DuplicatedDocumentProxy
|
||||||
)
|
)
|
||||||
|
|
||||||
app.conf.beat_schedule.update(
|
app.conf.beat_schedule.update(
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ class DuplicatedDocumentManager(models.Manager):
|
|||||||
self.filter(documents=None).delete()
|
self.filter(documents=None).delete()
|
||||||
|
|
||||||
def get_duplicated_documents(self):
|
def get_duplicated_documents(self):
|
||||||
Document = apps.get_model(
|
DuplicatedDocumentProxy = apps.get_model(
|
||||||
app_label='documents', model_name='Document'
|
app_label='documents', model_name='DuplicatedDocumentProxy'
|
||||||
)
|
)
|
||||||
return Document.objects.filter(
|
return DuplicatedDocumentProxy.objects.filter(
|
||||||
pk__in=self.filter(documents__isnull=False).values_list(
|
pk__in=self.filter(documents__isnull=False).values_list(
|
||||||
'document_id', flat=True
|
'document_id', flat=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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',),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -7,12 +7,15 @@ from django.apps import apps
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.template import Context, Template
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.acls.models import AccessControlList
|
||||||
|
|
||||||
from ..events import (
|
from ..events import (
|
||||||
event_document_create, event_document_properties_edit,
|
event_document_create, event_document_properties_edit,
|
||||||
event_document_type_change,
|
event_document_type_change,
|
||||||
@@ -21,6 +24,7 @@ from ..managers import (
|
|||||||
DocumentManager, DuplicatedDocumentManager, FavoriteDocumentManager,
|
DocumentManager, DuplicatedDocumentManager, FavoriteDocumentManager,
|
||||||
PassthroughManager, RecentDocumentManager, TrashCanManager
|
PassthroughManager, RecentDocumentManager, TrashCanManager
|
||||||
)
|
)
|
||||||
|
from ..permissions import permission_document_view
|
||||||
from ..settings import setting_language
|
from ..settings import setting_language
|
||||||
from ..signals import post_document_type_change
|
from ..signals import post_document_type_change
|
||||||
|
|
||||||
@@ -77,7 +81,6 @@ class Document(models.Model):
|
|||||||
'Whether or not this document is in the trash.'
|
'Whether or not this document is in the trash.'
|
||||||
), editable=False, verbose_name=_('In trash?')
|
), editable=False, verbose_name=_('In trash?')
|
||||||
)
|
)
|
||||||
# TODO: set editable to False
|
|
||||||
deleted_date_time = models.DateTimeField(
|
deleted_date_time = models.DateTimeField(
|
||||||
blank=True, editable=True, help_text=_(
|
blank=True, editable=True, help_text=_(
|
||||||
'The server date and time when the document was moved to the '
|
'The server date and time when the document was moved to the '
|
||||||
@@ -139,6 +142,24 @@ class Document(models.Model):
|
|||||||
if latest_version:
|
if latest_version:
|
||||||
return latest_version.get_api_image_url(*args, **kwargs)
|
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):
|
def invalidate_cache(self):
|
||||||
for document_version in self.versions.all():
|
for document_version in self.versions.all():
|
||||||
document_version.invalidate_cache()
|
document_version.invalidate_cache()
|
||||||
@@ -285,6 +306,20 @@ class DuplicatedDocument(models.Model):
|
|||||||
return force_text(self.document)
|
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
|
@python_2_unicode_compatible
|
||||||
class FavoriteDocument(models.Model):
|
class FavoriteDocument(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -87,10 +87,11 @@ class DocumentType(models.Model):
|
|||||||
|
|
||||||
def get_document_count(self, user):
|
def get_document_count(self, user):
|
||||||
queryset = AccessControlList.objects.filter_by_access(
|
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()
|
return queryset.count()
|
||||||
|
get_document_count.short_description = _('Documents')
|
||||||
|
|
||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
return (self.label,)
|
return (self.label,)
|
||||||
|
|||||||
@@ -181,6 +181,10 @@ class DocumentVersion(models.Model):
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def get_page_count(self):
|
||||||
|
return self.pages.count()
|
||||||
|
get_page_count.short_description = _('Pages')
|
||||||
|
|
||||||
def get_rendered_string(self, preserve_extension=False):
|
def get_rendered_string(self, preserve_extension=False):
|
||||||
if preserve_extension:
|
if preserve_extension:
|
||||||
filename, extension = os.path.splitext(self.document.label)
|
filename, extension = os.path.splitext(self.document.label)
|
||||||
@@ -196,6 +200,7 @@ class DocumentVersion(models.Model):
|
|||||||
return Template('{{ instance.timestamp }}').render(
|
return Template('{{ instance.timestamp }}').render(
|
||||||
context=Context({'instance': self})
|
context=Context({'instance': self})
|
||||||
)
|
)
|
||||||
|
get_rendered_timestamp.short_description = _('Date and time')
|
||||||
|
|
||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
return (self.checksum, self.document.natural_key())
|
return (self.checksum, self.document.natural_key())
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ from mayan.apps.common.generics import (
|
|||||||
SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView
|
SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView
|
||||||
)
|
)
|
||||||
from mayan.apps.common.mixins import MultipleInstanceActionMixin
|
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.models import Transformation
|
||||||
from mayan.apps.converter.permissions import (
|
from mayan.apps.converter.permissions import (
|
||||||
permission_transformation_delete, permission_transformation_edit
|
permission_transformation_delete, permission_transformation_edit
|
||||||
@@ -259,12 +258,7 @@ class DocumentDuplicatesListView(DocumentListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object_list(self):
|
def get_object_list(self):
|
||||||
try:
|
return self.get_document().get_duplicates()
|
||||||
return DuplicatedDocument.objects.get(
|
|
||||||
document=self.get_document()
|
|
||||||
).documents.all()
|
|
||||||
except DuplicatedDocument.DoesNotExist:
|
|
||||||
return Document.objects.none()
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentEditView(SingleObjectEditView):
|
class DocumentEditView(SingleObjectEditView):
|
||||||
@@ -842,16 +836,6 @@ class DuplicatedDocumentListView(DocumentListView):
|
|||||||
context = super(DuplicatedDocumentListView, self).get_extra_context()
|
context = super(DuplicatedDocumentListView, self).get_extra_context()
|
||||||
context.update(
|
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_icon': icon_duplicated_document_list,
|
||||||
'no_results_text': _(
|
'no_results_text': _(
|
||||||
'Duplicates are documents that are composed of the exact '
|
'Duplicates are documents that are composed of the exact '
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from .settings import (
|
from .settings import (
|
||||||
setting_display_height, setting_display_width, setting_preview_height,
|
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>' % (
|
return mark_safe('<a href="%s">%s</a>' % (
|
||||||
document.get_absolute_url(), document)
|
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()
|
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ from .links import (
|
|||||||
link_notification_mark_read_all, link_user_events,
|
link_notification_mark_read_all, link_user_events,
|
||||||
link_user_notifications_list
|
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):
|
class EventsApp(MayanAppConfig):
|
||||||
@@ -37,21 +39,22 @@ class EventsApp(MayanAppConfig):
|
|||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Action, label=_('Timestamp'), attribute='timestamp'
|
attribute='timestamp', is_identifier=True,
|
||||||
|
label=_('Date and time'), source=Action
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Action, label=_('Actor'),
|
func=widget_event_user_link, label=_('Actor'), source=Action
|
||||||
func=lambda context: event_user_link(context['object'])
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Action, label=_('Event'),
|
func=widget_event_type_link, label=_('Event'), source=Action
|
||||||
func=lambda context: event_type_link(context['object'])
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Action, label=_('Action object'),
|
func=widget_event_object_link, kwargs={
|
||||||
func=lambda context: event_object_link(
|
'attribute': 'action_object'
|
||||||
entry=context['object'], attribute='action_object'
|
}, label=_('Action object'), source=Action
|
||||||
)
|
)
|
||||||
|
SourceColumn(
|
||||||
|
func=widget_event_object_link, label=_('Target'), source=Action
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
@@ -62,25 +65,27 @@ class EventsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Notification, label=_('Timestamp'),
|
attribute='action.timestamp', is_identifier=True,
|
||||||
attribute='action.timestamp'
|
label=_('Date and time'), source=Notification
|
||||||
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Notification, label=_('Actor'), attribute='action.actor'
|
func=widget_event_user_link, kwargs={'attribute': 'action'},
|
||||||
|
label=_('Actor'), source=Notification
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Notification, label=_('Event'),
|
func=widget_event_type_link, kwargs={'attribute': 'action'},
|
||||||
func=lambda context: event_type_link(context['object'].action)
|
label=_('Event'), source=Notification
|
||||||
|
)
|
||||||
|
|
||||||
|
SourceColumn(
|
||||||
|
func=widget_event_object_link, kwargs={
|
||||||
|
'attribute': 'action.target'
|
||||||
|
}, label=_('Target'), source=Notification
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Notification, label=_('Target'),
|
attribute='read', label=_('Seen'), source=Notification,
|
||||||
func=lambda context: event_object_link(context['object'].action)
|
widget=TwoStateWidget
|
||||||
)
|
|
||||||
SourceColumn(
|
|
||||||
source=Notification, label=_('Seen'),
|
|
||||||
func=lambda context: TwoStateWidget(
|
|
||||||
state=context['object'].read
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
menu_list_facet.bind_links(
|
menu_list_facet.bind_links(
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ def get_kwargs_factory(variable_name):
|
|||||||
return get_kwargs
|
return get_kwargs
|
||||||
|
|
||||||
|
|
||||||
def get_notification_count(context):
|
|
||||||
return context['request'].user.notifications.filter(read=False).count()
|
|
||||||
|
|
||||||
|
|
||||||
link_current_user_events = Link(
|
link_current_user_events = Link(
|
||||||
icon_class=icon_events_user_list, text=_('My events'),
|
icon_class=icon_events_user_list, text=_('My events'),
|
||||||
view='events:current_user_events'
|
view='events:current_user_events'
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from actstream.models import Action, any_stream
|
|||||||
|
|
||||||
from mayan.apps.acls.models import AccessControlList
|
from mayan.apps.acls.models import AccessControlList
|
||||||
from mayan.apps.common.generics import FormView, SimpleView
|
from mayan.apps.common.generics import FormView, SimpleView
|
||||||
from mayan.apps.common.utils import encapsulate
|
|
||||||
from mayan.apps.common.views import SingleObjectListView
|
from mayan.apps.common.views import SingleObjectListView
|
||||||
|
|
||||||
from .classes import EventType, ModelEventType
|
from .classes import EventType, ModelEventType
|
||||||
@@ -26,7 +25,6 @@ from .icons import (
|
|||||||
from .links import link_event_types_subscriptions_list
|
from .links import link_event_types_subscriptions_list
|
||||||
from .models import StoredEventType
|
from .models import StoredEventType
|
||||||
from .permissions import permission_events_view
|
from .permissions import permission_events_view
|
||||||
from .widgets import event_object_link
|
|
||||||
|
|
||||||
|
|
||||||
class EventListView(SingleObjectListView):
|
class EventListView(SingleObjectListView):
|
||||||
@@ -34,14 +32,6 @@ class EventListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'extra_columns': (
|
|
||||||
{
|
|
||||||
'name': _('Target'),
|
|
||||||
'attribute': encapsulate(
|
|
||||||
lambda entry: event_object_link(entry)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
'hide_object': True,
|
'hide_object': True,
|
||||||
'title': _('Events'),
|
'title': _('Events'),
|
||||||
}
|
}
|
||||||
@@ -270,14 +260,6 @@ class UserEventListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'extra_columns': (
|
|
||||||
{
|
|
||||||
'name': _('Target'),
|
|
||||||
'attribute': encapsulate(
|
|
||||||
lambda entry: event_object_link(entry)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
'hide_object': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_events_user_list,
|
'no_results_icon': icon_events_user_list,
|
||||||
'no_results_text': _(
|
'no_results_text': _(
|
||||||
@@ -308,14 +290,6 @@ class CurrentUserEventListView(UserEventListView):
|
|||||||
class VerbEventListView(SingleObjectListView):
|
class VerbEventListView(SingleObjectListView):
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'extra_columns': (
|
|
||||||
{
|
|
||||||
'name': _('Target'),
|
|
||||||
'attribute': encapsulate(
|
|
||||||
lambda entry: event_object_link(entry)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
'hide_object': True,
|
'hide_object': True,
|
||||||
'title': _(
|
'title': _(
|
||||||
'Events of type: %s'
|
'Events of type: %s'
|
||||||
|
|||||||
@@ -5,15 +5,18 @@ from django.utils.encoding import force_text
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mayan.apps.common.utils import resolve_attribute
|
||||||
|
|
||||||
from .classes import EventType
|
from .classes import EventType
|
||||||
|
|
||||||
|
|
||||||
def event_object_link(entry, attribute='target'):
|
def widget_event_object_link(context, attribute='target'):
|
||||||
|
entry = context['object']
|
||||||
label = ''
|
label = ''
|
||||||
url = '#'
|
url = '#'
|
||||||
obj_type = ''
|
obj_type = ''
|
||||||
|
|
||||||
obj = getattr(entry, attribute)
|
obj = resolve_attribute(obj=entry, attribute=attribute)
|
||||||
|
|
||||||
if obj:
|
if obj:
|
||||||
obj_type = '{}: '.format(obj._meta.verbose_name)
|
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(
|
return mark_safe(
|
||||||
'<a href="%(url)s">%(label)s</a>' % {
|
'<a href="%(url)s">%(label)s</a>' % {
|
||||||
'url': reverse('events:events_by_verb', kwargs={'verb': entry.verb}),
|
'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:
|
if entry.actor == entry.target:
|
||||||
return _('System')
|
return _('System')
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -121,16 +121,17 @@ class FileMetadataApp(MayanAppConfig):
|
|||||||
model=DocumentTypeSettings, related='document_type',
|
model=DocumentTypeSettings, related='document_type',
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(source=FileMetadataEntry, attribute='key')
|
SourceColumn(attribute='key', source=FileMetadataEntry)
|
||||||
SourceColumn(source=FileMetadataEntry, attribute='value')
|
SourceColumn(attribute='value', source=FileMetadataEntry)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionDriverEntry, attribute='driver'
|
attribute='driver', source=DocumentVersionDriverEntry
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionDriverEntry, attribute='driver__internal_name'
|
attribute='driver__internal_name',
|
||||||
|
source=DocumentVersionDriverEntry
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionDriverEntry, attribute='get_attribute_count'
|
attribute='get_attribute_count', source=DocumentVersionDriverEntry
|
||||||
)
|
)
|
||||||
|
|
||||||
app.conf.task_queues.append(
|
app.conf.task_queues.append(
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -57,28 +57,22 @@ class LinkingApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=ResolvedSmartLink, label=_('Label'),
|
|
||||||
func=lambda context: context['object'].get_label_for(
|
func=lambda context: context['object'].get_label_for(
|
||||||
document=context['document']
|
document=context['document']
|
||||||
)
|
), label=_('Label'), source=ResolvedSmartLink
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=SmartLink, label=_('Dynamic label'),
|
attribute='label', is_identifier=True, source=SmartLink
|
||||||
attribute='dynamic_label'
|
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='dynamic_label', source=SmartLink)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=SmartLink, label=_('Enabled'),
|
attribute='enabled', source=SmartLink, widget=TwoStateWidget
|
||||||
func=lambda context: TwoStateWidget(
|
|
||||||
state=context['object'].enabled
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=SmartLinkCondition, label=_('Enabled'),
|
attribute='enabled', source=SmartLinkCondition,
|
||||||
func=lambda context: TwoStateWidget(
|
widget=TwoStateWidget
|
||||||
state=context['object'].enabled
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
menu_facet.bind_links(
|
menu_facet.bind_links(
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class SmartLinkListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_smart_link_setup,
|
'no_results_icon': icon_smart_link_setup,
|
||||||
'no_results_main_link': link_smart_link_create.resolve(
|
'no_results_main_link': link_smart_link_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
|
|||||||
@@ -53,30 +53,16 @@ class MailerApp(MayanAppConfig):
|
|||||||
|
|
||||||
MailerBackend.initialize()
|
MailerBackend.initialize()
|
||||||
|
|
||||||
|
SourceColumn(attribute='datetime', source=LogEntry)
|
||||||
|
SourceColumn(attribute='message', source=LogEntry)
|
||||||
|
SourceColumn(attribute='label', is_identifier=True, source=UserMailer)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=LogEntry, label=_('Date and time'), attribute='datetime'
|
attribute='default', source=UserMailer, widget=TwoStateWidget
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=LogEntry, label=_('Message'), attribute='message'
|
attribute='enabled', source=UserMailer, widget=TwoStateWidget
|
||||||
)
|
|
||||||
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'
|
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='backend_label', source=UserMailer)
|
||||||
|
|
||||||
ModelPermission.register(
|
ModelPermission.register(
|
||||||
model=Document, permissions=(
|
model=Document, permissions=(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class LogEntry(models.Model):
|
class LogEntry(models.Model):
|
||||||
datetime = models.DateTimeField(
|
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(
|
message = models.TextField(
|
||||||
blank=True, editable=False, verbose_name=_('Message')
|
blank=True, editable=False, verbose_name=_('Message')
|
||||||
@@ -76,6 +76,7 @@ class UserMailer(models.Model):
|
|||||||
property.
|
property.
|
||||||
"""
|
"""
|
||||||
return self.get_backend().label
|
return self.get_backend().label
|
||||||
|
backend_label.short_description = _('Backend label')
|
||||||
|
|
||||||
def dumps(self, data):
|
def dumps(self, data):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -35,15 +35,41 @@ def decode_metadata_from_querystring(querystring=None):
|
|||||||
return metadata_list
|
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
|
Return a printable representation of a metadata list
|
||||||
document
|
|
||||||
"""
|
"""
|
||||||
for item in metadata_list:
|
return ', '.join(metadata_repr_as_list(metadata_list))
|
||||||
save_metadata(
|
|
||||||
metadata_dict=item, document=document, create=create, _user=_user
|
|
||||||
)
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def save_metadata(metadata_dict, document, create=False, _user=None):
|
def save_metadata(metadata_dict, document, create=False, _user=None):
|
||||||
@@ -84,38 +110,12 @@ def save_metadata(metadata_dict, document, create=False, _user=None):
|
|||||||
document_metadata.save(_user=_user)
|
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))
|
for item in metadata_list:
|
||||||
|
save_metadata(
|
||||||
|
metadata_dict=item, document=document, create=create, _user=_user
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ from .permissions import (
|
|||||||
)
|
)
|
||||||
from .queues import * # NOQA
|
from .queues import * # NOQA
|
||||||
from .search import metadata_type_search # 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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -144,27 +144,27 @@ class MetadataApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Document, label=_('Metadata'),
|
func=widget_get_metadata_string, source=Document
|
||||||
func=lambda context: get_metadata_string(context['object'])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentPageSearchResult, label=_('Metadata'),
|
func=widget_get_metadata_string, kwargs={'attribute': 'document'},
|
||||||
func=lambda context: get_metadata_string(
|
source=DocumentPageSearchResult,
|
||||||
context['object'].document
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentMetadata, label=_('Value'),
|
attribute='metadata_type', is_identifier=True,
|
||||||
attribute='value'
|
source=DocumentMetadata
|
||||||
|
)
|
||||||
|
SourceColumn(attribute='value', source=DocumentMetadata)
|
||||||
|
SourceColumn(
|
||||||
|
attribute='is_required', source=DocumentMetadata,
|
||||||
|
widget=TwoStateWidget
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentMetadata, label=_('Required'),
|
attribute='label', is_identifier=True, source=MetadataType
|
||||||
func=lambda context: TwoStateWidget(
|
|
||||||
state=context['object'].is_required
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
|
SourceColumn(attribute='name', source=MetadataType)
|
||||||
|
|
||||||
app.conf.task_queues.append(
|
app.conf.task_queues.append(
|
||||||
Queue('metadata', Exchange('metadata'), routing_key='metadata'),
|
Queue('metadata', Exchange('metadata'), routing_key='metadata'),
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ class DocumentMetadata(models.Model):
|
|||||||
return self.metadata_type.get_required_for(
|
return self.metadata_type.get_required_for(
|
||||||
document_type=self.document.document_type
|
document_type=self.document.document_type
|
||||||
)
|
)
|
||||||
|
is_required.fget.short_description = _('Required')
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.metadata_type.pk not in self.document.document_type.metadata.values_list('metadata_type', flat=True):
|
if self.metadata_type.pk not in self.document.document_type.metadata.values_list('metadata_type', flat=True):
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ class DocumentMetadataListView(SingleObjectListView):
|
|||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
document = self.get_document()
|
document = self.get_document()
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'object': document,
|
'object': document,
|
||||||
'no_results_icon': icon_metadata,
|
'no_results_icon': icon_metadata,
|
||||||
'no_results_main_link': link_metadata_add.resolve(
|
'no_results_main_link': link_metadata_add.resolve(
|
||||||
@@ -618,13 +618,7 @@ class MetadataTypeListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'extra_columns': (
|
'hide_object': True,
|
||||||
{
|
|
||||||
'name': _('Internal name'),
|
|
||||||
'attribute': 'name',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
'hide_link': True,
|
|
||||||
'no_results_icon': icon_metadata,
|
'no_results_icon': icon_metadata,
|
||||||
'no_results_main_link': link_setup_metadata_type_create.resolve(
|
'no_results_main_link': link_setup_metadata_type_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.utils.html import format_html_join
|
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
|
Return a formated representation of a document's metadata values
|
||||||
"""
|
"""
|
||||||
|
obj = context['object']
|
||||||
|
|
||||||
|
if attribute:
|
||||||
|
obj = getattr(context['object'], attribute)
|
||||||
|
|
||||||
return format_html_join(
|
return format_html_join(
|
||||||
'\n', '<div class="metadata-display"><b>{}: </b><span data-metadata-type="{}" data-pk="{}">{}</span></div>',
|
'\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
|
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')
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from mayan.apps.acls.permissions import (
|
|||||||
from mayan.apps.common import (
|
from mayan.apps.common import (
|
||||||
MayanAppConfig, menu_list_facet, menu_object, menu_secondary, menu_setup
|
MayanAppConfig, menu_list_facet, menu_object, menu_secondary, menu_setup
|
||||||
)
|
)
|
||||||
|
from mayan.apps.common.widgets import TwoStateWidget
|
||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
|
|
||||||
from .links import (
|
from .links import (
|
||||||
@@ -45,16 +46,18 @@ class MOTDApp(MayanAppConfig):
|
|||||||
permission_message_view
|
permission_message_view
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Message, label=_('Enabled'), attribute='enabled'
|
attribute='label', is_identifier=True, source=Message
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Message, label=_('Start date time'),
|
attribute='enabled', source=Message, widget=TwoStateWidget
|
||||||
func=lambda context: context['object'].start_datetime or _('None')
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Message, label=_('End date time'),
|
attribute='start_datetime', empty_value=_('None'), source=Message
|
||||||
func=lambda context: context['object'].end_datetime or _('None')
|
)
|
||||||
|
SourceColumn(
|
||||||
|
attribute='end_datetime', empty_value=_('None'), source=Message
|
||||||
)
|
)
|
||||||
|
|
||||||
menu_list_facet.bind_links(
|
menu_list_facet.bind_links(
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class MessageListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_message_list,
|
'no_results_icon': icon_message_list,
|
||||||
'no_results_main_link': link_message_create.resolve(
|
'no_results_main_link': link_message_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ from django.template import VariableDoesNotExist, Variable
|
|||||||
from django.template.defaulttags import URLNode
|
from django.template.defaulttags import URLNode
|
||||||
from django.urls import Resolver404, resolve
|
from django.urls import Resolver404, resolve
|
||||||
from django.utils.encoding import force_str, force_text
|
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.common.utils import resolve_attribute
|
||||||
from mayan.apps.permissions import Permission
|
from mayan.apps.permissions import Permission
|
||||||
|
|
||||||
|
from .exceptions import NavigationError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -527,10 +530,11 @@ class SourceColumn(object):
|
|||||||
else:
|
else:
|
||||||
return result
|
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.source = source
|
||||||
self._label = label
|
self._label = label
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
|
self.empty_value = empty_value
|
||||||
self.func = func
|
self.func = func
|
||||||
self.kwargs = kwargs or {}
|
self.kwargs = kwargs or {}
|
||||||
self.order = order or 0
|
self.order = order or 0
|
||||||
@@ -538,20 +542,29 @@ class SourceColumn(object):
|
|||||||
self.__class__._registry.setdefault(source, [])
|
self.__class__._registry.setdefault(source, [])
|
||||||
self.__class__._registry[source].append(self)
|
self.__class__._registry[source].append(self)
|
||||||
self.label = None
|
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()
|
self._calculate_label()
|
||||||
|
|
||||||
def _calculate_label(self):
|
def _calculate_label(self):
|
||||||
if not self._label:
|
if not self._label:
|
||||||
if self.attribute:
|
if self.attribute:
|
||||||
name, model = SourceColumn.get_attribute_recursive(
|
try:
|
||||||
attribute=self.attribute, model=self.source._meta.model
|
attribute = resolve_attribute(obj=self.source, attribute=self.attribute)
|
||||||
)
|
self._label = getattr(attribute, 'short_description')
|
||||||
self._label = label_for_field(
|
except AttributeError:
|
||||||
name=name, model=model
|
name, model = SourceColumn.get_attribute_recursive(
|
||||||
)
|
attribute=self.attribute, model=self.source._meta.model
|
||||||
|
)
|
||||||
|
self._label = label_for_field(
|
||||||
|
name=name, model=model
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._label = 'Function'
|
self._label = getattr(self.func, 'short_description', _('Unnamed function'))
|
||||||
|
|
||||||
self.label = self._label
|
self.label = self._label
|
||||||
|
|
||||||
@@ -564,7 +577,17 @@ class SourceColumn(object):
|
|||||||
elif self.func:
|
elif self.func:
|
||||||
result = self.func(context=context, **self.kwargs)
|
result = self.func(context=context, **self.kwargs)
|
||||||
|
|
||||||
return result
|
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
|
||||||
|
|
||||||
|
|
||||||
class Text(Link):
|
class Text(Link):
|
||||||
|
|||||||
5
mayan/apps/navigation/exceptions.py
Normal file
5
mayan/apps/navigation/exceptions.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
class NavigationError(Exception):
|
||||||
|
"""Base navigation app exception"""
|
||||||
@@ -110,16 +110,16 @@ class OCRApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionOCRError, label=_('Document'),
|
func=lambda context: document_link(
|
||||||
func=lambda context: document_link(context['object'].document_version.document)
|
context['object'].document_version.document
|
||||||
|
), label=_('Document'), source=DocumentVersionOCRError
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionOCRError, label=_('Added'),
|
attribute='datetime_submitted', label=_('Date and time'),
|
||||||
attribute='datetime_submitted'
|
source=DocumentVersionOCRError
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DocumentVersionOCRError, label=_('Result'),
|
attribute='result', source=DocumentVersionOCRError
|
||||||
attribute='result'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
app.conf.task_queues.append(
|
app.conf.task_queues.append(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from mayan.apps.common import (
|
|||||||
menu_secondary, menu_setup
|
menu_secondary, menu_setup
|
||||||
)
|
)
|
||||||
from mayan.apps.common.signals import perform_upgrade
|
from mayan.apps.common.signals import perform_upgrade
|
||||||
|
from mayan.apps.navigation import SourceColumn
|
||||||
|
|
||||||
from .handlers import handler_purge_permissions
|
from .handlers import handler_purge_permissions
|
||||||
from .links import (
|
from .links import (
|
||||||
@@ -48,6 +49,8 @@ class PermissionsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SourceColumn(attribute='label', is_identifier=True, source=Role)
|
||||||
|
|
||||||
menu_list_facet.bind_links(
|
menu_list_facet.bind_links(
|
||||||
links=(
|
links=(
|
||||||
link_acl_list, link_role_groups, link_role_permissions,
|
link_acl_list, link_role_groups, link_role_permissions,
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ class RoleListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_role_list,
|
'no_results_icon': icon_role_list,
|
||||||
'no_results_main_link': link_role_create.resolve(
|
'no_results_main_link': link_role_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
|
|||||||
@@ -37,4 +37,3 @@ class ClassesTestCase(BaseTestCase):
|
|||||||
setting_value = setting_paginate_by.value
|
setting_value = setting_paginate_by.value
|
||||||
os.environ.pop('MAYAN_{}'.format(TEST_SETTING_NAME))
|
os.environ.pop('MAYAN_{}'.format(TEST_SETTING_NAME))
|
||||||
self.assertEqual(setting_value, TEST_SETTING_VALUE)
|
self.assertEqual(setting_value, TEST_SETTING_VALUE)
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class TagsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='label', source=DocumentTag,
|
attribute='label', is_identifier=True, source=DocumentTag,
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='get_preview_widget', source=DocumentTag
|
attribute='get_preview_widget', source=DocumentTag
|
||||||
|
|||||||
@@ -33,16 +33,12 @@ class TaskManagerApp(MayanAppConfig):
|
|||||||
source=CeleryQueue, label=_('Name'), attribute='name'
|
source=CeleryQueue, label=_('Name'), attribute='name'
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=CeleryQueue, label=_('Default queue?'),
|
attribute='is_default_queue', label=_('Default queue?'),
|
||||||
func=lambda context: TwoStateWidget(
|
source=CeleryQueue, widget=TwoStateWidget
|
||||||
state=context['object'].is_default_queue
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=CeleryQueue, label=_('Is transient?'),
|
attribute='is_transient', label=_('Is transient?'),
|
||||||
func=lambda context: TwoStateWidget(
|
source=CeleryQueue, widget=TwoStateWidget
|
||||||
state=context['object'].is_transient
|
|
||||||
).render()
|
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Task, label=_('Type'), attribute='task_type'
|
source=Task, label=_('Type'), attribute='task_type'
|
||||||
@@ -54,12 +50,12 @@ class TaskManagerApp(MayanAppConfig):
|
|||||||
source=Task, label=_('Host'),
|
source=Task, label=_('Host'),
|
||||||
func=lambda context: context['object'].kwargs['hostname']
|
func=lambda context: context['object'].kwargs['hostname']
|
||||||
)
|
)
|
||||||
SourceColumn(
|
#SourceColumn(
|
||||||
source=Task, label=_('Acknowledged'),
|
# source=Task, label=_('Acknowledged'),
|
||||||
func=lambda context: TwoStateWidget(
|
# func=lambda context: TwoStateWidget(
|
||||||
state=context['object'].kwargs['acknowledged']
|
# state=context['object'].kwargs['acknowledged']
|
||||||
).render()
|
# ).render()
|
||||||
)
|
#)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=Task, label=_('Arguments'),
|
source=Task, label=_('Arguments'),
|
||||||
func=lambda context: context['object'].kwargs['args']
|
func=lambda context: context['object'].kwargs['args']
|
||||||
|
|||||||
@@ -31,20 +31,7 @@ from .permissions import (
|
|||||||
permission_user_view
|
permission_user_view
|
||||||
)
|
)
|
||||||
from .search import * # NOQA
|
from .search import * # NOQA
|
||||||
|
from .utils import get_groups, get_users
|
||||||
|
|
||||||
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()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserManagementApp(MayanAppConfig):
|
class UserManagementApp(MayanAppConfig):
|
||||||
@@ -89,27 +76,27 @@ class UserManagementApp(MayanAppConfig):
|
|||||||
permission_user_view
|
permission_user_view
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SourceColumn(attribute='name', is_identifier=True, source=Group)
|
||||||
SourceColumn(
|
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(
|
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(
|
SourceColumn(
|
||||||
source=User, label=_('Email'), attribute='email'
|
attribute='has_usable_password', source=User, widget=TwoStateWidget
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=User, label=_('Active'),
|
attribute='has_usable_password', label=_('Has usable password?'),
|
||||||
func=lambda context: TwoStateWidget(
|
source=User, widget=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()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
menu_list_facet.bind_links(
|
menu_list_facet.bind_links(
|
||||||
|
|||||||
18
mayan/apps/user_management/utils.py
Normal file
18
mayan/apps/user_management/utils.py
Normal 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()
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -55,7 +55,7 @@ class GroupListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_group_setup,
|
'no_results_icon': icon_group_setup,
|
||||||
'no_results_main_link': link_group_create.resolve(
|
'no_results_main_link': link_group_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
@@ -261,7 +261,7 @@ class UserListView(SingleObjectListView):
|
|||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'hide_link': True,
|
'hide_object': True,
|
||||||
'no_results_icon': icon_user_setup,
|
'no_results_icon': icon_user_setup,
|
||||||
'no_results_main_link': link_user_create.resolve(
|
'no_results_main_link': link_user_create.resolve(
|
||||||
context=RequestContext(request=self.request)
|
context=RequestContext(request=self.request)
|
||||||
|
|||||||
Reference in New Issue
Block a user