# © 2019 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging import xml.etree.ElementTree as et import requests from cmislib.exceptions import ( ContentAlreadyExistsException, InvalidArgumentException, ObjectNotFoundException, UpdateConflictException, ) from odoo import _, exceptions, models _logger = logging.getLogger(__name__) class CgscopAlfresco(models.AbstractModel): """Appelle l'API alfresco et implémente les fonctions suivantes : alfresco_list_docs : liste les documents """ _name = "cgscop.alfresco" _description = "Connecteur Alfresco" def alfresco_get_by_url(self, url, call_type, json=None, files=None): """Création de la requête à Alfresco :param url: action API Alfresco :param call_type: post/get :param json: data to post :param files: byte to upload :param data: data to post @return response au format JSON """ _logger.info("Calling %s" % url) param = self.env["cmis.backend"].sudo().search([["active", "=", True]]) if not param: raise exceptions.UserError( _("La connexion avec Alfresco n'est pas configurée !") ) alfresco_url = param.url alfresco_ssl = param.ssl basicAuthCredentials = (param.username, param.password) try: if call_type == "get": response = requests.get( alfresco_url + url, auth=basicAuthCredentials, verify=alfresco_ssl, timeout=15, ) elif call_type == "post": response = requests.post( alfresco_url + url, auth=basicAuthCredentials, json=json, files=files, verify=alfresco_ssl, timeout=15, ) elif call_type == "put": response = requests.put( alfresco_url + url, auth=basicAuthCredentials, data=json, files=files, verify=alfresco_ssl, timeout=15, ) elif call_type == "delete": response = requests.delete( alfresco_url + url, auth=basicAuthCredentials, json=json, verify=alfresco_ssl, timeout=15, ) except Exception as err: _logger.warning( "Erreur de connexion. URL: %s", err.__str__(), ) raise exceptions.ValidationError(err.__str__()) # Gestion erreur API if response.status_code not in [200, 201]: try: message = response.json().get("message") except Exception: message = response.text raise exceptions.ValidationError( _( "L'appel url '%s' a échoué\n" "- Code erreur : %d\n" "- Message : %s" % (response.url, response.status_code, message) ) ) return response # Get Methods def alfresco_list_docs(self, node_id): """Liste les documents pour un organisme (raison sociale) :param name: raison sociale de la structure """ url = "/alfresco/s/erp/listedocuments?nodeId=" + node_id return self.alfresco_get_by_url(url=url, call_type="get").json() def alfresco_list_type(self): """Liste le valeurs du formulaire de dépôt d'un nouveau document @return: dict des valeurs possibles pour période, validité, type """ url = "/alfresco/s/api/properties?nsp=crm&n=document" list_type = self.alfresco_get_by_url(url=url, call_type="get").json() return { "periode": self.get_allowed_values( list_type=list_type, value="crm:periode" ), "type": self.get_allowed_values(list_type=list_type, value="crm:type"), } def alfresco_get_doc(self, nodeRef): """Retourne le contenu d'un document en base64 :param nodeRef: id Alfresco """ url = "/alfresco/s/document/" + nodeRef return self.alfresco_get_by_url(url=url, call_type="get").json() def alfresco_get_ticket(self, username): """Liste les documents pour un organisme (raison sociale) :param username: username de l'utilisateur """ url = "/alfresco/s/authentifier-entantque?compte=" + username result = self.alfresco_get_by_url(url=url, call_type="get") tree = et.ElementTree(et.fromstring(result.content)) root = tree.getroot() return root[0].text # Post Methods def alfresco_create_organism(self, partner): """Création d'un dossier Organisme :param partner: objet Odoo créé @return: id Alfresco (nodeRef) du dossier """ url = "/alfresco/s/erp/createdossierorganisme" json = { "raisonSociale": partner.name, "id": partner.id, "siret": partner.siret, "numAdherent": partner.member_number, "ur": partner.ur_id, } return self.get_nodeRef( self.alfresco_get_by_url(url=url, call_type="post", json=json).json() ) def alfresco_update_organism(self, partner): """Mise à jour d'un dossier Organisme :param partner: objet Odoo créé @return: id Alfresco (nodeRef) du dossier """ url = "/alfresco/s/erp/updatedossierorganisme/" + partner.id_alfresco json = { "raisonSociale": partner.name, "id": partner.id, "siret": partner.siret, "numAdherent": partner.member_number, "ur": partner.ur_id, } return self.get_nodeRef( self.alfresco_get_by_url(url=url, call_type="post", json=json).json() ) # Delete Methods def alfresco_remove(self, nodeRef): """Supprime un document d'Alfresco :param id_doc: id du document Alfresco """ url = "/alfresco/s/document/" + nodeRef return self.alfresco_get_by_url(url=url, call_type="delete").json() # Others def get_allowed_values(self, list_type, value): """Fonction qui itère dans l'objet retourné""" list_selected = list(filter(lambda i: i["name"] == value, list_type)) return ( list_selected[0] .get("constraints")[0] .get("parameters")[0] .get("allowedValues") ) def get_nodeRef(self, result): """Renvoie la référence Alfresco d'un JSON contenant au premier niveau la clé "nodeRef" puis supprime le chemin du workspace """ return result.get("nodeRef", "").replace("workspace://SpacesStore/", "") def push_alfresco_file(self, file, name, metadata=None, backend=None, folder=None): """ Ajoute un fichier sur la GED Alfresco @return: fonction get_partner_files() de res.partner """ if not backend: backend = self.env["cmis.backend"].search([], limit=1) if not folder: raise exceptions.ValidationError( _("Le dossier parent n'existe pas ou n'est pas renseigné !") ) cmis_rep = backend.get_cmis_repository() cmis_obj = cmis_rep.getObject(folder) _logger.info("Chargement document Alfresco : %s" % name) try: file_upload = cmis_obj.createDocument( name=name, properties=metadata, contentFile=file, contentType="application/pdf", ) return file_upload except UpdateConflictException: _logger.info("Le document %s existe déjà dans le dossier spécifié" % name) # Recherche noderef document existant children = cmis_obj.getChildren().getResults() old_cmis_file = list(filter(lambda f: f.name == name, children))[0] # Création d'une copie de travail du document pour versionnage pwc = old_cmis_file.checkout() # Mise à jour de la version pwc.checkin( contentFile=file, contentType="application/pdf", properties=metadata ) except ( ContentAlreadyExistsException, ObjectNotFoundException, InvalidArgumentException, ) as e: _logger.error(e) raise exceptions.ValidationError( _( "L'appel à Alfresco à échoué :\n- Code: %s\n\n- Url\n%s\n\n- Détail\n%s" ) % (e.status, e.url, e.details) ) except Exception as e: raise exceptions.ValidationError(e) def push_alfresco_file_api(self, file, name, metadata=None, folder=None): """ Ajoute un fichier sur la GED Alfresco @return: fonction get_partner_files() de res.partner """ if not folder: raise exceptions.ValidationError( _("Le dossier parent n'existe pas ou n'est pas renseigné !") ) # url = "/nodes/%s/children" % folder url = ( "/alfresco/api/-default-/public/alfresco/versions/1/nodes/%s/children?majorVersion=true" % folder ) _logger.info("Chargement document Alfresco : %s" % name) content_type = "multipart/form-data" params = { "name": name, "nodeType": "cmis:document", "overwrite": True, "properties": metadata, } files = {"filedata": (name, file, content_type, params)} return self.alfresco_get_by_url(url=url, call_type="post", files=files).json()