odoo-addon-dms 16.0.1.8.0.5__py3-none-any.whl → 17.0.1.0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/dms/README.rst +132 -78
- odoo/addons/dms/__manifest__.py +34 -16
- odoo/addons/dms/actions/file.xml +1 -3
- odoo/addons/dms/controllers/main.py +0 -34
- odoo/addons/dms/controllers/portal.py +178 -92
- odoo/addons/dms/data/onboarding_data.xml +77 -0
- odoo/addons/dms/demo/access_group.xml +3 -9
- odoo/addons/dms/i18n/de.po +332 -221
- odoo/addons/dms/i18n/dms.pot +137 -174
- odoo/addons/dms/i18n/es.po +355 -226
- odoo/addons/dms/i18n/fr.po +256 -342
- odoo/addons/dms/i18n/he_IL.po +208 -181
- odoo/addons/dms/i18n/it.po +369 -248
- odoo/addons/dms/i18n/nl.po +140 -168
- odoo/addons/dms/i18n/pt.po +352 -223
- odoo/addons/dms/i18n/pt_BR.po +358 -256
- odoo/addons/dms/i18n/ru.po +138 -172
- odoo/addons/dms/models/__init__.py +7 -1
- odoo/addons/dms/models/abstract_dms_mixin.py +1 -1
- odoo/addons/dms/models/access_groups.py +18 -11
- odoo/addons/dms/models/base.py +10 -2
- odoo/addons/dms/models/directory.py +46 -75
- odoo/addons/dms/models/{category.py → dms_category.py} +10 -42
- odoo/addons/dms/models/dms_file.py +125 -147
- odoo/addons/dms/models/dms_security_mixin.py +26 -22
- odoo/addons/dms/models/ir_attachment.py +0 -1
- odoo/addons/dms/models/ir_binary.py +19 -0
- odoo/addons/dms/models/mail_thread.py +2 -4
- odoo/addons/dms/models/mixins_thumbnail.py +13 -8
- odoo/addons/dms/models/onboarding_onboarding.py +16 -0
- odoo/addons/dms/models/onboarding_onboarding_step.py +50 -0
- odoo/addons/dms/models/res_company.py +22 -47
- odoo/addons/dms/models/res_config_settings.py +0 -1
- odoo/addons/dms/models/storage.py +4 -24
- odoo/addons/dms/models/tag.py +1 -1
- odoo/addons/dms/readme/CONFIGURE.md +59 -0
- odoo/addons/dms/readme/CONTRIBUTORS.md +14 -0
- odoo/addons/dms/readme/CREDITS.md +6 -0
- odoo/addons/dms/readme/DESCRIPTION.md +9 -0
- odoo/addons/dms/readme/INSTALL.md +4 -0
- odoo/addons/dms/readme/ROADMAP.md +19 -0
- odoo/addons/dms/readme/USAGE.md +11 -0
- odoo/addons/dms/security/security.xml +1 -0
- odoo/addons/dms/static/description/icon.png +0 -0
- odoo/addons/dms/static/description/icon.svg +4 -1
- odoo/addons/dms/static/description/index.html +133 -88
- odoo/addons/dms/static/description/portal_icon.svg +12 -0
- odoo/addons/dms/static/src/js/fields/{path_owl.esm.js → path_json/path_owl.esm.js} +22 -4
- odoo/addons/dms/static/src/js/fields/{path_owl.xml → path_json/path_owl.xml} +7 -4
- odoo/addons/dms/static/src/js/fields/preview_binary/preview_record.esm.js +47 -0
- odoo/addons/dms/static/src/js/{views/fields/binary → fields/preview_binary}/preview_record.xml +5 -4
- odoo/addons/dms/static/src/js/views/dms_file_upload.esm.js +155 -148
- odoo/addons/dms/static/src/js/views/file_kanban_controller.xml +0 -1
- odoo/addons/dms/static/src/js/views/file_kanban_record.esm.js +26 -27
- odoo/addons/dms/static/src/js/views/file_kanban_renderer.esm.js +1 -2
- odoo/addons/dms/static/src/js/views/file_kanban_renderer.xml +12 -16
- odoo/addons/dms/static/src/js/views/file_kanban_view.esm.js +11 -9
- odoo/addons/dms/static/src/js/views/file_list_controller.esm.js +1 -1
- odoo/addons/dms/static/src/js/views/file_list_renderer.xml +1 -7
- odoo/addons/dms/static/src/js/views/file_list_view.esm.js +11 -9
- odoo/addons/dms/static/src/js/views/search_panel.esm.js +9 -10
- odoo/addons/dms/static/src/models/attachment.esm.js +83 -82
- odoo/addons/dms/static/src/models/attachment_image.esm.js +30 -28
- odoo/addons/dms/static/src/models/attachment_viewer_viewable.esm.js +27 -25
- odoo/addons/dms/static/src/scss/directory_kanban.scss +28 -73
- odoo/addons/dms/static/src/scss/dms_common.scss +69 -0
- odoo/addons/dms/static/src/scss/file_kanban.scss +22 -34
- odoo/addons/dms/static/src/scss/portal.scss +12 -0
- odoo/addons/dms/static/tests/tours/dms_portal_tour.esm.js +46 -0
- odoo/addons/dms/{views/dms_portal_templates.xml → template/portal.xml} +13 -3
- odoo/addons/dms/tests/common.py +20 -4
- odoo/addons/dms/tests/data/mail01.eml +2 -2
- odoo/addons/dms/tests/data/mail02.eml +2 -2
- odoo/addons/dms/tests/test_benchmark.py +16 -21
- odoo/addons/dms/tests/test_directory.py +128 -40
- odoo/addons/dms/tests/test_file.py +112 -30
- odoo/addons/dms/tests/test_file_database.py +50 -24
- odoo/addons/dms/tests/test_portal.py +50 -37
- odoo/addons/dms/tests/test_storage_attachment.py +85 -63
- odoo/addons/dms/tests/test_storage_database.py +89 -35
- odoo/addons/dms/tools/file.py +63 -11
- odoo/addons/dms/views/dms_access_groups_views.xml +2 -17
- odoo/addons/dms/views/{category.xml → dms_category.xml} +4 -9
- odoo/addons/dms/views/{directory.xml → dms_directory.xml} +168 -176
- odoo/addons/dms/views/dms_file.xml +170 -191
- odoo/addons/dms/views/{tag.xml → dms_tag.xml} +50 -53
- odoo/addons/dms/views/menu.xml +52 -21
- odoo/addons/dms/views/res_config_settings.xml +31 -82
- odoo/addons/dms/views/storage.xml +7 -13
- odoo_addon_dms-17.0.1.0.0.3.dist-info/METADATA +257 -0
- {odoo_addon_dms-16.0.1.8.0.5.dist-info → odoo_addon_dms-17.0.1.0.0.3.dist-info}/RECORD +93 -95
- {odoo_addon_dms-16.0.1.8.0.5.dist-info → odoo_addon_dms-17.0.1.0.0.3.dist-info}/WHEEL +1 -1
- odoo_addon_dms-17.0.1.0.0.3.dist-info/top_level.txt +1 -0
- odoo/addons/dms/i18n/fa.po +0 -2277
- odoo/addons/dms/i18n/fa_IR.po +0 -2277
- odoo/addons/dms/readme/CONFIGURE.rst +0 -40
- odoo/addons/dms/readme/CONTRIBUTORS.rst +0 -16
- odoo/addons/dms/readme/CREDITS.rst +0 -6
- odoo/addons/dms/readme/DESCRIPTION.rst +0 -6
- odoo/addons/dms/readme/INSTALL.rst +0 -9
- odoo/addons/dms/readme/ROADMAP.rst +0 -8
- odoo/addons/dms/readme/USAGE.rst +0 -8
- odoo/addons/dms/static/src/js/components/path/path.esm.js +0 -0
- odoo/addons/dms/static/src/js/dms_portal_tour.js +0 -57
- odoo/addons/dms/static/src/js/fields/path.js +0 -81
- odoo/addons/dms/static/src/js/views/fields/binary/preview_record.esm.js +0 -35
- odoo/addons/dms/static/src/js/views/many_drop_target.js +0 -98
- odoo/addons/dms/static/src/scss/variables.scss +0 -9
- odoo/addons/dms/static/src/xml/views.xml +0 -72
- odoo/addons/dms/template/assets.xml +0 -44
- odoo/addons/dms/template/onboarding.xml +0 -97
- odoo_addon_dms-16.0.1.8.0.5.dist-info/METADATA +0 -203
- odoo_addon_dms-16.0.1.8.0.5.dist-info/top_level.txt +0 -1
@@ -1,5 +1,6 @@
|
|
1
1
|
# Copyright 2021-2022 Tecnativa - Víctor Martínez
|
2
2
|
# Copyright 2021 Tecnativa - João Marques
|
3
|
+
# Copyright 2024 Subteno - Timothée Vannier (https://www.subteno.com).
|
3
4
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
4
5
|
|
5
6
|
from odoo.tests.common import users
|
@@ -14,20 +15,25 @@ class StorageAttachmentTestCase(StorageAttachmentBaseCase):
|
|
14
15
|
def test_storage_attachment(self):
|
15
16
|
self._create_attachment("demo.txt")
|
16
17
|
self.assertTrue(
|
17
|
-
self.storage.storage_file_ids.filtered(lambda x: x.name == "demo.txt")
|
18
|
+
self.storage.storage_file_ids.filtered(lambda x: x.name == "demo.txt"),
|
19
|
+
"File should be created",
|
18
20
|
)
|
19
21
|
directory = self._get_partner_directory()
|
20
|
-
self.assertEqual(directory.count_files, 1)
|
21
|
-
self.assertEqual(
|
22
|
+
self.assertEqual(directory.count_files, 1, "Directory should have 1 file")
|
23
|
+
self.assertEqual(
|
24
|
+
directory.file_ids[0].name, "demo.txt", "File should be demo.txt"
|
25
|
+
)
|
22
26
|
file_01 = self.create_file(directory=directory)
|
23
|
-
self.assertEqual(file_01.storage_id, self.storage)
|
24
|
-
self.assertEqual(
|
25
|
-
|
26
|
-
|
27
|
+
self.assertEqual(file_01.storage_id, self.storage, "Storage should be the same")
|
28
|
+
self.assertEqual(
|
29
|
+
file_01.storage_id.save_type, "attachment", "Save type should be attachment"
|
30
|
+
)
|
31
|
+
self.assertEqual(file_01.save_type, "database", "Save type should be database")
|
32
|
+
self.assertEqual(directory.count_files, 2, "Directory should have 2 files")
|
27
33
|
# Assert cascade removal
|
28
34
|
self.partner.unlink()
|
29
|
-
self.assertFalse(file_01.exists())
|
30
|
-
self.assertFalse(directory.exists())
|
35
|
+
self.assertFalse(file_01.exists(), "File should not exist")
|
36
|
+
self.assertFalse(directory.exists(), "Directory should not exist")
|
31
37
|
|
32
38
|
@users("dms-manager")
|
33
39
|
def test_storage_attachment_misc(self):
|
@@ -35,81 +41,97 @@ class StorageAttachmentTestCase(StorageAttachmentBaseCase):
|
|
35
41
|
root_directory = self.storage.root_directory_ids.filtered(
|
36
42
|
lambda x: x.is_root_directory
|
37
43
|
)
|
38
|
-
self.assertEqual(
|
39
|
-
|
44
|
+
self.assertEqual(
|
45
|
+
root_directory.count_directories,
|
46
|
+
1,
|
47
|
+
"Root directory should have 1 directory",
|
48
|
+
)
|
49
|
+
self.assertEqual(
|
50
|
+
len(root_directory.child_directory_ids),
|
51
|
+
1,
|
52
|
+
"Root directory should have 1 child directory",
|
53
|
+
)
|
40
54
|
child_directory = root_directory.child_directory_ids[0]
|
41
|
-
self.assertEqual(
|
42
|
-
|
43
|
-
|
55
|
+
self.assertEqual(
|
56
|
+
child_directory.res_model,
|
57
|
+
self.partner_model._name,
|
58
|
+
"Model should be res.partner",
|
59
|
+
)
|
60
|
+
self.assertEqual(
|
61
|
+
child_directory.res_id, self.partner.id, "Record ID should be partner ID"
|
62
|
+
)
|
63
|
+
self.assertEqual(
|
64
|
+
len(root_directory.child_directory_ids.file_ids),
|
65
|
+
1,
|
66
|
+
"Root directory should have 1 file",
|
67
|
+
)
|
44
68
|
dms_file = root_directory.child_directory_ids.file_ids[0]
|
45
|
-
self.assertEqual(
|
46
|
-
|
47
|
-
|
48
|
-
self.assertEqual(dms_file.
|
69
|
+
self.assertEqual(
|
70
|
+
dms_file.attachment_id, attachment, "Attachment should be the same"
|
71
|
+
)
|
72
|
+
self.assertEqual(dms_file.name, "demo.txt", "Name should be demo.txt")
|
73
|
+
self.assertEqual(
|
74
|
+
dms_file.res_model, self.partner_model._name, "Model should be res.partner"
|
75
|
+
)
|
76
|
+
self.assertEqual(
|
77
|
+
dms_file.res_id, self.partner.id, "Record ID should be partner ID"
|
78
|
+
)
|
49
79
|
directory = self._get_partner_directory()
|
50
80
|
file_01 = self.create_file(directory=directory)
|
51
|
-
self.assertEqual(
|
52
|
-
|
53
|
-
|
54
|
-
self.assertEqual(
|
55
|
-
|
56
|
-
|
81
|
+
self.assertEqual(
|
82
|
+
file_01.res_model, self.partner_model._name, "Model should be res.partner"
|
83
|
+
)
|
84
|
+
self.assertEqual(
|
85
|
+
file_01.res_id, self.partner.id, "Record ID should be partner ID"
|
86
|
+
)
|
87
|
+
self.assertEqual(file_01.storage_id, self.storage, "Storage should be the same")
|
88
|
+
self.assertEqual(
|
89
|
+
file_01.storage_id.save_type, "attachment", "Save type should be attachment"
|
90
|
+
)
|
91
|
+
self.assertEqual(file_01.save_type, "database", "Save type should be database")
|
92
|
+
self.assertEqual(
|
93
|
+
self.storage.count_storage_files, 2, "Storage should have 2 files"
|
94
|
+
)
|
57
95
|
|
58
96
|
@users("dms-manager")
|
59
97
|
def test_storage_attachment_directory_record_ref_access_dms_manager(self):
|
60
98
|
self._create_attachment("demo.txt")
|
61
99
|
directory = self._get_partner_directory()
|
62
|
-
self.assertTrue(directory.permission_read)
|
63
|
-
self.assertEqual(self.partner.type, "contact")
|
64
|
-
self.
|
65
|
-
|
66
|
-
|
67
|
-
directory.invalidate_recordset()
|
68
|
-
self.assertFalse(directory.with_user(self.dms_manager_user).permission_read)
|
100
|
+
self.assertTrue(directory.permission_read, "Permission read should be True")
|
101
|
+
self.assertEqual(self.partner.type, "contact", "Type should be contact")
|
102
|
+
self.assertTrue(
|
103
|
+
directory.sudo().permission_read, "Permission read should be True"
|
104
|
+
)
|
69
105
|
|
70
106
|
@users("dms-user")
|
71
107
|
def test_storage_attachment_directory_record_ref_access_dms_user(self):
|
72
108
|
self._create_attachment("demo.txt")
|
73
109
|
directory = self._get_partner_directory()
|
74
|
-
self.assertTrue(directory.permission_read)
|
75
|
-
self.assertEqual(self.partner.type, "contact")
|
76
|
-
self.
|
77
|
-
|
78
|
-
|
110
|
+
self.assertTrue(directory.permission_read, "Permission read should be True")
|
111
|
+
self.assertEqual(self.partner.type, "contact", "Type should be contact")
|
112
|
+
self.assertTrue(
|
113
|
+
directory.sudo().permission_read, "Permission read should be True"
|
114
|
+
)
|
79
115
|
directory.invalidate_recordset()
|
80
|
-
self.
|
81
|
-
|
82
|
-
|
83
|
-
{
|
84
|
-
"groups_id": [
|
85
|
-
(
|
86
|
-
6,
|
87
|
-
0,
|
88
|
-
[
|
89
|
-
self.browse_ref("base.group_private_addresses").id,
|
90
|
-
self.browse_ref("base.group_user").id,
|
91
|
-
],
|
92
|
-
)
|
93
|
-
]
|
94
|
-
}
|
95
|
-
)
|
96
|
-
self.assertTrue(directory.permission_read)
|
116
|
+
self.assertTrue(
|
117
|
+
directory.permission_read, "Permission read should still be True"
|
118
|
+
)
|
97
119
|
|
98
120
|
@users("basic-user")
|
99
121
|
def test_storage_attachment_directory_record_ref_access_basic_user(self):
|
100
122
|
self._create_attachment("demo.txt")
|
101
123
|
directory = self._get_partner_directory()
|
102
|
-
self.assertTrue(directory.permission_read)
|
103
|
-
self.assertTrue(directory.permission_read)
|
104
|
-
self.assertEqual(self.partner.type, "contact")
|
105
|
-
self.partner.sudo().write({"type": "
|
106
|
-
self.assertEqual(self.partner.type, "
|
107
|
-
self.assertTrue(
|
108
|
-
|
109
|
-
|
124
|
+
self.assertTrue(directory.permission_read, "Permission read should be True")
|
125
|
+
self.assertTrue(directory.permission_read, "Permission read should be True")
|
126
|
+
self.assertEqual(self.partner.type, "contact", "Type should be contact")
|
127
|
+
self.partner.sudo().write({"type": "other"})
|
128
|
+
self.assertEqual(self.partner.type, "other", "Type should now be other")
|
129
|
+
self.assertTrue(
|
130
|
+
directory.sudo().permission_read, "Permission read should be True"
|
131
|
+
)
|
110
132
|
|
111
133
|
@users("basic-user")
|
112
134
|
def test_create_attachment_basic_user(self):
|
113
135
|
attachment = self._create_attachment("Test file")
|
114
|
-
self.assertEqual(attachment.name, "Test file")
|
115
|
-
self.assertTrue(self._get_partner_directory())
|
136
|
+
self.assertEqual(attachment.name, "Test file", "Name should be Test file")
|
137
|
+
self.assertTrue(self._get_partner_directory(), "Directory should exist")
|
@@ -16,11 +16,13 @@ class StorageDatabaseTestCase(StorageDatabaseBaseCase):
|
|
16
16
|
|
17
17
|
@users("dms-manager", "dms-user")
|
18
18
|
def test_count_storage_directories(self):
|
19
|
-
self.assertTrue(
|
19
|
+
self.assertTrue(
|
20
|
+
self.storage.count_storage_directories, "Storage should have directories"
|
21
|
+
)
|
20
22
|
|
21
23
|
@users("dms-manager", "dms-user")
|
22
24
|
def test_count_storage_files(self):
|
23
|
-
self.assertTrue(self.storage.count_storage_files)
|
25
|
+
self.assertTrue(self.storage.count_storage_files, "Storage should have files")
|
24
26
|
|
25
27
|
@users("dms-manager")
|
26
28
|
@mute_logger("odoo.models.unlink")
|
@@ -29,33 +31,61 @@ class StorageDatabaseTestCase(StorageDatabaseBaseCase):
|
|
29
31
|
lambda x: x.is_root_directory
|
30
32
|
)
|
31
33
|
file_01 = self.create_file(directory=root_directory)
|
32
|
-
self.assertEqual(
|
33
|
-
|
34
|
-
|
34
|
+
self.assertEqual(
|
35
|
+
file_01.storage_id, self.storage, "File should be in the storage"
|
36
|
+
)
|
37
|
+
self.assertEqual(
|
38
|
+
file_01.storage_id.save_type, "database", "Storage should be database"
|
39
|
+
)
|
40
|
+
self.assertEqual(
|
41
|
+
file_01.save_type, "database", "File savetype should be database"
|
42
|
+
)
|
35
43
|
self.storage.write({"save_type": "file"})
|
36
44
|
directory = self.create_directory(storage=self.storage)
|
37
45
|
file_02 = self.create_file(directory=directory)
|
38
|
-
self.assertEqual(
|
39
|
-
|
40
|
-
|
41
|
-
self.assertEqual(
|
42
|
-
self.assertEqual(
|
43
|
-
|
46
|
+
self.assertEqual(
|
47
|
+
file_01.storage_id, self.storage, "File should be in the storage"
|
48
|
+
)
|
49
|
+
self.assertEqual(file_01.storage_id.save_type, "file", "Storage should be file")
|
50
|
+
self.assertEqual(
|
51
|
+
file_01.save_type, "database", "File savetype should be database"
|
52
|
+
)
|
53
|
+
self.assertEqual(
|
54
|
+
file_02.storage_id, self.storage, "File should be in the storage"
|
55
|
+
)
|
56
|
+
self.assertEqual(file_02.storage_id.save_type, "file", "Storage should be file")
|
57
|
+
self.assertEqual(file_02.save_type, "file", "File savetype should be file")
|
44
58
|
self.storage.action_storage_migrate()
|
45
|
-
self.assertEqual(
|
46
|
-
|
47
|
-
|
48
|
-
self.assertEqual(
|
49
|
-
self.assertEqual(
|
50
|
-
self.assertEqual(
|
59
|
+
self.assertEqual(
|
60
|
+
file_01.storage_id, self.storage, "File should be in the storage"
|
61
|
+
)
|
62
|
+
self.assertEqual(file_01.storage_id.save_type, "file", "Storage should be file")
|
63
|
+
self.assertEqual(file_01.save_type, "file", "File savetype should be file")
|
64
|
+
self.assertEqual(
|
65
|
+
file_02.storage_id, self.storage, "File should be in the storage"
|
66
|
+
)
|
67
|
+
self.assertEqual(file_02.storage_id.save_type, "file", "Storage should be file")
|
68
|
+
self.assertEqual(file_02.save_type, "file", "File savetype should be file")
|
51
69
|
self.storage.write({"save_type": "database"})
|
52
70
|
self.storage.action_storage_migrate()
|
53
|
-
self.assertEqual(
|
54
|
-
|
55
|
-
|
56
|
-
self.assertEqual(
|
57
|
-
|
58
|
-
|
71
|
+
self.assertEqual(
|
72
|
+
file_01.storage_id, self.storage, "File should be in the storage"
|
73
|
+
)
|
74
|
+
self.assertEqual(
|
75
|
+
file_01.storage_id.save_type, "database", "Storage should be database"
|
76
|
+
)
|
77
|
+
self.assertEqual(
|
78
|
+
file_01.save_type, "database", "File savetype should be database"
|
79
|
+
)
|
80
|
+
self.assertEqual(
|
81
|
+
file_02.storage_id, self.storage, "File should be in the storage"
|
82
|
+
)
|
83
|
+
self.assertEqual(
|
84
|
+
file_02.storage_id.save_type, "database", "Storage should be database"
|
85
|
+
)
|
86
|
+
self.assertEqual(
|
87
|
+
file_02.save_type, "database", "File savetype should be database"
|
88
|
+
)
|
59
89
|
self.storage.write({"save_type": "attachment"})
|
60
90
|
model_partner = self.env.ref("base.model_res_partner")
|
61
91
|
partner = self.partner_model.create({"name": "Test partner"})
|
@@ -69,16 +99,40 @@ class StorageDatabaseTestCase(StorageDatabaseBaseCase):
|
|
69
99
|
}
|
70
100
|
)
|
71
101
|
file_03 = self.create_file(directory=directory)
|
72
|
-
self.assertEqual(
|
73
|
-
|
74
|
-
|
75
|
-
self.assertEqual(
|
76
|
-
|
77
|
-
|
102
|
+
self.assertEqual(
|
103
|
+
file_02.storage_id, self.storage, "File should be in the storage"
|
104
|
+
)
|
105
|
+
self.assertEqual(
|
106
|
+
file_02.storage_id.save_type, "attachment", "Storage should be attachment"
|
107
|
+
)
|
108
|
+
self.assertEqual(
|
109
|
+
file_02.save_type, "database", "File savetype should be database"
|
110
|
+
)
|
111
|
+
self.assertEqual(
|
112
|
+
file_03.storage_id, self.storage, "File should be in the storage"
|
113
|
+
)
|
114
|
+
self.assertEqual(
|
115
|
+
file_03.storage_id.save_type, "attachment", "Storage should be attachment"
|
116
|
+
)
|
117
|
+
self.assertEqual(
|
118
|
+
file_03.save_type, "database", "File savetype should be database"
|
119
|
+
)
|
78
120
|
self.storage.action_storage_migrate()
|
79
|
-
self.assertEqual(
|
80
|
-
|
81
|
-
|
82
|
-
self.assertEqual(
|
83
|
-
|
84
|
-
|
121
|
+
self.assertEqual(
|
122
|
+
file_02.storage_id, self.storage, "File should be in the storage"
|
123
|
+
)
|
124
|
+
self.assertEqual(
|
125
|
+
file_02.storage_id.save_type, "attachment", "Storage should be attachment"
|
126
|
+
)
|
127
|
+
self.assertEqual(
|
128
|
+
file_02.save_type, "database", "File savetype should be database"
|
129
|
+
)
|
130
|
+
self.assertEqual(
|
131
|
+
file_03.storage_id, self.storage, "File should be in the storage"
|
132
|
+
)
|
133
|
+
self.assertEqual(
|
134
|
+
file_03.storage_id.save_type, "attachment", "Storage should be attachment"
|
135
|
+
)
|
136
|
+
self.assertEqual(
|
137
|
+
file_03.save_type, "database", "File savetype should be database"
|
138
|
+
)
|
odoo/addons/dms/tools/file.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# Copyright 2020 Antoni Romera
|
2
2
|
# Copyright 2017-2019 MuK IT GmbH
|
3
|
+
# Copyright 2024 Subteno - Timothée Vannier (https://www.subteno.com).
|
3
4
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
4
5
|
|
5
6
|
import mimetypes
|
6
7
|
import os
|
8
|
+
import re
|
7
9
|
import shutil
|
8
10
|
import tempfile
|
9
11
|
|
@@ -11,10 +13,17 @@ from odoo.tools.mimetypes import guess_mimetype
|
|
11
13
|
|
12
14
|
|
13
15
|
def check_name(name):
|
16
|
+
"""
|
17
|
+
Check if a file name is valid.
|
18
|
+
|
19
|
+
:param str name: The file name to check.
|
20
|
+
:return: True if the file name is valid, False otherwise.
|
21
|
+
:rtype: bool
|
22
|
+
"""
|
14
23
|
tmp_dir = tempfile.mkdtemp()
|
15
24
|
try:
|
16
25
|
open(os.path.join(tmp_dir, name), "a").close()
|
17
|
-
except
|
26
|
+
except OSError:
|
18
27
|
return False
|
19
28
|
finally:
|
20
29
|
shutil.rmtree(tmp_dir)
|
@@ -22,26 +31,69 @@ def check_name(name):
|
|
22
31
|
|
23
32
|
|
24
33
|
def compute_name(name, suffix, escape_suffix):
|
34
|
+
"""
|
35
|
+
Compute a new name by adding a suffix to the original name.
|
36
|
+
|
37
|
+
:param str name: The original name.
|
38
|
+
:param int suffix: The suffix to add.
|
39
|
+
:param bool escape_suffix: If True, the suffix is added in between the name and
|
40
|
+
the file extension.
|
41
|
+
:return: The new name.
|
42
|
+
:rtype: str
|
43
|
+
"""
|
25
44
|
if escape_suffix:
|
26
45
|
name, extension = os.path.splitext(name)
|
27
|
-
return "{}({}){}"
|
28
|
-
|
29
|
-
|
46
|
+
return f"{name}({suffix}){extension}"
|
47
|
+
|
48
|
+
return f"{name}({suffix})"
|
30
49
|
|
31
50
|
|
32
51
|
def unique_name(name, names, escape_suffix=False):
|
52
|
+
"""
|
53
|
+
Generate a unique name by adding a suffix to the original name.
|
54
|
+
|
55
|
+
:param str name: The original name.
|
56
|
+
:param list names: The list of existing names.
|
57
|
+
:param bool escape_suffix: If True, the suffix is added in between the name and
|
58
|
+
the file extension.
|
59
|
+
:return: The unique name.
|
60
|
+
:rtype: str
|
61
|
+
"""
|
33
62
|
if name not in names:
|
34
63
|
return name
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
64
|
+
|
65
|
+
# Extract the suffix from the name
|
66
|
+
# e.g: "file(1).txt" -> "1"
|
67
|
+
# "Directory (1)(2)" -> "2"
|
68
|
+
digits = re.compile(r"\((\d+)\)(\.\w+)?$")
|
69
|
+
match = digits.search(name)
|
70
|
+
suffix = 1
|
71
|
+
if match:
|
72
|
+
suffix = int(match.group(1)) + 1
|
73
|
+
# get the name without the suffix and append the extension
|
74
|
+
name = name[: match.span()[0]] + (match.group(2) or "")
|
75
|
+
|
76
|
+
name = compute_name(name, suffix, escape_suffix)
|
77
|
+
while name in names:
|
78
|
+
# has to check recursively if the new name is unique because the user can
|
79
|
+
# rename the file with suffix n+1
|
80
|
+
# and then duplicate the file n, leading to a new file with suffix n+1 which
|
81
|
+
# is already used.
|
82
|
+
return unique_name(name, names, escape_suffix)
|
83
|
+
return name
|
42
84
|
|
43
85
|
|
44
86
|
def guess_extension(filename=None, mimetype=None, binary=None):
|
87
|
+
"""
|
88
|
+
Guess the extension of a file.
|
89
|
+
|
90
|
+
:param str filename: The name of the file.
|
91
|
+
:param str mimetype: The mimetype of the file.
|
92
|
+
:param str binary: The binary content of the file.
|
93
|
+
|
94
|
+
:return: The extension of the file.
|
95
|
+
:rtype: str
|
96
|
+
"""
|
45
97
|
extension = filename and os.path.splitext(filename)[1][1:].strip().lower()
|
46
98
|
if not extension and mimetype and mimetype != "application/x-empty":
|
47
99
|
extension = mimetypes.guess_extension(mimetype)[1:].strip().lower()
|
@@ -2,6 +2,7 @@
|
|
2
2
|
<!--
|
3
3
|
|
4
4
|
Copyright 2017-2019 MuK IT GmbH
|
5
|
+
Copyright 2024 Subteno - Timothée Vannier (https://www.subteno.com).
|
5
6
|
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
6
7
|
|
7
8
|
-->
|
@@ -90,17 +91,7 @@
|
|
90
91
|
</field>
|
91
92
|
</page>
|
92
93
|
<page string="Directories">
|
93
|
-
<field name="directory_ids"
|
94
|
-
<tree>
|
95
|
-
<field name="name" />
|
96
|
-
<field
|
97
|
-
name="count_directories"
|
98
|
-
string="Directories"
|
99
|
-
/>
|
100
|
-
<field name="count_files" string="Files" />
|
101
|
-
<field name="size" widget="binary_size" />
|
102
|
-
</tree>
|
103
|
-
</field>
|
94
|
+
<field name="directory_ids" />
|
104
95
|
</page>
|
105
96
|
</notebook>
|
106
97
|
</sheet>
|
@@ -118,10 +109,4 @@
|
|
118
109
|
(0, 0, {'view_mode': 'form', 'view_id': ref('view_dms_access_groups_form')})]"
|
119
110
|
/>
|
120
111
|
</record>
|
121
|
-
<menuitem
|
122
|
-
id="menu_dms_access_groups"
|
123
|
-
name="Access Groups"
|
124
|
-
parent="dms.cat_menu_dms_config_system"
|
125
|
-
action="action_dms_access_groups"
|
126
|
-
/>
|
127
112
|
</odoo>
|
@@ -2,6 +2,7 @@
|
|
2
2
|
<!--
|
3
3
|
|
4
4
|
Copyright 2017-2019 MuK IT GmbH
|
5
|
+
Copyright 2024 Subteno - Timothée Vannier (https://www.subteno.com).
|
5
6
|
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
6
7
|
|
7
8
|
-->
|
@@ -44,7 +45,7 @@
|
|
44
45
|
name="web_ribbon"
|
45
46
|
title="Archived"
|
46
47
|
bg_color="bg-danger"
|
47
|
-
|
48
|
+
invisible="active"
|
48
49
|
/>
|
49
50
|
<div class="oe_button_box" name="button_box" />
|
50
51
|
<div class="oe_title">
|
@@ -76,7 +77,7 @@
|
|
76
77
|
<field
|
77
78
|
name="tag_ids"
|
78
79
|
mode="kanban"
|
79
|
-
context="{'default_category_id':
|
80
|
+
context="{'default_category_id': id}"
|
80
81
|
/>
|
81
82
|
</page>
|
82
83
|
<page string="Subcategories">
|
@@ -84,7 +85,7 @@
|
|
84
85
|
name="child_category_ids"
|
85
86
|
widget="one2many"
|
86
87
|
mode="tree"
|
87
|
-
context="{'default_parent_id':
|
88
|
+
context="{'default_parent_id': id}"
|
88
89
|
/>
|
89
90
|
</page>
|
90
91
|
</notebook>
|
@@ -106,10 +107,4 @@
|
|
106
107
|
</p>
|
107
108
|
</field>
|
108
109
|
</record>
|
109
|
-
<menuitem
|
110
|
-
id="menu_dms_category"
|
111
|
-
name="Categories"
|
112
|
-
parent="cat_menu_dms_config_organizing"
|
113
|
-
action="action_dms_category"
|
114
|
-
/>
|
115
110
|
</odoo>
|