diff --git a/.travis.yml b/.travis.yml index 853f814d..32371ae3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,8 @@ install: - pip install --upgrade paramiko - pip install --upgrade pyth - travis_install_nightly + - sudo pip install paramiko + - sudo pip install fs script: - travis_run_tests diff --git a/attachment_metadata/README.rst b/attachment_metadata/README.rst new file mode 100644 index 00000000..f9640f29 --- /dev/null +++ b/attachment_metadata/README.rst @@ -0,0 +1,51 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License + +Attachment Metadata +====================== + +This module was written to extend the functionality of ir.attachment + +Installation +============ + +Installable without any requirements + +Usage +===== + +THe module just add some field to ir.attachment + +For further information, please visit: + +* https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + +Credits +======= + +* Joel Grand-Guillaume Camptocamp +* initOS +* Valentin CHEMIERE + +Contributors +------------ + +* Sebastien BEAU + +Maintainer +---------- + +* Valentin CHEMIERE + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/attachment_metadata/__init__.py b/attachment_metadata/__init__.py new file mode 100644 index 00000000..3e53cd60 --- /dev/null +++ b/attachment_metadata/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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 attachment diff --git a/attachment_metadata/__openerp__.py b/attachment_metadata/__openerp__.py new file mode 100644 index 00000000..232b1f84 --- /dev/null +++ b/attachment_metadata/__openerp__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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': 'attachment_metadata', + 'version': '0.0.1', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'description': """ + Add some useful field to ir.attachment object like: + internal and external hash for coherence verification + """, + 'depends': [ + 'base', + 'mail' + ], + 'data': [ + 'attachment_view.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, + 'application': False, + } diff --git a/attachment_metadata/attachment.py b/attachment_metadata/attachment.py new file mode 100644 index 00000000..b75ff47a --- /dev/null +++ b/attachment_metadata/attachment.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright 2011-2012 Camptocamp SA +# @author: Joel Grand-Guillaume +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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 import models, fields, api, _ +from openerp.exceptions import Warning +import hashlib +from base64 import b64decode + + +class IrAttachmentMetadata(models.Model): + _name = 'ir.attachment.metadata' + _inherits = {'ir.attachment': 'attachment_id'} + + internal_hash = fields.Char(store=True, compute='_compute_hash') + external_hash = fields.Char() + attachment_id = fields.Many2one('ir.attachment', required=True, + ondelete='cascade') + + @api.depends('datas', 'external_hash') + def _compute_hash(self): + if self.datas: + self.internal_hash = hashlib.md5(b64decode(self.datas)).hexdigest() + if self.external_hash and self.internal_hash != self.external_hash: + raise Warning(_('File corrupted'), + _("Something was wrong with the retreived file, " + "please relaunch the task.")) diff --git a/attachment_metadata/attachment_view.xml b/attachment_metadata/attachment_view.xml new file mode 100644 index 00000000..a9553559 --- /dev/null +++ b/attachment_metadata/attachment_view.xml @@ -0,0 +1,17 @@ + + + + + + ir.attachment.metadata + + + + + + + + + + + diff --git a/attachment_metadata/security/ir.model.access.csv b/attachment_metadata/security/ir.model.access.csv new file mode 100644 index 00000000..9b01638c --- /dev/null +++ b/attachment_metadata/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_attachment_metadata_user,ir.attachment.metadata.user,model_ir_attachment_metadata,base.group_user,1,0,0,0 diff --git a/external_file_location/README.rst b/external_file_location/README.rst new file mode 100644 index 00000000..d245fa00 --- /dev/null +++ b/external_file_location/README.rst @@ -0,0 +1,59 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License + +External File Location +====================== + +This module was written to extend the functionality of ir.attachment to support remote communication and allow you to import/export file to a remote server + +Installation +============ + +To install this module, you need to: + +* fs python module +* Paramiko python module + +Usage +===== + +To use this module, you need to: + +* Add a location with your server infos +* Create a task with your file info and remote communication method +* A cron task will trigger each task + +For further information, please visit: + +* https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + + +Credits +======= + +* Joel Grand-Guillaume Camptocamp +* initOS +* Valentin CHEMIERE + +Contributors +------------ + +* Sebastien BEAU + +Maintainer +---------- + +* Valentin CHEMIERE + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/external_file_location/__init__.py b/external_file_location/__init__.py new file mode 100644 index 00000000..101a9f4a --- /dev/null +++ b/external_file_location/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# 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 attachment +from . import location +from . import task +from . import tasks +from . import tests diff --git a/external_file_location/__openerp__.py b/external_file_location/__openerp__.py new file mode 100644 index 00000000..8d286d4e --- /dev/null +++ b/external_file_location/__openerp__.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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': 'external_file_location', + 'version': '0.0.1', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'depends': [ + 'attachment_metadata', + ], + 'external_dependencies': { + 'python': [ + 'fs', + 'paramiko', + ], + }, + 'data': [ + 'menu.xml', + 'attachment_view.xml', + 'location_view.xml', + 'task_view.xml', + 'cron.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, + 'application': True, + } diff --git a/external_file_location/abstract_task.py b/external_file_location/abstract_task.py new file mode 100644 index 00000000..15e2323e --- /dev/null +++ b/external_file_location/abstract_task.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from base64 import b64encode + + +class AbstractTask(object): + + _name = None + _key = None + _synchronize_type = None + _default_port = None + _hide_login = False + _hide_password = False + _hide_port = False + + def create_file(self, filename, data): + ir_attachment_id = self.env['ir.attachment.metadata'].create({ + 'name': filename, + 'datas': b64encode(data), + 'datas_fname': filename, + 'task_id': self.task and self.task.id or False, + 'location_id': self.task and self.task.location_id.id or False, + 'external_hash': self.ext_hash + }) + return ir_attachment_id diff --git a/external_file_location/attachment.py b/external_file_location/attachment.py new file mode 100644 index 00000000..26925547 --- /dev/null +++ b/external_file_location/attachment.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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 import models, fields + + +class IrAttachmentMetadata(models.Model): + _inherit = 'ir.attachment.metadata' + + sync_date = fields.Datetime() + state = fields.Selection([ + ('pending', 'Pending'), + ('failed', 'Failed'), + ('done', 'Done'), + ], readonly=False, required=True, default='pending') + state_message = fields.Text() + task_id = fields.Many2one('external.file.task', string='Task') + location_id = fields.Many2one('external.file.location', string='Location', + related='task_id.location_id', store=True + ) diff --git a/external_file_location/attachment_view.xml b/external_file_location/attachment_view.xml new file mode 100644 index 00000000..b11b6c4f --- /dev/null +++ b/external_file_location/attachment_view.xml @@ -0,0 +1,99 @@ + + + + + + ir.attachment.metadata + + + + + + + + + + + + + + ir.attachment.metadata + + + + + + + + + + + + + + + ir.attachment.metadata + + + + + + + + + + + + + + + + + + + + + + + + + + Attachments + ir.actions.act_window + ir.attachment.metadata + form + tree,form + + + + + + + + tree + + + + + + + form + + + + + + + + diff --git a/external_file_location/cron.xml b/external_file_location/cron.xml new file mode 100644 index 00000000..a18f5f6a --- /dev/null +++ b/external_file_location/cron.xml @@ -0,0 +1,18 @@ + + + + + + Run file exchange tasks + 30 + minutes + -1 + True + + external.file.task + _run + ([]) + + + + diff --git a/external_file_location/helper.py b/external_file_location/helper.py new file mode 100644 index 00000000..6bfe08dc --- /dev/null +++ b/external_file_location/helper.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Joel Grand-Guillaume +# Copyright 2011-2012 Camptocamp SA +# +# 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 . +# +############################################################################## + + +def itersubclasses(cls, _seen=None): + """ + itersubclasses(cls) + Generator over all subclasses of a given class, in depth first order. + >>> list(itersubclasses(int)) == [bool] + True + >>> class A(object): pass + >>> class B(A): pass + >>> class C(A): pass + >>> class D(B,C): pass + >>> class E(D): pass + >>> + >>> for cls in itersubclasses(A): + ... print(cls.__name__) + B + D + E + C + >>> # get ALL (new-style) classes currently defined + >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS + ['type', ...'tuple', ...] + """ + if not isinstance(cls, type): + raise TypeError('itersubclasses must be called with ' + 'new-style classes, not %.100r' % cls + ) + if _seen is None: + _seen = set() + try: + subs = cls.__subclasses__() + except TypeError: # fails only when cls is type + subs = cls.__subclasses__(cls) + for sub in subs: + if sub not in _seen: + _seen.add(sub) + yield sub + for sub in itersubclasses(sub, _seen): + yield sub diff --git a/external_file_location/location.py b/external_file_location/location.py new file mode 100644 index 00000000..91d4ad2f --- /dev/null +++ b/external_file_location/location.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE +# +# 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 import models, fields, api +from .abstract_task import AbstractTask +from .helper import itersubclasses + + +class Location(models.Model): + _name = 'external.file.location' + _description = 'Description' + + name = fields.Char(string='Name', required=True) + protocol = fields.Selection(selection='_get_protocol', required=True) + address = fields.Char(string='Address', required=True) + port = fields.Integer() + login = fields.Char() + password = fields.Char() + task_ids = fields.One2many('external.file.task', 'location_id') + hide_login = fields.Boolean() + hide_password = fields.Boolean() + hide_port = fields.Boolean() + + def _get_protocol(self): + res = [] + for cls in itersubclasses(AbstractTask): + if not cls._synchronize_type: + cls_info = (cls._key, cls._name) + res.append(cls_info) + elif not cls._synchronize_type and cls._key and cls._name: + pass + return res + + @api.onchange('protocol') + def onchange_protocol(self): + for cls in itersubclasses(AbstractTask): + if cls._key == self.protocol: + self.port = cls._default_port + if cls._hide_login: + self.hide_login = True + else: + self.hide_login = False + if cls._hide_password: + self.hide_password = True + else: + self.hide_password = False + if cls._hide_port: + self.hide_port = True + else: + self.hide_port = False diff --git a/external_file_location/location_view.xml b/external_file_location/location_view.xml new file mode 100644 index 00000000..5d2ee0a9 --- /dev/null +++ b/external_file_location/location_view.xml @@ -0,0 +1,69 @@ + + + + + + external.file.location + +
+ + +
+
+ + + + + + + + + + + + + + + +