From 064ea6bbb1f290d0ce8694a64d1f1f8614927e17 Mon Sep 17 00:00:00 2001
From: benjamin <benjamin@le-filament.com>
Date: Tue, 22 Nov 2022 11:33:13 +0100
Subject: [PATCH] [add] wizard for downloading file or folders

---
 __init__.py                             |   2 +-
 __manifest__.py                         |   5 +
 controllers/main.py                     |  17 +++
 models/api_alfresco.py                  |  38 +++++-
 security/ir.model.access.csv            |   2 +
 wizard/__init__.py                      |   4 +
 wizard/alfresco_download_zip_wizard.py  | 163 ++++++++++++++++++++++++
 wizard/alfresco_download_zip_wizard.xml |  97 ++++++++++++++
 8 files changed, 323 insertions(+), 5 deletions(-)
 create mode 100644 security/ir.model.access.csv
 create mode 100644 wizard/__init__.py
 create mode 100644 wizard/alfresco_download_zip_wizard.py
 create mode 100644 wizard/alfresco_download_zip_wizard.xml

diff --git a/__init__.py b/__init__.py
index 311c9ed..f9327db 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 1916523..e668907 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 859ebd1..af65d4c 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 0c45cc5..b80f809 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 0000000..67e7da0
--- /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 0000000..1820733
--- /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 0000000..2e91705
--- /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 0000000..a8bb6cd
--- /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>
-- 
GitLab