Compare commits

...

27 Commits

Author SHA1 Message Date
Roberto Rosario
71ede69c3f Update linking app to use organizations. 2016-07-01 02:02:34 -04:00
Roberto Rosario
b6161e9d92 PEP8 and code style cleanups. 2016-06-30 21:30:01 -04:00
Roberto Rosario
aa56e8ae14 Update dynamic_search app to work with organizations. Remove Recent search feature. 2016-06-30 18:46:26 -04:00
Roberto Rosario
1b8436add7 Merge remote-tracking branch 'origin/master' into feature/multi-tenant 2016-06-30 16:59:46 -04:00
Roberto Rosario
ce8377da03 Update changelog. 2016-06-30 16:03:17 -04:00
Roberto Rosario
4b7c4335e1 Improve TempfileCheckMixin. 2016-06-30 16:02:17 -04:00
Roberto Rosario
4f2744f916 Add missing link to the 2.1.3 release notes in the index file. 2016-06-30 14:40:18 -04:00
Roberto Rosario
883e1cc510 Merge remote-tracking branch 'origin/master' into feature/merge_test 2016-06-30 01:00:55 -04:00
Roberto Rosario
9483309332 Bump version to 2.1.3. 2016-06-29 19:05:38 -04:00
Roberto Rosario
07ac1cbbbe Add version 2.1.3 release notes and update the changelog. 2016-06-29 19:02:34 -04:00
Roberto Rosario
e62e6a9a08 Remove downloads badge as PyPI downloads tracking is broken and has been marked as "Won't Fix".
https://bitbucket.org/pypa/pypi/issues/396/download-stats-have-stopped-working-again
2016-06-29 18:44:51 -04:00
Roberto Rosario
97089670ee Change the file descriptor check to use open files instead. Add decorator to skip open file check. GitLab issue #309. 2016-06-29 18:40:15 -04:00
Roberto Rosario
063b325986 Fix file descriptor leak in document signature download test. 2016-06-28 03:01:29 -04:00
Roberto Rosario
2ea3c08c97 Add _future_ import to force unicode. 2016-06-28 03:01:04 -04:00
Roberto Rosario
f3f5cff36e Add missing temporary cleanup for the office documents section. 2016-06-28 01:53:53 -04:00
Roberto Rosario
d0aee4f72b Add parameter to fs_cleanup function to close a file descriptor before closing it. GitLab issue #309. 2016-06-27 19:21:42 -04:00
Roberto Rosario
5ac1276f25 Add base test class that includes unclaimed temporary and file descriptor test mixins. GitLab issue #309. 2016-06-27 19:20:42 -04:00
Roberto Rosario
113ad144e0 Add test mixins for file descriptor leaks and unclaimed temporary files. GitLab issue #309. 2016-06-27 19:19:37 -04:00
Roberto Rosario
debec7b4a2 Update the django_gpg app to support organizations. 2016-06-24 04:22:49 -04:00
Roberto Rosario
0be3e130c1 Merge branch 'master' into feature/merge2 2016-06-24 03:28:36 -04:00
Roberto Rosario
dd28e3dc09 Allow superusers to login from any organization. 2016-06-24 03:26:24 -04:00
Roberto Rosario
09e375654d Improve verbose name of custom group model. Allow user to be created without belonging to an organization, this is used for superusers. 2016-06-24 03:24:35 -04:00
Roberto Rosario
c531631a37 Update Changelog. 2016-06-23 22:32:12 -04:00
Roberto Rosario
a76a55f70a Incorporate temp file check in teh GenericViewTestCase class. 2016-06-23 22:31:08 -04:00
Roberto Rosario
98e6ba2b50 Cleanup temporary files after running the poppler parser. GitLab issue #309. Thanks to @miek770 for finding the issue and solution. 2016-06-23 20:56:53 -04:00
Roberto Rosario
945158bd60 Normalize handling of temporary file and directory creation. 2016-06-23 20:54:10 -04:00
Roberto Rosario
ab831aa493 Fix setting message, no longer are temp project wide temp folders created by default. 2016-06-23 20:28:39 -04:00
90 changed files with 748 additions and 458 deletions

View File

@@ -1,3 +1,19 @@
2.1.4 (2016-XX-XX)
==================
- Add missing link to the 2.1.3 release notes in the index file.
- Improve TempfileCheckMixin.
2.1.3 (2016-06-29)
==================
- Add help message when initialsetup migration phase fails. Relates to GitLab issue #296.
- Start using self.setdout instead of print as per documentation.
- Fix GitLab issue #295, "When editing a user the top bar jumps to the name of the user".
- Normalize handling of temporary file and directory creation.
- Fix GitLab issue #309, "Temp files quickly filling-up my /tmp (1GB tmpfs)".
- Explicitly check for residual temporary files in tests.
- Add missing temporary file cleanup for office documents.
- Fix file descriptor leak in the document signature download test.
2.1.2 (2016-05-20)
==================
- Sort document languages and user profile locale language lists. GitLab issue #292.

View File

@@ -1,4 +1,4 @@
|PyPI badge| |Build Status| |Coverage badge| |Installs badge| |License badge|
|PyPI badge| |Build Status| |Coverage badge| |License badge|
|Logo|
@@ -63,8 +63,6 @@ Contribute
:target: https://gitlab.com/mayan-edms/mayan-edms/commits/master
.. |Logo| image:: https://gitlab.com/mayan-edms/mayan-edms/raw/master/docs/_static/mayan_logo.png
.. |Animation| image:: https://gitlab.com/mayan-edms/mayan-edms/raw/master/docs/_static/overview.gif
.. |Installs badge| image:: http://img.shields.io/pypi/dm/mayan-edms.svg?style=flat
:target: https://crate.io/packages/mayan-edms/
.. |PyPI badge| image:: http://img.shields.io/pypi/v/mayan-edms.svg?style=flat
:target: http://badge.fury.io/py/mayan-edms
.. |License badge| image:: http://img.shields.io/badge/license-Apache%202.0-green.svg?style=flat

87
docs/releases/2.1.3.rst Normal file
View File

@@ -0,0 +1,87 @@
===============================
Mayan EDMS v2.1.3 release notes
===============================
Released: June 29, 2016
What's new
==========
This is a bug-fix release and all users are encouraged to upgrade.
Temporary files cleanup
-----------------------
When uploading PDF files that had been OCRed by previous software, the text
parser backend that uses Poppler, would leave behind some temporary files in
the /tmp folder. The issue has been resolved and from the fix a test mixin
system check has been devised that will identify places in the codebase with
similar behaviors, reducing the recurrence of similar issues in the future.
Other changes
-------------
- Add help message when initialsetup migration phase fails. Relates to GitLab issue #296
- Start using self.setdout instead of print as per documentation.
- Fix GitLab issue #295, "When editing a user the top bar jumps to the name of the user".
- Normalize handling of temporary file and directory creation.
- Explicitly check for residual temporary files in tests.
- Add missing temporary file cleanup for office documents.
- Fix file descriptor leak in the document signature download test.
Removals
--------
* None
Upgrading from a previous version
---------------------------------
Using PIP
~~~~~~~~~
Type in the console::
$ pip install -U mayan-edms
the requirements will also be updated automatically.
Using Git
~~~~~~~~~
If you installed Mayan EDMS by cloning the Git repository issue the commands::
$ git reset --hard HEAD
$ git pull
otherwise download the compressed archived and uncompress it overriding the
existing installation.
Next upgrade/add the new requirements::
$ pip install --upgrade -r requirements.txt
Common steps
~~~~~~~~~~~~
Migrate existing database schema with::
$ mayan-edms.py performupgrade
Add new static media::
$ mayan-edms.py collectstatic --noinput
The upgrade procedure is now complete.
Backward incompatible changes
=============================
* None
Bugs fixed or issues closed
===========================
* `GitLab issue #295 <https://gitlab.com/mayan-edms/mayan-edms/issues/295>`_ When editing a user the top bar jumps to the name of the user
* `GitLab issue #309 <https://gitlab.com/mayan-edms/mayan-edms/issues/309>`_ Temp files quickly filling-up my /tmp (1GB tmpfs)
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/

View File

@@ -22,6 +22,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
2.1.3
2.1.2
2.1.1
2.1

View File

@@ -1,8 +1,8 @@
from __future__ import unicode_literals
__title__ = 'Mayan EDMS'
__version__ = '2.1.2'
__build__ = 0x020102
__version__ = '2.1.3'
__build__ = 0x020103
__author__ = 'Roberto Rosario'
__author_email__ = 'roberto.rosario@mayan-edms.com'
__description__ = 'Free Open Source Electronic Document Management System'

View File

@@ -2,7 +2,7 @@ from __future__ import absolute_import, unicode_literals
from django.contrib.auth import get_user_model
from django.core.exceptions import PermissionDenied
from django.test import TestCase, override_settings
from django.test import override_settings
from documents.models import Document, DocumentType
from documents.permissions import permission_document_view

View File

@@ -1,6 +1,5 @@
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
@@ -15,6 +14,12 @@ class UsernameModelBackend(ModelBackend):
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
# Check for superadmins, they can login from any organization.
try:
user = UserModel.objects.filter(is_superuser=True).get(username=username)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)

View File

@@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings
from django.test import override_settings
from django.test.client import Client
from organizations.tests.base import OrganizationTestCase

View File

@@ -4,7 +4,7 @@ import datetime
import time
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from django.test import override_settings
from django.utils.timezone import now
from documents.exceptions import NewDocumentVersionNotAllowed

View File

@@ -12,9 +12,15 @@ class Command(management.BaseCommand):
def handle(self, *args, **options):
management.call_command('createsettings', interactive=False)
try:
result = management.call_command('migrate', interactive=False)
except OperationalError as exception:
self.stderr.write(self.style.NOTICE('Unable to migrate the database. The initialsetup command is to be used only on new installations. To upgrade existing installations use the performupgrade command.'))
management.call_command('migrate', interactive=False)
except OperationalError:
self.stderr.write(
self.style.NOTICE(
'Unable to migrate the database. The initialsetup command '
'is to be used only on new installations. To upgrade '
'existing installations use the performupgrade command.'
)
)
raise
management.call_command('createautoadmin', interactive=False)
management.call_command('createorganizationadmin', interactive=False)

View File

@@ -11,8 +11,7 @@ setting_temporary_directory = namespace.add_setting(
global_name='COMMON_TEMPORARY_DIRECTORY', default=tempfile.gettempdir(),
help_text=_(
'Temporary directory used site wide to store thumbnails, previews '
'and temporary files. If none is specified, one will be created '
'using tempfile.mkdtemp().'
'and temporary files.'
),
is_path=True
)

View File

@@ -0,0 +1,2 @@
from .base import BaseTestCase # NOQA
from .decorators import skip_file_descriptor_check # NOQA

View File

@@ -0,0 +1,11 @@
from __future__ import unicode_literals
from django.test import TestCase
from .mixins import OpenFileCheckMixin, TempfileCheckMixin
class BaseTestCase(OpenFileCheckMixin, TempfileCheckMixin, TestCase):
"""
This is the most basic test case class any test in the project should use.
"""

View File

@@ -0,0 +1,5 @@
def skip_file_descriptor_check(func):
def func_wrapper(item):
item._skip_file_descriptor_test = True
return func(item)
return func_wrapper

View File

@@ -0,0 +1,54 @@
from __future__ import unicode_literals
import os
import psutil
from ..settings import setting_temporary_directory
class TempfileCheckMixin(object):
def _get_temporary_entries(self):
return os.listdir(setting_temporary_directory.value)
def setUp(self):
super(TempfileCheckMixin, self).setUp()
self._temporary_entries = self._get_temporary_entries()
def tearDown(self):
for temporary_entry in self._get_temporary_entries():
self.assertFalse(
temporary_entry not in self._temporary_entries,
msg='Orphan temporary file. The number of temporary file and '
'directories at the start and at the end of the test are not the '
'same.'
)
super(TempfileCheckMixin, self).tearDown()
class OpenFileCheckMixin(object):
def _get_descriptor_count(self):
process = psutil.Process()
return process.num_fds()
def _get_open_files(self):
process = psutil.Process()
return process.open_files()
def setUp(self):
super(OpenFileCheckMixin, self).setUp()
self._open_files = self._get_open_files()
def tearDown(self):
if not getattr(self, '_skip_file_descriptor_test', False):
for new_open_file in self._get_open_files():
self.assertFalse(
new_open_file not in self._open_files,
msg='File descriptor leak. The number of file descriptors '
'at the start and at the end of the test are not the same.'
)
self._skip_file_descriptor_test = False
super(OpenFileCheckMixin, self).tearDown()

View File

@@ -5,7 +5,6 @@ from django.contrib.auth import get_user_model
from django.core.urlresolvers import clear_url_caches, reverse
from django.http import HttpResponse
from django.template import Context, Template
from django.test import TestCase
from organizations.tests.base import OrganizationTestCase
from permissions import Permission
@@ -23,6 +22,7 @@ from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
class GenericViewTestCase(OrganizationTestCase):
def setUp(self):
super(GenericViewTestCase, self).setUp()
self.has_test_view = False
self.admin_user = get_user_model().on_organization.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
@@ -46,7 +46,6 @@ class GenericViewTestCase(OrganizationTestCase):
self.client.logout()
if self.has_test_view:
urlpatterns.pop(0)
super(GenericViewTestCase, self).tearDown()
def add_test_view(self, test_object):
@@ -95,6 +94,12 @@ class GenericViewTestCase(OrganizationTestCase):
data=data, follow=follow
)
def login_user(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
def login_superuser(self):
self.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD)
class CommonViewTestCase(GenericViewTestCase):
def test_about_view(self):

View File

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
import logging
import os
import shutil
import tempfile
import types
@@ -10,6 +11,8 @@ from django.utils.datastructures import MultiValueDict
from django.utils.http import urlquote as django_urlquote
from django.utils.http import urlencode as django_urlencode
from .settings import setting_temporary_directory
logger = logging.getLogger(__name__)
@@ -42,17 +45,23 @@ def encapsulate(function):
return lambda: function
def fs_cleanup(filename, suppress_exceptions=True):
def fs_cleanup(filename, file_descriptor=None, suppress_exceptions=True):
"""
Tries to remove the given filename. Ignores non-existent files
"""
if file_descriptor:
os.close(file_descriptor)
try:
os.remove(filename)
except OSError:
if suppress_exceptions:
pass
else:
raise
try:
shutil.rmtree(filename)
except OSError:
if suppress_exceptions:
pass
else:
raise
def get_descriptor(file_input, read=True):
@@ -69,6 +78,21 @@ def get_descriptor(file_input, read=True):
return file_input
def TemporaryFile(*args, **kwargs):
kwargs.update({'dir': setting_temporary_directory.value})
return tempfile.TemporaryFile(*args, **kwargs)
def mkdtemp(*args, **kwargs):
kwargs.update({'dir': setting_temporary_directory.value})
return tempfile.mkdtemp(*args, **kwargs)
def mkstemp(*args, **kwargs):
kwargs.update({'dir': setting_temporary_directory.value})
return tempfile.mkstemp(*args, **kwargs)
def return_attrib(obj, attrib, arguments=None):
try:
if isinstance(attrib, types.FunctionType):

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
import io
import logging
import os
import tempfile
try:
from cStringIO import StringIO
@@ -16,7 +15,7 @@ import sh
from django.utils.translation import ugettext_lazy as _
from common.utils import fs_cleanup
from common.utils import fs_cleanup, mkstemp
from ..classes import ConverterBase
from ..exceptions import PageCountError
@@ -50,7 +49,7 @@ class Python(ConverterBase):
if self.mime_type == 'application/pdf' and pdftoppm:
new_file_object, input_filepath = tempfile.mkstemp()
new_file_object, input_filepath = mkstemp()
self.file_object.seek(0)
os.write(new_file_object, self.file_object.read())
self.file_object.seek(0)

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
import base64
import logging
import os
import tempfile
try:
from cStringIO import StringIO
@@ -16,7 +15,7 @@ import sh
from django.utils.translation import string_concat, ugettext_lazy as _
from common.settings import setting_temporary_directory
from common.utils import fs_cleanup
from common.utils import fs_cleanup, mkstemp
from mimetype.api import get_mimetype
from .exceptions import InvalidOfficeFormat, OfficeConversionError
@@ -122,7 +121,7 @@ class ConverterBase(object):
) % setting_libreoffice_path.value
)
new_file_object, input_filepath = tempfile.mkstemp()
new_file_object, input_filepath = mkstemp()
self.file_object.seek(0)
os.write(new_file_object, self.file_object.read())
self.file_object.seek(0)
@@ -168,6 +167,7 @@ class ConverterBase(object):
yield data
fs_cleanup(input_filepath)
fs_cleanup(converted_output)
def get_page(self, output_format=DEFAULT_FILE_FORMAT, as_base64=False):
if not self.image:

View File

@@ -4,12 +4,14 @@ import io
import logging
import os
import shutil
import tempfile
import gnupg
from django.db import models
from common.utils import mkdtemp, mkstemp
from organizations.managers import CurrentOrganizationManager
from .classes import KeyStub, SignatureVerification
from .exceptions import (
DecryptionError, KeyDoesNotExist, KeyFetchingError, VerificationError
@@ -22,7 +24,7 @@ logger = logging.getLogger(__name__)
class KeyManager(models.Manager):
def decrypt_file(self, file_object, all_keys=False, key_fingerprint=None, key_id=None):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
@@ -71,7 +73,7 @@ class KeyManager(models.Manager):
return io.BytesIO(decrypt_result.data)
def receive_key(self, key_id):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
@@ -92,7 +94,7 @@ class KeyManager(models.Manager):
return self.create(key_data=key_data)
def search(self, query):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
@@ -118,7 +120,7 @@ class KeyManager(models.Manager):
return self.filter(key_type=KEY_TYPE_SECRET)
def verify_file(self, file_object, signature_file=None, all_keys=False, key_fingerprint=None, key_id=None):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
@@ -156,7 +158,7 @@ class KeyManager(models.Manager):
if signature_file:
# Save the original data and invert the argument order
# Signature first, file second
temporary_file_object, temporary_filename = tempfile.mkstemp()
temporary_file_object, temporary_filename = mkstemp()
os.write(temporary_file_object, file_object.read())
os.close(temporary_file_object)
@@ -192,3 +194,7 @@ class KeyManager(models.Manager):
else:
logger.debug('file not signed')
raise VerificationError('File not signed')
class OrganizationKeyManager(KeyManager, CurrentOrganizationManager):
pass

View File

@@ -14,6 +14,9 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='key',
name='key_data',
field=models.TextField(help_text='ASCII armored version of the key.', verbose_name='Key data'),
field=models.TextField(
help_text='ASCII armored version of the key.',
verbose_name='Key data'
),
),
]

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import organizations.shortcuts
class Migration(migrations.Migration):
dependencies = [
('organizations', '0002_add_data_default_organization'),
('django_gpg', '0006_auto_20160510_0025'),
]
operations = [
migrations.AddField(
model_name='key',
name='organization',
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
),
]

View File

@@ -4,7 +4,6 @@ from datetime import date
import logging
import os
import shutil
import tempfile
import gnupg
@@ -14,20 +13,24 @@ from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from common.utils import mkdtemp
from organizations.models import Organization
from organizations.shortcuts import get_current_organization
from .exceptions import NeedPassphrase, PassphraseError
from .literals import (
ERROR_MSG_NEED_PASSPHRASE, ERROR_MSG_BAD_PASSPHRASE,
ERROR_MSG_GOOD_PASSPHRASE, KEY_TYPE_CHOICES, KEY_TYPE_SECRET,
OUTPUT_MESSAGE_CONTAINS_PRIVATE_KEY
)
from .managers import KeyManager
from .managers import KeyManager, OrganizationKeyManager
from .settings import setting_gpg_path
logger = logging.getLogger(__name__)
def gpg_command(function):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
gpg = gnupg.GPG(
@@ -43,6 +46,9 @@ def gpg_command(function):
@python_2_unicode_compatible
class Key(models.Model):
organization = models.ForeignKey(
Organization, default=get_current_organization
)
key_data = models.TextField(
help_text=_('ASCII armored version of the key.'),
verbose_name=_('Key data')
@@ -71,11 +77,15 @@ class Key(models.Model):
)
objects = KeyManager()
on_organization = OrganizationKeyManager()
class Meta:
verbose_name = _('Key')
verbose_name_plural = _('Keys')
def __str__(self):
return '{} - {}'.format(self.key_id, self.user_id)
def clean(self):
def import_key(gpg):
return gpg.import_keys(key_data=self.key_data)
@@ -85,14 +95,14 @@ class Key(models.Model):
if not import_results.count:
raise ValidationError(_('Invalid key data'))
if Key.objects.filter(fingerprint=import_results.fingerprints[0]).exists():
if Key.on_organization.filter(fingerprint=import_results.fingerprints[0]).exists():
raise ValidationError(_('Key already exists.'))
def get_absolute_url(self):
return reverse('django_gpg:key_detail', args=(self.pk,))
def save(self, *args, **kwargs):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)
@@ -122,9 +132,6 @@ class Key(models.Model):
super(Key, self).save(*args, **kwargs)
def __str__(self):
return '{} - {}'.format(self.key_id, self.user_id)
def sign_file(self, file_object, passphrase=None, clearsign=False, detached=False, binary=False, output=None):
# WARNING: using clearsign=True and subsequent decryption corrupts the
# file. Appears to be a problem in python-gnupg or gpg itself.
@@ -133,7 +140,7 @@ class Key(models.Model):
# file, and appear to be due to random data being inserted in the
# output data stream."
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
os.chmod(temporary_directory, 0x1C0)

View File

@@ -1,12 +1,12 @@
from __future__ import unicode_literals
import StringIO
import tempfile
import gnupg
import mock
from django.test import TestCase
from common.utils import TemporaryFile
from organizations.tests import OrganizationTestCase
from ..exceptions import (
DecryptionError, KeyDoesNotExist, NeedPassphrase, PassphraseError,
@@ -43,7 +43,7 @@ def mock_recv_keys(self, keyserver, *keyids):
return ImportResult()
class KeyTestCase(TestCase):
class KeyTestCase(OrganizationTestCase):
def test_key_instance_creation(self):
# Creating a Key instance is analogous to importing a key
key = Key.objects.create(key_data=TEST_KEY_DATA)
@@ -74,7 +74,7 @@ class KeyTestCase(TestCase):
)
def test_cleartext_file_verification(self):
cleartext_file = tempfile.TemporaryFile()
cleartext_file = TemporaryFile()
cleartext_file.write('test')
cleartext_file.seek(0)
@@ -124,7 +124,7 @@ class KeyTestCase(TestCase):
self.assertEqual(result.read(), TEST_SIGNED_FILE_CONTENT)
def test_cleartext_file_decryption(self):
cleartext_file = tempfile.TemporaryFile()
cleartext_file = TemporaryFile()
cleartext_file.write('test')
cleartext_file.seek(0)

View File

@@ -0,0 +1,83 @@
from __future__ import unicode_literals
from django.test import override_settings
from django_downloadview.test import assert_download_response
from organizations.tests.test_organization_views import OrganizationViewTestCase
from ..models import Key
from .literals import TEST_KEY_DATA, TEST_KEY_FINGERPRINT
@override_settings(OCR_AUTO_OCR=False)
class OrganizationKeyViewsTestCase(OrganizationViewTestCase):
def create_key(self):
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.key = Key.on_organization.create(key_data=TEST_KEY_DATA)
def test_key_delete_view(self):
self.create_key()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.post(
'django_gpg:key_delete',
args=(self.key.pk,), follow=True
)
self.assertEqual(response.status_code, 404)
def test_key_download_view(self):
self.create_key()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.get(
viewname='django_gpg:key_download', args=(self.key.pk,)
)
self.assertEqual(response.status_code, 404)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.get(
viewname='django_gpg:key_download', args=(self.key.pk,)
)
assert_download_response(
self, response=response, content=self.key.key_data,
basename=self.key.key_id,
)
def test_key_upload_view(self):
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.post(
viewname='django_gpg:key_upload',
data={'key_data': TEST_KEY_DATA}, follow=True
)
self.assertContains(response, 'created', status_code=200)
self.assertEqual(Key.on_organization.count(), 1)
self.assertEqual(
Key.on_organization.first().fingerprint, TEST_KEY_FINGERPRINT
)
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
self.assertEqual(Key.on_organization.count(), 0)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.assertEqual(Key.on_organization.count(), 1)
def test_key_private_list_view(self):
self.create_key()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.get(viewname='django_gpg:key_private_list')
self.assertNotContains(
response, text=self.key.key_id, status_code=200
)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.get(viewname='django_gpg:key_private_list')
self.assertContains(
response, text=self.key.key_id, status_code=200
)

View File

@@ -3,9 +3,7 @@ from __future__ import absolute_import, unicode_literals
from django_downloadview.test import assert_download_response
from common.tests.test_views import GenericViewTestCase
from user_management.tests import (
TEST_USER_USERNAME, TEST_USER_PASSWORD
)
from user_management.tests import TEST_USER_USERNAME, TEST_USER_PASSWORD
from ..models import Key
from ..permissions import permission_key_download, permission_key_upload

View File

@@ -25,7 +25,6 @@ logger = logging.getLogger(__name__)
class KeyDeleteView(SingleObjectDeleteView):
model = Key
object_permission = permission_key_delete
def get_post_action_redirect(self):
@@ -37,10 +36,12 @@ class KeyDeleteView(SingleObjectDeleteView):
def get_extra_context(self):
return {'title': _('Delete key: %s') % self.get_object()}
def get_queryset(self):
return Key.on_organization.all()
class KeyDetailView(SingleObjectDetailView):
form_class = KeyDetailForm
model = Key
object_permission = permission_key_view
def get_extra_context(self):
@@ -48,9 +49,11 @@ class KeyDetailView(SingleObjectDetailView):
'title': _('Details for key: %s') % self.get_object(),
}
def get_queryset(self):
return Key.on_organization.all()
class KeyDownloadView(SingleObjectDownloadView):
model = Key
object_permission = permission_key_download
def get_file(self):
@@ -58,6 +61,9 @@ class KeyDownloadView(SingleObjectDownloadView):
return ContentFile(key.key_data, name=key.key_id)
def get_queryset(self):
return Key.on_organization.all()
class KeyReceive(ConfirmView):
post_action_redirect = reverse_lazy('django_gpg:key_public_list')
@@ -71,7 +77,7 @@ class KeyReceive(ConfirmView):
def view_action(self):
try:
Key.objects.receive_key(key_id=self.kwargs['key_id'])
Key.on_organization.receive_key(key_id=self.kwargs['key_id'])
except Exception as exception:
messages.error(
self.request,
@@ -122,14 +128,13 @@ class KeyQueryResultView(SingleObjectListView):
def get_queryset(self):
term = self.request.GET.get('term')
if term:
return Key.objects.search(query=term)
return Key.on_organization.search(query=term)
else:
return ()
class KeyUploadView(SingleObjectCreateView):
fields = ('key_data',)
model = Key
post_action_redirect = reverse_lazy('django_gpg:key_public_list')
view_permission = permission_key_upload
@@ -138,10 +143,12 @@ class KeyUploadView(SingleObjectCreateView):
'title': _('Upload new key'),
}
def get_queryset(self):
return Key.on_organization.all()
class PublicKeyListView(SingleObjectListView):
object_permission = permission_key_view
queryset = Key.objects.public_keys()
def get_extra_context(self):
return {
@@ -149,13 +156,18 @@ class PublicKeyListView(SingleObjectListView):
'title': _('Public keys')
}
def get_queryset(self):
return Key.on_organization.public_keys()
class PrivateKeyListView(SingleObjectListView):
object_permission = permission_key_view
queryset = Key.objects.private_keys()
def get_extra_context(self):
return {
'hide_object': True,
'title': _('Private keys')
}
def get_queryset(self):
return Key.on_organization.private_keys()

View File

@@ -2,10 +2,10 @@ from __future__ import unicode_literals
import logging
import os
import tempfile
from django.db import models
from common.utils import mkstemp
from django_gpg.exceptions import DecryptionError
from django_gpg.models import Key
from documents.models import DocumentVersion
@@ -34,7 +34,7 @@ class EmbeddedSignatureManager(models.Manager):
)
def sign_document_version(self, document_version, key, passphrase=None, user=None):
temporary_file_object, temporary_filename = tempfile.mkstemp()
temporary_file_object, temporary_filename = mkstemp()
try:
with document_version.open() as file_object:

View File

@@ -218,9 +218,10 @@ class SignaturesViewTestCase(GenericDocumentViewTestCase):
args=(signature.pk,),
)
assert_download_response(
self, response=response, content=signature.signature_file.read(),
)
with signature.signature_file as file_object:
assert_download_response(
self, response=response, content=file_object.read(),
)
def test_signature_delete_view_no_permission(self):
with open(TEST_KEY_FILE) as file_object:

View File

@@ -1,6 +1,5 @@
from __future__ import absolute_import, unicode_literals
import tempfile
import logging
from django.contrib import messages
@@ -16,6 +15,7 @@ from common.generics import (
ConfirmView, FormView, SingleObjectCreateView, SingleObjectDeleteView,
SingleObjectDetailView, SingleObjectDownloadView, SingleObjectListView
)
from common.utils import TemporaryFile
from django_gpg.exceptions import NeedPassphrase, PassphraseError
from django_gpg.permissions import permission_key_sign
from documents.models import DocumentVersion
@@ -83,7 +83,7 @@ class DocumentVersionDetachedSignatureCreateView(FormView):
)
)
else:
temporary_file_object = tempfile.TemporaryFile()
temporary_file_object = TemporaryFile()
temporary_file_object.write(detached_signature.data)
temporary_file_object.seek(0)
@@ -188,7 +188,7 @@ class DocumentVersionEmbeddedSignatureCreateView(FormView):
)
)
else:
temporary_file_object = tempfile.TemporaryFile()
temporary_file_object = TemporaryFile()
temporary_file_object.write(signature_result.data)
temporary_file_object.seek(0)

View File

@@ -6,12 +6,7 @@ from organizations.tests.test_organization_views import OrganizationViewTestCase
from ..models import Workflow
from .literals import (
TEST_WORKFLOW_LABEL, TEST_WORKFLOW_LABEL_EDITED,
TEST_WORKFLOW_INITIAL_STATE_LABEL, TEST_WORKFLOW_INITIAL_STATE_COMPLETION,
TEST_WORKFLOW_STATE_LABEL, TEST_WORKFLOW_STATE_COMPLETION,
TEST_WORKFLOW_TRANSITION_LABEL
)
from .literals import TEST_WORKFLOW_LABEL, TEST_WORKFLOW_LABEL_EDITED
@override_settings(OCR_AUTO_OCR=False)

View File

@@ -4,7 +4,6 @@ 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
@@ -182,8 +181,6 @@ class RecentDocumentManager(models.Manager):
class TrashedDocumentManager(models.Manager):
def get_queryset(self):
DocumentType = apps.get_model('documents', 'DocumentType')
return super(
TrashedDocumentManager, self
).get_queryset().filter(in_trash=True)

View File

@@ -11,9 +11,6 @@ from django.utils.six import BytesIO
from rest_framework import status
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from .literals import (
TEST_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE,

View File

@@ -4,6 +4,7 @@ from __future__ import unicode_literals
from actstream.models import Action
from common.tests import skip_file_descriptor_check
from user_management.tests.literals import (
TEST_USER_PASSWORD, TEST_USER_USERNAME
)
@@ -38,7 +39,10 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(list(Action.objects.any(obj=self.document)), [])
@skip_file_descriptor_check
def test_document_download_event_with_permissions(self):
# TODO: Skip this test's file descriptor check until it gets migrate
# SingleObjectDownloadView CBV
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)

View File

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
from datetime import timedelta
import time
from django.test import TestCase, override_settings
from django.test import override_settings
from organizations.tests.base import OrganizationTestCase

View File

@@ -2,12 +2,10 @@ from __future__ import unicode_literals
from organizations.tests.test_organization_views import OrganizationViewTestCase
from ..models import Document,DocumentType
from ..models import DocumentType
from .literals import (
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_2_LABEL,
TEST_DOCUMENT_TYPE_QUICK_LABEL, TEST_SMALL_DOCUMENT_CHECKSUM,
TEST_SMALL_DOCUMENT_PATH
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_2_LABEL, TEST_SMALL_DOCUMENT_PATH
)

View File

@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.test import override_settings
from django.utils.six import BytesIO
from common.tests import skip_file_descriptor_check
from common.tests.test_views import GenericViewTestCase
from converter.models import Transformation
from converter.permissions import permission_transformation_delete
@@ -221,7 +222,11 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
Document.on_organization.first().document_type, document_type
)
@skip_file_descriptor_check
def test_document_download_user_view(self):
# TODO: Skip this test's file descriptor check until it gets migrate
# SingleObjectDownloadView CBV
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
@@ -253,7 +258,11 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
del(buf)
@skip_file_descriptor_check
def test_document_multiple_download_user_view(self):
# TODO: Skip this test's file descriptor check until it gets migrate
# SingleObjectDownloadView CBV
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
@@ -287,7 +296,11 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
del(buf)
@skip_file_descriptor_check
def test_document_version_download_user_view(self):
# TODO: Skip this test's file descriptor check until it gets migrate
# SingleObjectDownloadView CBV
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)

View File

@@ -1,14 +0,0 @@
from __future__ import unicode_literals
from django.contrib import admin
from .models import RecentSearch
@admin.register(RecentSearch)
class RecentSearchAdmin(admin.ModelAdmin):
date_hierarchy = 'datetime_created'
list_display = ('user', 'query', 'datetime_created', 'hits')
list_display_links = ('user', 'query', 'datetime_created', 'hits')
list_filter = ('user',)
readonly_fields = ('user', 'query', 'datetime_created', 'hits')

View File

@@ -6,29 +6,6 @@ from rest_framework.exceptions import ParseError
from rest_api.filters import MayanObjectPermissionsFilter
from .classes import SearchModel
from .filters import RecentSearchUserFilter
from .models import RecentSearch
from .serializers import RecentSearchSerializer
class APIRecentSearchListView(generics.ListAPIView):
"""
Returns a list of all the recent searches for the logged user.
"""
filter_backends = (RecentSearchUserFilter,)
queryset = RecentSearch.objects.all()
serializer_class = RecentSearchSerializer
class APIRecentSearchView(generics.RetrieveAPIView):
"""
Returns the selected recent search details.
"""
filter_backends = (RecentSearchUserFilter,)
queryset = RecentSearch.objects.all()
serializer_class = RecentSearchSerializer
class APISearchView(generics.ListAPIView):

View File

@@ -2,10 +2,10 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from common import MayanAppConfig, menu_facet, menu_sidebar
from common import MayanAppConfig, menu_facet
from rest_api.classes import APIEndPoint
from .links import link_search, link_search_advanced, link_search_again
from .links import link_search, link_search_advanced
class DynamicSearchApp(MayanAppConfig):
@@ -26,6 +26,3 @@ class DynamicSearchApp(MayanAppConfig):
'search:search', 'search:search_advanced', 'search:results'
)
)
menu_sidebar.bind_links(
links=(link_search_again,), sources=('search:results',)
)

View File

@@ -12,7 +12,6 @@ from django.utils.module_loading import import_string
from acls.models import AccessControlList
from permissions import Permission
from .models import RecentSearch
from .settings import setting_limit
logger = logging.getLogger(__name__)
@@ -148,7 +147,7 @@ class SearchModel(object):
for query in field_query_list:
logger.debug('query: %s', query)
term_query_result_set = set(
model.objects.filter(query).values_list(
model.on_organization.filter(query).values_list(
data['return_value'], flat=True
)
)
@@ -183,7 +182,7 @@ class SearchModel(object):
datetime.datetime.now() - start_time
).split(':')[2]
queryset = self.model.objects.filter(
queryset = self.model.on_organization.filter(
pk__in=list(result_set)[:setting_limit.value]
)
@@ -195,10 +194,6 @@ class SearchModel(object):
self.permission, user, queryset
)
RecentSearch.objects.add_query_for_user(
user, query_string, len(result_set)
)
return queryset, result_set, elapsed_time
def assemble_query(self, terms, search_fields):

View File

@@ -1,11 +0,0 @@
from __future__ import unicode_literals
from rest_framework.filters import BaseFilterBackend
class RecentSearchUserFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if request.user.is_staff or request.user.is_superuser:
return queryset
else:
return queryset.filter(user=self.request.user)

View File

@@ -8,4 +8,3 @@ link_search = Link(text=_('Search'), view='search:search')
link_search_advanced = Link(
text=_('Advanced search'), view='search:search_advanced'
)
link_search_again = Link(text=_('Search again'), view='search:search_again')

View File

@@ -1,40 +0,0 @@
import urlparse
from django.contrib.auth.models import AnonymousUser
from django.db import models
from django.utils.http import urlencode
from .settings import setting_recent_count
class RecentSearchManager(models.Manager):
def add_query_for_user(self, user, query_string, hits):
parsed_query = urlparse.parse_qs(
urlencode(dict(query_string.items()))
)
for key, value in parsed_query.items():
parsed_query[key] = ' '.join(value)
if 'q' in query_string:
# Is a simple query
if not query_string['q']:
# Don't store empty simple searches
return
else:
# Cleanup query string and only store the q parameter
parsed_query = {'q': parsed_query['q']}
if parsed_query and not isinstance(user, AnonymousUser):
# If the URL query has at least one variable with a value
new_recent, created = self.model.objects.get_or_create(
user=user, query=urlencode(parsed_query),
defaults={'hits': hits}
)
if not created:
new_recent.hits = hits
new_recent.save()
for recent_to_delete in self.model.objects.filter(user=user)[setting_recent_count.value:]:
recent_to_delete.delete()

View File

@@ -15,7 +15,10 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='recentsearch',
name='user',
field=models.ForeignKey(editable=False, to=settings.AUTH_USER_MODEL, verbose_name='User'),
field=models.ForeignKey(
editable=False, to=settings.AUTH_USER_MODEL,
verbose_name='User'
),
preserve_default=True,
),
]

View File

@@ -1,75 +0,0 @@
from __future__ import unicode_literals
import urllib
import urlparse
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import (
python_2_unicode_compatible, smart_str, smart_unicode
)
from django.utils.translation import ugettext_lazy as _
from .managers import RecentSearchManager
@python_2_unicode_compatible
class RecentSearch(models.Model):
"""
Keeps a list of the [n] most recent search keywords for a given user
"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL, editable=False, verbose_name=_('User')
)
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()
def __str__(self):
# TODO: Fix this hack, store the search model name in the recent
# search entry
from .classes import SearchModel
document_search = SearchModel.get('documents.Document')
query_dict = urlparse.parse_qs(
urllib.unquote_plus(smart_str(self.query))
)
if self.is_advanced():
# Advanced search
advanced_string = []
for key, value in query_dict.items():
search_field = document_search.get_search_field(key)
advanced_string.append(
'%s: %s' % (
search_field.label, smart_unicode(' '.join(value))
)
)
display_string = ', '.join(advanced_string)
else:
# Is a simple search
display_string = smart_unicode(' '.join(query_dict['q']))
return '%s (%s)' % (display_string, self.hits)
def save(self, *args, **kwargs):
super(RecentSearch, self).save(*args, **kwargs)
def url(self):
view = 'search:results' if self.is_advanced() else 'search:search'
return '%s?%s' % (reverse(view), self.query)
def is_advanced(self):
return 'q' not in urlparse.parse_qs(self.query)
class Meta:
ordering = ('-datetime_created',)
verbose_name = _('Recent search')
verbose_name_plural = _('Recent searches')

View File

@@ -1,19 +0,0 @@
from __future__ import unicode_literals
from rest_framework import serializers
from user_management.serializers import UserSerializer
from .models import RecentSearch
class RecentSearchSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='rest_api:recentsearch-detail'
)
user = UserSerializer()
class Meta:
fields = ('datetime_created', 'hits', 'query', 'url', 'user')
model = RecentSearch
read_only_fields = ('datetime_created', 'hits', 'query', 'user')

View File

@@ -1,38 +1,21 @@
from __future__ import unicode_literals
from json import loads
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from rest_framework.test import APITestCase
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from user_management.tests import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from rest_api.tests import GenericAPITestCase
@override_settings(OCR_AUTO_OCR=False)
class SearchAPITestCase(APITestCase):
class SearchAPITestCase(GenericAPITestCase):
"""
Test the search API endpoints
"""
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
def test_search(self):
document_type = DocumentType.objects.create(
document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -45,6 +28,5 @@ class SearchAPITestCase(APITestCase):
'{}?q={}'.format(reverse('rest_api:search-view'), document.label)
)
content = loads(response.content)
self.assertEqual(content['results'][0]['label'], document.label)
self.assertEqual(content['count'], 1)
self.assertEqual(response.data['results'][0]['label'], document.label)
self.assertEqual(response.data['count'], 1)

View File

@@ -1,23 +1,25 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.test import TestCase
from documents.models import DocumentType
from documents.search import document_search
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from organizations.tests import OrganizationTestCase
from user_management.tests import (
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL
)
class DocumentSearchTestCase(TestCase):
class DocumentSearchTestCase(OrganizationTestCase):
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
super(DocumentSearchTestCase, self).setUp()
self.admin_user = get_user_model().on_organization.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -28,6 +30,7 @@ class DocumentSearchTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(DocumentSearchTestCase, self).tearDown()
def test_simple_search_after_related_name_change(self):
"""

View File

@@ -1,39 +1,26 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client
from common.tests.test_views import GenericViewTestCase
from documents.models import DocumentType
from documents.search import document_search
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from user_management.tests import (
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL
)
class Issue46TestCase(TestCase):
class Issue46TestCase(GenericViewTestCase):
"""
Functional tests to make sure issue 46 is fixed
"""
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
self.client = Client()
# Login the admin user
logged_in = self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
self.assertTrue(logged_in)
self.assertTrue(self.admin_user.is_authenticated())
super(Issue46TestCase, self).setUp()
self.login_superuser()
self.document_count = 4
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -46,9 +33,11 @@ class Issue46TestCase(TestCase):
)
def tearDown(self):
for document_type in DocumentType.objects.all():
for document_type in DocumentType.on_organization.all():
document_type.delete()
super(Issue46TestCase, self).tearDown()
def test_advanced_search_past_first_page(self):
# Make sure all documents are returned by the search
model_list, result_set, elapsed_time = document_search.search(

View File

@@ -2,28 +2,17 @@ from __future__ import unicode_literals
from django.conf.urls import patterns, url
from .api_views import (
APIRecentSearchListView, APIRecentSearchView, APISearchView
)
from .api_views import APISearchView
from .views import AdvancedSearchView, ResultsView, SearchView
urlpatterns = patterns(
'dynamic_search.views',
url(r'^$', SearchView.as_view(), name='search'),
url(r'^advanced/$', AdvancedSearchView.as_view(), name='search_advanced'),
url(r'^again/$', 'search_again', name='search_again'),
url(r'^results/$', ResultsView.as_view(), name='results'),
)
api_urls = patterns(
'',
url(
r'^recent_searches/$', APIRecentSearchListView.as_view(),
name='recentsearch-list'
),
url(
r'^recent_searches/(?P<pk>[0-9]+)/$', APIRecentSearchView.as_view(),
name='recentsearch-detail'
),
url(r'^search/$', APISearchView.as_view(), name='search-view'),
)

View File

@@ -1,11 +1,8 @@
from __future__ import unicode_literals
import logging
import urlparse
from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from common.generics import SimpleView, SingleObjectListView
@@ -82,12 +79,3 @@ class AdvancedSearchView(SearchView):
return AdvancedSearchForm(
data=self.request.GET, search_model=document_search
)
def search_again(request):
query = urlparse.urlparse(
request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))
).query
return HttpResponseRedirect(
'{}?{}'.format(reverse('search:search_advanced'), query)
)

View File

@@ -1,15 +1,11 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from ..models import Folder

View File

@@ -11,7 +11,7 @@ class FolderOrganizationViewTestCase(OrganizationViewTestCase):
def test_folder_create_view(self):
# Create a folder for organization A
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.post(
self.post(
'folders:folder_create', data={
'label': TEST_FOLDER_LABEL
}

View File

@@ -2,6 +2,8 @@ from __future__ import unicode_literals
from django.contrib import admin
from organizations.admin import OrganizationAdminMixin
from .models import SmartLink, SmartLinkCondition
@@ -13,7 +15,7 @@ class SmartLinkConditionInline(admin.StackedInline):
@admin.register(SmartLink)
class SmartLinkAdmin(admin.ModelAdmin):
class SmartLinkAdmin(OrganizationAdminMixin, admin.ModelAdmin):
def document_type_list(self, instance):
return ','.join(
instance.document_types.values_list('label', flat=True)

View File

@@ -0,0 +1,15 @@
from __future__ import unicode_literals
from django.apps import apps
from django.db import models
class OrganizationSmartLinkConditionManager(models.Manager):
def get_queryset(self):
SmartLink = apps.get_model('linking', 'SmartLink')
return super(
OrganizationSmartLinkConditionManager, self
).get_queryset().filter(
smart_link__in=SmartLink.on_organization.all(),
)

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import organizations.shortcuts
class Migration(migrations.Migration):
dependencies = [
('organizations', '0002_add_data_default_organization'),
('linking', '0005_auto_20150729_2344'),
]
operations = [
migrations.AddField(
model_name='smartlink',
name='organization',
field=models.ForeignKey(
default=organizations.shortcuts.get_current_organization,
to='organizations.Organization'
),
),
]

View File

@@ -7,14 +7,21 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from documents.models import Document, DocumentType
from organizations.models import Organization
from organizations.managers import CurrentOrganizationManager
from organizations.shortcuts import get_current_organization
from .literals import (
INCLUSION_AND, INCLUSION_CHOICES, INCLUSION_OR, OPERATOR_CHOICES
)
from .managers import OrganizationSmartLinkConditionManager
@python_2_unicode_compatible
class SmartLink(models.Model):
organization = models.ForeignKey(
Organization, default=get_current_organization
)
label = models.CharField(max_length=96, verbose_name=_('Label'))
dynamic_label = models.CharField(
blank=True, max_length=96, help_text=_(
@@ -29,6 +36,9 @@ class SmartLink(models.Model):
DocumentType, verbose_name=_('Document types')
)
objects = models.Manager()
on_organization = CurrentOrganizationManager()
def __str__(self):
return self.label
@@ -75,9 +85,9 @@ class SmartLink(models.Model):
smart_link_query |= condition_query
if smart_link_query:
return Document.objects.filter(smart_link_query)
return Document.on_organization.filter(smart_link_query)
else:
return Document.objects.none()
return Document.on_organization.none()
def resolve_for(self, document):
return ResolvedSmartLink(
@@ -123,6 +133,9 @@ class SmartLinkCondition(models.Model):
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
objects = models.Manager()
on_organization = OrganizationSmartLinkConditionManager()
def __str__(self):
return '%s foreign %s %s %s %s' % (
self.get_inclusion_display(),

View File

@@ -3,10 +3,11 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
from organizations.tests import OrganizationTestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
@@ -17,9 +18,11 @@ from .literals import TEST_SMART_LINK_LABEL, TEST_SMART_LINK_DYNAMIC_LABEL
@override_settings(OCR_AUTO_OCR=False)
class SmartLinkTestCase(TestCase):
class SmartLinkTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(SmartLinkTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -28,16 +31,17 @@ class SmartLinkTestCase(TestCase):
file_object=file_object
)
self.user = get_user_model().objects.create_superuser(
self.user = get_user_model().on_organization.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
def tearDown(self):
self.document_type.delete()
super(SmartLinkTestCase, self).tearDown()
def test_dynamic_label(self):
smart_link = SmartLink.objects.create(
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL,
dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL
)

View File

@@ -0,0 +1,94 @@
from __future__ import unicode_literals
from organizations.tests.test_organization_views import OrganizationViewTestCase
from ..models import SmartLink
from .literals import (
TEST_SMART_LINK_DYNAMIC_LABEL, TEST_SMART_LINK_EDITED_LABEL,
TEST_SMART_LINK_LABEL
)
class OrganizationIndexViewTestCase(OrganizationViewTestCase):
def create_smart_link(self):
self.smart_link = SmartLink.on_organization.create(
organization=self.organization_a, label=TEST_SMART_LINK_LABEL
)
def test_smart_link_create_view(self):
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.post(
'linking:smart_link_create', data={
'label': TEST_SMART_LINK_LABEL
}
)
self.assertEqual(SmartLink.on_organization.count(), 1)
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
self.assertEqual(SmartLink.on_organization.count(), 0)
def test_smart_link_delete_view(self):
self.create_smart_link()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.post(
'linking:smart_link_delete', args=(self.smart_link.pk,)
)
self.assertEqual(response.status_code, 404)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.assertEqual(SmartLink.on_organization.count(), 1)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.post(
'linking:smart_link_delete', args=(self.smart_link.pk,)
)
self.assertEqual(response.status_code, 302)
self.assertEqual(SmartLink.on_organization.count(), 0)
def test_smart_link_edit_view(self):
self.create_smart_link()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.post(
'linking:smart_link_edit', args=(self.smart_link.pk,),
data={
'label': TEST_SMART_LINK_EDITED_LABEL
}
)
self.assertEqual(response.status_code, 404)
self.assertEqual(self.smart_link.label, TEST_SMART_LINK_LABEL)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.post(
'linking:smart_link_edit', args=(self.smart_link.pk,),
data={
'label': TEST_SMART_LINK_EDITED_LABEL
}
)
self.assertEqual(response.status_code, 302)
self.smart_link.refresh_from_db()
self.assertEqual(
self.smart_link.label, TEST_SMART_LINK_EDITED_LABEL
)
def test_smart_link_list_view(self):
self.create_smart_link()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.get('linking:smart_link_list')
self.assertNotContains(
response, text=self.smart_link.label, status_code=200
)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.get('linking:smart_link_list')
self.assertContains(
response, text=self.smart_link.label, status_code=200
)

View File

@@ -29,7 +29,7 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
)
self.assertEquals(response.status_code, 403)
self.assertEqual(SmartLink.objects.count(), 0)
self.assertEqual(SmartLink.on_organization.count(), 0)
def test_smart_link_create_view_with_permission(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
@@ -44,21 +44,23 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
}, follow=True
)
self.assertContains(response, text='created', status_code=200)
self.assertEqual(SmartLink.objects.count(), 1)
self.assertEqual(SmartLink.on_organization.count(), 1)
self.assertEqual(
SmartLink.objects.first().label, TEST_SMART_LINK_LABEL
SmartLink.on_organization.first().label, TEST_SMART_LINK_LABEL
)
def test_smart_link_delete_view_no_permission(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL)
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL
)
response = self.post(
'linking:smart_link_delete', args=(smart_link.pk,)
)
self.assertEqual(response.status_code, 403)
self.assertEqual(SmartLink.objects.count(), 1)
self.assertEqual(SmartLink.on_organization.count(), 1)
def test_smart_link_delete_view_with_permission(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
@@ -67,19 +69,23 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
permission_smart_link_delete.stored_permission
)
smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL)
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL
)
response = self.post(
'linking:smart_link_delete', args=(smart_link.pk,), follow=True
)
self.assertContains(response, text='deleted', status_code=200)
self.assertEqual(SmartLink.objects.count(), 0)
self.assertEqual(SmartLink.on_organization.count(), 0)
def test_smart_link_edit_view_no_permission(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL)
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL
)
response = self.post(
'linking:smart_link_edit', args=(smart_link.pk,), data={
@@ -87,7 +93,7 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
}
)
self.assertEqual(response.status_code, 403)
smart_link = SmartLink.objects.get(pk=smart_link.pk)
smart_link = SmartLink.on_organization.get(pk=smart_link.pk)
self.assertEqual(smart_link.label, TEST_SMART_LINK_LABEL)
def test_smart_link_edit_view_with_permission(self):
@@ -97,7 +103,9 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
permission_smart_link_edit.stored_permission
)
smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL)
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL
)
response = self.post(
'linking:smart_link_edit', args=(smart_link.pk,), data={
@@ -105,18 +113,18 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase):
}, follow=True
)
smart_link = SmartLink.objects.get(pk=smart_link.pk)
smart_link = SmartLink.on_organization.get(pk=smart_link.pk)
self.assertContains(response, text='update', status_code=200)
self.assertEqual(smart_link.label, TEST_SMART_LINK_EDITED_LABEL)
def setup_smart_links(self):
smart_link = SmartLink.objects.create(
smart_link = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL,
dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL
)
smart_link.document_types.add(self.document_type)
smart_link_2 = SmartLink.objects.create(
smart_link_2 = SmartLink.on_organization.create(
label=TEST_SMART_LINK_LABEL,
dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL
)

View File

@@ -31,10 +31,10 @@ logger = logging.getLogger(__name__)
class ResolvedSmartLinkView(DocumentListView):
def dispatch(self, request, *args, **kwargs):
self.document = get_object_or_404(
Document, pk=self.kwargs['document_pk']
Document.on_organization, pk=self.kwargs['document_pk']
)
self.smart_link = get_object_or_404(
SmartLink, pk=self.kwargs['smart_link_pk']
SmartLink.on_organization, pk=self.kwargs['smart_link_pk']
)
try:
@@ -63,7 +63,7 @@ class ResolvedSmartLinkView(DocumentListView):
try:
queryset = self.smart_link.get_linked_document_for(self.document)
except Exception as exception:
queryset = Document.objects.none()
queryset = Document.on_organization.none()
if self.request.user.is_staff or self.request.user.is_superuser:
messages.error(
@@ -110,12 +110,14 @@ class SetupSmartLinkDocumentTypesView(AssignRemoveView):
}
def get_object(self):
return get_object_or_404(SmartLink, pk=self.kwargs['pk'])
return get_object_or_404(
SmartLink.on_organization, pk=self.kwargs['pk']
)
def left_list(self):
# TODO: filter document type list by user ACL
return AssignRemoveView.generate_choices(
DocumentType.objects.exclude(
DocumentType.on_organization.exclude(
pk__in=self.get_object().document_types.all()
)
)
@@ -144,12 +146,14 @@ class SmartLinkListView(SingleObjectListView):
return super(SmartLinkListView, self).get_queryset()
def get_smart_link_queryset(self):
return SmartLink.objects.all()
return SmartLink.on_organization.all()
class DocumentSmartLinkListView(SmartLinkListView):
def dispatch(self, request, *args, **kwargs):
self.document = get_object_or_404(Document, pk=self.kwargs['pk'])
self.document = get_object_or_404(
Document.on_organization, pk=self.kwargs['pk']
)
try:
Permission.check_permissions(
@@ -174,7 +178,7 @@ class DocumentSmartLinkListView(SmartLinkListView):
}
def get_smart_link_queryset(self):
return ResolvedSmartLink.objects.filter(
return ResolvedSmartLink.on_organization.filter(
document_types=self.document.document_type, enabled=True
)
@@ -188,7 +192,6 @@ class SmartLinkCreateView(SingleObjectCreateView):
class SmartLinkEditView(SingleObjectEditView):
form_class = SmartLinkForm
model = SmartLink
post_action_redirect = reverse_lazy('linking:smart_link_list')
view_permission = permission_smart_link_edit
@@ -198,9 +201,11 @@ class SmartLinkEditView(SingleObjectEditView):
'title': _('Edit smart link: %s') % self.get_object()
}
def get_queryset(self):
return SmartLink.on_organization.all()
class SmartLinkDeleteView(SingleObjectDeleteView):
model = SmartLink
post_action_redirect = reverse_lazy('linking:smart_link_list')
view_permission = permission_smart_link_delete
@@ -210,6 +215,9 @@ class SmartLinkDeleteView(SingleObjectDeleteView):
'title': _('Delete smart link: %s') % self.get_object()
}
def get_queryset(self):
return SmartLink.on_organization.all()
class SmartLinkConditionListView(SingleObjectListView):
view_permission = permission_smart_link_edit
@@ -227,7 +235,9 @@ class SmartLinkConditionListView(SingleObjectListView):
return self.get_smart_link().conditions.all()
def get_smart_link(self):
return get_object_or_404(SmartLink, pk=self.kwargs['pk'])
return get_object_or_404(
SmartLink.on_organization, pk=self.kwargs['pk']
)
class SmartLinkConditionCreateView(SingleObjectCreateView):
@@ -269,12 +279,13 @@ class SmartLinkConditionCreateView(SingleObjectCreateView):
return self.get_smart_link().conditions.all()
def get_smart_link(self):
return get_object_or_404(SmartLink, pk=self.kwargs['pk'])
return get_object_or_404(
SmartLink.on_organization, pk=self.kwargs['pk']
)
class SmartLinkConditionEditView(SingleObjectEditView):
form_class = SmartLinkConditionForm
model = SmartLinkCondition
def dispatch(self, request, *args, **kwargs):
try:
@@ -306,10 +317,11 @@ class SmartLinkConditionEditView(SingleObjectEditView):
)
)
def get_queryset(self):
return SmartLinkCondition.on_organization.all()
class SmartLinkConditionDeleteView(SingleObjectDeleteView):
model = SmartLinkCondition
def dispatch(self, request, *args, **kwargs):
try:
Permission.check_permissions(
@@ -341,3 +353,6 @@ class SmartLinkConditionDeleteView(SingleObjectDeleteView):
self.get_object().smart_link.pk,
)
)
def get_queryset(self):
return SmartLinkCondition.on_organization.all()

View File

@@ -1,15 +1,11 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from ..models import DocumentMetadata, DocumentTypeMetadataType, MetadataType

View File

@@ -5,9 +5,8 @@ from organizations.tests.test_organization_views import OrganizationViewTestCase
from ..models import MetadataType
from .literals import (
TEST_DOCUMENT_METADATA_VALUE_2, TEST_METADATA_TYPE_LABEL,
TEST_METADATA_TYPE_LABEL_2, TEST_METADATA_TYPE_NAME,
TEST_METADATA_TYPE_NAME_2
TEST_METADATA_TYPE_LABEL, TEST_METADATA_TYPE_LABEL_2,
TEST_METADATA_TYPE_NAME,
)

View File

@@ -8,12 +8,10 @@ from pdfminer.pdfpage import PDFPage
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
import subprocess
import tempfile
from django.utils.translation import ugettext_lazy as _
from common.settings import setting_temporary_directory
from common.utils import copyfile
from common.utils import copyfile, fs_cleanup, mkstemp
from .exceptions import ParserError, NoMIMETypeMatch
from .models import DocumentPageContent
@@ -137,9 +135,7 @@ class PopplerParser(Parser):
def execute(self, file_object, page_number):
logger.debug('Parsing PDF page: %d', page_number)
destination_descriptor, temp_filepath = tempfile.mkstemp(
dir=setting_temporary_directory.value
)
destination_descriptor, temp_filepath = mkstemp()
copyfile(file_object, temp_filepath)
command = []
@@ -158,9 +154,12 @@ class PopplerParser(Parser):
return_code = proc.wait()
if return_code != 0:
logger.error(proc.stderr.readline())
fs_cleanup(temp_filepath, file_descriptor=destination_descriptor)
raise ParserError
output = proc.stdout.read()
fs_cleanup(temp_filepath, file_descriptor=destination_descriptor)
if output == b'\x0c':
logger.debug('Parser didn\'t return any output')

View File

@@ -16,7 +16,6 @@ from ..parsers import PDFMinerParser, PopplerParser
class ParserTestCase(OrganizationTestCase):
def setUp(self):
super(ParserTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)

View File

@@ -1,12 +1,10 @@
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):
class OrganizationApp(MayanAppConfig):
name = 'organizations'
verbose_name = _('Organizations')

View File

@@ -1,15 +1,10 @@
from __future__ import unicode_literals
import os
from django.conf import settings
from django.core import management
from django.utils.crypto import get_random_string
from ...models import Organization
class Command(management.BaseCommand):
help = 'Creates an organization admin user with a secure random password and all permissions.'

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.db import migrations
from ..literals import DEFAULT_ORGANIZATION_LABEL
@@ -9,7 +9,7 @@ from ..literals import DEFAULT_ORGANIZATION_LABEL
def default_data(apps, schema_editor):
# We can't import the Organization model directly as it may be a newer
# version than this migration expects. We use the historical version.
Organization = apps.get_model("organizations", "Organization")
Organization = apps.get_model('organizations', 'Organization')
default_organization = Organization(label=DEFAULT_ORGANIZATION_LABEL)
default_organization.save()

View File

@@ -1,16 +1,13 @@
from __future__ import absolute_import, unicode_literals
import logging
import string
import warnings
from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.exceptions import ImproperlyConfigured
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 _
@@ -110,7 +107,7 @@ class Organization(models.Model):
)
account = UserModel.objects.get(
**{UserModel.USERNAME_FIELD: username, 'organization': self,}
**{UserModel.USERNAME_FIELD: username, 'organization': self}
)
account.set_password(raw_password=password_value)
account.save()

View File

@@ -1,7 +1,5 @@
from __future__ import unicode_literals
import tempfile
from django.utils.translation import ugettext_lazy as _
from smart_settings import Namespace

View File

@@ -4,5 +4,5 @@ from django.apps import apps
def get_current_organization():
from .models import Organization
Organization = apps.get_model('organizations', 'Organization')
return Organization.objects.get_current().pk

View File

@@ -1 +1 @@
from .base import OrganizationTestCase #NOQA
from .base import OrganizationTestCase # NOQA

View File

@@ -1,12 +1,12 @@
from __future__ import unicode_literals
from django.test import TestCase
from common.tests import BaseTestCase
from ..models import Organization
from ..utils import create_default_organization
class OrganizationTestCase(TestCase):
class OrganizationTestCase(BaseTestCase):
def setUp(self):
create_default_organization()

View File

@@ -1,4 +1,3 @@
from __future__ import absolute_import, unicode_literals
TEST_ORGANIZATION_LABEL = 'test organization label'
TEST_ORGANIZATION_EDITED_LABEL = 'test organization edited label'

View File

@@ -4,7 +4,7 @@ import unittest
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.db import connections, router
from django.http import HttpRequest
from django.test import TestCase, modify_settings, override_settings

View File

@@ -4,7 +4,7 @@ from django.test import TestCase
from ..models import Organization
from .literals import TEST_ORGANIZATION_LABEL, TEST_ORGANIZATION_EDITED_LABEL
from .literals import TEST_ORGANIZATION_LABEL
class OrganizationModelTestCase(TestCase):

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
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
from .literals import DEFAULT_ORGANIZATION_LABEL
@@ -28,12 +27,14 @@ def create_default_organization(verbosity=2, interactive=True, using=DEFAULT_DB_
# 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")
print('Creating default Organization object')
Organization(pk=1, label=DEFAULT_ORGANIZATION_LABEL).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])
sequence_sql = connections[using].ops.sequence_reset_sql(
no_style(), [Organization]
)
if sequence_sql:
if verbosity >= 2:
print('Resetting sequence')

View File

@@ -9,7 +9,7 @@ from user_management.models import MayanGroup
from user_management.tests import TEST_GROUP, TEST_USER_USERNAME
from ..classes import Permission
from ..models import Role, StoredPermission
from ..models import Role
from ..permissions import permission_role_view
from .literals import TEST_ROLE_LABEL

View File

@@ -1 +1 @@
from .base import GenericAPITestCase #NOQA
from .base import GenericAPITestCase # NOQA

View File

@@ -1,8 +1,5 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.test import override_settings
from rest_framework.test import APITestCase
from organizations.models import Organization
@@ -24,7 +21,6 @@ class GenericAPITestCase(APITestCase):
username=self.admin_user.username, password=password
)
def tearDown(self):
super(GenericAPITestCase, self).tearDown()
self.admin_user.delete()

View File

@@ -2,18 +2,18 @@ from __future__ import unicode_literals
import os
import shutil
import tempfile
from django.test import TestCase
from documents.tests import TEST_NON_ASCII_DOCUMENT_PATH
from common.utils import mkdtemp
from ..classes import StagingFile
class StagingFileTestCase(TestCase):
def test_unicode_staging_file(self):
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
shutil.copy(TEST_NON_ASCII_DOCUMENT_PATH, temporary_directory)
filename = os.path.basename(TEST_NON_ASCII_DOCUMENT_PATH)

View File

@@ -1,13 +1,13 @@
from __future__ import unicode_literals
import shutil
import tempfile
from django.contrib.auth import get_user_model
from django.core.files.base import File
from django.test import TestCase, override_settings
from django.test.client import Client
from common.utils import mkdtemp
from documents.models import Document, DocumentType
from documents.tests import (
TEST_COMPRESSED_DOCUMENT_PATH, TEST_DOCUMENT_TYPE,
@@ -55,7 +55,7 @@ class UploadDocumentTestCase(TestCase):
gh-issue #163 https://github.com/mayan-edms/mayan-edms/issues/163
"""
temporary_directory = tempfile.mkdtemp()
temporary_directory = mkdtemp()
shutil.copy(TEST_NON_ASCII_DOCUMENT_PATH, temporary_directory)
watch_folder = WatchFolderSource.on_organization.create(

View File

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
import os
import shutil
import tempfile
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
@@ -11,6 +10,7 @@ from django.test import TestCase, override_settings
from acls.models import AccessControlList
from common.tests.test_views import GenericViewTestCase
from common.utils import fs_cleanup, mkdtemp
from documents.models import Document, DocumentType, NewVersionBlock
from documents.permissions import permission_document_create
from documents.tests import (
@@ -230,15 +230,14 @@ class NewDocumentVersionViewTestCase(GenericDocumentViewTestCase):
class StagingFolderTestCase(GenericViewTestCase):
def setUp(self):
super(StagingFolderTestCase, self).setUp()
self.temporary_directory = tempfile.mkdtemp()
# TODO: remove temp directory after test
self.temporary_directory = mkdtemp()
shutil.copy(TEST_SMALL_DOCUMENT_PATH, self.temporary_directory)
self.filename = os.path.basename(TEST_SMALL_DOCUMENT_PATH)
def tearDown(self):
fs_cleanup(self.temporary_directory)
super(StagingFolderTestCase, self).tearDown()
shutil.rmtree(self.temporary_directory)
def test_staging_folder_delete_no_permission(self):
self.login(

View File

@@ -1,15 +1,11 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from rest_api.tests.base import GenericAPITestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from ..models import Tag

View File

@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from django.core.files.base import File
from django.test import TestCase, override_settings
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH

View File

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.utils.translation import ugettext_lazy as _
from organizations.admin import OrganizationAdminMixin

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.contrib.auth.models
import organizations.shortcuts
import user_management.models
class Migration(migrations.Migration):
dependencies = [
('user_management', '0003_auto_20160525_0155'),
]
operations = [
migrations.AlterModelManagers(
name='mayanuser',
managers=[
('objects', django.contrib.auth.models.UserManager()),
('on_organization', user_management.models.OrganizationUserManagerHybridClass()),
],
),
migrations.AlterField(
model_name='mayanuser',
name='organization',
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, blank=True, to='organizations.Organization', null=True),
),
migrations.AlterField(
model_name='mayanuser',
name='organization_groups',
field=models.ManyToManyField(related_query_name='user', related_name='users', to='user_management.MayanGroup', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='Groups'),
),
]

View File

@@ -18,6 +18,10 @@ class MayanGroup(Group):
objects = GroupManager()
on_organization = CurrentOrganizationManager()
class Meta:
verbose_name = _('Group')
verbose_name_plural = _('Groups')
class OrganizationUserManagerHybridClass(CurrentOrganizationManager, UserManager):
"""
@@ -27,7 +31,7 @@ class OrganizationUserManagerHybridClass(CurrentOrganizationManager, UserManager
class MayanUser(AbstractUser):
organization = models.ForeignKey(
Organization, default=get_current_organization
Organization, blank=True, default=get_current_organization, null=True
)
organization_groups = models.ManyToManyField(

View File

@@ -4,4 +4,4 @@ coveralls==0.5
django-test-without-migrations==0.2
mock==2.0.0
tox==2.1.1
psutil==4.3.0