Refactored add child feature, to have all the page specific code in CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER (and not in the general node code).

Note that ideally PAGE could be a separated page node module (as it is for blog).
Added listing of children for each "page".
This commit is contained in:
2015-09-09 21:58:16 +02:00
parent eb70af6f19
commit 32a409b7e9
11 changed files with 261 additions and 276 deletions

View File

@@ -281,14 +281,17 @@ feature -- Access: Node
else
Result := l_partial_node
end
-- Update link with aliasing.
if Result /= Void and then Result.has_id then
Result.set_link (node_link (Result))
end
else
Result := a_node
if Result.has_id and Result.link = Void then
Result.set_link (node_link (Result))
end
-- Update link with aliasing.
if a_node /= Void and then a_node.has_id then
a_node.set_link (node_link (a_node))
end
check has_link: Result.has_id implies attached Result.link as lnk and then lnk.location.same_string (node_link (Result).location) end
-- Update partial user if needed.
if
@@ -327,60 +330,47 @@ feature -- Access: Node
end
end
feature -- Access: page/book outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- Children of node `a_node'.
-- note: this is the partial version of the nodes.
do
Result := node_storage.children (a_node)
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- List of possible parents nodes for node `a_node'.
-- Ensure the list of possible parent nodes does not allow a potential cycle.
-- Potential parent nodes for node `a_node'.
-- Ensure no cycle exists.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
across node_storage.available_parents_for_node (a_node) as ic loop
if not has_cycle (a_node, ic.item) then
check distinct: not a_node.same_node (ic.item) end
if not is_node_a_parent_of (a_node, ic.item) then
Result.force (ic.item)
end
end
ensure
not_cycle: Result.for_all (agent not_cycle (a_node, ?))
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
end
feature {NONE} -- Implementation
not_cycle (a_node: CMS_NODE; a_parent: CMS_NODE): BOOLEAN
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
-- Is `a_node' a direct or indirect parent of node `a_child'?
require
distinct_nodes: not a_node.same_node (a_child)
do
Result := not has_cycle (a_node, a_parent)
end
has_cycle (a_node: CMS_NODE; a_parent: CMS_NODE): BOOLEAN
-- Check if adding the node `a_parent' as parent of node `a_node' form a cycle.
local
l_flag: BOOLEAN
l_item: detachable CMS_PAGE
do
Result := False
if
attached {CMS_PAGE} a_node as l_page and then
attached {CMS_PAGE} full_node (a_parent) as l_page_parent
attached {CMS_PAGE} full_node (a_child) as l_child_page and then
attached l_child_page.parent as l_parent
then
l_page.set_parent (l_page_parent)
from
l_item := l_page_parent
until
l_flag or else Result
loop
if attached l_item and then attached {CMS_PAGE} node (l_item.id) as l_parent then
if l_parent.id = l_page.id then
if l_parent.same_node (l_child_page) then
Result := True
else
l_item := l_parent.parent
end
else
l_flag := True
Result := is_node_a_parent_of (a_node, l_parent)
end
end
-- Set parent to void.
l_page.set_parent (Void)
end
end
feature -- Permission Scope: Node

View File

@@ -170,6 +170,8 @@ feature -- Access
Result.force ("trash own " + l_type_name)
Result.force ("restore own " + l_type_name)
Result.force ("view unpublished " + l_type_name)
Result.force ("view revisions own " + l_type_name)
end
end

View File

@@ -10,7 +10,7 @@ deferred class
CMS_NODE
inherit
DEBUG_OUTPUT
REFACTORING_HELPER
feature{NONE} -- Initialization
@@ -166,6 +166,23 @@ feature -- Access: menu
link: detachable CMS_LOCAL_LINK
-- Associated menu link.
feature -- Status report
debug_output: STRING_32
-- <Precursor>
do
create Result.make_from_string_general ("#")
Result.append_integer_64 (id)
Result.append_character (' ')
Result.append_character ('<')
Result.append_string_general (content_type)
Result.append_character ('>')
Result.append_character (' ')
Result.append_character ('%"')
Result.append (title)
Result.append_character ('%"')
end
feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format)

View File

@@ -277,7 +277,7 @@ feature -- Output
create lnk.make ("Trash", node_api.node_path (a_node) + "/trash")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
elseif a_node /= Void and then a_node.has_id then
elseif a_node.has_id then
-- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status.
create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
lnk.set_weight (2)

View File

@@ -11,6 +11,8 @@ inherit
redefine
content_type,
append_html_output_to,
populate_form,
new_node,
update_node
end
@@ -24,159 +26,112 @@ feature -- Access
feature -- Forms ...
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: WSF_FORM_NUMBER_INPUT
l_parent_id, nid: INTEGER_64
do
Precursor (response, f, a_node)
if attached {CMS_PAGE} a_node as l_page then
create ti.make ("select_parent_node")
if attached l_page.parent as l_parent_node then
l_parent_id := l_parent_node.id
f.extend_html_text ("<div><strong>Currently, the parent page is </strong> ")
f.extend_html_text (response.node_html_link (l_parent_node, l_parent_node.title))
f.extend_html_text ("</div>")
ti.set_label ("Change parent")
ti.set_description ("Select a new parent ...")
else
ti.set_label ("Select parent")
ti.set_description ("Select a parent ...")
end
ti.set_validation_action (agent parent_validation (response, ?))
f.extend (ti)
if response.location.ends_with_general ("/add_child/page") then
nid := response.node_id_path_parameter (response.request)
l_parent_id := nid
end
if l_parent_id > 0 then
ti.set_default_value (l_parent_id.out)
end
end
end
update_node (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
-- <Precursor>
local
l_parent_id: INTEGER_64
do
Precursor (a_response, fd, a_node)
if attached {CMS_PAGE} a_node as l_page then
if attached fd.integer_item ("select_parent_node") as i_parent_node then
l_parent_id := i_parent_node.to_integer_64
end
if
attached {CMS_PAGE} a_node as l_node_page and then
attached fd.integer_item ("select_parent_node") as i_parent_node and then
i_parent_node > 0 and then
attached {CMS_PAGE} a_response.node_api.node (i_parent_node) as l_parent_page
then
l_node_page.set_parent (l_parent_page)
elseif attached {CMS_PAGE} a_node as l_node_page and then
attached fd.integer_item ("select_parent_node") as i_parent_node and then
i_parent_node = -1
l_parent_id > 0 and then
attached {CMS_PAGE} a_response.node_api.node (l_parent_id) as l_parent_page
then
l_page.set_parent (l_parent_page)
elseif l_parent_id = -1 then
-- Set parent to Void
l_node_page.set_parent (Void)
l_page.set_parent (Void)
end
end
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): like content_type.new_node
-- <Precursor>
do
Result := Precursor (response, fd, a_node)
if attached fd.integer_item ("select_parent_node") as l_parent_id then
if l_parent_id = -1 then
Result.set_parent (Void)
elseif attached {CMS_PAGE} response.node_api.node (l_parent_id) as l_parent then
Result.set_parent (l_parent)
end
end
end
-- fill_edit_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
-- local
-- ti: WSF_FORM_TEXT_INPUT
-- fset: WSF_FORM_FIELD_SET
-- ta: WSF_FORM_TEXTAREA
-- tselect: WSF_FORM_SELECT
-- opt: WSF_FORM_SELECT_OPTION
-- do
-- create ti.make ("title")
-- ti.set_label ("Title")
-- ti.set_size (70)
-- if a_node /= Void then
-- ti.set_text_value (a_node.title)
-- end
-- ti.set_is_required (True)
-- f.extend (ti)
-- f.extend_html_text ("<br/>")
-- create ta.make ("body")
-- ta.set_rows (10)
-- ta.set_cols (70)
-- if a_node /= Void then
-- ta.set_text_value (a_node.content)
-- end
---- ta.set_label ("Body")
-- ta.set_description ("This is the main content")
-- ta.set_is_required (False)
-- create fset.make
-- fset.set_legend ("Body")
-- fset.extend (ta)
-- fset.extend_html_text ("<br/>")
-- create tselect.make ("format")
-- tselect.set_label ("Body's format")
-- tselect.set_is_required (True)
-- across
-- content_type.available_formats as c
-- loop
-- create opt.make (c.item.name, c.item.title)
-- if attached c.item.html_help as f_help then
-- opt.set_description ("<ul>" + f_help + "</ul>")
-- end
-- tselect.add_option (opt)
-- end
-- if a_node /= Void and then attached a_node.format as l_format then
-- tselect.set_text_by_value (l_format)
-- end
-- fset.extend (tselect)
-- f.extend (fset)
-- end
-- change_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: like new_node)
-- local
-- b: detachable READABLE_STRING_8
-- f: detachable CONTENT_FORMAT
-- do
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then
-- check a_node.id = l_id end
-- end
-- if attached fd.string_item ("title") as l_title then
-- a_node.set_title (l_title)
-- end
-- if attached fd.string_item ("body") as l_body then
-- b := l_body
-- end
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- else
-- f := response.api.formats.default_format
-- end
-- if b /= Void then
-- a_node.set_content (b, Void, f.name) -- FIXME: summary
-- end
-- end
-- new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): CMS_PAGE
-- -- <Precursor>
-- local
-- b: detachable READABLE_STRING_8
-- f: detachable CONTENT_FORMAT
-- l_node: detachable like new_node
-- do
-- l_node := a_node
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then
-- if l_node /= Void then
-- check l_node.id = l_id end
-- else
-- if attached {like new_node} response.node_api.node (l_id) as n then
-- l_node := n
-- else
-- -- FIXME: Error
-- end
-- end
-- end
-- if attached fd.string_item ("title") as l_title then
-- if l_node = Void then
-- l_node := content_type.new_node (Void)
-- l_node.set_title (l_title)
-- else
-- l_node.set_title (l_title)
-- end
-- else
-- if l_node = Void then
-- l_node := content_type.new_node_with_title ("...", Void)
-- end
-- end
-- l_node.set_author (response.user)
-- if attached fd.string_item ("body") as l_body then
-- b := l_body
-- end
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- else
-- f := response.api.formats.default_format
-- end
-- if b /= Void then
-- l_node.set_content (b, Void, f.name)
-- end
-- Result := l_node
-- end
parent_validation (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA)
local
l_selected: BOOLEAN
node_api: CMS_NODE_API
l_parent_id: INTEGER_64
do
node_api := a_response.node_api
if attached fd.integer_item ("select_parent_node") as s_parent_node then
l_parent_id := s_parent_node.to_integer_64
else
l_parent_id := 0
end
if
l_parent_id > 0 and then
attached node_api.node (a_response.node_id_path_parameter (a_response.request)) as l_node
then
if not a_response.location.ends_with_general ("/add_child/page") then
across
node_api.available_parents_for_node (l_node) as ic
until
l_selected
loop
if ic.item.id = l_parent_id then
l_selected := True
end
end
if not l_selected then
fd.report_invalid_field ("select_parent_node", "Invalid node id " + l_parent_id.out)
end
end
elseif l_parent_id = -1 or else l_parent_id = 0 then
-- -1 is Used to unassign a parent node
-- 0 is not taken into account, any other input value is considered invalid.
else
fd.report_invalid_field ("select_parent_node", "Invalid node id")
end
end
feature -- Output
@@ -184,8 +139,20 @@ feature -- Output
-- <Precursor>
local
s: STRING
node_api: CMS_NODE_API
lnk: CMS_LOCAL_LINK
do
node_api := a_response.node_api
Precursor (a_node, a_response)
if a_node.has_id and then not a_node.is_trashed then
if node_api.has_permission_for_action_on_node ("create", a_node, a_response.user) then
create lnk.make ("Add Child", node_api.node_path (a_node) + "/add_child/page")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
if attached a_response.main_content as l_main_content then
s := l_main_content
else
@@ -193,11 +160,22 @@ feature -- Output
end
if attached {CMS_PAGE} a_node as l_node_page then
s.append ("<ul class=%"page-navigation%">")
if attached l_node_page.parent as l_parent_node then
s.append ("<div>Parent page is ")
s.append (a_response.link (l_parent_node.title + " (#" + l_parent_node.id.out + ")", a_response.node_api.node_path (l_parent_node), Void))
s.append ("</div>")
s.append ("<li class=%"page-parent%">Go to parent page ")
s.append (a_response.link (l_parent_node.title, a_response.node_api.node_path (l_parent_node), Void))
s.append ("</li>")
end
if attached node_api.children (a_node) as l_children then
across
l_children as ic
loop
s.append ("<li>")
s.append (a_response.link (ic.item.title, a_response.node_api.node_path (ic.item), Void))
s.append ("</li>")
end
end
s.append ("</ul>")
end
a_response.set_main_content (s)

View File

@@ -37,8 +37,6 @@ feature -- Execution
-- Computed response message.
local
b: STRING_8
f: like new_edit_form
fd: detachable WSF_FORM_DATA
nid: INTEGER_64
do
create b.make_empty
@@ -67,6 +65,7 @@ feature -- Execution
location.ends_with_general ("/add_child/page") and then
has_permissions (<<"create any", "create " + l_type.name>>)
then
-- FIXME: remove page dep from node module.
create_new_node (l_type, b)
else
b.append ("<h1>")
@@ -128,10 +127,13 @@ feature {NONE} -- Create a new node
f.process (Current)
fd := f.last_data
end
set_title ("Edit " + html_encoded (a_type.title) + " #" + l_node.id.out)
if l_node.has_id then
set_title ("Edit " + html_encoded (a_type.title) + " #" + l_node.id.out)
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
else
set_title ("New " + html_encoded (a_type.title))
end
f.append_to_html (wsf_theme, b)
else
@@ -233,7 +235,6 @@ feature -- Form
local
l_preview: BOOLEAN
l_format: detachable CONTENT_FORMAT
l_selected: BOOLEAN
do
l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview")
if l_preview then
@@ -255,32 +256,6 @@ feature -- Form
end
b.append ("</div>")
end
if
attached fd.integer_item ("select_parent_node") as s_parent_node and then
s_parent_node.to_integer_64 > 0 and then
attached node_api.node (s_parent_node) as l_parent_node and then
attached node_api.node (node_id_path_parameter (request)) as l_node
then
across node_api.available_parents_for_node (l_node) as ic until l_selected loop
if l_parent_node.same_node (ic.item) then
l_selected := True
end
end
if not l_selected then
fd.report_invalid_field ("select_parent_node", "Invalid node id " + s_parent_node.out)
end
elseif
attached fd.integer_item ("select_parent_node") as s_parent_node and then
(s_parent_node = -1 or else s_parent_node = 0)
then
-- -1 is Used to unassing a parent node
-- 0 is not taken into account, any other input value is considered invalid.
else
fd.report_invalid_field ("select_parent_node", "Invalid node id")
end
end
edit_form_submit (fd: WSF_FORM_DATA; a_node: detachable CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING)
@@ -322,15 +297,6 @@ feature -- Form
fixme ("for now, publishing is not implemented, so let's assume any node saved is published.") -- FIXME
l_node.mark_published
-- Save parent id in current node.
nid := node_id_path_parameter (request)
if location.ends_with_general ("/add_child/page") and then
nid > 0 and then attached {CMS_PAGE} l_node as l_new_page and then
attached {CMS_PAGE} node_api.node (nid) as l_page
then
l_new_page.set_parent (l_page)
end
node_api.save_node (l_node)
if attached user as u then
api.log ("node",
@@ -369,8 +335,6 @@ feature -- Form
f: CMS_FORM
ts: WSF_FORM_SUBMIT_INPUT
th: WSF_FORM_HIDDEN_INPUT
ti: WSF_FORM_NUMBER_INPUT
l_item: CMS_NODE
do
create f.make (a_url, a_name)
create th.make ("node-id")
@@ -382,29 +346,6 @@ feature -- Form
f.extend (th)
populate_form (a_node_type, f, a_node)
f.extend_html_text ("<br/>")
if attached {CMS_PAGE} a_node as l_node_page then
if attached l_node_page.parent as l_parent_node then
f.extend_html_text ("<div><strong>Parent page is </strong> ")
f.extend_html_text (node_html_link (l_parent_node, l_parent_node.title + "(#" + l_parent_node.id.out + ")"))
f.extend_html_text ("<br/>")
f.extend_html_text ("Change parent to?")
create ti.make ("select_parent_node")
ti.set_label ("Parent Node")
f.extend (ti)
f.extend_html_text ("</div>")
else
f.extend_html_text ("<div><strong>Not has parent page</strong> ")
f.extend_html_text ("<br/>")
f.extend_html_text ("Add parent to?")
create ti.make ("select_parent_node")
ti.set_label ("Parent Node")
f.extend (ti)
f.extend_html_text ("</div>")
end
end
f.extend_html_text ("<br/>")
create ts.make ("op")

View File

@@ -126,8 +126,11 @@ feature -- HTTP Methods
then
l_node := node_api.revision_node (l_nid, l_rev)
end
if l_node = Void then
send_not_found (req, res)
else
if
l_node /= Void and then (l_rev > 0 or else l_node.is_published)
l_rev > 0 or else l_node.is_published
then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
@@ -135,15 +138,17 @@ feature -- HTTP Methods
view_response.execute
elseif
attached current_user (req) as l_user and then
l_node /= Void and then ( node_api.is_author_of_node (l_user, l_node) or else api.user_api.is_admin_user (l_user))
( node_api.is_author_of_node (l_user, l_node)
or else api.user_api.user_has_permission (l_user, "view unpublished " + l_node.content_type)
)
then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
view_response.set_revision (l_rev)
view_response.execute
else
send_not_found (req, res)
send_access_denied (req, res)
end
end
else
-- redirect_to (req.absolute_script_url ("/node/"), res) -- New node.

View File

@@ -72,7 +72,6 @@ feature -- Execution
attached node_api.node_type_webform_manager (l_content_type) as l_manager
then
l_manager.append_html_output_to (l_node, Current)
add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Add Child", node_api.node_path (l_node) + "/add_child/page"))
end
elseif revision > 0 then
set_main_content ("Missing revision node!")

View File

@@ -117,9 +117,19 @@ feature -- Access
deferred
end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- Children of node `a_node'.
-- note: this is the partial version of the nodes.
deferred
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- Given the node `a_node', return the list of possible parent nodes id
deferred
ensure
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
end
feature -- Change: Node

View File

@@ -86,6 +86,13 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- <Precursor>
do
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- <Precursor>
do

View File

@@ -255,6 +255,32 @@ feature -- Access
-- end
end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".children")
from
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_query (sql_select_children_of_node, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- <Precursor>
@@ -482,7 +508,17 @@ feature {NONE} -- Queries
Sql_last_insert_node_revision: STRING = "SELECT MAX(revision) FROM node_revisions;"
Sql_last_insert_node_revision_for_nid: STRING = "SELECT MAX(revision) FROM node_revisions WHERE nid=:nid;"
sql_select_available_parents_for_node : STRING = "SELECT pn_1.nid, pn_1.revision, pn_1.type, title, summary, content, format, author, publish, created, changed, status FROM nodes pn_1 LEFT JOIN page_nodes pn_2 ON pn_1.nid = pn_2.nid AND pn_1.nid != :nid WHERE pn_2.parent != :nid AND pn_1.status != -1 GROUP BY pn_1.nid, pn_1.revision;"
sql_select_available_parents_for_node : STRING = "[
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
]"
sql_select_children_of_node: STRING = "[
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
]"
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author