imio.smartweb.common 1.2.38__py3-none-any.whl → 1.2.40__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.
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <metadata>
3
- <version>1033</version>
3
+ <version>1034</version>
4
4
  <dependencies>
5
5
  <dependency>profile-plone.restapi:default</dependency>
6
6
  <dependency>profile-eea.facetednavigation:default</dependency>
@@ -53,11 +53,8 @@
53
53
  <element>code</element>
54
54
  <element>charmap</element>
55
55
  <element>fullscreen</element>
56
- <element>hr</element>
57
56
  <element>lists</element>
58
57
  <element>nonbreaking</element>
59
- <element>noneditable</element>
60
- <element>paste</element>
61
58
  <element>searchreplace</element>
62
59
  <element>table</element>
63
60
  <element>visualblocks</element>
@@ -70,13 +67,7 @@
70
67
  <record interface="plone.base.interfaces.controlpanel.ITinyMCESchema"
71
68
  name="plone.menubar"
72
69
  >
73
- <value purge="true">
74
- <element>edit</element>
75
- <element>format</element>
76
- <element>insert</element>
77
- <element>table</element>
78
- <element>view</element>
79
- </value>
70
+ <value purge="true">edit format insert table view</value>
80
71
  </record>
81
72
 
82
73
  <!-- See possible values in https://www.tiny.cloud/docs/tinymce/latest/available-menu-items/ -->
@@ -106,8 +97,8 @@
106
97
  "items": ""
107
98
  },
108
99
  "insert": {
109
- "title": "Insert",
110
- "items": "hr"
100
+ "title": "Insert",
101
+ "items": "hr"
111
102
  },
112
103
  "table": {
113
104
  "title": "Table",
@@ -25,8 +25,6 @@ class FindEndpointHandler(SearchHandler):
25
25
  if not types:
26
26
  raise Unauthorized("No types found, you are not allowed to search")
27
27
  self._constrain_query_by_path(query)
28
- if not query.get("path"):
29
- query["path"] = {"query": "/Plone"}
30
28
  # query = self._parse_query(query)
31
29
  if query.get("type_of_request") == "count_contents_types":
32
30
  results = self.count_contents_types(query)
@@ -164,7 +162,6 @@ class FindEndpointHandler(SearchHandler):
164
162
  count = counter.get(key, 0)
165
163
  percent = (count / total) * 100 if total > 0 else 0
166
164
  result[label] = {"count": count, "percent": round(percent, 2)}
167
-
168
165
  return result
169
166
 
170
167
 
@@ -0,0 +1,238 @@
1
+ # -*- coding: utf-8 -*-
2
+ from imio.smartweb.common.rest.endpoint import FindEndpointHandler
3
+ from imio.smartweb.common.testing import IMIO_SMARTWEB_COMMON_INTEGRATION_TESTING
4
+ from plone import api
5
+ from plone.app.testing import setRoles
6
+ from plone.app.testing import TEST_USER_ID
7
+ from plone.namedfile.file import NamedBlobFile, NamedBlobImage
8
+ from unittest.mock import patch
9
+ from zExceptions import Unauthorized
10
+
11
+ import unittest
12
+
13
+
14
+ class TestRestEndpoint(unittest.TestCase):
15
+ layer = IMIO_SMARTWEB_COMMON_INTEGRATION_TESTING
16
+
17
+ def setUp(self):
18
+ self.request = self.layer["request"]
19
+ self.portal = self.layer["portal"]
20
+
21
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
22
+ # Simule un header d'auth présent dans la requête
23
+ self.request._orig_env = {"HTTP_AUTHORIZATION": "Bearer TEST"}
24
+ self.folder = api.content.create(
25
+ container=self.portal,
26
+ type="Folder",
27
+ title="Folder",
28
+ )
29
+ self.doc1 = api.content.create(
30
+ container=self.folder, type="Document", title="Doc 1"
31
+ )
32
+ self.doc2 = api.content.create(
33
+ container=self.folder, type="Document", title="Doc 2"
34
+ )
35
+ self.page = api.content.create(
36
+ container=self.folder, type="Document", title="Doc 3"
37
+ )
38
+
39
+ def test_no_types(self):
40
+ query = {
41
+ "type_of_request": "count_contents_types",
42
+ "portal_type": "Document",
43
+ "path": {"query": self.portal.absolute_url_path()},
44
+ "operator": "and",
45
+ }
46
+ handler = FindEndpointHandler(self.portal, self.request)
47
+ self.assertRaises(Unauthorized, handler.search, query)
48
+
49
+ # ------------------------------
50
+ # count_contents_types
51
+ # ------------------------------
52
+ @patch("imio.smartweb.common.rest.endpoint.get_json")
53
+ def test_count_contents_types_operator_and(self, mjson):
54
+ mjson.return_value = [
55
+ {
56
+ "@id": f"https://{self.portal.absolute_url()}/@types/Document",
57
+ "addable": "false",
58
+ "id": "Document",
59
+ "immediately_addable": "false",
60
+ "title": "Document",
61
+ }
62
+ ]
63
+ handler = FindEndpointHandler(self.portal, self.request)
64
+ query = {
65
+ "type_of_request": "count_contents_types",
66
+ "portal_type": "Document",
67
+ "path": {"query": self.portal.absolute_url_path()},
68
+ "operator": "and",
69
+ }
70
+ res = handler.search(query)
71
+ self.assertEqual(res, {"items": [{"portal_type": "Document", "nb_items": 3}]})
72
+
73
+ # Test without path in query.
74
+ # Path is automaticaly set.
75
+ query = {
76
+ "type_of_request": "count_contents_types",
77
+ "portal_type": "Document",
78
+ "operator": "and",
79
+ }
80
+ res = handler.search(query)
81
+ self.assertEqual(res, {"items": [{"portal_type": "Document", "nb_items": 3}]})
82
+
83
+ @patch("imio.smartweb.common.rest.endpoint.get_json")
84
+ def test_count_contents_types_operator_or_multiple_types(self, mjson):
85
+ mjson.return_value = [
86
+ {
87
+ "@id": f"https://{self.portal.absolute_url()}/@types/Document",
88
+ "addable": "false",
89
+ "id": "Document",
90
+ "immediately_addable": "false",
91
+ "title": "Document",
92
+ },
93
+ {
94
+ "@id": f"https://{self.portal.absolute_url()}/@types/Folder",
95
+ "addable": "false",
96
+ "id": "Folder",
97
+ "immediately_addable": "false",
98
+ "title": "Folder",
99
+ },
100
+ ]
101
+ handler = FindEndpointHandler(self.portal, self.request)
102
+ query = {
103
+ "type_of_request": "count_contents_types",
104
+ "portal_type": ["Document", "Folder"],
105
+ "path": {"query": self.portal.absolute_url_path()},
106
+ "operator": "and",
107
+ }
108
+ res = handler.search(query)
109
+ self.assertEqual(
110
+ res, {"items": [{"portal_type": ["Document", "Folder"], "nb_items": 4}]}
111
+ )
112
+
113
+ @patch("imio.smartweb.common.rest.endpoint.get_json")
114
+ def test_get_max_depth(self, mjson):
115
+ mjson.return_value = [
116
+ {
117
+ "@id": f"https://{self.portal.absolute_url()}/@types/Document",
118
+ "addable": "false",
119
+ "id": "Document",
120
+ "immediately_addable": "false",
121
+ "title": "Document",
122
+ }
123
+ ]
124
+ handler = FindEndpointHandler(self.portal, self.request)
125
+ res = handler.search({"type_of_request": "get_max_depth"})
126
+ self.assertEqual(res["max_depth"], 3)
127
+
128
+ paths = {i["path"] for i in res["items"]}
129
+ self.assertEqual(
130
+ paths, {"/plone/folder/doc-2", "/plone/folder/doc-3", "/plone/folder/doc-1"}
131
+ )
132
+
133
+ folder2 = api.content.create(
134
+ container=self.folder,
135
+ type="Folder",
136
+ title="Folder 2",
137
+ )
138
+
139
+ api.content.create(
140
+ container=folder2,
141
+ type="Document",
142
+ title="Kamoulox",
143
+ )
144
+ handler = FindEndpointHandler(self.portal, self.request)
145
+ res = handler.search({"type_of_request": "get_max_depth"})
146
+ self.assertEqual(res["max_depth"], 4)
147
+
148
+ paths = {i["path"] for i in res["items"]}
149
+ self.assertEqual(paths, {"/plone/folder/folder-2/kamoulox"})
150
+
151
+ @patch("imio.smartweb.common.rest.endpoint.get_json")
152
+ def test_check_value_of_field_counts_and_percents(self, mjson):
153
+ mjson.return_value = [
154
+ {
155
+ "@id": f"https://{self.portal.absolute_url()}/@types/Document",
156
+ "addable": "false",
157
+ "id": "Document",
158
+ "immediately_addable": "false",
159
+ "title": "Document",
160
+ }
161
+ ]
162
+ api.content.create(
163
+ container=self.folder,
164
+ type="Document",
165
+ title="Doc 2",
166
+ )
167
+ handler = FindEndpointHandler(self.portal, self.request)
168
+ q = {
169
+ "type_of_request": "check_value_of_field",
170
+ "portal_type": "Document",
171
+ "field_name": "title",
172
+ "expected_values": ["Doc 1", "Doc 2", "Doc 3"],
173
+ }
174
+ res = handler.search(q)
175
+ # Number
176
+ self.assertEqual(res["Doc 1"]["count"], 1)
177
+ self.assertEqual(res["Doc 2"]["count"], 2)
178
+ self.assertEqual(res["Doc 3"]["count"], 1)
179
+
180
+ # Pourcentages
181
+ self.assertEqual(res["Doc 1"]["percent"], round(1 / 4 * 100, 2)) # 25%
182
+ self.assertEqual(res["Doc 2"]["percent"], round(2 / 4 * 100, 2)) # 50%
183
+ self.assertEqual(res["Doc 3"]["percent"], round(1 / 4 * 100, 2)) # 25%
184
+
185
+ @patch("imio.smartweb.common.rest.endpoint.get_json")
186
+ def test_find_big_files_or_images(self, mjson):
187
+ mjson.return_value = [
188
+ {
189
+ "@id": f"https://{self.portal.absolute_url()}/@types/Image",
190
+ "addable": "false",
191
+ "id": "Image",
192
+ "immediately_addable": "false",
193
+ "title": "Image",
194
+ },
195
+ {
196
+ "@id": f"https://{self.portal.absolute_url()}/@types/File",
197
+ "addable": "false",
198
+ "id": "File",
199
+ "immediately_addable": "false",
200
+ "title": "File",
201
+ },
202
+ ]
203
+ api.content.create(
204
+ container=self.folder,
205
+ type="File",
206
+ id="bigfile",
207
+ title="Big file",
208
+ file=NamedBlobFile(data=b"x" * 2500000, filename="big.pdf"), # 2,5 Mo
209
+ )
210
+
211
+ api.content.create(
212
+ container=self.folder,
213
+ type="Image",
214
+ id="smallimage",
215
+ title="Small image",
216
+ image=NamedBlobImage(data=b"x" * 100000, filename="small.jpg"), # 0,1 Mo
217
+ )
218
+
219
+ api.content.create(
220
+ container=self.folder,
221
+ type="Image",
222
+ id="bigimage",
223
+ title="Big image",
224
+ image=NamedBlobImage(data=b"x" * 3100000, filename="big.jpg"), # 3,1 Mo
225
+ )
226
+ handler = FindEndpointHandler(self.portal, self.request)
227
+ q = {
228
+ "type_of_request": "find_big_files_or_images",
229
+ "portal_type": ["Image", "File"],
230
+ "size": 1000000,
231
+ }
232
+ res = handler.search(q)
233
+ # On attend seulement les gros
234
+ titles = [it["title"] for it in res["items"]]
235
+ self.assertEqual(set(titles), {"Big file", "Big image"})
236
+ # Vérifie calcul Mo arrondi (2_500_000 bytes ≈ 2.38 Mo)
237
+ big_entry = next(i for i in res["items"] if i["title"] == "Big file")
238
+ self.assertAlmostEqual(big_entry["size"], round(2_500_000 / (1024 * 1024), 2))
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from imio.smartweb.common.testing import IMIO_SMARTWEB_COMMON_ACCEPTANCE_TESTING
4
+ from imio.smartweb.common.rest.utils import batch_results
5
+ from imio.smartweb.common.rest.utils import get_json
6
+ from imio.smartweb.common.rest.utils import hash_md5
7
+ from unittest.mock import patch
8
+ from unittest.mock import Mock
9
+
10
+ import json
11
+ import requests
12
+ import unittest
13
+
14
+
15
+ class TestRestUtils(unittest.TestCase):
16
+ layer = IMIO_SMARTWEB_COMMON_ACCEPTANCE_TESTING
17
+
18
+ def setUp(self):
19
+ self.request = self.layer["request"]
20
+ self.portal = self.layer["portal"]
21
+ self.portal_url = self.portal.absolute_url()
22
+ self.url = "https://api.kamoulox.test/endpoint"
23
+
24
+ @patch("imio.smartweb.common.rest.utils.requests.get")
25
+ def test_get_json_success(self, mock_get):
26
+ payload = {"ok": True, "items": [1, 2]}
27
+ mock_get.return_value = Mock(status_code=200, text=json.dumps(payload))
28
+ result = get_json(self.url)
29
+ self.assertEqual(result, payload)
30
+ mock_get.assert_called_once()
31
+ (called_url,) = mock_get.call_args[0]
32
+ self.assertEqual(called_url, self.url)
33
+ kwargs = mock_get.call_args.kwargs
34
+ self.assertEqual(kwargs["timeout"], 5)
35
+ self.assertEqual(kwargs["headers"]["Accept"], "application/json")
36
+ self.assertNotIn("Authorization", kwargs["headers"])
37
+
38
+ @patch("imio.smartweb.common.rest.utils.requests.get")
39
+ def test_get_json_non_200_returns_none(self, mock_get):
40
+ mock_get.return_value = Mock(status_code=404, text="Not found")
41
+ result = get_json(self.url)
42
+ self.assertIsNone(result)
43
+
44
+ @patch("imio.smartweb.common.rest.utils.logger")
45
+ @patch("imio.smartweb.common.rest.utils.requests.get")
46
+ def test_get_json_timeout_returns_none_and_logs(self, mock_get, mock_logger):
47
+ mock_get.side_effect = requests.exceptions.Timeout()
48
+ result = get_json(self.url)
49
+ self.assertIsNone(result)
50
+ mock_logger.warning.assert_called_once()
51
+ # check if url is in log
52
+ self.assertIn(self.url, mock_logger.warning.call_args[0][0])
53
+
54
+ @patch("imio.smartweb.common.rest.utils.requests.get")
55
+ def test_get_json_other_exception_returns_none(self, mock_get):
56
+ mock_get.side_effect = RuntimeError("kamoulox")
57
+ result = get_json(self.url)
58
+ self.assertIsNone(result)
59
+
60
+ @patch("imio.smartweb.common.rest.utils.requests.get")
61
+ def test_get_json_sets_auth_header_when_provided(self, mock_get):
62
+ mock_get.return_value = Mock(status_code=200, text="{}")
63
+ auth = "Bearer TOKEN123"
64
+ _ = get_json(self.url, auth=auth, timeout=10)
65
+ mock_get.assert_called_once()
66
+ kwargs = mock_get.call_args.kwargs
67
+ self.assertEqual(kwargs["timeout"], 10)
68
+ self.assertEqual(kwargs["headers"]["Authorization"], auth)
69
+ self.assertEqual(kwargs["headers"]["Accept"], "application/json")
70
+
71
+ @patch("imio.smartweb.common.rest.utils.requests.get")
72
+ def test_get_json_custom_timeout_is_used(self, mock_get):
73
+ mock_get.return_value = Mock(status_code=200, text="{}")
74
+ _ = get_json(self.url, timeout=2.5)
75
+ self.assertEqual(mock_get.call_args.kwargs["timeout"], 2.5)
76
+
77
+ def test_batch_results_exact_division(self):
78
+ data = [1, 2, 3, 4]
79
+ result = batch_results(data, 2)
80
+ self.assertEqual(result, [[1, 2], [3, 4]])
81
+
82
+ def test_batch_results_not_exact(self):
83
+ data = [1, 2, 3, 4, 5]
84
+ result = batch_results(data, 2)
85
+ self.assertEqual(result, [[1, 2], [3, 4], [5]])
86
+
87
+ def test_batch_results_batch_size_larger_than_list(self):
88
+ data = [1, 2, 3]
89
+ result = batch_results(data, 10)
90
+ self.assertEqual(result, [[1, 2, 3]])
91
+
92
+ def test_batch_results_empty_iterable(self):
93
+ data = []
94
+ result = batch_results(data, 3)
95
+ self.assertEqual(result, [])
96
+
97
+ def test_batch_results_with_generator(self):
98
+ gen = (i for i in range(5))
99
+ result = batch_results(gen, 2)
100
+ self.assertEqual(result, [[0, 1], [2, 3], [4]])
101
+
102
+ def test_hash_md5_basic_string(self):
103
+ result = hash_md5("hello")
104
+ self.assertEqual(result, "5d41402abc4b2a76b9719d911017c592")
105
+
106
+ def test_hash_md5_empty_string(self):
107
+ result = hash_md5("")
108
+ self.assertEqual(result, "d41d8cd98f00b204e9800998ecf8427e")
109
+
110
+ def test_hash_md5_unicode_string(self):
111
+ result = hash_md5("éèà")
112
+ # tu peux vérifier avec hashlib directement
113
+ import hashlib
114
+
115
+ expected = hashlib.md5("éèà".encode()).hexdigest()
116
+ self.assertEqual(result, expected)
@@ -102,6 +102,22 @@ class TestUtils(unittest.TestCase):
102
102
  self.assertEqual(obj.geolocation.latitude, 1)
103
103
  self.assertEqual(obj.geolocation.longitude, 2)
104
104
 
105
+ def test_geocode_object_geocoder_unavailable(self):
106
+ obj = GeolocatedObject()
107
+ obj.street = "Test Street"
108
+ obj.number = "1"
109
+ obj.complement = ""
110
+ obj.zipcode = "12345"
111
+ obj.city = "Testville"
112
+ obj.country = "be"
113
+ with patch("geopy.geocoders.Nominatim") as mock_nominatim, patch(
114
+ "geopy.exc.GeocoderUnavailable", new=geopy.exc.GeocoderUnavailable
115
+ ):
116
+ instance = mock_nominatim.return_value
117
+ instance.geocode.side_effect = geopy.exc.GeocoderUnavailable
118
+ result = geocode_object(obj)
119
+ self.assertFalse(result)
120
+
105
121
  def test_get_uncroppable_scales_infos(self):
106
122
  folder = api.content.create(
107
123
  container=self.portal,
@@ -165,6 +165,14 @@
165
165
  directory="profiles/1032_to_1033"
166
166
  />
167
167
 
168
+ <genericsetup:registerProfile
169
+ name="upgrade_1033_to_1034"
170
+ title="Upgrade common from 1033 to 1034"
171
+ description="Update configuration from TinyMCE 7.7.0 to be TinyMCE 7.9.1"
172
+ provides="Products.GenericSetup.interfaces.EXTENSION"
173
+ directory="profiles/1033_to_1034"
174
+ />
175
+
168
176
  <genericsetup:upgradeStep
169
177
  title="Configure first official release"
170
178
  description="Run needed registry step"
@@ -515,4 +523,15 @@
515
523
  />
516
524
  </genericsetup:upgradeSteps>
517
525
 
526
+ <genericsetup:upgradeSteps
527
+ profile="imio.smartweb.common:default"
528
+ source="1033"
529
+ destination="1034"
530
+ >
531
+ <genericsetup:upgradeDepends
532
+ title="Update configuration from TinyMCE 7.7.0 to be TinyMCE 7.9.1"
533
+ import_profile="imio.smartweb.common.upgrades:upgrade_1033_to_1034"
534
+ />
535
+ </genericsetup:upgradeSteps>
536
+
518
537
  </configure>
@@ -0,0 +1,70 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <registry>
3
+
4
+ <record interface="plone.base.interfaces.controlpanel.ITinyMCESchema"
5
+ name="plone.plugins"
6
+ >
7
+ <value purge="true">
8
+ <element>code</element>
9
+ <element>charmap</element>
10
+ <element>fullscreen</element>
11
+ <element>lists</element>
12
+ <element>nonbreaking</element>
13
+ <element>searchreplace</element>
14
+ <element>table</element>
15
+ <element>visualblocks</element>
16
+ <element>visualchars</element>
17
+ <element>wordcount</element>
18
+ </value>
19
+ </record>
20
+
21
+ <!-- See possible values in https://www.tiny.cloud/docs/tinymce/latest/menus-configuration-options/#menubar -->
22
+ <record interface="plone.base.interfaces.controlpanel.ITinyMCESchema"
23
+ name="plone.menubar">
24
+ <value purge="true">edit format insert table view</value>
25
+ </record>
26
+
27
+ <record interface="plone.base.interfaces.controlpanel.ITinyMCESchema"
28
+ name="plone.menu">
29
+ <value purge="true">
30
+ {
31
+ "file": {
32
+ "title": "File",
33
+ "items": ""
34
+ },
35
+ "tools": {
36
+ "title": "Tools",
37
+ "items": ""
38
+ },
39
+ "edit": {
40
+ "title": "Edit",
41
+ "items": "undo redo | cut copy paste | searchreplace selectall"
42
+ },
43
+ "format": {
44
+ "title": "Format",
45
+ "items": ""
46
+ },
47
+ "insert": {
48
+ "title": "Insert",
49
+ "items": "hr"
50
+ },
51
+ "table": {
52
+ "title": "Table",
53
+ "items": "inserttable deletetable | cell row column"
54
+ },
55
+ "view": {
56
+ "title": "View",
57
+ "items": "visualblocks preview fullscreen"
58
+ }
59
+ }
60
+ </value>
61
+ </record>
62
+
63
+ <!-- See possible values in https://www.tiny.cloud/docs/tinymce/latest/available-toolbar-buttons/ -->
64
+ <record interface="plone.base.interfaces.controlpanel.ITinyMCESchema"
65
+ name="plone.toolbar"
66
+ >
67
+ <value>undo redo | styleselect | bold italic superscript | bullist numlist | nonbreaking | plonelink unlink | fullscreen</value>
68
+ </record>
69
+
70
+ </registry>
@@ -14,6 +14,7 @@ from plone.namedfile.field import NamedBlobImage
14
14
  from plone.namedfile.interfaces import IAvailableSizes
15
15
  from urllib.parse import urlparse
16
16
  from zope.component import getUtility
17
+ from zope.globalrequest import getRequest
17
18
  from zope.i18n import translate
18
19
  from zope.schema import getFields
19
20
  from zope.schema.interfaces import IVocabularyFactory
@@ -77,7 +78,18 @@ def geocode_object(obj):
77
78
  if not address:
78
79
  return
79
80
  geolocator = geopy.geocoders.Nominatim(user_agent="contact@imio.be", timeout=3)
80
- location = geolocator.geocode(address)
81
+ location = None
82
+ try:
83
+ location = geolocator.geocode(address)
84
+ except geopy.exc.GeocoderUnavailable:
85
+ api.portal.show_message(
86
+ _(
87
+ "Error: Geolocation service is unavailable. Your content is not geocoded."
88
+ ),
89
+ request=getRequest(),
90
+ type="warning",
91
+ )
92
+ return False
81
93
  if location:
82
94
  obj.geolocation = Geolocation(
83
95
  latitude=location.latitude, longitude=location.longitude
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imio.smartweb.common
3
- Version: 1.2.38
3
+ Version: 1.2.40
4
4
  Summary: Common utilities, vocabularies, taxonomies for imio.smartweb & co products
5
5
  Home-page: https://github.com/imio/imio.smartweb.common
6
6
  Author: iMio
@@ -178,6 +178,21 @@ Changelog
178
178
  =========
179
179
 
180
180
 
181
+ 1.2.40 (2025-11-05)
182
+ -------------------
183
+
184
+ - Catch Exception on geocoding to avoid blocking content creation
185
+ if geopy service is down
186
+ [remdub]
187
+
188
+
189
+ 1.2.39 (2025-11-03)
190
+ -------------------
191
+
192
+ - Migration to Plone 6.1.3
193
+ [boulch]
194
+
195
+
181
196
  1.2.38 (2025-09-10)
182
197
  -------------------
183
198
 
@@ -1,4 +1,4 @@
1
- imio.smartweb.common-1.2.38-py3.12-nspkg.pth,sha256=XZ3YhlzwpUCC8tXtelHRqxVxo3NWomIiMsUfUshrbeE,1011
1
+ imio.smartweb.common-1.2.40-py3.13-nspkg.pth,sha256=XZ3YhlzwpUCC8tXtelHRqxVxo3NWomIiMsUfUshrbeE,1011
2
2
  imio/smartweb/common/__init__.py,sha256=Na9XBfEQUMrm2c5jbqQgwWeg40ih0aXVG1vT8NeAjMQ,2709
3
3
  imio/smartweb/common/adapters.py,sha256=dG4MALuHPQI6lTeNvs5p4vVOxHoBBYaWN6g8J96KxzQ,1507
4
4
  imio/smartweb/common/adapters.zcml,sha256=VO3luZDtRL9FIIEghxPrqpx8paZF3m6MGEy-8kF1sOQ,437
@@ -20,7 +20,7 @@ imio/smartweb/common/subscribers.zcml,sha256=8v2lqNNVAHlrh2G3jtjVgr3Asrw8WJLS9jS
20
20
  imio/smartweb/common/testing.py,sha256=GKlYMHJUSxZFVX0o_R_c_Au1VYlkd-GjykQqfONHv7I,1985
21
21
  imio/smartweb/common/testing.zcml,sha256=7N-ALtklyWeLSzZmlxN2Tp-aZe_3An--w_6BGl1991E,172
22
22
  imio/smartweb/common/utility_overrides.zcml,sha256=Nn1q1FwQUyktB-EJM2qzPfsc-zUmHxX0Gj9OQTlYKQM,413
23
- imio/smartweb/common/utils.py,sha256=bQaR1i9_dID2_QKmHImZfJhWOYzK8AzFtI43-IG7xPo,7831
23
+ imio/smartweb/common/utils.py,sha256=Or5oV0l33wuoc9-Y4WDeVjXzGgluFZVyQWBoc_PRUjo,8195
24
24
  imio/smartweb/common/vocabularies.py,sha256=L4H73GQxLbIRZOpn_fHBU4-J-gY6BeiYjDyU90jV0U8,4809
25
25
  imio/smartweb/common/vocabularies.zcml,sha256=hnsIJYrof0k4gCj1wAJQtekXznvfFHIQXu-58eqXevk,1306
26
26
  imio/smartweb/common/widgets.zcml,sha256=GQ0aJblx44OJpj0HGXiyQJnFeSaJlOTMd1RAZn_1Qgk,375
@@ -63,10 +63,10 @@ imio/smartweb/common/faceted/widget.py,sha256=EMvFyEcS9t6lJAgz4zIkUnGAPd1fQcrv4A
63
63
  imio/smartweb/common/profiles/default/actions.xml,sha256=q5ajChGAPlPtpN_H80voDgg4C8Z-yDcIghUJIgFYtRg,1193
64
64
  imio/smartweb/common/profiles/default/browserlayer.xml,sha256=Up2dVOgFtzE43eLMNJY1xXvdUbQRSEpNxnySqSncFqQ,185
65
65
  imio/smartweb/common/profiles/default/catalog.xml,sha256=hB2Cu64TPp29EKICqbfixwa3AFmrDJVC784KeKTMX90,576
66
- imio/smartweb/common/profiles/default/metadata.xml,sha256=ay0Er9TDIXpaEiDIRwdMbhxZt_kZVzRQyQdQGCMc86w,432
66
+ imio/smartweb/common/profiles/default/metadata.xml,sha256=-sNjkCsLXvI_7uF69oQlt2DClwKxecy0vDVeN5oFQ-w,432
67
67
  imio/smartweb/common/profiles/default/rolemap.xml,sha256=XCdOazRleiwTAAZt3lzTsOw3ZtIwoPDqA8s4zXACvkc,1595
68
68
  imio/smartweb/common/profiles/default/registry/registry.xml,sha256=GXo92ntNu5GPhFOvDzde5ES7z6pgzrvK1aJtH-ElAHg,44314
69
- imio/smartweb/common/profiles/default/registry/tinymce.xml,sha256=bRDdJ_cS8NS3d5oleipENhY1qYDOgpV1vuYR6hRtitA,5143
69
+ imio/smartweb/common/profiles/default/registry/tinymce.xml,sha256=rHtD373Y3-3MYHv6xCo3HVhbGPijwfM8mDNghEwBVk8,4912
70
70
  imio/smartweb/common/profiles/testing/catalog.xml,sha256=CzEXLuHkT5NH-lRzTSDIR_3y_U7t2D7oBDm1oAJIoP4,214
71
71
  imio/smartweb/common/profiles/testing/metadata.xml,sha256=hPZnje-9ffhOiQfVamGoE_lOf9ISjBBf2ynQlg-Hv20,229
72
72
  imio/smartweb/common/profiles/testing/registry.xml,sha256=8lTQSGT_bnP2Z0y-86ztgpdQY8yDOLoXgqTC7fXiD2Y,1010
@@ -76,7 +76,7 @@ imio/smartweb/common/profiles/testing/types/Folder.xml,sha256=RPxIPgViAK0GGGTPKb
76
76
  imio/smartweb/common/profiles/uninstall/browserlayer.xml,sha256=glaCGlITwc36-1Fi2z0VCbLcoQ5GddkAhuW-SGYeZy8,130
77
77
  imio/smartweb/common/rest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  imio/smartweb/common/rest/configure.zcml,sha256=eT5gr-PcfMRxQzMME7T3nzz2Y0BVI4r9HyKHIccQC2s,575
79
- imio/smartweb/common/rest/endpoint.py,sha256=6HUaGF4KgC7p0-0XSVGgUAP7WG8at1052l_zCjIkquE,7494
79
+ imio/smartweb/common/rest/endpoint.py,sha256=QDxyYuhI7ipB-JkFbMsl2OS7Vk-Uc39NwTJ41ZKZVDc,7411
80
80
  imio/smartweb/common/rest/odwb.py,sha256=zRZxowUaE76xlW_blc92vRCzToUpnzw3O5mxFXaeScU,2249
81
81
  imio/smartweb/common/rest/search_filters.py,sha256=upZAZ6h50qiSXo0q22lUgNlcmX5elFtrIQs_jFBl1-s,3832
82
82
  imio/smartweb/common/rest/utils.py,sha256=9zc5ge_DmVuexxN09c3U6QAITAI1s2hc0m6eZuylSxM,1269
@@ -97,12 +97,14 @@ imio/smartweb/common/tests/test_local_roles.py,sha256=4cwVdtrkeecp3HRM26db6AJHJ7
97
97
  imio/smartweb/common/tests/test_permissions.py,sha256=LCz7Ixpd1IrzJeTV6Ov5GXjWNVz7h5UbpS9-DD4SjmE,1578
98
98
  imio/smartweb/common/tests/test_privacy.py,sha256=ObnquDYgd29P9hxnpF6VxuOtyVgxvW8HQnf8ZBDGnXk,2953
99
99
  imio/smartweb/common/tests/test_rest.py,sha256=cXbiQjXXHCaXBMCCVk_PjnjG6Z4A_EfVUEpYVfhHDBM,5303
100
+ imio/smartweb/common/tests/test_rest_endpoint.py,sha256=iwoPp5tSjyi9oXyPH7jmobe11Q8IM6TI8Y9CUy2ztiQ,8779
101
+ imio/smartweb/common/tests/test_rest_utils.py,sha256=-YpTtRerWUZQBZGEvWfJoDqmFSu5YDmnP44YfydJkL4,4598
100
102
  imio/smartweb/common/tests/test_robot.py,sha256=0Qdn5A8lXoVxQ9zE9IIhekSqfJJF003KiYqLVuDrros,955
101
103
  imio/smartweb/common/tests/test_setup.py,sha256=TxoQZrESFOgGRBN9uO8FzEOx57G550YICBd-NzaGHvY,2102
102
104
  imio/smartweb/common/tests/test_subscribers.py,sha256=AiJ5LTqGP2JKGJFYNeCCAMJL53DWO41iYQ9VYiA7DIA,1699
103
105
  imio/smartweb/common/tests/test_taxonomy.py,sha256=z4WawTa_hIsW7kVqQSlGBDWSnkeHAa2CPYFkfjZ0oRg,2600
104
106
  imio/smartweb/common/tests/test_text.py,sha256=Ll6TYnYxEBtpYe3T0yTOVT9wXWJaWGWW23eUV2jK92A,1570
105
- imio/smartweb/common/tests/test_utils.py,sha256=S4QrNVvkHv3Oxp45fWQ65dmGsk7POgUn_d3kB8DPdBk,9314
107
+ imio/smartweb/common/tests/test_utils.py,sha256=7cYVfHCxsty6U1UgW1Knzpi1yfzb8IG4TLVDQ2AZ6c8,9946
106
108
  imio/smartweb/common/tests/test_viewlets.py,sha256=NVEZWEmYo4K_-81Q6w-JTx2nT_3NBcJCl3946MAUnhU,1738
107
109
  imio/smartweb/common/tests/test_vocabularies.py,sha256=C-78USTvOHljuMSLkNb9onSRxLEJmBQLpktetAO74RE,848
108
110
  imio/smartweb/common/tests/test_vocabulary.py,sha256=4E5i5znt_hLhnnRay1iBaOZRmQyFJs8-OMdPm4iSGSg,1638
@@ -112,7 +114,7 @@ imio/smartweb/common/tests/resources/image_1400x800.png,sha256=Iy3bWGH4NBlgYC5Bo
112
114
  imio/smartweb/common/tests/resources/image_1800x700.png,sha256=aQcMisSFQO78c6QX4mO7qIfDb9SDRmfzTuPjxMJ_0HM,32643
113
115
  imio/smartweb/common/tests/robot/test_example.robot,sha256=3ClbWps1Pfr6-RHT4FmmzGWGmtJrNnDW53bJGP69Nz4,2011
114
116
  imio/smartweb/common/upgrades/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
- imio/smartweb/common/upgrades/configure.zcml,sha256=m-Trixs77TNqYg_WCdH8fqthgtCD0YGSjHkJ12jRpx8,16760
117
+ imio/smartweb/common/upgrades/configure.zcml,sha256=jaV3j9lpj6GYVhgRIMDhBwAyaqGGy929u5Xvh81N800,17424
116
118
  imio/smartweb/common/upgrades/upgrades.py,sha256=NObmvdWPUrfgaPJhTB_tIZsmLawxg5jbui-TRLhS2NE,5474
117
119
  imio/smartweb/common/upgrades/profiles/1007_to_1008/rolemap.xml,sha256=dKUuBq-2pO4dGtHshBisISSTBP8aWfbwTGHj_pJvY5A,1346
118
120
  imio/smartweb/common/upgrades/profiles/1008_to_1009/actions.xml,sha256=D41gp1PDVINobhCM8L11IMXABYatjmoUuW-EQkPT804,721
@@ -136,6 +138,7 @@ imio/smartweb/common/upgrades/profiles/1029_to_1030/registry.xml,sha256=cApJaG7d
136
138
  imio/smartweb/common/upgrades/profiles/1030_to_1031/registry/tinymce.xml,sha256=EYBpIVuUnQyfxntiLmXoTLiRHBzMlwzfFXmTqwMNrn0,5813
137
139
  imio/smartweb/common/upgrades/profiles/1031_to_1032/registry/tinymce.xml,sha256=3IOzZra7FIfxDO8DDIXwygL8z92f-cUCYEXq2GsVetg,2419
138
140
  imio/smartweb/common/upgrades/profiles/1032_to_1033/registry/tinymce.xml,sha256=V-8TeadC9Wk4aEBPOKOKd_48iSeyQEYlYXMLP2ILJ10,2421
141
+ imio/smartweb/common/upgrades/profiles/1033_to_1034/registry/tinymce.xml,sha256=vxpLtDuSApvFQJ5t7hI6_5cWtZ78BoycJgy1emPkhr8,2013
139
142
  imio/smartweb/common/viewlets/__init__.py,sha256=iwhKnzeBJLKxpRVjvzwiRE63_zNpIBfaKLITauVph-0,24
140
143
  imio/smartweb/common/viewlets/analytics.pt,sha256=J6e8fWmusLWMKXg3cJbNYV0LPS9ONz2yJ921vMvrrzE,33
141
144
  imio/smartweb/common/viewlets/colophon.pt,sha256=qaEUV2N5aZpAEcztLzK_R2eINBod1AByWxi11B1HYa0,1278
@@ -148,10 +151,10 @@ imio/smartweb/common/viewlets/skip_to_content.py,sha256=wm22NUf8Qh5uzz8p4vkLCdFN
148
151
  imio/smartweb/common/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
152
  imio/smartweb/common/widgets/select.py,sha256=5sV0UIgwu4AvwpgVmJeSZwUWz7OPD9VxYMowfmjcfbA,1313
150
153
  imio/smartweb/common/widgets/templates/ajaxselect_display.pt,sha256=v8TT2Xc19er6SUGWabgNfIdrayFYIjxyLK9H_XkIG3M,1209
151
- imio_smartweb_common-1.2.38.dist-info/licenses/LICENSE.GPL,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
152
- imio_smartweb_common-1.2.38.dist-info/licenses/LICENSE.rst,sha256=5dd78Fdt0e-oM2ICBrMpjHnT8vEP-jhBDF7akXni6B4,655
153
- imio_smartweb_common-1.2.38.dist-info/METADATA,sha256=fdTxjcsoQJD07U_hYF5HBvcyhNXJWkTHaKJfkIvycLI,19026
154
- imio_smartweb_common-1.2.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
155
- imio_smartweb_common-1.2.38.dist-info/namespace_packages.txt,sha256=Pg8AH8t9viMMW1hJbNZvTy_n2jXG2igIYUpon5RA4Js,19
156
- imio_smartweb_common-1.2.38.dist-info/top_level.txt,sha256=ZktC0EGzThvMTAin9_q_41rzvvfMT2FYbP8pbhSLMSA,5
157
- imio_smartweb_common-1.2.38.dist-info/RECORD,,
154
+ imio_smartweb_common-1.2.40.dist-info/licenses/LICENSE.GPL,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
155
+ imio_smartweb_common-1.2.40.dist-info/licenses/LICENSE.rst,sha256=5dd78Fdt0e-oM2ICBrMpjHnT8vEP-jhBDF7akXni6B4,655
156
+ imio_smartweb_common-1.2.40.dist-info/METADATA,sha256=bbUZwn_XP16TqLQeimrjRCkdjBeW7sHPRhmdgfXH-k0,19254
157
+ imio_smartweb_common-1.2.40.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
158
+ imio_smartweb_common-1.2.40.dist-info/namespace_packages.txt,sha256=Pg8AH8t9viMMW1hJbNZvTy_n2jXG2igIYUpon5RA4Js,19
159
+ imio_smartweb_common-1.2.40.dist-info/top_level.txt,sha256=ZktC0EGzThvMTAin9_q_41rzvvfMT2FYbP8pbhSLMSA,5
160
+ imio_smartweb_common-1.2.40.dist-info/RECORD,,