imio.smartweb.core 1.2.91__py3-none-any.whl → 1.3.1__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 (107) hide show
  1. imio/smartweb/core/browser/configure.zcml +16 -0
  2. imio/smartweb/core/browser/controlpanel.py +42 -5
  3. imio/smartweb/core/browser/search/search.py +15 -0
  4. imio/smartweb/core/browser/sitemap.py +155 -107
  5. imio/smartweb/core/browser/utils.py +4 -1
  6. imio/smartweb/core/configure.zcml +5 -1
  7. imio/smartweb/core/contents/__init__.py +2 -0
  8. imio/smartweb/core/contents/rest/base.py +5 -2
  9. imio/smartweb/core/contents/rest/campaign/__init__.py +0 -0
  10. imio/smartweb/core/contents/rest/campaign/configure.zcml +80 -0
  11. imio/smartweb/core/contents/rest/campaign/content.py +83 -0
  12. imio/smartweb/core/contents/rest/campaign/endpoint.py +211 -0
  13. imio/smartweb/core/contents/rest/campaign/view.pt +19 -0
  14. imio/smartweb/core/contents/rest/campaign/view.py +29 -0
  15. imio/smartweb/core/contents/rest/configure.zcml +1 -0
  16. imio/smartweb/core/contents/rest/directory/endpoint.py +11 -3
  17. imio/smartweb/core/contents/rest/events/endpoint.py +11 -3
  18. imio/smartweb/core/contents/rest/news/endpoint.py +11 -3
  19. imio/smartweb/core/contents/rest/search/endpoint.py +4 -1
  20. imio/smartweb/core/contents/rest/utils.py +3 -3
  21. imio/smartweb/core/interfaces.py +6 -0
  22. imio/smartweb/core/overrides.zcml +10 -0
  23. imio/smartweb/core/profiles/default/metadata.xml +1 -1
  24. imio/smartweb/core/profiles/ideabox/browserlayer.xml +7 -0
  25. imio/smartweb/core/profiles/ideabox/metadata.xml +5 -0
  26. imio/smartweb/core/profiles/ideabox/registry/iaideabox.xml +9 -0
  27. imio/smartweb/core/profiles/ideabox/types/imio.smartweb.CampaignView.xml +40 -0
  28. imio/smartweb/core/profiles/ideabox/types/imio.smartweb.Folder.xml +71 -0
  29. imio/smartweb/core/profiles/ideabox/types.xml +17 -0
  30. imio/smartweb/core/profiles/ideabox_uninstall/browserlayer.xml +7 -0
  31. imio/smartweb/core/profiles/ideabox_uninstall/types/imio.smartweb.Folder.xml +25 -0
  32. imio/smartweb/core/profiles/ideabox_uninstall/types.xml +4 -0
  33. imio/smartweb/core/profiles/testing/metadata.xml +1 -0
  34. imio/smartweb/core/profiles/testing/registry.xml +2 -2
  35. imio/smartweb/core/profiles.zcml +18 -0
  36. imio/smartweb/core/subscribers.py +27 -1
  37. imio/smartweb/core/subscribers.zcml +8 -0
  38. imio/smartweb/core/tests/resources/json_ideabox_campaign.json +222 -0
  39. imio/smartweb/core/tests/resources/json_ideabox_campaigns.json +425 -0
  40. imio/smartweb/core/tests/resources/json_ideabox_projects.json +1871 -0
  41. imio/smartweb/core/tests/test_iadeliberations.py +38 -40
  42. imio/smartweb/core/tests/test_ideabox.py +152 -0
  43. imio/smartweb/core/tests/test_procedure.py +7 -3
  44. imio/smartweb/core/tests/test_rest.py +6 -1
  45. imio/smartweb/core/tests/test_sitemap.py +81 -42
  46. imio/smartweb/core/tests/test_vocabularies.py +8 -10
  47. imio/smartweb/core/tests/test_vocabulary.py +18 -0
  48. imio/smartweb/core/upgrades/configure.zcml +16 -1
  49. imio/smartweb/core/upgrades/upgrades.py +64 -0
  50. imio/smartweb/core/utils.py +33 -0
  51. imio/smartweb/core/viewlets/offcanvas.pt +2 -0
  52. imio/smartweb/core/viewlets/ogptags.py +9 -0
  53. imio/smartweb/core/vocabularies.py +35 -4
  54. imio/smartweb/core/vocabularies.zcml +6 -0
  55. imio/smartweb/core/webcomponents/build/css/373.smartweb-webcomponents-compiled.css +1 -1
  56. imio/smartweb/core/webcomponents/build/css/420.smartweb-webcomponents-compiled.css +1 -0
  57. imio/smartweb/core/webcomponents/build/css/486.smartweb-webcomponents-compiled.css +1 -1
  58. imio/smartweb/core/webcomponents/build/css/666.smartweb-webcomponents-compiled.css +1 -0
  59. imio/smartweb/core/webcomponents/build/css/884.smartweb-webcomponents-compiled.css +1 -1
  60. imio/smartweb/core/webcomponents/build/css/919.smartweb-webcomponents-compiled.css +1 -1
  61. imio/smartweb/core/webcomponents/build/css/smartweb-webcomponents-compiled.css +1 -1
  62. imio/smartweb/core/webcomponents/build/js/141.smartweb-webcomponents-compiled.js +2 -0
  63. imio/smartweb/core/webcomponents/build/js/{828.smartweb-webcomponents-compiled.js.LICENSE.txt → 141.smartweb-webcomponents-compiled.js.LICENSE.txt} +3 -3
  64. imio/smartweb/core/webcomponents/build/js/15.smartweb-webcomponents-compiled.js +2 -0
  65. imio/smartweb/core/webcomponents/build/js/21.smartweb-webcomponents-compiled.js +1 -0
  66. imio/smartweb/core/webcomponents/build/js/218.smartweb-webcomponents-compiled.js +1 -1
  67. imio/smartweb/core/webcomponents/build/js/218.smartweb-webcomponents-compiled.js.LICENSE.txt +0 -5
  68. imio/smartweb/core/webcomponents/build/js/373.smartweb-webcomponents-compiled.js +1 -1
  69. imio/smartweb/core/webcomponents/build/js/486.smartweb-webcomponents-compiled.js +1 -1
  70. imio/smartweb/core/webcomponents/build/js/666.smartweb-webcomponents-compiled.js +1 -0
  71. imio/smartweb/core/webcomponents/build/js/799.smartweb-webcomponents-compiled.js +1 -1
  72. imio/smartweb/core/webcomponents/build/js/824.smartweb-webcomponents-compiled.js +1 -1
  73. imio/smartweb/core/webcomponents/build/js/884.smartweb-webcomponents-compiled.js +1 -1
  74. imio/smartweb/core/webcomponents/build/js/919.smartweb-webcomponents-compiled.js +1 -1
  75. imio/smartweb/core/webcomponents/build/js/922.smartweb-webcomponents-compiled.js +1 -0
  76. imio/smartweb/core/webcomponents/build/js/smartweb-webcomponents-compiled.js +1 -1
  77. imio/smartweb/core/webcomponents/package.json +80 -80
  78. imio/smartweb/core/webcomponents/src/components/Annuaire/Annuaire.scss +1 -0
  79. imio/smartweb/core/webcomponents/src/components/Campaign/Campaign.jsx +306 -0
  80. imio/smartweb/core/webcomponents/src/components/Campaign/Campaign.scss +672 -0
  81. imio/smartweb/core/webcomponents/src/components/Campaign/CampaignCard/CampaignCard.jsx +79 -0
  82. imio/smartweb/core/webcomponents/src/components/Campaign/CampaignContent/CampaignContent.jsx +193 -0
  83. imio/smartweb/core/webcomponents/src/components/Campaign/CampaignList/CampaignList.jsx +67 -0
  84. imio/smartweb/core/webcomponents/src/components/Campaign/Filters/Filter.jsx +259 -0
  85. imio/smartweb/core/webcomponents/src/components/Campaign/index.js +2 -0
  86. imio/smartweb/core/webcomponents/src/components/Filters/MainFilter.scss +1 -0
  87. imio/smartweb/core/webcomponents/src/components/News/NewsContent/NewsContent.jsx +0 -1
  88. imio/smartweb/core/webcomponents/src/components/Search/Search.jsx +54 -9
  89. imio/smartweb/core/webcomponents/src/components/Search/Search.scss +9 -0
  90. imio/smartweb/core/webcomponents/src/hooks/useAxios.js +45 -9
  91. imio/smartweb/core/webcomponents/src/index.jsx +2 -0
  92. imio/smartweb/core/webcomponents/src/index.scss +2 -2
  93. imio/smartweb/core/webcomponents/src/utils/CampaignMap.jsx +177 -0
  94. imio/smartweb/core/webcomponents/src/utils/Map.jsx +1 -1
  95. imio/smartweb/core/webcomponents/src/utils/Map.scss +15 -1
  96. imio/smartweb/core/webcomponents/src/utils/translation.js +247 -199
  97. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/METADATA +24 -1
  98. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/RECORD +105 -72
  99. imio/smartweb/core/webcomponents/build/js/499.smartweb-webcomponents-compiled.js +0 -2
  100. imio/smartweb/core/webcomponents/build/js/828.smartweb-webcomponents-compiled.js +0 -2
  101. /imio/smartweb/core/webcomponents/build/js/{499.smartweb-webcomponents-compiled.js.LICENSE.txt → 15.smartweb-webcomponents-compiled.js.LICENSE.txt} +0 -0
  102. /imio.smartweb.core-1.2.91-py3.12-nspkg.pth → /imio.smartweb.core-1.3.1-py3.12-nspkg.pth +0 -0
  103. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/LICENSE.GPL +0 -0
  104. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/LICENSE.rst +0 -0
  105. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/WHEEL +0 -0
  106. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/namespace_packages.txt +0 -0
  107. {imio.smartweb.core-1.2.91.dist-info → imio.smartweb.core-1.3.1.dist-info}/top_level.txt +0 -0
@@ -96,6 +96,22 @@
96
96
  layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
97
97
  />
98
98
 
99
+ <adapter
100
+ factory="imio.smartweb.core.browser.sitemap.SitemapNavtreeStrategy"
101
+ provides="imio.smartweb.core.browser.sitemap.ISmartwebNavtreeStrategy"
102
+ for="*
103
+ imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
104
+ />
105
+
106
+ <browser:page
107
+ name="sitemap_builder_view"
108
+ for="*"
109
+ class=".sitemap.CatalogSiteMap"
110
+ allowed_attributes="siteMap"
111
+ permission="zope.Public"
112
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
113
+ />
114
+
99
115
  <browser:page
100
116
  name="events_view"
101
117
  for="*"
@@ -22,11 +22,10 @@ class ISendinblueTextRowSchema(Interface):
22
22
 
23
23
 
24
24
  class ISmartwebControlPanel(Interface):
25
- url_formdefs_api = schema.TextLine(
26
- title=_("Url to get forms from your e-guichet"),
27
- description=_(
28
- "Example : https://COMMUNE-formulaires.guichet-citoyen.be/api/formdefs/"
29
- ),
25
+ # https://COMMUNE-formulaires.guichet-citoyen.be/api
26
+ url_ts = schema.TextLine(
27
+ title=_("Url to e-guichet"),
28
+ description=_("Example : https://COMMUNE.guichet-citoyen.be"),
30
29
  required=False,
31
30
  )
32
31
 
@@ -36,6 +35,21 @@ class ISmartwebControlPanel(Interface):
36
35
  required=False,
37
36
  )
38
37
 
38
+ iaideabox_api_username = schema.TextLine(
39
+ title=_(
40
+ "Username to consume e-guichet ideabox API (get Campaign, projects,...)"
41
+ ),
42
+ default="ideabox",
43
+ required=False,
44
+ )
45
+
46
+ iaideabox_api_password = schema.Password(
47
+ title=_(
48
+ "Password to consume e-guichet ideabox API (get Campaign, projects,...)"
49
+ ),
50
+ required=False,
51
+ )
52
+
39
53
  propose_directory_url = schema.URI(
40
54
  title=_("Url to propose a new citizen contact"),
41
55
  required=False,
@@ -203,17 +217,40 @@ class SmartwebControlPanelForm(RegistryEditForm):
203
217
  iadeliberation_pwd = api.portal.get_registry_record(
204
218
  "smartweb.iadeliberation_api_password"
205
219
  )
220
+
221
+ iaideabox_pwd = api.portal.get_registry_record(
222
+ "smartweb.iaideabox_api_password"
223
+ )
224
+
206
225
  secret_key_api = api.portal.get_registry_record("smartweb.secret_key_api")
207
226
 
208
227
  # if data is None when we apply changes for password fields we keep password from registry
209
228
  if not data.get("iadeliberation_api_password"):
210
229
  data["iadeliberation_api_password"] = iadeliberation_pwd
211
230
 
231
+ if not data.get("iaideabox_api_password"):
232
+ data["iaideabox_api_password"] = iaideabox_pwd
233
+
212
234
  if not data.get("secret_key_api"):
213
235
  data["secret_key_api"] = secret_key_api
214
236
 
215
237
  return super().applyChanges(data)
216
238
 
239
+ def updateFields(self):
240
+ """Override updateFields to hide fields based on ideabox profile installation."""
241
+ super(SmartwebControlPanelForm, self).updateFields()
242
+
243
+ # Check if the 'ideabox' profile is installed
244
+ portal_setup = api.portal.get_tool(name="portal_setup")
245
+ profile_version = portal_setup.getLastVersionForProfile(
246
+ "imio.smartweb.core:ideabox"
247
+ )
248
+
249
+ if profile_version == "unknown":
250
+ # Hide the field related to Idea Box if the profile is not installed
251
+ self.fields = self.fields.omit("iaideabox_api_username")
252
+ self.fields = self.fields.omit("iaideabox_api_password")
253
+
217
254
 
218
255
  SmartwebControlPanelView = layout.wrap_form(
219
256
  SmartwebControlPanelForm, ControlPanelFormWrapper
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ from imio.smartweb.core.contents.rest.search.endpoint import get_default_view_url
3
4
  from plone import api
4
5
  from Products.CMFPlone.browser.search import Search
5
6
  from Products.CMFPlone.utils import normalizeString
@@ -68,6 +69,20 @@ class Search(Search):
68
69
 
69
70
  return json.dumps(response)
70
71
 
72
+ @property
73
+ def are_views_available(self):
74
+ news = False if get_default_view_url("news") == "" else True
75
+ events = False if get_default_view_url("events") == "" else True
76
+ directory = False if get_default_view_url("directory") == "" else True
77
+ return json.dumps({"news": news, "events": events, "directory": directory})
78
+
79
+ @property
80
+ def context_user_has_roles(self):
81
+ # is current user has more role(s) than just authenticated
82
+ if len(api.user.get_roles()) > 1:
83
+ return True
84
+ return False
85
+
71
86
  @property
72
87
  def current_language(self):
73
88
  return api.portal.get_current_language()[:2]
@@ -1,49 +1,108 @@
1
+ from Acquisition import aq_inner
1
2
  from BTrees.OOBTree import OOBTree
2
3
  from imio.smartweb.core.config import DIRECTORY_URL
3
4
  from imio.smartweb.core.config import EVENTS_URL
4
5
  from imio.smartweb.core.config import NEWS_URL
5
- from imio.smartweb.core.contents.rest.utils import get_auth_sources_response
6
- from imio.smartweb.core.contents.rest.utils import get_entity_id
7
- from plone import api
8
- from plone.base.interfaces import IPloneSiteRoot
6
+ from imio.smartweb.core.contents.rest.directory.endpoint import DirectoryEndpointGet
7
+ from imio.smartweb.core.contents.rest.events.endpoint import EventsEndpointGet
8
+ from imio.smartweb.core.contents.rest.news.endpoint import NewsEndpointGet
9
+ from imio.smartweb.core.interfaces import IImioSmartwebCoreLayer
10
+ from plone.app.layout.navigation.navtree import buildFolderTree
9
11
  from plone.app.layout.sitemap.sitemap import SiteMapView
10
- from plone.registry.interfaces import IRegistry
12
+ from plone.base.interfaces import IPloneSiteRoot
13
+ from Products.CMFPlone.browser.navigation import CatalogSiteMap as BaseCatalogSiteMap
14
+ from Products.CMFPlone.browser.navtree import (
15
+ SitemapNavtreeStrategy as BaseSitemapNavtreeStrategy,
16
+ )
17
+ from Products.CMFPlone.browser.navtree import (
18
+ SitemapQueryBuilder as BaseSitemapQueryBuilder,
19
+ )
11
20
  from Products.CMFCore.utils import getToolByName
12
21
  from Products.CMFPlone.utils import normalizeString
13
- from zope.component import getUtility
22
+ from zope.component import getMultiAdapter
23
+ from zope.interface import implementer
24
+ from zope.interface import Interface
14
25
 
15
26
  import logging
27
+ import Missing
16
28
 
17
29
  logger = logging.getLogger("imio.smartweb.core")
18
30
 
19
31
 
32
+ FRIENDLY_TYPES = [
33
+ "Collection",
34
+ "Image",
35
+ "Link",
36
+ "imio.smartweb.Folder",
37
+ "imio.smartweb.Page",
38
+ "imio.smartweb.PortalPage",
39
+ "imio.smartweb.Procedure",
40
+ "imio.smartweb.CirkwiView",
41
+ "imio.smartweb.DirectoryView",
42
+ "imio.smartweb.EventsView",
43
+ "imio.smartweb.NewsView",
44
+ ]
45
+
46
+
47
+ def get_endpoint_data(obj, request):
48
+ """Retourne les données issues du bon endpoint pour un objet donné"""
49
+ endpoint_mapping = {
50
+ "imio.smartweb.DirectoryView": DirectoryEndpointGet,
51
+ "imio.smartweb.EventsView": EventsEndpointGet,
52
+ "imio.smartweb.NewsView": NewsEndpointGet,
53
+ }
54
+
55
+ endpoint_class = endpoint_mapping.get(obj.portal_type)
56
+ if not endpoint_class:
57
+ return {}
58
+
59
+ endpoint = endpoint_class()
60
+ batch_size = 1000 if obj.portal_type == "imio.smartweb.DirectoryView" else 365
61
+ return (
62
+ endpoint.reply_for_given_object(
63
+ obj, request, fullobjects=0, batch_size=batch_size
64
+ )
65
+ or {}
66
+ )
67
+
68
+
69
+ def format_sitemap_items(items, base_url):
70
+ """Formatte les items pour le sitemap"""
71
+ formatted_items = []
72
+ for item in items:
73
+ item_id = normalizeString(item.get("title"))
74
+ item_uid = item.get("id")
75
+ lastmod = item.get("modified") or "1970-01-01T00:00:00Z"
76
+ formatted_items.append(
77
+ {
78
+ "loc": f"{base_url}/{item_id}?u={item_uid}",
79
+ "lastmod": lastmod,
80
+ "Title": item_id,
81
+ "Description": item.get("description", ""),
82
+ "getURL": f"{base_url}/{item_id}?u={item_uid}",
83
+ "getRemoteUrl": Missing.Value,
84
+ "currentItem": False,
85
+ "currentParent": False,
86
+ "normalized_review_state": "published",
87
+ "normalized_portal_type": "imio-smartweb-directory-item",
88
+ }
89
+ )
90
+ return formatted_items
91
+
92
+
20
93
  class CustomSiteMapView(SiteMapView):
94
+ """Custom sitemap view. (get items for sitemap.xml.gz)"""
21
95
 
22
96
  def objects(self):
23
97
  """Returns the data to create the sitemap."""
24
-
25
- friendlytypes = [
26
- "Link",
27
- "imio.smartweb.Folder",
28
- "LIF",
29
- "LRF",
30
- "imio.smartweb.Page",
31
- "imio.smartweb.Procedure",
32
- "File",
33
- "Collection",
34
- "imio.smartweb.PortalPage",
35
- "Image",
36
- "imio.smartweb.CirkwiView",
37
- ]
98
+ friendlytypes = FRIENDLY_TYPES
38
99
 
39
100
  catalog = getToolByName(self.context, "portal_catalog")
40
- query = {}
41
- utils = getToolByName(self.context, "plone_utils")
42
- query["portal_type"] = utils.getUserFriendlyTypes(friendlytypes)
43
- registry = getUtility(IRegistry)
44
- typesUseViewActionInListings = frozenset(
45
- registry.get("plone.types_use_view_action_in_listings", [])
46
- )
101
+ query = {
102
+ "portal_type": getToolByName(
103
+ self.context, "plone_utils"
104
+ ).getUserFriendlyTypes(friendlytypes)
105
+ }
47
106
 
48
107
  is_plone_site_root = IPloneSiteRoot.providedBy(self.context)
49
108
  if not is_plone_site_root:
@@ -56,93 +115,82 @@ class CustomSiteMapView(SiteMapView):
56
115
  value = (item.modified.micros(), item.modified.ISO8601())
57
116
  default_page_modified[key] = value
58
117
 
59
- # The plone site root is not catalogued.
60
118
  if is_plone_site_root:
61
119
  loc = self.context.absolute_url()
62
120
  date = self.context.modified()
63
- # Comparison must be on GMT value
64
- modified = (date.micros(), date.ISO8601())
65
- default_modified = default_page_modified.get(loc, None)
66
- if default_modified is not None:
67
- modified = max(modified, default_modified)
68
- lastmod = modified[1]
69
- yield {
70
- "loc": loc,
71
- "lastmod": lastmod,
72
- # 'changefreq': 'always',
73
- # hourly/daily/weekly/monthly/yearly/never
74
- # 'prioriy': 0.5, # 0.0 to 1.0
75
- }
121
+ modified = max(
122
+ (date.micros(), date.ISO8601()), default_page_modified.get(loc, (0, ""))
123
+ )
124
+ yield {"loc": loc, "lastmod": modified[1]}
76
125
 
77
126
  query["is_default_page"] = False
78
127
  for item in catalog.searchResults(query):
79
128
  loc = item.getURL()
80
129
  date = item.modified
81
- # Comparison must be on GMT value
82
- modified = (date.micros(), date.ISO8601())
83
- default_modified = default_page_modified.get(loc, None)
84
- if default_modified is not None:
85
- modified = max(modified, default_modified)
86
- lastmod = modified[1]
87
- if item.portal_type in typesUseViewActionInListings:
88
- loc += "/view"
89
- yield {
90
- "loc": loc,
91
- "lastmod": lastmod,
92
- }
93
-
94
- auth_sources = {
95
- "directory": {
96
- "entity_reg_var": "smartweb.directory_entity_uid",
97
- "main_rest_view": "smartweb.default_directory_view",
98
- "vocabulary": "imio.smartweb.vocabulary.RemoteDirectoryEntities",
99
- "url": DIRECTORY_URL,
100
- },
101
- "events": {
102
- "entity_reg_var": "smartweb.events_entity_uid",
103
- "main_rest_view": "smartweb.default_events_view",
104
- "vocabulary": "imio.smartweb.vocabulary.RemoteEventsEntities",
105
- "url": EVENTS_URL,
106
- },
107
- "news": {
108
- "entity_reg_var": "smartweb.news_entity_uid",
109
- "main_rest_view": "smartweb.default_news_view",
110
- "vocabulary": "imio.smartweb.vocabulary.RemoteNewsEntities",
111
- "url": NEWS_URL,
112
- },
113
- }
114
- for auth_source_key, auth_source_value in auth_sources.items():
115
- entity_uid = api.portal.get_registry_record(
116
- auth_source_value.get("entity_reg_var")
117
- )
118
- if entity_uid is None:
119
- return self.request.response.setStatus(404, "No entity found")
120
- entity_id = get_entity_id(auth_source_value.get("vocabulary"), entity_uid)
121
- auth_source_uid = api.portal.get_registry_record(
122
- f"smartweb.default_{auth_source_key}_view", default=None
130
+ modified = max(
131
+ (date.micros(), date.ISO8601()), default_page_modified.get(loc, (0, ""))
123
132
  )
124
- if auth_source_uid is None:
125
- return self.request.response.setStatus(
126
- 404, "No default authentic source found"
127
- )
128
- obj = api.content.get(UID=auth_source_uid)
129
- if obj is None:
130
- logger.warning(
131
- f"Seems that a main authentic view (for {auth_source_key}) is not found"
132
- )
133
- continue
134
- auth_source_view_url = obj.absolute_url()
135
- results = get_auth_sources_response(
136
- auth_source_key, normalizeString(entity_id), (60 * 60 * 24)
137
- ).json()
138
- if results is None or results.get("type") == "ValueError":
133
+ yield {"loc": loc, "lastmod": modified[1]}
134
+
135
+ for brain in catalog(
136
+ portal_type=[
137
+ "imio.smartweb.EventsView",
138
+ "imio.smartweb.NewsView",
139
+ "imio.smartweb.DirectoryView",
140
+ ]
141
+ ):
142
+ obj = brain.getObject()
143
+ data = get_endpoint_data(obj, obj.REQUEST)
144
+ yield from format_sitemap_items(data.get("items", {}), obj.absolute_url())
145
+
146
+
147
+ @implementer(IImioSmartwebCoreLayer)
148
+ class CatalogSiteMap(BaseCatalogSiteMap):
149
+ def siteMap(self):
150
+ context = aq_inner(self.context)
151
+
152
+ queryBuilder = SitemapQueryBuilder(context)
153
+ query = queryBuilder()
154
+ strategy = getMultiAdapter((context, self), ISmartwebNavtreeStrategy)
155
+ base_folder_tree = buildFolderTree(
156
+ context, obj=context, query=query, strategy=strategy
157
+ )
158
+
159
+ for child in base_folder_tree.get("children"):
160
+ obj = child.get("item").getObject()
161
+ data = get_endpoint_data(obj, obj.REQUEST)
162
+ if not data:
139
163
  continue
140
- for item in results.get("items"):
141
- item_id = normalizeString(item.get("title"))
142
- item_uid = item.get("id")
143
- loc = f"{auth_source_view_url}/{item_id}?u={item_uid}"
144
- lastmod = item.get("modified")
145
- yield {
146
- "loc": loc,
147
- "lastmod": lastmod,
148
- }
164
+ child["children"] = format_sitemap_items(
165
+ data.get("items", []), obj.absolute_url()
166
+ )
167
+
168
+ return base_folder_tree
169
+
170
+
171
+ class SitemapQueryBuilder(BaseSitemapQueryBuilder):
172
+ """Construit la requête pour le sitemap avec des types de contenu personnalisés."""
173
+
174
+ def __init__(self, context):
175
+ self.context = context
176
+
177
+ def __call__(self):
178
+ query = {}
179
+ custom_query = getattr(self.context, "getCustomNavQuery", None)
180
+ if custom_query and callable(custom_query):
181
+ query = custom_query()
182
+ query["portal_type"] = FRIENDLY_TYPES
183
+ query["review_state"] = "published"
184
+ return query
185
+
186
+
187
+ class ISmartwebNavtreeStrategy(Interface):
188
+ """Marker interface for the smartweb navtree strategy."""
189
+
190
+
191
+ @implementer(ISmartwebNavtreeStrategy)
192
+ class SitemapNavtreeStrategy(BaseSitemapNavtreeStrategy):
193
+
194
+ def nodeFilter(self, node):
195
+ # Return all nodes in sitemap even if they are exclude from nav
196
+ return True
@@ -2,6 +2,8 @@
2
2
  from imio.smartweb.core.contents import IPages
3
3
  from imio.smartweb.core.contents.pages.procedure.utils import sign_url
4
4
  from imio.smartweb.core.utils import get_plausible_vars
5
+ from imio.smartweb.core.utils import get_ts_api_url
6
+ from imio.smartweb.core.utils import get_value_from_registry
5
7
  from imio.smartweb.locales import SmartwebMessageFactory as _
6
8
  from plone import api
7
9
  from plone.api.portal import get_registry_record
@@ -54,7 +56,8 @@ class UtilsView(BrowserView):
54
56
 
55
57
  def is_eguichet_aware(self):
56
58
  self.request.response.setHeader("Content-Type", "application/json")
57
- url = api.portal.get_registry_record("smartweb.url_formdefs_api")
59
+ wcs_api = get_ts_api_url("wcs")
60
+ url = f"{wcs_api}/formdefs/"
58
61
  key = api.portal.get_registry_record("smartweb.secret_key_api")
59
62
  orig = "ia.smartweb"
60
63
  if not url:
@@ -14,7 +14,10 @@
14
14
  <include package="collective.solr" />
15
15
  <include package="collective.taxonomy" />
16
16
  <include package="collective.z3cform.datagridfield" />
17
-
17
+
18
+ <!-- Call this one before overrides adapter zope.container.interfaces.INameChooser-->
19
+ <include package="plone.app.content" />
20
+
18
21
  <!-- Temporary fix for plone.gallery 1.0.1 -->
19
22
  <include package="plone.app.contentmenu" />
20
23
  <include package="plone.gallery" />
@@ -24,6 +27,7 @@
24
27
  <include package="imio.smartweb.locales" />
25
28
 
26
29
  <include file="indexers.zcml" />
30
+ <include file="overrides.zcml" />
27
31
  <include file="permissions.zcml" />
28
32
  <include file="profiles.zcml" />
29
33
  <include file="subscribers.zcml" />
@@ -21,6 +21,8 @@ from .publication.content import Publication # NOQA
21
21
  from .publication.content import IPublication # NOQA
22
22
  from .rest.base import RestView # NOQA
23
23
  from .rest.base import IRestView # NOQA
24
+ from .rest.campaign.content import CampaignView # NOQA
25
+ from .rest.campaign.content import ICampaignView # NOQA
24
26
  from .rest.directory.content import DirectoryView # NOQA
25
27
  from .rest.directory.content import IDirectoryView # NOQA
26
28
  from .rest.events.content import EventsView # NOQA
@@ -15,9 +15,12 @@ class BaseEndpoint(object):
15
15
  language = "fr"
16
16
  remote_endpoint = ""
17
17
 
18
- def __init__(self, context, request):
18
+ def __init__(self, context, request, fullobjects=1, batch_size=0):
19
+
19
20
  self.context = context
20
21
  self.request = request
22
+ self.fullobjects = fullobjects
23
+ self.batch_size = batch_size
21
24
 
22
25
  def __call__(self):
23
26
  results = get_json(self.query_url, timeout=20)
@@ -46,7 +49,7 @@ class BaseEndpoint(object):
46
49
  item[f"{field}_full_scale"] = (
47
50
  f"{item['@id']}/@@images/{field}/?cache_key={modified_hash}"
48
51
  )
49
- del item[field]
52
+ item.pop(field, None)
50
53
 
51
54
  def construct_query_string(self, params):
52
55
  params = "&".join(params)
File without changes
@@ -0,0 +1,80 @@
1
+ <configure
2
+ xmlns="http://namespaces.zope.org/zope"
3
+ xmlns:browser="http://namespaces.zope.org/browser"
4
+ xmlns:plone="http://namespaces.plone.org/plone"
5
+ i18n_domain="imio.smartweb">
6
+
7
+ <browser:page
8
+ name="view"
9
+ for="imio.smartweb.core.contents.ICampaignView"
10
+ class=".view.CampaignViewView"
11
+ template="view.pt"
12
+ permission="zope2.View"
13
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
14
+ />
15
+
16
+ <plone:service
17
+ name="@results"
18
+ method="GET"
19
+ accept="application/json"
20
+ for="imio.smartweb.core.contents.ICampaignView"
21
+ factory=".endpoint.CampaignEndpointGet"
22
+ permission="zope2.View"
23
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
24
+ />
25
+
26
+ <plone:service
27
+ name="@auth"
28
+ method="GET"
29
+ accept="text/plain"
30
+ for="imio.smartweb.core.contents.ICampaignView"
31
+ factory=".endpoint.AuthCampaignEndpointGet"
32
+ permission="zope.Public"
33
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
34
+ />
35
+
36
+ <plone:service
37
+ name="@zones"
38
+ method="GET"
39
+ accept="application/json"
40
+ for="imio.smartweb.core.contents.ICampaignView"
41
+ factory=".endpoint.ZonesEndpointGet"
42
+ permission="zope2.View"
43
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
44
+ />
45
+
46
+
47
+ <!-- endpoint to get topics from TS -->
48
+ <plone:service
49
+ name="@ts_topics"
50
+ method="GET"
51
+ accept="application/json"
52
+ for="imio.smartweb.core.contents.ICampaignView"
53
+ factory=".endpoint.TsTopicsEndpointGet"
54
+ permission="zope2.View"
55
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
56
+ />
57
+
58
+ <!-- endpoint to expose smartweb topics to TS -->
59
+ <plone:service
60
+ name="@topics"
61
+ method="GET"
62
+ accept="application/json"
63
+ for="plone.dexterity.interfaces.IDexterityContent"
64
+ factory=".endpoint.TopicsEndpointGet"
65
+ permission="zope2.View"
66
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
67
+ />
68
+
69
+ <!-- endpoint to expose smartweb AND TS topics to campaign view -->
70
+ <plone:service
71
+ name="@all_topics"
72
+ method="GET"
73
+ accept="application/json"
74
+ for="imio.smartweb.core.contents.ICampaignView"
75
+ factory=".endpoint.AllTopicsEndpointGet"
76
+ permission="zope2.View"
77
+ layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
78
+ />
79
+
80
+ </configure>
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from collective.instancebehavior.interfaces import IInstanceBehaviorAssignableContent
4
+ from imio.smartweb.core.contents import RestView
5
+ from imio.smartweb.core.utils import get_basic_auth_json
6
+ from imio.smartweb.core.utils import get_ts_api_url
7
+ from imio.smartweb.core.utils import get_value_from_registry
8
+ from imio.smartweb.locales import SmartwebMessageFactory as _
9
+ from plone import api
10
+ from plone.app.content.namechooser import NormalizingNameChooser
11
+ from plone.i18n.normalizer import idnormalizer
12
+ from plone.supermodel import model
13
+ from zope import schema
14
+ from zope.component import getUtility
15
+ from zope.container.interfaces import INameChooser
16
+ from zope.interface import implementer
17
+
18
+ import logging
19
+
20
+ logger = logging.getLogger("imio.smartweb.core")
21
+
22
+
23
+ class ICampaignView(model.Schema):
24
+ """ """
25
+
26
+ linked_campaign = schema.Choice(
27
+ vocabulary="imio.smartweb.vocabulary.PublikCampaigns",
28
+ title=_("E-Guichet campaign"),
29
+ required=False,
30
+ default=None,
31
+ )
32
+
33
+ propose_project_url = schema.TextLine(
34
+ title=_("Propose project URL"),
35
+ description=_("URL to propose a project"),
36
+ required=False,
37
+ )
38
+
39
+ nb_results = schema.Int(
40
+ title=_("Number of items to display"), default=20, required=True
41
+ )
42
+
43
+ display_map = schema.Bool(
44
+ title=_("Display map"),
45
+ description=_("If selected, map will be displayed"),
46
+ required=False,
47
+ default=True,
48
+ )
49
+
50
+
51
+ @implementer(ICampaignView, IInstanceBehaviorAssignableContent)
52
+ class CampaignView(RestView):
53
+ """Campaign class"""
54
+
55
+
56
+ @implementer(INameChooser)
57
+ class CampaignNameChooser(NormalizingNameChooser):
58
+ def chooseName(self, name, obj):
59
+ """Génère un ID basé sur le titre récupéré de l'API."""
60
+ if ICampaignView.providedBy(obj):
61
+ if not obj.title:
62
+ # Récupération du titre via l'API
63
+ wcs_api = get_ts_api_url("wcs")
64
+ ts_campaign_endpoint = "imio-ideabox-campagne"
65
+ url = f"{wcs_api}/cards/{ts_campaign_endpoint}/{obj.linked_campaign}"
66
+ user = get_value_from_registry("smartweb.iaideabox_api_username")
67
+ pwd = get_value_from_registry("smartweb.iaideabox_api_password")
68
+ json_campaign = get_basic_auth_json(url, user, pwd)
69
+
70
+ if json_campaign and "fields" in json_campaign:
71
+ obj.title = json_campaign["fields"].get(
72
+ "titre", "campagne-sans-nom"
73
+ )
74
+ else:
75
+ obj.title = "campaign-without-name"
76
+
77
+ # Normaliser l'ID en utilisant le titre
78
+ normalized_id = idnormalizer.normalize(obj.title)
79
+
80
+ # Vérifier que l'ID généré est bien unique dans le conteneur
81
+ return super().chooseName(normalized_id, obj)
82
+
83
+ return super().chooseName(name, obj)