Skip to content

Commit

Permalink
Merge 12e3310 into bb4d7ff
Browse files Browse the repository at this point in the history
  • Loading branch information
lmignon committed Aug 11, 2017
2 parents bb4d7ff + 12e3310 commit 8763bd9
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 2 deletions.
74 changes: 74 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/
html

# PyBuilder
target/

# virtualenv
venv

# editable sources
/src/

# release directory
release

# pydev
.project
.jshintrc
.pydevproject
.settings
.idea

3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ addons:
- expect-dev # provides unbuffer utility
- python-lxml # because pip installation is slow
- wkhtmltopdf
- python-pychart

language: python

Expand All @@ -28,7 +29,7 @@ before_install:
- "sh -e /etc/init.d/xvfb start"

install:
- git clone -b pip-install-sbi https://github.com/acsone/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- git clone -b pip-install-no-pychart-lmi https://github.com/acsone/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly

Expand Down
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
9.0.?.?.? (?)
~~~~~~~~~~~~~

* Improvement: Handle name conflict on folder create.
A new parameter on the backend let's you choice between 2 strategies:
'error' or 'increment. If a folder already exists with the same name, the
system will raise an error if 'error' is specified as strategy (default)
otherwise a suffix is added to the name to make it unique.
* Improvement: Allow the preview of image files.
* Fix: Display the node title if set into the CMIS container.
* Fix: On the import document dialog, rename 'Create' button into 'Add'
Expand Down
1 change: 1 addition & 0 deletions cmis_field/fields/cmis_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def _create_in_cmis(self, records, backend):
else:
backend.is_valid_cmis_name(name, raise_if_invalid=True)
parent = parents[record.id]
name = backend.get_unique_folder_name(name, parent)
props = properties[record.id] or {}
value = repo.createFolder(
parent, name, props)
Expand Down
47 changes: 47 additions & 0 deletions cmis_field/models/cmis_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ def _check_sanitize_replace_char(self):
help='Character used as replacement of invalid characters found in'
'the value to use as cmis:name by the sanitize method',
default='_')
folder_name_conflict_handler = fields.Selection(
selection=[
('error', _('Raise exception')),
('increment', _('Create as "name_(X)"'))
],
string='Strategy in case of duplicate',
required=True,
default='error',
)

@api.model
def _get_web_description(self, record):
Expand Down Expand Up @@ -121,3 +130,41 @@ def get_folder_by_path_parts(self, path_parts, create_if_not_found=True,
path = "/".join(path_parts)
return self.get_folder_by_path(
path, create_if_not_found, cmis_parent_objectid)

@api.multi
def get_unique_folder_name(self, name, parent, conflict_handler=None):
"""Return a unique name for a folder into its parent.
Check if the name already exists into the parent.
If the name already exists:
if bakend.folder_name_conflict_handler == 'error'
ValidationError is raised
if backend.folder_name_conflict_handler == 'increment'
return a new name with suffix '_X'
:return: a unique name
"""
self.ensure_one()
conflict_handler = (conflict_handler or
self.folder_name_conflict_handler)
cmis_qry = ("SELECT cmis:objectId FROM cmis:folder WHERE "
"IN_FOLDER('%s') AND cmis:name='%s'" %
(parent.getObjectId(), name))
rs = parent.repository.query(cmis_qry)
num_found_items = rs.getNumItems()
if num_found_items > 0:
if conflict_handler == 'error':
raise ValidationError(
_('Folder "%s" already exists in CMIS') % (name))
if conflict_handler == 'increment':
testname = name + '_(%)'
cmis_qry = ("SELECT * FROM cmis:folder WHERE "
"IN_FOLDER('%s') AND cmis:name like '%s'" %
(parent.getObjectId(), testname))
rs = parent.repository.query(cmis_qry)
names = [r.name for r in rs]
max_num = 0
if names:
max_num = max(
[int(re.findall(r'_\((\d+)\)', n)[-1]) for n in names])
return name + '_(%d)' % (max_num + 1)
return name
17 changes: 17 additions & 0 deletions cmis_field/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,20 @@ def setUpClass(cls):
cls.cmis_test_model = cls._init_test_model(models.CmisTestModel)
cls.cmis_backend = cls.env.ref('cmis.cmis_backend_alfresco')
cls.cmis_backend.initial_directory_write = '/odoo'

def setUp(self):
super(BaseTestCmis, self).setUp()

# global patch

def get_unique_folder_name(name, parent):
return name
self.patched_get_unique_folder_name = mock.patch.object(
self.cmis_backend.__class__, 'get_unique_folder_name',
side_effect=get_unique_folder_name
)
self.patched_get_unique_folder_name.start()

def tearDown(self):
super(BaseTestCmis, self).tearDown()
self.patched_get_unique_folder_name.stop()
44 changes: 44 additions & 0 deletions cmis_field/tests/test_cmis_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Copyright 2016 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import mock
from openerp.tests import common
from openerp.exceptions import UserError, ValidationError

Expand Down Expand Up @@ -59,3 +60,46 @@ def test_sanitize_cmis_name(self):
sanitized = self.backend_instance.sanitize_cmis_names(
['/y dir*', 'sub/dir'], ' ')
self.assertEqual(sanitized, ['y dir', 'sub dir'])

def test_get_unique_folder_name(self):
mocked_parent = mock.MagicMock()
# if no name found, the method return the same name
repository = mock.MagicMock()
mocked_parent.repository = repository
query_result = mock.MagicMock()
repository.query.side_effect = lambda *a: query_result
query_result.getNumItems.side_effect = lambda *a: 0
self.assertEqual('test', self.backend_instance.get_unique_folder_name(
'test', mocked_parent))
# if the same name is found and the folder_name_conflict_handler ==
# 'error' a ValidationError is raised
query_result.getNumItems.side_effect = lambda *a: 1
self.backend_instance.folder_name_conflict_handler = 'error'
with self.assertRaises(ValidationError):
self.backend_instance.get_unique_folder_name('test', mocked_parent)

self.cpt = 0

def query(q):
if self.cpt == 0:
self.cpt += 1
query_result.getNumItems.side_effect = lambda *a: 1
return query_result
if self.cpt == 1:
test = mock.Mock()
test.configure_mock()
ret = []
for v in ['test_(1)', 'test_(3)']:
m = mock.Mock()
m.configure_mock(name=v)
ret.append(m)
query_result.__iter__.return_value = ret
return query_result
# if the same name is found and the folder_name_conflict_handler ==
# 'increment' the method must return a new name with a suffix _(X)
# where X is the value max found as X for the same name + 1
self.backend_instance.folder_name_conflict_handler = 'increment'
repository.query.side_effect = query
name = self.backend_instance.get_unique_folder_name(
'test', mocked_parent)
self.assertEqual('test_(4)', name)
2 changes: 1 addition & 1 deletion cmis_field/tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,4 @@ def test_cmis_folder_get_desciption(self):
self.cmis_backend.unlink()
descr = inst._fields['cmis_folder'].get_description(self.env)
backend_description = descr.get('backend')
self.assertTrue('backend_error' in descr.get('backend'))
self.assertTrue('backend_error' in backend_description)
1 change: 1 addition & 0 deletions cmis_field/views/cmis_backend_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<field name="location" position="after">
<field name="enable_sanitize_cmis_name" colspan="2"/>
<field name="sanitize_replace_char" colspan="2" attrs="{'invisible': [('enable_sanitize_cmis_name', '=', False)]}"/>
<field name="folder_name_conflict_handler" colspan="4"/>
</field>
</field>
</record>
Expand Down
1 change: 1 addition & 0 deletions cmis_web_alf/models/cmis_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright 2016 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

# pylint: disable=missing-import-error, missing-manifest-dependency
from cmislib.browser.binding import safe_urlencode
from openerp import api, models

Expand Down

0 comments on commit 8763bd9

Please sign in to comment.