Skip to content

Commit 5267fd8

Browse files
committed
dynamic view and styles
1 parent dbe8e74 commit 5267fd8

7 files changed

Lines changed: 210 additions & 26 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Handles reordering of dynamic page rows
3+
*/
4+
5+
(function () {
6+
"use strict";
7+
8+
// Initialize when DOM is loaded
9+
if (document.readyState === "loading") {
10+
document.addEventListener("DOMContentLoaded", initRowReordering);
11+
} else {
12+
initRowReordering();
13+
}
14+
15+
function initRowReordering() {
16+
// Only run on dynamic-view with edit permissions
17+
if (
18+
!document.body.classList.contains("template-dynamic-view") ||
19+
!document.body.classList.contains("userrole-manager")
20+
) {
21+
console.log(
22+
"Not in dynamic-view edit mode, skipping row reordering initialization"
23+
);
24+
return;
25+
}
26+
27+
console.log("Initializing row reordering...");
28+
// Find all move up/down buttons
29+
const moveUpButtons = document.querySelectorAll('a[data-action="move-up"]');
30+
const moveDownButtons = document.querySelectorAll(
31+
'a[data-action="move-down"]'
32+
);
33+
console.log(
34+
`Found ${moveUpButtons.length} move-up buttons and ${moveDownButtons.length} move-down buttons`
35+
);
36+
37+
// Add event listeners to move up buttons
38+
moveUpButtons.forEach((button) => {
39+
button.addEventListener("click", function (e) {
40+
e.preventDefault();
41+
console.log("Move up button clicked");
42+
const row = this.closest(".dynamic-row");
43+
console.log(`Moving row with ID: ${row.dataset.rowid} up`);
44+
moveRow(row, -1);
45+
});
46+
});
47+
48+
// Add event listeners to move down buttons
49+
moveDownButtons.forEach((button) => {
50+
button.addEventListener("click", function (e) {
51+
e.preventDefault();
52+
console.log("Move down button clicked");
53+
const row = this.closest(".dynamic-row");
54+
console.log(`Moving row with ID: ${row.dataset.rowid} down`);
55+
moveRow(row, 1);
56+
});
57+
});
58+
}
59+
60+
function moveRow(row, delta) {
61+
const rowId = row.dataset.rowid;
62+
if (!rowId) {
63+
const errorMsg = "No data-row-id attribute found on row";
64+
console.error(errorMsg);
65+
alert(errorMsg);
66+
return;
67+
}
68+
69+
console.log(`Preparing to move row ${rowId} with delta ${delta}`);
70+
71+
const baseUrl = row.dataset.rowurl || "";
72+
console.log(`Sending request to: ${baseUrl}`);
73+
74+
const requestBody = {
75+
ordering: {
76+
obj_id: rowId,
77+
delta: delta,
78+
},
79+
};
80+
81+
console.log("Request payload:", JSON.stringify(requestBody, null, 2));
82+
83+
fetch(baseUrl, {
84+
method: "PATCH",
85+
headers: {
86+
"Content-Type": "application/json",
87+
Accept: "application/json",
88+
"X-Requested-With": "XMLHttpRequest",
89+
},
90+
body: JSON.stringify(requestBody),
91+
credentials: "same-origin",
92+
}).then((response) => {
93+
console.log(`Received response with status: ${response.status}`);
94+
console.log("Response headers:", response.headers);
95+
console.log("Response ok:", response.ok);
96+
if (!response.ok) {
97+
const error = new Error(`HTTP error! status: ${response.status}`);
98+
console.error("Response not OK:", error);
99+
throw error;
100+
}
101+
// Refresh the page after successful update
102+
window.location.reload();
103+
});
104+
}
105+
})();

src/cs_dynamicpages/content/dynamic_page_row.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class IDynamicPageRow(model.Schema):
2424
row_type = schema.Choice(
2525
title="Row type",
2626
required=True,
27+
default="cs_dynamicpages-horizontal-rule-view",
2728
vocabulary="cs_dynamicpages.RowType",
2829
)
2930

@@ -38,6 +39,14 @@ def review_state(self):
3839
def can_edit(self):
3940
return api.user.has_permission("Modify portal content", obj=self)
4041

42+
def featured_list(self):
43+
return api.content.find(
44+
context=self,
45+
portal_type="DynamicPageRowFeatured",
46+
sort_on="getObjPositionInParent",
47+
depth=1,
48+
)
49+
4150
def render(self, request):
4251
if self.row_type:
4352
try:

src/cs_dynamicpages/content/dynamic_page_row_featured.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# from z3c.form.browser.radio import RadioFieldWidget
1010
# from zope import schema
1111
from zope.interface import implementer
12-
12+
from plone import api
1313

1414
# from cs_dynamicpages import _
1515

@@ -22,6 +22,9 @@ class IDynamicPageRowFeatured(model.Schema):
2222
class DynamicPageRowFeatured(Item):
2323
"""Content-type class for IDynamicPageRowFeatured"""
2424

25+
def review_state(self):
26+
return api.content.get_state(obj=self)
27+
2528
def related_image_object(self):
2629
related_image = self.related_image
2730
if related_image:

src/cs_dynamicpages/profiles/default/registry/dynamic_bundle.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@
1515
<value key="load_defer">False</value>
1616
<value key="expression" />
1717
</records>
18+
<records interface="Products.CMFPlone.interfaces.IBundleRegistry"
19+
prefix="plone.bundles/cs_dynamicpages.dynamicpagerow_view"
20+
>
21+
<value key="enabled">True</value>
22+
<value key="jscompilation">++plone++cs_dynamicpages.edit/reorder-rows.js</value>
23+
<value key="depends">plone</value>
24+
<value key="load_async">True</value>
25+
<value key="load_defer">False</value>
26+
<value key="expression" />
27+
</records>
1828
</registry>

src/cs_dynamicpages/views/dynamic_page_row_view.pt

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,34 @@
1414
</tal:conditionisanon>
1515
<tal:conditionisanon tal:condition="not:isAnon">
1616
<section class="dynamic-row state-${context/review_state}">
17-
<div class="edit-buttons d-flex" tal:condition="featured/can_edit">
18-
<a href="${featured/absolute_url}/edit">Edit</a>
19-
<span class="ms-auto">Review state: ${featured/review_state}</span>
17+
<div class="edit-buttons d-flex gap-2" tal:condition="context/can_edit">
18+
<a class="btn btn-outline-primary btn-sm align-self-start" href="${context/absolute_url}/edit">Edit</a>
19+
<div class="ms-auto d-flex gap-2 align-items-start">
20+
<a class="btn btn-outline-primary btn-sm align-self-start"
21+
href="${context/aq_parent/aq_parent/absolute_url}">Go
22+
to top</a>
23+
<small
24+
class="d-inline-flex mb-3 px-2 py-1 fw-semibold text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 position-relative">
25+
Review state: ${context/review_state}
26+
<span
27+
tal:attributes="class python:context.review_state() == 'published' and 'position-absolute top-0 start-100 translate-middle p-2 bg-success border border-light rounded-circle' or 'position-absolute top-0 start-100 translate-middle p-2 bg-danger border border-light rounded-circle'">
28+
</span>
29+
</small>
30+
</div>
31+
2032
</div>
33+
<tal:condition tal:condition="context/can_edit">
34+
<div class="features-list d-flex gap-2 align-items-start mb-3"
35+
tal:define="features_list context/featured_list" tal:condition="features_list">
36+
<tal:featured repeat="featured_brain features_list">
37+
<div tal:define="featured_obj featured_brain/getObject;">
38+
<a tal:attributes="class python:featured_obj.review_state() == 'published' and 'btn btn-outline-success btn-sm bg-success-subtle' or 'btn btn-outline-danger btn-sm bg-danger-subtle'"
39+
href="${featured_obj/absolute_url}">${featured_obj/Title}</a>
40+
</div>
41+
</tal:featured>
42+
</div>
43+
</tal:condition>
44+
2145
<div class="content">
2246
<replace tal:replace="structure python:context.render(request)"></replace>
2347
</div>

src/cs_dynamicpages/views/dynamic_view.pt

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,60 @@
11
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:i18n="http://xml.zope.org/namespaces/i18n"
22
xmlns:metal="http://xml.zope.org/namespaces/metal" xmlns:tal="http://xml.zope.org/namespaces/tal"
3-
metal:use-macro="context/main_template/macros/master" i18n:domain="cs_dynamicpages">
3+
metal:use-macro="context/main_template/macros/master" i18n:domain="cs_dynamicpages"
4+
tal:define="portal_url context/portal_url;" data-portal-url="${portal_url}">
45

56
<body>
67
<metal:main fill-slot="main">
78
<main id="content">
89
<tal:features define="
9-
features view/features;
10+
rows view/rows;
1011
">
11-
<tal:featured repeat="brain features">
12-
<div tal:define="featured brain/getObject;">
13-
<section class="dynamic-row state-${featured/review_state}">
14-
<div class="edit-buttons d-flex gap-2" tal:condition="featured/can_edit">
15-
<a class="btn btn-outline-primary btn-sm align-self-start" href="${featured/absolute_url}">View</a>
16-
<a class="btn btn-outline-primary btn-sm align-self-start" href="${featured/absolute_url}/edit">Edit</a>
17-
<a class="btn btn-outline-primary btn-sm align-self-start" href="${featured/absolute_url}">Move up</a>
18-
<a class="btn btn-outline-primary btn-sm align-self-start" href="${featured/absolute_url}">Move down</a>
19-
<small
20-
class="d-inline-flex ms-auto mb-3 px-2 py-1 fw-semibold text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 position-relative">
21-
Review state: ${featured/review_state}
22-
<span
23-
tal:attributes="class python:featured.review_state() == 'published' and 'position-absolute top-0 start-100 translate-middle p-2 bg-success border border-light rounded-circle' or 'position-absolute top-0 start-100 translate-middle p-2 bg-danger border border-light rounded-circle'">
24-
</span>
25-
</small>
12+
<tal:featured repeat="brain rows">
13+
<div tal:define="row brain/getObject;">
14+
<section class="dynamic-row state-${row/review_state}" data-rowid="${row/id}"
15+
data-rowurl="${row/aq_parent/absolute_url}">
16+
<div class="edit-buttons d-flex gap-2" tal:condition="row/can_edit">
17+
<a class="btn btn-outline-primary btn-sm align-self-start" href="${row/absolute_url}">View</a>
18+
<a class="btn btn-outline-primary btn-sm align-self-start" href="${row/absolute_url}/edit">Edit</a>
19+
20+
<div class="ms-auto d-flex gap-2 align-items-start">
21+
<a tal:condition="python:not(repeat['brain'].start)"
22+
class="btn btn-outline-primary btn-sm align-self-start" href="#" data-action="move-up">Move up</a>
23+
<a tal:condition="python:not(repeat['brain'].end)"
24+
class="btn btn-outline-primary btn-sm align-self-start" href="#" data-action="move-down">Move
25+
down</a>
26+
<small
27+
class="d-inline-flex ms-auto mb-3 px-2 py-1 fw-semibold text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 position-relative">
28+
Review state: ${row/review_state}
29+
<span
30+
tal:attributes="class python:row.review_state() == 'published' and 'position-absolute top-0 start-100 translate-middle p-2 bg-success border border-light rounded-circle' or 'position-absolute top-0 start-100 translate-middle p-2 bg-danger border border-light rounded-circle'">
31+
</span>
32+
</small>
33+
</div>
34+
2635
</div>
36+
<tal:condition tal:condition="row/can_edit">
37+
<div class="features-list d-flex gap-2 align-items-start mb-3"
38+
tal:define="features_list row/featured_list" tal:condition="features_list">
39+
<tal:featured repeat="featured_brain features_list">
40+
<div tal:define="featured_obj featured_brain/getObject;">
41+
<a tal:attributes="class python:featured_obj.review_state() == 'published' and 'btn btn-outline-success btn-sm bg-success-subtle' or 'btn btn-outline-danger btn-sm bg-danger-subtle'"
42+
href="${featured_obj/absolute_url}">${featured_obj/Title}</a>
43+
</div>
44+
</tal:featured>
45+
</div>
46+
</tal:condition>
2747
<div class="content">
28-
<replace tal:replace="structure python:featured.render(request)"></replace>
48+
<replace tal:replace="structure python:row.render(request)"></replace>
2949
</div>
3050
</section>
3151
</div>
3252
</tal:featured>
3353
</tal:features>
54+
<div class="dynamic-row d-flex justify-content-center align-items-center">
55+
<a class="btn btn-primary btn-lg" href="${view/dynamic_page_folder_element_url}/++add++DynamicPageRow">Add
56+
new row</a>
57+
</div>
3458
</main>
3559
</metal:main>
3660
</body>

src/cs_dynamicpages/views/dynamic_view.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,23 @@ class IDynamicView(Interface):
1414

1515
@implementer(IDynamicView)
1616
class DynamicView(BrowserView):
17-
def features(self):
18-
dynamic_page_folder = api.content.find(
19-
portal_type="DynamicPageFolder", context=self.context
20-
)
17+
def rows(self):
18+
dynamic_page_folder = self.dynamic_page_folder_element()
2119
if dynamic_page_folder:
2220
return api.content.find(
2321
portal_type="DynamicPageRow",
2422
sort_on="getObjPositionInParent",
2523
context=dynamic_page_folder[0].getObject(),
2624
)
2725
return []
26+
27+
def dynamic_page_folder_element(self):
28+
return api.content.find(
29+
portal_type="DynamicPageFolder", context=self.context, depth=1, sort_on="getObjPositionInParent"
30+
)
31+
32+
def dynamic_page_folder_element_url(self):
33+
dynamic_page_folder = self.dynamic_page_folder_element()
34+
if dynamic_page_folder:
35+
return dynamic_page_folder[0].getObject().absolute_url()
36+
return ""

0 commit comments

Comments
 (0)