Skip to content

Commit

Permalink
[18.0][MIG] dms: Migration to 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kobros-tech committed Jan 26, 2025
1 parent e8e75ff commit 58ffd79
Show file tree
Hide file tree
Showing 34 changed files with 453 additions and 401 deletions.
14 changes: 9 additions & 5 deletions dms/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ Document Management System
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fdms-lightgray.png?logo=github
:target: https://github.com/OCA/dms/tree/17.0/dms
:target: https://github.com/OCA/dms/tree/18.0/dms
:alt: OCA/dms
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/dms-17-0/dms-17-0-dms
:target: https://translation.odoo-community.org/projects/dms-18-0/dms-18-0-dms
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/dms&target_branch=17.0
:target: https://runboat.odoo-community.org/builds?repo=OCA/dms&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|
Expand Down Expand Up @@ -180,7 +180,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/dms/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/dms/issues/new?body=module:%20dms%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/dms/issues/new?body=module:%20dms%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Expand Down Expand Up @@ -216,6 +216,10 @@ Contributors

- Timothée Vannier <tva@subteno.com>

- `Kencove <https://www.kencove.com>`__:

- Mohamed Alkobrosli <malkobrosly@kencove.com>

Other credits
-------------

Expand All @@ -240,6 +244,6 @@ 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.

This module is part of the `OCA/dms <https://github.com/OCA/dms/tree/17.0/dms>`_ project on GitHub.
This module is part of the `OCA/dms <https://github.com/OCA/dms/tree/18.0/dms>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 1 addition & 1 deletion dms/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"name": "Document Management System",
"summary": """Document Management System for Odoo""",
"version": "17.0.1.2.1",
"version": "18.0.1.0.0",
"category": "Document Management",
"license": "LGPL-3",
"website": "https://github.com/OCA/dms",
Expand Down
1 change: 0 additions & 1 deletion dms/data/onboarding_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
-->
<odoo noupdate="1">

<!-- Steps (has to be fist so that onboarding panel can access it) -->
<record id="onboarding_step_document_storage" model="onboarding.onboarding.step">
<field name="title">Storage</field>
Expand Down
1 change: 1 addition & 0 deletions dms/demo/category.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<odoo noupdate="1">
<record id="category_01_demo" model="dms.category">
<field name="name">Internal</field>
<field name="parent_id" eval="False" />
</record>
<record id="category_02_demo" model="dms.category">
<field name="name">Human Resource</field>
Expand Down
8 changes: 4 additions & 4 deletions dms/models/access_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class DmsAccessGroups(models.Model):
_parent_name = "parent_group_id"

name = fields.Char(string="Group Name", required=True, translate=True)
parent_path = fields.Char(index="btree", unaccent=False)
parent_path = fields.Char(index="btree")

# Permissions written directly on this group
perm_create = fields.Boolean(string="Create Access")
Expand Down Expand Up @@ -122,9 +122,9 @@ def _compute_inclusive_permissions(self):
for one in self:
one.update(
{
"perm_inclusive_%s" % perm: (
one["perm_%s" % perm]
or one.parent_group_id["perm_inclusive_%s" % perm]
f"perm_inclusive_{perm}": (
one[f"perm_{perm}"]
or one.parent_group_id[f"perm_inclusive_{perm}"]
)
for perm in ("create", "unlink", "write")
}
Expand Down
20 changes: 9 additions & 11 deletions dms/models/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from odoo.osv.expression import AND, OR
from odoo.tools import consteq, human_size

from odoo.addons.http_routing.models.ir_http import slugify

from ..tools.file import check_name, unique_name

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -46,7 +44,7 @@ class DmsDirectory(models.Model):
_parent_name = "parent_id"
_directory_field = _parent_name

parent_path = fields.Char(index="btree", unaccent=False)
parent_path = fields.Char(index="btree")
is_root_directory = fields.Boolean(
default=False,
help="""Indicates if the directory is a root directory.
Expand Down Expand Up @@ -216,7 +214,7 @@ def _get_domain_by_access_groups(self, operation):
"""Special rules for directories."""
self_filter = [
("storage_id_inherit_access_from_parent_record", "=", False),
("id", "inselect", self._get_access_groups_query(operation)),
("id", "in", self._get_access_groups_query(operation)),
]
# Upstream only filters by parent directory
result = super()._get_domain_by_access_groups(operation)
Expand All @@ -237,7 +235,7 @@ def _get_domain_by_access_groups(self, operation):
def _compute_access_url(self):
res = super()._compute_access_url()
for item in self:
item.access_url = "/my/dms/directory/%s" % (item.id)
item.access_url = f"/my/dms/directory/{item.id}"
return res

def check_access_token(self, access_token=False):
Expand Down Expand Up @@ -278,7 +276,7 @@ def _get_parent_categories(self, access_token):
and consteq(current_directory.access_token, access_token)
)
or not access_token
and current_directory.check_access_rights("read")
and current_directory.check_access("read")
):
return directories
current_directory = current_directory.parent_id
Expand Down Expand Up @@ -389,9 +387,8 @@ def _search_starred(self, operator, operand):
def _compute_complete_name(self):
for category in self:
if category.parent_id:
category.complete_name = "{} / {}".format(
category.parent_id.complete_name,
category.name,
category.complete_name = (
f"{category.parent_id.complete_name} / {category.name}"
)
else:
category.complete_name = category.name
Expand Down Expand Up @@ -530,7 +527,7 @@ def _onchange_model_id(self):
# Constrains
@api.constrains("parent_id")
def _check_directory_recursion(self):
if not self._check_recursion():
if self._has_cycle():
raise ValidationError(_("Error! You cannot create recursive directories."))
return True

Expand Down Expand Up @@ -628,7 +625,8 @@ def message_new(self, msg_dict, custom_values=None):
parent_directory._process_message(msg_dict)
return parent_directory
names = parent_directory.child_directory_ids.mapped("name")
subject = slugify(msg_dict.get("subject", _("Alias-Mail-Extraction")))
slug = self.env["ir.http"]._slug
subject = slug(msg_dict.get("subject", _("Alias-Mail-Extraction")))
defaults = dict(
{"name": unique_name(subject, names, escape_suffix=True)}, **custom_values
)
Expand Down
4 changes: 2 additions & 2 deletions dms/models/dms_category.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class DMSCategory(models.Model):
comodel_name="dms.category",
inverse_name="parent_id",
)
parent_path = fields.Char(index="btree", unaccent=False)
parent_path = fields.Char(index="btree")
tag_ids = fields.One2many(
string="Tags", comodel_name="dms.tag", inverse_name="category_id"
)
Expand Down Expand Up @@ -99,6 +99,6 @@ def _compute_count_files(self):

@api.constrains("parent_id")
def _check_category_recursion(self):
if not self._check_recursion():
if self._has_cycle():
raise ValidationError(_("Error! You cannot create recursive categories."))
return True
10 changes: 5 additions & 5 deletions dms/models/dms_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,14 @@ def _compute_image_1920(self):
):
one.image_1920 = one.content

def check_access_rule(self, operation):
self.mapped("directory_id").check_access_rule(operation)
return super().check_access_rule(operation)
def check_access(self, operation):
self.mapped("directory_id").check_access(operation)
return super().check_access(operation)

def _compute_access_url(self):
res = super()._compute_access_url()
for item in self:
item.access_url = "/my/dms/file/%s/download" % (item.id)
item.access_url = f"/my/dms/file/{item.id}/download"
return res

def check_access_token(self, access_token=False):
Expand Down Expand Up @@ -240,7 +240,7 @@ def _get_forbidden_extensions(self):
return [extension.strip() for extension in extensions.split(",")]

def _get_icon_placeholder_name(self):
return self.extension and "file_%s.svg" % self.extension or ""
return self.extension and f"file_{self.extension}.svg" or ""

# Actions
def action_migrate(self, should_logging=True):
Expand Down
86 changes: 58 additions & 28 deletions dms/models/dms_security_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
OR,
TRUE_DOMAIN,
)
from odoo.tools import SQL

_logger = getLogger(__name__)

Expand Down Expand Up @@ -86,10 +87,10 @@ def _compute_permissions(self):
)
return

creatable = self._filter_access_rules("create")
readable = self._filter_access_rules("read")
unlinkable = self._filter_access_rules("unlink")
writeable = self._filter_access_rules("write")
creatable = self._filtered_access("create")
readable = self._filtered_access("read")
unlinkable = self._filtered_access("unlink")
writeable = self._filtered_access("write")
for one in self:
one.update(
{
Expand Down Expand Up @@ -135,15 +136,15 @@ def _get_domain_by_inheritance(self, operation):
)
continue
# Check model access only once per batch
if not model.check_access_rights(operation, raise_exception=False):
if not model.check_access(operation):
continue
domains.append([("res_model", "=", model._name), ("res_id", "=", False)])
# Check record access in batch too
res_ids = [i for i in group["res_id"] if i] # Hack to remove None res_id
# Apply exists to skip records that do not exist. (e.g. a res.partner
# deleted by database).
model_records = model.browse(res_ids).exists()
related_ok = model_records._filter_access_rules_python(operation)
related_ok = model_records._filtered_access(operation)
if not related_ok:
continue
domains.append(
Expand All @@ -161,33 +162,53 @@ def _get_access_groups_query(self, operation):
"unlink": "AND dag.perm_inclusive_unlink",
"write": "AND dag.perm_inclusive_write",
}[operation]
select = f"""
SELECT
dir_group_rel.aid
FROM
dms_directory_complete_groups_rel AS dir_group_rel
INNER JOIN dms_access_group AS dag
ON dir_group_rel.gid = dag.id
INNER JOIN dms_access_group_users_rel AS users
ON users.gid = dag.id
WHERE
users.uid = %s {operation_check}
"""
return select, (self.env.uid,)
if operation == "read":
sql = SQL(
"""(
SELECT
dir_group_rel.aid
FROM
dms_directory_complete_groups_rel AS dir_group_rel
INNER JOIN dms_access_group AS dag
ON dir_group_rel.gid = dag.id
INNER JOIN dms_access_group_users_rel AS users
ON users.gid = dag.id
WHERE
users.uid = %s
)""",
self.env.uid,
)
else:
sql = SQL(
"""(
SELECT
dir_group_rel.aid
FROM
dms_directory_complete_groups_rel AS dir_group_rel
INNER JOIN dms_access_group AS dag
ON dir_group_rel.gid = dag.id
INNER JOIN dms_access_group_users_rel AS users
ON users.gid = dag.id
WHERE
users.uid = %s %s
)""",
self.env.uid,
operation_check,
)
return sql

@api.model
def _get_domain_by_access_groups(self, operation):
"""Get domain for records accessible applying DMS access groups."""
result = [
(
"%s.storage_id_inherit_access_from_parent_record"
% self._directory_field,
f"{self._directory_field}.storage_id_inherit_access_from_parent_record",
"=",
False,
),
(
self._directory_field,
"inselect",
"in",
self._get_access_groups_query(operation),
),
]
Expand Down Expand Up @@ -236,16 +257,26 @@ def _search_permission_unlink(self, operator, value):
def _search_permission_write(self, operator, value):
return self._get_permission_domain(operator, value, "write")

def _filter_access_rules_python(self, operation):
def _filtered_access_no_recursion(self, operation: str):
"""This method is just the same as _filtered_access
but it can not be called withoud super due to
recursion error.
"""
if self and not self.env.su and (result := self._check_access(operation)):
return self - result[0]
return self

def _filtered_access(self, operation):
# Only kept to not break inheritance; see next comment
result = super()._filter_access_rules_python(operation)
result = super()._filtered_access(operation)
# HACK Always fall back to applying rules by SQL.
# Upstream `_filter_access_rules_python()` doesn't use computed fields
# Upstream `_filtered_access()` doesn't use computed fields
# search methods. Thus, it will take the `[('permission_{operation}',
# '=', user.id)]` rule literally. Obviously that will always fail
# because `self[f"permission_{operation}"]` will always be a `bool`,
# while `user.id` will always be an `int`.
result |= self._filter_access_rules(operation)
result |= self._filtered_access_no_recursion(operation)
return result

@api.model_create_multi
Expand All @@ -258,6 +289,5 @@ def create(self, vals_list):
res.flush_recordset()
# Go back to the original sudo state and check we really had creation permission
res = res.sudo(self.env.su)
res.check_access_rights("create")
res.check_access_rule("create")
res.check_access("create")
return res
4 changes: 2 additions & 2 deletions dms/models/ir_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
class IrBinary(models.AbstractModel):
_inherit = "ir.binary"

def _find_record_check_access(self, record, access_token):
def _find_record_check_access(self, record, access_token, field):
if record._name in ("dms.file", "dms.directory"):
if record.sudo().check_access_token(access_token):
# sudo because the user might not usually have access to the record but
# now the token is valid.
# Used to display the icon in the portal.
return record.sudo()

return super()._find_record_check_access(record, access_token)
return super()._find_record_check_access(record, access_token, field)
2 changes: 1 addition & 1 deletion dms/models/mixins_thumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def _get_icon_url(self):
"""Obtain URL to record icon."""
local_path = self._get_icon_disk_path()
icon_name = os.path.basename(local_path)
return "/dms/static/icons/%s" % icon_name
return f"/dms/static/icons/{icon_name}"

@api.depends("image_128")
def _compute_icon_url(self):
Expand Down
6 changes: 3 additions & 3 deletions dms/models/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class Storage(models.Model):
name = fields.Char(required=True)
save_type = fields.Selection(
selection=[
("database", _("Database")),
("file", _("Filestore")),
("attachment", _("Attachment")),
("database", "Database"),
("file", "Filestore"),
("attachment", "Attachment"),
],
default="database",
required=True,
Expand Down
Loading

0 comments on commit 58ffd79

Please sign in to comment.