Skip to content

Commit

Permalink
Improve CmisFolder field API
Browse files Browse the repository at this point in the history
  • Loading branch information
lmignon committed Sep 4, 2016
1 parent ecbe9d8 commit d7421a5
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 154 deletions.
2 changes: 1 addition & 1 deletion cmis/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import main
from . import main
7 changes: 4 additions & 3 deletions cmis/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

class CmisController(http.Controller):

@http.route('/web/cmis/field/init_value', type='json', methods=['POST'],
@http.route('/web/cmis/field/create_value', type='json', methods=['POST'],
auth="user")
@main.serialize_exception
def init_field_value(self, model_name, res_id, field_name):
def create_field_value(self, model_name, res_id, field_name):
model_inst = http.request.env[model_name].browse(int(res_id))
value = model_inst._fields[field_name].init_value(model_inst)
model_inst._fields[field_name].create_value(model_inst)
value = getattr(model_inst, field_name)
response = werkzeug.Response(json.dumps(
{'value': value}), mimetype='application/json')
return response
6 changes: 0 additions & 6 deletions cmis/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,3 @@ class CMISError(UserError):
"""CMIS Error!"""
def __init__(self, value):
super(CMISError, self).__init__(value)


class CMISConnectionError(CMISError):
"""CMIS connection Error!"""
def __init__(self, value):
super(CMISConnectionError, self).__init__(value)
201 changes: 137 additions & 64 deletions cmis/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,172 @@


class CmisFolder(fields.Field):
""" A reference to a cmis:folder. The reference must be formatted as
follow: backend_name + ':' cmis:objectId
""" A reference to a cmis:folder. (cmis:objectId)
:param backend_name:
The attribute ``backend_name`` is mandatory
The attribute ``backend_name`` is mandatory
:param allow_create: (by default True)
:param allow_delete: (by default False)
:param cmis_create_method:
:param cmis_path: (by default backend.initial_directory_write + '/' model._name)
:param cmis_name_get: (by default instance.name_get)
:param create_method: name of a method that create the field into the
CMIS repository. The method must assign the field on all records of the
invoked recordset. The method is called with the field definition
instance and the bakend as paramaters
(optional)
:param create_parent_get: name of a method that return the cmis:objectId of
the folder to use as parent. The method is called with the field
definition instance and the bakend as paramaters.
(optional: by default the folder is
created as child of backend.initial_directory_write + '/' model._name)
:rtype: dict
:return: a dictionay with an entry for each record of the invoked
recordset with the following structure ::
{record.id: 'cmis:objectId'}
:param create_name_get: name of a method that return the name of the
folder to create into the CMIS repository. The method is called with
the field definition instance and the bakend as paramaters.
(optional: by default instance.name_get)
:rtype: dict
:return: a dictionay with an entry for each record of the invoked
recordset with the following structure ::
{record.id: 'name'}
:parem create_properties_get: name of a method that return a dictionary of
CMIS properties ro use to create the folder. The method is called
with the field definition instance and the bakend as paramaters
(optional: default empty)
:rtype: dict
:return: a dictionay with an entry for each record of the invoked
recordset with the following structure ::
{record.id: {'cmis:xxx': 'val1', ...}}
"""
type = 'char' # Postgresl
widget = 'cmis_folder' # Web widget
_slots = {
'backend_name': None,
'cmis_name_get': 'name_get',
'create_method': None,
'create_name_get': 'name_get',
'create_parent_get': None,
'create_properties_get': None,
'allow_create': True,
'allow_delete': False,
'cmis_path': None,
'cmis_create_method': None
'allow_delete': False
}

def __init__(self, backend_name=None, string=None, **kwargs):
super(CmisFolder, self).__init__(
backend_name=backend_name, string=string, **kwargs)


def get_description(self, env):
""" Return a dictionary that describes the field ``self``. """
desc = super(CmisFolder, self).get_description(env)
desc['type'] = self.widget
return desc

_description_backend_name = property(attrgetter('backend_name'))

def init_value(self, record):
if record is None:
return self # the field is accessed through the owner class

env = record.env
if not record:
# null record -> return the null value for this field
return self.null(env)

self._check_null(record)

value = None
backend = env['cmis.backend'].get_by_name(name=self.backend_name)
if self.cmis_create_method:
fct = self.cmis_create_method
def get_backend(self, records):
return records.env['cmis.backend'].get_by_name(name=self.backend_name)

def create_value(self, records):
"""Create a new folder for each record into the cmis container and
store the value as field value
"""
for record in records:
self._check_null(record)
backend = self.get_backend(records)
if self.create_method:
fct = self.create_method
if not callable(fct):
fct = getattr(record, fct)
value = fct(backend)
else:
value = self._create_in_cmis(record, backend)
self.__set__(record, value)
return value

def _create_in_cmis(self, record, backend):
name = self._get_cmis_name(record)
path = self._get_cmis_path(record, backend)
#create
parent_cmis_object = backend.get_folder_by_path(
path, create_if_not_found=True)
fct = getattr(records, fct)
fct(self, backend)
return
self._create_in_cmis(records, backend)

def _create_in_cmis(self, records, backend):
names = self.get_create_names(records, backend)
parents = self.get_create_parents(records, backend)
properties = self.get_create_properties(records, backend)
repo = backend.get_cmis_repository()
new_folder = repo.createFolder(
parent_cmis_object, name)
return new_folder.getObjectId()
for record in records:
name = names[record.id]
parent = parents[record.id]
props = properties[record.id]
value = repo.createFolder(
parent, name, props)
self.__set__(record, value.getObjectId())

def _check_null(self, record, raise_exception=True):
val = self.__get__(record, record)
if val and raise_exception:
raise UserError('A value is already assigned to %s' % self)
return val
return val

def get_create_names(self, records, backend):
"""return the names of the folders to create into the CMIS repository.
:rtype: dict
:return: a dictionay with an entry for each record with the following
structure ::
{record.id: 'name'}
def _get_cmis_name(self, record):
if self.cmis_name_get == 'name_get':
return record.name_get()[0][1]
fct = self.cmis_name_get
"""
if self.create_name_get == 'name_get':
return dict(records.name_get())
fct = self.create_name_get
if not callable(fct):
fct = getattr(record, fct)
return fct()

def _get_cmis_path(self, record, backend):
if self.cmis_path:
return self.cmis_path
else:
return '/'.join([backend.initial_directory_write,
record._name.replace('.', '_')])
fct = getattr(records, fct)
return fct(self, backend)

def get_create_parents(self, records, backend):
"""return the cmis:objectId of the cmis folder to use as parent of the
new folder.
:rtype: dict
:return: a dictionay with an entry for each record with the following
structure ::
{record.id: 'cmis:objectId'}
"""
if self.create_parent_get:
fct = self.create_parent_get
if not callable(fct):
fct = getattr(records, fct)
return fct(self, backend)
path = self.get_default_parent_path(records, backend)
parent_cmis_object = backend.get_folder_by_path(
path, create_if_not_found=True)
return dict.fromkeys(records.ids, parent_cmis_object)

def get_create_properties(self, records, backend):
"""Return the properties to use to created the folder into the CMIS
container.
:rtype: dict
:return: a dictionay with an entry for each record with the following
structure ::
{record.id: {'cmis:xxx': 'val1', ...}}
"""
if self.create_properties_get:
fct = self.create_properties_get
if not callable(fct):
fct = getattr(records, fct)
return fct(self, backend)
return dict.fromkeys(records.ids, None)

def get_default_parent_path(self, records, backend):
"""Return the default path into the cmis container to use as parent
on folder create. By default:
backend.initial_directory_write / record._name
"""
return '/'.join([backend.initial_directory_write,
records[0]._name.replace('.', '_')])
30 changes: 15 additions & 15 deletions cmis/models/cmis_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
# © 2014-2015 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
# Copyright 2016 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
import cmislib.exceptions
from cmislib.model import CmisClient
from cmislib.browser.binding import BrowserBinding
from cmislib.exceptions import ObjectNotFoundException

from openerp import api, fields, models, tools
from openerp.exceptions import Warning
from openerp.exceptions import UserError
from openerp.tools.translate import _
from ..exceptions import CMISError

_logger = logging.getLogger(__name__)


class CmisBackend(models.Model):
_name = 'cmis.backend'
Expand All @@ -30,15 +33,15 @@ class CmisBackend(models.Model):

def _clear_caches(self):
pass
#self.get_cmis_client.clear()
#self.get_by_name.clear()
self.get_cmis_client.clear()
self.get_by_name.clear()

@api.multi
def write(self, vals):
self._clear_caches()
return super(CmisBackend, self).write(vals)

#@tools.cache
@tools.cache()
@api.multi
def get_cmis_client(self):
"""
Expand All @@ -51,8 +54,8 @@ def get_cmis_client(self):
self.password,
binding=BrowserBinding())

#@tools.cache
@api.model
@tools.cache('name')
def get_by_name(self, name):
backend = self.search([('name', '=', name)])
backend.ensure_one()
Expand All @@ -71,7 +74,6 @@ def check_directory_of_write(self):
datas_fname = 'testdoc'
for this in self:
# login with the cmis account
repo = this.get_cmis_repository()
folder_path_write = this.initial_directory_write
path_write_objectid = self.get_folder_by_path(
folder_path_write,
Expand All @@ -90,10 +92,15 @@ def check_directory_of_write(self):
_("The test file already exists in the DMS. "
"Please remove it and try again."))
except cmislib.exceptions.RuntimeException:
_logger.exception("Please check your access right.")
raise CMISError(
("Please check your access right."))
self.get_error_for_path(path_write_objectid != False,
folder_path_write)
if path_write_objectid is not False:
raise UserError(_("Path is correct for : %s") %
path_write_objectid)
else:
raise CMISError(_("Error path for : %s") %
path_write_objectid)

@api.multi
def get_folder_by_path(self, path, create_if_not_found=True,
Expand All @@ -120,13 +127,6 @@ def get_folder_by_path(self, path, create_if_not_found=True,
root = new_root
return root

def get_error_for_path(self, is_valid, path):
"""Return following the boolean the right error message"""
if is_valid:
raise Warning(_("Path is correct for : %s") % path)
else:
raise CMISError(_("Error path for : %s") % path)

def sanitize_input(self, file_name):
"""Prevent injection by escaping: '%_"""
file_name = file_name.replace("'", r"\'")
Expand Down
3 changes: 2 additions & 1 deletion cmis/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
# © 2014-2015 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import test_cmis_backend
from . import test_fields
from . import test_controllers
from . import test_controllers
4 changes: 2 additions & 2 deletions cmis/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def _init_test_model(cls, model_cls):
@classmethod
def setUpClass(cls):
super(BaseTestCmis, cls).setUpClass()
# mock commit since it"s called in the _auto_init method
# mock commit since it"s called in the _auto_init method
cls.cr.commit = mock.MagicMock()
cls.cmis_test_model = cls._init_test_model(models.CmisTestModel)
cls.cmis_backend = cls.env['cmis.backend'].create({
Expand All @@ -33,4 +33,4 @@ def setUpClass(cls):
'username': 'admin',
'password': 'admin',
'initial_directory_write': '/odoo',
})
})

0 comments on commit d7421a5

Please sign in to comment.