Compare commits

..

22 Commits

Author SHA1 Message Date
Roberto Rosario
adf47646bf Initial commit for the document trashed event
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-24 08:29:45 -04:00
Roberto Rosario
4d7c0552bd Fix help text of the platformtemplate command
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-22 00:04:34 -04:00
Roberto Rosario
575e42357a Update 3.2.2 release notes
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-21 14:43:04 -04:00
Roberto Rosario
487c250f3e Merge branch 'master' into versions/minor 2019-06-21 11:52:40 -04:00
Roberto Rosario
0ae214f2fd Support configurable GUnicorn timeouts
Defaults to current value of 120 seconds.

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-21 11:51:01 -04:00
Roberto Rosario
037b942c4a Update release date of version 3.2.3
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-21 11:48:01 -04:00
Roberto Rosario
dcbf777429 Merge branch 'defect/628-positional-arguments-for-mailbox' into 'master'
Defect/628 positional arguments for mailbox

Closes #628

See merge request mayan-edms/mayan-edms!51
2019-06-21 13:35:23 +00:00
Jesaja Everling
0c5a0b54c1 Defect/628 positional arguments for mailbox 2019-06-21 13:35:23 +00:00
Roberto Rosario
0f5f8c0dd1 Update build number
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-21 00:03:53 -04:00
Roberto Rosario
2d875ff044 Bump version to 3.2.3
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-21 00:01:37 -04:00
Roberto Rosario
a77708ebe6 Update release notes
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-06-20 23:52:00 -04:00
Roberto Rosario
440822896a Fix the Django SMTP backend username field name
Increase the Django STMP username. GitLab issue #625. Thanks to Jesaja
Everling (@jeverling) for the report and the research.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-20 23:42:16 -04:00
Roberto Rosario
6f4802ac6a Fix mailing profile log columns mappings
GitLab issue #626. Thanks to Jesaja Everling (@jeverling)
for the report.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-20 23:14:33 -04:00
Roberto Rosario
c21a9afcfa Merge branch 'master' into versions/minor 2019-06-19 23:53:04 -04:00
Roberto Rosario
0494fa9e6a Merge branch 'defect/619-fix-poplib-initialization' into 'master'
Fix kwargs for poplib.POP3_SSL and poplib.POP3

Closes #619

See merge request mayan-edms/mayan-edms!50
2019-06-20 03:51:31 +00:00
Jesaja Everling
e09bd48d65 Fix kwargs for poplib.POP3_SSL and poplib.POP3 2019-06-20 03:51:31 +00:00
Roberto Rosario
97887c4e9c Allow disabling the random primary key test mixin
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 23:05:01 -04:00
Roberto Rosario
90e884e502 Fix test permission use
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 10:22:56 -04:00
Roberto Rosario
ea7b4e46ac Update build number
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 00:40:02 -04:00
Roberto Rosario
e0d74d54b1 Update release data and bump version to 3.2.2
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 00:39:13 -04:00
Roberto Rosario
fbd2b43b5e Update the task_check_interval_source reference
GitLab issue #617. Thanks to Lukas Gill (@lukkigi) for
the report and debug information.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 00:33:44 -04:00
Roberto Rosario
03e59ed964 Fix document parsing tool view typo
Closes GitLab issue #615. Thanks to Tyler Page (@iamtpage) for the
report.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-06-19 00:19:18 -04:00
28 changed files with 485 additions and 145 deletions

View File

@@ -1,7 +1,32 @@
3.2.2 (2019-06-XX) 3.2.4 (2019-06-XX)
==================
* Support configurable GUnicorn timeouts. Defaults to
current value of 120 seconds.
* Fix help text of the platformtemplate command.
3.2.3 (2019-06-21)
==================
* Add support for disabling the random primary key
test mixin.
* Fix mailing profile log columns mappings.
GitLab issue #626. Thanks to Jesaja Everling (@jeverling)
for the report.
* Fix the Django SMTP backend username field name.
GitLab issue #625. Thanks to Jesaja Everling (@jeverling)
for the report and the research.
* Increase the Django STMP username.
GitLab issue #625. Thanks to Jesaja Everling (@jeverling)
for the report and the research.
3.2.2 (2019-06-19)
================== ==================
* Fix document type change view. Closes GitLab issue #614 * Fix document type change view. Closes GitLab issue #614
Thanks to Christoph Roeder (@brightdroid) for the report. Thanks to Christoph Roeder (@brightdroid) for the report.
* Fix document parsing tool view typo. Closes GitLab issue #615.
Thanks to Tyler Page (@iamtpage) for the report.
* Update the task_check_interval_source reference
GitLab issue #617. Thanks to Lukas Gill (@lukkigi) for
the report and debug information.
3.2.1 (2019-06-14) 3.2.1 (2019-06-14)
================== ==================

View File

@@ -57,9 +57,8 @@ apt-get update \
&& echo "maxmemory-policy allkeys-lru" >> /etc/redis/redis.conf \ && echo "maxmemory-policy allkeys-lru" >> /etc/redis/redis.conf \
# Disable saving the Redis database # Disable saving the Redis database
echo "save \"\"" >> /etc/redis/redis.conf \ echo "save \"\"" >> /etc/redis/redis.conf \
# Only provision 2 database. One for the broker and the other for # Only provision 1 database
# results && echo "databases 1" >> /etc/redis/redis.conf
&& echo "databases 2" >> /etc/redis/redis.conf
#### ####
@@ -97,14 +96,14 @@ apt-get install -y --no-install-recommends \
libssl-dev \ libssl-dev \
g++ \ g++ \
gcc \ gcc \
python3-dev \ python-dev \
python3-virtualenv \ python-virtualenv \
&& mkdir -p "${PROJECT_INSTALL_DIR}" \ && mkdir -p "${PROJECT_INSTALL_DIR}" \
&& chown -R mayan:mayan "${PROJECT_INSTALL_DIR}" \ && chown -R mayan:mayan "${PROJECT_INSTALL_DIR}" \
&& chown -R mayan:mayan /src && chown -R mayan:mayan /src
USER mayan USER mayan
RUN python3 -m virtualenv -p /usr/bin/python3 "${PROJECT_INSTALL_DIR}" \ RUN python -m virtualenv "${PROJECT_INSTALL_DIR}" \
&& . "${PROJECT_INSTALL_DIR}/bin/activate" \ && . "${PROJECT_INSTALL_DIR}/bin/activate" \
&& pip install --no-cache-dir --no-use-pep517 \ && pip install --no-cache-dir --no-use-pep517 \
librabbitmq==1.6.1 \ librabbitmq==1.6.1 \

View File

@@ -22,6 +22,7 @@ export MAYAN_SETTINGS_MODULE=${MAYAN_SETTINGS_MODULE:-mayan.settings.production}
export MAYAN_GUNICORN_BIN=${MAYAN_PYTHON_BIN_DIR}gunicorn export MAYAN_GUNICORN_BIN=${MAYAN_PYTHON_BIN_DIR}gunicorn
export MAYAN_GUNICORN_WORKERS=${MAYAN_GUNICORN_WORKERS:-2} export MAYAN_GUNICORN_WORKERS=${MAYAN_GUNICORN_WORKERS:-2}
export MAYAN_GUNICORN_TIMEOUT=${MAYAN_GUNICORN_TIMEOUT:-120}
export MAYAN_PIP_BIN=${MAYAN_PYTHON_BIN_DIR}pip export MAYAN_PIP_BIN=${MAYAN_PYTHON_BIN_DIR}pip
export MAYAN_STATIC_ROOT=${MAYAN_INSTALL_DIR}/static export MAYAN_STATIC_ROOT=${MAYAN_INSTALL_DIR}/static

View File

@@ -1 +1 @@
3.2.1 3.2.3

View File

@@ -1,7 +1,7 @@
Version 3.2.2 Version 3.2.2
============= =============
Released: June 17, 2019 Released: June 19, 2019
Changes Changes
@@ -9,6 +9,11 @@ Changes
- Fix document type change view. Closes GitLab issue #614. - Fix document type change view. Closes GitLab issue #614.
Thanks to Christoph Roeder (@brightdroid) for the report. Thanks to Christoph Roeder (@brightdroid) for the report.
- Fix document parsing tool view typo. Closes GitLab issue #615.
Thanks to Tyler Page (@iamtpage) for the report.
- Update the task_check_interval_source reference
GitLab issue #617. Thanks to Lukas Gill (@lukkigi) for
the report and debug information.
Removals Removals
-------- --------
@@ -28,7 +33,7 @@ Remove deprecated requirements::
Type in the console:: Type in the console::
$ pip install mayan-edms==3.2.1 $ pip install mayan-edms==3.2.2
the requirements will also be updated automatically. the requirements will also be updated automatically.
@@ -98,5 +103,7 @@ Bugs fixed or issues closed
--------------------------- ---------------------------
- :gitlab-issue:`614` change type exception - :gitlab-issue:`614` change type exception
- :gitlab-issue:`615` TypeError: success() got an unexpected keyword argument 'requrest'
- :gitlab-issue:`617` Watcher Task not running
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/ .. _PyPI: https://pypi.python.org/pypi/mayan-edms/

114
docs/releases/3.2.3.rst Normal file
View File

@@ -0,0 +1,114 @@
Version 3.2.3
=============
Released: June 21, 2019
Changes
-------
- Add support for disabling the random primary key
test mixin.
- Fix mailing profile log columns mappings.
GitLab issue #626. Thanks to Jesaja Everling (@jeverling)
for the report.
- Fix the Django SMTP backend username field name.
GitLab issue #625. Thanks to Jesaja Everling (@jeverling)
for the report and the research.
- Increase the Django STMP username.
GitLab issue #625. Thanks to Jesaja Everling (@jeverling)
for the report and the research.
Removals
--------
- None
Upgrading from a previous version
---------------------------------
If installed via Python's PIP
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Remove deprecated requirements::
$ curl https://gitlab.com/mayan-edms/mayan-edms/raw/master/removals.txt | pip uninstall -r /dev/stdin
Type in the console::
$ pip install mayan-edms==3.2.1
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.
Remove deprecated requirements::
$ pip uninstall -y -r removals.txt
Next upgrade/add the new requirements::
$ pip install --upgrade -r requirements.txt
Common steps
^^^^^^^^^^^^
Perform these steps after updating the code from either step above.
Make a backup of your supervisord file::
sudo cp /etc/supervisor/conf.d/mayan.conf /etc/supervisor/conf.d/mayan.conf.bck
Update the supervisord configuration file. Replace the environment
variables values show here with your respective settings. This step will refresh
the supervisord configuration file with the new queues and the latest
recommended layout::
MAYAN_DATABASE_ENGINE=django.db.backends.postgresql MAYAN_DATABASE_NAME=mayan \
MAYAN_DATABASE_PASSWORD=mayanuserpass MAYAN_DATABASE_USER=mayan \
MAYAN_DATABASE_HOST=127.0.0.1 MAYAN_MEDIA_ROOT=/opt/mayan-edms/media \
/opt/mayan-edms/bin/mayan-edms.py platformtemplate supervisord > /etc/supervisor/conf.d/mayan.conf
Edit the supervisord configuration file and update any setting the template
generator missed::
vi /etc/supervisor/conf.d/mayan.conf
Migrate existing database schema with::
$ mayan-edms.py performupgrade
Add new static media::
$ mayan-edms.py preparestatic --noinput
The upgrade procedure is now complete.
Backward incompatible changes
-----------------------------
- None
Bugs fixed or issues closed
---------------------------
- :gitlab-issue:`619` poplib.POP3_SSL and poplib.POP3 initialized with wrong kwarg
- :gitlab-issue:`625` mayan.apps.mailer.mailers.DjangoSMTP uses "user", but django.core.mail.backends.smtp.EmailBackend expects "username"
- :gitlab-issue:`626` Mailing profile error log is empty, despite errors
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/

103
docs/releases/3.2.4.rst Normal file
View File

@@ -0,0 +1,103 @@
Version 3.2.4
=============
Released: June XX, 2019
Changes
-------
- Support configurable GUnicorn timeouts. Defaults to
current value of 120 seconds.
- Fix help text of the platformtemplate command.
Removals
--------
- None
Upgrading from a previous version
---------------------------------
If installed via Python's PIP
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Remove deprecated requirements::
$ curl https://gitlab.com/mayan-edms/mayan-edms/raw/master/removals.txt | pip uninstall -r /dev/stdin
Type in the console::
$ pip install mayan-edms==3.2.3
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.
Remove deprecated requirements::
$ pip uninstall -y -r removals.txt
Next upgrade/add the new requirements::
$ pip install --upgrade -r requirements.txt
Common steps
^^^^^^^^^^^^
Perform these steps after updating the code from either step above.
Make a backup of your supervisord file::
sudo cp /etc/supervisor/conf.d/mayan.conf /etc/supervisor/conf.d/mayan.conf.bck
Update the supervisord configuration file. Replace the environment
variables values show here with your respective settings. This step will refresh
the supervisord configuration file with the new queues and the latest
recommended layout::
MAYAN_DATABASE_ENGINE=django.db.backends.postgresql MAYAN_DATABASE_NAME=mayan \
MAYAN_DATABASE_PASSWORD=mayanuserpass MAYAN_DATABASE_USER=mayan \
MAYAN_DATABASE_HOST=127.0.0.1 MAYAN_MEDIA_ROOT=/opt/mayan-edms/media \
/opt/mayan-edms/bin/mayan-edms.py platformtemplate supervisord > /etc/supervisor/conf.d/mayan.conf
Edit the supervisord configuration file and update any setting the template
generator missed::
vi /etc/supervisor/conf.d/mayan.conf
Migrate existing database schema with::
$ mayan-edms.py performupgrade
Add new static media::
$ mayan-edms.py preparestatic --noinput
The upgrade procedure is now complete.
Backward incompatible changes
-----------------------------
- None
Bugs fixed or issues closed
---------------------------
- :gitlab-issue:`628` mailbox.user in POP3Email gets passed keyword argument, but only accepts "user" or positional argument
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/

View File

@@ -20,6 +20,8 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
3.2.4
3.2.3
3.2.2 3.2.2
3.2.1 3.2.1
3.2 3.2

View File

@@ -1,9 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__title__ = 'Mayan EDMS' __title__ = 'Mayan EDMS'
__version__ = '3.2.1' __version__ = '3.2.3'
__build__ = 0x030201 __build__ = 0x030203
__build_string__ = 'v3.2.1_Fri Jun 14 03:01:40 2019 -0400' __build_string__ = 'v3.2.3_Fri Jun 21 00:01:37 2019 -0400'
__django_version__ = '1.11' __django_version__ = '1.11'
__author__ = 'Roberto Rosario' __author__ = 'Roberto Rosario'
__author_email__ = 'roberto.rosario@mayan-edms.com' __author_email__ = 'roberto.rosario@mayan-edms.com'

View File

@@ -144,6 +144,7 @@ class RandomPrimaryKeyModelMonkeyPatchMixin(object):
random_primary_key_random_floor = 100 random_primary_key_random_floor = 100
random_primary_key_random_ceiling = 10000 random_primary_key_random_ceiling = 10000
random_primary_key_maximum_attempts = 100 random_primary_key_maximum_attempts = 100
random_primary_key_enable = True
@staticmethod @staticmethod
def get_unique_primary_key(model): def get_unique_primary_key(model):
@@ -170,47 +171,49 @@ class RandomPrimaryKeyModelMonkeyPatchMixin(object):
return primary_key return primary_key
def setUp(self): def setUp(self):
self.method_save_original = models.Model.save if self.random_primary_key_enable:
self.method_save_original = models.Model.save
def method_save_new(instance, *args, **kwargs): def method_save_new(instance, *args, **kwargs):
if instance.pk: if instance.pk:
return self.method_save_original(instance, *args, **kwargs) return self.method_save_original(instance, *args, **kwargs)
else: else:
# Set meta.auto_created to True to have the original save_base # Set meta.auto_created to True to have the original save_base
# not send the pre_save signal which would normally send # not send the pre_save signal which would normally send
# the instance without a primary key. Since we assign a random # the instance without a primary key. Since we assign a random
# primary key any pre_save signal handler that relies on an # primary key any pre_save signal handler that relies on an
# empty primary key will fail. # empty primary key will fail.
# The meta.auto_created and manual pre_save sending emulates # The meta.auto_created and manual pre_save sending emulates
# the original behavior. Since meta.auto_created also disables # the original behavior. Since meta.auto_created also disables
# the post_save signal we must also send it ourselves. # the post_save signal we must also send it ourselves.
# This hack work with Django 1.11 .save_base() but can break # This hack work with Django 1.11 .save_base() but can break
# in future versions if that method is updated. # in future versions if that method is updated.
pre_save.send( pre_save.send(
sender=instance.__class__, instance=instance, raw=False, sender=instance.__class__, instance=instance, raw=False,
update_fields=None, update_fields=None,
) )
instance._meta.auto_created = True instance._meta.auto_created = True
instance.pk = RandomPrimaryKeyModelMonkeyPatchMixin.get_unique_primary_key( instance.pk = RandomPrimaryKeyModelMonkeyPatchMixin.get_unique_primary_key(
model=instance._meta.model model=instance._meta.model
) )
instance.id = instance.pk instance.id = instance.pk
result = instance.save_base(force_insert=True) result = instance.save_base(force_insert=True)
instance._meta.auto_created = False instance._meta.auto_created = False
post_save.send( post_save.send(
sender=instance.__class__, instance=instance, created=True, sender=instance.__class__, instance=instance, created=True,
update_fields=None, raw=False update_fields=None, raw=False
) )
return result return result
setattr(models.Model, 'save', method_save_new) setattr(models.Model, 'save', method_save_new)
super(RandomPrimaryKeyModelMonkeyPatchMixin, self).setUp() super(RandomPrimaryKeyModelMonkeyPatchMixin, self).setUp()
def tearDown(self): def tearDown(self):
models.Model.save = self.method_save_original if self.random_primary_key_enable:
models.Model.save = self.method_save_original
super(RandomPrimaryKeyModelMonkeyPatchMixin, self).tearDown() super(RandomPrimaryKeyModelMonkeyPatchMixin, self).tearDown()

View File

@@ -7,7 +7,8 @@ from mayan.apps.documents.tests import (
) )
from ..permissions import ( from ..permissions import (
permission_content_view, permission_document_type_parsing_setup permission_content_view, permission_document_type_parsing_setup,
permission_parse_document
) )
from ..utils import get_document_content from ..utils import get_document_content
@@ -106,3 +107,43 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase):
response = self._request_test_document_type_parsing_settings() response = self._request_test_document_type_parsing_settings()
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
class DocumentContentToolsViewsTestCase(GenericDocumentViewTestCase):
_skip_file_descriptor_test = True
# Ensure we use a PDF file
test_document_filename = TEST_HYBRID_DOCUMENT
def _request_document_parsing_tool_view(self):
return self.post(
viewname='document_parsing:document_type_submit', data={
'document_type': self.test_document_type.pk
}
)
def _get_document_content(self):
return ''.join(list(get_document_content(document=self.test_document)))
def test_document_parsing_tool_view_no_permission(self):
response = self._request_document_parsing_tool_view()
self.assertEqual(response.status_code, 200)
self.assertNotContains(
response=response, status_code=200,
text=self.test_document_type.label
)
self.assertNotEqual(
self._get_document_content(), TEST_DOCUMENT_CONTENT
)
def test_document_parsing_tool_view_with_permission(self):
self.grant_permission(permission=permission_parse_document)
response = self._request_document_parsing_tool_view()
self.assertEqual(response.status_code, 302)
self.assertEqual(
self._get_document_content(), TEST_DOCUMENT_CONTENT
)

View File

@@ -185,7 +185,7 @@ class DocumentTypeSubmitView(FormView):
'%(count)d documents added to the parsing queue.' '%(count)d documents added to the parsing queue.'
) % { ) % {
'count': count, 'count': count,
}, requrest=self.request }, request=self.request
) )
return HttpResponseRedirect(redirect_to=self.get_success_url()) return HttpResponseRedirect(redirect_to=self.get_success_url())

View File

@@ -18,6 +18,9 @@ event_document_new_version = namespace.add_event_type(
event_document_properties_edit = namespace.add_event_type( event_document_properties_edit = namespace.add_event_type(
label=_('Document properties edited'), name='document_edit' label=_('Document properties edited'), name='document_edit'
) )
event_document_trashed = namespace.add_event_type(
label=_('Document moved to trash'), name='document_trashed'
)
# The type of an existing document is changed to another type # The type of an existing document is changed to another type
event_document_type_change = namespace.add_event_type( event_document_type_change = namespace.add_event_type(
label=_('Document type changed'), name='document_type_change' label=_('Document type changed'), name='document_type_change'

View File

@@ -5,7 +5,7 @@ import uuid
from django.apps import apps from django.apps import apps
from django.core.files import File from django.core.files import File
from django.db import models from django.db import models, transaction
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now from django.utils.timezone import now
@@ -13,7 +13,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from ..events import ( from ..events import (
event_document_create, event_document_properties_edit, event_document_create, event_document_properties_edit,
event_document_type_change, event_document_trashed, event_document_type_change,
) )
from ..managers import DocumentManager, PassthroughManager, TrashCanManager from ..managers import DocumentManager, PassthroughManager, TrashCanManager
from ..settings import setting_language from ..settings import setting_language
@@ -104,11 +104,14 @@ class Document(models.Model):
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
to_trash = kwargs.pop('to_trash', True) to_trash = kwargs.pop('to_trash', True)
_user = kwargs.pop('_user', None)
if not self.in_trash and to_trash: if not self.in_trash and to_trash:
self.in_trash = True with transaction.atomic():
self.deleted_date_time = now() self.in_trash = True
self.save() self.deleted_date_time = now()
self.save()
event_document_trashed.commit(actor=_user, target=self)
else: else:
for version in self.versions.all(): for version in self.versions.all():
version.delete() version.delete()
@@ -177,21 +180,23 @@ class Document(models.Model):
user = kwargs.pop('_user', None) user = kwargs.pop('_user', None)
_commit_events = kwargs.pop('_commit_events', True) _commit_events = kwargs.pop('_commit_events', True)
new_document = not self.pk new_document = not self.pk
super(Document, self).save(*args, **kwargs)
if new_document: with transaction.atomic():
if user: super(Document, self).save(*args, **kwargs)
self.add_as_recent_document_for_user(user)
event_document_create.commit( if new_document:
actor=user, target=self, action_object=self.document_type if user:
) self.add_as_recent_document_for_user(user)
event_document_create.commit(
actor=user, target=self, action_object=self.document_type
)
else:
event_document_create.commit(
target=self, action_object=self.document_type
)
else: else:
event_document_create.commit( if _commit_events:
target=self, action_object=self.document_type event_document_properties_edit.commit(actor=user, target=self)
)
else:
if _commit_events:
event_document_properties_edit.commit(actor=user, target=self)
def save_to_file(self, *args, **kwargs): def save_to_file(self, *args, **kwargs):
return self.latest_version.save_to_file(*args, **kwargs) return self.latest_version.save_to_file(*args, **kwargs)
@@ -200,15 +205,16 @@ class Document(models.Model):
has_changed = self.document_type != document_type has_changed = self.document_type != document_type
self.document_type = document_type self.document_type = document_type
self.save() with transaction.atomic():
if has_changed or force: self.save()
post_document_type_change.send( if has_changed or force:
sender=self.__class__, instance=self post_document_type_change.send(
) sender=self.__class__, instance=self
)
event_document_type_change.commit(actor=_user, target=self) event_document_type_change.commit(actor=_user, target=self)
if _user: if _user:
self.add_as_recent_document_for_user(user=_user) self.add_as_recent_document_for_user(user=_user)
@property @property
def size(self): def size(self):

View File

@@ -228,9 +228,6 @@ class DocumentViewTestMixin(object):
data={'id_list': self.test_document.pk} data={'id_list': self.test_document.pk}
) )
def _request_empty_trash_view(self):
return self.post(viewname='documents:trash_can_empty')
def _request_document_print_view(self): def _request_document_print_view(self):
return self.get( return self.get(
viewname='documents:document_print', kwargs={ viewname='documents:document_print', kwargs={
@@ -239,3 +236,52 @@ class DocumentViewTestMixin(object):
'page_group': PAGE_RANGE_ALL 'page_group': PAGE_RANGE_ALL
} }
) )
class DocumentTrashViewMixin(object):
def _request_document_delete_get_view(self):
return self.get(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_delete_post_view(self):
return self.post(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_list_deleted_view(self):
return self.get(viewname='documents:document_list_deleted')
def _request_document_restore_get_view(self):
return self.get(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_restore_post_view(self):
return self.post(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_trash_get_view(self):
return self.get(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def _request_empty_trash_view(self):
return self.post(viewname='documents:trash_can_empty')
def _request_document_trash_post_view(self):
return self.post(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)

View File

@@ -3,12 +3,16 @@ from __future__ import unicode_literals
from actstream.models import Action from actstream.models import Action
from django_downloadview import assert_download_response from django_downloadview import assert_download_response
from ..events import event_document_download, event_document_view from ..events import (
event_document_download, event_document_trashed, event_document_view
)
from ..permissions import ( from ..permissions import (
permission_document_download, permission_document_view permission_document_download, permission_document_trash,
permission_document_view
) )
from .base import GenericDocumentViewTestCase from .base import GenericDocumentViewTestCase
from .mixins import DocumentTrashViewMixin
TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label' TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label'
@@ -84,3 +88,20 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase):
self.assertEqual(event.actor, self._test_case_user) self.assertEqual(event.actor, self._test_case_user)
self.assertEqual(event.target, self.test_document) self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_view.id) self.assertEqual(event.verb, event_document_view.id)
class DocumentTrashEventsTestCase(DocumentTrashViewMixin, GenericDocumentViewTestCase):
def test_document_trash_event_with_permissions(self):
Action.objects.all().delete()
self.grant_access(
obj=self.test_document, permission=permission_document_trash
)
response = self._request_document_trash_post_view()
self.assertEqual(response.status_code, 302)
event = Action.objects.any(obj=self.test_document).first()
#self.assertEqual(event.actor, self._test_case_user)
self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_trashed.id)

View File

@@ -7,16 +7,10 @@ from ..permissions import (
) )
from .base import GenericDocumentViewTestCase from .base import GenericDocumentViewTestCase
from .mixins import DocumentTrashViewMixin
class TrashedDocumentTestCase(GenericDocumentViewTestCase): class TrashedDocumentTestCase(DocumentTrashViewMixin, GenericDocumentViewTestCase):
def _request_document_restore_get_view(self):
return self.get(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def test_document_restore_get_view_no_permission(self): def test_document_restore_get_view_no_permission(self):
self.test_document.delete() self.test_document.delete()
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
@@ -43,13 +37,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(Document.objects.count(), document_count) self.assertEqual(Document.objects.count(), document_count)
def _request_document_restore_post_view(self):
return self.post(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def test_document_restore_post_view_no_permission(self): def test_document_restore_post_view_no_permission(self):
self.test_document.delete() self.test_document.delete()
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
@@ -74,13 +61,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 1) self.assertEqual(Document.objects.count(), 1)
def _request_document_trash_get_view(self):
return self.get(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def test_document_trash_get_view_no_permissions(self): def test_document_trash_get_view_no_permissions(self):
document_count = Document.objects.count() document_count = Document.objects.count()
@@ -101,13 +81,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(Document.objects.count(), document_count) self.assertEqual(Document.objects.count(), document_count)
def _request_document_trash_post_view(self):
return self.post(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def test_document_trash_post_view_no_permissions(self): def test_document_trash_post_view_no_permissions(self):
response = self._request_document_trash_post_view() response = self._request_document_trash_post_view()
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
@@ -126,13 +99,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
def _request_document_delete_get_view(self):
return self.get(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def test_document_delete_get_view_no_permissions(self): def test_document_delete_get_view_no_permissions(self):
self.test_document.delete() self.test_document.delete()
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
@@ -165,13 +131,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
DeletedDocument.objects.count(), trashed_document_count DeletedDocument.objects.count(), trashed_document_count
) )
def _request_document_delete_post_view(self):
return self.post(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def test_document_delete_post_view_no_permissions(self): def test_document_delete_post_view_no_permissions(self):
self.test_document.delete() self.test_document.delete()
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
@@ -198,9 +157,6 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
def _request_document_list_deleted_view(self):
return self.get(viewname='documents:document_list_deleted')
def test_deleted_document_list_view_no_permissions(self): def test_deleted_document_list_view_no_permissions(self):
self.test_document.delete() self.test_document.delete()

View File

@@ -51,6 +51,7 @@ class MailerApp(MayanAppConfig):
LogEntry = self.get_model(model_name='LogEntry') LogEntry = self.get_model(model_name='LogEntry')
UserMailer = self.get_model(model_name='UserMailer') UserMailer = self.get_model(model_name='UserMailer')
UserMailerLogEntry = self.get_model(model_name='UserMailerLogEntry')
MailerBackend.initialize() MailerBackend.initialize()
@@ -79,6 +80,13 @@ class MailerApp(MayanAppConfig):
SourceColumn( SourceColumn(
source=UserMailer, attribute='backend_label' source=UserMailer, attribute='backend_label'
) )
SourceColumn(
attribute='datetime', label=_('Date and time'),
source=UserMailerLogEntry
)
SourceColumn(
attribute='message', label=_('Message'), source=UserMailerLogEntry
)
ModelPermission.register( ModelPermission.register(
model=Document, permissions=( model=Document, permissions=(

View File

@@ -96,7 +96,7 @@ class UserMailerDynamicForm(DynamicModelForm):
if self.instance.backend_data: if self.instance.backend_data:
backend_data = json.loads(self.instance.backend_data) backend_data = json.loads(self.instance.backend_data)
for key in self.instance.get_backend().fields: for key in self.instance.get_backend().fields:
self.fields[key].initial = backend_data[key] self.fields[key].initial = backend_data.get(key)
return result return result

View File

@@ -12,11 +12,11 @@ class DjangoSMTP(MailerBackend):
Backend that wraps Django's SMTP backend Backend that wraps Django's SMTP backend
""" """
class_fields = ( class_fields = (
'host', 'port', 'use_tls', 'use_ssl', 'user', 'password' 'host', 'port', 'use_tls', 'use_ssl', 'username', 'password'
) )
class_path = 'django.core.mail.backends.smtp.EmailBackend' class_path = 'django.core.mail.backends.smtp.EmailBackend'
field_order = ( field_order = (
'host', 'port', 'use_tls', 'use_ssl', 'user', 'password', 'from' 'host', 'port', 'use_tls', 'use_ssl', 'username', 'password', 'from'
) )
fields = { fields = {
'from': { 'from': {
@@ -60,14 +60,14 @@ class DjangoSMTP(MailerBackend):
'that "Use TLS" and "Use SSL" are mutually exclusive, ' 'that "Use TLS" and "Use SSL" are mutually exclusive, '
'so only set one of those settings to True.' 'so only set one of those settings to True.'
), 'required': False ), 'required': False
}, 'user': { }, 'username': {
'label': _('Username'), 'label': _('Username'),
'class': 'django.forms.CharField', 'default': '', 'class': 'django.forms.CharField', 'default': '',
'help_text': _( 'help_text': _(
'Username to use for the SMTP server. If empty, ' 'Username to use for the SMTP server. If empty, '
'authentication won\'t attempted.' 'authentication won\'t attempted.'
), 'kwargs': { ), 'kwargs': {
'max_length': 48 'max_length': 254
}, 'required': False }, 'required': False
}, 'password': { }, 'password': {
'label': _('Password'), 'label': _('Password'),

View File

@@ -208,7 +208,7 @@ class UserMailerLogEntryListView(SingleObjectListView):
return { return {
'hide_object': True, 'hide_object': True,
'object': self.get_user_mailer(), 'object': self.get_user_mailer(),
'title': _('%s error log') % self.get_user_mailer(), 'title': _('Error log for: %s') % self.get_user_mailer(),
} }
def get_source_queryset(self): def get_source_queryset(self):

View File

@@ -108,7 +108,7 @@ class PlatformTemplate(object):
class PlatformTemplateSupervisord(PlatformTemplate): class PlatformTemplateSupervisord(PlatformTemplate):
context_defaults = { context_defaults = {
'BROKER_URL': 'redis://127.0.0.1:6379/0', 'BROKER_URL': 'redis://127.0.0.1:6379/0',
'CELERY_RESULT_BACKEND': 'redis://127.0.0.1:6379/1', 'CELERY_RESULT_BACKEND': 'redis://127.0.0.1:6379/0',
} }
label = _('Template for Supervisord.') label = _('Template for Supervisord.')
name = 'supervisord' name = 'supervisord'
@@ -120,6 +120,10 @@ class PlatformTemplateSupervisord(PlatformTemplate):
name='GUNICORN_WORKERS', default=2, name='GUNICORN_WORKERS', default=2,
environment_name='MAYAN_GUNICORN_WORKERS' environment_name='MAYAN_GUNICORN_WORKERS'
), ),
Variable(
name='GUNICORN_TIMEOUT', default=120,
environment_name='MAYAN_GUNICORN_TIMEOUT'
),
Variable( Variable(
name='DATABASE_CONN_MAX_AGE', default=0, name='DATABASE_CONN_MAX_AGE', default=0,
environment_name='MAYAN_DATABASE_CONN_MAX_AGE' environment_name='MAYAN_DATABASE_CONN_MAX_AGE'

View File

@@ -16,7 +16,8 @@ class Command(management.BaseCommand):
) )
parser.add_argument( parser.add_argument(
'--context', action='store', default='', dest='context', '--context', action='store', default='', dest='context',
help='Show a list of available templates.', help='Pass a context to the template in the form of a JSON encoded '
'dictionary.',
) )
def handle(self, *args, **options): def handle(self, *args, **options):

View File

@@ -16,7 +16,7 @@ environment=
[program:mayan-gunicorn] [program:mayan-gunicorn]
autorestart = true autorestart = true
autostart = true autostart = true
command = {{ INSTALLATION_PATH }}/bin/gunicorn -w {{ GUNICORN_WORKERS }} mayan.wsgi --max-requests 500 --max-requests-jitter 50 --worker-class gevent --bind 0.0.0.0:8000 --timeout 120 command = {{ INSTALLATION_PATH }}/bin/gunicorn -w {{ GUNICORN_WORKERS }} mayan.wsgi --max-requests 500 --max-requests-jitter 50 --worker-class gevent --bind 0.0.0.0:8000 --timeout {{ GUNICORN_TIMEOUT }}
user = mayan user = mayan
{% for worker in workers %} {% for worker in workers %}

View File

@@ -1,7 +1,7 @@
[program:mayan-gunicorn] [program:mayan-gunicorn]
autorestart = false autorestart = false
autostart = true autostart = true
command = /bin/bash -c "${MAYAN_GUNICORN_BIN} -w ${MAYAN_GUNICORN_WORKERS} mayan.wsgi --max-requests 500 --max-requests-jitter 50 --worker-class gevent --bind 0.0.0.0:8000 --env DJANGO_SETTINGS_MODULE=${MAYAN_SETTINGS_MODULE}" --timeout 120 command = /bin/bash -c "${MAYAN_GUNICORN_BIN} -w ${MAYAN_GUNICORN_WORKERS} mayan.wsgi --max-requests 500 --max-requests-jitter 50 --worker-class gevent --bind 0.0.0.0:8000 --env DJANGO_SETTINGS_MODULE=${MAYAN_SETTINGS_MODULE}" --timeout ${MAYAN_GUNICORN_TIMEOUT}
redirect_stderr = true redirect_stderr = true
stderr_logfile = /dev/fd/2 stderr_logfile = /dev/fd/2
stderr_logfile_maxbytes = 0 stderr_logfile_maxbytes = 0

View File

@@ -234,7 +234,7 @@ class IntervalBaseModel(OutOfProcessSource):
PeriodicTask.objects.create( PeriodicTask.objects.create(
name=self._get_periodic_task_name(), name=self._get_periodic_task_name(),
interval=interval_instance, interval=interval_instance,
task='sources.tasks.task_check_interval_source', task='mayan.apps.sources.tasks.task_check_interval_source',
kwargs=json.dumps({'source_id': self.pk}) kwargs=json.dumps({'source_id': self.pk})
) )

View File

@@ -277,15 +277,15 @@ class POP3Email(EmailBaseModel):
logger.debug('ssl: %s', self.ssl) logger.debug('ssl: %s', self.ssl)
if self.ssl: if self.ssl:
mailbox = poplib.POP3_SSL(host=self.host, post=self.port) mailbox = poplib.POP3_SSL(host=self.host, port=self.port)
else: else:
mailbox = poplib.POP3( mailbox = poplib.POP3(
host=self.host, post=self.port, timeout=self.timeout host=self.host, port=self.port, timeout=self.timeout
) )
mailbox.getwelcome() mailbox.getwelcome()
mailbox.user(username=self.username) mailbox.user(self.username)
mailbox.pass_(password=self.password) mailbox.pass_(self.password)
messages_info = mailbox.list() messages_info = mailbox.list()
logger.debug(msg='messages_info:') logger.debug(msg='messages_info:')

View File

@@ -203,7 +203,10 @@ class POP3SourceTestCase(GenericDocumentTestCase):
def list(self, which=None): def list(self, which=None):
return (None, ['1 test']) return (None, ['1 test'])
def pass_(self, password): def user(self, user):
return
def pass_(self, pswd):
return return
def quit(self): def quit(self):
@@ -214,10 +217,7 @@ class POP3SourceTestCase(GenericDocumentTestCase):
1, [TEST_EMAIL_BASE64_FILENAME] 1, [TEST_EMAIL_BASE64_FILENAME]
) )
def user(self, username): @mock.patch('poplib.POP3_SSL', autospec=True)
return
@mock.patch('poplib.POP3_SSL')
def test_download_document(self, mock_poplib): def test_download_document(self, mock_poplib):
mock_poplib.return_value = POP3SourceTestCase.MockMailbox() mock_poplib.return_value = POP3SourceTestCase.MockMailbox()
self.source = POP3Email.objects.create( self.source = POP3Email.objects.create(