diff --git a/modules/node/cms_node_api.e b/modules/node/cms_node_api.e
index 8f0a8d9..2e8caf4 100644
--- a/modules/node/cms_node_api.e
+++ b/modules/node/cms_node_api.e
@@ -212,6 +212,14 @@ feature -- Access: Node
Result := node_storage.nodes
end
+
+ trash_nodes (a_user: CMS_USER): LIST [CMS_NODE]
+ -- List of nodes with status in {CMS_NODE_API}.trashed.
+ -- if the current user is admin, it will retrieve all the trashed nodes
+ do
+ Result := node_storage.trash_nodes (a_user.id)
+ end
+
recent_nodes (a_offset, a_rows: INTEGER): LIST [CMS_NODE]
-- List of the `a_rows' most recent nodes starting from `a_offset'.
do
@@ -316,6 +324,21 @@ feature -- Change: Node
node_storage.update_node (a_node)
end
+ trash_node (a_node: CMS_NODE)
+ -- Trash node `a_node'.
+ --! remove the node from the storage.
+ do
+ node_storage.trash_node (a_node)
+ end
+
+
+ revert_node (a_node: CMS_NODE)
+ -- Revert node `a_node'.
+ -- From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
+ do
+ node_storage.revert_node (a_node)
+ end
+
feature -- Node status
diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e
index 5d991e9..fd90c12 100644
--- a/modules/node/handler/cms_node_type_webform_manager.e
+++ b/modules/node/handler/cms_node_type_webform_manager.e
@@ -182,21 +182,28 @@ feature -- Output
a_response.add_variable (a_node, "node")
create lnk.make (a_response.translation ("View", Void), a_response.node_local_link (a_node).location)
lnk.set_weight (1)
-
- a_response.add_to_primary_tabs (lnk)
- create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
- lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
- if
- a_node /= Void and then
- a_node.id > 0 and then
- attached node_api.node_type_for (a_node) as l_type and then
- node_api.has_permission_for_action_on_node ("delete", a_node, a_response.current_user (a_response.request))
- then
- create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")
- lnk.set_weight (3)
+ if a_node.status = {CMS_NODE_API}.trashed then
+ create lnk.make ("Trash", node_api.node_path (a_node) + "/trash")
+ lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
+ else
+ -- 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)
+ a_response.add_to_primary_tabs (lnk)
+
+ if
+ a_node /= Void and then
+ a_node.id > 0 and then
+ attached node_api.node_type_for (a_node) as l_type and then
+ node_api.has_permission_for_action_on_node ("delete", a_node, a_response.current_user (a_response.request))
+ then
+ create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")
+ lnk.set_weight (3)
+ a_response.add_to_primary_tabs (lnk)
+ end
end
create s.make_empty
diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e
index 598a09b..dc57770 100644
--- a/modules/node/handler/node_form_response.e
+++ b/modules/node/handler/node_form_response.e
@@ -99,6 +99,29 @@ feature -- Execution
set_title (formatted_string (translation ("Delete $1 #$2", Void), [l_type.title, l_node.id]))
f.append_to_html (wsf_theme, b)
end
+ elseif
+ request.path_info.ends_with_general ("/trash") and then
+ node_api.has_permission_for_action_on_node ("trash", l_node, current_user (request))
+ then
+ f := new_trash_form (l_node, url (request.path_info, Void), "trash-" + l_type.name, l_type)
+ invoke_form_alter (f, fd)
+ if request.is_post_request_method then
+ f.process (Current)
+ fd := f.last_data
+ end
+ if l_node.has_id then
+ add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("View", Void), node_url (l_node)), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", "/node/" + l_node.id.out + "/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) + " trasged")
+ else
+ set_title (formatted_string (translation ("Trash $1 #$2", Void), [l_type.title, l_node.id]))
+ f.append_to_html (wsf_theme, b)
+ end
else
b.append ("
")
b.append (translation ("Access denied", Void))
@@ -305,6 +328,47 @@ feature -- Form
Result := f
end
+
+ new_trash_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_node_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM
+ -- Create a web form named `a_name' for node `a_node' (if set), using form action url `a_url', and for type of node `a_node_type'.
+ local
+ f: CMS_FORM
+ ts: WSF_FORM_SUBMIT_INPUT
+ do
+ create f.make (a_url, a_name)
+
+ f.extend_html_text ("
")
+ f.extend_html_text ("")
+ if
+ a_node /= Void and then
+ a_node.id > 0
+ then
+ create ts.make ("op")
+ ts.set_default_value ("Trash")
+ fixme ("[
+ ts.set_default_value (translation ("Trash"))
+ ]")
+ f.extend (ts)
+ end
+ f.extend_html_text ("
")
+ f.extend_html_text ("")
+ if
+ a_node /= Void and then
+ a_node.id > 0
+ then
+ create ts.make ("op")
+ ts.set_default_value ("Revert")
+ fixme ("[
+ ts.set_default_value (translation ("Revert"))
+ ]")
+ f.extend (ts)
+ end
+ Result := f
+ end
+
+
+
+
populate_form (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form: WSF_FORM; a_node: detachable CMS_NODE)
-- Fill the web form `a_form' with data from `a_node' if set,
-- and apply this to content type `a_content_type'.
diff --git a/modules/node/handler/node_handler.e b/modules/node/handler/node_handler.e
index 5ef662d..83ea7e6 100644
--- a/modules/node/handler/node_handler.e
+++ b/modules/node/handler/node_handler.e
@@ -93,6 +93,10 @@ feature -- HTTP Methods
check valid_url: req.path_info.starts_with_general ("/node/") end
create edit_response.make (req, res, api, node_api)
edit_response.execute
+ elseif req.path_info.ends_with_general ("/trash") then
+ check valid_url: req.path_info.starts_with_general ("/node/") end
+ create edit_response.make (req, res, api, node_api)
+ edit_response.execute
else
-- Display existing node
l_nid := node_id_path_parameter (req)
@@ -129,6 +133,18 @@ feature -- HTTP Methods
then
do_delete (req, res)
end
+ elseif req.path_info.ends_with_general ("/trash") then
+ if
+ attached {WSF_STRING} req.form_parameter ("op") as l_op and then
+ l_op.value.same_string ("Trash")
+ then
+ do_trash (req, res)
+ elseif
+ attached {WSF_STRING} req.form_parameter ("op") as l_op and then
+ l_op.value.same_string ("Revert")
+ then
+ do_revert (req, res)
+ end
elseif req.path_info.starts_with_general ("/node/add/") then
create edit_response.make (req, res, api, node_api)
edit_response.execute
@@ -178,6 +194,62 @@ feature -- HTTP Methods
send_not_implemented ("REST API not yet implemented", req, res)
end
+feature {NONE} -- Trash:Revert
+
+ do_trash (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Trash a node from the database.
+ do
+ if attached current_user (req) as l_user then
+ if attached {WSF_STRING} req.path_parameter ("id") as l_id then
+ if
+ l_id.is_integer and then
+ attached node_api.node (l_id.integer_value) as l_node
+ then
+ if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then
+ node_api.trash_node (l_node)
+ res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
+ else
+ send_access_denied (req, res)
+ -- send_not_authorized ?
+ end
+ else
+ do_error (req, res, l_id)
+ end
+ else
+ (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute
+ end
+ else
+ send_access_denied (req, res)
+ end
+ end
+
+ do_revert (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Revert a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
+ do
+ if attached current_user (req) as l_user then
+ if attached {WSF_STRING} req.path_parameter ("id") as l_id then
+ if
+ l_id.is_integer and then
+ attached node_api.node (l_id.integer_value) as l_node
+ then
+ if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then
+ node_api.revert_node (l_node)
+ res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
+ else
+ send_access_denied (req, res)
+ -- send_not_authorized ?
+ end
+ else
+ do_error (req, res, l_id)
+ end
+ else
+ (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute
+ end
+ else
+ send_access_denied (req, res)
+ end
+ end
+
feature -- Error
do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING)
diff --git a/modules/node/handler/trash_handler.e b/modules/node/handler/trash_handler.e
new file mode 100644
index 0000000..0811073
--- /dev/null
+++ b/modules/node/handler/trash_handler.e
@@ -0,0 +1,78 @@
+note
+ description: "Request handler related to /trash "
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ TRASH_HANDLER
+
+
+inherit
+ CMS_NODE_HANDLER
+
+ WSF_URI_HANDLER
+ rename
+ new_mapping as new_uri_mapping
+ end
+
+ WSF_RESOURCE_HANDLER_HELPER
+ redefine
+ do_get
+ end
+
+ REFACTORING_HELPER
+
+create
+ make
+
+feature -- execute
+
+ execute (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Execute request handler
+ do
+ execute_methods (req, res)
+ end
+
+feature -- HTTP Methods
+
+ do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
+ --
+ local
+ l_page: CMS_RESPONSE
+ s: STRING
+ n: CMS_NODE
+ lnk: CMS_LOCAL_LINK
+ do
+ -- At the moment the template is hardcoded, but we can
+ -- get them from the configuration file and load them into
+ -- the setup class.
+
+ create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
+
+ if attached current_user (req) as l_user then
+
+ l_page.add_variable (node_api.trash_nodes (l_user), "nodes")
+
+ -- NOTE: for development purposes we have the following hardcode output.
+ create s.make_from_string ("Nodes:
")
+ if attached node_api.trash_nodes (l_user) as lst then
+ s.append ("%N")
+ across
+ lst as ic
+ loop
+ n := ic.item
+ lnk := node_api.node_link (n)
+ s.append ("- ")
+ s.append (l_page.link (lnk.title, lnk.location, Void))
+ s.append ("
%N")
+ end
+ s.append ("
%N")
+ end
+
+ l_page.set_main_content (s)
+ -- l_page.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented
", Void), "highlighted")
+ l_page.execute
+ end
+ end
+
+end
diff --git a/modules/node/node_module.e b/modules/node/node_module.e
index 2f49c9c..4431205 100644
--- a/modules/node/node_module.e
+++ b/modules/node/node_module.e
@@ -127,6 +127,7 @@ feature -- Access: router
l_node_handler: NODE_HANDLER
l_nodes_handler: NODES_HANDLER
l_uri_mapping: WSF_URI_MAPPING
+ l_trash_handler: TRASH_HANDLER
do
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
create l_node_handler.make (a_api, a_node_api)
@@ -136,6 +137,7 @@ feature -- Access: router
a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
+ a_router.handle_with_request_methods ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/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)
@@ -144,6 +146,13 @@ feature -- Access: router
create l_nodes_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
+
+ --Trash
+
+ create l_trash_handler.make (a_api, a_node_api)
+ create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
+ a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
+
end
feature -- Hooks
diff --git a/modules/node/persistence/cms_node_storage_i.e b/modules/node/persistence/cms_node_storage_i.e
index 6090d95..e359796 100644
--- a/modules/node/persistence/cms_node_storage_i.e
+++ b/modules/node/persistence/cms_node_storage_i.e
@@ -70,6 +70,11 @@ feature -- Access
deferred
end
+ trash_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes by user `a_user_id'.
+ deferred
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
deferred
@@ -135,6 +140,36 @@ feature -- Change: Node
deferred
end
+ trash_node (a_node: CMS_NODE)
+ -- Trash `a_node'.
+ do
+ if a_node.has_id then
+ trash_node_by_id (a_node.id)
+ end
+ end
+
+ revert_node (a_node: CMS_NODE)
+ -- Revert `a_node'.
+ do
+ if a_node.has_id then
+ revert_node_by_id (a_node.id)
+ end
+ end
+
+ trash_node_by_id (a_id: INTEGER_64)
+ -- Trash node by id `a_id'.
+ require
+ valid_node_id: a_id > 0
+ deferred
+ end
+
+ revert_node_by_id (a_id: INTEGER_64)
+ -- Revert node by id `a_id'.
+ require
+ valid_node_id: a_id > 0
+ deferred
+ end
+
-- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- -- Update node title to `a_title', node identified by id `a_node_id'.
-- -- The user `a_user_id' is an existing or new collaborator.
diff --git a/modules/node/persistence/cms_node_storage_null.e b/modules/node/persistence/cms_node_storage_null.e
index 87559af..02aaeb2 100644
--- a/modules/node/persistence/cms_node_storage_null.e
+++ b/modules/node/persistence/cms_node_storage_null.e
@@ -41,6 +41,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
+ trash_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes by user `a_user_id'.
+ do
+ create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of the `a_count' most recent nodes, starting from `a_lower'.
do
@@ -80,6 +86,16 @@ feature -- Node
do
end
+ trash_node_by_id (a_id: INTEGER_64)
+ --
+ do
+ end
+
+ revert_node_by_id (a_id: INTEGER_64)
+ --
+ do
+ end
+
-- update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- --
-- do
diff --git a/modules/node/persistence/cms_node_storage_sql.e b/modules/node/persistence/cms_node_storage_sql.e
index 06ecadc..971f8c8 100644
--- a/modules/node/persistence/cms_node_storage_sql.e
+++ b/modules/node/persistence/cms_node_storage_sql.e
@@ -59,6 +59,36 @@ feature -- Access
-- end
end
+ trash_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes.
+ local
+ l_parameters: STRING_TABLE [detachable ANY]
+ do
+ create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
+
+ error_handler.reset
+ write_information_log (generator + ".trash_nodes")
+
+ from
+ create l_parameters.make (1)
+ if a_user_id > 1 then
+ -- Not admin user
+ l_parameters.put (a_user_id, "author")
+ sql_query (sql_select_trash_nodes_by_author, l_parameters)
+ else
+ sql_query (sql_select_trash_nodes, Void)
+ end
+ sql_start
+ until
+ sql_after
+ loop
+ if attached fetch_node as l_node then
+ Result.force (l_node)
+ end
+ sql_forth
+ end
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
local
@@ -147,7 +177,7 @@ feature -- Change: Node
l_time: DATE_TIME
do
create l_time.make_now_utc
- write_information_log (generator + ".delete_node")
+ write_information_log (generator + ".delete_node {" + a_id.out + "}")
error_handler.reset
create l_parameters.make (1)
@@ -157,6 +187,40 @@ feature -- Change: Node
sql_change (sql_delete_node, l_parameters)
end
+
+ trash_node_by_id (a_id: INTEGER_64)
+ --
+ local
+ l_parameters: STRING_TABLE [ANY]
+ l_time: DATE_TIME
+ do
+ create l_time.make_now_utc
+ write_information_log (generator + ".trash_node {" + a_id.out + "}")
+
+ error_handler.reset
+ create l_parameters.make (1)
+ l_parameters.put (a_id, "nid")
+ sql_change (sql_trash_node, l_parameters)
+ end
+
+ revert_node_by_id (a_id: INTEGER_64)
+ --
+ local
+ l_parameters: STRING_TABLE [ANY]
+ l_time: DATE_TIME
+ do
+ create l_time.make_now_utc
+ write_information_log (generator + ".revert_node {" + a_id.out + "}")
+
+ error_handler.reset
+ create l_parameters.make (1)
+ l_parameters.put (l_time, "changed")
+ l_parameters.put ({CMS_NODE_API}.not_published, "status")
+ l_parameters.put (a_id, "nid")
+ sql_change (sql_revert_node, l_parameters)
+ end
+
+
feature {NONE} -- Implementation
store_node (a_node: CMS_NODE)
@@ -228,6 +292,14 @@ feature {NONE} -- Queries
-- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
+ sql_select_trash_nodes: STRING = "SELECT * FROM Nodes WHERE status = -1 ;"
+ -- SQL Query to retrieve all trahsed nodes.
+ --| note: {CMS_NODE_API}.trashed = -1
+
+ sql_select_trash_nodes_by_author: STRING = "SELECT * FROM Nodes WHERE status = -1 and author = :author ;"
+ -- SQL Query to retrieve all nodes by a given author.
+ --| note: {CMS_NODE_API}.trashed = -1
+
sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes WHERE nid =:nid ORDER BY revision DESC, publish DESC LIMIT 1;"
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes ORDER BY nid DESC, publish DESC LIMIT :rows OFFSET :offset ;"
@@ -243,6 +315,12 @@ feature {NONE} -- Queries
sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata.
+ sql_trash_node: STRING = "DELETE FROM NODES WHERE nid=:nid"
+ -- Physical deletion with free metadata.
+
+ sql_revert_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
+ -- Revert node to {CMS_NODE_API}.not_publised.
+
-- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;"
-- sql_update_node_title: STRING ="UPDATE nodes SET title=:title, changed=:changed, revision = revision + 1 WHERE nid=:nid;"
diff --git a/src/persistence/sql/cms_storage_sql_builder.e b/src/persistence/sql/cms_storage_sql_builder.e
index 6000207..a1bec3b 100644
--- a/src/persistence/sql/cms_storage_sql_builder.e
+++ b/src/persistence/sql/cms_storage_sql_builder.e
@@ -40,6 +40,10 @@ feature -- Initialization
--| Node
-- FIXME: move that initialization to node module
+ -- TODO: should we move the initialization to an
+ --! external configuration file?
+ --! at the moment we only have 1 admin to the whole site.
+ --! is that ok?
l_anonymous_role.add_permission ("view any page")
a_storage.save_user_role (l_anonymous_role)
@@ -47,9 +51,11 @@ feature -- Initialization
l_authenticated_role.add_permission ("view any page")
l_authenticated_role.add_permission ("edit any page")
l_authenticated_role.add_permission ("delete page")
+ l_authenticated_role.add_permission ("trash page")
l_authenticated_role.add_permission ("view own page")
l_authenticated_role.add_permission ("edit own page")
l_authenticated_role.add_permission ("delete own page")
+ l_authenticated_role.add_permission ("trash own page")
a_storage.save_user_role (l_authenticated_role)