diff --git a/__init__.py b/__init__.py index 311c9edac9731273aa9175579ffd9777f58f7a7b..f9327dbb4a5d3bcf541198b1cc7463c3fe8aa051 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ # © 2019 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import controllers, models +from . import controllers, models, wizard diff --git a/__manifest__.py b/__manifest__.py index 1916523bfe895ce76bb4b234abf0c0718c004bf8..e668907817bc5744543122fae4092c88300c6ca2 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -12,6 +12,11 @@ "cmis_web", ], "data": [ + # security + "security/ir.model.access.csv", + # wizard + "wizard/alfresco_download_zip_wizard.xml", + # views "views/assets.xml", "views/cmis_backend.xml", ], diff --git a/controllers/main.py b/controllers/main.py index 859ebd14d45a388caaba065be5870056f0b8c5ae..af65d4c7c643277c0e17cd745ad467cae2208520 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -1,8 +1,12 @@ # © 2020 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import base64 + from odoo import http +from odoo.addons.web.controllers.main import content_disposition, serialize_exception + class AlfrescoController(http.Controller): @http.route(["/web/alfresco/session"], type="json", auth="user", method=["POST"]) @@ -21,6 +25,19 @@ class AlfrescoController(http.Controller): "backend_location": backend.location, } + @http.route( + ["/web/alfresco/download-zip"], type="http", auth="user", method=["GET"] + ) + @serialize_exception + def get_alfresco_download_zip(self, node): + doc = http.request.env["cgscop.alfresco"].alfresco_get_doc(node) + headers = [ + ("Content-Type", "application/octet-stream"), + ("Content-Disposition", content_disposition(doc.get("name"))), + ] + data = base64.b64decode(doc.get("base64NodeContent")) + return http.request.make_response(data, headers=headers) + # ------------------------------------------------------ # Override parent # ------------------------------------------------------ diff --git a/models/api_alfresco.py b/models/api_alfresco.py index 0c45cc5f842dac07b07f1f0d57e963b9ee04de56..b80f8090c1435a5bbcef75016c928531b4cd3ecf 100644 --- a/models/api_alfresco.py +++ b/models/api_alfresco.py @@ -75,7 +75,6 @@ class CgscopAlfresco(models.AbstractModel): timeout=15, ) except Exception as err: - print(err) _logger.warning( "Erreur de connexion. URL: %s", err.__str__(), @@ -83,7 +82,7 @@ class CgscopAlfresco(models.AbstractModel): raise exceptions.ValidationError(err.__str__()) # Gestion erreur API - if response.status_code not in [200, 201]: + if response.status_code not in [200, 201, 202]: try: message = response.json().get("message") except Exception: @@ -142,6 +141,27 @@ class CgscopAlfresco(models.AbstractModel): root = tree.getroot() return root[0].text + def alfresco_get_node(self, node): + """ + Appelle l'API Core Alfresco + Permet de télécharger un noeud de téléchargement + :params download_id: string - id du noeud + """ + url = "/alfresco/api/-default-/public/alfresco/versions/1/nodes/" + node + return self.alfresco_get_by_url(url=url, call_type="get").json() + + def alfresco_get_download(self, download_id): + """ + Appelle l'API Core Alfresco + Permet d'afficher l'état'un noeud de téléchargement + :params download_id: string - id du noeud + """ + url = ( + "/alfresco/api/-default-/public/alfresco/versions/1/downloads/" + + download_id + ) + return self.alfresco_get_by_url(url=url, call_type="get").json() + # ------------------------------------------------------ # API POST Methods # ------------------------------------------------------ @@ -191,8 +211,8 @@ class CgscopAlfresco(models.AbstractModel): ) url = ( - "/alfresco/api/-default-/public/alfresco/versions/1/nodes/%s/children?majorVersion=true" - % folder + "/alfresco/api/-default-/public/alfresco/versions" + "/1/nodes/%s/children?majorVersion=true" % folder ) _logger.info("Chargement document Alfresco : %s" % name) @@ -215,6 +235,16 @@ class CgscopAlfresco(models.AbstractModel): url = "/alfresco/api/-default-/public/search/versions/1/search" return self.alfresco_get_by_url(url=url, call_type="post", json=query).json() + def alfresco_create_download(self, nodes): + """ + Appelle l'API Core Alfresco + Permet de créer un noeud de téléchargement en fonction d'une liste de noderef + :params nodes: liste de noderef + """ + url = "/alfresco/api/-default-/public/alfresco/versions/1/downloads" + json = {"nodeIds": nodes} + return self.alfresco_get_by_url(url=url, call_type="post", json=json).json() + # ------------------------------------------------------ # API DELETE Methods # ------------------------------------------------------ diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000000000000000000000000000000000000..67e7da056e135b7a6bd6dec49062fe882077fbff --- /dev/null +++ b/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_alfresco_download_zip_wizard_admin,access_alfresco_download_zip_wizard_admin,model_alfresco_download_zip_wizard,base.group_erp_manager,1,1,1,1 diff --git a/wizard/__init__.py b/wizard/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1820733f036d799c21bf0a409cc50be6986a1bc0 --- /dev/null +++ b/wizard/__init__.py @@ -0,0 +1,4 @@ +# © 2019 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import alfresco_download_zip_wizard diff --git a/wizard/alfresco_download_zip_wizard.py b/wizard/alfresco_download_zip_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..2e91705d0cf38bb295bd12265e847a0ff768bf57 --- /dev/null +++ b/wizard/alfresco_download_zip_wizard.py @@ -0,0 +1,163 @@ +# © 2019 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import time + +from odoo import _, fields, models +from odoo.exceptions import UserError + +_logger = logging.getLogger(__name__) + + +class AlfrescoDownloadZipWizard(models.TransientModel): + _name = "alfresco.download.zip.wizard" + _description = "Wizard de téléchargement de fichers" + + # ------------------------------------------------------ + # Fields declaration + # ------------------------------------------------------ + # Odoo coops + partner_select = fields.Selection( + [ + ("query", "Par caractéristique"), + ("partner", "Sélectionner des coopératives"), + ], + string="Type de sélection", + default="query", + ) + partner_ids = fields.Many2many( + comodel_name="res.partner", + string="Coopératives", + domain=[("is_cooperative", "=", True)], + ) + cooperative_form_ids = fields.Many2many( + relation="alfresco_download_wizard_form_rel", + comodel_name="res.partner.cooperative.form", + column1="alfresco_wizard_form_id", + column2="coop_form_id", + string="Forme coopérative", + ) + company_type_ids = fields.Many2many( + relation="alfresco_download_wizard_type_rel", + comodel_name="res.partner.company.type", + column1="alfresco_wizard_type_id", + column2="coop_type_id", + string="Forme juridique", + ) + is_cae = fields.Boolean(string="CAE") + is_member = fields.Boolean(string="Adhérents") + ur_id = fields.Many2many( + relation="alfresco_download_wizard_ur_rel", + comodel_name="union.regionale", + column1="alfresco_wizard_ur_id", + column2="ur_id", + string="Union Régionale", + ) + + # Alfresco Metadata + download_type = fields.Selection( + [ + ("file", "Fichiers"), + ("folder", "Dossiers"), + ], + string="Type de téléchargement", + default="file", + ) + year = fields.Char("Année") + context = fields.Char("Contexte") + type = fields.Char("Type") + + # ------------------------------------------------------ + # Button functions + # ------------------------------------------------------ + def get_zip(self): + download_type = ( + "crm:document" if self.download_type == "file" else "cmis:folder" + ) + query = ( + "SELECT * FROM %s as d " + "JOIN crm:organisme as o ON d.cmis:objectId = o.cmis:objectId WHERE " + ) % (download_type,) + # Sélection des coopératives + coops = self._get_partner_ids() + # Tableau des variables + variables = [ + { + "value": ", ".join("'" + str(p) + "'" for p in coops), + "clause": "o.crm:id in (%s) ", + }, + ] + if self.download_type == "file": + variables += [ + {"value": self.year, "clause": "d.crm:annee = '%s' "}, + {"value": self.context, "clause": "d.crm:contexte = '%s' "}, + {"value": self.type, "clause": "d.crm:type = '%s' "}, + ] + # Suppression des variables nulles + clauses = list(filter(None, [x if x.get("value") else None for x in variables])) + if not clauses: + raise UserError(_("La requête est trop large et non permise")) + # Création de la requête + for index, clause in enumerate(clauses): + if index == 0: + query += clause.get("clause") % clause.get("value") + else: + query += ("and " + clause.get("clause")) % clause.get("value") + # Recherche des documents + Alfresco = self.env["cgscop.alfresco"] + search_result = Alfresco.alfresco_query_search( + { + "query": { + "query": query, + "language": "cmis", + }, + "paging": { + "maxItems": "10000", + }, + } + ) + nodes = [x["entry"].get("id") for x in search_result["list"].get("entries")] + if not nodes: + raise UserError(_("Il n'y a pas de documents associés à cette recherche")) + + download = Alfresco.alfresco_create_download(nodes) + download_id = download["entry"].get("id") + download_get = Alfresco.alfresco_get_download(download_id) + download_status = download_get["entry"].get("status") + while download_status != "DONE": + time.sleep(0.5) + download_get = Alfresco.alfresco_get_download(download_id) + download_status = download_get["entry"].get("status") + logging.info("Statut du téléchargement : %s" % download_status) + + return { + "type": "ir.actions.act_url", + "url": "/web/alfresco/download-zip?node=" + download_id, + "target": "new", + } + + # ------------------------------------------------------ + # Common functions + # ------------------------------------------------------ + def _get_partner_ids(self): + if self.partner_select == "partner": + if not self.partner_ids: + raise UserError(_("Vous devez sélectionner des coopératives.")) + return self.partner_ids.ids + if self.partner_select == "query": + domain = [("is_cooperative", "=", True)] + if self.cooperative_form_ids: + domain.append( + ("cooperative_form_id", "in", self.cooperative_form_ids.ids) + ) + if self.company_type_ids: + domain.append( + ("partner_company_type_id", "in", self.company_type_ids.ids) + ) + if self.is_cae: + domain.append(("cae", "=", True)) + if self.is_member: + domain.append(("membership_status", "=", "member")) + partner_ids = self.env["res.partner"].search(domain) + return partner_ids.ids diff --git a/wizard/alfresco_download_zip_wizard.xml b/wizard/alfresco_download_zip_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..a8bb6cd97b02440f24c21f3aff6fad15f5f15b72 --- /dev/null +++ b/wizard/alfresco_download_zip_wizard.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" ?> +<!-- Copyright 2019 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> +<odoo> + <data> + + <!-- Form view --> + <record id="alfresco_download_zip_wizard_view_form" model="ir.ui.view"> + <field name="name">alfresco.download.zip.wizard.view.form</field> + <field name="model">alfresco.download.zip.wizard</field> + <field name="arch" type="xml"> + <form string="Téléchargement de fichiers Alfresco"> + <sheet> + <group string="Coopératives" name="coop"> + <field name="partner_select" widget="radio" /> + <field + name="partner_ids" + widget="many2many_tags" + options="{'no_create': 1, 'no_edit': 1}" + attrs="{'invisible': [('partner_select', '!=', 'partner')]}" + /> + <field + name="cooperative_form_ids" + widget="many2many_tags" + options="{'no_create': 1, 'no_edit': 1}" + attrs="{'invisible': [('partner_select', '!=', 'query')]}" + /> + <field + name="company_type_ids" + widget="many2many_tags" + options="{'no_create': 1, 'no_edit': 1}" + attrs="{'invisible': [('partner_select', '!=', 'query')]}" + /> + <field + name="is_cae" + attrs="{'invisible': [('partner_select', '!=', 'query')]}" + /> + <field + name="is_member" + attrs="{'invisible': [('partner_select', '!=', 'query')]}" + /> + <field + name="ur_id" + widget="many2many_tags" + options="{'no_create': 1, 'no_edit': 1}" + attrs="{'invisible': [('partner_select', '!=', 'query')]}" + /> + </group> + <group string="Métadonnées" name="metadata"> + <field name="download_type" widget="radio" /> + <field + name="year" + attrs="{'invisible': [('download_type', '!=', 'file')]}" + /> + <field + name="context" + attrs="{'invisible': [('download_type', '!=', 'file')]}" + /> + <field + name="type" + attrs="{'invisible': [('download_type', '!=', 'file')]}" + /> + </group> + <footer> + <button + name="get_zip" + type="object" + string="Télécharger les documents" + class="btn-primary" + /> + <button special="cancel" string="Annuler" /> + </footer> + </sheet> + </form> + </field> + </record> + + <!-- Action --> + <record id="alfresco_download_zip_wizard_action" model="ir.actions.act_window"> + <field name="name">Télécharger des documents Alfresco</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">alfresco.download.zip.wizard</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <!-- Menu --> + <menuitem + id="alfresco_download_zip_wizard_menu" + name="Télécharger des documents Alfresco" + parent="cmis.cmis_root_menu" + action="alfresco_download_zip_wizard_action" + sequence="90" + /> + + </data> +</odoo>