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.
Files changed (113) hide show
  1. odoo/addons/dms/README.rst +132 -78
  2. odoo/addons/dms/__manifest__.py +34 -16
  3. odoo/addons/dms/actions/file.xml +1 -3
  4. odoo/addons/dms/controllers/main.py +0 -34
  5. odoo/addons/dms/controllers/portal.py +178 -92
  6. odoo/addons/dms/data/onboarding_data.xml +77 -0
  7. odoo/addons/dms/demo/access_group.xml +3 -9
  8. odoo/addons/dms/i18n/de.po +332 -221
  9. odoo/addons/dms/i18n/dms.pot +137 -174
  10. odoo/addons/dms/i18n/es.po +355 -226
  11. odoo/addons/dms/i18n/fr.po +256 -342
  12. odoo/addons/dms/i18n/he_IL.po +208 -181
  13. odoo/addons/dms/i18n/it.po +369 -248
  14. odoo/addons/dms/i18n/nl.po +140 -168
  15. odoo/addons/dms/i18n/pt.po +352 -223
  16. odoo/addons/dms/i18n/pt_BR.po +358 -256
  17. odoo/addons/dms/i18n/ru.po +138 -172
  18. odoo/addons/dms/models/__init__.py +7 -1
  19. odoo/addons/dms/models/abstract_dms_mixin.py +1 -1
  20. odoo/addons/dms/models/access_groups.py +18 -11
  21. odoo/addons/dms/models/base.py +10 -2
  22. odoo/addons/dms/models/directory.py +46 -75
  23. odoo/addons/dms/models/{category.py → dms_category.py} +10 -42
  24. odoo/addons/dms/models/dms_file.py +125 -147
  25. odoo/addons/dms/models/dms_security_mixin.py +26 -22
  26. odoo/addons/dms/models/ir_attachment.py +0 -1
  27. odoo/addons/dms/models/ir_binary.py +19 -0
  28. odoo/addons/dms/models/mail_thread.py +2 -4
  29. odoo/addons/dms/models/mixins_thumbnail.py +13 -8
  30. odoo/addons/dms/models/onboarding_onboarding.py +16 -0
  31. odoo/addons/dms/models/onboarding_onboarding_step.py +50 -0
  32. odoo/addons/dms/models/res_company.py +22 -47
  33. odoo/addons/dms/models/res_config_settings.py +0 -1
  34. odoo/addons/dms/models/storage.py +4 -24
  35. odoo/addons/dms/models/tag.py +1 -1
  36. odoo/addons/dms/readme/CONFIGURE.md +59 -0
  37. odoo/addons/dms/readme/CONTRIBUTORS.md +14 -0
  38. odoo/addons/dms/readme/CREDITS.md +6 -0
  39. odoo/addons/dms/readme/DESCRIPTION.md +9 -0
  40. odoo/addons/dms/readme/INSTALL.md +4 -0
  41. odoo/addons/dms/readme/ROADMAP.md +19 -0
  42. odoo/addons/dms/readme/USAGE.md +11 -0
  43. odoo/addons/dms/security/security.xml +1 -0
  44. odoo/addons/dms/static/description/icon.png +0 -0
  45. odoo/addons/dms/static/description/icon.svg +4 -1
  46. odoo/addons/dms/static/description/index.html +133 -88
  47. odoo/addons/dms/static/description/portal_icon.svg +12 -0
  48. odoo/addons/dms/static/src/js/fields/{path_owl.esm.js → path_json/path_owl.esm.js} +22 -4
  49. odoo/addons/dms/static/src/js/fields/{path_owl.xml → path_json/path_owl.xml} +7 -4
  50. odoo/addons/dms/static/src/js/fields/preview_binary/preview_record.esm.js +47 -0
  51. odoo/addons/dms/static/src/js/{views/fields/binary → fields/preview_binary}/preview_record.xml +5 -4
  52. odoo/addons/dms/static/src/js/views/dms_file_upload.esm.js +155 -148
  53. odoo/addons/dms/static/src/js/views/file_kanban_controller.xml +0 -1
  54. odoo/addons/dms/static/src/js/views/file_kanban_record.esm.js +26 -27
  55. odoo/addons/dms/static/src/js/views/file_kanban_renderer.esm.js +1 -2
  56. odoo/addons/dms/static/src/js/views/file_kanban_renderer.xml +12 -16
  57. odoo/addons/dms/static/src/js/views/file_kanban_view.esm.js +11 -9
  58. odoo/addons/dms/static/src/js/views/file_list_controller.esm.js +1 -1
  59. odoo/addons/dms/static/src/js/views/file_list_renderer.xml +1 -7
  60. odoo/addons/dms/static/src/js/views/file_list_view.esm.js +11 -9
  61. odoo/addons/dms/static/src/js/views/search_panel.esm.js +9 -10
  62. odoo/addons/dms/static/src/models/attachment.esm.js +83 -82
  63. odoo/addons/dms/static/src/models/attachment_image.esm.js +30 -28
  64. odoo/addons/dms/static/src/models/attachment_viewer_viewable.esm.js +27 -25
  65. odoo/addons/dms/static/src/scss/directory_kanban.scss +28 -73
  66. odoo/addons/dms/static/src/scss/dms_common.scss +69 -0
  67. odoo/addons/dms/static/src/scss/file_kanban.scss +22 -34
  68. odoo/addons/dms/static/src/scss/portal.scss +12 -0
  69. odoo/addons/dms/static/tests/tours/dms_portal_tour.esm.js +46 -0
  70. odoo/addons/dms/{views/dms_portal_templates.xml → template/portal.xml} +13 -3
  71. odoo/addons/dms/tests/common.py +20 -4
  72. odoo/addons/dms/tests/data/mail01.eml +2 -2
  73. odoo/addons/dms/tests/data/mail02.eml +2 -2
  74. odoo/addons/dms/tests/test_benchmark.py +16 -21
  75. odoo/addons/dms/tests/test_directory.py +128 -40
  76. odoo/addons/dms/tests/test_file.py +112 -30
  77. odoo/addons/dms/tests/test_file_database.py +50 -24
  78. odoo/addons/dms/tests/test_portal.py +50 -37
  79. odoo/addons/dms/tests/test_storage_attachment.py +85 -63
  80. odoo/addons/dms/tests/test_storage_database.py +89 -35
  81. odoo/addons/dms/tools/file.py +63 -11
  82. odoo/addons/dms/views/dms_access_groups_views.xml +2 -17
  83. odoo/addons/dms/views/{category.xml → dms_category.xml} +4 -9
  84. odoo/addons/dms/views/{directory.xml → dms_directory.xml} +168 -176
  85. odoo/addons/dms/views/dms_file.xml +170 -191
  86. odoo/addons/dms/views/{tag.xml → dms_tag.xml} +50 -53
  87. odoo/addons/dms/views/menu.xml +52 -21
  88. odoo/addons/dms/views/res_config_settings.xml +31 -82
  89. odoo/addons/dms/views/storage.xml +7 -13
  90. odoo_addon_dms-17.0.1.0.0.3.dist-info/METADATA +257 -0
  91. {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
  92. {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
  93. odoo_addon_dms-17.0.1.0.0.3.dist-info/top_level.txt +1 -0
  94. odoo/addons/dms/i18n/fa.po +0 -2277
  95. odoo/addons/dms/i18n/fa_IR.po +0 -2277
  96. odoo/addons/dms/readme/CONFIGURE.rst +0 -40
  97. odoo/addons/dms/readme/CONTRIBUTORS.rst +0 -16
  98. odoo/addons/dms/readme/CREDITS.rst +0 -6
  99. odoo/addons/dms/readme/DESCRIPTION.rst +0 -6
  100. odoo/addons/dms/readme/INSTALL.rst +0 -9
  101. odoo/addons/dms/readme/ROADMAP.rst +0 -8
  102. odoo/addons/dms/readme/USAGE.rst +0 -8
  103. odoo/addons/dms/static/src/js/components/path/path.esm.js +0 -0
  104. odoo/addons/dms/static/src/js/dms_portal_tour.js +0 -57
  105. odoo/addons/dms/static/src/js/fields/path.js +0 -81
  106. odoo/addons/dms/static/src/js/views/fields/binary/preview_record.esm.js +0 -35
  107. odoo/addons/dms/static/src/js/views/many_drop_target.js +0 -98
  108. odoo/addons/dms/static/src/scss/variables.scss +0 -9
  109. odoo/addons/dms/static/src/xml/views.xml +0 -72
  110. odoo/addons/dms/template/assets.xml +0 -44
  111. odoo/addons/dms/template/onboarding.xml +0 -97
  112. odoo_addon_dms-16.0.1.8.0.5.dist-info/METADATA +0 -203
  113. 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(directory.file_ids[0].name, "demo.txt")
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(file_01.storage_id.save_type, "attachment")
25
- self.assertEqual(file_01.save_type, "database")
26
- self.assertEqual(directory.count_files, 2)
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(root_directory.count_directories, 1)
39
- self.assertEqual(len(root_directory.child_directory_ids), 1)
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(child_directory.res_model, self.partner_model._name)
42
- self.assertEqual(child_directory.res_id, self.partner.id)
43
- self.assertEqual(len(root_directory.child_directory_ids.file_ids), 1)
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(dms_file.attachment_id, attachment)
46
- self.assertEqual(dms_file.name, "demo.txt")
47
- self.assertEqual(dms_file.res_model, self.partner_model._name)
48
- self.assertEqual(dms_file.res_id, self.partner.id)
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(file_01.res_model, self.partner_model._name)
52
- self.assertEqual(file_01.res_id, self.partner.id)
53
- self.assertEqual(file_01.storage_id, self.storage)
54
- self.assertEqual(file_01.storage_id.save_type, "attachment")
55
- self.assertEqual(file_01.save_type, "database")
56
- self.assertEqual(self.storage.count_storage_files, 2)
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.partner.sudo().write({"type": "private"})
65
- self.assertEqual(self.partner.type, "private")
66
- self.assertTrue(directory.sudo().permission_read)
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.partner.sudo().write({"type": "private"})
77
- self.assertEqual(self.partner.type, "private")
78
- self.assertTrue(directory.sudo().permission_read)
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.assertFalse(directory.with_user(self.dms_user).permission_read)
81
- # user can access self.partner
82
- self.dms_user.write(
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": "private"})
106
- self.assertEqual(self.partner.type, "private")
107
- self.assertTrue(directory.sudo().permission_read)
108
- directory.invalidate_recordset()
109
- self.assertFalse(directory.with_user(self.user).permission_read)
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(self.storage.count_storage_directories)
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(file_01.storage_id, self.storage)
33
- self.assertEqual(file_01.storage_id.save_type, "database")
34
- self.assertEqual(file_01.save_type, "database")
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(file_01.storage_id, self.storage)
39
- self.assertEqual(file_01.storage_id.save_type, "file")
40
- self.assertEqual(file_01.save_type, "database")
41
- self.assertEqual(file_02.storage_id, self.storage)
42
- self.assertEqual(file_02.storage_id.save_type, "file")
43
- self.assertEqual(file_02.save_type, "file")
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(file_01.storage_id, self.storage)
46
- self.assertEqual(file_01.storage_id.save_type, "file")
47
- self.assertEqual(file_01.save_type, "file")
48
- self.assertEqual(file_02.storage_id, self.storage)
49
- self.assertEqual(file_02.storage_id.save_type, "file")
50
- self.assertEqual(file_02.save_type, "file")
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(file_01.storage_id, self.storage)
54
- self.assertEqual(file_01.storage_id.save_type, "database")
55
- self.assertEqual(file_01.save_type, "database")
56
- self.assertEqual(file_02.storage_id, self.storage)
57
- self.assertEqual(file_02.storage_id.save_type, "database")
58
- self.assertEqual(file_02.save_type, "database")
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(file_02.storage_id, self.storage)
73
- self.assertEqual(file_02.storage_id.save_type, "attachment")
74
- self.assertEqual(file_02.save_type, "database")
75
- self.assertEqual(file_03.storage_id, self.storage)
76
- self.assertEqual(file_03.storage_id.save_type, "attachment")
77
- self.assertEqual(file_03.save_type, "database")
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(file_02.storage_id, self.storage)
80
- self.assertEqual(file_02.storage_id.save_type, "attachment")
81
- self.assertEqual(file_02.save_type, "database")
82
- self.assertEqual(file_03.storage_id, self.storage)
83
- self.assertEqual(file_03.storage_id.save_type, "attachment")
84
- self.assertEqual(file_03.save_type, "database")
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
+ )
@@ -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 IOError:
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 "{}({}){}".format(name, suffix, extension)
28
- else:
29
- return "{}({})".format(name, suffix)
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
- else:
36
- suffix = 1
37
- name = compute_name(name, suffix, escape_suffix)
38
- while name in names:
39
- suffix += 1
40
- name = compute_name(name, suffix, escape_suffix)
41
- return name
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
- attrs="{'invisible': [('active', '=', True)]}"
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': active_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': active_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>