Compare commits

...

4 Commits

Author SHA1 Message Date
3fa29340b2 Updated code to follow review comments. 2015-05-12 20:01:14 +02:00
jvelilla
b1988d5fe7 Updated CMS_NODE_API, with status, not_published, published and trashed.
Removed class cms_node_constants.
Updated Form response to use permission scopes.
Updated sqlquery to retrieve user author.
Added logger info in cms_response
2015-05-11 23:51:25 -03:00
jvelilla
e767e1bc47 Updated table node to use status (1:not_published, 2:published, 3:trash )instead of deleted_at to implement soft deletes.
Updated queries to use the new status field.
Updated CMS_NODE with a new status attribute.
2015-05-11 16:38:51 -03:00
jvelilla
c2d0fbf445 Updated table nodes to support soft deletes using the new field
'deleted_at' as Datetime and give us free metadata.
Updated Sqlite builder to test different scenarios for users and roles.
Updated NODE_FORM_RESPONSE.edit_form feature to add a delete operation iff
there is a node ie node id >0 and the current user has delete permission on it.
Updated NODE_HANDLER.do_post to handle the operation "DELETE".
Updated queries to retrieve nodes filter by no logical deleted rows (ie. deleted_at is NULL).
Updated CMS_USER_API.has_permissions. (authenticated_user_role seems to generic).
2015-05-08 18:40:46 -03:00
8 changed files with 168 additions and 62 deletions

View File

@@ -11,7 +11,8 @@ CREATE TABLE "nodes"(
"author" INTEGER, "author" INTEGER,
"publish" DATETIME, "publish" DATETIME,
"created" DATETIME NOT NULL, "created" DATETIME NOT NULL,
"changed" DATETIME NOT NULL "changed" DATETIME NOT NULL,
"status" INTEGER
); );
CREATE TABLE page_nodes( CREATE TABLE page_nodes(

View File

@@ -47,30 +47,62 @@ feature -- Factory
initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL) initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL)
local local
u: CMS_USER u: CMS_USER
r: CMS_USER_ROLE l_anonymous_role, l_authenticated_role, r: CMS_USER_ROLE
l_roles: LIST [CMS_USER_ROLE]
do do
-- Schema --| Schema
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql")) a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql"))
-- Data --| Roles
-- Users create l_anonymous_role.make ("anonymous")
a_storage.save_user_role (l_anonymous_role)
create l_authenticated_role.make ("authenticated")
a_storage.save_user_role (l_authenticated_role)
--| Users
create u.make ("admin") create u.make ("admin")
u.set_password ("istrator#") u.set_password ("istrator#")
u.set_email (a_setup.site_email) u.set_email (a_setup.site_email)
a_storage.new_user (u) a_storage.new_user (u)
-- Roles --| Node
create r.make ("anonymous") -- FIXME: move that initialization to node module
a_storage.save_user_role (r) l_anonymous_role.add_permission ("view any page")
create r.make ("authenticated") a_storage.save_user_role (l_anonymous_role)
r.add_permission ("create page")
r.add_permission ("edit page") l_authenticated_role.add_permission ("create page")
l_authenticated_role.add_permission ("view any page")
l_authenticated_role.add_permission ("edit own page")
l_authenticated_role.add_permission ("delete own page")
a_storage.save_user_role (l_authenticated_role)
--| For testing purpose, to be removed later.
-- Roles, view role for testing.
create r.make ("view")
r.add_permission ("view page")
a_storage.save_user_role (r) a_storage.save_user_role (r)
-- Test custom value create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1)
l_roles.force (r)
a_storage.set_custom_value ("abc", "123", "test") create u.make ("auth")
a_storage.set_custom_value ("abc", "OK", "test") u.set_password ("enticated#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
create u.make ("test")
u.set_password ("test#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
create u.make ("view")
u.set_password ("only#")
u.set_email (a_setup.site_email)
u.set_roles (l_roles)
a_storage.new_user (u)
end end
end end

View File

@@ -249,6 +249,29 @@ feature -- Access: Node
end 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
Result := u.same_as (l_author)
end
end
feature -- Permission Scope: Node
permission_scope (u: detachable CMS_USER; a_node: CMS_NODE): STRING
-- Result 'own' if the user `u' is the owner of the node `a_node', in other case
-- `any'.
do
-- FIXME: check if this is ok, since a role may have "any" permission enabled, and "own" disabled,
-- in this case, we should check both permissions
-- obviously such case should be rare, and look like bad configured permissions, but this may occurs.
Result := "any"
if u /= Void and then is_author_of_node (u, a_node) then
Result := "own"
end
end
feature -- Change: Node feature -- Change: Node
save_node (a_node: CMS_NODE) save_node (a_node: CMS_NODE)
@@ -279,32 +302,16 @@ feature -- Change: Node
node_storage.update_node (a_node) node_storage.update_node (a_node)
end 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, with user identified by `a_id', with node id `a_node_id' and a new title `a_title'.
-- do
-- debug ("refactor_fixme")
-- fixme ("Check preconditions")
-- end
-- node_storage.update_node_title (a_user_id, a_node_id, a_title)
-- end
-- update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) feature -- Node status
-- -- Update node summary, with user identified by `a_user_id', with node id `a_node_id' and a new summary `a_summary'.
-- do
-- debug ("refactor_fixme")
-- fixme ("Check preconditions")
-- end
-- node_storage.update_node_summary (a_user_id, a_node_id, a_summary)
-- end
-- update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) Not_published: INTEGER = 0
-- -- Update node content, with user identified by `a_user_id', with node id `a_node_id' and a new content `a_content'. -- The node is not published.
-- do
-- debug ("refactor_fixme")
-- fixme ("Check preconditions")
-- end
-- node_storage.update_node_content (a_user_id, a_node_id, a_content)
-- end
Published: INTEGER = 1
-- The node is published.
Trashed: INTEGER = -1
-- The node is trashed (soft delete), ready to be deleted/destroyed from storage.
end end

View File

@@ -13,10 +13,6 @@ inherit
REFACTORING_HELPER REFACTORING_HELPER
--create
-- make,
-- make_empty
feature{NONE} -- Initialization feature{NONE} -- Initialization
make_empty make_empty
@@ -35,10 +31,7 @@ feature{NONE} -- Initialization
set_creation_date (l_time) set_creation_date (l_time)
set_modification_date (l_time) set_modification_date (l_time)
set_publication_date (l_time) set_publication_date (l_time)
mark_not_published
debug ("refactor_fixme")
fixme ("Remove default harcoded format")
end
ensure ensure
title_set: title = a_title title_set: title = a_title
end end
@@ -60,6 +53,7 @@ feature -- Conversion
a_node.summary, a_node.summary,
a_node.format a_node.format
) )
set_status (a_node.status)
end end
feature -- Access feature -- Access
@@ -78,6 +72,12 @@ feature -- Access
deferred deferred
end end
status: INTEGER
-- Associated status for the current node.
-- default: {CMS_NODE_API}.Not_Published}
-- {CMS_NODE_API}.Published
-- {CMS_NODE_API}.Trashed
feature -- Access feature -- Access
title: READABLE_STRING_32 title: READABLE_STRING_32
@@ -211,6 +211,41 @@ feature -- Element change
auther_set: author = u auther_set: author = u
end end
mark_not_published
-- Set status to not_published.
do
set_status ({CMS_NODE_API}.not_published)
ensure
status_not_published: status = {CMS_NODE_API}.not_published
end
mark_published
-- Set status to published.
do
set_status ({CMS_NODE_API}.published)
ensure
status_published: status = {CMS_NODE_API}.published
end
mark_trashed
-- Set status to published
do
set_status ({CMS_NODE_API}.trashed)
ensure
status_trash: status = {CMS_NODE_API}.trashed
end
feature {CMS_NODE_STORAGE_I} -- Access: status change.
set_status (a_status: like status)
-- Assign `status' with `a_status'.
do
status := a_status
ensure
status_set: status = a_status
end
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -48,7 +48,7 @@ feature -- Execution
attached node_api.node (nid) as l_node attached node_api.node (nid) as l_node
then then
if attached node_api.node_type_for (l_node) as l_type then if attached node_api.node_type_for (l_node) as l_type then
if has_permission ("edit " + l_type.name) then if has_permission ("edit " + node_api.permission_scope (current_user (request), l_node) + " " + l_type.name) then
f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type)
if request.is_post_request_method then if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b)) f.validation_actions.extend (agent edit_form_validate (?, b))
@@ -82,7 +82,7 @@ feature -- Execution
attached {WSF_STRING} request.path_parameter ("type") as p_type and then attached {WSF_STRING} request.path_parameter ("type") as p_type and then
attached node_api.node_type (p_type.value) as l_type attached node_api.node_type (p_type.value) as l_type
then then
if has_permission ("create " + l_type.name) then if has_permission ("create " + l_type.name) then
if attached l_type.new_node (Void) as l_node then if attached l_type.new_node (Void) as l_node then
f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type)
if request.is_post_request_method then if request.is_post_request_method then
@@ -228,6 +228,15 @@ feature -- Form
ts.set_default_value ("Preview") ts.set_default_value ("Preview")
f.extend (ts) f.extend (ts)
if a_node /= Void and then a_node.id > 0 and then has_permission ("delete " + a_name) then
create ts.make ("op")
ts.set_default_value ("Delete")
fixme ("[
ts.set_default_value (i18n ("Delete"))i18n or other name such as "translated" or "translation
]")
f.extend (ts)
end
Result := f Result := f
end end

View File

@@ -114,9 +114,17 @@ feature -- HTTP Methods
local local
edit_response: NODE_FORM_RESPONSE edit_response: NODE_FORM_RESPONSE
do do
fixme ("Refactor code: extract methods: edit_node and add_node")
if req.path_info.ends_with_general ("/edit") then if req.path_info.ends_with_general ("/edit") then
create edit_response.make (req, res, api, node_api) if
edit_response.execute attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Delete")
then
do_delete (req, res)
else
create edit_response.make (req, res, api, node_api)
edit_response.execute
end
elseif req.path_info.starts_with_general ("/node/add/") then elseif req.path_info.starts_with_general ("/node/add/") then
create edit_response.make (req, res, api, node_api) create edit_response.make (req, res, api, node_api)
edit_response.execute edit_response.execute
@@ -142,11 +150,12 @@ feature -- HTTP Methods
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if api.user_has_permission (l_user, "delete " + l_node.content_type) then if api.user_has_permission (l_user, "delete " + node_api.permission_scope (l_user, l_node) + " " + l_node.content_type) then
node_api.delete_node (l_node) node_api.delete_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
send_access_denied (req, res) send_access_denied (req, res)
-- send_not_authorized ?
end end
else else
do_error (req, res, l_id) do_error (req, res, l_id)

View File

@@ -108,8 +108,8 @@ feature -- Access
error_handler.reset error_handler.reset
write_information_log (generator + ".node_author") write_information_log (generator + ".node_author")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_id, "node_id") l_parameters.put (a_id, "nid")
sql_query (select_node_author, l_parameters) sql_query (Select_user_author, l_parameters)
if sql_rows_count >= 1 then if sql_rows_count >= 1 then
Result := fetch_author Result := fetch_author
end end
@@ -144,11 +144,15 @@ feature -- Change: Node
-- Remove node by id `a_id'. -- Remove node by id `a_id'.
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME
do do
create l_time.make_now_utc
write_information_log (generator + ".delete_node") write_information_log (generator + ".delete_node")
error_handler.reset error_handler.reset
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (l_time, "changed")
l_parameters.put ({CMS_NODE_API}.trashed, "status")
l_parameters.put (a_id, "nid") l_parameters.put (a_id, "nid")
sql_change (sql_delete_node, l_parameters) sql_change (sql_delete_node, l_parameters)
end end
@@ -209,7 +213,7 @@ feature {NONE} -- Implementation
error_handler.reset error_handler.reset
write_information_log (generator + ".store_node") write_information_log (generator + ".store_node")
create l_parameters.make (8) create l_parameters.make (9)
l_parameters.put (a_node.content_type, "type") l_parameters.put (a_node.content_type, "type")
l_parameters.put (a_node.title, "title") l_parameters.put (a_node.title, "title")
l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.summary, "summary")
@@ -217,6 +221,7 @@ feature {NONE} -- Implementation
l_parameters.put (a_node.format, "format") l_parameters.put (a_node.format, "format")
l_parameters.put (a_node.publication_date, "publish") l_parameters.put (a_node.publication_date, "publish")
l_parameters.put (now, "changed") l_parameters.put (now, "changed")
l_parameters.put (a_node.status, "status")
if attached a_node.author as l_author then if attached a_node.author as l_author then
check valid_author: l_author.has_id end check valid_author: l_author.has_id end
l_parameters.put (l_author.id, "author") l_parameters.put (l_author.id, "author")
@@ -260,24 +265,28 @@ feature -- Helpers
feature {NONE} -- Queries feature {NONE} -- Queries
sql_select_nodes_count: STRING = "SELECT count(*) from Nodes;" sql_select_nodes_count: STRING = "SELECT count(*) FROM Nodes WHERE status != -1 ;"
-- Nodes count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_nodes: STRING = "SELECT * from Nodes;" sql_select_nodes: STRING = "SELECT * FROM Nodes WHERE status != -1 ;"
-- SQL Query to retrieve all nodes. -- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM Nodes WHERE nid =:nid ORDER BY revision desc, publish desc LIMIT 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 FROM Nodes ORDER BY nid desc, publish desc LIMIT :rows OFFSET :offset ;" 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, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :author);" 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 to add a new node. -- 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, author=:author WHERE nid=:nid;" 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.! -- 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_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 node.
sql_delete_node: STRING = "DELETE FROM nodes WHERE nid=:nid;" sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata.
-- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;" -- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;"
@@ -294,7 +303,7 @@ feature {NONE} -- Queries
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM Nodes INNER JOIN users ON nodes.author=users.uid AND users.uid = :uid;" 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_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;"
@@ -335,6 +344,9 @@ feature {NONE} -- Implementation
if attached sql_read_date_time (11) as l_modif_date then if attached sql_read_date_time (11) as l_modif_date then
Result.set_modification_date (l_modif_date) Result.set_modification_date (l_modif_date)
end end
if attached sql_read_integer_32 (12) as l_status then
Result.set_status (l_status)
end
end end
end end

View File

@@ -182,6 +182,7 @@ feature -- Permission
has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN
-- Does current user has permission `a_permission' ? -- Does current user has permission `a_permission' ?
do do
api.logger.put_information (generator + ".has_permission", a_permission)
Result := user_has_permission (current_user (request), a_permission) Result := user_has_permission (current_user (request), a_permission)
end end