Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIG] Migration to 16.0 : field cmis_document #185

Open
wants to merge 60 commits into
base: 16.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8bc1144
cmis document widget
pierre-halleux Feb 25, 2021
bc94053
cmis_field: add method to get cmis object to cmis_document field
pierre-halleux Apr 8, 2022
ce948b1
cmis_web: allow adding document when no value
pierre-halleux May 30, 2022
1442ee8
document field: adapt fix to handle multiple backends
pierre-halleux Mar 26, 2021
6f0f53a
cmis_field: remove file decoding from document creation
pierre-halleux Sep 15, 2022
3b8484c
cmis_field: properly decode file before adding new document
pierre-halleux Sep 27, 2022
3e836e8
[MIG] field cmis_document: migration to 16.0
benwillig Dec 13, 2023
dfcec84
add a created column
pierre-halleux Mar 26, 2021
6b9e289
Update cmis_web.pot
May 31, 2022
ab4c04d
cmis document widget
pierre-halleux Feb 25, 2021
ce09f42
refactoring of cmis_document widget
pierre-halleux Feb 25, 2021
1d782c2
cmis_web: add preview to cmis_document
pierre-halleux Mar 9, 2021
6b8ce17
cmis_web: add versions to cmis_document
pierre-halleux Mar 11, 2021
ecc7cc3
cmis_web: Add Download to cmis_document
pierre-halleux Jun 8, 2021
88b45ef
Hide dead buttons (context and alfresco) until fixed
pierre-halleux Jul 27, 2021
ad37a22
cmis_web: make cmis_document widget usable in a treeview
pierre-halleux Feb 24, 2022
10190b1
cmis_document: remove file name from widget
pierre-halleux Mar 31, 2022
80b8278
cmis_document: add link to alfresco
pierre-halleux Mar 31, 2022
8aa843d
stop event propagation when clicking on widget
pierre-halleux Apr 14, 2022
411fa75
Hide version selection untill fixed
pierre-halleux Apr 14, 2022
800d867
add actions context button
pierre-halleux Apr 21, 2022
1304107
fix version update issues
pierre-halleux Apr 25, 2022
afa3e37
FieldCmisDocument cleanup
pierre-halleux May 6, 2022
5b5a4f3
cmis document: add version selection
pierre-halleux May 9, 2022
96817b3
cmis_web: allow adding document when no value
pierre-halleux May 30, 2022
addadc4
update cmis_web translations
pierre-halleux Jun 10, 2022
77b1f94
cmis_web: improve selection style
pierre-halleux Jun 13, 2022
0d3cff7
cmis_web: createdate removal after bad rebase
pierre-halleux Jun 20, 2022
c762813
pre-commit
pierre-halleux Jun 21, 2022
936c2ab
- cmis document: dropdown menu should be visible in tree view
sbejaoui Aug 31, 2022
879f6f0
- cmis document: set the new name into the widget after acceptence by…
sbejaoui Aug 31, 2022
047d649
- cmis_document: manage version visibilty
sbejaoui Aug 31, 2022
14e184c
- cmis document: add option to display file name
sbejaoui Aug 31, 2022
a572c7d
- cmis_document: stop event propagationat more action click
sbejaoui Sep 5, 2022
529edf0
- fix add document to cmis_folder
sbejaoui Sep 13, 2022
3a4cfd2
cmis document: dropdown menu visibility
pierre-halleux Sep 22, 2022
56dcba5
cmis_web: add import new document action
pierre-halleux Sep 27, 2022
6ec238c
cmis_web:document: add version history
pierre-halleux Sep 29, 2022
b3c170b
cmis_web: improve safety of the checkout process
pierre-halleux Sep 29, 2022
f348b0b
cmis document: add show document details action
pierre-halleux Oct 4, 2022
a7d2d33
cmis_web: add possibility to link widget with an existing document
pierre-halleux Mar 17, 2023
27b0134
pre-commit
pierre-halleux Mar 22, 2023
5807553
Prevent to trigger a reload if there was an error during the cmis upload
benwillig Apr 6, 2023
f7a1c99
changed the way to display the document data to prevent the UI to be …
benwillig May 10, 2023
23d98cb
updated translations
benwillig May 10, 2023
6dff25e
allow the dropdown actions to be visible when the widget is included …
benwillig May 22, 2023
c0493e8
hide the dropdown once the user entered and leaved the area or when c…
benwillig May 22, 2023
9301cbf
cmis_web: fix rendering of FieldCmisDocument In some cases the render…
jdoutreloux Aug 16, 2023
2bc6c13
cmis_web: make cmis document available only in readonly mode If the u…
jdoutreloux Aug 22, 2023
96a8b6c
cmis_document: allow to search documents when linking to an existing …
benwillig Dec 1, 2023
d89a1a4
allow to delete the link with the current document
benwillig Dec 4, 2023
4ea84f2
added translations
benwillig Dec 12, 2023
70d0569
[WIP] field cmis_document migration to 16.0
benwillig Dec 20, 2023
34820f0
[ADD] allow to link with an existing document
benwillig Jan 4, 2024
b02d8ff
[FIX] fix cased when there was no source folder to search on
benwillig Feb 28, 2024
e65921b
[FIX] stopPropagation of the click event to avoid opening the popup w…
benwillig Mar 29, 2024
b0a0008
[CHG] perform search when using enter key + prevent refresh when clic…
benwillig May 28, 2024
d9b7c31
[ADD] allow to hide optional columns on cmis table + better handling …
benwillig May 30, 2024
1bac173
[FIX] dialogs title translations
benwillig May 30, 2024
3c42c2b
[FIX] cmis_web_proxy, cmis_web_alf: save object token and use it with…
benwillig Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cmis_field/controllers/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import base64
from io import BytesIO

from odoo import http


Expand All @@ -12,3 +15,32 @@ def create_field_value(self, model_name, res_id, field_name):
model_inst._fields[field_name].create_value(model_inst)
value = getattr(model_inst, field_name)
return {"value": value}

@http.route('/web/cmis/field/create_document_value', type='json', methods=['POST'],
auth="user")
def create_document_field_value(self, model_name, res_id, field_name, documents):
if documents:
self._decode_files(documents)
model_inst = http.request.env[model_name].browse(int(res_id))
model_inst._fields[field_name].create_value(model_inst, {res_id: documents[0]})
value = getattr(model_inst, field_name)
return {'value': value}
return {'value': None}

@http.route(
'/web/cmis/field/cmis_document/get_document_parent',
type='json',
methods=['POST'],
auth="user"
)
def get_document_parent(self, model_name, res_id, backend_id, field_name):
env = http.request.env
record = env[model_name].browse(int(res_id))
backend = env["cmis.backend"].browse(int(backend_id))
return record._fields[field_name].get_create_parents(record, backend)[record.id]

def _decode_files(self, documents):
for doc in documents:
file_ = doc.get("data")
_, content = file_.split(",")
doc["data"] = BytesIO(base64.b64decode(content))
1 change: 1 addition & 0 deletions cmis_field/fields/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .cmis_document import CmisDocument
from .cmis_folder import CmisFolder
202 changes: 202 additions & 0 deletions cmis_field/fields/cmis_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Copyright 2020 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import threading
import time
from functools import partial
from odoo import api, fields, registry, SUPERUSER_ID, _
from odoo.exceptions import UserError
from odoo.tools.sql import pg_varchar
from cmislib.exceptions import ObjectNotFoundException


class CmisDocument(fields.Field):
type = "cmis_document"
column_type = ("varchar", pg_varchar())
backend_name = None
create_method = None
create_parent_get = None
create_properties_get = None
copy = False # noderef are not copied by default

def __init__(self, **kwargs):
self.backend_name = kwargs.get("backend_name")
super().__init__(**kwargs)

def _is_registry_loading_mode(self, env):
"""
Check if we are in the installation process.
"""
return env.context.get("install_mode")

def get_backend(self, env, raise_if_not_found=True):
return env['cmis.backend'].get_by_name(
self.backend_name, raise_if_not_found)

def _description_backend(self, env):
if self.inherited:
# In the case of a cmis field inherited from another module
# the attribute backend_name is not inherited so we have to
# get it on the original fiel
backend = self.inherited_field.get_backend(env, raise_if_not_found=False)
else:
backend = self.get_backend(env, raise_if_not_found=False)
if len(backend) > 1:
if self._is_registry_loading_mode(env):
# While the registry is loading, specific attributes are not available
# on the field (such as `backend_name`). At this stage, the fields
# are accessed to validate the xml views of the module being
# loaded/updated. We can therefore safely takes the first backend
# into the list.
backend = backend[:1]
else:
msg = (_('Too many backend found. '
'Please check your configuration.'))
return {'backend_error': msg}
if not backend:
if self.backend_name:
msg = (_('Backend named %s not found. '
'Please check your configuration.') %
self.backend_name)
else:
msg = _('No backend found. Please check your configuration.')
return {'backend_error': msg}
result = backend.get_web_description()[backend.id]
return result

def get_cmis_object(self, record):
"""Returns an instance of
:class:`cmislib.browser.binding.BrowserDocument`
This instance is a proxy object that can be used to perform action on
the document into the cmis container
:param record:
"""
val = self.__get__(record, record)
if not val:
return None
backend = self.get_backend(record.env)
repo = backend.get_cmis_repository()
return repo.getObject(val)

def create_value(self, records, documents):
"""Create a new document for each record into the cmis container and
store the value as field value

:param records: An odoo recordset
:param documents: A mapping of files to their corresponding record as a dict of
dicts. The key of the parent dict should be the id of the corresponding record,
while each child dict should contain a 'name' key for the name of the file,
a 'mimetype' key for its mimetype and a 'data' key for its content (bytes).
:type documents: dict[:int, dict['name': str, 'mimetype': str, 'file': bytes]]

"""
for record in records:
self._check_null(record)
self._create_value(records, documents)

def _create_value(self, records, documents):
backend = self.get_backend(records.env)
if self.create_method:
fct = self.create_method
if not callable(fct):
fct = getattr(records, fct)
fct(self, backend)
return
self._create_in_cmis(records, backend, documents)

def _create_in_cmis(self, records, backend, documents):
parents = self.get_create_parents(records, backend)
properties = self.get_create_properties(records, backend)
for record in records:
document = documents[record.id]
name = document.get("name")
backend.is_valid_cmis_name(name, raise_if_invalid=True)
parent = parents[record.id]
props = properties[record.id] or {}
object_id = parent.createDocument(
name,
properties=props,
contentFile=document.get("data"),
contentType=document.get("mimetype"),
)

def clean_up_document(cmis_object_id, backend_id, dbname):
db_registry = registry(dbname)
with api.Environment.manage(), db_registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
backend = env["cmis.backend"].browse(backend_id)
_repo = backend.get_cmis_repository()
# The rollback is delayed by an arbitrary length of time to give
# the GED time to create the folder. If the folder is not properly
# created at the time the rollback executes, it cannot be deleted.
time.sleep(0.5)
try:
_repo.getObject(cmis_object_id).delete()
except ObjectNotFoundException:
pass

# remove created resource in case of rollback
test_mode = getattr(threading.current_thread(), 'testing', False)
if not test_mode:
record.env.cr.postrollback.add(
partial(
clean_up_document,
object_id,
backend.id,
record.env.cr.dbname
)
)

self.__set__(record, object_id)

def get_create_parents(self, records, backend):
"""return the cmis:objectId of the cmis folder to use as parent of the
new folder.
:rtype: dict
:return: a dictionay with an entry for each record with the following
structure ::

{record.id: 'cmis:objectId'}

"""
if self.create_parent_get:
fct = self.create_parent_get
if not callable(fct):
fct = getattr(records, fct)
return fct(self, backend)
path_parts = self.get_default_parent_path_parts(records, backend)
parent_cmis_object = backend.get_folder_by_path_parts(
path_parts, create_if_not_found=True)
return dict.fromkeys(records.ids, parent_cmis_object)

def get_default_parent_path_parts(self, records, backend):
"""Return the default path parts into the cmis container to use as
parent on folder create. By default:
backend.initial_directory_write / record._name
"""
path_parts = backend.initial_directory_write.split('/')
path_parts.append(records[0]._name.replace('.', '_'))
return path_parts

def get_create_properties(self, records, backend):
"""Return the properties to use to created the folder into the CMIS
container.
:rtype: dict
:return: a dictionay with an entry for each record with the following
structure ::

{record.id: {'cmis:xxx': 'val1', ...}}

"""
if self.create_properties_get:
fct = self.create_properties_get
if not callable(fct):
fct = getattr(records, fct)
return fct(self, backend)
return dict.fromkeys(records.ids, None)

def _check_null(self, record, raise_exception=True):
val = self.__get__(record, record)
if val and raise_exception:
raise UserError(_('A value is already assigned to %s') % self)
return val
4 changes: 2 additions & 2 deletions cmis_field/models/ir_model_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ class IrModelFields(models.Model):
_inherit = "ir.model.fields"

ttype = fields.Selection(
selection_add=[("cmis_folder", "CMIS Folder")],
ondelete={"cmis_folder": "cascade"},
selection_add=[('cmis_folder', 'CMIS Folder'), ('cmis_document', 'CMIS Document')],
ondelete = {"cmis_folder": "cascade", "cmis_document": "cascade"},
)
2 changes: 1 addition & 1 deletion cmis_web/CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Changelog

.. Future (?)
.. ~~~~~~~~~~
..
..
.. *
3 changes: 1 addition & 2 deletions cmis_web/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
# Copyright 2016-2023 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import mixins
4 changes: 4 additions & 0 deletions cmis_web/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"web.assets_backend": [
"/cmis_web/static/lib/cmisjs/superagent.7.2.0.js",
"/cmis_web/static/lib/cmisjs/cmis.0.3.1.js",
"/cmis_web/static/src/cmis_document/*",
"/cmis_web/static/src/cmis_document_link_existing/*",
"/cmis_web/static/src/cmis_document_search/*",
"/cmis_web/static/src/cmis_folder/cmis_folder.js",
"/cmis_web/static/src/cmis_folder/cmis_folder.scss",
"/cmis_web/static/src/cmis_folder/cmis_folder.xml",
Expand All @@ -40,6 +43,7 @@
"/cmis_web/static/src/cmis_attachment_viewer/cmis_attachment_viewer.js",
"/cmis_web/static/src/cmis_attachment_viewer/cmis_attachment_viewer.xml",
"/cmis_web/static/src/cmis_object_wrapper_service.js",
"/cmis_web/static/src/cmis_utils.js",
]
},
}
Loading
Loading