From 544e6540ed59c4a9fad55a27f074b76810822757 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 8 Sep 2015 10:49:22 -0300 Subject: [PATCH] Add child page support. - Add support to create a new node as a child of an existing node. - Update or add a parent for a given node. Fix delete and trash behavior: when a node is not published only the - author or admin see it. Fix Node and Page node Revisions. --- modules/node/cms_node_api.e | 7 + modules/node/cms_node_module.e | 4 + .../cms_page_node_type_webform_manager.e | 20 +- modules/node/handler/node_form_response.e | 266 ++++++++++++------ modules/node/handler/node_handler.e | 22 +- modules/node/handler/node_view_response.e | 3 + modules/node/persistence/cms_node_storage_i.e | 5 + .../node/persistence/cms_node_storage_null.e | 6 + .../node/persistence/cms_node_storage_sql.e | 28 ++ .../cms_node_storage_sql_page_extension.e | 2 +- 10 files changed, 274 insertions(+), 89 deletions(-) diff --git a/modules/node/cms_node_api.e b/modules/node/cms_node_api.e index b575968..7a969e5 100644 --- a/modules/node/cms_node_api.e +++ b/modules/node/cms_node_api.e @@ -327,6 +327,13 @@ feature -- Access: Node end end + + available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE] + -- Given the node `a_node', return the list of possible parent nodes + do + Result := node_storage.available_parents_for_node(a_node) + end + feature -- Permission Scope: Node has_permission_for_action_on_node (a_action: READABLE_STRING_8; a_node: CMS_NODE; a_user: detachable CMS_USER; ): BOOLEAN diff --git a/modules/node/cms_node_module.e b/modules/node/cms_node_module.e index 2031c83..6ea424f 100644 --- a/modules/node/cms_node_module.e +++ b/modules/node/cms_node_module.e @@ -205,6 +205,10 @@ feature -- Access: router a_router.handle ("/node/{id}/delete", l_node_handler, a_router.methods_get_post) a_router.handle ("/node/{id}/trash", l_node_handler, a_router.methods_get_post) + -- Add child + a_router.handle ("/node/{id}/add_child/{type}", l_node_handler, a_router.methods_get_post) + + a_router.handle ("/node/{id}", l_node_handler, a_router.methods_get) -- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post) diff --git a/modules/node/handler/cms_page_node_type_webform_manager.e b/modules/node/handler/cms_page_node_type_webform_manager.e index 498f392..9f68f71 100644 --- a/modules/node/handler/cms_page_node_type_webform_manager.e +++ b/modules/node/handler/cms_page_node_type_webform_manager.e @@ -10,7 +10,8 @@ inherit CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE] redefine content_type, - append_html_output_to + append_html_output_to, + update_node end create @@ -21,8 +22,23 @@ feature -- Access content_type: CMS_PAGE_NODE_TYPE -- Associated content type. -feature -- Forms ... +feature -- Forms ... + + update_node (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE) + -- + do + Precursor (a_response, fd, a_node) + 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) + end + + end -- fill_edit_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) -- local -- ti: WSF_FORM_TEXT_INPUT diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e index b2364a1..e8e54a1 100644 --- a/modules/node/handler/node_form_response.e +++ b/modules/node/handler/node_form_response.e @@ -48,80 +48,26 @@ feature -- Execution attached node_api.node (nid) as l_node then if attached node_api.node_type_for (l_node) as l_type then - fixme ("refactor: process_edit, process_create process edit") if location.ends_with_general ("/edit") and then node_api.has_permission_for_action_on_node ("edit", l_node, user) then - f := new_edit_form (l_node, url (location, Void), "edit-" + l_type.name, l_type) - hooks.invoke_form_alter (f, fd, Current) - if request.is_post_request_method then - f.validation_actions.extend (agent edit_form_validate (?, b)) - f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) - f.process (Current) - fd := f.last_data - end - if l_node.has_id then - 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) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs) - end - - if attached redirection as l_location then - -- FIXME: Hack for now - set_title (l_node.title) - b.append (html_encoded (l_type.title) + " saved") - else - set_title (formatted_string (translation ("Edit $1 #$2", Void), [l_type.title, l_node.id])) - f.append_to_html (wsf_theme, b) - end + edit_node (l_node, l_type, b) elseif location.ends_with_general ("/delete") and then node_api.has_permission_for_action_on_node ("delete", l_node, user) then - f := new_delete_form (l_node, url (location, Void), "delete-" + l_type.name, l_type) - hooks.invoke_form_alter (f, fd, Current) - if request.is_post_request_method then - f.process (Current) - fd := f.last_data - end - if l_node.has_id then - 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) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs) - end - - if attached redirection as l_location then - -- FIXME: Hack for now - set_title (l_node.title) - b.append (html_encoded (l_type.title) + " deleted") - else - set_title (formatted_string (translation ("Delete $1 #$2", Void), [l_type.title, l_node.id])) - f.append_to_html (wsf_theme, b) - end + delete_node (l_node, l_type, b) elseif location.ends_with_general ("/trash") and then node_api.has_permission_for_action_on_node ("trash", l_node, user) then - f := new_trash_form (l_node, url (location, Void), "trash-" + l_type.name, l_type) - hooks.invoke_form_alter (f, fd, Current) - if request.is_post_request_method then - f.process (Current) - fd := f.last_data - end - if l_node.has_id then - add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", node_api.node_path (l_node) + "/Trash"), primary_tabs) - end - - if attached redirection as l_location then - -- FIXME: Hack for now - set_title (l_node.title) - b.append (html_encoded (l_type.title) + " trashed") - else - set_title (formatted_string (translation ("Trash $1 #$2", Void), [l_type.title, l_node.id])) - f.append_to_html (wsf_theme, b) - end + trash_node (l_node, l_type, b) + elseif + location.ends_with_general ("/add_child/page") and then + has_permissions (<<"create any", "create " + l_type.name>>) + then + create_new_node (l_type, b) else b.append ("

") b.append (translation ("Access denied", Void)) @@ -135,29 +81,8 @@ feature -- Execution attached node_api.node_type (p_type.value) as l_type then if has_permissions (<<"create any", "create " + l_type.name>>) then - if attached l_type.new_node (Void) as l_node then - f := new_edit_form (l_node, url (location, Void), "edit-" + l_type.name, l_type) - hooks.invoke_form_alter (f, fd, Current) - if request.is_post_request_method then - f.validation_actions.extend (agent edit_form_validate (?, b)) - f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) - f.process (Current) - fd := f.last_data - end - - set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out) - - if l_node.has_id then - 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) - end - - f.append_to_html (wsf_theme, b) - else - b.append ("

") - b.append (translation ("Server error", Void)) - b.append ("

") - end + -- create new node + create_new_node (l_type, b) else b.append ("

") b.append (translation ("Access denied", Void)) @@ -185,6 +110,123 @@ feature -- Execution set_main_content (b) end + +feature {NONE} -- Create a new node + + create_new_node (a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8) + local + f: like new_edit_form + fd: detachable WSF_FORM_DATA + do + if attached a_type.new_node (Void) as l_node then + -- create new node + f := new_edit_form (l_node, url (location, Void), "edit-" + a_type.name, a_type) + hooks.invoke_form_alter (f, fd, Current) + if request.is_post_request_method then + f.validation_actions.extend (agent edit_form_validate (?, b)) + f.submit_actions.extend (agent edit_form_submit (?, l_node, a_type, b)) + 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 + 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) + end + f.append_to_html (wsf_theme, b) + else + b.append ("

") + b.append (translation ("Server error", Void)) + b.append ("

") + end + end + + + edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8) + local + f: like new_edit_form + fd: detachable WSF_FORM_DATA + do + f := new_edit_form (A_node, url (location, Void), "edit-" + a_type.name, a_type) + hooks.invoke_form_alter (f, fd, Current) + if request.is_post_request_method then + f.validation_actions.extend (agent edit_form_validate (?, b)) + f.submit_actions.extend (agent edit_form_submit (?, a_node, a_type, b)) + f.process (Current) + fd := f.last_data + end + if a_node.has_id then + add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs) + end + + if attached redirection as l_location then + -- FIXME: Hack for now + set_title (a_node.title) + b.append (html_encoded (a_type.title) + " saved") + else + set_title (formatted_string (translation ("Edit $1 #$2", Void), [a_type.title, a_node.id])) + f.append_to_html (wsf_theme, b) + end + end + + + delete_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8) + local + f: like new_edit_form + fd: detachable WSF_FORM_DATA + do + f := new_delete_form (a_node, url (location, Void), "delete-" + a_type.name, a_type) + hooks.invoke_form_alter (f, fd, Current) + if request.is_post_request_method then + f.process (Current) + fd := f.last_data + end + if a_node.has_id then + add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs) + end + + if attached redirection as l_location then + -- FIXME: Hack for now + set_title (a_node.title) + b.append (html_encoded (a_type.title) + " deleted") + else + set_title (formatted_string (translation ("Delete $1 #$2", Void), [a_type.title, a_node.id])) + f.append_to_html (wsf_theme, b) + end + end + + + trash_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8) + local + f: like new_edit_form + fd: detachable WSF_FORM_DATA + do + f := new_trash_form (a_node, url (location, Void), "trash-" + a_type.name, a_type) + hooks.invoke_form_alter (f, fd, Current) + if request.is_post_request_method then + f.process (Current) + fd := f.last_data + end + if a_node.has_id then + add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", node_api.node_path (a_node) + "/Trash"), primary_tabs) + end + + if attached redirection as l_location then + -- FIXME: Hack for now + set_title (a_node.title) + b.append (html_encoded (a_type.title) + " trashed") + else + set_title (formatted_string (translation ("Trash $1 #$2", Void), [a_type.title, a_node.id])) + f.append_to_html (wsf_theme, b) + end + end + + feature -- Form edit_form_validate (fd: WSF_FORM_DATA; b: STRING) @@ -220,6 +262,7 @@ feature -- Form l_node: detachable CMS_NODE s: STRING l_path_alias: detachable READABLE_STRING_8 + nid: INTEGER_64 do l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview") if not l_preview then @@ -250,6 +293,16 @@ feature -- Form fixme ("for now, publishing is not implemented, so let's assume any node saved is published.") -- FIXME l_node.mark_published + + -- Save Child Page + 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", @@ -288,9 +341,13 @@ feature -- Form f: CMS_FORM ts: WSF_FORM_SUBMIT_INPUT th: WSF_FORM_HIDDEN_INPUT + tl: WSF_FORM_SELECT + to: WSF_FORM_SELECT_OPTION do create f.make (a_url, a_name) + + create th.make ("node-id") if a_node /= Void then th.set_text_value (a_node.id.out) @@ -302,6 +359,45 @@ feature -- Form populate_form (a_node_type, f, a_node) f.extend_html_text ("
") + -- Select for nodes + create tl.make ("select_parent_node") + create to.make ("0", "NONE") + tl.add_option (to) + + 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 ("
Parent page is ") + f.extend_html_text (node_html_link (l_parent_node, l_parent_node.title + "(#" + l_parent_node.id.out + ")")) + f.extend_html_text ("
") + f.extend_html_text ("Change parent to?") + if a_node /= Void then + across node_api.available_parents_for_node (a_node) as ic loop + create to.make (ic.item.id.out, node_html_link (ic.item, ic.item.title + "(#" + ic.item.id.out + ")")) + if l_parent_node.id = ic.item.id then + to.set_is_selected (True) + end + tl.add_option (to) + end + end + f.extend (tl) + f.extend_html_text ("
") + else + f.extend_html_text ("
Not has parent page ") + f.extend_html_text ("
") + f.extend_html_text ("Add parent to?") + if a_node /= Void then + across node_api.available_parents_for_node (a_node) as ic loop + create to.make (ic.item.id.out, node_html_link (ic.item, ic.item.title + "(#" + ic.item.id.out + ")")) + tl.add_option (to) + end + f.extend (tl) + f.extend_html_text ("
") + end + end + end + + + f.extend_html_text ("
") create ts.make ("op") ts.set_default_value ("Save") f.extend (ts) diff --git a/modules/node/handler/node_handler.e b/modules/node/handler/node_handler.e index 1262432..fa80c23 100644 --- a/modules/node/handler/node_handler.e +++ b/modules/node/handler/node_handler.e @@ -99,6 +99,15 @@ feature -- HTTP Methods edit_response.execute elseif req.percent_encoded_path_info.ends_with ("/revision") then do_revisions (req, res) + elseif req.percent_encoded_path_info.ends_with ("/add_child/page") then + -- Add child node + l_nid := node_id_path_parameter (req) + if l_nid > 0 then + -- create a new child node with node id l_id as parent. + create_new_node (req, res) + else + send_not_found (req, res) + end else -- Display existing node l_nid := node_id_path_parameter (req) @@ -124,7 +133,16 @@ feature -- HTTP Methods view_response.set_node (l_node) view_response.set_revision (l_rev) 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)) + 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) end else @@ -166,6 +184,9 @@ feature -- HTTP Methods elseif req.percent_encoded_path_info.starts_with ("/node/add/") then create edit_response.make (req, res, api, node_api) edit_response.execute + elseif req.percent_encoded_path_info.ends_with ("/add_child/page") then + create edit_response.make (req, res, api, node_api) + edit_response.execute else to_implement ("REST API") send_not_implemented ("REST API not yet implemented", req, res) @@ -355,5 +376,4 @@ feature {NONE} -- Node send_bad_request (req, res) end end - end diff --git a/modules/node/handler/node_view_response.e b/modules/node/handler/node_view_response.e index 3c9d910..10aa4bd 100644 --- a/modules/node/handler/node_view_response.e +++ b/modules/node/handler/node_view_response.e @@ -72,6 +72,7 @@ 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!") @@ -84,6 +85,8 @@ feature -- Execution if l_node /= Void and revision > 0 then set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title)) end + + end end diff --git a/modules/node/persistence/cms_node_storage_i.e b/modules/node/persistence/cms_node_storage_i.e index 48ead87..7be4201 100644 --- a/modules/node/persistence/cms_node_storage_i.e +++ b/modules/node/persistence/cms_node_storage_i.e @@ -117,6 +117,11 @@ feature -- Access 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 + end + feature -- Change: Node save_node (a_node: CMS_NODE) diff --git a/modules/node/persistence/cms_node_storage_null.e b/modules/node/persistence/cms_node_storage_null.e index 695a23e..d15fe6f 100644 --- a/modules/node/persistence/cms_node_storage_null.e +++ b/modules/node/persistence/cms_node_storage_null.e @@ -86,6 +86,12 @@ feature -- Access: node create {ARRAYED_LIST [CMS_USER]} Result.make (0) end + available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE] + -- + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + end + feature -- Node new_node (a_node: CMS_NODE) diff --git a/modules/node/persistence/cms_node_storage_sql.e b/modules/node/persistence/cms_node_storage_sql.e index adf1b75..91c995f 100644 --- a/modules/node/persistence/cms_node_storage_sql.e +++ b/modules/node/persistence/cms_node_storage_sql.e @@ -255,6 +255,32 @@ feature -- Access -- end end + + available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE] + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + + error_handler.reset + write_information_log (generator + ".available_parents_for_node") + + from + create l_parameters.make (1) + l_parameters.put (a_node.id, "nid") + sql_query (sql_select_available_parents_for_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 + feature -- Change: Node new_node (a_node: CMS_NODE) @@ -456,6 +482,8 @@ 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;" + feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid AND nodes.revision = :revision;" diff --git a/modules/node/persistence/cms_node_storage_sql_page_extension.e b/modules/node/persistence/cms_node_storage_sql_page_extension.e index d28091b..03f3477 100644 --- a/modules/node/persistence/cms_node_storage_sql_page_extension.e +++ b/modules/node/persistence/cms_node_storage_sql_page_extension.e @@ -138,7 +138,7 @@ feature {NONE} -- Implementation feature -- SQL - sql_select_node_data: STRING = "SELECT nid, revision, parent FROM page_nodes WHERE nid =:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;" + sql_select_node_data: STRING = "SELECT nid, revision, parent FROM page_nodes WHERE nid =:nid AND revision =:revision ORDER BY revision DESC LIMIT 1;" sql_insert_node_data: STRING = "INSERT INTO page_nodes (nid, revision, parent) VALUES (:nid, :revision, :parent);" sql_update_node_data: STRING = "UPDATE page_nodes SET nid=:nid, revision=:revision, parent=:parent WHERE nid=:nid AND revision=:revision;"