diff --git a/cmis_write/__init__.py b/cmis_write/__init__.py new file mode 100644 index 00000000..15102914 --- /dev/null +++ b/cmis_write/__init__.py @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import ir_attachment +from . import metadata + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/cmis_write/__openerp__.py b/cmis_write/__openerp__.py new file mode 100644 index 00000000..d1e6ec1f --- /dev/null +++ b/cmis_write/__openerp__.py @@ -0,0 +1,88 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'CMIS Write', + 'version': '0.1', + 'category': 'Knowledge Management', + 'summary': 'Create Document in DMS from OpenERP', + 'description': """ +Add Documents from OpenERP +========================== + +This module allows you to store OpenERP document in the DMS repository. + +Configuration +============= + +* Create a new CMIS backend with the host, login and password. +* Configure the path in the repository where documents will be dropped. + By default, it uses the home directory of the user. + +Usage +===== + +* On one OpenERP record, click "Add document". +* Upload your documents +* Uploaded documents will be enqueued for storage in the DMS + +Add Metadata +============ + +To manage a custom aspect using CMIS (and all the other supported ways) +you have to: + +* Define a new custom model configuring Alfresco. To do this I suggest you +http://wiki.alfresco.com/wiki/Step-By-Step:_Creating_A_Custom_Model. + +* Add the custom aspect to the document you upload or create in Alfresco. +Using CMIS I suggest you: +http://docs.alfresco.com/4.1/index.jsp?topic=%2Fcom.alfresco.enterprise.doc%2Fconcepts%2Fopencmis-ext-adding.html. + +* Set the custom property in the way you probably know using CMIS. + +Contributors +------------ +* El Hadji Dem (elhadji.dem@savoirfairelinux.com) +""", + 'author': 'Savoir-faire Linux', + 'website': 'www.savoirfairelinux.com', + 'license': 'AGPL-3', + 'depends': [ + 'document', + 'cmis', + ], + 'data': [ + 'metadata_view.xml', + 'security/ir.model.access.csv', + ], + 'js': [ + 'static/src/js/document.js' + ], + 'qweb': [], + 'test': [], + 'demo': [], + 'installable': True, + 'auto_install': False, +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/cmis_write/i18n/cmis_write.pot b/cmis_write/i18n/cmis_write.pot new file mode 100644 index 00000000..c33e42bb --- /dev/null +++ b/cmis_write/i18n/cmis_write.pot @@ -0,0 +1,89 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * cmis_write +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-03-21 14:59+0000\n" +"PO-Revision-Date: 2014-03-21 10:59-0500\n" +"Last-Translator: EL Hadji DEM \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 1.5.4\n" + +#. module: cmis_write +#: model:_description:0 model:ir.model,name:cmis_write.model_metadata_list +msgid "List of Metadata" +msgstr "" + +#. module: cmis_write +#: field:metadata,name:0 +msgid "Name" +msgstr "" + +#. module: cmis_write +#: model:ir.actions.act_window,name:cmis_write.action_metadata +#: model:ir.ui.menu,name:cmis_write.menu_action_metadata +msgid "Metadata Editing" +msgstr "" + +#. module: cmis_write +#: field:metadata,metadata_list_ids:0 +msgid "List of fields" +msgstr "" + +#. module: cmis_write +#: field:metadata,field_ids:0 field:metadata.list,field_id:0 +msgid "Fields" +msgstr "" + +#. module: cmis_write +#: view:metadata:0 +msgid "Object" +msgstr "" + +#. module: cmis_write +#: view:metadata:0 +msgid "Metadata list fields form" +msgstr "" + +#. module: cmis_write +#: help:ir.attachment,id_edm:0 +msgid "Id of Edm." +msgstr "" + +#. module: cmis_write +#: field:ir.attachment,id_edm:0 +msgid "Id of Dms" +msgstr "" + +#. module: cmis_write +#: view:metadata:0 +msgid "Metadata list fields Tree" +msgstr "" + +#. module: cmis_write +#: field:metadata,model_id:0 +msgid "Model" +msgstr "" + +#. module: cmis_write +#: field:metadata,model_ids:0 +msgid "Model List" +msgstr "" + +#. module: cmis_write +#: model:_description:0 model:ir.model,name:cmis_write.model_ir_attachment +msgid "ir.attachment" +msgstr "" + +#. module: cmis_write +#: model:_description:0 model:ir.model,name:cmis_write.model_metadata +#: field:metadata.list,metadata_id:0 +msgid "Metadata" +msgstr "" diff --git a/cmis_write/ir_attachment.py b/cmis_write/ir_attachment.py new file mode 100644 index 00000000..8426c96b --- /dev/null +++ b/cmis_write/ir_attachment.py @@ -0,0 +1,223 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from openerp.addons.connector.session import ConnectorSession +from openerp.addons.connector.queue.job import job +import base64 +from openerp import SUPERUSER_ID +from openerp.tools.translate import _ +import logging +_logger = logging.getLogger(__name__) + + +class ir_attachment_download(orm.TransientModel): + _name = 'ir.attachment.download' + + _columns = { + 'name': fields.char('Attachment Name', size=256, required=True, + help='Attachment Name'), + 'datas': fields.binary('File', readonly=True), + 'type': fields.char('Type', size=256, help='Type'), + 'file_type': fields.char('Content Type', help='Content Type'), + 'attachment_id': fields.many2one('ir.attachment', 'Attachment'), + } + _defaults = { + 'type': 'binary', + } + + +class ir_attachment(orm.Model): + _inherit = 'ir.attachment' + + def create(self, cr, uid, values, context=None): + metadata_obj = self.pool.get('metadata') + user_obj = self.pool.get('res.users') + user_login = user_obj.browse(cr, uid, uid, context=context).login + session = ConnectorSession(cr, uid, context=context) + value = { + 'name': values.get('name'), + 'datas_fname': values.get('datas_fname'), + 'file_type': values.get('file_type') or '', + 'datas': values.get('datas'), + 'description': values.get('description') or '', + + } + + metadata_ids = metadata_obj.search(cr, uid, [], context=context) + dict_metadata = {} + list_fields = [] + # Get list of metadata + if values.get('res_model'): + for line in metadata_obj.browse(cr, uid, metadata_ids, + context=context): + if line.model_id.model == values.get('res_model'): + if line.metadata_list_ids: + for one_field in line.metadata_list_ids: + list_fields.append(one_field.field_id.name) + result = self.pool.get(values.get('res_model')).read(cr, uid, [ + values.get('res_id')], list_fields, context=context)[0] + + for one_field in list_fields: + dict_metadata['cmis:' + one_field] = result[one_field] + values['datas'] = None + res = super(ir_attachment, self).create(cr, uid, values, + context=context) + # Create Job + # if bool_testdoc in context, we don't need to create + # the doc in the DMS + if not context.get('bool_testdoc'): + create_doc_in_edm.delay( + session, 'ir.attachment', value, res, dict_metadata, + user_login) + return res + + def action_download(self, cr, uid, ids, context=None): + if context is None: + context = {} + cmis_backend_obj = self.pool.get('cmis.backend') + # login with the cmis account + repo = cmis_backend_obj._auth(cr, uid, context=context) + cmis_backend_rec = self.read( + cr, uid, ids, ['id_dms'], context=context)[0] + id_dms = cmis_backend_rec['id_dms'] + # Get results from id of document + results = repo.query(" SELECT * FROM cmis:document WHERE \ + cmis:objectId ='" + id_dms + "'") + datas = results[0].getContentStream().read().encode('base64') + return datas + + def _data_set(self, cr, uid, id, name, value, arg, context=None): + # We dont handle setting data to null + if not value: + return True + if context is None: + context = {} + location = self.pool.get('ir.config_parameter').get_param( + cr, uid, 'ir_attachment.location') + file_size = len(value.decode('base64')) + if location: + attach = self.browse(cr, uid, id, context=context) + if attach.store_fname: + self._file_delete(cr, uid, location, attach.store_fname) + fname = self._file_write(cr, uid, location, value) + # SUPERUSER_ID as probably don't have write access, + # trigger during create + super(ir_attachment, self).write( + cr, SUPERUSER_ID, [id], + {'store_fname': fname, 'file_size': file_size}, + context=context) + else: + super(ir_attachment, self).write( + cr, SUPERUSER_ID, [id], + {'db_datas': value, 'file_size': file_size}, context=context) + return True + + def _data_get(self, cr, uid, ids, name, arg, context=None): + if context is None: + context = {} + result = {} + location = self.pool.get('ir.config_parameter').get_param( + cr, uid, 'ir_attachment.location') + bin_size = context.get('bin_size') + for attach in self.browse(cr, uid, ids, context=context): + if location and attach.store_fname: + result[attach.id] = self._file_read( + cr, uid, location, attach.store_fname, bin_size) + elif attach.id_dms: + datas = self.action_download( + cr, uid, attach.id, context=context) + result[attach.id] = datas + file_type, index_content = self._index( + cr, uid, datas.decode('base64'), attach.datas_fname, None) + self.write( + cr, uid, [attach.id], + {'file_type': file_type, 'index_content': index_content}, + context=context) + else: + raise orm.except_orm(_('Access error of document'), + _("Document is not available in DMS; " + "Please try again")) + return result + + _columns = { + 'id_dms': fields.char('Id of Dms', size=256, help="Id of Dms."), + 'download_id': fields.one2many('ir.attachment.download', + 'attachment_id', + 'Attachment download'), + 'datas': fields.function(_data_get, fnct_inv=_data_set, + string='File Content', + type="binary", nodrop=True), + } + + +@job +def create_doc_in_edm(session, model_name, value, res, + dict_metadata, user_login, filters=None): + ir_attach_obj = session.pool.get('ir.attachment') + cmis_backend_obj = session.pool.get('cmis.backend') + if session.context is None: + session.context = {} + # login with the cmis account + repo = cmis_backend_obj._auth(session.cr, session.uid, + context=session.context) + root = repo.rootFolder + ids = cmis_backend_obj.search(session.cr, session.uid, [], session.context) + + folder_path = cmis_backend_obj.read( + session.cr, session.uid, + ids, + ['initial_directory_write'], + context=session.context)[0]['initial_directory_write'] + # Document properties + if value['name']: + file_name = value['name'] + elif value['datas_fname']: + file_name = value['datas_fname'] + else: + file_name = value['datas_fname'] + props = { + 'cmis:name': file_name, + 'cmis:description': value['description'], + 'cmis:createdBy': user_login, + } + # Add list of metadata in props + if len(dict_metadata): + for k, v in dict_metadata.iteritems(): + props[k] = v + if folder_path: + sub1 = repo.getObjectByPath(folder_path) + else: + sub1 = root + someDoc = sub1.createDocumentFromString(file_name, + contentString=base64.b64decode( + value['datas']), + contentType=value['file_type']) + # TODO: create custom properties on a document (Alfresco) + # someDoc.getProperties().update(props) + # Updating ir.attachment object with the new id + # of document generated by DMS + ir_attach_obj.write(session.cr, session.uid, res, { + 'id_dms': someDoc.getObjectId()}, session.context) + return True + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/cmis_write/metadata.py b/cmis_write/metadata.py new file mode 100644 index 00000000..3ab11414 --- /dev/null +++ b/cmis_write/metadata.py @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields + + +class metadata_list(orm.Model): + _description = 'List of Metadata' + _name = 'metadata.list' + + _columns = { + 'field_id': fields.many2one('ir.model.fields', 'Fields'), + 'metadata_id': fields.many2one('metadata', 'Metadata'), + } + + +class metadata(orm.Model): + _name = "metadata" + _description = "Metadata" + _columns = { + 'name': fields.char("Name", size=64, required=True, select=1), + 'model_id': fields.many2one('ir.model', 'Model', + required=True, select=1), + 'field_ids': fields.many2many('ir.model.fields', 'metadata_field_rel', + 'meta_id', 'field_id', 'Fields'), + 'metadata_list_ids': fields.one2many('metadata.list', 'metadata_id', + 'List of fields'), + 'model_ids': fields.many2many('ir.model', string='Model List'), + } + + def onchange_model(self, cr, uid, ids, model_id, context=None): + if context is None: + context = {} + if not model_id: + return {'value': {'model_ids': [(6, 0, [])]}} + model_ids = [model_id] + model_obj = self.pool.get('ir.model') + active_model_obj = self.pool.get(model_obj.browse( + cr, uid, model_id, context=context).model) + if active_model_obj._inherits: + for key, val in active_model_obj._inherits.items(): + found_model_ids = model_obj.search(cr, + uid, [('model', '=', key)], + context=context) + model_ids += found_model_ids + return {'value': {'model_ids': [(6, 0, model_ids)]}} + +# vim:expandtab:smartindent:toabstop=4:softtabstop=4:shiftwidth=4: diff --git a/cmis_write/metadata_view.xml b/cmis_write/metadata_view.xml new file mode 100644 index 00000000..46548711 --- /dev/null +++ b/cmis_write/metadata_view.xml @@ -0,0 +1,54 @@ + + + + + + metadata.form + metadata + form + +
+ + + + + + + + + + + + + +
+ + + metadata.tree + metadata + form + + + + + + + + + + Metadata Editing + + metadata + tree,form + + + + +
+
diff --git a/cmis_write/security/ir.model.access.csv b/cmis_write/security/ir.model.access.csv new file mode 100644 index 00000000..4ea92c2d --- /dev/null +++ b/cmis_write/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_metadata_list_group_user,metadata.list user,model_metadata_list,base.group_document_user,1,1,1,1 +access_metadata,access_metadata,model_metadata,base.group_document_user,1,1,1,1 diff --git a/cmis_write/static/src/js/document.js b/cmis_write/static/src/js/document.js new file mode 100644 index 00000000..524594a9 --- /dev/null +++ b/cmis_write/static/src/js/document.js @@ -0,0 +1,42 @@ +openerp.cmis_write = function(instance, m) { +var _t = instance.web._t, + QWeb = instance.web.qweb; + + instance.web.Sidebar.include({ + start: function() { + var self = this; + this._super(this); + this.redraw(); + this.$el.on('click','.oe_dropdown_menu li a', function(event) { + var section = $(this).data('section'); + var index = $(this).data('index'); + var item = self.items[section][index]; + if (item.callback) { + item.callback.apply(self, [item]); + } else if (item.action) { + self.on_item_action_clicked(item); + } else if (!item.id_dms) { + alert(_t("Document is not available in DMS.Please try again !!!")); + } else if (item.url) { + return true; + } + event.preventDefault(); + }); + }, + do_attachement_update: function(dataset, model_id, args) { + var self = this; + this.dataset = dataset; + this.model_id = model_id; + if (args && args[0].error) { + this.do_warn(_t('Uploading Error'), args[0].error); + } + if (!model_id) { + this.on_attachments_loaded([]); + } else { + var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ]; + var ds = new instance.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom); + ds.read_slice(['name', 'url', 'id_dms','type', 'create_uid', 'create_date', 'write_uid', 'write_date'], {}).done(this.on_attachments_loaded); + } + }, + }); +}; diff --git a/cmis_write/tests/__init__.py b/cmis_write/tests/__init__.py new file mode 100644 index 00000000..4189b3d0 --- /dev/null +++ b/cmis_write/tests/__init__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014 Savoir-faire Linux (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import ( + test_attachment, +) + +checks = [ + test_attachment, +] diff --git a/cmis_write/tests/test_attachment.py b/cmis_write/tests/test_attachment.py new file mode 100644 index 00000000..ac75f727 --- /dev/null +++ b/cmis_write/tests/test_attachment.py @@ -0,0 +1,71 @@ +# -*- encoding: utf-8 -*- +############################################################################### +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp.tests.common import TransactionCase +from openerp.addons.connector.session import ConnectorSession + + +class test_attachment(TransactionCase): + + def setUp(self): + super(test_attachment, self).setUp() + # Clean up registries + self.registry('ir.model').clear_caches() + self.registry('ir.model.data').clear_caches() + # Get registries + self.user_model = self.registry("res.users") + self.ir_attachment_model = self.registry("ir.attachment") + self.partner_model = self.registry('res.partner') + self.metadata_model = self.registry('metadata') + # Get context + self.context = self.user_model.context_get(self.cr, self.uid) + + partner_id = self.partner_model.create( + self.cr, self.uid, + {'name': 'Test Partner', + 'email': 'test@localhost', + 'is_company': True, + }, context=None) + + blob1 = 'blob1' + blob1_b64 = blob1.encode('base64') + + self.vals = { + 'name': 'a1', + 'datas': blob1_b64, + 'attachment_document_ids': [(0, 0, { + 'res_model': "res.partner", + 'res_id': partner_id, + 'res_name': 'Test Partner', + })], + } + + def test_create_test_attachment(self): + cr, uid, vals, context = self.cr, self.uid, self.vals, self.context + vals['datas'] = None + context['bool_testdoc'] = True + ir_attachment_id = self.ir_attachment_model.create( + cr, uid, vals, context=context) + ir_attachment_pool = self.ir_attachment_model.browse( + cr, uid, ir_attachment_id, context=context) + + self.assertEqual(ir_attachment_pool.name, vals['name'])