Compare commits

..

1 Commits

Author SHA1 Message Date
Roberto Rosario
7889ff64d9 Document zip file encoding issues
Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-10-01 00:20:50 -04:00
14 changed files with 327 additions and 355 deletions

View File

@@ -1,4 +1,4 @@
3.2.8 (2019-10-01) 3.2.8 (2019-XX-XX)
================== ==================
- Fix error when accessing some API entry points without - Fix error when accessing some API entry points without
being authenticated. being authenticated.

View File

@@ -1 +1 @@
3.2.8 3.2.7

View File

@@ -533,7 +533,7 @@ Release using GitLab CI
:: ::
git checkout releases/all git checkout releases/all
git merge <corresponding branch> git merge versions/next
#. Push code to trigger builds: #. Push code to trigger builds:
:: ::

View File

@@ -1,21 +1,12 @@
Version 3.2.8 Version 3.2.8
============= =============
Released: October 1, 2019 Released: XX, 2019
Changes Changes
------- -------
API
^^^
Fix an error when accessing some API entry points without
being authenticated. Accessing API endpoints without being authenticated
will now always return empty results.
Cabinets Cabinets
^^^^^^^^ ^^^^^^^^
@@ -23,43 +14,26 @@ Tweaked the jstree component's appearance to cope with long labels.
Added a scrollbar, reduced the font size, switched to a sans serif font, Added a scrollbar, reduced the font size, switched to a sans serif font,
and reduced padding. Thanks for forum user @briboe for the report. and reduced padding. Thanks for forum user @briboe for the report.
Workflow actions to add and remove documents from cabinets was added.
Other changes
^^^^^^^^^^^^^
Dependencies - Fix error when accessing some API entry points without
^^^^^^^^^^^^ being authenticated.
- Add cabinet add and remove workflow actions.
The Django version used was updated to version 1.11.24. The jQuery version - Update Django to version 1.11.24.
used was updated to version 3.4.1. Both as fully backwards compatible with - Update jQuery to version 3.4.1
their previous versions. - Add support for deleting the OCR content of a document
or selection of documents.
- Add OCR content deleted event.
OCR - Add missing recursive option to Docker entrypoint
^^^ chown. GitLab issue #668. Thanks to John Wice (@brilthor)
Support was added to delete the content of document's OCR or parsed content.
Events for both situations was added allowing content deletion to be used
as workflow transition triggers.
Docker
^^^^^^
A missing recursive option was added to the Docker entrypoint
command "chown" to change the ownership of files when specifying a custom
UID or GID. Closes GitLab issue #668. Thanks to John Wice (@brilthor)
for the report. for the report.
- Add support for deleting the parsed content of a document
Two fonts were added to the Docker image to support rendering Chinese office of selection of documents.
documents. Closes GitLab issue #666. Thanks to javawcy (@javawcy) and forum - Add parsed content deleted event.
user @leoliu for the report and help closing this issue. - Allow scaling of UI on mobile devices.
- Add Chinese fonts to the Docker image
Usability
^^^^^^^^^
Descriptions for screenreaders was added via image alt tag. The user interface
will also now allow scaling.
Removals Removals

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.8' __version__ = '3.2.7'
__build__ = 0x030208 __build__ = 0x030207
__build_string__ = 'v3.2.8_Tue Oct 1 13:31:40 2019 -0400' __build_string__ = 'v3.2.7_Wed Aug 28 17:31:08 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

@@ -11,6 +11,7 @@ except ImportError:
COMPRESSION = zipfile.ZIP_STORED COMPRESSION = zipfile.ZIP_STORED
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils.encoding import force_text
from mayan.apps.mimetype.api import get_mimetype from mayan.apps.mimetype.api import get_mimetype
@@ -136,9 +137,32 @@ class ZipArchive(Archive):
return self._archive.read(filename) return self._archive.read(filename)
def members(self): def members(self):
return [ results = []
filename for filename in self._archive.namelist() if not filename.endswith('/') from django.utils.encoding import force_str
]
for filename in self._archive.namelist():
# Zip files only support UTF-8 and CP437 encodings.
# Attempt to decode CP437 to be able to check if it ends
# with a slash.
# Future improvement that violates the Zip format:
# Add chardet.detect to detect the most likely encoding
# if other than CP437.
try:
filename = filename.decode('CP437')
is_unicode = False
except UnicodeEncodeError:
is_unicode = True
if not filename.endswith('/'):
# Re encode in the original encoding
if not is_unicode:
filename = filename.encode(
encoding='CP437', errors='strict'
)
results.append(filename)
return results
def open_member(self, filename): def open_member(self, filename):
return self._archive.open(filename) return self._archive.open(filename)

View File

@@ -12,6 +12,7 @@ TEST_VIEW_URL = 'test-view-url'
# Filenames # Filenames
TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER = 'test_archvive_with_special_characters_filename_member.zip' TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER = 'test_archvive_with_special_characters_filename_member.zip'
TEST_ARCHIVE_ZIP_CP437_MEMBER = 'test_archvive_with_cp437_member.zip'
TEST_FILENAME1 = 'test_file1.txt' TEST_FILENAME1 = 'test_file1.txt'
TEST_FILENAME2 = 'test_file2.txt' TEST_FILENAME2 = 'test_file2.txt'
TEST_FILENAME3 = 'test_file3.txt' TEST_FILENAME3 = 'test_file3.txt'
@@ -28,6 +29,10 @@ TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER_PATH = os.path.join(
settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib', settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib',
TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER
) )
TEST_ARCHIVE_ZIP_CP437_MEMBER_PATH = os.path.join(
settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib',
TEST_ARCHIVE_ZIP_CP437_MEMBER
)
TEST_FILE3_PATH = os.path.join( TEST_FILE3_PATH = os.path.join(
settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib', TEST_FILENAME3 settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib', TEST_FILENAME3
) )

View File

@@ -5,6 +5,7 @@ from mayan.apps.common.tests import BaseTestCase
from ..compressed_files import Archive, TarArchive, ZipArchive from ..compressed_files import Archive, TarArchive, ZipArchive
from .literals import ( from .literals import (
TEST_ARCHIVE_ZIP_CP437_MEMBER_PATH,
TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER_PATH, TEST_ARCHIVE_ZIP_SPECIAL_CHARACTERS_FILENAME_MEMBER_PATH,
TEST_COMPRESSED_FILE_CONTENTS, TEST_FILE_CONTENTS_1, TEST_FILE3_PATH, TEST_COMPRESSED_FILE_CONTENTS, TEST_FILE_CONTENTS_1, TEST_FILE3_PATH,
TEST_FILENAME1, TEST_FILENAME3, TEST_TAR_BZ2_FILE_PATH, TEST_FILENAME1, TEST_FILENAME3, TEST_TAR_BZ2_FILE_PATH,
@@ -64,6 +65,11 @@ class ZipArchiveClassTestCase(TarArchiveClassTestCase):
archive = Archive.open(file_object=file_object) archive = Archive.open(file_object=file_object)
list(archive.get_members()) list(archive.get_members())
def test_open_cp437_member(self):
with open(TEST_ARCHIVE_ZIP_CP437_MEMBER_PATH, mode='rb') as file_object:
archive = Archive.open(file_object=file_object)
list(archive.get_members())
class TarGzArchiveClassTestCase(TarArchiveClassTestCase): class TarGzArchiveClassTestCase(TarArchiveClassTestCase):
archive_path = TEST_TAR_GZ_FILE_PATH archive_path = TEST_TAR_GZ_FILE_PATH
@@ -73,3 +79,5 @@ class TarGzArchiveClassTestCase(TarArchiveClassTestCase):
class TarBz2ArchiveClassTestCase(TarArchiveClassTestCase): class TarBz2ArchiveClassTestCase(TarArchiveClassTestCase):
archive_path = TEST_TAR_BZ2_FILE_PATH archive_path = TEST_TAR_BZ2_FILE_PATH
cls = TarArchive cls = TarArchive

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django import forms from django import forms
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html_join, mark_safe from django.utils.html import format_html_join, mark_safe
from django.utils.translation import ugettext_lazy as _
def widget_transition_events(transition): def widget_transition_events(transition):

View File

@@ -10,14 +10,6 @@ from .literals import (
) )
class SmartLinkDocumentViewTestMixin(object):
def _request_test_smart_link_document_instances_view(self):
return self.get(
viewname='linking:smart_link_instances_for_document',
kwargs={'pk': self.test_document.pk}
)
class SmartLinkTestMixin(object): class SmartLinkTestMixin(object):
def _create_test_smart_link(self, add_test_document_type=False): def _create_test_smart_link(self, add_test_document_type=False):
self.test_smart_link = SmartLink.objects.create( self.test_smart_link = SmartLink.objects.create(

File diff suppressed because it is too large Load Diff

View File

@@ -14,15 +14,10 @@ from .literals import (
TEST_SMART_LINK_DYNAMIC_LABEL, TEST_SMART_LINK_LABEL_EDITED, TEST_SMART_LINK_DYNAMIC_LABEL, TEST_SMART_LINK_LABEL_EDITED,
TEST_SMART_LINK_LABEL TEST_SMART_LINK_LABEL
) )
from .mixins import ( from .mixins import SmartLinkTestMixin, SmartLinkViewTestMixin
SmartLinkDocumentViewTestMixin, SmartLinkTestMixin,
SmartLinkViewTestMixin
)
class SmartLinkViewTestCase( class SmartLinkViewTestCase(SmartLinkTestMixin, SmartLinkViewTestMixin, GenericViewTestCase):
SmartLinkTestMixin, SmartLinkViewTestMixin, GenericViewTestCase
):
def test_smart_link_create_view_no_permission(self): def test_smart_link_create_view_no_permission(self):
response = self._request_test_smart_link_create_view() response = self._request_test_smart_link_create_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
@@ -79,15 +74,10 @@ class SmartLinkViewTestCase(
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.test_smart_link.refresh_from_db() self.test_smart_link.refresh_from_db()
self.assertEqual( self.assertEqual(self.test_smart_link.label, TEST_SMART_LINK_LABEL_EDITED)
self.test_smart_link.label, TEST_SMART_LINK_LABEL_EDITED
)
class SmartLinkDocumentViewTestCase( class SmartLinkDocumentViewTestCase(SmartLinkTestMixin, GenericDocumentViewTestCase):
SmartLinkTestMixin, SmartLinkDocumentViewTestMixin,
GenericDocumentViewTestCase
):
def setUp(self): def setUp(self):
super(SmartLinkDocumentViewTestCase, self).setUp() super(SmartLinkDocumentViewTestCase, self).setUp()
self._create_test_smart_link() self._create_test_smart_link()
@@ -99,6 +89,12 @@ class SmartLinkDocumentViewTestCase(
) )
self.test_smart_link_2.document_types.add(self.test_document_type) self.test_smart_link_2.document_types.add(self.test_document_type)
def _request_test_smart_link_document_instances_view(self):
return self.get(
viewname='linking:smart_link_instances_for_document',
kwargs={'pk': self.test_document.pk}
)
def test_document_smart_link_list_view_no_permission(self): def test_document_smart_link_list_view_no_permission(self):
self.grant_access( self.grant_access(
obj=self.test_document, permission=permission_document_view obj=self.test_document, permission=permission_document_view

View File

@@ -56,7 +56,7 @@ def find_packages(directory):
return packages return packages
install_requires = """ install_requires = """
django==1.11.24 django==1.11.22
Pillow==6.0.0 Pillow==6.0.0
PyPDF2==1.26.0 PyPDF2==1.26.0
PyYAML==5.1.1 PyYAML==5.1.1