imio.smartweb.core 1.2.32__py3-none-any.whl → 1.2.34__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.
- imio/smartweb/core/browser/configure.zcml +16 -0
- imio/smartweb/core/browser/sitemap.py +135 -0
- imio/smartweb/core/browser/static/smartweb-view-compiled.js +1 -1
- imio/smartweb/core/browser/static/src/view.js +11 -0
- imio/smartweb/core/browser/utils.py +1 -2
- imio/smartweb/core/browser/vocabulary.py +35 -0
- imio/smartweb/core/contents/sections/contact/utils.py +0 -4
- imio/smartweb/core/contents/sections/macros.pt +13 -9
- imio/smartweb/core/contents/sections/text/view.pt +4 -3
- imio/smartweb/core/profiles/default/diff_tool.xml +8 -0
- imio/smartweb/core/profiles/default/metadata.xml +1 -1
- imio/smartweb/core/profiles/default/repositorytool.xml +1 -0
- imio/smartweb/core/profiles/default/types/imio.smartweb.SectionHTML.xml +0 -1
- imio/smartweb/core/profiles/validation/contentrules.xml +102 -0
- imio/smartweb/core/profiles.zcml +8 -0
- imio/smartweb/core/tests/test_sections.py +18 -0
- imio/smartweb/core/tests/test_sitemap.py +155 -0
- imio/smartweb/core/upgrades/configure.zcml +18 -0
- imio/smartweb/core/upgrades/profiles/1052_to_1053/diff_tool.xml +8 -0
- imio/smartweb/core/upgrades/profiles/1052_to_1053/repositorytool.xml +9 -0
- imio/smartweb/core/upgrades/profiles/1052_to_1053/types/imio.smartweb.SectionHTML.xml +14 -0
- imio/smartweb/core/viewlets/ogptags.py +1 -2
- imio/smartweb/core/vocabularies.py +2 -0
- imio/smartweb/core/webcomponents/build/js/392.smartweb-webcomponents-compiled.js +1 -1
- imio/smartweb/core/webcomponents/src/components/Filters/DateFilter.jsx +1 -0
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/METADATA +35 -3
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/RECORD +33 -25
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/WHEEL +1 -1
- /imio.smartweb.core-1.2.32-py3.8-nspkg.pth → /imio.smartweb.core-1.2.34-py3.10-nspkg.pth +0 -0
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/LICENSE.GPL +0 -0
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/LICENSE.rst +0 -0
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/namespace_packages.txt +0 -0
- {imio.smartweb.core-1.2.32.dist-info → imio.smartweb.core-1.2.34.dist-info}/top_level.txt +0 -0
@@ -88,6 +88,14 @@
|
|
88
88
|
layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
|
89
89
|
/>
|
90
90
|
|
91
|
+
<browser:page
|
92
|
+
name="sitemap.xml.gz"
|
93
|
+
for="plone.base.interfaces.INavigationRoot"
|
94
|
+
class=".sitemap.CustomSiteMapView"
|
95
|
+
permission="zope2.Public"
|
96
|
+
layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
|
97
|
+
/>
|
98
|
+
|
91
99
|
<browser:page
|
92
100
|
name="events_view"
|
93
101
|
for="*"
|
@@ -112,6 +120,14 @@
|
|
112
120
|
layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
|
113
121
|
/>
|
114
122
|
|
123
|
+
<browser:page
|
124
|
+
name="getVocabulary"
|
125
|
+
for="*"
|
126
|
+
class=".vocabulary.SmartwebVocabularyView"
|
127
|
+
permission="zope2.View"
|
128
|
+
layer="imio.smartweb.core.interfaces.IImioSmartwebCoreLayer"
|
129
|
+
/>
|
130
|
+
|
115
131
|
<z3c:widgetTemplate
|
116
132
|
mode="input"
|
117
133
|
widget="plone.app.z3cform.interfaces.ILinkWidget"
|
@@ -0,0 +1,135 @@
|
|
1
|
+
from BTrees.OOBTree import OOBTree
|
2
|
+
from imio.smartweb.core.config import DIRECTORY_URL
|
3
|
+
from imio.smartweb.core.config import EVENTS_URL
|
4
|
+
from imio.smartweb.core.config import NEWS_URL
|
5
|
+
from imio.smartweb.core.utils import get_json
|
6
|
+
from plone import api
|
7
|
+
from plone.base.interfaces import IPloneSiteRoot
|
8
|
+
from plone.app.layout.sitemap.sitemap import SiteMapView
|
9
|
+
from plone.registry.interfaces import IRegistry
|
10
|
+
from Products.CMFCore.utils import getToolByName
|
11
|
+
from Products.CMFPlone.utils import normalizeString
|
12
|
+
from zope.component import getUtility
|
13
|
+
|
14
|
+
|
15
|
+
class CustomSiteMapView(SiteMapView):
|
16
|
+
|
17
|
+
def objects(self):
|
18
|
+
"""Returns the data to create the sitemap."""
|
19
|
+
|
20
|
+
friendlytypes = [
|
21
|
+
"Link",
|
22
|
+
"imio.smartweb.Folder",
|
23
|
+
"LIF",
|
24
|
+
"LRF",
|
25
|
+
"imio.smartweb.Page",
|
26
|
+
"imio.smartweb.Procedure",
|
27
|
+
"File",
|
28
|
+
"Collection",
|
29
|
+
"imio.smartweb.PortalPage",
|
30
|
+
"Image",
|
31
|
+
"imio.smartweb.CirkwiView",
|
32
|
+
]
|
33
|
+
|
34
|
+
catalog = getToolByName(self.context, "portal_catalog")
|
35
|
+
query = {}
|
36
|
+
utils = getToolByName(self.context, "plone_utils")
|
37
|
+
query["portal_type"] = utils.getUserFriendlyTypes(friendlytypes)
|
38
|
+
registry = getUtility(IRegistry)
|
39
|
+
typesUseViewActionInListings = frozenset(
|
40
|
+
registry.get("plone.types_use_view_action_in_listings", [])
|
41
|
+
)
|
42
|
+
|
43
|
+
is_plone_site_root = IPloneSiteRoot.providedBy(self.context)
|
44
|
+
if not is_plone_site_root:
|
45
|
+
query["path"] = "/".join(self.context.getPhysicalPath())
|
46
|
+
|
47
|
+
query["is_default_page"] = True
|
48
|
+
default_page_modified = OOBTree()
|
49
|
+
for item in catalog.searchResults(query):
|
50
|
+
key = item.getURL().rsplit("/", 1)[0]
|
51
|
+
value = (item.modified.micros(), item.modified.ISO8601())
|
52
|
+
default_page_modified[key] = value
|
53
|
+
|
54
|
+
# The plone site root is not catalogued.
|
55
|
+
if is_plone_site_root:
|
56
|
+
loc = self.context.absolute_url()
|
57
|
+
date = self.context.modified()
|
58
|
+
# Comparison must be on GMT value
|
59
|
+
modified = (date.micros(), date.ISO8601())
|
60
|
+
default_modified = default_page_modified.get(loc, None)
|
61
|
+
if default_modified is not None:
|
62
|
+
modified = max(modified, default_modified)
|
63
|
+
lastmod = modified[1]
|
64
|
+
yield {
|
65
|
+
"loc": loc,
|
66
|
+
"lastmod": lastmod,
|
67
|
+
# 'changefreq': 'always',
|
68
|
+
# hourly/daily/weekly/monthly/yearly/never
|
69
|
+
# 'prioriy': 0.5, # 0.0 to 1.0
|
70
|
+
}
|
71
|
+
|
72
|
+
query["is_default_page"] = False
|
73
|
+
for item in catalog.searchResults(query):
|
74
|
+
loc = item.getURL()
|
75
|
+
date = item.modified
|
76
|
+
# Comparison must be on GMT value
|
77
|
+
modified = (date.micros(), date.ISO8601())
|
78
|
+
default_modified = default_page_modified.get(loc, None)
|
79
|
+
if default_modified is not None:
|
80
|
+
modified = max(modified, default_modified)
|
81
|
+
lastmod = modified[1]
|
82
|
+
if item.portal_type in typesUseViewActionInListings:
|
83
|
+
loc += "/view"
|
84
|
+
yield {
|
85
|
+
"loc": loc,
|
86
|
+
"lastmod": lastmod,
|
87
|
+
}
|
88
|
+
|
89
|
+
auth_sources = [
|
90
|
+
{"directory": {"path": DIRECTORY_URL, "type": "imio.directory.Contact"}},
|
91
|
+
{"news": {"path": NEWS_URL, "type": "imio.news.NewsItem"}},
|
92
|
+
{"events": {"path": EVENTS_URL, "type": "imio.events.Event"}},
|
93
|
+
]
|
94
|
+
for auth_source in auth_sources:
|
95
|
+
for auth_source_key, auth_source_value in auth_source.items():
|
96
|
+
entity_uid = api.portal.get_registry_record(
|
97
|
+
f"smartweb.{auth_source_key}_entity_uid", default=None
|
98
|
+
)
|
99
|
+
auth_source_uid = api.portal.get_registry_record(
|
100
|
+
f"smartweb.default_{auth_source_key}_view", default=None
|
101
|
+
)
|
102
|
+
if entity_uid is None or auth_source_uid is None:
|
103
|
+
continue
|
104
|
+
brains = api.content.find(UID=auth_source_uid)
|
105
|
+
obj = brains[0].getObject()
|
106
|
+
auth_source_view_url = obj.absolute_url()
|
107
|
+
endpoint = "@search"
|
108
|
+
if auth_source_key == "directory":
|
109
|
+
selected_container = f"selected_entities={entity_uid}"
|
110
|
+
if auth_source_key == "news":
|
111
|
+
selected_container = (
|
112
|
+
f"selected_news_folders={obj.selected_news_folder}"
|
113
|
+
)
|
114
|
+
if auth_source_key == "events":
|
115
|
+
selected_container = f"selected_agendas={obj.selected_agenda}"
|
116
|
+
endpoint = "@events"
|
117
|
+
params = [
|
118
|
+
selected_container,
|
119
|
+
f"portal_type={auth_source_value.get('type')}",
|
120
|
+
"fullobjects=0",
|
121
|
+
"sort_on=sortable_title",
|
122
|
+
]
|
123
|
+
url = f"{auth_source_value.get('path')}/{endpoint}?{'&'.join(params)}"
|
124
|
+
results = get_json(url)
|
125
|
+
if results is None:
|
126
|
+
continue
|
127
|
+
for item in results.get("items"):
|
128
|
+
item_id = normalizeString(item.get("title"))
|
129
|
+
item_uid = item.get("id")
|
130
|
+
loc = f"{auth_source_view_url}/{item_id}?u={item_uid}"
|
131
|
+
lastmod = item.get("modified")
|
132
|
+
yield {
|
133
|
+
"loc": loc,
|
134
|
+
"lastmod": lastmod,
|
135
|
+
}
|
@@ -1 +1 @@
|
|
1
|
-
(()=>{"use strict";jQuery(document).ready((function(e){e(".opening_informations").click((function(
|
1
|
+
(()=>{"use strict";jQuery(document).ready((function(e){e(".opening_informations").click((function(t){e(this).siblings(".table_schedule").toggleClass("table_schedule--active"),t.preventDefault()})),e(document).click((function(t){e(".table_schedule").hasClass("table_schedule--active")&&(e(".table_schedule").is(t.target)||e(".opening_informations").is(t.target)||e(".table_schedule td").is(t.target)||e(".table_schedule").toggleClass("table_schedule--active"))}))})),$(document).ready((function(){const e=$("#portal-globalnav"),t=$("#subsite-navigation");function s(){e.removeClass("activated"),t.removeClass("activated"),document.body.classList.remove("submenu-open-nav-overflow"),document.documentElement.classList.remove("submenu-open-nav-overflow"),$(".show-nav").removeClass("show-nav"),$(".mask-menu").removeClass("in")}$(".close-nav").click((function(){s()})),$(document).mouseup((a=>{e.is(a.target)||0!==e.has(a.target).length||t.is(a.target)||0!==t.has(a.target).length||s()})),$("li.has_subtree > a").click((function(){return $(this).closest(e).length>0&&(!$("#portal-globalnav .show-nav").length>0&&($(e).toggleClass("activated"),$(".mask-menu").toggleClass("in")),$(this).parent().toggleClass("show-nav"),$(this).parent().find(".show-nav").toggleClass("show-nav"),$(this).parent().siblings(".show-nav").toggleClass("show-nav"),$(this).parent().find(".activated").toggleClass(".activated"),!$("#portal-globalnav .show-nav").length>0&&s()),$(this).closest(t).length>0&&(!$("#subsite-navigation .show-nav").length>0&&($(t).toggleClass("activated"),document.body.classList.add("submenu-open-nav-overflow"),document.documentElement.classList.add("submenu-open-nav-overflow")),$(this).parent().toggleClass("show-nav"),$(this).parent().find(".show-nav").toggleClass("show-nav"),$(this).parent().siblings(".show-nav").toggleClass("show-nav"),$(this).parent().find(".activated").toggleClass(".activated"),!$("#subsite-navigation .show-nav").length>0&&s()),!1})),$(".prev-nav").click((function(){$(this).closest(".show-nav").toggleClass("show-nav")})),e.on("focusout",(function(t){setTimeout((function(){e.has(document.activeElement).length||s()}),0)}))}))})();
|
@@ -83,6 +83,17 @@ $(document).ready(function () {
|
|
83
83
|
$(this).closest(".show-nav").toggleClass("show-nav")
|
84
84
|
});
|
85
85
|
|
86
|
+
|
87
|
+
// ici on ferme le menu au focusout
|
88
|
+
menu.on('focusout', function(e) {
|
89
|
+
// Utilisez setTimeout pour s'assurer que document.activeElement est mis à jour
|
90
|
+
setTimeout(function() {
|
91
|
+
if (!menu.has(document.activeElement).length) {
|
92
|
+
closeNav();
|
93
|
+
}
|
94
|
+
}, 0);
|
95
|
+
});
|
96
|
+
|
86
97
|
// close nav fonction
|
87
98
|
function closeNav() {
|
88
99
|
menu.removeClass('activated');
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
from Products.Five.browser import BrowserView
|
3
2
|
from imio.smartweb.core.contents import IPages
|
4
3
|
from imio.smartweb.core.contents.pages.procedure.utils import sign_url
|
5
4
|
from imio.smartweb.core.utils import get_plausible_vars
|
@@ -7,12 +6,12 @@ from imio.smartweb.locales import SmartwebMessageFactory as _
|
|
7
6
|
from plone import api
|
8
7
|
from plone.api.portal import get_registry_record
|
9
8
|
from plone.formwidget.geolocation.vocabularies import _ as _geo
|
9
|
+
from Products.Five.browser import BrowserView
|
10
10
|
from zope.component import getMultiAdapter
|
11
11
|
from zope.i18n import translate
|
12
12
|
|
13
13
|
import json
|
14
14
|
import requests
|
15
|
-
import os
|
16
15
|
|
17
16
|
|
18
17
|
class UtilsView(BrowserView):
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from plone.app.content.browser.vocabulary import VocabularyView
|
3
|
+
from plone.app.content.browser.vocabulary import VocabLookupException
|
4
|
+
from plone.app.content.utils import json_dumps
|
5
|
+
|
6
|
+
|
7
|
+
class SmartwebVocabularyView(VocabularyView):
|
8
|
+
|
9
|
+
def __call__(self):
|
10
|
+
form = self.request.form
|
11
|
+
name = form.get("name")
|
12
|
+
if name != "imio.smartweb.vocabulary.RemoteContacts":
|
13
|
+
return super(SmartwebVocabularyView, self).__call__()
|
14
|
+
|
15
|
+
self.request.response.setHeader(
|
16
|
+
"Content-Type", "application/json; charset=utf-8"
|
17
|
+
)
|
18
|
+
|
19
|
+
try:
|
20
|
+
vocabulary = self.get_vocabulary()
|
21
|
+
except VocabLookupException as e:
|
22
|
+
return json_dumps({"error": e.args[0]})
|
23
|
+
|
24
|
+
query = form.get("query")
|
25
|
+
items = []
|
26
|
+
if not query:
|
27
|
+
items = vocabulary
|
28
|
+
else:
|
29
|
+
for term in vocabulary:
|
30
|
+
if query.lower() in term.title.lower():
|
31
|
+
items.append(term)
|
32
|
+
|
33
|
+
results = [{"id": item.value, "text": item.title} for item in items]
|
34
|
+
|
35
|
+
return json_dumps({"results": results, "total": len(results)})
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
from datetime import date, datetime
|
4
|
-
from datetime import timedelta
|
5
|
-
from decimal import Decimal
|
6
3
|
from imio.smartweb.common.contact_utils import ContactProperties as ContactSchedule
|
7
4
|
from imio.smartweb.common.utils import rich_description
|
8
5
|
from imio.smartweb.core.utils import batch_results
|
@@ -11,7 +8,6 @@ from imio.smartweb.core.utils import hash_md5
|
|
11
8
|
from imio.smartweb.locales import SmartwebMessageFactory as _
|
12
9
|
from plone import api
|
13
10
|
from zope.i18n import translate
|
14
|
-
from zope.i18nmessageid import MessageFactory
|
15
11
|
|
16
12
|
import json
|
17
13
|
|
@@ -56,15 +56,19 @@
|
|
56
56
|
<tal:dates define="modified context/ModificationDate">
|
57
57
|
<span class="documentModified"
|
58
58
|
tal:condition="modified">
|
59
|
-
<
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
59
|
+
<a class="documentModified"
|
60
|
+
tal:attributes="href string:${context/absolute_url}/@@historyview"
|
61
|
+
tal:omit-tag="python:context.portal_type != 'imio.smartweb.SectionText'">
|
62
|
+
<span i18n:translate="">
|
63
|
+
Modified
|
64
|
+
</span>
|
65
|
+
<span class="pat-display-time"
|
66
|
+
data-pat-display-time="from-now: true"
|
67
|
+
tal:attributes="datetime modified"
|
68
|
+
tal:content="modified">
|
69
|
+
Modified
|
70
|
+
</span>
|
71
|
+
</a>
|
68
72
|
</span>
|
69
73
|
</tal:dates>
|
70
74
|
|
@@ -10,10 +10,11 @@
|
|
10
10
|
<metal:main fill-slot="content-core">
|
11
11
|
<metal:content-core define-macro="content-core">
|
12
12
|
<metal:macro use-macro="context/@@sections_macros/section_edition" />
|
13
|
-
<div tal:define="image context/image | nothing;
|
14
|
-
|
13
|
+
<div tal:define="image context/image | nothing;
|
14
|
+
collapse_klass_container python: 'section-text-collapsed' if context.collapsible_section else '';"
|
15
15
|
id=""
|
16
|
-
tal:attributes="
|
16
|
+
tal:attributes="class string:container section-container section-text ${collapse_klass_container};
|
17
|
+
id string:container-section-${context/id};">
|
17
18
|
<metal:macro use-macro="context/@@sections_macros/section_title" />
|
18
19
|
<div tal:define="collapse_klass python: 'collapse' if context.collapsible_section else '';
|
19
20
|
klass string:body-section figure-${context/alignment} figure-${context/image_size} ${collapse_klass};"
|
@@ -0,0 +1,102 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<contentrules>
|
3
|
+
<rule name="rule-1" title="Notification de contenu à valider"
|
4
|
+
cascading="False" description="" enabled="True"
|
5
|
+
event="Products.CMFCore.interfaces.IActionSucceededEvent"
|
6
|
+
stop-after="False">
|
7
|
+
<conditions>
|
8
|
+
<condition type="plone.conditions.PortalType">
|
9
|
+
<property name="check_types">
|
10
|
+
<element>imio.smartweb.Page</element>
|
11
|
+
<element>imio.smartweb.PortalPage</element>
|
12
|
+
<element>imio.smartweb.Procedure</element>
|
13
|
+
</property>
|
14
|
+
</condition>
|
15
|
+
<condition type="plone.conditions.WorkflowState">
|
16
|
+
<property name="wf_states">
|
17
|
+
<element>pending</element>
|
18
|
+
</property>
|
19
|
+
</condition>
|
20
|
+
</conditions>
|
21
|
+
<actions>
|
22
|
+
<action type="plone.actions.Mail">
|
23
|
+
<property name="subject">Nouveau contenu sur le site</property>
|
24
|
+
<property name="source"/>
|
25
|
+
<property name="recipients">${reviewer_emails}</property>
|
26
|
+
<property name="exclude_actor">False</property>
|
27
|
+
<property name="message">Un nouveau contenu a été soumis à validation. Cliquez sur le lien pour aller le valider ou le refuser : ${absolute_url}</property>
|
28
|
+
</action>
|
29
|
+
<action type="plone.actions.Logger">
|
30
|
+
<property name="targetLogger">imio.smartweb.core</property>
|
31
|
+
<property name="loggingLevel">20</property>
|
32
|
+
<property
|
33
|
+
name="message">Content to review notification sent for &c.</property>
|
34
|
+
</action>
|
35
|
+
</actions>
|
36
|
+
</rule>
|
37
|
+
<rule name="rule-2" title="Notification de modification de section de contenu publié"
|
38
|
+
cascading="False" description="" enabled="True"
|
39
|
+
event="zope.lifecycleevent.interfaces.IObjectModifiedEvent"
|
40
|
+
stop-after="False">
|
41
|
+
<conditions>
|
42
|
+
<condition type="plone.conditions.PortalType">
|
43
|
+
<property name="check_types">
|
44
|
+
<element>imio.smartweb.SectionText</element>
|
45
|
+
</property>
|
46
|
+
</condition>
|
47
|
+
<condition type="plone.conditions.TalesExpression">
|
48
|
+
<property
|
49
|
+
name="tales_expression">python: hasattr(here, "aq_parent") and portal.portal_workflow.getInfoFor(here.aq_parent, "review_state", "") == "published"</property>
|
50
|
+
</condition>
|
51
|
+
</conditions>
|
52
|
+
<actions>
|
53
|
+
<action type="plone.actions.Mail">
|
54
|
+
<property name="subject">Contenu publié modifié sur le site</property>
|
55
|
+
<property name="source"/>
|
56
|
+
<property name="recipients">${reviewer_emails}</property>
|
57
|
+
<property name="exclude_actor">False</property>
|
58
|
+
<property name="message">Une section texte d'un contenu déjà publié a été modifiée. Cliquez sur le lien pour consulter la modification effectuée : ${absolute_url}/@@historyview</property>
|
59
|
+
</action>
|
60
|
+
<action type="plone.actions.Logger">
|
61
|
+
<property name="targetLogger">imio.smartweb.core</property>
|
62
|
+
<property name="loggingLevel">20</property>
|
63
|
+
<property
|
64
|
+
name="message">Published content change notification sent for &c (user: &u).</property>
|
65
|
+
</action>
|
66
|
+
</actions>
|
67
|
+
</rule>
|
68
|
+
<rule name="rule-3" title="Notification d'ajout de section à un contenu publié"
|
69
|
+
cascading="False" description="" enabled="True"
|
70
|
+
event="zope.lifecycleevent.interfaces.IObjectAddedEvent"
|
71
|
+
stop-after="False">
|
72
|
+
<conditions>
|
73
|
+
<condition type="plone.conditions.PortalType">
|
74
|
+
<property name="check_types">
|
75
|
+
<element>imio.smartweb.SectionText</element>
|
76
|
+
</property>
|
77
|
+
</condition>
|
78
|
+
<condition type="plone.conditions.TalesExpression">
|
79
|
+
<property
|
80
|
+
name="tales_expression">python: hasattr(here, "aq_parent") and portal.portal_workflow.getInfoFor(here.aq_parent, "review_state", "") == "published"</property>
|
81
|
+
</condition>
|
82
|
+
</conditions>
|
83
|
+
<actions>
|
84
|
+
<action type="plone.actions.Mail">
|
85
|
+
<property name="subject">Contenu publié modifié sur le site</property>
|
86
|
+
<property name="source"/>
|
87
|
+
<property name="recipients">${reviewer_emails}</property>
|
88
|
+
<property name="exclude_actor">False</property>
|
89
|
+
<property name="message">Une section texte a été ajoutée à un contenu déjà publié. Cliquez sur le lien pour consulter la section texte ajoutée : ${absolute_url}</property>
|
90
|
+
</action>
|
91
|
+
<action type="plone.actions.Logger">
|
92
|
+
<property name="targetLogger">imio.smartweb.core</property>
|
93
|
+
<property name="loggingLevel">20</property>
|
94
|
+
<property
|
95
|
+
name="message">Published content add notification sent for &c (user: &u).</property>
|
96
|
+
</action>
|
97
|
+
</actions>
|
98
|
+
</rule>
|
99
|
+
<assignment name="rule-1" bubbles="True" enabled="True" location=""/>
|
100
|
+
<assignment name="rule-2" bubbles="True" enabled="True" location=""/>
|
101
|
+
<assignment name="rule-3" bubbles="True" enabled="True" location=""/>
|
102
|
+
</contentrules>
|
imio/smartweb/core/profiles.zcml
CHANGED
@@ -45,6 +45,14 @@
|
|
45
45
|
provides="Products.GenericSetup.interfaces.EXTENSION"
|
46
46
|
/>
|
47
47
|
|
48
|
+
<genericsetup:registerProfile
|
49
|
+
name="validation"
|
50
|
+
title="imio.smartweb.core validation"
|
51
|
+
directory="profiles/validation"
|
52
|
+
description="Installs smartweb validation content rules"
|
53
|
+
provides="Products.GenericSetup.interfaces.EXTENSION"
|
54
|
+
/>
|
55
|
+
|
48
56
|
<genericsetup:registerProfile
|
49
57
|
name="last-compilation"
|
50
58
|
title="imio.smartweb.core last compilation dates"
|
@@ -588,3 +588,21 @@ class TestSections(ImioSmartwebTestCase):
|
|
588
588
|
self.assertEqual(
|
589
589
|
view.get_class(section), "sectiontext my-css col-sm-3 with-background"
|
590
590
|
)
|
591
|
+
|
592
|
+
def test_sections_history(self):
|
593
|
+
api.content.transition(self.page, "publish")
|
594
|
+
section_types = get_sections_types()
|
595
|
+
page_view = queryMultiAdapter((self.page, self.request), name="full_view")()
|
596
|
+
count_historyview_link = page_view.count("@@historyview")
|
597
|
+
self.assertEqual(count_historyview_link, 1)
|
598
|
+
for section_type in section_types:
|
599
|
+
api.content.create(
|
600
|
+
container=self.page,
|
601
|
+
type=section_type,
|
602
|
+
title="Title of my {}".format(section_type),
|
603
|
+
)
|
604
|
+
page_view = queryMultiAdapter((self.page, self.request), name="full_view")()
|
605
|
+
section_text = api.content.find(portal_type="imio.smartweb.SectionText")[0]
|
606
|
+
self.assertIn(f"{section_text.getURL()}/@@historyview", page_view)
|
607
|
+
# One more history view link on SectionText
|
608
|
+
self.assertEqual(page_view.count("@@historyview"), count_historyview_link + 1)
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
from freezegun import freeze_time
|
4
|
+
from gzip import GzipFile
|
5
|
+
from io import BytesIO
|
6
|
+
from imio.smartweb.core.testing import IMIO_SMARTWEB_CORE_FUNCTIONAL_TESTING
|
7
|
+
from imio.smartweb.core.testing import ImioSmartwebTestCase
|
8
|
+
from imio.smartweb.core.tests.utils import get_json
|
9
|
+
from imio.smartweb.core.tests.utils import make_named_image
|
10
|
+
from plone import api
|
11
|
+
from plone.app.testing import setRoles
|
12
|
+
from plone.app.testing import TEST_USER_ID
|
13
|
+
from plone.app.textfield.value import RichTextValue
|
14
|
+
from plone.base.utils import safe_text
|
15
|
+
from plone.namedfile.file import NamedBlobImage
|
16
|
+
from zope.component import getMultiAdapter
|
17
|
+
|
18
|
+
import json
|
19
|
+
import requests_mock
|
20
|
+
|
21
|
+
|
22
|
+
class TestPage(ImioSmartwebTestCase):
|
23
|
+
layer = IMIO_SMARTWEB_CORE_FUNCTIONAL_TESTING
|
24
|
+
|
25
|
+
@freeze_time("2024-02-02 8:00:00")
|
26
|
+
def setUp(self):
|
27
|
+
self.request = self.layer["request"]
|
28
|
+
self.portal = self.layer["portal"]
|
29
|
+
setRoles(self.portal, TEST_USER_ID, ["Manager"])
|
30
|
+
api.portal.set_registry_record("plone.enable_sitemap", True)
|
31
|
+
|
32
|
+
self.default_page = api.content.create(
|
33
|
+
container=self.portal,
|
34
|
+
type="imio.smartweb.PortalPage",
|
35
|
+
title="Portal page",
|
36
|
+
id="portal-page",
|
37
|
+
)
|
38
|
+
self.portal.setDefaultPage("portal-page")
|
39
|
+
|
40
|
+
self.folder = api.content.create(
|
41
|
+
container=self.portal,
|
42
|
+
type="imio.smartweb.Folder",
|
43
|
+
title="Folder",
|
44
|
+
id="folder",
|
45
|
+
)
|
46
|
+
self.page = api.content.create(
|
47
|
+
container=self.folder,
|
48
|
+
type="imio.smartweb.Page",
|
49
|
+
title="Page 1",
|
50
|
+
id="page1",
|
51
|
+
)
|
52
|
+
self.section_text = api.content.create(
|
53
|
+
container=self.page,
|
54
|
+
type="imio.smartweb.SectionText",
|
55
|
+
title="Section text",
|
56
|
+
)
|
57
|
+
self.section_text.text = RichTextValue(
|
58
|
+
"<p>Kamoulox</p>", "text/html", "text/html"
|
59
|
+
)
|
60
|
+
|
61
|
+
self.rest_directory = api.content.create(
|
62
|
+
container=self.portal,
|
63
|
+
type="imio.smartweb.DirectoryView",
|
64
|
+
title="directory view",
|
65
|
+
)
|
66
|
+
self.rest_agenda = api.content.create(
|
67
|
+
container=self.portal,
|
68
|
+
type="imio.smartweb.EventsView",
|
69
|
+
title="agenda view",
|
70
|
+
)
|
71
|
+
self.rest_agenda.selected_agenda = "64f4cbee9a394a018a951f6d94452914"
|
72
|
+
self.rest_news = api.content.create(
|
73
|
+
container=self.portal,
|
74
|
+
type="imio.smartweb.NewsView",
|
75
|
+
title="news view",
|
76
|
+
)
|
77
|
+
self.rest_news.selected_news_folder = "64f4cbee9a394a018a951f6d94452914"
|
78
|
+
api.content.transition(self.rest_directory, "publish")
|
79
|
+
api.content.transition(self.rest_agenda, "publish")
|
80
|
+
api.content.transition(self.rest_news, "publish")
|
81
|
+
|
82
|
+
# 'http://localhost:8080/Plone/@search?selected_entities=396907b3b1b04a97896b12cc792c77f8&portal_type=imio.directory.Contact&fullobjects=0&sort_on=sortable_title'
|
83
|
+
@freeze_time("2024-02-02 10:00:00")
|
84
|
+
@requests_mock.Mocker()
|
85
|
+
def test_sitemap(self, m):
|
86
|
+
sitemap = getMultiAdapter(
|
87
|
+
(self.portal, self.portal.REQUEST), name="sitemap.xml.gz"
|
88
|
+
)
|
89
|
+
xml = self.uncompress(sitemap())
|
90
|
+
self.assertIn("<lastmod >2024-02-02T08:00:00+00:00</lastmod>", xml)
|
91
|
+
self.assertIn("<loc>http://nohost/plone/folder</loc>", xml)
|
92
|
+
self.assertIn("http://nohost/plone/folder/page1", xml)
|
93
|
+
self.assertNotIn(
|
94
|
+
"<loc>http://nohost/plone/folder/page1/gallery/image/view</loc>", xml
|
95
|
+
)
|
96
|
+
|
97
|
+
# Gallery and image created 2024-02-02 10:00:00
|
98
|
+
gallery = api.content.create(
|
99
|
+
container=self.page,
|
100
|
+
type="imio.smartweb.SectionGallery",
|
101
|
+
title="Gallery",
|
102
|
+
)
|
103
|
+
image = api.content.create(
|
104
|
+
container=gallery,
|
105
|
+
type="Image",
|
106
|
+
title="Image",
|
107
|
+
)
|
108
|
+
image.image = NamedBlobImage(**make_named_image())
|
109
|
+
sitemap = getMultiAdapter(
|
110
|
+
(self.portal, self.portal.REQUEST), name="sitemap.xml.gz"
|
111
|
+
)
|
112
|
+
xml = self.uncompress(sitemap())
|
113
|
+
self.assertIn(
|
114
|
+
"<loc>http://nohost/plone/folder/page1/gallery/image/view</loc>", xml
|
115
|
+
)
|
116
|
+
# Gallery and image created 2024-02-02 10:00:00
|
117
|
+
self.assertIn(
|
118
|
+
"<loc>http://nohost/plone/folder/page1/gallery/image/view</loc>\n <lastmod >2024-02-02T10:00:00+00:00</lastmod>",
|
119
|
+
xml,
|
120
|
+
)
|
121
|
+
|
122
|
+
rest_views = {
|
123
|
+
"directory": self.rest_directory,
|
124
|
+
"events": self.rest_agenda,
|
125
|
+
"news": self.rest_news,
|
126
|
+
}
|
127
|
+
for k, v in rest_views.items():
|
128
|
+
api.portal.set_registry_record(f"smartweb.default_{k}_view", v.UID())
|
129
|
+
|
130
|
+
self.json_contacts = get_json("resources/json_contacts_raw_mock.json")
|
131
|
+
self.json_news = get_json("resources/json_rest_news.json")
|
132
|
+
self.json_events = get_json("resources/json_rest_events.json")
|
133
|
+
|
134
|
+
contact_search_url = "http://localhost:8080/Plone/@search?selected_entities=396907b3b1b04a97896b12cc792c77f8&portal_type=imio.directory.Contact&fullobjects=0&sort_on=sortable_title"
|
135
|
+
news_search_url = f"http://localhost:8080/Plone/@search?selected_news_folders={self.rest_news.selected_news_folder}&portal_type=imio.news.NewsItem&fullobjects=0&sort_on=sortable_title"
|
136
|
+
events_search_url = f"http://localhost:8080/Plone/@events?selected_agendas={self.rest_agenda.selected_agenda}&portal_type=imio.events.Event&fullobjects=0&sort_on=sortable_title"
|
137
|
+
|
138
|
+
m.get(contact_search_url, text=json.dumps(self.json_contacts))
|
139
|
+
m.get(news_search_url, text=json.dumps(self.json_news))
|
140
|
+
m.get(events_search_url, text=json.dumps(self.json_events))
|
141
|
+
|
142
|
+
sitemap = getMultiAdapter(
|
143
|
+
(self.portal, self.portal.REQUEST), name="sitemap.xml.gz"
|
144
|
+
)
|
145
|
+
xml = self.uncompress(sitemap())
|
146
|
+
self.assertIn("<loc>http://nohost/plone/directory-view/contact1-title", xml)
|
147
|
+
self.assertIn("<loc>http://nohost/plone/news-view/", xml)
|
148
|
+
self.assertIn("<loc>http://nohost/plone/agenda-view/", xml)
|
149
|
+
|
150
|
+
def uncompress(self, sitemapdata):
|
151
|
+
sio = BytesIO(sitemapdata)
|
152
|
+
unzipped = GzipFile(fileobj=sio)
|
153
|
+
xml = unzipped.read()
|
154
|
+
unzipped.close()
|
155
|
+
return safe_text(xml)
|