PEP8 cleanups and code style cleanups.
This commit is contained in:
@@ -17,6 +17,6 @@ def get_kwargs_factory(variable_name):
|
||||
|
||||
|
||||
link_acl_delete = Link(permissions=[permission_acl_edit], tags='dangerous', text=_('Delete'), view='acls:acl_delete', args='resolved_object.pk')
|
||||
link_acl_new = Link(permissions=[permission_acl_edit], text=_('New ACL'), view='acls:acl_new', kwargs=get_kwargs_factory('resolved_object'))
|
||||
link_acl_list = Link(permissions=[permission_acl_view], text=_('ACLs'), view='acls:acl_list', kwargs=get_kwargs_factory('resolved_object'))
|
||||
link_acl_new = Link(permissions=[permission_acl_edit], text=_('New ACL'), view='acls:acl_new', kwargs=get_kwargs_factory('resolved_object'))
|
||||
link_acl_permissions = Link(permissions=[permission_acl_edit], text=_('Permissions'), view='acls:acl_permissions', args='resolved_object.pk')
|
||||
|
||||
@@ -10,7 +10,6 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from permissions.models import Role, StoredPermission
|
||||
|
||||
from .classes import ModelPermission
|
||||
from .managers import AccessControlListManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.permissions import permission_document_view
|
||||
from documents.test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_SMALL_DOCUMENT_FILENAME, TEST_NON_ASCII_DOCUMENT_FILENAME,
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH,
|
||||
TEST_SIGNED_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_NON_ASCII_DOCUMENT_PATH, TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_TYPE
|
||||
)
|
||||
from documents.test_models import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
from permissions.classes import Permission
|
||||
from permissions.models import Role
|
||||
|
||||
@@ -128,7 +118,6 @@ class PermissionTestCase(TestCase):
|
||||
self.assertTrue(self.document_2 in result)
|
||||
self.assertTrue(self.document_3 not in result)
|
||||
|
||||
|
||||
def test_filtering_with_inherited_permissions_and_local_acl(self):
|
||||
self.group.user_set.add(self.user)
|
||||
self.role.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
@@ -24,7 +24,6 @@ from .permissions import (
|
||||
)
|
||||
|
||||
|
||||
|
||||
class CheckoutsApp(MayanAppConfig):
|
||||
name = 'checkouts'
|
||||
verbose_name = _('Checkouts')
|
||||
@@ -34,18 +33,10 @@ class CheckoutsApp(MayanAppConfig):
|
||||
|
||||
APIEndPoint('checkouts')
|
||||
|
||||
Document.add_to_class('is_checked_out', lambda document: DocumentCheckout.objects.is_document_checked_out(document))
|
||||
Document.add_to_class('check_in', lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user))
|
||||
Document.add_to_class('checkout_info', lambda document: DocumentCheckout.objects.document_checkout_info(document))
|
||||
Document.add_to_class('checkout_state', lambda document: DocumentCheckout.objects.document_checkout_state(document))
|
||||
|
||||
app.conf.CELERYBEAT_SCHEDULE.update({
|
||||
'check_expired_check_outs': {
|
||||
'task': 'checkouts.tasks.task_check_expired_check_outs',
|
||||
'schedule': timedelta(seconds=CHECK_EXPIRED_CHECK_OUTS_INTERVAL),
|
||||
'options': {'queue': 'checkouts'}
|
||||
},
|
||||
})
|
||||
Document.add_to_class('is_checked_out', lambda document: DocumentCheckout.objects.is_document_checked_out(document))
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(
|
||||
@@ -55,6 +46,14 @@ class CheckoutsApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
app.conf.CELERYBEAT_SCHEDULE.update({
|
||||
'check_expired_check_outs': {
|
||||
'task': 'checkouts.tasks.task_check_expired_check_outs',
|
||||
'schedule': timedelta(seconds=CHECK_EXPIRED_CHECK_OUTS_INTERVAL),
|
||||
'options': {'queue': 'checkouts'}
|
||||
},
|
||||
})
|
||||
|
||||
menu_facet.bind_links(links=[link_checkout_info], sources=[Document])
|
||||
menu_main.bind_links(links=[link_checkout_list])
|
||||
menu_sidebar.bind_links(links=[link_checkout_document, link_checkin_document], sources=['checkouts:checkout_info', 'checkouts:checkout_document', 'checkouts:checkin_document'])
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
|
||||
event_document_check_out = Event(name='checkouts_document_check_out', label=_('Document checked out'))
|
||||
event_document_check_in = Event(name='checkouts_document_check_in', label=_('Document checked in'))
|
||||
event_document_auto_check_in = Event(name='checkouts_document_auto_check_in', label=_('Document automatically checked in'))
|
||||
event_document_check_in = Event(name='checkouts_document_check_in', label=_('Document checked in'))
|
||||
event_document_check_out = Event(name='checkouts_document_check_out', label=_('Document checked out'))
|
||||
event_document_forceful_check_in = Event(name='checkouts_document_forceful_check_in', label=_('Document forcefully checked in'))
|
||||
|
||||
@@ -12,4 +12,3 @@ STATE_LABELS = {
|
||||
STATE_CHECKED_OUT: _('Checked out'),
|
||||
STATE_CHECKED_IN: _('Checked in/available'),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def move_from_content_type_user_to_foreign_key_field_user(apps, schema_editor):
|
||||
|
||||
@@ -35,6 +35,9 @@ class DocumentCheckout(models.Model):
|
||||
def __str__(self):
|
||||
return unicode(self.document)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('checkout:checkout_info', args=[self.document.pk])
|
||||
|
||||
def clean(self):
|
||||
if self.expiration_datetime < now():
|
||||
raise ValidationError(_('Check out expiration date and time must be in the future.'))
|
||||
@@ -51,9 +54,6 @@ class DocumentCheckout(models.Model):
|
||||
|
||||
return result
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('checkout:checkout_info', args=[self.document.pk])
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Document checkout')
|
||||
verbose_name_plural = _('Document checkouts')
|
||||
|
||||
@@ -6,6 +6,6 @@ from permissions import PermissionNamespace
|
||||
|
||||
namespace = PermissionNamespace('checkouts', _('Document checkout'))
|
||||
|
||||
permission_document_checkout = namespace.add_permission(name='checkout_document', label=_('Check out documents'))
|
||||
permission_document_checkin = namespace.add_permission(name='checkin_document', label=_('Check in documents'))
|
||||
permission_document_checkin_override = namespace.add_permission(name='checkin_document_override', label=_('Forcefully check in documents'))
|
||||
permission_document_checkout = namespace.add_permission(name='checkout_document', label=_('Check out documents'))
|
||||
|
||||
382
mayan/apps/common/generics.py
Normal file
382
mayan/apps/common/generics.py
Normal file
@@ -0,0 +1,382 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import FormView, TemplateView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from .forms import ChoiceForm
|
||||
from .mixins import (
|
||||
ExtraContextMixin, ObjectListPermissionFilterMixin,
|
||||
ObjectPermissionCheckMixin, RedirectionMixin, ViewPermissionCheckMixin
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'AssignRemoveView', 'ConfirmView', 'MultiFormView', 'ParentChildListView',
|
||||
'SingleObjectCreateView', 'SingleObjectDeleteView',
|
||||
'SingleObjectEditView', 'SingleObjectListView', 'SimpleView',
|
||||
)
|
||||
|
||||
|
||||
class AssignRemoveView(ExtraContextMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, TemplateView):
|
||||
decode_content_type = False
|
||||
extra_context = None
|
||||
grouped = False
|
||||
left_list_title = None
|
||||
right_list_title = None
|
||||
template_name = 'appearance/generic_form.html'
|
||||
|
||||
LEFT_LIST_NAME = 'left_list'
|
||||
RIGHT_LIST_NAME = 'right_list'
|
||||
|
||||
@staticmethod
|
||||
def generate_choices(choices):
|
||||
results = []
|
||||
for choice in choices:
|
||||
ct = ContentType.objects.get_for_model(choice)
|
||||
if isinstance(choice, User):
|
||||
label = choice.get_full_name() if choice.get_full_name() else choice
|
||||
else:
|
||||
label = unicode(choice)
|
||||
|
||||
results.append(('%s,%s' % (ct.model, choice.pk), '%s' % (label)))
|
||||
|
||||
# Sort results by the label not the key value
|
||||
return sorted(results, key=lambda x: x[1])
|
||||
|
||||
def left_list(self):
|
||||
# Subclass must override
|
||||
raise NotImplementedError
|
||||
|
||||
def right_list(self):
|
||||
# Subclass must override
|
||||
raise NotImplementedError
|
||||
|
||||
def add(self, item):
|
||||
# Subclass must override
|
||||
raise NotImplementedError
|
||||
|
||||
def remove(self, item):
|
||||
# Subclass must override
|
||||
raise NotImplementedError
|
||||
|
||||
def get_disabled_choices(self):
|
||||
return ()
|
||||
|
||||
def get_help_text(self):
|
||||
return self.help_text
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.unselected_list = ChoiceForm(prefix=self.LEFT_LIST_NAME, choices=self.left_list())
|
||||
self.selected_list = ChoiceForm(prefix=self.RIGHT_LIST_NAME, choices=self.right_list(), disabled_choices=self.get_disabled_choices(), help_text=self.get_help_text())
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def process_form(self, prefix, items_function, action_function):
|
||||
if '%s-submit' % prefix in self.request.POST.keys():
|
||||
form = ChoiceForm(
|
||||
self.request.POST, prefix=prefix,
|
||||
choices=items_function()
|
||||
)
|
||||
|
||||
if form.is_valid():
|
||||
for selection in form.cleaned_data['selection']:
|
||||
if self.grouped:
|
||||
flat_list = []
|
||||
for group in items_function():
|
||||
flat_list.extend(group[1])
|
||||
else:
|
||||
flat_list = items_function()
|
||||
|
||||
label = dict(flat_list)[selection]
|
||||
if self.decode_content_type:
|
||||
model, pk = selection.split(',')
|
||||
selection_obj = ContentType.objects.get(model=model).get_object_for_this_type(pk=pk)
|
||||
else:
|
||||
selection_obj = selection
|
||||
|
||||
try:
|
||||
action_function(selection_obj)
|
||||
except:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
else:
|
||||
messages.error(self.request, _('Unable to transfer selection: %s.') % label)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.process_form(prefix=self.LEFT_LIST_NAME, items_function=self.left_list, action_function=self.add)
|
||||
self.process_form(prefix=self.RIGHT_LIST_NAME, items_function=self.right_list, action_function=self.remove)
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super(AssignRemoveView, self).get_context_data(**kwargs)
|
||||
data.update({
|
||||
'subtemplates_list': [
|
||||
{
|
||||
'name': 'appearance/generic_form_subtemplate.html',
|
||||
'column_class': 'col-xs-12 col-sm-6 col-md-6 col-lg-6',
|
||||
'context': {
|
||||
'form': self.unselected_list,
|
||||
'title': self.left_list_title or ' ',
|
||||
'submit_label': _('Add'),
|
||||
'submit_icon': 'fa fa-plus',
|
||||
'hide_labels': True,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'appearance/generic_form_subtemplate.html',
|
||||
'column_class': 'col-xs-12 col-sm-6 col-md-6 col-lg-6',
|
||||
'context': {
|
||||
'form': self.selected_list,
|
||||
'title': self.right_list_title or ' ',
|
||||
'submit_label': _('Remove'),
|
||||
'submit_icon': 'fa fa-minus',
|
||||
'hide_labels': True,
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
})
|
||||
return data
|
||||
|
||||
|
||||
class ConfirmView(ObjectListPermissionFilterMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView):
|
||||
template_name = 'appearance/generic_confirm.html'
|
||||
|
||||
|
||||
class MultiFormView(FormView):
|
||||
prefixes = {}
|
||||
|
||||
prefix = None
|
||||
|
||||
def get_form_kwargs(self, form_name):
|
||||
kwargs = {}
|
||||
kwargs.update({'initial': self.get_initial(form_name)})
|
||||
kwargs.update({'prefix': self.get_prefix(form_name)})
|
||||
|
||||
if self.request.method in ('POST', 'PUT'):
|
||||
kwargs.update({
|
||||
'data': self.request.POST,
|
||||
'files': self.request.FILES,
|
||||
})
|
||||
|
||||
return kwargs
|
||||
|
||||
def _create_form(self, form_name, klass):
|
||||
form_kwargs = self.get_form_kwargs(form_name)
|
||||
form_create_method = 'create_%s_form' % form_name
|
||||
if hasattr(self, form_create_method):
|
||||
form = getattr(self, form_create_method)(**form_kwargs)
|
||||
else:
|
||||
form = klass(**form_kwargs)
|
||||
return form
|
||||
|
||||
def get_forms(self, form_classes):
|
||||
return dict([(key, self._create_form(key, klass)) for key, klass in form_classes.items()])
|
||||
|
||||
def get_initial(self, form_name):
|
||||
initial_method = 'get_%s_initial' % form_name
|
||||
if hasattr(self, initial_method):
|
||||
return getattr(self, initial_method)()
|
||||
else:
|
||||
return self.initial.copy()
|
||||
|
||||
def get_prefix(self, form_name):
|
||||
return self.prefixes.get(form_name, self.prefix)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
form_classes = self.get_form_classes()
|
||||
forms = self.get_forms(form_classes)
|
||||
return self.render_to_response(self.get_context_data(forms=forms))
|
||||
|
||||
def forms_valid(self, forms):
|
||||
for form_name, form in forms.items():
|
||||
form_valid_method = '%s_form_valid' % form_name
|
||||
|
||||
if hasattr(self, form_valid_method):
|
||||
return getattr(self, form_valid_method)(form)
|
||||
|
||||
self.all_forms_valid(forms)
|
||||
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def forms_invalid(self, forms):
|
||||
return self.render_to_response(self.get_context_data(forms=forms))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form_classes = self.get_form_classes()
|
||||
forms = self.get_forms(form_classes)
|
||||
|
||||
if all([form.is_valid() for form in forms.values()]):
|
||||
return self.forms_valid(forms)
|
||||
else:
|
||||
return self.forms_invalid(forms)
|
||||
|
||||
|
||||
class ParentChildListView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, ListView, SingleObjectMixin):
|
||||
parent_model = None
|
||||
parent_queryset = None
|
||||
template_name = 'appearance/generic_list.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
# Parent
|
||||
self.object = self.get_object()
|
||||
|
||||
# Children
|
||||
self.object_list = self.get_queryset()
|
||||
allow_empty = self.get_allow_empty()
|
||||
if not allow_empty:
|
||||
# When pagination is enabled and object_list is a queryset,
|
||||
# it's better to do a cheap query than to load the unpaginated
|
||||
# queryset in memory.
|
||||
if (self.get_paginate_by(self.object_list) is not None
|
||||
and hasattr(self.object_list, 'exists')):
|
||||
is_empty = not self.object_list.exists()
|
||||
else:
|
||||
is_empty = len(self.object_list) == 0
|
||||
if is_empty:
|
||||
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.")
|
||||
% {'class_name': self.__class__.__name__})
|
||||
|
||||
context = self.get_context_data(object=self.object)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
# Use a custom queryset if provided; this is required for subclasses
|
||||
# like DateDetailView
|
||||
if queryset is None:
|
||||
queryset = self.get_parent_queryset()
|
||||
# Next, try looking up by primary key.
|
||||
pk = self.kwargs.get(self.pk_url_kwarg, None)
|
||||
slug = self.kwargs.get(self.slug_url_kwarg, None)
|
||||
if pk is not None:
|
||||
queryset = queryset.filter(pk=pk)
|
||||
# Next, try looking up by slug.
|
||||
if slug is not None and (pk is None or self.query_pk_and_slug):
|
||||
slug_field = self.get_slug_field()
|
||||
queryset = queryset.filter(**{slug_field: slug})
|
||||
# If none of those are defined, it's an error.
|
||||
if pk is None and slug is None:
|
||||
raise AttributeError("Generic detail view %s must be called with "
|
||||
"either an object pk or a slug."
|
||||
% self.__class__.__name__)
|
||||
try:
|
||||
# Get the single item from the filtered queryset
|
||||
obj = queryset.get()
|
||||
except queryset.model.DoesNotExist:
|
||||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
return obj
|
||||
|
||||
def get_parent_queryset(self):
|
||||
"""
|
||||
Return the `QuerySet` that will be used to look up the object.
|
||||
Note that this method is called by the default implementation of
|
||||
`get_object` and may not be called if `get_object` is overridden.
|
||||
"""
|
||||
if self.parent_queryset is None:
|
||||
if self.parent_model:
|
||||
return self.model._default_manager.all()
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"%(cls)s is missing a QuerySet. Define "
|
||||
"%(cls)s.parent_model, %(cls)s.parent_queryset, or override "
|
||||
"%(cls)s.get_parent_queryset()." % {
|
||||
'cls': self.__class__.__name__
|
||||
}
|
||||
)
|
||||
return self.parent_queryset.all()
|
||||
|
||||
def get_queryset(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SimpleView(ViewPermissionCheckMixin, ExtraContextMixin, TemplateView):
|
||||
pass
|
||||
|
||||
|
||||
class SingleObjectCreateView(ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, CreateView):
|
||||
template_name = 'appearance/generic_form.html'
|
||||
|
||||
def form_invalid(self, form):
|
||||
result = super(SingleObjectCreateView, self).form_invalid(form)
|
||||
|
||||
try:
|
||||
messages.error(self.request, _('Error creating new %s.') % self.extra_context['object_name'])
|
||||
except KeyError:
|
||||
messages.error(self.request, _('Error creating object.'))
|
||||
|
||||
return result
|
||||
|
||||
def form_valid(self, form):
|
||||
result = super(SingleObjectCreateView, self).form_valid(form)
|
||||
try:
|
||||
messages.success(self.request, _('%s created successfully.') % self.extra_context['object_name'].capitalize())
|
||||
except KeyError:
|
||||
messages.success(self.request, _('New object created successfully.'))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class SingleObjectDeleteView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DeleteView):
|
||||
template_name = 'appearance/generic_confirm.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SingleObjectDeleteView, self).get_context_data(**kwargs)
|
||||
context.update({'delete_view': True})
|
||||
return context
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
try:
|
||||
result = super(SingleObjectDeleteView, self).delete(request, *args, **kwargs)
|
||||
except Exception as exception:
|
||||
try:
|
||||
messages.error(self.request, _('Error deleting %s.') % self.extra_context['object_name'])
|
||||
except KeyError:
|
||||
messages.error(self.request, _('Error deleting object.'))
|
||||
|
||||
raise exception
|
||||
else:
|
||||
try:
|
||||
messages.success(self.request, _('%s deleted successfully.') % self.extra_context['object_name'].capitalize())
|
||||
except KeyError:
|
||||
messages.success(self.request, _('Object deleted successfully.'))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# TODO: check/test if ViewPermissionCheckMixin, ObjectPermissionCheckMixin are
|
||||
# in the right MRO
|
||||
class SingleObjectEditView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, UpdateView):
|
||||
template_name = 'appearance/generic_form.html'
|
||||
|
||||
def form_invalid(self, form):
|
||||
result = super(SingleObjectEditView, self).form_invalid(form)
|
||||
|
||||
try:
|
||||
messages.error(self.request, _('Error saving %s details.') % self.extra_context['object_name'])
|
||||
except KeyError:
|
||||
messages.error(self.request, _('Error saving details.'))
|
||||
|
||||
return result
|
||||
|
||||
def form_valid(self, form):
|
||||
result = super(SingleObjectEditView, self).form_valid(form)
|
||||
|
||||
try:
|
||||
messages.success(self.request, _('%s details saved successfully.') % self.extra_context['object_name'].capitalize())
|
||||
except KeyError:
|
||||
messages.success(self.request, _('Details saved successfully.'))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class SingleObjectListView(ViewPermissionCheckMixin, ObjectListPermissionFilterMixin, ExtraContextMixin, RedirectionMixin, ListView):
|
||||
template_name = 'appearance/generic_list.html'
|
||||
@@ -11,7 +11,7 @@ class TimezoneMiddleware(object):
|
||||
if hasattr(request, 'session'):
|
||||
tzname = request.session.get(settings.TIMEZONE_SESSION_KEY)
|
||||
else:
|
||||
tzname = HttpRequest.COOKIES.get(settings.TIMEZONE_COOKIE_NAME)
|
||||
tzname = request.COOKIES.get(settings.TIMEZONE_COOKIE_NAME)
|
||||
|
||||
if tzname:
|
||||
timezone.activate(pytz.timezone(tzname))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -6,7 +6,6 @@ import tempfile
|
||||
import types
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.http import urlquote as django_urlquote
|
||||
from django.utils.http import urlencode as django_urlencode
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@ import os
|
||||
|
||||
from django import forms
|
||||
from django.forms.util import flatatt
|
||||
from django.utils.datastructures import MultiValueDict, MergeDict
|
||||
from django.utils.encoding import force_unicode, force_text
|
||||
from django.utils.html import conditional_escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -4,7 +4,6 @@ import logging
|
||||
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
@@ -5,7 +5,7 @@ from django import forms
|
||||
from common.classes import ModelAttribute
|
||||
from documents.models import Document
|
||||
|
||||
from .models import Index, IndexTemplateNode
|
||||
from .models import IndexTemplateNode
|
||||
|
||||
|
||||
class IndexTemplateNodeForm(forms.ModelForm):
|
||||
|
||||
@@ -89,9 +89,9 @@ class IndexInstanceNodeManager(models.Manager):
|
||||
|
||||
self.delete_empty_index_nodes()
|
||||
|
||||
def rebuild_all_indexes():
|
||||
def rebuild_all_indexes(self):
|
||||
for instance_node in self.all():
|
||||
instance_node.delete()
|
||||
|
||||
for document in Document.objects.all():
|
||||
index_document(document)
|
||||
self.index_document(document)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
from django.core.files.base import File
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.models import DocumentType
|
||||
from documents.test_models import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
from metadata.models import MetadataType, DocumentTypeMetadataType
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
|
||||
import io
|
||||
import logging
|
||||
|
||||
from django.core.files import File
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from acls import ModelPermission
|
||||
|
||||
@@ -23,15 +23,12 @@ class DocumentVersionSignature(models.Model):
|
||||
"""
|
||||
Model that describes a document version signature properties
|
||||
"""
|
||||
document_version = models.ForeignKey(DocumentVersion, verbose_name=_('Document version'), editable=False)
|
||||
signature_file = models.FileField(blank=True, null=True, upload_to=upload_to, storage=storage_backend, verbose_name=_('Signature file'))
|
||||
document_version = models.ForeignKey(DocumentVersion, editable=False, verbose_name=_('Document version'))
|
||||
signature_file = models.FileField(blank=True, null=True, storage=storage_backend, upload_to=upload_to, verbose_name=_('Signature file'))
|
||||
has_embedded_signature = models.BooleanField(default=False, verbose_name=_('Has embedded signature'))
|
||||
|
||||
objects = DocumentVersionSignatureManager()
|
||||
|
||||
def delete_detached_signature_file(self):
|
||||
self.signature_file.storage.delete(self.signature_file.path)
|
||||
|
||||
def check_for_embedded_signature(self):
|
||||
logger.debug('checking for embedded signature')
|
||||
|
||||
@@ -39,6 +36,9 @@ class DocumentVersionSignature(models.Model):
|
||||
self.has_embedded_signature = gpg.has_embedded_signature(file_object)
|
||||
self.save()
|
||||
|
||||
def delete_detached_signature_file(self):
|
||||
self.signature_file.storage.delete(self.signature_file.path)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Document version signature')
|
||||
verbose_name_plural = _('Document version signatures')
|
||||
|
||||
@@ -6,14 +6,13 @@ from django.conf import settings
|
||||
from django.core.files.base import File
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.test_models import TEST_DOCUMENT_TYPE
|
||||
from documents.models import DocumentType
|
||||
from documents.test_models import TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
from django_gpg.literals import SIGNATURE_STATE_VALID
|
||||
from django_gpg.runtime import gpg
|
||||
|
||||
from .models import DocumentVersionSignature
|
||||
|
||||
TEST_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf')
|
||||
TEST_SIGNED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.gpg')
|
||||
TEST_SIGNATURE_FILE_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.sig')
|
||||
TEST_KEY_FILE = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'key0x5F3F7F75D210724D.asc')
|
||||
|
||||
@@ -91,6 +91,9 @@ class WorkflowInstance(models.Model):
|
||||
workflow = models.ForeignKey(Workflow, related_name='instances', verbose_name=_('Workflow'))
|
||||
document = models.ForeignKey(Document, related_name='workflows', verbose_name=_('Document'))
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self.workflow)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('document_states:workflow_instance_detail', args=[str(self.pk)])
|
||||
|
||||
@@ -108,24 +111,21 @@ class WorkflowInstance(models.Model):
|
||||
except AttributeError:
|
||||
return self.workflow.get_initial_state()
|
||||
|
||||
def get_last_transition(self):
|
||||
try:
|
||||
return self.get_last_log_entry().transition
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_last_log_entry(self):
|
||||
try:
|
||||
return self.log_entries.order_by('datetime').last()
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_last_transition(self):
|
||||
try:
|
||||
return self.get_last_log_entry().transition
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_transition_choices(self):
|
||||
return self.get_current_state().origin_transitions.all()
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self.workflow)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('document', 'workflow')
|
||||
verbose_name = _('Workflow instance')
|
||||
|
||||
@@ -7,10 +7,8 @@ from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from common.models import SharedUploadedFile
|
||||
from converter.exceptions import UnkownConvertError, UnknownFileFormat
|
||||
from converter.literals import (
|
||||
DEFAULT_PAGE_NUMBER, DEFAULT_ROTATION, DEFAULT_ZOOM_LEVEL
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import documents.models
|
||||
from django.conf import settings
|
||||
|
||||
import storage.backends.filebasedstorage
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import storage.backends.filebasedstorage
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -4,7 +4,6 @@ from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files import File
|
||||
from django.utils.timezone import now
|
||||
|
||||
from mayan.celery import app
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from json import loads
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework import status
|
||||
@@ -18,11 +14,9 @@ from rest_framework.test import APIClient
|
||||
from .models import Document, DocumentType
|
||||
from .test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_SMALL_DOCUMENT_FILENAME, TEST_NON_ASCII_DOCUMENT_FILENAME,
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH,
|
||||
TEST_SIGNED_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_NON_ASCII_DOCUMENT_PATH, TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_TYPE
|
||||
TEST_SMALL_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH,
|
||||
TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_TYPE
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from json import loads
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from .models import DeletedDocument, Document, DocumentType
|
||||
|
||||
TEST_ADMIN_PASSWORD = 'test_admin_password'
|
||||
@@ -24,7 +16,6 @@ TEST_SMALL_DOCUMENT_FILENAME = 'title_page.png'
|
||||
TEST_NON_ASCII_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png'
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png.zip'
|
||||
TEST_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf')
|
||||
TEST_SIGNED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.gpg')
|
||||
TEST_SMALL_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_SMALL_DOCUMENT_FILENAME)
|
||||
TEST_NON_ASCII_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_NON_ASCII_DOCUMENT_FILENAME)
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME)
|
||||
|
||||
@@ -2,27 +2,16 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from json import loads
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from .models import DeletedDocument, Document, DocumentType
|
||||
from .test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_SMALL_DOCUMENT_FILENAME, TEST_NON_ASCII_DOCUMENT_FILENAME,
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH,
|
||||
TEST_SIGNED_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_NON_ASCII_DOCUMENT_PATH, TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_TYPE
|
||||
TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
)
|
||||
|
||||
|
||||
@@ -45,7 +34,7 @@ class DocumentsViewsFunctionalTestCase(TestCase):
|
||||
self.assertTrue(logged_in)
|
||||
self.assertTrue(self.admin_user.is_authenticated())
|
||||
|
||||
with open(TEST_DOCUMENT_PATH) as file_object:
|
||||
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||
self.document = self.document_type.new_document(file_object=File(file_object), label='mayan_11_1.pdf')
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -23,9 +23,9 @@ class RecentSearch(models.Model):
|
||||
# Should be fixed by DRF v2.4.4
|
||||
# TODO: Fix after upgrade to DRF v2.4.4
|
||||
|
||||
query = models.TextField(verbose_name=_('Query'), editable=False)
|
||||
datetime_created = models.DateTimeField(verbose_name=_('Datetime created'), auto_now=True, db_index=True)
|
||||
hits = models.IntegerField(verbose_name=_('Hits'), editable=False)
|
||||
query = models.TextField(editable=False, verbose_name=_('Query'))
|
||||
datetime_created = models.DateTimeField(auto_now=True, db_index=True, verbose_name=_('Datetime created'))
|
||||
hits = models.IntegerField(editable=False, verbose_name=_('Hits'))
|
||||
|
||||
objects = RecentSearchManager()
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files.base import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.models import DocumentType
|
||||
from documents.search import document_search
|
||||
from documents.test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.models import DocumentType
|
||||
from documents.search import document_search
|
||||
from documents.test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models.loading import get_model
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from actstream.models import Action, any_stream
|
||||
|
||||
@@ -9,7 +9,6 @@ from common import (
|
||||
MayanAppConfig, menu_facet, menu_main, menu_object, menu_secondary,
|
||||
menu_sidebar, menu_multi_item
|
||||
)
|
||||
from common.utils import encapsulate
|
||||
from documents.models import Document
|
||||
from navigation import CombinedSource, SourceColumn
|
||||
from rest_api.classes import APIEndPoint
|
||||
|
||||
@@ -15,12 +15,6 @@ from .permissions import permission_folder_view
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FolderForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Folder
|
||||
fields = ('label',)
|
||||
|
||||
|
||||
class FolderListForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user', None)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -11,9 +11,9 @@ from documents.models import Document
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Folder(models.Model):
|
||||
label = models.CharField(max_length=128, verbose_name=_('Label'), db_index=True)
|
||||
label = models.CharField(db_index=True, max_length=128, verbose_name=_('Label'))
|
||||
user = models.ForeignKey(User, verbose_name=_('User'))
|
||||
datetime_created = models.DateTimeField(verbose_name=_('Datetime created'), auto_now_add=True)
|
||||
datetime_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Datetime created'))
|
||||
documents = models.ManyToManyField(Document, related_name='folders', verbose_name=_('Documents'))
|
||||
|
||||
def __str__(self):
|
||||
@@ -23,7 +23,7 @@ class Folder(models.Model):
|
||||
return reverse('folders:folder_view', args=[self.pk])
|
||||
|
||||
class Meta:
|
||||
unique_together = ('label', 'user')
|
||||
ordering = ('label',)
|
||||
unique_together = ('label', 'user')
|
||||
verbose_name = _('Folder')
|
||||
verbose_name_plural = _('Folders')
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.test import TestCase
|
||||
from authentication.test_views import (
|
||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||
)
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.models import DocumentType
|
||||
from documents.test_models import TEST_DOCUMENT_TYPE
|
||||
|
||||
from .models import Folder
|
||||
@@ -35,7 +35,6 @@ class FolderTestCase(TestCase):
|
||||
folder.delete()
|
||||
|
||||
def test_addition_of_documents(self):
|
||||
user = User.objects.all()[0]
|
||||
folder = Folder.objects.create(label='test', user=self.user)
|
||||
folder.documents.add(self.document)
|
||||
|
||||
@@ -44,7 +43,6 @@ class FolderTestCase(TestCase):
|
||||
folder.delete()
|
||||
|
||||
def test_addition_and_deletion_of_documents(self):
|
||||
user = User.objects.all()[0]
|
||||
folder = Folder.objects.create(label='test', user=self.user)
|
||||
folder.documents.add(self.document)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render_to_response
|
||||
@@ -21,7 +21,7 @@ from documents.models import Document
|
||||
from documents.views import DocumentListView
|
||||
from permissions import Permission
|
||||
|
||||
from .forms import FolderForm, FolderListForm
|
||||
from .forms import FolderListForm
|
||||
from .models import Folder
|
||||
from .permissions import (
|
||||
permission_folder_add_document, permission_folder_create,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -15,7 +15,7 @@ from .literals import (
|
||||
@python_2_unicode_compatible
|
||||
class SmartLink(models.Model):
|
||||
label = models.CharField(max_length=96, verbose_name=_('Label'))
|
||||
dynamic_label = models.CharField(blank=True, max_length=96, verbose_name=_('Dynamic label'), help_text=_('This expression will be evaluated against the current selected document.'))
|
||||
dynamic_label = models.CharField(blank=True, max_length=96, help_text=_('This expression will be evaluated against the current selected document.'), verbose_name=_('Dynamic label'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
|
||||
document_types = models.ManyToManyField(DocumentType, verbose_name=_('Document types'))
|
||||
|
||||
@@ -31,9 +31,6 @@ class SmartLink(models.Model):
|
||||
else:
|
||||
return self.label
|
||||
|
||||
def resolve_for(self, document):
|
||||
return ResolvedSmartLink(smart_link=self, queryset=self.get_linked_document_for(document))
|
||||
|
||||
def get_linked_document_for(self, document):
|
||||
if document.document_type.pk not in self.document_types.values_list('pk', flat=True):
|
||||
raise Exception(_('This smart link is not allowed for the selected document\'s type.'))
|
||||
@@ -57,6 +54,9 @@ class SmartLink(models.Model):
|
||||
else:
|
||||
return Document.objects.none()
|
||||
|
||||
def resolve_for(self, document):
|
||||
return ResolvedSmartLink(smart_link=self, queryset=self.get_linked_document_for(document))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Smart link')
|
||||
verbose_name_plural = _('Smart links')
|
||||
@@ -70,11 +70,11 @@ class ResolvedSmartLink(SmartLink):
|
||||
@python_2_unicode_compatible
|
||||
class SmartLinkCondition(models.Model):
|
||||
smart_link = models.ForeignKey(SmartLink, related_name='conditions', verbose_name=_('Smart link'))
|
||||
inclusion = models.CharField(default=INCLUSION_AND, max_length=16, choices=INCLUSION_CHOICES, help_text=_('The inclusion is ignored for the first item.'))
|
||||
foreign_document_data = models.CharField(max_length=128, verbose_name=_('Foreign document attribute'), help_text=_('This represents the metadata of all other documents.'))
|
||||
operator = models.CharField(max_length=16, choices=OPERATOR_CHOICES)
|
||||
expression = models.TextField(verbose_name=_('Expression'), help_text=_('This expression will be evaluated against the current document.'))
|
||||
negated = models.BooleanField(default=False, verbose_name=_('Negated'), help_text=_('Inverts the logic of the operator.'))
|
||||
inclusion = models.CharField(choices=INCLUSION_CHOICES, default=INCLUSION_AND, help_text=_('The inclusion is ignored for the first item.'), max_length=16)
|
||||
foreign_document_data = models.CharField(help_text=_('This represents the metadata of all other documents.'), max_length=128, verbose_name=_('Foreign document attribute'))
|
||||
operator = models.CharField(choices=OPERATOR_CHOICES, max_length=16)
|
||||
expression = models.TextField(help_text=_('This expression will be evaluated against the current document.'), verbose_name=_('Expression'))
|
||||
negated = models.BooleanField(default=False, help_text=_('Inverts the logic of the operator.'), verbose_name=_('Negated'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -85,7 +85,7 @@ class ResolvedSmartLinkView(DocumentListView):
|
||||
queryset = Document.objects.none()
|
||||
|
||||
if self.request.user.is_staff or self.request.user.is_superuser:
|
||||
messages.error(request, _('Smart link query error: %s' % exception))
|
||||
messages.error(self.request, _('Smart link query error: %s' % exception))
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ from .settings import DEFAULT_LOCK_TIMEOUT
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Lock(models.Model):
|
||||
creation_datetime = models.DateTimeField(verbose_name=_('Creation datetime'), auto_now_add=True)
|
||||
creation_datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Creation datetime'))
|
||||
timeout = models.IntegerField(default=DEFAULT_LOCK_TIMEOUT, verbose_name=_('Timeout'))
|
||||
name = models.CharField(max_length=64, verbose_name=_('Name'), unique=True)
|
||||
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
|
||||
|
||||
objects = LockManager()
|
||||
|
||||
|
||||
@@ -58,8 +58,6 @@ class MetadataApp(MayanAppConfig):
|
||||
ModelAttribute(Document, 'metadata__value', label=_('Metadata type value'), type_name='query')
|
||||
ModelAttribute(Document, 'metadata_value_of', label=_('Value of a metadata'), description=_('Return the value of a specific document metadata'), type_name=['property', 'indexing'])
|
||||
|
||||
SourceColumn(source=Document, label=_('Metadata'), attribute=encapsulate(lambda document: get_metadata_string(document)))
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(
|
||||
permission_metadata_document_add, permission_metadata_document_edit,
|
||||
@@ -67,6 +65,8 @@ class MetadataApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
SourceColumn(source=Document, label=_('Metadata'), attribute=encapsulate(lambda document: get_metadata_string(document)))
|
||||
|
||||
document_search.add_model_field(field='metadata__metadata_type__name', label=_('Metadata type'))
|
||||
document_search.add_model_field(field='metadata__value', label=_('Metadata value'))
|
||||
|
||||
@@ -79,6 +79,6 @@ class MetadataApp(MayanAppConfig):
|
||||
menu_sidebar.bind_links(links=[link_metadata_add, link_metadata_edit, link_metadata_remove], sources=['metadata:metadata_add', 'metadata:metadata_edit', 'metadata:metadata_remove', 'metadata:metadata_view'])
|
||||
menu_tools.bind_links(links=[link_documents_missing_required_metadata])
|
||||
|
||||
post_save.connect(post_document_type_metadata_type_add, dispatch_uid='post_document_type_metadata_type_add', sender=DocumentTypeMetadataType)
|
||||
post_delete.connect(post_document_type_metadata_type_delete, dispatch_uid='post_document_type_metadata_type_delete', sender=DocumentTypeMetadataType)
|
||||
post_document_type_change.connect(post_post_document_type_change_metadata, dispatch_uid='post_post_document_type_change_metadata', sender=Document)
|
||||
post_save.connect(post_document_type_metadata_type_add, dispatch_uid='post_document_type_metadata_type_add', sender=DocumentTypeMetadataType)
|
||||
|
||||
@@ -7,10 +7,7 @@ from django.utils.module_loading import import_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import MetadataType
|
||||
from .settings import (
|
||||
setting_available_functions, setting_available_models,
|
||||
setting_available_validators
|
||||
)
|
||||
from .settings import setting_available_functions, setting_available_models
|
||||
|
||||
|
||||
class MetadataForm(forms.Form):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -20,16 +20,12 @@ class MetadataType(models.Model):
|
||||
"""
|
||||
Define a type of metadata
|
||||
"""
|
||||
name = models.CharField(unique=True, max_length=48, verbose_name=_('Name'), help_text=_('Do not use python reserved words, or spaces.'))
|
||||
name = models.CharField(max_length=48, help_text=_('Do not use python reserved words, or spaces.'), unique=True, verbose_name=_('Name'))
|
||||
label = models.CharField(max_length=48, verbose_name=_('Label'))
|
||||
default = models.CharField(max_length=128, blank=True, null=True,
|
||||
verbose_name=_('Default'),
|
||||
help_text=_('Enter a string to be evaluated.'))
|
||||
default = models.CharField(blank=True, max_length=128, null=True, help_text=_('Enter a string to be evaluated.'), verbose_name=_('Default'))
|
||||
# TODO: Add enable_lookup boolean to allow users to switch the lookup on and
|
||||
# off without losing the lookup expression
|
||||
lookup = models.TextField(blank=True, null=True,
|
||||
verbose_name=_('Lookup'),
|
||||
help_text=_('Enter a string to be evaluated that returns an iterable.'))
|
||||
lookup = models.TextField(blank=True, null=True, help_text=_('Enter a string to be evaluated that returns an iterable.'), verbose_name=_('Lookup'))
|
||||
validation = models.CharField(blank=True, choices=validation_choices(), max_length=64, verbose_name=_('Validation function name'))
|
||||
# TODO: Find a different way to let users know what models and functions are
|
||||
# available now that we removed these from the help_text
|
||||
@@ -55,7 +51,7 @@ class DocumentMetadata(models.Model):
|
||||
"""
|
||||
document = models.ForeignKey(Document, related_name='metadata', verbose_name=_('Document'))
|
||||
metadata_type = models.ForeignKey(MetadataType, verbose_name=_('Type'))
|
||||
value = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('Value'), db_index=True)
|
||||
value = models.CharField(blank=True, db_index=True, max_length=255, null=True, verbose_name=_('Value'))
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self.metadata_type)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def move_content_from_documents_to_ocr_app(apps, schema_editor):
|
||||
DocumentPage = apps.get_model('documents', 'DocumentPage')
|
||||
|
||||
@@ -22,7 +22,7 @@ class DocumentTypeSettings(models.Model):
|
||||
@python_2_unicode_compatible
|
||||
class DocumentVersionOCRError(models.Model):
|
||||
document_version = models.ForeignKey(DocumentVersion, verbose_name=_('Document version'))
|
||||
datetime_submitted = models.DateTimeField(verbose_name=_('Date time submitted'), auto_now=True, db_index=True)
|
||||
datetime_submitted = models.DateTimeField(auto_now=True, db_index=True, verbose_name=_('Date time submitted'))
|
||||
result = models.TextField(blank=True, null=True, verbose_name=_('Result'))
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
from django.core.files.base import File
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.models import DocumentType
|
||||
from documents.test_models import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from documents.models import Document, DocumentType, DocumentVersion
|
||||
from permissions import Permission
|
||||
|
||||
from .forms import DocumentContentForm
|
||||
from .models import DocumentTypeSettings, DocumentVersionOCRError
|
||||
from .models import DocumentVersionOCRError
|
||||
from .permissions import (
|
||||
permission_ocr_content_view, permission_ocr_document,
|
||||
permission_ocr_document_delete, permission_document_type_ocr_setup
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common import (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from .classes import Permission
|
||||
from .models import Role, StoredPermission
|
||||
from .models import Role
|
||||
from .permissions import permission_role_view
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import sources.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -108,11 +108,11 @@ class StagingFolderSource(InteractiveSource):
|
||||
is_interactive = True
|
||||
source_type = SOURCE_CHOICE_STAGING
|
||||
|
||||
folder_path = models.CharField(max_length=255, verbose_name=_('Folder path'), help_text=_('Server side filesystem path.'))
|
||||
preview_width = models.IntegerField(verbose_name=_('Preview width'), help_text=_('Width value to be passed to the converter backend.'))
|
||||
preview_height = models.IntegerField(blank=True, null=True, verbose_name=_('Preview height'), help_text=_('Height value to be passed to the converter backend.'))
|
||||
uncompress = models.CharField(max_length=1, choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, verbose_name=_('Uncompress'), help_text=_('Whether to expand or not compressed archives.'))
|
||||
delete_after_upload = models.BooleanField(default=True, verbose_name=_('Delete after upload'), help_text=_('Delete the file after is has been successfully uploaded.'))
|
||||
folder_path = models.CharField(max_length=255, help_text=_('Server side filesystem path.'), verbose_name=_('Folder path'))
|
||||
preview_width = models.IntegerField(help_text=_('Width value to be passed to the converter backend.'), verbose_name=_('Preview width'))
|
||||
preview_height = models.IntegerField(blank=True, null=True, help_text=_('Height value to be passed to the converter backend.'), verbose_name=_('Preview height'))
|
||||
uncompress = models.CharField(choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, max_length=1, help_text=_('Whether to expand or not compressed archives.'), verbose_name=_('Uncompress'))
|
||||
delete_after_upload = models.BooleanField(default=True, help_text=_('Delete the file after is has been successfully uploaded.'), verbose_name=_('Delete after upload'))
|
||||
|
||||
def get_preview_size(self):
|
||||
dimensions = []
|
||||
@@ -155,7 +155,7 @@ class WebFormSource(InteractiveSource):
|
||||
source_type = SOURCE_CHOICE_WEB_FORM
|
||||
|
||||
# TODO: unify uncompress as an InteractiveSource field
|
||||
uncompress = models.CharField(max_length=1, choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, verbose_name=_('Uncompress'), help_text=_('Whether to expand or not compressed archives.'))
|
||||
uncompress = models.CharField(choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, help_text=_('Whether to expand or not compressed archives.'), max_length=1, verbose_name=_('Uncompress'))
|
||||
# Default path
|
||||
|
||||
def get_upload_file_object(self, form_data):
|
||||
@@ -175,9 +175,9 @@ class OutOfProcessSource(Source):
|
||||
|
||||
|
||||
class IntervalBaseModel(OutOfProcessSource):
|
||||
interval = models.PositiveIntegerField(default=DEFAULT_INTERVAL, verbose_name=_('Interval'), help_text=_('Interval in seconds between checks for new documents.'))
|
||||
document_type = models.ForeignKey(DocumentType, verbose_name=_('Document type'), help_text=_('Assign a document type to documents uploaded from this source.'))
|
||||
uncompress = models.CharField(max_length=1, choices=SOURCE_UNCOMPRESS_CHOICES, verbose_name=_('Uncompress'), help_text=_('Whether to expand or not, compressed archives.'))
|
||||
interval = models.PositiveIntegerField(default=DEFAULT_INTERVAL, help_text=_('Interval in seconds between checks for new documents.'), verbose_name=_('Interval'))
|
||||
document_type = models.ForeignKey(DocumentType, help_text=_('Assign a document type to documents uploaded from this source.'), verbose_name=_('Document type'))
|
||||
uncompress = models.CharField(choices=SOURCE_UNCOMPRESS_CHOICES, help_text=_('Whether to expand or not, compressed archives.'), max_length=1, verbose_name=_('Uncompress'))
|
||||
|
||||
def _get_periodic_task_name(self, pk=None):
|
||||
return 'check_interval_source-%i' % (pk or self.pk)
|
||||
@@ -223,7 +223,7 @@ class IntervalBaseModel(OutOfProcessSource):
|
||||
class EmailBaseModel(IntervalBaseModel):
|
||||
host = models.CharField(max_length=128, verbose_name=_('Host'))
|
||||
ssl = models.BooleanField(default=True, verbose_name=_('SSL'))
|
||||
port = models.PositiveIntegerField(blank=True, null=True, verbose_name=_('Port'), help_text=_('Typical choices are 110 for POP3, 995 for POP3 over SSL, 143 for IMAP, 993 for IMAP over SSL.'))
|
||||
port = models.PositiveIntegerField(blank=True, null=True, help_text=_('Typical choices are 110 for POP3, 995 for POP3 over SSL, 143 for IMAP, 993 for IMAP over SSL.'), verbose_name=_('Port'))
|
||||
username = models.CharField(max_length=96, verbose_name=_('Username'))
|
||||
password = models.CharField(max_length=96, verbose_name=_('Password'))
|
||||
|
||||
@@ -302,7 +302,7 @@ class POP3Email(EmailBaseModel):
|
||||
class IMAPEmail(EmailBaseModel):
|
||||
source_type = SOURCE_CHOICE_EMAIL_IMAP
|
||||
|
||||
mailbox = models.CharField(max_length=64, default=DEFAULT_IMAP_MAILBOX, verbose_name=_('Mailbox'), help_text=_('Mail from which to check for messages with attached documents.'))
|
||||
mailbox = models.CharField(default=DEFAULT_IMAP_MAILBOX, help_text=_('Mail from which to check for messages with attached documents.'), max_length=64, verbose_name=_('Mailbox'))
|
||||
|
||||
# http://www.doughellmann.com/PyMOTW/imaplib/
|
||||
def check_source(self):
|
||||
@@ -341,7 +341,7 @@ class IMAPEmail(EmailBaseModel):
|
||||
class WatchFolderSource(IntervalBaseModel):
|
||||
source_type = SOURCE_CHOICE_WATCH
|
||||
|
||||
folder_path = models.CharField(max_length=255, verbose_name=_('Folder path'), help_text=_('Server side filesystem path.'))
|
||||
folder_path = models.CharField(help_text=_('Server side filesystem path.'), max_length=255, verbose_name=_('Folder path'))
|
||||
|
||||
def check_source(self):
|
||||
# Force self.folder_path to unicode to avoid os.listdir returning
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files import File
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.celery import app
|
||||
|
||||
@@ -4,23 +4,18 @@ import shutil
|
||||
import tempfile
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from sources.literals import SOURCE_CHOICE_WEB_FORM
|
||||
from sources.models import WebFormSource
|
||||
|
||||
from documents.test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_TYPE,
|
||||
TEST_NON_ASCII_DOCUMENT_FILENAME, TEST_NON_ASCII_DOCUMENT_PATH,
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH
|
||||
TEST_DOCUMENT_TYPE, TEST_NON_ASCII_DOCUMENT_FILENAME,
|
||||
TEST_NON_ASCII_DOCUMENT_PATH, TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH
|
||||
)
|
||||
|
||||
from .literals import SOURCE_UNCOMPRESS_CHOICE_N, SOURCE_UNCOMPRESS_CHOICE_Y
|
||||
from .literals import SOURCE_UNCOMPRESS_CHOICE_Y
|
||||
from .models import WatchFolderSource
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
@@ -16,13 +13,8 @@ from documents.test_models import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH,
|
||||
TEST_DOCUMENT_DESCRIPTION, TEST_DOCUMENT_TYPE,
|
||||
TEST_NON_ASCII_DOCUMENT_FILENAME, TEST_NON_ASCII_DOCUMENT_PATH,
|
||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH
|
||||
)
|
||||
|
||||
from .literals import SOURCE_UNCOMPRESS_CHOICE_N, SOURCE_UNCOMPRESS_CHOICE_Y
|
||||
from .models import WatchFolderSource
|
||||
|
||||
|
||||
class UploadDocumentTestCase(TestCase):
|
||||
"""
|
||||
|
||||
@@ -11,8 +11,8 @@ from .literals import COLOR_CHOICES, COLOR_CODES
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Tag(models.Model):
|
||||
label = models.CharField(max_length=128, verbose_name=_('Label'), unique=True, db_index=True)
|
||||
color = models.CharField(max_length=3, choices=COLOR_CHOICES, verbose_name=_('Color'))
|
||||
label = models.CharField(db_index=True, max_length=128, unique=True, verbose_name=_('Label'))
|
||||
color = models.CharField(choices=COLOR_CHOICES, max_length=3, verbose_name=_('Color'))
|
||||
documents = models.ManyToManyField(Document, related_name='tags', verbose_name=_('Documents'))
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ from permissions import PermissionNamespace
|
||||
|
||||
namespace = PermissionNamespace('user_management', _('User management'))
|
||||
|
||||
permission_user_create = namespace.add_permission(name='user_create', label=_('Create new users'))
|
||||
permission_user_edit = namespace.add_permission(name='user_edit', label=_('Edit existing users'))
|
||||
permission_user_view = namespace.add_permission(name='user_view', label=_('View existing users'))
|
||||
permission_user_delete = namespace.add_permission(name='user_delete', label=_('Delete existing users'))
|
||||
permission_group_create = namespace.add_permission(name='group_create', label=_('Create new groups'))
|
||||
permission_group_delete = namespace.add_permission(name='group_delete', label=_('Delete existing groups'))
|
||||
permission_group_edit = namespace.add_permission(name='group_edit', label=_('Edit existing groups'))
|
||||
permission_group_view = namespace.add_permission(name='group_view', label=_('View existing groups'))
|
||||
permission_group_delete = namespace.add_permission(name='group_delete', label=_('Delete existing groups'))
|
||||
permission_user_create = namespace.add_permission(name='user_create', label=_('Create new users'))
|
||||
permission_user_delete = namespace.add_permission(name='user_delete', label=_('Delete existing users'))
|
||||
permission_user_edit = namespace.add_permission(name='user_edit', label=_('Edit existing users'))
|
||||
permission_user_view = namespace.add_permission(name='user_view', label=_('View existing users'))
|
||||
|
||||
@@ -13,6 +13,13 @@ from .views import (
|
||||
|
||||
urlpatterns = patterns(
|
||||
'user_management.views',
|
||||
url(r'^group/list/$', GroupListView.as_view(), name='group_list'),
|
||||
url(r'^group/add/$', GroupCreateView.as_view(), name='group_add'),
|
||||
url(r'^group/(?P<pk>\d+)/edit/$', GroupEditView.as_view(), name='group_edit'),
|
||||
url(r'^group/(?P<group_id>\d+)/delete/$', 'group_delete', name='group_delete'),
|
||||
url(r'^group/multiple/delete/$', 'group_multiple_delete', name='group_multiple_delete'),
|
||||
url(r'^group/(?P<pk>\d+)/members/$', GroupMembersView.as_view(), name='group_members'),
|
||||
|
||||
url(r'^user/list/$', UserListView.as_view(), name='user_list'),
|
||||
url(r'^user/add/$', 'user_add', name='user_add'),
|
||||
url(r'^user/(?P<user_id>\d+)/edit/$', 'user_edit', name='user_edit'),
|
||||
@@ -21,13 +28,6 @@ urlpatterns = patterns(
|
||||
url(r'^user/(?P<user_id>\d+)/set_password/$', 'user_set_password', name='user_set_password'),
|
||||
url(r'^user/multiple/set_password/$', 'user_multiple_set_password', name='user_multiple_set_password'),
|
||||
url(r'^user/(?P<pk>\d+)/groups/$', UserGroupsView.as_view(), name='user_groups'),
|
||||
|
||||
url(r'^group/list/$', GroupListView.as_view(), name='group_list'),
|
||||
url(r'^group/add/$', GroupCreateView.as_view(), name='group_add'),
|
||||
url(r'^group/(?P<pk>\d+)/edit/$', GroupEditView.as_view(), name='group_edit'),
|
||||
url(r'^group/(?P<group_id>\d+)/delete/$', 'group_delete', name='group_delete'),
|
||||
url(r'^group/multiple/delete/$', 'group_multiple_delete', name='group_multiple_delete'),
|
||||
url(r'^group/(?P<pk>\d+)/members/$', GroupMembersView.as_view(), name='group_members'),
|
||||
)
|
||||
|
||||
api_urls = patterns(
|
||||
|
||||
Reference in New Issue
Block a user