Make folders and tags apps multitenant.
This commit is contained in:
@@ -70,7 +70,7 @@
|
||||
{% if not user.is_authenticated %}
|
||||
{% trans 'Anonymous' %}
|
||||
{% else %}
|
||||
<li><a href="{% url 'common:current_user_details' %}" title="{% trans 'User details' %}">{{ user.get_full_name|default:user }} <i class="fa fa-user"></i></a></li>
|
||||
<li><a href="{% url 'common:current_user_details' %}" title="{% trans 'User details' %}">{{ request.organization }}: {{ user.get_full_name|default:user }} <i class="fa fa-user"></i></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -104,7 +104,7 @@ class DocumentTypeSelectForm(forms.Form):
|
||||
as form #1 in the document creation wizard
|
||||
"""
|
||||
document_type = forms.ModelChoiceField(
|
||||
queryset=DocumentType.objects.all(), label=_('Document type')
|
||||
queryset=DocumentType.on_organization.all(), label=_('Document type')
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
|
||||
@@ -132,7 +133,8 @@ class RecentDocumentManager(models.Manager):
|
||||
|
||||
if user.is_authenticated():
|
||||
return document_model.objects.filter(
|
||||
recentdocument__user=user
|
||||
recentdocument__user=user,
|
||||
document_type__organization__id=settings.ORGANIZATION_ID
|
||||
).order_by('-recentdocument__datetime_accessed')
|
||||
else:
|
||||
return document_model.objects.none()
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('organizations', '0001_initial'),
|
||||
('documents', '0028_newversionblock'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='documenttype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=1, to='organizations.Organization'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('organizations', '0001_initial'),
|
||||
('documents', '0029_documenttype_organization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='document',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=1, to='organizations.Organization'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('documents', '0030_document_organization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='document',
|
||||
name='organization',
|
||||
),
|
||||
]
|
||||
21
mayan/apps/documents/migrations/0032_auto_20160307_0504.py
Normal file
21
mayan/apps/documents/migrations/0032_auto_20160307_0504.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import organizations.shortcuts
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('documents', '0031_remove_document_organization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='documenttype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
@@ -24,6 +24,9 @@ from converter.exceptions import InvalidOfficeFormat, PageCountError
|
||||
from converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION
|
||||
from converter.models import Transformation
|
||||
from mimetype.api import get_mimetype
|
||||
from organizations.models import Organization
|
||||
from organizations.managers import CurrentOrganizationManager
|
||||
from organizations.shortcuts import get_current_organization
|
||||
from permissions import Permission
|
||||
|
||||
from .events import (
|
||||
@@ -62,6 +65,9 @@ class DocumentType(models.Model):
|
||||
Define document types or classes to which a specific set of
|
||||
properties can be attached
|
||||
"""
|
||||
organization = models.ForeignKey(
|
||||
Organization, default=get_current_organization
|
||||
)
|
||||
label = models.CharField(
|
||||
max_length=32, unique=True, verbose_name=_('Label')
|
||||
)
|
||||
@@ -87,6 +93,7 @@ class DocumentType(models.Model):
|
||||
)
|
||||
|
||||
objects = DocumentTypeManager()
|
||||
on_organization = CurrentOrganizationManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
@@ -72,7 +72,7 @@ class DocumentListView(SingleObjectListView):
|
||||
object_permission = permission_document_view
|
||||
|
||||
def get_document_queryset(self):
|
||||
return Document.objects.all()
|
||||
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.get_document_queryset().filter(is_stub=False)
|
||||
@@ -88,7 +88,7 @@ class DeletedDocumentListView(DocumentListView):
|
||||
}
|
||||
|
||||
def get_document_queryset(self):
|
||||
queryset = Document.trash.all()
|
||||
queryset = Document.trash.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(
|
||||
@@ -111,7 +111,7 @@ class DeletedDocumentDeleteView(ConfirmView):
|
||||
|
||||
def object_action(self, instance):
|
||||
source_document = get_object_or_404(
|
||||
Document.passthrough, pk=instance.pk
|
||||
Document.passthrough.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=instance.pk
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -139,14 +139,15 @@ class DeletedDocumentDeleteManyView(MultipleInstanceActionMixin, DeletedDocument
|
||||
extra_context = {
|
||||
'title': _('Delete the selected documents?')
|
||||
}
|
||||
model = DeletedDocument
|
||||
success_message = '%(count)d document deleted.'
|
||||
success_message_plural = '%(count)d documents deleted.'
|
||||
|
||||
def get_queryset(self):
|
||||
return DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentEditView(SingleObjectEditView):
|
||||
form_class = DocumentForm
|
||||
model = Document
|
||||
object_permission = permission_document_properties_edit
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -162,16 +163,19 @@ class DocumentEditView(SingleObjectEditView):
|
||||
'title': _('Edit properties of document: %s') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_save_extra_data(self):
|
||||
return {
|
||||
'_user': self.request.user
|
||||
}
|
||||
def get_queryset(self):
|
||||
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse(
|
||||
'documents:document_properties', args=(self.get_object().pk,)
|
||||
)
|
||||
|
||||
def get_save_extra_data(self):
|
||||
return {
|
||||
'_user': self.request.user
|
||||
}
|
||||
|
||||
|
||||
class DocumentRestoreView(ConfirmView):
|
||||
extra_context = {
|
||||
@@ -180,7 +184,7 @@ class DocumentRestoreView(ConfirmView):
|
||||
|
||||
def object_action(self, instance):
|
||||
source_document = get_object_or_404(
|
||||
Document.passthrough, pk=instance.pk
|
||||
Document.passthrough.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=instance.pk
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -210,10 +214,12 @@ class DocumentRestoreManyView(MultipleInstanceActionMixin, DocumentRestoreView):
|
||||
extra_context = {
|
||||
'title': _('Restore the selected documents?')
|
||||
}
|
||||
model = DeletedDocument
|
||||
success_message = '%(count)d document restored.'
|
||||
success_message_plural = '%(count)d documents restored.'
|
||||
|
||||
def get_queryset(self):
|
||||
return DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentPageListView(SingleObjectListView):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -232,7 +238,7 @@ class DocumentPageListView(SingleObjectListView):
|
||||
).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_document(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_document().pages.all()
|
||||
@@ -287,11 +293,10 @@ class DocumentPageView(SimpleView):
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(DocumentPage, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__pk=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
|
||||
class DocumentPreviewView(SingleObjectDetailView):
|
||||
model = Document
|
||||
object_permission = permission_document_view
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -309,6 +314,9 @@ class DocumentPreviewView(SingleObjectDetailView):
|
||||
'title': _('Preview of document: %s') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTrashView(ConfirmView):
|
||||
def get_extra_context(self):
|
||||
@@ -318,7 +326,7 @@ class DocumentTrashView(ConfirmView):
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse('documents:document_list_recent')
|
||||
@@ -348,7 +356,6 @@ class DocumentTrashView(ConfirmView):
|
||||
|
||||
|
||||
class DocumentTrashManyView(MultipleInstanceActionMixin, DocumentTrashView):
|
||||
model = Document
|
||||
success_message = '%(count)d document moved to the trash.'
|
||||
success_message_plural = '%(count)d documents moved to the trash.'
|
||||
|
||||
@@ -357,10 +364,13 @@ class DocumentTrashManyView(MultipleInstanceActionMixin, DocumentTrashView):
|
||||
'title': _('Move the selected documents to the trash?')
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeDocumentListView(DocumentListView):
|
||||
def get_document_type(self):
|
||||
return get_object_or_404(DocumentType, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
def get_document_queryset(self):
|
||||
return self.get_document_type().documents.all()
|
||||
@@ -374,7 +384,6 @@ class DocumentTypeDocumentListView(DocumentListView):
|
||||
|
||||
|
||||
class DocumentTypeListView(SingleObjectListView):
|
||||
model = DocumentType
|
||||
view_permission = permission_document_type_view
|
||||
|
||||
def get_extra_context(self):
|
||||
@@ -383,13 +392,15 @@ class DocumentTypeListView(SingleObjectListView):
|
||||
'title': _('Document types'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeCreateView(SingleObjectCreateView):
|
||||
fields = (
|
||||
'label', 'trash_time_period', 'trash_time_unit', 'delete_time_period',
|
||||
'delete_time_unit'
|
||||
)
|
||||
model = DocumentType
|
||||
post_action_redirect = reverse_lazy('documents:document_type_list')
|
||||
view_permission = permission_document_type_create
|
||||
|
||||
@@ -398,9 +409,11 @@ class DocumentTypeCreateView(SingleObjectCreateView):
|
||||
'title': _('Create document type'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeDeleteView(SingleObjectDeleteView):
|
||||
model = DocumentType
|
||||
post_action_redirect = reverse_lazy('documents:document_type_list')
|
||||
view_permission = permission_document_type_delete
|
||||
|
||||
@@ -411,13 +424,15 @@ class DocumentTypeDeleteView(SingleObjectDeleteView):
|
||||
'title': _('Delete the document type: %s?') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeEditView(SingleObjectEditView):
|
||||
fields = (
|
||||
'label', 'trash_time_period', 'trash_time_unit', 'delete_time_period',
|
||||
'delete_time_unit'
|
||||
)
|
||||
model = DocumentType
|
||||
post_action_redirect = reverse_lazy('documents:document_type_list')
|
||||
view_permission = permission_document_type_edit
|
||||
|
||||
@@ -427,13 +442,15 @@ class DocumentTypeEditView(SingleObjectEditView):
|
||||
'title': _('Edit document type: %s') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeFilenameListView(SingleObjectListView):
|
||||
model = DocumentType
|
||||
view_permission = permission_document_type_view
|
||||
|
||||
def get_document_type(self):
|
||||
return get_object_or_404(DocumentType, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -451,7 +468,6 @@ class DocumentTypeFilenameListView(SingleObjectListView):
|
||||
|
||||
class DocumentTypeFilenameEditView(SingleObjectEditView):
|
||||
fields = ('enabled', 'filename',)
|
||||
model = DocumentTypeFilename
|
||||
view_permission = permission_document_type_edit
|
||||
|
||||
def get_extra_context(self):
|
||||
@@ -476,9 +492,11 @@ class DocumentTypeFilenameEditView(SingleObjectEditView):
|
||||
args=(self.get_object().document_type.pk,)
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentTypeFilename.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentTypeFilenameDeleteView(SingleObjectDeleteView):
|
||||
model = DocumentTypeFilename
|
||||
view_permission = permission_document_type_edit
|
||||
|
||||
def get_extra_context(self):
|
||||
@@ -501,6 +519,9 @@ class DocumentTypeFilenameDeleteView(SingleObjectDeleteView):
|
||||
args=(self.get_object().document_type.pk,)
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return DocumentTypeFilename.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class DocumentVersionListView(SingleObjectListView):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -520,7 +541,7 @@ class DocumentVersionListView(SingleObjectListView):
|
||||
).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_document(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -533,7 +554,6 @@ class DocumentVersionListView(SingleObjectListView):
|
||||
|
||||
|
||||
class DocumentView(SingleObjectDetailView):
|
||||
model = Document
|
||||
object_permission = permission_document_view
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -592,6 +612,9 @@ class DocumentView(SingleObjectDetailView):
|
||||
instance=document, extra_fields=document_fields
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
|
||||
class EmptyTrashCanView(ConfirmView):
|
||||
extra_context = {
|
||||
@@ -603,7 +626,7 @@ class EmptyTrashCanView(ConfirmView):
|
||||
)
|
||||
|
||||
def view_action(self):
|
||||
for deleted_document in DeletedDocument.objects.all():
|
||||
for deleted_document in DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID):
|
||||
deleted_document.delete()
|
||||
|
||||
messages.success(self.request, _('Trash emptied successfully'))
|
||||
@@ -622,11 +645,13 @@ class RecentDocumentListView(DocumentListView):
|
||||
def document_document_type_edit(request, document_id=None, document_id_list=None):
|
||||
post_action_redirect = None
|
||||
|
||||
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
if document_id:
|
||||
queryset = Document.objects.filter(pk=document_id)
|
||||
queryset = queryset.filter(pk=document_id)
|
||||
post_action_redirect = reverse('documents:document_list_recent')
|
||||
elif document_id_list:
|
||||
queryset = Document.objects.filter(pk__in=document_id_list)
|
||||
queryset = queryset.filter(pk__in=document_id_list)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(
|
||||
@@ -700,7 +725,7 @@ def document_multiple_document_type_edit(request):
|
||||
|
||||
# TODO: Get rid of this view and convert widget to use API and base64 only images
|
||||
def get_document_image(request, document_id, size=setting_preview_size.value):
|
||||
document = get_object_or_404(Document.passthrough, pk=document_id)
|
||||
document = get_object_or_404(Document.passthrough.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=document_id)
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_view,))
|
||||
except PermissionDenied:
|
||||
@@ -732,12 +757,14 @@ def get_document_image(request, document_id, size=setting_preview_size.value):
|
||||
def document_download(request, document_id=None, document_id_list=None, document_version_pk=None):
|
||||
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))))
|
||||
|
||||
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
if document_id:
|
||||
documents = Document.objects.filter(pk=document_id)
|
||||
documents = queryset.filter(pk=document_id)
|
||||
elif document_id_list:
|
||||
documents = Document.objects.filter(pk__in=document_id_list)
|
||||
documents = queryset.filter(pk__in=document_id_list)
|
||||
elif document_version_pk:
|
||||
documents = Document.objects.filter(
|
||||
documents = queryset.filter(
|
||||
pk=get_object_or_404(
|
||||
DocumentVersion, pk=document_version_pk
|
||||
).document.pk
|
||||
@@ -762,6 +789,7 @@ def document_download(request, document_id=None, document_id_list=None, document
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: check organization
|
||||
if document_version_pk:
|
||||
queryset = DocumentVersion.objects.filter(pk=document_version_pk)
|
||||
else:
|
||||
@@ -873,10 +901,12 @@ def document_multiple_download(request):
|
||||
|
||||
|
||||
def document_update_page_count(request, document_id=None, document_id_list=None):
|
||||
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
if document_id:
|
||||
documents = Document.objects.filter(pk=document_id)
|
||||
documents = queryset.filter(pk=document_id)
|
||||
elif document_id_list:
|
||||
documents = Document.objects.filter(pk__in=document_id_list)
|
||||
documents = queryset.objects.filter(pk__in=document_id_list)
|
||||
|
||||
if not documents:
|
||||
messages.error(request, _('At least one document must be selected.'))
|
||||
@@ -936,11 +966,13 @@ def document_multiple_update_page_count(request):
|
||||
|
||||
|
||||
def document_clear_transformations(request, document_id=None, document_id_list=None):
|
||||
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
|
||||
|
||||
if document_id:
|
||||
documents = Document.objects.filter(pk=document_id)
|
||||
documents = queryset.filter(pk=document_id)
|
||||
post_redirect = documents[0].get_absolute_url()
|
||||
elif document_id_list:
|
||||
documents = Document.objects.filter(pk__in=document_id_list)
|
||||
documents = queryset.filter(pk__in=document_id_list)
|
||||
post_redirect = None
|
||||
|
||||
if not documents:
|
||||
@@ -1020,7 +1052,7 @@ def document_page_view_reset(request, document_page_id):
|
||||
|
||||
|
||||
def document_page_navigation_next(request, document_page_id):
|
||||
document_page = get_object_or_404(DocumentPage, pk=document_page_id)
|
||||
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_view,))
|
||||
@@ -1038,7 +1070,7 @@ def document_page_navigation_next(request, document_page_id):
|
||||
|
||||
|
||||
def document_page_navigation_previous(request, document_page_id):
|
||||
document_page = get_object_or_404(DocumentPage, pk=document_page_id)
|
||||
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_view,))
|
||||
@@ -1056,7 +1088,7 @@ def document_page_navigation_previous(request, document_page_id):
|
||||
|
||||
|
||||
def document_page_navigation_first(request, document_page_id):
|
||||
document_page = get_object_or_404(DocumentPage, pk=document_page_id)
|
||||
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
|
||||
document_page = get_object_or_404(document_page.siblings, page_number=1)
|
||||
|
||||
try:
|
||||
@@ -1070,7 +1102,7 @@ def document_page_navigation_first(request, document_page_id):
|
||||
|
||||
|
||||
def document_page_navigation_last(request, document_page_id):
|
||||
document_page = get_object_or_404(DocumentPage, pk=document_page_id)
|
||||
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
|
||||
document_page = get_object_or_404(document_page.siblings, page_number=document_page.siblings.count())
|
||||
|
||||
try:
|
||||
@@ -1084,7 +1116,7 @@ def document_page_navigation_last(request, document_page_id):
|
||||
|
||||
|
||||
def transform_page(request, document_page_id, zoom_function=None, rotation_function=None):
|
||||
document_page = get_object_or_404(DocumentPage, pk=document_page_id)
|
||||
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_view,))
|
||||
@@ -1147,7 +1179,7 @@ def document_page_rotate_left(request, document_page_id):
|
||||
|
||||
|
||||
def document_print(request, document_id):
|
||||
document = get_object_or_404(Document, pk=document_id)
|
||||
document = get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=document_id)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_print,))
|
||||
@@ -1194,7 +1226,7 @@ def document_print(request, document_id):
|
||||
def document_type_filename_create(request, document_type_id):
|
||||
Permission.check_permissions(request.user, (permission_document_type_edit,))
|
||||
|
||||
document_type = get_object_or_404(DocumentType, pk=document_type_id)
|
||||
document_type = get_object_or_404(DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID), pk=document_type_id)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = DocumentTypeFilenameForm_create(request.POST)
|
||||
@@ -1240,7 +1272,7 @@ def document_clear_image_cache(request):
|
||||
|
||||
|
||||
def document_version_revert(request, document_version_pk):
|
||||
document_version = get_object_or_404(DocumentVersion, pk=document_version_pk)
|
||||
document_version = get_object_or_404(DocumentVersion.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_version_pk)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_document_version_revert,))
|
||||
|
||||
@@ -21,7 +21,7 @@ class FolderListForm(forms.Form):
|
||||
logger.debug('user: %s', user)
|
||||
super(FolderListForm, self).__init__(*args, **kwargs)
|
||||
|
||||
queryset = Folder.objects.all()
|
||||
queryset = Folder.on_organization.all()
|
||||
try:
|
||||
Permission.check_permissions(user, (permission_folder_view,))
|
||||
except PermissionDenied:
|
||||
|
||||
22
mayan/apps/folders/migrations/0005_folder_organization.py
Normal file
22
mayan/apps/folders/migrations/0005_folder_organization.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import organizations.shortcuts
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('organizations', '0001_initial'),
|
||||
('folders', '0004_documentfolder'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='folder',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
@@ -10,6 +10,9 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from acls.models import AccessControlList
|
||||
from documents.models import Document
|
||||
from documents.permissions import permission_document_view
|
||||
from organizations.models import Organization
|
||||
from organizations.managers import CurrentOrganizationManager
|
||||
from organizations.shortcuts import get_current_organization
|
||||
from permissions import Permission
|
||||
|
||||
from .managers import FolderManager
|
||||
@@ -17,6 +20,9 @@ from .managers import FolderManager
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Folder(models.Model):
|
||||
organization = models.ForeignKey(
|
||||
Organization, default=get_current_organization
|
||||
)
|
||||
label = models.CharField(
|
||||
db_index=True, max_length=128, verbose_name=_('Label')
|
||||
)
|
||||
@@ -29,6 +35,7 @@ class Folder(models.Model):
|
||||
)
|
||||
|
||||
objects = FolderManager()
|
||||
on_organization = CurrentOrganizationManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
@@ -33,7 +33,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class FolderEditView(SingleObjectEditView):
|
||||
fields = ('label',)
|
||||
model = Folder
|
||||
object_permission = permission_folder_edit
|
||||
post_action_redirect = reverse_lazy('folders:folder_list')
|
||||
|
||||
@@ -43,6 +42,9 @@ class FolderEditView(SingleObjectEditView):
|
||||
'title': _('Edit folder: %s') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_document_queryset(self):
|
||||
return Folder.on_organization.all()
|
||||
|
||||
|
||||
class FolderListView(SingleObjectListView):
|
||||
object_permission = permission_folder_view
|
||||
@@ -54,7 +56,7 @@ class FolderListView(SingleObjectListView):
|
||||
}
|
||||
|
||||
def get_folder_queryset(self):
|
||||
return Folder.objects.all()
|
||||
return Folder.on_organization.all()
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.get_folder_queryset()
|
||||
@@ -63,7 +65,6 @@ class FolderListView(SingleObjectListView):
|
||||
|
||||
class FolderCreateView(SingleObjectCreateView):
|
||||
fields = ('label',)
|
||||
model = Folder
|
||||
view_permission = permission_folder_create
|
||||
|
||||
def form_valid(self, form):
|
||||
@@ -90,9 +91,12 @@ class FolderCreateView(SingleObjectCreateView):
|
||||
'title': _('Create folder'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return Folder.on_organization.all()
|
||||
|
||||
|
||||
def folder_delete(request, folder_id):
|
||||
folder = get_object_or_404(Folder, pk=folder_id)
|
||||
folder = get_object_or_404(Folder.on_organization, pk=folder_id)
|
||||
|
||||
try:
|
||||
Permission.check_permissions(request.user, (permission_folder_delete,))
|
||||
@@ -142,7 +146,7 @@ class FolderDetailView(DocumentListView):
|
||||
}
|
||||
|
||||
def get_folder(self):
|
||||
folder = get_object_or_404(Folder, pk=self.kwargs['pk'])
|
||||
folder = get_object_or_404(Folder.on_organization, pk=self.kwargs['pk'])
|
||||
|
||||
try:
|
||||
Permission.check_permissions(
|
||||
@@ -267,7 +271,7 @@ class DocumentFolderListView(FolderListView):
|
||||
def folder_document_remove(request, folder_id, document_id=None, document_id_list=None):
|
||||
post_action_redirect = None
|
||||
|
||||
folder = get_object_or_404(Folder, pk=folder_id)
|
||||
folder = get_object_or_404(Folder.on_organization, pk=folder_id)
|
||||
|
||||
if document_id:
|
||||
queryset = Document.objects.filter(pk=document_id)
|
||||
|
||||
3
mayan/apps/organizations/__init__.py
Normal file
3
mayan/apps/organizations/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
default_app_config = 'organizations.apps.OrganizationApp'
|
||||
11
mayan/apps/organizations/admin.py
Normal file
11
mayan/apps/organizations/admin.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Organization
|
||||
|
||||
|
||||
@admin.register(Organization)
|
||||
class OrganizationAdmin(admin.ModelAdmin):
|
||||
list_display = ('label',)
|
||||
search_fields = ('label',)
|
||||
12
mayan/apps/organizations/apps.py
Normal file
12
mayan/apps/organizations/apps.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.apps import MayanAppConfig
|
||||
|
||||
|
||||
class OrganizationApp(AppConfig):
|
||||
name = 'organizations'
|
||||
verbose_name = _('Organizations')
|
||||
43
mayan/apps/organizations/management.py
Normal file
43
mayan/apps/organizations/management.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Creates the default Organization object.
|
||||
"""
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.management.color import no_style
|
||||
from django.db import DEFAULT_DB_ALIAS, connections, router
|
||||
from django.db.models import signals
|
||||
|
||||
|
||||
def create_default_organization(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
|
||||
try:
|
||||
Organization = apps.get_model('organizations', 'Organization')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
if not router.allow_migrate(using, Organization):
|
||||
return
|
||||
|
||||
if not Organization.objects.using(using).exists():
|
||||
# The default settings set ORGANIZATION_ID = 1, and some tests in Django's test
|
||||
# suite rely on this value. However, if database sequences are reused
|
||||
# (e.g. in the test suite after flush/syncdb), it isn't guaranteed that
|
||||
# the next id will be 1, so we coerce it. See #15573 and #16353. This
|
||||
# can also crop up outside of tests - see #15346.
|
||||
if verbosity >= 2:
|
||||
print("Creating default Organization object")
|
||||
Organization(pk=1, label='Default').save(using=using)
|
||||
|
||||
# We set an explicit pk instead of relying on auto-incrementation,
|
||||
# so we need to reset the database sequence. See #17415.
|
||||
sequence_sql = connections[using].ops.sequence_reset_sql(no_style(), [Organization])
|
||||
if sequence_sql:
|
||||
if verbosity >= 2:
|
||||
print('Resetting sequence')
|
||||
with connections[using].cursor() as cursor:
|
||||
for command in sequence_sql:
|
||||
cursor.execute(command)
|
||||
|
||||
Organization.objects.clear_cache()
|
||||
|
||||
|
||||
signals.post_migrate.connect(create_default_organization, sender=apps.get_app_config('organizations'))
|
||||
64
mayan/apps/organizations/managers.py
Normal file
64
mayan/apps/organizations/managers.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import checks
|
||||
from django.db import models
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
|
||||
|
||||
class CurrentOrganizationManager(models.Manager):
|
||||
"Use this to limit objects to those associated with the current organization."
|
||||
|
||||
def __init__(self, field_name=None):
|
||||
super(CurrentOrganizationManager, self).__init__()
|
||||
self.__field_name = field_name
|
||||
|
||||
def check(self, **kwargs):
|
||||
errors = super(CurrentOrganizationManager, self).check(**kwargs)
|
||||
errors.extend(self._check_field_name())
|
||||
return errors
|
||||
|
||||
def _check_field_name(self):
|
||||
field_name = self._get_field_name()
|
||||
try:
|
||||
field = self.model._meta.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
return [
|
||||
checks.Error(
|
||||
"CurrentOrganizationManager could not find a field named '%s'." % field_name,
|
||||
hint=None,
|
||||
obj=self,
|
||||
id='organizations.E001',
|
||||
)
|
||||
]
|
||||
|
||||
if not isinstance(field, (models.ForeignKey, models.ManyToManyField)):
|
||||
return [
|
||||
checks.Error(
|
||||
"CurrentOrganizationManager cannot use '%s.%s' as it is not a ForeignKey or ManyToManyField." % (
|
||||
self.model._meta.object_name, field_name
|
||||
),
|
||||
hint=None,
|
||||
obj=self,
|
||||
id='organizations.E002',
|
||||
)
|
||||
]
|
||||
|
||||
return []
|
||||
|
||||
def _get_field_name(self):
|
||||
""" Return self.__field_name or 'organization' or 'organizations'. """
|
||||
|
||||
if not self.__field_name:
|
||||
try:
|
||||
self.model._meta.get_field('organization')
|
||||
except FieldDoesNotExist:
|
||||
self.__field_name = 'organizations'
|
||||
else:
|
||||
self.__field_name = 'organization'
|
||||
return self.__field_name
|
||||
|
||||
def get_queryset(self):
|
||||
return super(CurrentOrganizationManager, self).get_queryset().filter(
|
||||
**{self._get_field_name() + '__id': settings.ORGANIZATION_ID})
|
||||
10
mayan/apps/organizations/middleware.py
Normal file
10
mayan/apps/organizations/middleware.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from .models import Organization
|
||||
|
||||
|
||||
class CurrentOrganizationMiddleware(object):
|
||||
"""
|
||||
Middleware that sets `organization` attribute to request object.
|
||||
"""
|
||||
|
||||
def process_request(self, request):
|
||||
request.organization = Organization.objects.get_current()
|
||||
26
mayan/apps/organizations/migrations/0001_initial.py
Normal file
26
mayan/apps/organizations/migrations/0001_initial.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Organization',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('label', models.CharField(max_length=50, verbose_name='Label')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('label',),
|
||||
'verbose_name': 'Organization',
|
||||
'verbose_name_plural': 'Organizations',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
0
mayan/apps/organizations/migrations/__init__.py
Normal file
0
mayan/apps/organizations/migrations/__init__.py
Normal file
71
mayan/apps/organizations/models.py
Normal file
71
mayan/apps/organizations/models.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import string
|
||||
import warnings
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
from django.db import models
|
||||
from django.db.models.signals import pre_save, pre_delete
|
||||
from django.utils.deprecation import RemovedInDjango19Warning
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
ORGANIZATION_CACHE = {}
|
||||
|
||||
|
||||
class OrganizationManager(models.Manager):
|
||||
def get_current(self):
|
||||
"""
|
||||
Returns the current ``Organization`` based on the ORGANIZATION_ID in
|
||||
the project's settings. The ``Organization`` object is cached the first
|
||||
time it's retrieved from the database.
|
||||
"""
|
||||
from django.conf import settings
|
||||
try:
|
||||
oid = settings.ORGANIZATION_ID
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"You're using the Django \"organizations framework\" without "
|
||||
"having set the ORGANIZATION_ID setting. Create a site in "
|
||||
"your database and set the SITE_ID setting to fix this error."
|
||||
)
|
||||
try:
|
||||
current_organization = ORGANIZATION_CACHE[oid]
|
||||
except KeyError:
|
||||
current_organization = self.get(pk=oid)
|
||||
ORGANIZATION_CACHE[oid] = current_organization
|
||||
return current_organization
|
||||
|
||||
def clear_cache(self):
|
||||
"""Clears the ``Organization`` object cache."""
|
||||
global ORGANIZATION_CACHE
|
||||
ORGANIZATION_CACHE = {}
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Organization(models.Model):
|
||||
label = models.CharField(max_length=50, verbose_name=_('Label'))
|
||||
objects = OrganizationManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Organization')
|
||||
verbose_name_plural = _('Organizations')
|
||||
ordering = ('label',)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
||||
def clear_organization_cache(sender, **kwargs):
|
||||
"""
|
||||
Clears the cache (if primed) each time a organization is saved or deleted
|
||||
"""
|
||||
instance = kwargs['instance']
|
||||
try:
|
||||
del ORGANIZATION_CACHE[instance.pk]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
pre_save.connect(clear_organization_cache, sender=Organization)
|
||||
pre_delete.connect(clear_organization_cache, sender=Organization)
|
||||
8
mayan/apps/organizations/shortcuts.py
Normal file
8
mayan/apps/organizations/shortcuts.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
|
||||
|
||||
def get_current_organization():
|
||||
from .models import Organization
|
||||
return Organization.objects.get_current().pk
|
||||
149
mayan/apps/organizations/tests.py
Normal file
149
mayan/apps/organizations/tests.py
Normal file
@@ -0,0 +1,149 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.db import connections, router
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase, modify_settings, override_settings
|
||||
|
||||
from .management import create_default_organization
|
||||
from .middleware import CurrentOrganizationMiddleware
|
||||
from .models import Organization
|
||||
from .shortcuts import get_current_organization
|
||||
|
||||
|
||||
class OrganizationsFrameworkTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
Organization(id=settings.ORGANIZATION_ID, domain="example.com", name="example.com").save()
|
||||
|
||||
def test_organization_manager(self):
|
||||
# Make sure that get_current() does not return a deleted Organization object.
|
||||
s = Organization.objects.get_current()
|
||||
self.assertTrue(isinstance(s, Organization))
|
||||
s.delete()
|
||||
self.assertRaises(ObjectDoesNotExist, Organization.objects.get_current)
|
||||
|
||||
def test_organization_cache(self):
|
||||
# After updating a Organization object (e.g. via the admin), we shouldn't return a
|
||||
# bogus value from the ORGANIZATION_CACHE.
|
||||
organization = Organization.objects.get_current()
|
||||
self.assertEqual("example.com", organization.name)
|
||||
s2 = Organization.objects.get(id=settings.ORGANIZATION_ID)
|
||||
s2.name = "Example organization"
|
||||
s2.save()
|
||||
organization = Organization.objects.get_current()
|
||||
self.assertEqual("Example organization", organization.name)
|
||||
|
||||
def test_delete_all_organizations_clears_cache(self):
|
||||
# When all organization objects are deleted the cache should also
|
||||
# be cleared and get_current() should raise a DoesNotExist.
|
||||
self.assertIsInstance(Organization.objects.get_current(), Organization)
|
||||
Organization.objects.all().delete()
|
||||
self.assertRaises(Organization.DoesNotExist, Organization.objects.get_current)
|
||||
|
||||
@override_settings(ALLOWED_HOSTS=['example.com'])
|
||||
def test_get_current_organization(self):
|
||||
# Test that the correct Organization object is returned
|
||||
request = HttpRequest()
|
||||
request.META = {
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80",
|
||||
}
|
||||
organization = get_current_organization(request)
|
||||
self.assertTrue(isinstance(organization, Organization))
|
||||
self.assertEqual(organization.id, settings.ORGANIZATION_ID)
|
||||
|
||||
# Test that an exception is raised if the organizations framework is installed
|
||||
# but there is no matching Organization
|
||||
organization.delete()
|
||||
self.assertRaises(ObjectDoesNotExist, get_current_organization, request)
|
||||
|
||||
# A RequestOrganization is returned if the organizations framework is not installed
|
||||
with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.organizations'}):
|
||||
organization = get_current_organization(request)
|
||||
self.assertTrue(isinstance(organization, RequestOrganization))
|
||||
self.assertEqual(organization.name, "example.com")
|
||||
|
||||
def test_domain_name_with_whitespaces(self):
|
||||
# Regression for #17320
|
||||
# Domain names are not allowed contain whitespace characters
|
||||
organization = Organization(name="test name", domain="test test")
|
||||
self.assertRaises(ValidationError, organization.full_clean)
|
||||
organization.domain = "test\ttest"
|
||||
self.assertRaises(ValidationError, organization.full_clean)
|
||||
organization.domain = "test\ntest"
|
||||
self.assertRaises(ValidationError, organization.full_clean)
|
||||
|
||||
|
||||
class JustOtherRouter(object):
|
||||
def allow_migrate(self, db, model):
|
||||
return db == 'other'
|
||||
|
||||
|
||||
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.organizations'})
|
||||
class CreateDefaultOrganizationTests(TestCase):
|
||||
multi_db = True
|
||||
|
||||
def setUp(self):
|
||||
self.app_config = apps.get_app_config('organizations')
|
||||
# Delete the organization created as part of the default migration process.
|
||||
Organization.objects.all().delete()
|
||||
|
||||
def test_basic(self):
|
||||
"""
|
||||
#15346, #15573 - create_default_organization() creates an example organization only if
|
||||
none exist.
|
||||
"""
|
||||
create_default_organization(self.app_config, verbosity=0)
|
||||
self.assertEqual(Organization.objects.count(), 1)
|
||||
|
||||
create_default_organization(self.app_config, verbosity=0)
|
||||
self.assertEqual(Organization.objects.count(), 1)
|
||||
|
||||
@unittest.skipIf('other' not in connections, "Requires 'other' database connection.")
|
||||
def test_multi_db_with_router(self):
|
||||
"""
|
||||
#16353, #16828 - The default organization creation should respect db routing.
|
||||
"""
|
||||
old_routers = router.routers
|
||||
router.routers = [JustOtherRouter()]
|
||||
try:
|
||||
create_default_organization(self.app_config, using='default', verbosity=0)
|
||||
create_default_organization(self.app_config, using='other', verbosity=0)
|
||||
self.assertFalse(Organization.objects.using('default').exists())
|
||||
self.assertTrue(Organization.objects.using('other').exists())
|
||||
finally:
|
||||
router.routers = old_routers
|
||||
|
||||
@unittest.skipIf('other' not in connections, "Requires 'other' database connection.")
|
||||
def test_multi_db(self):
|
||||
create_default_organization(self.app_config, using='default', verbosity=0)
|
||||
create_default_organization(self.app_config, using='other', verbosity=0)
|
||||
self.assertTrue(Organization.objects.using('default').exists())
|
||||
self.assertTrue(Organization.objects.using('other').exists())
|
||||
|
||||
def test_save_another(self):
|
||||
"""
|
||||
#17415 - Another organization can be created right after the default one.
|
||||
|
||||
On some backends the sequence needs to be reset after saving with an
|
||||
explicit ID. Test that there isn't a sequence collisions by saving
|
||||
another organization. This test is only meaningful with databases that use
|
||||
sequences for automatic primary keys such as PostgreSQL and Oracle.
|
||||
"""
|
||||
create_default_organization(self.app_config, verbosity=0)
|
||||
Organization(domain='example2.com', name='example2.com').save()
|
||||
|
||||
|
||||
class MiddlewareTest(TestCase):
|
||||
|
||||
def test_request(self):
|
||||
""" Makes sure that the request has correct `organization` attribute. """
|
||||
middleware = CurrentOrganizationMiddleware()
|
||||
request = HttpRequest()
|
||||
middleware.process_request(request)
|
||||
self.assertEqual(request.organization.id, settings.ORGANIZATION_ID)
|
||||
@@ -21,7 +21,7 @@ class TagListForm(forms.Form):
|
||||
logger.debug('user: %s', user)
|
||||
super(TagListForm, self).__init__(*args, **kwargs)
|
||||
|
||||
queryset = Tag.objects.all()
|
||||
queryset = Tag.on_organization.all()
|
||||
try:
|
||||
Permission.check_permissions(user, (permission_tag_view,))
|
||||
except PermissionDenied:
|
||||
|
||||
22
mayan/apps/tags/migrations/0007_tag_organization.py
Normal file
22
mayan/apps/tags/migrations/0007_tag_organization.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import organizations.shortcuts
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('organizations', '0001_initial'),
|
||||
('tags', '0006_documenttag'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
@@ -11,11 +11,17 @@ from colorful.fields import RGBColorField
|
||||
from acls.models import AccessControlList
|
||||
from documents.models import Document
|
||||
from documents.permissions import permission_document_view
|
||||
from organizations.models import Organization
|
||||
from organizations.managers import CurrentOrganizationManager
|
||||
from organizations.shortcuts import get_current_organization
|
||||
from permissions import Permission
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Tag(models.Model):
|
||||
organization = models.ForeignKey(
|
||||
Organization, default=get_current_organization
|
||||
)
|
||||
label = models.CharField(
|
||||
db_index=True, max_length=128, unique=True, verbose_name=_('Label')
|
||||
)
|
||||
@@ -24,6 +30,9 @@ class Tag(models.Model):
|
||||
Document, related_name='tags', verbose_name=_('Documents')
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
on_organization = CurrentOrganizationManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
||||
@@ -33,10 +33,12 @@ logger = logging.getLogger(__name__)
|
||||
class TagCreateView(SingleObjectCreateView):
|
||||
extra_context = {'title': _('Create tag')}
|
||||
fields = ('label', 'color')
|
||||
model = Tag
|
||||
post_action_redirect = reverse_lazy('tags:tag_list')
|
||||
view_permission = permission_tag_create
|
||||
|
||||
def get_queryset(self):
|
||||
return Tag.on_organization.all()
|
||||
|
||||
|
||||
def tag_attach(request, document_id=None, document_id_list=None):
|
||||
if document_id:
|
||||
@@ -144,17 +146,18 @@ class TagListView(SingleObjectListView):
|
||||
return super(TagListView, self).get_queryset()
|
||||
|
||||
def get_tag_queryset(self):
|
||||
return Tag.objects.all()
|
||||
return Tag.on_organization.all()
|
||||
|
||||
|
||||
def tag_delete(request, tag_id=None, tag_id_list=None):
|
||||
post_action_redirect = None
|
||||
queryset = Tag.on_organization.all()
|
||||
|
||||
if tag_id:
|
||||
queryset = Tag.objects.filter(pk=tag_id)
|
||||
queryset = organization.filter(pk=tag_id)
|
||||
post_action_redirect = reverse('tags:tag_list')
|
||||
elif tag_id_list:
|
||||
queryset = Tag.objects.filter(pk__in=tag_id_list)
|
||||
queryset = organization.filter(pk__in=tag_id_list)
|
||||
|
||||
if not queryset:
|
||||
messages.error(request, _('Must provide at least one tag.'))
|
||||
@@ -221,7 +224,6 @@ def tag_multiple_delete(request):
|
||||
|
||||
class TagEditView(SingleObjectEditView):
|
||||
fields = ('label', 'color')
|
||||
model = Tag
|
||||
object_permission = permission_tag_edit
|
||||
post_action_redirect = reverse_lazy('tags:tag_list')
|
||||
|
||||
@@ -231,10 +233,13 @@ class TagEditView(SingleObjectEditView):
|
||||
'title': _('Edit tag: %s') % self.get_object(),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return Tag.on_organization.all()
|
||||
|
||||
|
||||
class TagTaggedItemListView(DocumentListView):
|
||||
def get_tag(self):
|
||||
return get_object_or_404(Tag, pk=self.kwargs['pk'])
|
||||
return get_object_or_404(Tag.on_organization, pk=self.kwargs['pk'])
|
||||
|
||||
def get_document_queryset(self):
|
||||
return self.get_tag().documents.all()
|
||||
@@ -309,17 +314,20 @@ def tag_remove(request, document_id=None, document_id_list=None, tag_id=None, ta
|
||||
}
|
||||
|
||||
template = 'appearance/generic_confirm.html'
|
||||
|
||||
queryset = Tag.on_organization.all()
|
||||
|
||||
if tag_id:
|
||||
tags = Tag.objects.filter(pk=tag_id)
|
||||
tags = queryset.filter(pk=tag_id)
|
||||
elif tag_id_list:
|
||||
tags = Tag.objects.filter(pk__in=tag_id_list)
|
||||
tags = queryset.filter(pk__in=tag_id_list)
|
||||
else:
|
||||
template = 'appearance/generic_form.html'
|
||||
|
||||
if request.method == 'POST':
|
||||
form = TagListForm(request.POST, user=request.user)
|
||||
if form.is_valid():
|
||||
tags = Tag.objects.filter(pk=form.cleaned_data['tag'].pk)
|
||||
tags = Tag.on_organization.filter(pk=form.cleaned_data['tag'].pk)
|
||||
else:
|
||||
if not tag_id and not tag_id_list:
|
||||
form = TagListForm(user=request.user)
|
||||
|
||||
@@ -73,6 +73,7 @@ INSTALLED_APPS = (
|
||||
'lock_manager',
|
||||
'mimetype',
|
||||
'navigation',
|
||||
'organizations',
|
||||
'permissions',
|
||||
'smart_settings',
|
||||
'user_management',
|
||||
@@ -113,6 +114,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'common.middleware.strip_spaces_widdleware.SpacelessMiddleware',
|
||||
'authentication.middleware.login_required_middleware.LoginRequiredMiddleware',
|
||||
'common.middleware.ajax_redirect.AjaxRedirect',
|
||||
'organizations.middleware.CurrentOrganizationMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'mayan.urls'
|
||||
@@ -284,3 +286,5 @@ SWAGGER_SETTINGS = {
|
||||
# ------ Timezone --------
|
||||
TIMEZONE_COOKIE_NAME = 'django_timezone'
|
||||
TIMEZONE_SESSION_KEY = 'django_timezone'
|
||||
# ------ Organization -------
|
||||
ORGANIZATION_ID = 1
|
||||
|
||||
Reference in New Issue
Block a user