Added revisions support to node management.

Updated node extension implementation.
Updated known permissions for node module.
Improved code for node storage extension , in preparation to code factorization.
Ensured that author is updated when saved.
This commit is contained in:
2015-08-07 19:17:25 +02:00
parent 44ada4b6b1
commit cd0c2acd87
15 changed files with 464 additions and 132 deletions

View File

@@ -11,12 +11,27 @@ CREATE TABLE nodes (
`publish` DATETIME,
`created` DATETIME NOT NULL,
`changed` DATETIME NOT NULL,
`status` INTEGER
`status` INTEGER,
CONSTRAINT Unique_nid_revision UNIQUE (nid,revision)
);
CREATE TABLE node_revisions (
`nid` INTEGER NOT NULL,
`revision` INTEGER NOT NULL,
`title` VARCHAR(255) NOT NULL,
`summary` TEXT,
`content` TEXT,
`format` VARCHAR(128),
`author` INTEGER,
`changed` DATETIME NOT NULL,
`status` INTEGER,
CONSTRAINT Unique_nid_revision PRIMARY KEY (nid,revision)
);
CREATE TABLE page_nodes(
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`revision` INTEGER,
`parent` INTEGER
`nid` INTEGER NOT NULL,
`revision` INTEGER NOT NULL,
`parent` INTEGER,
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);

View File

@@ -49,12 +49,12 @@ feature {CMS_API} -- Module Initialization
Precursor (api)
if attached {CMS_NODE_API} api.module_api ({CMS_NODE_MODULE}) as l_node_api then
create ct
create blog_api.make (api, l_node_api)
node_api := l_node_api
-- Depends on {CMS_NODE_MODULE}
create ct
--| For now, add all available formats to content type `ct'.
across
api.formats as ic
@@ -67,7 +67,7 @@ feature {CMS_API} -- Module Initialization
-- Add support for CMS_BLOG, which requires a storage extension to store the optional "tags" value
-- For now, we only have extension based on SQL statement.
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_BLOG_EXTENSION}.make (l_sql_node_storage))
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_BLOG_EXTENSION}.make (l_node_api, l_sql_node_storage))
end
end
end
@@ -83,9 +83,10 @@ feature {CMS_API} -- Module management
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then
sql := "[
CREATE TABLE blog_post_nodes(
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK("nid">=0),
`revision` INTEGER,
`tags` VARCHAR(255)
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);
]"
l_sql_storage.sql_execute_script (sql, Void)

View File

@@ -11,6 +11,7 @@ inherit
CMS_PROXY_STORAGE_SQL
rename
make as make_proxy,
sql_storage as node_storage
redefine
node_storage
@@ -21,6 +22,12 @@ create
feature {NONE} -- Initialization
make (a_node_api: CMS_NODE_API; a_sql_storage: CMS_NODE_STORAGE_SQL)
do
set_node_api (a_node_api)
make_proxy (a_sql_storage)
end
node_storage: CMS_NODE_STORAGE_SQL
-- <Precursor>
@@ -39,22 +46,19 @@ feature -- Persistence
l_new_tags: detachable STRING_32
l_previous_tags: detachable STRING_32
l_update: BOOLEAN
l_has_modif: BOOLEAN
do
error_handler.reset
if attached api as l_api then
l_api.logger.put_information (generator + ".store", Void)
end
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_blog_data, l_parameters)
if not has_error then
if sql_rows_count = 1 then
l_previous_tags := sql_read_string_32 (3)
l_update := True
error_handler.reset
-- Check existing record, if any.
if attached node_data (a_node) as d then
l_update := d.revision = a_node.revision
l_previous_tags := d.tags
end
if not has_error then
if attached a_node.tags as l_tags and then not l_tags.is_empty then
create l_new_tags.make (0)
across
@@ -68,37 +72,60 @@ feature -- Persistence
else
l_new_tags := Void
end
l_parameters.put (l_new_tags, "tags")
if l_update and l_new_tags /~ l_previous_tags then
sql_change (sql_update_blog_data, l_parameters)
elseif l_new_tags /= Void then
sql_change (sql_insert_blog_data, l_parameters)
l_has_modif := l_has_modif or (l_new_tags /~ l_previous_tags)
create l_parameters.make (3)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
l_parameters.force (l_new_tags, "tags")
if l_update then
if l_has_modif then
sql_change (sql_update_node_data, l_parameters)
end
else
-- no blog data, means everything is empty.
if l_has_modif then
sql_change (sql_insert_node_data, l_parameters)
else
-- no page data, means everything is empty.
-- FOR NOW: always record row
-- sql_change (sql_insert_node_data, l_parameters)
end
end
end
end
load (a_node: CMS_BLOG)
local
l_tags: READABLE_STRING_32
do
if attached node_data (a_node) as d then
l_tags := d.tags
a_node.set_tags_from_string (l_tags)
end
end
node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; tags: READABLE_STRING_32]
local
l_parameters: STRING_TABLE [ANY]
l_rev: INTEGER_64
l_tags: detachable READABLE_STRING_32
n: INTEGER
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_blog_data, l_parameters)
sql_query (sql_select_node_data, l_parameters)
if not has_error then
n := sql_rows_count
if n = 1 then
-- nid, revision, parent
if
attached sql_read_string_32 (3) as l_tags and then
not l_tags.is_whitespace
then
-- FIXME: find a simple way to access the declared content types.
a_node.set_tags_from_string (l_tags)
-- nid, revision, tags
l_rev := sql_read_integer_64 (2)
l_tags := sql_read_string_32 (3)
if l_tags /= Void then
Result := [l_rev, l_tags]
end
else
check unique_data: n = 0 end
@@ -108,8 +135,8 @@ feature -- Persistence
feature -- SQL
sql_select_blog_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid =:nid AND revision=:revision;"
sql_insert_blog_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
sql_update_blog_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
sql_select_node_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid =:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;"
sql_insert_node_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
sql_update_node_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
end

View File

@@ -25,6 +25,7 @@ feature {NONE} -- Initialization
do
node_storage := a_node_storage
make (a_api)
-- error_handler.add_synchronization (a_node_storage.error_handler)
end
initialize
@@ -220,6 +221,12 @@ feature -- Access: Node
Result := node_storage.nodes
end
node_revisions (a_node: CMS_NODE): LIST [CMS_NODE]
do
Result := node_storage.node_revisions (a_node)
Result := full_nodes (Result)
end
trashed_nodes (a_user: detachable CMS_USER): LIST [CMS_NODE]
-- List of nodes with status in {CMS_NODE_API}.trashed.
-- if `a_user' is set, return nodes related to this user.
@@ -242,6 +249,11 @@ feature -- Access: Node
Result := full_node (node_storage.node_by_id (a_id))
end
revision_node (a_node_id: INTEGER_64; a_revision_id: INTEGER_64): detachable CMS_NODE
do
Result := full_node (node_storage.node_by_id_and_revision (a_node_id, a_revision_id))
end
full_node (a_node: detachable CMS_NODE): detachable CMS_NODE
-- If `a_node' is partial, return the full node from `a_node',
-- otherwise return directly `a_node'.
@@ -277,10 +289,24 @@ feature -- Access: Node
end
end
full_nodes (a_nodes: LIST [CMS_NODE]): LIST [CMS_NODE]
-- Convert list of nodes into a list of nodes when possible.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (a_nodes.count)
across
a_nodes as ic
loop
if attached full_node (ic.item) as l_full then
Result.force (l_full)
end
end
end
is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN
-- Is the user `u' owner of the node `n'.
do
if attached node_storage.node_author (a_node.id) as l_author then
if attached node_storage.node_author (a_node) as l_author then
Result := u.same_as (l_author)
end
end
@@ -306,7 +332,9 @@ feature -- Change: Node
save_node (a_node: CMS_NODE)
-- Save `a_node'.
do
reset_error
node_storage.save_node (a_node)
error_handler.append (node_storage.error_handler)
end
new_node (a_node: CMS_NODE)
@@ -314,39 +342,47 @@ feature -- Change: Node
require
no_id: not a_node.has_id
do
reset_error
node_storage.new_node (a_node)
error_handler.append (node_storage.error_handler)
end
delete_node (a_node: CMS_NODE)
-- Delete `a_node'.
do
reset_error
if a_node.has_id then
node_storage.delete_node (a_node)
error_handler.append (node_storage.error_handler)
end
end
update_node (a_node: CMS_NODE)
-- Update node `a_node' data.
do
reset_error
node_storage.update_node (a_node)
error_handler.append (node_storage.error_handler)
end
trash_node (a_node: CMS_NODE)
-- Trash node `a_node'.
--! remove the node from the storage.
do
reset_error
node_storage.trash_node (a_node)
error_handler.append (node_storage.error_handler)
end
restore_node (a_node: CMS_NODE)
-- Restore node `a_node'.
-- From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
do
reset_error
node_storage.restore_node (a_node)
error_handler.append (node_storage.error_handler)
end
feature -- Node status
Not_published: INTEGER = 0

View File

@@ -71,7 +71,7 @@ feature {CMS_API} -- Module Initialization
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
-- For now, we only have extension based on SQL statement.
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_sql_node_storage))
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
-- FIXME: the following code is mostly for test purpose/initialization, remove later
if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then
@@ -157,10 +157,18 @@ feature -- Access
Result.force ("view any " + l_type_name)
Result.force ("edit any " + l_type_name)
Result.force ("delete any " + l_type_name)
Result.force ("trash any " + l_type_name)
Result.force ("restore any " + l_type_name)
Result.force ("view revisions any " + l_type_name)
Result.force ("view own " + l_type_name)
Result.force ("edit own " + l_type_name)
Result.force ("delete own " + l_type_name)
Result.force ("trash own " + l_type_name)
Result.force ("restore own " + l_type_name)
Result.force ("view revisions own " + l_type_name)
end
end
end
@@ -189,6 +197,7 @@ feature -- Access: router
a_router.map (l_uri_mapping, a_router.methods_get_post)
a_router.handle ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle ("/node/{id}/revision", l_node_handler, a_router.methods_get)
a_router.handle ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
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)
@@ -202,7 +211,6 @@ feature -- Access: router
a_router.map (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 (l_uri_mapping, a_router.methods_get)

View File

@@ -187,7 +187,8 @@ feature -- Forms ...
a_node.set_content (b, s, f.name)
end
-- Update author
a_node.set_author (response.user)
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable CMS_NODE): G
@@ -272,20 +273,25 @@ feature -- Output
lnk.set_weight (1)
a_response.add_to_primary_tabs (lnk)
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
elseif a_node /= Void and then 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)
a_response.add_to_primary_tabs (lnk)
if
node_api.has_permission_for_action_on_node ("view revisions", a_node, l_user)
then
create lnk.make ("Revisions", node_api.node_path (a_node) + "/revision")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
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, l_user)
then
create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")

View File

@@ -236,17 +236,15 @@ feature -- Form
end
if a_node /= Void then
l_node := a_node
apply_form_data_to_node (a_type, fd, a_node)
if l_node.has_id then
change_node (a_type, fd, a_node)
s := "modified"
else
change_node (a_type, fd, a_node)
l_node.set_author (user)
s := "created"
end
else
l_node := new_node (a_type, fd, Void)
l_node.set_author (user)
s := "created"
end
@@ -261,7 +259,11 @@ feature -- Form
else
api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node, Void))
end
if node_api.has_error then
add_error_message ("Node #" + l_node.id.out + " failed to save.")
else
add_success_message ("Node #" + l_node.id.out + " saved.")
end
if
attached fd.string_item ("path_alias") as f_path_alias and then
@@ -398,9 +400,10 @@ feature -- Form
else
Result := a_content_type.new_node (a_node)
end
Result.set_author (user)
end
change_node (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE)
apply_form_data_to_node (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE)
-- Update node `a_node' with form_data `a_form_data' for the given content type `a_content_type'.
do
if attached node_api.node_type_webform_manager (a_content_type) as wf then

View File

@@ -81,7 +81,7 @@ feature -- HTTP Methods
-- <Precursor>
local
l_node: detachable CMS_NODE
l_nid: INTEGER_64
l_nid, l_rev: INTEGER_64
edit_response: NODE_FORM_RESPONSE
view_response: NODE_VIEW_RESPONSE
do
@@ -97,16 +97,32 @@ feature -- HTTP Methods
check valid_url: req.percent_encoded_path_info.starts_with ("/node/") end
create edit_response.make (req, res, api, node_api)
edit_response.execute
elseif req.percent_encoded_path_info.ends_with ("/revision") then
do_revisions (req, res)
else
-- Display existing node
l_nid := node_id_path_parameter (req)
if l_nid > 0 then
if
attached {WSF_STRING} req.query_parameter ("revision") as p_rev and then
p_rev.value.is_integer_64
then
l_rev := p_rev.value.to_integer_64
end
l_node := node_api.node (l_nid)
if
l_node /= Void and then l_node.is_published
l_node /= Void and then
l_rev > 0 and then
node_api.has_permission_for_action_on_node ("view revisions", l_node, current_user (req))
then
l_node := node_api.revision_node (l_nid, l_rev)
end
if
l_node /= Void and then (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)
view_response.set_revision (l_rev)
view_response.execute
else
send_not_found (req, res)
@@ -234,7 +250,7 @@ feature {NONE} -- Trash:Restore
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
if node_api.has_permission_for_action_on_node ("restore", l_node, current_user (req)) then
node_api.restore_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else
@@ -252,6 +268,58 @@ feature {NONE} -- Trash:Restore
end
end
do_revisions (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Display revisions of a node.
local
r: GENERIC_VIEW_CMS_RESPONSE
b: STRING
n: CMS_NODE
l_link: CMS_LOCAL_LINK
do
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 ("view revisions", l_node, current_user (req)) then
create r.make (req, res, api)
create b.make_empty
b.append ("<ul>")
across
node_api.node_revisions (l_node) as ic
loop
n := ic.item
b.append ("<li>")
b.append ("<a href=%"")
b.append (r.url (node_api.node_path (n) + "?revision=" + n.revision.out, Void))
b.append ("%">")
b.append (n.revision.out)
b.append (" : ")
b.append (n.modification_date.out)
b.append ("</a>")
if attached n.author as l_author then
b.append (" by ")
b.append (r.link (l_author.name, "user/" + l_author.id.out, Void))
end
b.append ("</li>")
end
b.append ("</ul>")
r.set_title ("Revisions for " + html_encoded (l_node.title))
r.set_main_content (b)
r.execute
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
end
feature -- Error
do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING)

View File

@@ -36,6 +36,9 @@ feature -- Access
node: detachable CMS_NODE
revision: INTEGER_64
-- If not zero, about history version of the node.
feature -- Element change
set_node (a_node: like node)
@@ -43,18 +46,22 @@ feature -- Element change
node := a_node
end
set_revision (a_rev: like revision)
do
revision := a_rev
end
feature -- Execution
process
-- Computed response message.
local
b: STRING_8
b: detachable STRING_8
nid: INTEGER_64
l_node: like node
do
l_node := node
if l_node = Void then
create b.make_empty
nid := node_id_path_parameter (request)
if nid > 0 then
l_node := node_api.node (nid)
@@ -67,8 +74,16 @@ feature -- Execution
then
l_manager.append_html_output_to (l_node, Current)
end
elseif revision > 0 then
set_main_content ("Missing revision node!")
else
set_main_content ("Missing node")
set_main_content ("Missing node!")
end
if revision > 0 then
add_warning_message ("The revisions let you track differences between multiple versions of a post.")
end
if l_node /= Void and revision > 0 then
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
end
end

View File

@@ -6,8 +6,17 @@ note
deferred class
CMS_NODE_STORAGE_EXTENSION [G -> CMS_NODE]
feature -- Change
set_node_api (a_node_api: CMS_NODE_API)
do
node_api := a_node_api
end
feature -- Access
node_api: CMS_NODE_API
content_type: READABLE_STRING_8
deferred
end

View File

@@ -73,6 +73,11 @@ feature -- Access
deferred
end
node_revisions (a_node: CMS_NODE): LIST [CMS_NODE]
-- Revisions of node `a_node'.
deferred
end
trashed_nodes (a_user: detachable CMS_USER): LIST [CMS_NODE]
-- List of nodes by user `a_user' if set, or any.
require
@@ -92,10 +97,18 @@ feature -- Access
deferred
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
node_by_id_and_revision (a_node_id, a_revision: INTEGER_64): detachable CMS_NODE
-- Retrieve node by node id `a_node_id' and revision `a_revision', if any.
require
has_node_id: a_node_id > 0
has_revision: a_revision > 0
deferred
end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author. if any.
require
valid_node: a_id > 0
valid_node: a_node.has_id
deferred
end

View File

@@ -41,6 +41,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
node_revisions (a_node: CMS_NODE): LIST [CMS_NODE]
-- Revisions of node `a_node'.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
trashed_nodes (a_user: detachable CMS_USER): LIST [CMS_NODE]
-- <Precursor>.
do
@@ -58,7 +64,12 @@ feature -- Access: node
do
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
node_by_id_and_revision (a_node_id, a_revision: INTEGER_64): detachable CMS_NODE
-- <Precuror>
do
end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author. if any.
do
end

View File

@@ -60,6 +60,33 @@ feature -- Access
-- end
end
node_revisions (a_node: CMS_NODE): LIST [CMS_NODE]
-- Revisions of node `a_node'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
Result.force (a_node)
error_handler.reset
write_information_log (generator + ".node_revisions")
from
create l_parameters.make (1)
l_parameters.force (a_node.id, "nid")
l_parameters.force (a_node.revision, "revision")
sql_query (sql_select_node_revisions, 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
trashed_nodes (a_user: detachable CMS_USER): LIST [CMS_NODE]
-- List of nodes.
local
@@ -130,15 +157,32 @@ feature -- Access
end
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
node_by_id_and_revision (a_node_id, a_revision: INTEGER_64): detachable CMS_NODE
-- Retrieve node by id `a_id', if any.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".node")
create l_parameters.make (1)
l_parameters.put (a_node_id, "nid")
l_parameters.put (a_revision, "revision")
sql_query (sql_select_node_by_id_and_revision, l_parameters)
if sql_rows_count = 1 then
Result := fetch_node
end
end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author for the given node id.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".node_author")
create l_parameters.make (1)
l_parameters.put (a_id, "nid")
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (Select_user_author, l_parameters)
if sql_rows_count >= 1 then
Result := fetch_author
@@ -156,6 +200,33 @@ feature -- Access
end
end
last_inserted_node_revision (a_node: detachable CMS_NODE): INTEGER_64
-- Last insert revision for node of id `nid'.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".last_inserted_node_revision")
if a_node /= Void and then a_node.has_id then
create l_parameters.make (1)
l_parameters.force (a_node.id, "nid")
sql_query (Sql_last_insert_node_revision_for_nid, l_parameters)
if sql_rows_count = 1 then
if sql_item (1) /= Void then
Result := sql_read_integer_64 (1)
end
end
end
-- if Result = 0 and not has_error then --| include the case a_node = Void
-- sql_query (Sql_last_insert_node_revision, Void)
-- if sql_rows_count = 1 then
-- if sql_item (1) /= Void then
-- Result := sql_read_integer_64 (1)
-- end
-- end
-- end
end
feature -- Change: Node
new_node (a_node: CMS_NODE)
@@ -174,20 +245,17 @@ feature -- Change: Node
-- Remove node by id `a_id'.
local
l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME
do
create l_time.make_now_utc
write_information_log (generator + ".delete_node {" + a_id.out + "}")
error_handler.reset
create l_parameters.make (1)
l_parameters.put (l_time, "changed")
create l_parameters.make (3)
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
l_parameters.put ({CMS_NODE_API}.trashed, "status")
l_parameters.put (a_id, "nid")
sql_change (sql_delete_node, l_parameters)
end
trash_node_by_id (a_id: INTEGER_64)
-- <Precursor>
local
@@ -225,7 +293,9 @@ feature {NONE} -- Implementation
store_node (a_node: CMS_NODE)
local
l_copy_parameters: STRING_TABLE [detachable ANY]
l_parameters: STRING_TABLE [detachable ANY]
l_rev: like last_inserted_node_revision
now: DATE_TIME
do
create now.make_now_utc
@@ -248,27 +318,46 @@ feature {NONE} -- Implementation
l_parameters.put (0, "author")
end
sql_begin_transaction
if a_node.has_id then
l_rev := a_node.revision.max (last_inserted_node_revision (a_node)) + 1 --| starts at (nid, 1)
else
l_rev := last_inserted_node_revision (a_node) + 1 --| starts at (nid, 1)
end
if a_node.has_id then
-- Copy existing node data to node_revisions table.
create l_copy_parameters.make (2)
l_copy_parameters.force (a_node.id, "nid")
-- l_copy_parameters.force (l_rev - 1, "revision")
sql_change (sql_copy_node_to_revision, l_copy_parameters)
if not has_error then
a_node.set_revision (l_rev)
-- Update
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_change (sql_update_node, l_parameters)
if not error_handler.has_error then
-- FIXED: FOR NOW no revision
-- a_node.set_revision (a_node.revision + 1) -- FIXME: Should be incremented by one, in same transaction...but check!
a_node.set_modification_date (now)
end
end
else
-- Store new node
l_parameters.put (a_node.creation_date, "created")
l_parameters.put (l_rev, "revision")
sql_change (sql_insert_node, l_parameters)
if not error_handler.has_error then
a_node.set_modification_date (now)
a_node.set_id (last_inserted_node_id)
a_node.set_revision (1) -- New object.
a_node.set_revision (l_rev) -- New object.
-- check a_node.revision = last_inserted_node_revision (a_node) end
end
end
if not error_handler.has_error then
extended_store (a_node)
extended_store (a_node) -- Note, `a_node.revision' is updated.
end
if error_handler.has_error then
sql_rollback_transaction
@@ -298,6 +387,9 @@ feature {NONE} -- Queries
-- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_node_revisions: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision < :revision ORDER BY node_revisions.revision DESC;"
-- SQL query to get node revisions (missing the latest one).
sql_select_trash_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE status = -1 ;"
-- SQL Query to retrieve all trahsed nodes.
--| note: {CMS_NODE_API}.trashed = -1
@@ -308,15 +400,15 @@ feature {NONE} -- Queries
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_node_by_id_and_revision: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision = :revision ORDER BY node_revisions.revision DESC;"
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 ;"
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);"
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (:revision, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);"
-- SQL Insert to add a new node.
sql_update_node : STRING = "UPDATE nodes SET revision = revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, status=:status, author=:author WHERE nid=:nid;"
-- FIXME: for now no revision inc.!
-- sql_update_node : STRING = "UPDATE nodes SET revision = revision + 1, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, revision = revision + 1, author=:author WHERE nid=:nid;"
-- SQL node.
sql_update_node : STRING = "UPDATE nodes SET revision=:revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, status=:status, author=:author WHERE nid=:nid;"
-- SQL update node.
sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata.
@@ -327,24 +419,18 @@ feature {NONE} -- Queries
sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Restore 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;"
-- -- SQL update node title.
-- sql_update_node_summary: STRING ="UPDATE nodes SET summary=:summary, changed=:changed, revision = revision + 1 WHERE nid=:nid;"
-- -- SQL update node summary.
-- sql_update_node_content: STRING ="UPDATE nodes SET content=:content, changed=:changed, revision = revision + 1 WHERE nid=:nid;"
-- -- SQL node content.
sql_last_insert_node_id: STRING = "SELECT MAX(nid) FROM nodes;"
sql_copy_node_to_revision: STRING = "INSERT INTO node_revisions (nid, revision, title, summary, content, format, author, changed, status) SELECT nid, revision, title, summary, content, format, author, changed, status FROM nodes WHERE nid=:nid;"
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;"
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;"
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;"
Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;"
-- Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;"
feature {NONE} -- Implementation

View File

@@ -11,6 +11,7 @@ inherit
CMS_PROXY_STORAGE_SQL
rename
make as make_proxy,
sql_storage as node_storage
redefine
node_storage
@@ -21,6 +22,12 @@ create
feature {NONE} -- Initialization
make (a_node_api: CMS_NODE_API; a_sql_storage: CMS_NODE_STORAGE_SQL)
do
set_node_api (a_node_api)
make_proxy (a_sql_storage)
end
node_storage: CMS_NODE_STORAGE_SQL
-- <Precursor>
@@ -38,62 +45,86 @@ feature -- Persistence
l_parameters: STRING_TABLE [ANY]
l_new_parent_id, l_previous_parent_id: INTEGER_64
l_update: BOOLEAN
l_has_modif: BOOLEAN
do
error_handler.reset
write_information_log (generator + ".store_page_data")
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_page_data, l_parameters)
if not has_error then
if sql_rows_count = 1 then
l_previous_parent_id := sql_read_integer_64 (3)
l_update := True
if attached api as l_api then
l_api.logger.put_information (generator + ".store", Void)
end
error_handler.reset
-- Check existing record, if any.
if attached node_data (a_node) as d then
l_update := True
l_previous_parent_id := d.parent_id
end
if not has_error then
if attached a_node.parent as l_parent then
l_new_parent_id := l_parent.id
else
l_new_parent_id := 0
end
l_parameters.put (l_new_parent_id, "parent")
if l_update and l_new_parent_id /= l_previous_parent_id then
sql_change (sql_update_page_data, l_parameters)
elseif l_new_parent_id > 0 then
sql_change (sql_insert_page_data, l_parameters)
l_has_modif := l_has_modif or (l_new_parent_id /= l_previous_parent_id)
create l_parameters.make (3)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
l_parameters.force (l_new_parent_id, "parent")
if l_update then
if l_has_modif then
sql_change (sql_update_node_data, l_parameters)
end
else
if l_has_modif then
sql_change (sql_insert_node_data, l_parameters)
else
-- no page data, means everything is empty.
-- FOR NOW: always record row
sql_change (sql_insert_node_data, l_parameters)
end
end
end
end
load (a_node: CMS_PAGE)
local
l_parameters: STRING_TABLE [ANY]
n: INTEGER
ct: CMS_PAGE_NODE_TYPE
l_parent_id: INTEGER_64
do
error_handler.reset
write_information_log (generator + ".fill_page")
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_page_data, l_parameters)
if not has_error then
n := sql_rows_count
if n = 1 then
-- nid, revision, parent
if attached node_data (a_node) as d then
l_parent_id := d.parent_id
if
attached sql_read_integer_64 (3) as l_parent_id and then
l_parent_id > 0 and then
l_parent_id /= a_node.id and then
attached node_storage.node_by_id (l_parent_id) as l_parent
then
-- FIXME: find a simple way to access the declared content types.
if attached {CMS_PAGE_NODE_TYPE} node_api.content_type (l_parent.content_type) as l_parent_ct then
ct := l_parent_ct
else
create ct
end
a_node.set_parent (ct.new_node (l_parent))
else
write_debug_log ("Invalid parent node id!")
end
end
end
node_data (a_node: CMS_NODE): detachable TUPLE [parent_id: INTEGER_64]
local
l_parameters: STRING_TABLE [ANY]
n: INTEGER
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_node_data, l_parameters)
if not has_error then
n := sql_rows_count
if n = 1 then
-- nid, revision, parent
Result := [sql_read_integer_64 (3)]
else
check unique_data: n = 0 end
end
@@ -102,9 +133,8 @@ feature -- Persistence
feature -- SQL
sql_select_page_data: STRING = "SELECT nid, revision, parent FROM page_nodes WHERE nid =:nid AND revision=:revision;"
sql_insert_page_data: STRING = "INSERT INTO page_nodes (nid, revision, parent) VALUES (:nid, :revision, :parent);"
sql_update_page_data: STRING = "UPDATE page_nodes SET nid=:nid, revision=:revision, parent=:parent WHERE nid=:nid AND revision=:revision;"
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;"
end

View File

@@ -890,6 +890,10 @@ feature -- Generation
add_to_primary_menu (lnk)
invoke_menu_system_alter (menu_system)
if api.enabled_modules.count = 0 then
add_to_primary_menu (create {CMS_LOCAL_LINK}.make ("Install", "admin/install"))
end
-- Blocks
create l_menu_list_prepared.make (0)
get_blocks