diff --git a/HISTORY.rst b/HISTORY.rst index 5fba951d48..d0f865924d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..7645214f2b --- /dev/null +++ b/Makefile @@ -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= - 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 diff --git a/docs/releases/2.1.2.rst b/docs/releases/2.1.2.rst new file mode 100644 index 0000000000..1f9aeda0d9 --- /dev/null +++ b/docs/releases/2.1.2.rst @@ -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 `_ Unicode characters not supported as metadata values +* `GitLab issue #292 `_ Sort languages by name not by abbreviation + + +.. _PyPI: https://pypi.python.org/pypi/mayan-edms/ diff --git a/docs/releases/index.rst b/docs/releases/index.rst index ef8f792541..8af72af3ff 100644 --- a/docs/releases/index.rst +++ b/docs/releases/index.rst @@ -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 diff --git a/mayan/__init__.py b/mayan/__init__.py index 195d1759f5..011981549a 100644 --- a/mayan/__init__.py +++ b/mayan/__init__.py @@ -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' diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index fa0c16d258..8c1c493726 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -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): diff --git a/mayan/apps/documents/forms.py b/mayan/apps/documents/forms.py index a1633cc86c..b625441407 100644 --- a/mayan/apps/documents/forms.py +++ b/mayan/apps/documents/forms.py @@ -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) diff --git a/mayan/apps/metadata/classes.py b/mayan/apps/metadata/classes.py index 3d16e79cdb..08bd9a5d03 100644 --- a/mayan/apps/metadata/classes.py +++ b/mayan/apps/metadata/classes.py @@ -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 diff --git a/mayan/apps/user_management/apps.py b/mayan/apps/user_management/apps.py index b01334daa4..992b11dc9c 100644 --- a/mayan/apps/user_management/apps.py +++ b/mayan/apps/user_management/apps.py @@ -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( diff --git a/mayan/apps/user_management/tests/test_views.py b/mayan/apps/user_management/tests/test_views.py index 6e7ce52595..ff931c096a 100644 --- a/mayan/apps/user_management/tests/test_views.py +++ b/mayan/apps/user_management/tests/test_views.py @@ -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=''.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=''.format( + Group.objects.first().name, Group.objects.first().name + ), status_code=200 + )