From 97bd91f83e04643a6d9402d17e590e7626906ab4 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 00:35:14 -0400 Subject: [PATCH 01/17] Fix version number in changelog Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 50dc9d3c9e..141bda16a4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,4 +1,4 @@ -3.3.11 (2019-XX-XX) +3.2.11 (2019-XX-XX) =================== - Backport transaction handling to document model events. From a031e9d582179dfd90e1a5350c49d9cd0cbb9071 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 00:29:09 -0400 Subject: [PATCH 02/17] Update example LDAP authentication settings file Signed-off-by: Roberto Rosario --- contrib/settings/ldap_connection_settings.py | 153 ++++++++++++++----- 1 file changed, 114 insertions(+), 39 deletions(-) diff --git a/contrib/settings/ldap_connection_settings.py b/contrib/settings/ldap_connection_settings.py index 6ae809a3be..a708ae9fb9 100644 --- a/contrib/settings/ldap_connection_settings.py +++ b/contrib/settings/ldap_connection_settings.py @@ -1,64 +1,139 @@ from __future__ import absolute_import +# Install Python LDAP with: +# $ pip install python-ldap +# or if using Docker, pass the environment variable MAYAN_PIP_INSTALLS: +# -e MAYAN_PIP_INSTALLS=python-ldap import ldap -from django_auth_ldap.config import LDAPSearch -from .base import * # NOQA -from django.contrib.auth import get_user_model +from django_auth_ldap.config import ( + LDAPSearch, LDAPSearchUnion, NestedActiveDirectoryGroupType +) -SECRET_KEY = '' +from mayan.settings.production import * -# makes sure this works in Active Directory -ldap.set_option(ldap.OPT_REFERRALS, 0) +# Makes sure this works in Active Directory +ldap.set_option(ldap.OPT_REFERRALS, False) -# This is the default, but I like to be explicit. +# Turn of debug output, turn this off when everything is working as expected +ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1) + +# Default: True AUTH_LDAP_ALWAYS_UPDATE_USER = True -LDAP_USER_AUTO_CREATION = "False" -LDAP_URL = "ldap://:389/" -LDAP_BASE_DN = "dc=paramatrix,dc=co,dc=in" -LDAP_ADDITIONAL_USER_DN = "dc=people" -LDAP_ADMIN_DN = "" -LDAP_PASSWORD = "" +# Use TLS to talk to the LDAP server +# Requires acquiring the server's certificate +# $ openssl s_client -connect :636 +# Part of the output of this file will be the Base-64 encoded .cer file +# that was presented for LDAPS. Cut and paste into a file beginning at +# "-Begin Certificate" through "-End Certificate--" and save as a .crt, for +# example: ldapserver.crt +# $ CERT=ldapserver.crt +# $ cp /root/$CERT /usr/share/ca-certificates/$CERT +# # notice the + sign which tells to activate the certificate. +# $ echo "+$CERT" >/etc/ca-certificates/update.d/activate_my_cert +# $ dpkg-reconfigure ca-certificates; +AUTH_LDAP_START_TLS = False + +LDAP_ADDITIONAL_USER_DN = 'dc=people' +LDAP_ADMIN_DN = '' +LDAP_BASE_DN = 'dc=,dc=co,dc=in' +LDAP_PASSWORD = '' +LDAP_USER_AUTO_CREATION = 'False' +LDAP_URL = 'ldap://:389/' -AUTH_LDAP_SERVER_URI = LDAP_URL AUTH_LDAP_BIND_DN = LDAP_ADMIN_DN AUTH_LDAP_BIND_PASSWORD = LDAP_PASSWORD +AUTH_LDAP_SERVER_URI = LDAP_URL - +# Simple search AUTH_LDAP_USER_SEARCH = LDAPSearch( '%s,%s' % (LDAP_ADDITIONAL_USER_DN, LDAP_BASE_DN), ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) + +# If you need to search in more than one place for a user, you can use +# LDAPSearchUnion. This takes multiple LDAPSearch objects and returns the +# union of the results. The precedence of the underlying searches is +# unspecified. +# https://django-auth-ldap.readthedocs.io/en/latest/authentication.html +# AUTH_LDAP_USER_SEARCH = LDAPSearchUnion( +# LDAPSearch( +# 'ou=Users,ou=Admin,dc=,dc=local', ldap.SCOPE_SUBTREE, +# '(samaccountname=%(user)s)' +# ), +# LDAPSearch( +# 'ou=Users,ou=,dc=,dc=local', +# ldap.SCOPE_SUBTREE, '(samaccountname=%(user)s)' +# ), +# LDAPSearch( +# 'ou=Users,ou=,dc=,dc=local', +# ldap.SCOPE_SUBTREE, '(samaccountname=%(user)s)' +# ), +# ) + +# User attributes to map from LDAP to Mayan's user model. AUTH_LDAP_USER_ATTR_MAP = { 'first_name': 'cn', 'last_name': 'sn', 'email': 'mail' } +# Another example map +# AUTH_LDAP_USER_ATTR_MAP = { +# 'username': 'sAMAccountName', +# 'first_name': 'givenName', +# 'last_name': 'sn', +# 'email': 'mail' +# } +# Only string fields can be mapped to attributes. Boolean fields can be +# defined by group membership: +# AUTH_LDAP_USER_FLAGS_BY_GROUP = { +# 'is_active': 'cn=active,ou=groups,dc=example,dc=com', +# 'is_staff': ( +# LDAPGroupQuery('cn=staff,ou=groups,dc=example,dc=com') +# | LDAPGroupQuery('cn=admin,ou=groups,dc=example,dc=com') +# ), +# 'is_superuser': 'cn=superuser,ou=groups,dc=example,dc=com', +# } + +# Simple group search +# AUTH_LDAP_GROUP_SEARCH = LDAPSearch( +# 'ou=groups,dc=example,dc=com', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)' +# ) +# AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() + +# Advanced group search +# AUTH_LDAP_GROUP_SEARCH = LDAPSearchUnion( +# LDAPSearch( +# 'ou=Domain Global,OU=Security,OU=Groups,OU=,dc=,dc=local', +# ldap.SCOPE_SUBTREE, +# '(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=2147483648))' +# ), +# LDAPSearch( +# 'ou=Domain Global,OU=Security,OU=Groups,OU=,dc=,dc=local', +# ldap.SCOPE_SUBTREE, +# '(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=2147483648))' +# ), +# ) +# AUTH_LDAP_CACHE_GROUPS = True +# AUTH_LDAP_FIND_GROUP_PERMS = False +# AUTH_LDAP_GROUP_TYPE = NestedActiveDirectoryGroupType() +# AUTH_LDAP_MIRROR_GROUPS = True + +# To minimize traffic to the LDAP server, LDAPBackend can make use of +# Django’s cache framework to keep a copy of a user’s LDAP group memberships. +# To enable this feature, set AUTH_LDAP_CACHE_TIMEOUT, which determines +# the timeout of cache entries in seconds. +# AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 + +# Limiting Access +# The simplest use of groups is to limit the users who are allowed to log in. +# If AUTH_LDAP_REQUIRE_GROUP is set, then only users who are members of that +# group will successfully authenticate. AUTH_LDAP_DENY_GROUP is the reverse: +# if given, members of this group will be rejected. +# AUTH_LDAP_REQUIRE_GROUP = 'cn=enabled,ou=groups,dc=example,dc=com' +# AUTH_LDAP_DENY_GROUP = 'cn=disabled,ou=groups,dc=example,dc=com' + AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', - 'mayan.settings.settings_local.EmailOrUsernameModelBackend', ) - - -class EmailOrUsernameModelBackend(object): - """ - This is a ModelBacked that allows authentication with either a username or an email address. - """ - def authenticate(self, username=None, password=None): - if '@' in username: - kwargs = {'email': username} - else: - kwargs = {'username': username} - try: - user = get_user_model().objects.get(**kwargs) - if user.check_password(password): - return user - except get_user_model().DoesNotExist: - return None - - def get_user(self, username): - try: - return get_user_model().objects.get(pk=username) - except get_user_model().DoesNotExist: - return None From 3705457526c9f894e7aefa7d61c4f0985f999ef7 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 00:31:57 -0400 Subject: [PATCH 03/17] Update FAQ entry about the LDAP file Signed-off-by: Roberto Rosario --- docs/topics/faq.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/topics/faq.rst b/docs/topics/faq.rst index a06ed9cda3..a12c5b08c0 100644 --- a/docs/topics/faq.rst +++ b/docs/topics/faq.rst @@ -175,7 +175,8 @@ How to do LDAP authentication A sample settings file called ldap_connection_settings.py is included in the contrib/settings/ folder of the repository showing how to setup LDAP -authentication. +authentication. This file can be found on the web at: https://gitlab.com/mayan-edms/mayan-edms/blob/master/contrib/settings/ldap_connection_settings.py. This is a community contributed file. Use the Python +settings file method to use this file. Operating systems From 9981be60da5448848cfe2a377c0d41fd4d96f8e4 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 14:26:05 -0400 Subject: [PATCH 04/17] Update changelog Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 141bda16a4..9538dd108b 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,8 @@ 3.2.11 (2019-XX-XX) =================== - Backport transaction handling to document model events. +- Update example LDAP authentication settings file. +- Update FAQ entry about the LDAP file. 3.2.10 (2019-11-19) =================== From 9205c40e9b8da3d1c1a5fbfaf19726bd3fe87053 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 14:08:00 -0400 Subject: [PATCH 05/17] Automate documentation building dependencies Signed-off-by: Roberto Rosario --- HISTORY.rst | 1 + Makefile | 1 + mayan/apps/common/dependencies.py | 43 ++++++++++++++++++++++++++++-- mayan/apps/dependencies/classes.py | 6 +++++ requirements/documentation.txt | 4 --- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9538dd108b..3b3cdd82fa 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,7 @@ - Backport transaction handling to document model events. - Update example LDAP authentication settings file. - Update FAQ entry about the LDAP file. +- Automate documentation building dependencies. 3.2.10 (2019-11-19) =================== diff --git a/Makefile b/Makefile index 194df3a46f..110d56b5a8 100644 --- a/Makefile +++ b/Makefile @@ -240,6 +240,7 @@ generate-setup: generate-requirements generate-requirements: ## Generate all requirements files from the project depedency declarations. @./manage.py generaterequirements build > requirements/build.txt @./manage.py generaterequirements development > requirements/development.txt + @./manage.py generaterequirements documentation > requirements/documentation.txt @./manage.py generaterequirements testing > requirements/testing-base.txt @./manage.py generaterequirements production --exclude=django > requirements/base.txt @./manage.py generaterequirements production --only=django > requirements/common.txt diff --git a/mayan/apps/common/dependencies.py b/mayan/apps/common/dependencies.py index 8a6e811c62..40bdfeb05d 100644 --- a/mayan/apps/common/dependencies.py +++ b/mayan/apps/common/dependencies.py @@ -3,8 +3,8 @@ from __future__ import absolute_import, unicode_literals from django.utils.translation import ugettext_lazy as _ from mayan.apps.dependencies.classes import ( - environment_build, environment_development, environment_testing, - PythonDependency + environment_build, environment_development, environment_documentation, + environment_testing, PythonDependency ) PythonDependency( @@ -289,6 +289,8 @@ PythonDependency( module=__name__, name='whitenoise', version_string='==4.1.4' ) +# Development + PythonDependency( module=__name__, environment=environment_development, name='Werkzeug', version_string='==0.15.4' @@ -330,6 +332,8 @@ PythonDependency( module=__name__, name='transifex-client', version_string='==0.13.6' ) +# Testing + PythonDependency( environment=environment_testing, module=__name__, name='codecov', version_string='==2.0.15' @@ -356,6 +360,8 @@ PythonDependency( version_string='==5.6.3' ) +# Build + PythonDependency( environment=environment_build, module=__name__, name='twine', version_string='==1.9.1' @@ -364,3 +370,36 @@ PythonDependency( environment=environment_build, module=__name__, name='wheel', version_string='==0.30.0' ) + +# Documentation + +PythonDependency( + environment=environment_documentation, module=__name__, name='Sphinx', + version_string='==1.8.5' +) +PythonDependency( + environment=environment_documentation, module=__name__, + name='sphinx-autobuild', + version_string='==0.7.1' +) +PythonDependency( + environment=environment_documentation, module=__name__, + name='sphinx_rtd_theme', + version_string='==0.4.3' +) +PythonDependency( + environment=environment_documentation, module=__name__, + name='sphinxcontrib-blockdiag', + version_string='==1.5.5' +) +PythonDependency( + environment=environment_documentation, module=__name__, + name='sphinxcontrib-spelling', + version_string='==4.2.1' +) +# sphinx-autobuild has a dependency on Tornado, +# but Tornado 6.0 dropped support for Python 2.7 +PythonDependency( + environment=environment_documentation, module=__name__, name='tornado', + version_string='<6.0' +) diff --git a/mayan/apps/dependencies/classes.py b/mayan/apps/dependencies/classes.py index bb2173546a..cc62c77935 100644 --- a/mayan/apps/dependencies/classes.py +++ b/mayan/apps/dependencies/classes.py @@ -77,6 +77,12 @@ environment_development = DependencyEnvironment( 'can ignore missing dependencies under this environment.' ), label=_('Development'), name='development' ) +environment_documentation = DependencyEnvironment( + help_text=_( + 'Environment used for building the documentation. End users ' + 'can ignore missing dependencies under this environment.' + ), label=_('Documentation'), name='documentation' +) environment_production = DependencyEnvironment( help_text=_( 'Normal environment for end users. A missing dependency under this ' diff --git a/requirements/documentation.txt b/requirements/documentation.txt index 2718faf654..0d2604016a 100644 --- a/requirements/documentation.txt +++ b/requirements/documentation.txt @@ -1,10 +1,6 @@ Sphinx==1.8.5 - sphinx-autobuild==0.7.1 sphinx_rtd_theme==0.4.3 sphinxcontrib-blockdiag==1.5.5 sphinxcontrib-spelling==4.2.1 - -# sphinx-autobuild has a dependency on Tornado, -# but Tornado 6.0 dropped support for Python 2.7 tornado<6.0 From 45c8fefdc49e7d71be2070bbd88a2a5f88231fea Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 14:22:07 -0400 Subject: [PATCH 06/17] Add sphinx sitemap extension Signed-off-by: Roberto Rosario --- HISTORY.rst | 1 + docs/conf.py | 8 ++++++-- mayan/apps/common/dependencies.py | 5 +++++ requirements/documentation.txt | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 3b3cdd82fa..58a592f38a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,7 @@ - Update example LDAP authentication settings file. - Update FAQ entry about the LDAP file. - Automate documentation building dependencies. +- Add sphinx sitemap extension. 3.2.10 (2019-11-19) =================== diff --git a/docs/conf.py b/docs/conf.py index 8b7eda9633..fdbce57f67 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,8 +44,8 @@ sys.path.append( # extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] # extensions = ["djangodocs", "sphinx.ext.intersphinx"] extensions = [ - 'sphinx.ext.extlinks', 'sphinxcontrib.blockdiag', - 'sphinxcontrib.spelling' + 'sphinx_sitemap', 'sphinx.ext.extlinks', 'sphinxcontrib.blockdiag', + 'sphinxcontrib.spelling', ] blockdiag_antialias = True @@ -264,6 +264,10 @@ extlinks = { ) } +# -- Options for sitemap extension --------------------------------------------- + +html_baseurl = 'https://docs.mayan-edms.com/' + def setup(app): BASE_DIRECTORY = '/opt/' diff --git a/mayan/apps/common/dependencies.py b/mayan/apps/common/dependencies.py index 40bdfeb05d..d261e62d90 100644 --- a/mayan/apps/common/dependencies.py +++ b/mayan/apps/common/dependencies.py @@ -382,6 +382,11 @@ PythonDependency( name='sphinx-autobuild', version_string='==0.7.1' ) +PythonDependency( + environment=environment_documentation, module=__name__, + name='sphinx-sitemap', + version_string='==1.0.2' +) PythonDependency( environment=environment_documentation, module=__name__, name='sphinx_rtd_theme', diff --git a/requirements/documentation.txt b/requirements/documentation.txt index 0d2604016a..ebe46c305c 100644 --- a/requirements/documentation.txt +++ b/requirements/documentation.txt @@ -1,5 +1,6 @@ Sphinx==1.8.5 sphinx-autobuild==0.7.1 +sphinx-sitemap==1.0.2 sphinx_rtd_theme==0.4.3 sphinxcontrib-blockdiag==1.5.5 sphinxcontrib-spelling==4.2.1 From a36c2a6590d6411fca80396290fa71a647f66224 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 20 Nov 2019 17:33:42 -0400 Subject: [PATCH 07/17] Move the file patching code Moved from the Dependency class to a generalized utility of the storages app. Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 ++ mayan/apps/dependencies/classes.py | 30 ++----------------------- mayan/apps/storage/utils.py | 36 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 58a592f38a..a2cc419254 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,6 +5,8 @@ - Update FAQ entry about the LDAP file. - Automate documentation building dependencies. - Add sphinx sitemap extension. +- Move the file patching code from the Dependency class to a + generalized utility of the storages app. 3.2.10 (2019-11-19) =================== diff --git a/mayan/apps/dependencies/classes.py b/mayan/apps/dependencies/classes.py index cc62c77935..e91859ca64 100644 --- a/mayan/apps/dependencies/classes.py +++ b/mayan/apps/dependencies/classes.py @@ -1,6 +1,5 @@ from __future__ import print_function, unicode_literals -import fileinput import json from importlib import import_module import logging @@ -25,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext from mayan.apps.common.compat import FileNotFoundErrorException from mayan.apps.common.utils import resolve_attribute -from mayan.apps.storage.utils import mkdtemp +from mayan.apps.storage.utils import mkdtemp, patch_files as storage_patch_files from .algorithms import HashAlgorithm from .exceptions import DependenciesException @@ -410,20 +409,6 @@ class Dependency(object): return self.version_string or _('Not specified') def patch_files(self, path=None, replace_list=None): - """ - Search and replace content from a list of file based on a pattern - replace_list[ - { - 'filename_pattern': '*.css', - 'content_patterns': [ - { - 'search': '', - 'replace': '', - } - ] - } - ] - """ print(_('Patching files... '), end='') try: @@ -437,18 +422,7 @@ class Dependency(object): if not replace_list: replace_list = self.replace_list - path_object = Path(path) - for replace_entry in replace_list or []: - for path_entry in path_object.glob('**/{}'.format(replace_entry['filename_pattern'])): - if path_entry.is_file(): - # PY3 - # Don't use context processor to allow working on Python 2.7 - # Update on Mayan EDMS version >= 4.0 - file_object = fileinput.FileInput(force_text(path_entry), inplace=True) - for line in file_object: - for pattern in replace_entry['content_patterns']: - print(line.replace(pattern['search'], pattern['replace']), end='') - file_object.close() + storage_patch_files(path=path, replace_list=replace_list) def verify(self): """ diff --git a/mayan/apps/storage/utils.py b/mayan/apps/storage/utils.py index 90eab5bae5..e288734c01 100644 --- a/mayan/apps/storage/utils.py +++ b/mayan/apps/storage/utils.py @@ -1,10 +1,16 @@ from __future__ import unicode_literals +import fileinput import logging import os import shutil import tempfile +from pathlib2 import Path + +from django.utils.encoding import force_text +from django.utils.module_loading import import_string + from .settings import setting_temporary_directory logger = logging.getLogger(__name__) @@ -49,6 +55,36 @@ def mkstemp(*args, **kwargs): return tempfile.mkstemp(*args, **kwargs) +def patch_files(path=None, replace_list=None): + """ + Search and replace content from a list of file based on a pattern + replace_list[ + { + 'filename_pattern': '*.css', + 'content_patterns': [ + { + 'search': '', + 'replace': '', + } + ] + } + ] + """ + path_object = Path(path) + for replace_entry in replace_list or []: + for path_entry in path_object.glob('**/{}'.format(replace_entry['filename_pattern'])): + if path_entry.is_file(): + # PY3 + # Don't use context processor to allow working on Python 2.7 + # Update on Mayan EDMS version >= 4.0 + file_object = fileinput.FileInput(force_text(path_entry), inplace=True) + for line in file_object: + for pattern in replace_entry['content_patterns']: + line = line.replace(pattern['search'], pattern['replace']) + print(line, end='') + file_object.close() + + def validate_path(path): if not os.path.exists(path): # If doesn't exist try to create it From 86399a5ee2090618c14c909151b861e847028d2d Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 00:38:03 -0400 Subject: [PATCH 08/17] Add book link to the documentation Signed-off-by: Roberto Rosario --- .gitignore | 1 + HISTORY.rst | 1 + docs/_static/book_cover.jpg | Bin 0 -> 74875 bytes docs/_templates/donate.html | 16 ------------ docs/_templates/message_area.html | 41 ++++++++++++++++++++++++++++++ docs/conf.py | 3 ++- docs/utils.py | 33 ++++++++++++++++++++++++ 7 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 docs/_static/book_cover.jpg delete mode 100644 docs/_templates/donate.html create mode 100644 docs/_templates/message_area.html diff --git a/.gitignore b/.gitignore index 2f445a7b83..9104d6592e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ static_collected/ google_fonts/ node_modules/ docs/build/ +docs/_templates/layout.html diff --git a/HISTORY.rst b/HISTORY.rst index a2cc419254..144f1a5c31 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,6 +7,7 @@ - Add sphinx sitemap extension. - Move the file patching code from the Dependency class to a generalized utility of the storages app. +- Add book link to the documentation. 3.2.10 (2019-11-19) =================== diff --git a/docs/_static/book_cover.jpg b/docs/_static/book_cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3eb727842af2adb40a2254d589462bb8b0ffcdf4 GIT binary patch literal 74875 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 Q{*O|-|1Uqp{M+<@0VE3D{r~^~ literal 0 HcmV?d00001 diff --git a/docs/_templates/donate.html b/docs/_templates/donate.html deleted file mode 100644 index 42850e33d2..0000000000 --- a/docs/_templates/donate.html +++ /dev/null @@ -1,16 +0,0 @@ -

Support

-

- Consulting and support plans are available, click here. -

-
-

- Or consider supporting Mayan EDMS by contributing to its development. (US tax payers, please note this contribution is not tax deductible). -

-
- - - - -
- -
diff --git a/docs/_templates/message_area.html b/docs/_templates/message_area.html new file mode 100644 index 0000000000..71dc332b9f --- /dev/null +++ b/docs/_templates/message_area.html @@ -0,0 +1,41 @@ + + +
+

Get the book!

+ + + + + +
+ +

+ On-site consulting and support plans are available, click here. +

+ +
+ +

+ Or consider donating to support the continued development of the project. +

+ +
+ + + + +
+
+ + diff --git a/docs/conf.py b/docs/conf.py index fdbce57f67..e3300b7885 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -123,7 +123,7 @@ html_theme = 'sphinx_rtd_theme' # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - 'analytics_id': 'UA-52965619-6', + 'analytics_id': 'UA-52965619-6' } # Add any paths that contain custom themes here, relative to this directory. @@ -305,3 +305,4 @@ def setup(app): substitutions=substitutions ) ) + utils.patch_theme_template(app, templates_path=templates_path[0]) diff --git a/docs/utils.py b/docs/utils.py index bed070d934..f9fc3bba9a 100644 --- a/docs/utils.py +++ b/docs/utils.py @@ -1,5 +1,11 @@ from __future__ import unicode_literals +import shutil +from pathlib2 import Path +import sphinx_rtd_theme + +from mayan.apps.storage.utils import patch_files + def load_env_file(filename='../config.env'): result = {} @@ -19,3 +25,30 @@ def generate_substitutions(dictionary): result.append(('|{}|'.format(key), value)) return result + + +def patch_theme_template(app, templates_path): + package_path = Path(sphinx_rtd_theme.__file__) + source_file_path = package_path.parent / 'layout.html' + destination_path = Path(app.srcdir) / templates_path + destination_file_path = destination_path / 'layout.html' + + with source_file_path.open(mode='r') as source_file_object: + with destination_file_path.open(mode='w+') as destination_file_object: + shutil.copyfileobj( + fsrc=source_file_object, fdst=destination_file_object + ) + + patch_files( + path=destination_path, replace_list=[ + { + 'filename_pattern': 'layout.html', + 'content_patterns': [ + { + 'search': '\n \n\n
', + 'replace': '{% include "message_area.html" %}\n \n\n
', + }, + ] + } + ] + ) From 3f41d5274beb0512ee963f3107dc3646c1d71576 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 00:40:05 -0400 Subject: [PATCH 09/17] Organize dependencies tests Signed-off-by: Roberto Rosario --- mayan/apps/dependencies/tests/mocks.py | 11 +++++++++++ mayan/apps/dependencies/tests/test_classes.py | 19 +++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 mayan/apps/dependencies/tests/mocks.py diff --git a/mayan/apps/dependencies/tests/mocks.py b/mayan/apps/dependencies/tests/mocks.py new file mode 100644 index 0000000000..1bc8bda2ff --- /dev/null +++ b/mayan/apps/dependencies/tests/mocks.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals + +from ..classes import Dependency, Provider + + +class TestProvider(Provider): + """Test provider""" + + +class TestDependency(Dependency): + provider_class = TestProvider diff --git a/mayan/apps/dependencies/tests/test_classes.py b/mayan/apps/dependencies/tests/test_classes.py index 9ba3a68d9c..63312b51e6 100644 --- a/mayan/apps/dependencies/tests/test_classes.py +++ b/mayan/apps/dependencies/tests/test_classes.py @@ -7,15 +7,7 @@ from mayan.apps.common.tests import BaseTestCase from mayan.apps.common.tests.utils import mute_stdout from mayan.apps.storage.utils import mkdtemp -from ..classes import Dependency, Provider - - -class TestProvider(Provider): - """Test provider""" - - -class TestDependency(Dependency): - provider_class = TestProvider +from .mocks import TestDependency class DependencyClassTestCase(BaseTestCase): @@ -40,7 +32,7 @@ class DependencyClassTestCase(BaseTestCase): super(DependencyClassTestCase, self).tearDown() shutil.rmtree(self.temporary_directory, ignore_errors=True) - def test_file_patching(self): + def _patch_test_file(self): replace_list = [ { 'filename_pattern': '*', @@ -59,8 +51,11 @@ class DependencyClassTestCase(BaseTestCase): ) with self.path_test_file.open(mode='r') as file_object: - final_text = file_object.read() + self.final_text = file_object.read() + + def test_file_patching(self): + self._patch_test_file() self.assertEqual( - final_text, '@import url({});'.format(self.test_replace_text) + self.final_text, '@import url({});'.format(self.test_replace_text) ) From b5c4c61b3f991cd72199628516fecdb78bbaa525 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 00:40:26 -0400 Subject: [PATCH 10/17] Add file patching tests to the storages app Signed-off-by: Roberto Rosario --- mayan/apps/storage/apps.py | 1 + mayan/apps/storage/tests/__init__.py | 0 mayan/apps/storage/tests/test_utils.py | 80 ++++++++++++++++++++++++++ mayan/apps/storage/utils.py | 47 +++++++++++---- 4 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 mayan/apps/storage/tests/__init__.py create mode 100644 mayan/apps/storage/tests/test_utils.py diff --git a/mayan/apps/storage/apps.py b/mayan/apps/storage/apps.py index 2cc2965e07..c849a06760 100644 --- a/mayan/apps/storage/apps.py +++ b/mayan/apps/storage/apps.py @@ -5,5 +5,6 @@ from django.utils.translation import ugettext_lazy as _ class StorageApp(apps.AppConfig): + has_tests = True name = 'mayan.apps.storage' verbose_name = _('Storage') diff --git a/mayan/apps/storage/tests/__init__.py b/mayan/apps/storage/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mayan/apps/storage/tests/test_utils.py b/mayan/apps/storage/tests/test_utils.py new file mode 100644 index 0000000000..b048edd053 --- /dev/null +++ b/mayan/apps/storage/tests/test_utils.py @@ -0,0 +1,80 @@ +from __future__ import print_function, unicode_literals + +from pathlib2 import Path +import shutil + +from mayan.apps.common.tests.base import BaseTestCase +from mayan.apps.storage.utils import mkdtemp + +from ..utils import patch_files + + +class PatchFilesTestCase(BaseTestCase): + test_replace_text = 'replaced_text' + + def setUp(self): + super(PatchFilesTestCase, self).setUp() + + self.temporary_directory = mkdtemp() + self.path_temporary_directory = Path(self.temporary_directory) + self.path_test_file = self.path_temporary_directory / 'test_file.txt' + + with self.path_test_file.open(mode='w') as file_object: + file_object.writelines( + [ + 'line 1\n', + ' line 2\n', + 'line 3\n', + ] + ) + + def tearDown(self): + super(PatchFilesTestCase, self).tearDown() + shutil.rmtree(self.temporary_directory, ignore_errors=True) + + def _patch_test_file(self): + replace_list = [ + { + 'filename_pattern': '*', + 'content_patterns': [ + { + 'search': self.test_search_text, + 'replace': self.test_replace_text, + } + ] + } + ] + patch_files( + path=self.path_temporary_directory, replace_list=replace_list + ) + + with self.path_test_file.open(mode='r') as file_object: + self.final_text = file_object.read() + + def test_file_patching_single_line(self): + self.test_search_text = 'line 1' + + self._patch_test_file() + + self.assertEqual(self.final_text, 'replaced_text\n line 2\nline 3\n') + + def test_file_patching_multi_line(self): + self.test_search_text = 'line 2\nline 3\n' + + self._patch_test_file() + + self.assertEqual(self.final_text, 'line 1\n replaced_text') + + def test_file_patching_spaces(self): + self.test_search_text = ' line 2' + + self._patch_test_file() + + self.assertEqual(self.final_text, 'line 1\nreplaced_text\nline 3\n') + + def test_file_patching_no_matches(self): + self.test_search_text = 'line 4' + + self._patch_test_file() + + self.assertEqual(self.final_text, 'line 1\n line 2\nline 3\n') diff --git a/mayan/apps/storage/utils.py b/mayan/apps/storage/utils.py index e288734c01..27175ba45c 100644 --- a/mayan/apps/storage/utils.py +++ b/mayan/apps/storage/utils.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -import fileinput import logging import os import shutil @@ -8,7 +7,6 @@ import tempfile from pathlib2 import Path -from django.utils.encoding import force_text from django.utils.module_loading import import_string from .settings import setting_temporary_directory @@ -74,15 +72,42 @@ def patch_files(path=None, replace_list=None): for replace_entry in replace_list or []: for path_entry in path_object.glob('**/{}'.format(replace_entry['filename_pattern'])): if path_entry.is_file(): - # PY3 - # Don't use context processor to allow working on Python 2.7 - # Update on Mayan EDMS version >= 4.0 - file_object = fileinput.FileInput(force_text(path_entry), inplace=True) - for line in file_object: - for pattern in replace_entry['content_patterns']: - line = line.replace(pattern['search'], pattern['replace']) - print(line, end='') - file_object.close() + for pattern in replace_entry['content_patterns']: + with path_entry.open(mode='r+') as source_file_object: + with tempfile.TemporaryFile(mode='r+') as temporary_file_object: + source_position = 0 + destination_position = 0 + + while(True): + source_file_object.seek(source_position) + letter = source_file_object.read(1) + + if len(letter) == 0: + break + else: + if letter == pattern['search'][0]: + text = '{}{}'.format(letter, source_file_object.read(len(pattern['search']) - 1)) + + temporary_file_object.seek(destination_position) + if text == pattern['search']: + text = pattern['replace'] + source_position = source_position + len(pattern['search']) + destination_position = destination_position + len(pattern['replace']) + temporary_file_object.write(text) + + else: + source_position = source_position + 1 + destination_position = destination_position + 1 + temporary_file_object.write(letter) + else: + source_position = source_position + 1 + destination_position = destination_position + 1 + temporary_file_object.write(letter) + + source_file_object.seek(0) + source_file_object.truncate() + temporary_file_object.seek(0) + shutil.copyfileobj(fsrc=temporary_file_object, fdst=source_file_object) def validate_path(path): From b0729ea714d9eff0b6d62a05cb6fe10939868270 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 01:52:57 -0400 Subject: [PATCH 11/17] Update patch_files to work with Python 2 and 3 Signed-off-by: Roberto Rosario --- mayan/apps/storage/utils.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mayan/apps/storage/utils.py b/mayan/apps/storage/utils.py index 27175ba45c..379c5605d0 100644 --- a/mayan/apps/storage/utils.py +++ b/mayan/apps/storage/utils.py @@ -7,6 +7,7 @@ import tempfile from pathlib2 import Path +from django.utils.six import PY3 from django.utils.module_loading import import_string from .settings import setting_temporary_directory @@ -68,13 +69,18 @@ def patch_files(path=None, replace_list=None): } ] """ + if PY3: + file_open_mode = 'r+' + else: + file_open_mode = 'rb+' + path_object = Path(path) for replace_entry in replace_list or []: for path_entry in path_object.glob('**/{}'.format(replace_entry['filename_pattern'])): if path_entry.is_file(): for pattern in replace_entry['content_patterns']: - with path_entry.open(mode='r+') as source_file_object: - with tempfile.TemporaryFile(mode='r+') as temporary_file_object: + with path_entry.open(mode=file_open_mode) as source_file_object: + with tempfile.TemporaryFile(mode=file_open_mode) as temporary_file_object: source_position = 0 destination_position = 0 From f0e04fc0692f525e285284d7ccc2a3dc9a969539 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 01:54:22 -0400 Subject: [PATCH 12/17] Add missing db.transaction import Signed-off-by: Roberto Rosario --- mayan/apps/documents/models/document_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mayan/apps/documents/models/document_models.py b/mayan/apps/documents/models/document_models.py index 373303f185..129ddae920 100644 --- a/mayan/apps/documents/models/document_models.py +++ b/mayan/apps/documents/models/document_models.py @@ -5,7 +5,7 @@ import uuid from django.apps import apps from django.core.files import File -from django.db import models +from django.db import models, transaction from django.urls import reverse from django.utils.encoding import python_2_unicode_compatible from django.utils.timezone import now From fc69f41fd05762c5da741d1c492b78ad8978f8c2 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 02:05:57 -0400 Subject: [PATCH 13/17] Style cleanup Signed-off-by: Roberto Rosario --- docs/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/utils.py b/docs/utils.py index f9fc3bba9a..adabac56fc 100644 --- a/docs/utils.py +++ b/docs/utils.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import shutil + from pathlib2 import Path import sphinx_rtd_theme From 3177a7a096c40977c855e358787c48198254c59c Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 02:06:22 -0400 Subject: [PATCH 14/17] Add pathlib2 to documentation requirements Signed-off-by: Roberto Rosario --- Makefile | 3 ++- requirements/documentation.txt | 8 +------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 110d56b5a8..2b77c3f0fc 100644 --- a/Makefile +++ b/Makefile @@ -240,7 +240,8 @@ generate-setup: generate-requirements generate-requirements: ## Generate all requirements files from the project depedency declarations. @./manage.py generaterequirements build > requirements/build.txt @./manage.py generaterequirements development > requirements/development.txt - @./manage.py generaterequirements documentation > requirements/documentation.txt + @./manage.py generaterequirements documentation >> requirements/documentation.txt + @./manage.py generaterequirements production --only=pathlib2 > requirements/documentation.txt @./manage.py generaterequirements testing > requirements/testing-base.txt @./manage.py generaterequirements production --exclude=django > requirements/base.txt @./manage.py generaterequirements production --only=django > requirements/common.txt diff --git a/requirements/documentation.txt b/requirements/documentation.txt index ebe46c305c..eef2e1b7cc 100644 --- a/requirements/documentation.txt +++ b/requirements/documentation.txt @@ -1,7 +1 @@ -Sphinx==1.8.5 -sphinx-autobuild==0.7.1 -sphinx-sitemap==1.0.2 -sphinx_rtd_theme==0.4.3 -sphinxcontrib-blockdiag==1.5.5 -sphinxcontrib-spelling==4.2.1 -tornado<6.0 +pathlib2==2.3.5 From 96b257a7b8baa582411466e3347ced5a32b4796f Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 02:06:56 -0400 Subject: [PATCH 15/17] Remove unused import Signed-off-by: Roberto Rosario --- mayan/apps/storage/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mayan/apps/storage/utils.py b/mayan/apps/storage/utils.py index 379c5605d0..35c65f28b5 100644 --- a/mayan/apps/storage/utils.py +++ b/mayan/apps/storage/utils.py @@ -8,7 +8,6 @@ import tempfile from pathlib2 import Path from django.utils.six import PY3 -from django.utils.module_loading import import_string from .settings import setting_temporary_directory From 2d5ec1a3ccb7a40441a6df813bd30eb040495e02 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 02:11:49 -0400 Subject: [PATCH 16/17] Add target to make releases easier Signed-off-by: Roberto Rosario --- Makefile | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Makefile b/Makefile index 2b77c3f0fc..6819983080 100644 --- a/Makefile +++ b/Makefile @@ -246,6 +246,30 @@ generate-requirements: ## Generate all requirements files from the project deped @./manage.py generaterequirements production --exclude=django > requirements/base.txt @./manage.py generaterequirements production --only=django > requirements/common.txt +gitlab-release-documentation: ## Trigger the documentation build and publication using GitLab CI +gitlab-release-documentation: + git push + git push origin :releases/documentation + git push origin HEAD:releases/documentation + +gitlab-release-docker: ## Trigger the Docker image build and publication using GitLab CI +gitlab-release-docker: + git push + git push origin :releases/docker + git push origin HEAD:releases/docker + +gitlab-release-python: ## Trigger the Python package build and publication using GitLab CI +gitlab-release-python: + git push + git push origin :releases/python + git push origin HEAD:releases/python + +gitlab-release-all: ## Trigger the Python package, Docker image, and documentation build and publication using GitLab CI +gitlab-release-all: + git push + git push origin :releases/all + git push origin HEAD:releases/all + # Dev server runserver: ## Run the development server. From ae134ae4095e50b5aa95c2f30d8c97f142283b74 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Nov 2019 02:17:17 -0400 Subject: [PATCH 17/17] Fix documentation requirements generation Signed-off-by: Roberto Rosario --- Makefile | 4 ++-- requirements/documentation.txt | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6819983080..182834b696 100644 --- a/Makefile +++ b/Makefile @@ -240,8 +240,8 @@ generate-setup: generate-requirements generate-requirements: ## Generate all requirements files from the project depedency declarations. @./manage.py generaterequirements build > requirements/build.txt @./manage.py generaterequirements development > requirements/development.txt - @./manage.py generaterequirements documentation >> requirements/documentation.txt - @./manage.py generaterequirements production --only=pathlib2 > requirements/documentation.txt + @./manage.py generaterequirements documentation > requirements/documentation.txt + @./manage.py generaterequirements production --only=pathlib2 >> requirements/documentation.txt @./manage.py generaterequirements testing > requirements/testing-base.txt @./manage.py generaterequirements production --exclude=django > requirements/base.txt @./manage.py generaterequirements production --only=django > requirements/common.txt diff --git a/requirements/documentation.txt b/requirements/documentation.txt index eef2e1b7cc..b54ef1897a 100644 --- a/requirements/documentation.txt +++ b/requirements/documentation.txt @@ -1 +1,8 @@ +Sphinx==1.8.5 +sphinx-autobuild==0.7.1 +sphinx-sitemap==1.0.2 +sphinx_rtd_theme==0.4.3 +sphinxcontrib-blockdiag==1.5.5 +sphinxcontrib-spelling==4.2.1 +tornado<6.0 pathlib2==2.3.5