Merge branch 'development' into feature/multi-tenant
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
2.1.2 (2016-05-20)
|
||||
==================
|
||||
- Sort document languages and user profile locale language lists. GitLab issue #292.
|
||||
- Fix metadata lookup for {{ users }} and {{ group }}. Fixes GitLab #290.
|
||||
- Add Makefile for common development tasks
|
||||
|
||||
2.1.1 (2016-05-17)
|
||||
==================
|
||||
- Fix navigation issue that make it impossible to add new sources. GitLab issue #288.
|
||||
|
||||
113
Makefile
Normal file
113
Makefile
Normal file
@@ -0,0 +1,113 @@
|
||||
.PHONY: clean-pyc clean-build
|
||||
|
||||
define BROWSER_PYSCRIPT
|
||||
import sys, webbrowser
|
||||
webbrowser.open(sys.argv[1])
|
||||
endef
|
||||
export BROWSER_PYSCRIPT
|
||||
BROWSER := python -c "$$BROWSER_PYSCRIPT"
|
||||
|
||||
|
||||
help:
|
||||
@echo
|
||||
@echo "clean-build - Remove build artifacts."
|
||||
@echo "clean-pyc - Remove Python artifacts."
|
||||
@echo "clean - Remove Python and build artifacts."
|
||||
|
||||
@echo "test MODULE=<python module name> - Run tests for a single App, module or test class."
|
||||
@echo "test-all - Run all tests."
|
||||
@echo "docs_serve - Run the livehtml documentation generator."
|
||||
|
||||
@echo "translations_make - Refresh all translation files."
|
||||
@echo "translations_compile - Compile all translation files."
|
||||
@echo "translations_push - Upload all translation files to Transifex."
|
||||
@echo "translations_pull - Download all translation files from Transifex."
|
||||
|
||||
@echo "requirements_dev - Install development requirements."
|
||||
@echo "requirements_docs - Install documentation requirements."
|
||||
@echo "requirements_testing - Install testing requirements."
|
||||
|
||||
@echo "sdist - Build the source distribution package."
|
||||
@echo "wheel - Build the wheel distribution package."
|
||||
@echo "release - Package (sdist and wheel) and upload a release."
|
||||
|
||||
@echo "runserver - Run the development server."
|
||||
|
||||
|
||||
# Cleaning
|
||||
|
||||
clean: clean-build clean-pyc
|
||||
|
||||
clean-build:
|
||||
rm -fr build/
|
||||
rm -fr dist/
|
||||
rm -fr *.egg-info
|
||||
|
||||
clean-pyc:
|
||||
find . -name '*.pyc' -exec rm -f {} +
|
||||
find . -name '*.pyo' -exec rm -f {} +
|
||||
find . -name '*~' -exec rm -f {} +
|
||||
|
||||
|
||||
# Testing
|
||||
|
||||
test:
|
||||
./manage.py test $(MODULE) --settings=mayan.settings.testing --nomigrations
|
||||
|
||||
test-all:
|
||||
./manage.py runtests --settings=mayan.settings.testing --nomigrations
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
docs_serve:
|
||||
$(BROWSER) http://127.0.0.1:8000
|
||||
cd docs;make livehtml
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
translations_make:
|
||||
contrib/scripts/process_messages.py -m
|
||||
|
||||
translations_compile:
|
||||
contrib/scripts/process_messages.py -c
|
||||
|
||||
translations_push:
|
||||
tx push -s
|
||||
|
||||
translations_pull:
|
||||
tx pull
|
||||
|
||||
|
||||
# Requirements
|
||||
|
||||
requirements_dev:
|
||||
pip install -r requirements/development.txt
|
||||
|
||||
requirements_docs:
|
||||
pip install -r requirements/documentation.txt
|
||||
|
||||
requirements_testing:
|
||||
pip install -r requirements/testing.txt
|
||||
|
||||
|
||||
# Releases
|
||||
|
||||
release: clean
|
||||
python setup.py sdist bdist_wheel upload
|
||||
|
||||
sdist: clean
|
||||
python setup.py sdist
|
||||
ls -l dist
|
||||
|
||||
wheel: clean
|
||||
python setup.py bdist_wheel
|
||||
ls -l dist
|
||||
|
||||
|
||||
# Dev server
|
||||
|
||||
runserver:
|
||||
$(BROWSER) http://127.0.0.1:8000
|
||||
./manage.py runserver
|
||||
86
docs/releases/2.1.2.rst
Normal file
86
docs/releases/2.1.2.rst
Normal file
@@ -0,0 +1,86 @@
|
||||
===============================
|
||||
Mayan EDMS v2.1.2 release notes
|
||||
===============================
|
||||
|
||||
Released: May 20, 2016
|
||||
|
||||
What's new
|
||||
==========
|
||||
|
||||
This is a bugfix release and all users are encouraged to upgrade.
|
||||
|
||||
Language list sorting
|
||||
---------------------
|
||||
The document language list and the user locale profile language list are now
|
||||
sorted to make it easier to find the desired language.
|
||||
|
||||
Fixed the metadata lookup options: {{ users }} and {{ groups }}
|
||||
---------------------------------------------------------------
|
||||
When configuring metadata type lookup options the {{ users }} and {{ groups }}
|
||||
special options can be used to display a list of users or a list of groups.
|
||||
These options where producing a list in the wrong format and were updated.
|
||||
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Add Makefile for common development tasks
|
||||
|
||||
|
||||
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 #290 <https://gitlab.com/mayan-edms/mayan-edms/issues/290>`_ Unicode characters not supported as metadata values
|
||||
* `GitLab issue #292 <https://gitlab.com/mayan-edms/mayan-edms/issues/292>`_ Sort languages by name not by abbreviation
|
||||
|
||||
|
||||
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/
|
||||
@@ -22,6 +22,7 @@ versions of the documentation contain the release notes for any later releases.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
2.1.2
|
||||
2.1.1
|
||||
2.1
|
||||
2.0.2
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__title__ = 'Mayan EDMS'
|
||||
__version__ = '2.1.1'
|
||||
__build__ = 0x020101
|
||||
__version__ = '2.1.2'
|
||||
__build__ = 0x020102
|
||||
__author__ = 'Roberto Rosario'
|
||||
__author_email__ = 'roberto.rosario@mayan-edms.com'
|
||||
__description__ = 'Free Open Source Electronic Document Management System'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from operator import itemgetter
|
||||
import os
|
||||
|
||||
from django import forms
|
||||
@@ -114,10 +115,44 @@ class LicenseForm(FileDisplayForm):
|
||||
FILENAME = 'LICENSE'
|
||||
|
||||
|
||||
class LocaleProfileForm(forms.ModelForm):
|
||||
class ModelForm(forms.ModelForm):
|
||||
"""
|
||||
ModelForm subclass that supports field choices sorting
|
||||
|
||||
class Meta:
|
||||
# Dictionary of field names and the key used to sort the field
|
||||
sorted_fields = None
|
||||
|
||||
# Example:
|
||||
# sorted_fields = {'language': operator.itemgetter(1))}
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ModelForm, self).__init__(*args, **kwargs)
|
||||
|
||||
for field, key in getattr(self.Meta, 'sorted_fields', {}).items():
|
||||
# Would be the cleaner "opts.sorted_fields" if these were addressed
|
||||
# https://code.djangoproject.com/ticket/5793
|
||||
# of a get_options_class for Forms/ModelForms
|
||||
# https://code.djangoproject.com/ticket/18540
|
||||
choices = self.fields[field].choices
|
||||
|
||||
if not self.fields[field].required:
|
||||
# Remove empty choice before sorting
|
||||
empty_choice = choices.pop(0)
|
||||
|
||||
self.fields[field].choices = sorted(choices, key=key)
|
||||
|
||||
if not self.fields[field].required:
|
||||
# Add empty choice after sorting
|
||||
self.fields[field].choices.insert(0, empty_choice)
|
||||
|
||||
|
||||
class LocaleProfileForm(ModelForm):
|
||||
class Meta:
|
||||
fields = ('language', 'timezone')
|
||||
model = UserLocaleProfile
|
||||
sorted_fields = {'language': itemgetter(1)}
|
||||
|
||||
|
||||
class LocaleProfileForm_view(DetailForm):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
from operator import itemgetter
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@@ -8,7 +9,7 @@ from django.template.defaultfilters import filesizeformat
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from common.forms import DetailForm
|
||||
from common.forms import DetailForm, ModelForm
|
||||
from permissions import Permission
|
||||
|
||||
from .models import (
|
||||
@@ -60,13 +61,14 @@ class DocumentPreviewForm(forms.Form):
|
||||
preview = forms.CharField(widget=DocumentPagesCarouselWidget())
|
||||
|
||||
|
||||
class DocumentForm(forms.ModelForm):
|
||||
class DocumentForm(ModelForm):
|
||||
"""
|
||||
Form sub classes from DocumentForm used only when editing a document
|
||||
"""
|
||||
class Meta:
|
||||
model = Document
|
||||
fields = ('label', 'description', 'language')
|
||||
model = Document
|
||||
sorted_fields = {'language': itemgetter(1)}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
document_type = kwargs.pop('document_type', None)
|
||||
|
||||
@@ -29,7 +29,10 @@ class MetadataLookup(object):
|
||||
def get_as_context(cls):
|
||||
result = {}
|
||||
for entry in cls._registry:
|
||||
result[entry.name] = entry.value
|
||||
try:
|
||||
result[entry.name] = entry.value()
|
||||
except TypeError:
|
||||
result[entry.name] = entry.value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@ from .links import (
|
||||
)
|
||||
|
||||
|
||||
def get_groups():
|
||||
return ','.join([group.name for group in Group.objects.all()])
|
||||
|
||||
|
||||
def get_users():
|
||||
return ','.join([user.get_full_name() or user.username for user in get_user_model().objects.all()])
|
||||
|
||||
|
||||
class UserManagementApp(MayanAppConfig):
|
||||
app_url = 'accounts'
|
||||
name = 'user_management'
|
||||
@@ -36,12 +44,12 @@ class UserManagementApp(MayanAppConfig):
|
||||
APIEndPoint(app=self, version_string='1')
|
||||
|
||||
MetadataLookup(
|
||||
description=_('All the groups.'), name='group',
|
||||
value=Group.objects.all()
|
||||
description=_('All the groups.'), name='groups',
|
||||
value=get_groups
|
||||
)
|
||||
MetadataLookup(
|
||||
description=_('All the users.'), name='users',
|
||||
value=User.objects.all()
|
||||
value=get_users
|
||||
)
|
||||
|
||||
SourceColumn(
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from common.tests.test_views import GenericViewTestCase
|
||||
from documents.tests.test_views import GenericDocumentViewTestCase
|
||||
|
||||
from metadata.models import MetadataType
|
||||
from metadata.permissions import permission_metadata_document_edit
|
||||
|
||||
from metadata.tests.literals import (
|
||||
TEST_METADATA_TYPE_LABEL, TEST_METADATA_TYPE_NAME,
|
||||
)
|
||||
|
||||
from ..permissions import (
|
||||
permission_user_delete, permission_user_edit, permission_user_view
|
||||
@@ -179,3 +188,58 @@ class UserManagementViewTestCase(GenericViewTestCase):
|
||||
|
||||
self.assertContains(response, text='deleted', status_code=200)
|
||||
self.assertEqual(get_user_model().objects.count(), 2)
|
||||
|
||||
|
||||
class MetadataLookupIntegrationTestCase(GenericDocumentViewTestCase):
|
||||
def setUp(self):
|
||||
super(MetadataLookupIntegrationTestCase, self).setUp()
|
||||
|
||||
self.metadata_type = MetadataType.objects.create(
|
||||
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
||||
)
|
||||
|
||||
self.document_type.metadata.create(metadata_type=self.metadata_type)
|
||||
|
||||
def test_user_list_lookup_render(self):
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||
)
|
||||
|
||||
self.metadata_type.lookup = '{{ users }}'
|
||||
self.metadata_type.save()
|
||||
self.document.metadata.create(metadata_type=self.metadata_type)
|
||||
self.role.permissions.add(
|
||||
permission_metadata_document_edit.stored_permission
|
||||
)
|
||||
|
||||
response = self.get(
|
||||
viewname='metadata:metadata_edit', args=(self.document.pk,)
|
||||
)
|
||||
|
||||
self.assertContains(
|
||||
response, text='<option value="{}">{}</option>'.format(
|
||||
TEST_USER_USERNAME, TEST_USER_USERNAME
|
||||
), status_code=200
|
||||
)
|
||||
|
||||
def test_group_list_lookup_render(self):
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||
)
|
||||
|
||||
self.metadata_type.lookup = '{{ groups }}'
|
||||
self.metadata_type.save()
|
||||
self.document.metadata.create(metadata_type=self.metadata_type)
|
||||
self.role.permissions.add(
|
||||
permission_metadata_document_edit.stored_permission
|
||||
)
|
||||
|
||||
response = self.get(
|
||||
viewname='metadata:metadata_edit', args=(self.document.pk,)
|
||||
)
|
||||
|
||||
self.assertContains(
|
||||
response, text='<option value="{}">{}</option>'.format(
|
||||
Group.objects.first().name, Group.objects.first().name
|
||||
), status_code=200
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user