Audit common app

Add support to override settings of the FilteredSelectionForm
via subclass attributes. Add keyword arguments to calls.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-02 14:32:22 -04:00
parent 92e615ce4c
commit 125c133334
14 changed files with 134 additions and 99 deletions

View File

@@ -301,7 +301,9 @@ class Template(object):
self.__class__._registry[name] = self
def get_absolute_url(self):
return reverse('rest_api:template-detail', args=(self.name,))
return reverse(
viewname='rest_api:template-detail', kwargs={'template_pk': self.name}
)
def render(self, request):
context = {

View File

@@ -9,6 +9,8 @@ from django.db import models
from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList
from .classes import Package
from .models import UserLocaleProfile
from .utils import resolve_attribute
@@ -134,12 +136,20 @@ class FilteredSelectionForm(forms.Form):
Form to select the from a list of choice filtered by access. Can be
configure to allow single or multiple selection.
"""
_field_name = None
_label = None
_help_text = None
_permission = None
_queryset = None
_widget_class = None
_widget_attributes = None
def __init__(self, *args, **kwargs):
field_name = kwargs.pop('field_name', None)
label = kwargs.pop('label', None)
help_text = kwargs.pop('help_text', None)
permission = kwargs.pop('permission', None)
queryset = kwargs.pop('queryset', None)
field_name = self._field_name or kwargs.pop('field_name', None)
label = self._label or kwargs.pop('label', None)
help_text = self._help_text or kwargs.pop('help_text', None)
permission = self._permission or kwargs.pop('permission', None)
queryset = self.get_queryset() or kwargs.pop('queryset', None)
if queryset is None:
model = kwargs.pop('model', None)
@@ -150,12 +160,19 @@ class FilteredSelectionForm(forms.Form):
queryset = model.objects.all()
user = kwargs.pop('user', None)
widget_class = kwargs.pop('widget_class', None)
widget_attributes = kwargs.pop('widget_attributes', {'size': '10'})
user = self.get_user() or kwargs.pop('user', None)
widget_class = self._widget_class or kwargs.pop('widget_class', None)
widget_attributes = self._widget_attributes or kwargs.pop(
'widget_attributes', {'size': '10'}
)
if not widget_class:
if kwargs.pop('allow_multiple', False):
if self._allow_multiple is not None:
allow_multiple = self._allow_multiple
else:
allow_multiple = self.kwargs.pop('allow_multiple', False)
if allow_multiple:
extra_kwargs = {}
field_class = forms.ModelMultipleChoiceField
widget_class = forms.widgets.SelectMultiple
@@ -177,6 +194,12 @@ class FilteredSelectionForm(forms.Form):
widget=widget_class(attrs=widget_attributes), **extra_kwargs
)
def get_queryset(self):
return self._queryset
def get_user(self):
return None
class LicenseForm(FileDisplayForm):
DIRECTORY = ()

View File

@@ -46,7 +46,7 @@ class DeleteExtraDataMixin(object):
else:
self.object.delete()
return HttpResponseRedirect(success_url)
return HttpResponseRedirect(redirect_to=success_url)
class DynamicFormViewMixin(object):
@@ -179,9 +179,9 @@ class MultipleInstanceActionMixin(object):
def get_success_message(self, count):
return ungettext(
self.success_message,
self.success_message_plural,
count
singular=self.success_message,
plural=self.success_message_plural,
number=count
) % {
'count': count,
}
@@ -197,11 +197,11 @@ class MultipleInstanceActionMixin(object):
count += 1
messages.success(
self.request,
self.get_success_message(count=count)
request=self.request,
message=self.get_success_message(count=count)
)
return HttpResponseRedirect(self.get_success_url())
return HttpResponseRedirect(redirect_to=self.get_success_url())
class MultipleObjectMixin(object):
@@ -278,9 +278,9 @@ class ObjectActionMixin(object):
def get_success_message(self, count):
return ungettext(
self.success_message,
self.success_message_plural,
count
singular=self.success_message,
plural=self.success_message_plural,
number=count
) % {
'count': count,
}
@@ -299,14 +299,15 @@ class ObjectActionMixin(object):
pass
except ActionError:
messages.error(
self.request, self.error_message % {'instance': instance}
request=self.request,
message=self.error_message % {'instance': instance}
)
else:
self.action_count += 1
messages.success(
self.request,
self.get_success_message(count=self.action_count)
request=self.request,
message=self.get_success_message(count=self.action_count)
)
@@ -321,17 +322,20 @@ class ObjectListPermissionFilterMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.access_object_retrieve_method and self.object_permission:
AccessControlList.objects.check_access(
permissions=(self.object_permission,), user=request.user,
obj=getattr(self, self.access_object_retrieve_method)()
obj=getattr(self, self.access_object_retrieve_method)(),
permissions=(self.object_permission,), user=request.user
)
return super(ObjectListPermissionFilterMixin, self).dispatch(request, *args, **kwargs)
return super(ObjectListPermissionFilterMixin, self).dispatch(
request, *args, **kwargs
)
def get_queryset(self):
queryset = super(ObjectListPermissionFilterMixin, self).get_queryset()
if not self.access_object_retrieve_method and self.object_permission:
return AccessControlList.objects.filter_by_access(
self.object_permission, self.request.user, queryset=queryset
obj=self.object_permission, queryset=queryset,
user=self.request.user
)
else:
return queryset
@@ -368,9 +372,10 @@ class ObjectPermissionCheckMixin(object):
if self.object_permission:
try:
AccessControlList.objects.check_access(
permissions=self.object_permission, user=request.user,
obj=self.get_permission_object(),
related=getattr(self, 'object_permission_related', None)
permissions=self.object_permission,
related=getattr(self, 'object_permission_related', None),
user=request.user
)
except PermissionDenied:
if self.object_permission_raise_404:
@@ -437,8 +442,8 @@ class ViewPermissionCheckMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.view_permission:
Permission.check_permissions(
requester=self.request.user,
permissions=(self.view_permission,)
permissions=(self.view_permission,),
requester=self.request.user
)
return super(

View File

@@ -18,6 +18,7 @@ from .storages import storage_sharedupload
logger = logging.getLogger(__name__)
#TODO: move outside of models.py
def upload_to(instance, filename):
return 'shared-file-{}'.format(uuid.uuid4().hex)

View File

@@ -48,8 +48,8 @@ class PurePaginator(Paginator):
self.allow_empty_first_page = allow_empty_first_page
self.object_list = object_list
self.orphans = orphans
self.per_page = per_page
self.page_kwarg = page_kwarg
self.per_page = per_page
self.request = request
def page(self, number):

View File

@@ -7,5 +7,5 @@ from mayan.apps.permissions import PermissionNamespace
namespace = PermissionNamespace(label=_('Common'), name='common')
permission_error_log_view = namespace.add_permission(
name='error_log_view', label=_('View error log')
label=_('View error log'), name='error_log_view'
)

View File

@@ -5,13 +5,13 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.task_manager.classes import CeleryQueue
queue_default = CeleryQueue(
name='default', label=_('Default'), is_default_queue=True
is_default_queue=True, label=_('Default'), name='default'
)
queue_tools = CeleryQueue(name='tools', label=_('Tools'))
queue_tools = CeleryQueue(label=_('Tools'), name='tools')
queue_common_periodic = CeleryQueue(
name='common_periodic', label=_('Common periodic'), transient=True
label=_('Common periodic'), name='common_periodic', transient=True
)
queue_common_periodic.add_task_type(
name='mayan.apps.common.tasks.task_delete_stale_uploads',
label=_('Delete stale uploads')
label=_('Delete stale uploads'),
name='mayan.apps.common.tasks.task_delete_stale_uploads'
)

View File

@@ -11,7 +11,7 @@ from mayan.apps.smart_settings import Namespace
from .literals import DEFAULT_COMMON_HOME_VIEW
namespace = Namespace(name='common', label=_('Common'))
namespace = Namespace(label=_('Common'), name='common')
setting_auto_logging = namespace.add_setting(
global_name='COMMON_AUTO_LOGGING',
@@ -86,7 +86,7 @@ setting_temporary_directory = namespace.add_setting(
is_path=True
)
namespace = Namespace(name='django', label=_('Django'))
namespace = Namespace(label=_('Django'), name='django')
setting_django_allowed_hosts = namespace.add_setting(
global_name='ALLOWED_HOSTS', default=settings.ALLOWED_HOSTS,
@@ -402,7 +402,7 @@ setting_django_wsgi_application = namespace.add_setting(
),
)
namespace = Namespace(name='celery', label=_('Celery'))
namespace = Namespace(label=_('Celery'), name='celery')
setting_celery_always_eager = namespace.add_setting(
global_name='CELERY_TASK_ALWAYS_EAGER',

View File

@@ -37,9 +37,7 @@ class BaseTestCase(DatabaseConversionMixin, ACLBaseTestMixin, ContentTypeCheckMi
class GenericViewTestCase(BaseTestCase):
def setUp(self):
super(GenericViewTestCase, self).setUp()
self.has_test_view = False
has_test_view = False
def tearDown(self):
from mayan.urls import urlpatterns
@@ -87,13 +85,10 @@ class GenericViewTestCase(BaseTestCase):
path=path, data=data, follow=follow
)
def login(self, username, password):
logged_in = self.client.login(username=username, password=password)
def login(self, *args, **kwargs):
logged_in = self.client.login(*args, **kwargs)
user = get_user_model().objects.get(username=username)
self.assertTrue(logged_in)
self.assertTrue(user.is_authenticated)
return logged_in
def login_user(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)

View File

@@ -12,7 +12,7 @@ TEST_TEMPLATE_RESULT = '<div'
class CommonAPITestCase(BaseAPITestCase):
def test_content_type_list_view(self):
response = self.client.get(reverse('rest_api:content-type-list'))
response = self.client.get(reverse(viewname='rest_api:content-type-list'))
self.assertEqual(response.status_code, 200)
@override_settings(LANGUAGE_CODE='de')

View File

@@ -6,5 +6,5 @@ from mayan.apps.user_management.tests.mixins import UserTestMixin
class UserLocaleProfileTestCase(UserTestMixin, BaseTestCase):
def test_natural_keys(self):
self._create_user()
self._create_test_user()
self._test_database_conversion('auth', 'common')

View File

@@ -40,7 +40,19 @@ class CommonViewTestCase(GenericViewTestCase):
}, follow=True
)
def test_object_error_list_view_with_permissions(self):
def test_object_error_list_view_no_permissions(self):
self._create_error_log_entry()
self.login_user()
response = self._request_object_error_log_list()
self.assertNotContains(
response=response, text=TEST_ERROR_LOG_ENTRY_RESULT,
status_code=403
)
def test_object_error_list_view_with_access(self):
self._create_error_log_entry()
self.login_user()
@@ -54,15 +66,3 @@ class CommonViewTestCase(GenericViewTestCase):
response=response, text=TEST_ERROR_LOG_ENTRY_RESULT,
status_code=200
)
def test_object_error_list_view_no_permissions(self):
self._create_error_log_entry()
self.login_user()
response = self._request_object_error_log_list()
self.assertNotContains(
response=response, text=TEST_ERROR_LOG_ENTRY_RESULT,
status_code=403
)

View File

@@ -15,67 +15,69 @@ from .views import (
)
urlpatterns = [
url(r'^$', RootView.as_view(), name='root'),
url(r'^home/$', HomeView.as_view(), name='home'),
url(r'^about/$', AboutView.as_view(), name='about_view'),
url(regex=r'^$', name='root', view=RootView.as_view()),
url(regex=r'^home/$', name='home', view=HomeView.as_view()),
url(regex=r'^about/$', name='about_view', view=AboutView.as_view()),
url(
r'^check_version/$', CheckVersionView.as_view(),
name='check_version_view'
regex=r'^check_version/$', name='check_version_view',
view=CheckVersionView.as_view()
),
url(r'^license/$', LicenseView.as_view(), name='license_view'),
url(regex=r'^license/$', name='license_view', view=LicenseView.as_view()),
url(
r'^packages/licenses/$', PackagesLicensesView.as_view(),
name='packages_licenses_view'
regex=r'^packages/licenses/$', name='packages_licenses_view',
view=PackagesLicensesView.as_view()
),
url(
r'^object/multiple/action/$', multi_object_action_view,
name='multi_object_action_view'
regex=r'^objects/multiple/action/$', name='multi_object_action_view',
view=multi_object_action_view
),
url(r'^setup/$', SetupListView.as_view(), name='setup_list'),
url(r'^tools/$', ToolsListView.as_view(), name='tools_list'),
url(regex=r'^setup/$', name='setup_list', view=SetupListView.as_view()),
url(regex=r'^tools/$', name='tools_list', view=ToolsListView.as_view()),
url(
r'^user/locale/$', CurrentUserLocaleProfileDetailsView.as_view(),
name='current_user_locale_profile_details'
regex=r'^users/current/locale/$',
name='current_user_locale_profile_details',
view=CurrentUserLocaleProfileDetailsView.as_view()
),
url(
r'^user/locale/edit/$', CurrentUserLocaleProfileEditView.as_view(),
name='current_user_locale_profile_edit'
regex=r'^users/current/locale/edit/$',
name='current_user_locale_profile_edit',
view=CurrentUserLocaleProfileEditView.as_view()
),
url(
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/errors/$',
ObjectErrorLogEntryListView.as_view(), name='object_error_list'
regex=r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/errors/$',
name='object_error_list', view=ObjectErrorLogEntryListView.as_view()
),
url(
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/errors/clear/$',
ObjectErrorLogEntryListClearView.as_view(),
name='object_error_list_clear'
regex=r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/errors/clear/$',
name='object_error_list_clear',
view=ObjectErrorLogEntryListClearView.as_view()
),
]
urlpatterns += [
url(
r'^favicon\.ico$', FaviconRedirectView.as_view()
regex=r'^favicon\.ico$', view=FaviconRedirectView.as_view()
),
url(
r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog,
name='javascript_catalog'
regex=r'^jsi18n/(?P<packages>\S+?)/$', name='javascript_catalog',
view=javascript_catalog
),
url(
r'^set_language/$', set_language, name='set_language'
regex=r'^set_language/$', name='set_language', view=set_language
),
]
api_urls = [
url(
r'^content_types/$', APIContentTypeList.as_view(),
name='content-type-list'
regex=r'^content_types/$', name='content-type-list',
view=APIContentTypeList.as_view()
),
url(
r'^templates/$', APITemplateListView.as_view(),
name='template-list'
regex=r'^templates/$', name='template-list',
view=APITemplateListView.as_view()
),
url(
r'^templates/(?P<name>[-\w]+)/$', APITemplateView.as_view(),
name='template-detail'
regex=r'^templates/(?P<name>[-\w]+)/$', name='template-detail',
view=APITemplateView.as_view()
),
]

View File

@@ -15,6 +15,9 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import RedirectView, TemplateView
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.mixins import (
ContentTypeViewMixin, ExternalObjectViewMixin
)
from .exceptions import NotLatestVersion, UnknownLatestVersion
from .forms import (
@@ -171,7 +174,9 @@ class ObjectErrorLogEntryListClearView(ConfirmView):
)
class ObjectErrorLogEntryListView(SingleObjectListView):
class ObjectErrorLogEntryListView(ContentTypeViewMixin, ExternalObjectViewMixin, SingleObjectListView):
#TODO: Update for MERC 6. Return 404.
"""
def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access(
obj=self.get_object(), permissions=permission_error_log_view,
@@ -181,6 +186,7 @@ class ObjectErrorLogEntryListView(SingleObjectListView):
return super(ObjectErrorLogEntryListView, self).dispatch(
request, *args, **kwargs
)
"""
def get_extra_context(self):
return {
@@ -202,6 +208,7 @@ class ObjectErrorLogEntryListView(SingleObjectListView):
'title': _('Error log entries for: %s' % self.get_object()),
}
"""
def get_object(self):
content_type = get_object_or_404(
klass=ContentType, app_label=self.kwargs['app_label'],
@@ -211,9 +218,9 @@ class ObjectErrorLogEntryListView(SingleObjectListView):
return get_object_or_404(
klass=content_type.model_class(), pk=self.kwargs['object_id']
)
"""
def get_object_list(self):
return self.get_object().error_logs.all()
return self.get_external_object().error_logs.all()
class PackagesLicensesView(SimpleView):