diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index c0b2da1..b0b7ae1 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -9,13 +9,13 @@ + + - - @@ -29,6 +29,7 @@ + diff --git a/library/persistence/implementation/common/cms_storage_store_sql.e b/library/persistence/implementation/common/cms_storage_store_sql.e new file mode 100644 index 0000000..0b8c32c --- /dev/null +++ b/library/persistence/implementation/common/cms_storage_store_sql.e @@ -0,0 +1,106 @@ +note + description: "Summary description for {CMS_STORAGE_STORE_SQL}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_STORAGE_STORE_SQL + +inherit + CMS_STORAGE + + CMS_STORAGE_SQL + +feature {NONE} -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- + require + is_connected: a_connection.is_connected + do + connection := a_connection + log.write_information (generator + ".make - is database connected? "+ a_connection.is_connected.out ) + + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + + create error_handler.make +-- error_handler.add_synchronization (db_handler.database_error_handler) + end + +feature {NONE} -- Implementation + + db_handler: DATABASE_HANDLER + + connection: DATABASE_CONNECTION + -- Current database connection. + +feature -- Query + + sql_post_execution + -- Post database execution. + do + error_handler.append (db_handler.database_error_handler) + if error_handler.has_error then + log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) + end + end + + sql_begin_transaction + do + connection.begin_transaction + end + + sql_commit_transaction + do + connection.commit + end + + sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) + do + check_sql_query_validity (a_sql_statement, a_params) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) + db_handler.execute_query + end + + sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) + do + check_sql_query_validity (a_sql_statement, a_params) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) + db_handler.execute_change + end + + sql_rows_count: INTEGER + -- Number of rows for last sql execution. + do + Result := db_handler.count + end + + sql_start + -- Set the cursor on first element. + do + db_handler.start + end + + sql_after: BOOLEAN + -- Are there no more items to iterate over? + do + Result := db_handler.after + end + + sql_forth + -- Fetch next row from last sql execution, if any. + do + db_handler.forth + end + + sql_item (a_index: INTEGER): detachable ANY + do + if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then + Result := l_item.item (a_index) + else + check has_item_at_index: False end + end + end + +end diff --git a/library/persistence/implementation/mysql/src/cms_storage_mysql.e b/library/persistence/implementation/mysql/src/cms_storage_mysql.e index feaafae..2bfd7ed 100644 --- a/library/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/library/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -7,8 +7,9 @@ class CMS_STORAGE_MYSQL inherit - CMS_STORAGE + + CMS_STORAGE_STORE_SQL CMS_USER_STORAGE_MYSQL @@ -19,85 +20,93 @@ inherit create make -feature {NONE} -- Initialization +--feature {NONE} -- Initialization - make (a_connection: DATABASE_CONNECTION) - -- - require - is_connected: a_connection.is_connected - do - connection := a_connection - log.write_information (generator + ".make - is database connected? "+ a_connection.is_connected.out ) +-- make (a_connection: DATABASE_CONNECTION) +-- -- +-- require +-- is_connected: a_connection.is_connected +-- do +-- connection := a_connection +-- log.write_information (generator + ".make - is database connected? "+ a_connection.is_connected.out ) - create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) +-- create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) - create error_handler.make --- error_handler.add_synchronization (db_handler.database_error_handler) - end +-- create error_handler.make +---- error_handler.add_synchronization (db_handler.database_error_handler) +-- end - db_handler: DATABASE_HANDLER +-- db_handler: DATABASE_HANDLER - connection: DATABASE_CONNECTION - -- Current database connection. +-- connection: DATABASE_CONNECTION +-- -- Current database connection. -feature -- Query +--feature -- Query - sql_post_execution - -- Post database execution. - do - error_handler.append (db_handler.database_error_handler) - if error_handler.has_error then - log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) - end - end +-- sql_post_execution +-- -- Post database execution. +-- do +-- error_handler.append (db_handler.database_error_handler) +-- if error_handler.has_error then +-- log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) +-- end +-- end - sql_begin_transaction - do - connection.begin_transaction - end +-- sql_begin_transaction +-- do +-- connection.begin_transaction +-- end - sql_commit_transaction - do - connection.commit - end +-- sql_commit_transaction +-- do +-- connection.commit +-- end - sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) - do - db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) - db_handler.execute_query - end +-- sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) +-- do +-- check_sql_query_validity (a_sql_statement, a_params) +-- db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) +-- db_handler.execute_query +-- end - sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) - do - db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) - db_handler.execute_change - end +-- sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) +-- do +-- check_sql_query_validity (a_sql_statement, a_params) +-- db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) +-- db_handler.execute_change +-- end - sql_rows_count: INTEGER - -- Number of rows for last sql execution. - do - Result := db_handler.count - end +-- sql_rows_count: INTEGER +-- -- Number of rows for last sql execution. +-- do +-- Result := db_handler.count +-- end - sql_after: BOOLEAN - -- Are there no more items to iterate over? - do - Result := db_handler.after - end +-- sql_start +-- -- Set the cursor on first element. +-- do +-- db_handler.start +-- end - sql_forth - -- Fetch next row from last sql execution, if any. - do - db_handler.forth - end +-- sql_after: BOOLEAN +-- -- Are there no more items to iterate over? +-- do +-- Result := db_handler.after +-- end - sql_item (a_index: INTEGER): detachable ANY - do - if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then - Result := l_item.item (a_index) - else - check has_item_at_index: False end - end - end +-- sql_forth +-- -- Fetch next row from last sql execution, if any. +-- do +-- db_handler.forth +-- end + +-- sql_item (a_index: INTEGER): detachable ANY +-- do +-- if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then +-- Result := l_item.item (a_index) +-- else +-- check has_item_at_index: False end +-- end +-- end end diff --git a/library/persistence/implementation/sqlite/scripts/create_database.sql b/library/persistence/implementation/sqlite/scripts/create_database.sql deleted file mode 100644 index 4b9a157..0000000 --- a/library/persistence/implementation/sqlite/scripts/create_database.sql +++ /dev/null @@ -1,61 +0,0 @@ --- Creator: MySQL Workbench 6.1.7/ExportSQLite plugin 2009.12.02 --- Author: javier --- Caption: New Model --- Project: Name of the project --- Changed: 2014-09-16 23:12 --- Created: 2014-09-16 23:12 -PRAGMA foreign_keys = OFF; - --- Schema: cms_dev -BEGIN; -CREATE TABLE "nodes"( - "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), - "publication_date" DATE NOT NULL, - "creation_date" DATE NOT NULL, - "modification_date" DATE NOT NULL, - "title" VARCHAR(255) NOT NULL, - "summary" TEXT NOT NULL, - "content" MEDIUMTEXT NOT NULL -); -CREATE TABLE "roles"( - "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), - "role" VARCHAR(100) NOT NULL, - CONSTRAINT "role" - UNIQUE("role") -); -CREATE TABLE "users"( - "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), - "username" VARCHAR(100) NOT NULL, - "password" VARCHAR(100) NOT NULL, - "salt" VARCHAR(100) NOT NULL, - "email" VARCHAR(250) NOT NULL, - CONSTRAINT "username" - UNIQUE("username") -); -CREATE TABLE "users_nodes"( - "users_id" INTEGER NOT NULL CHECK("users_id">=0), - "nodes_id" INTEGER NOT NULL CHECK("nodes_id">=0), - PRIMARY KEY("users_id","nodes_id"), - CONSTRAINT "fk_users_has_nodes_nodes1" - FOREIGN KEY("nodes_id") - REFERENCES "nodes"("id"), - CONSTRAINT "fk_users_has_nodes_users" - FOREIGN KEY("users_id") - REFERENCES "users"("id") -); -CREATE INDEX "users_nodes.fk_users_has_nodes_nodes1_idx" ON "users_nodes"("nodes_id"); -CREATE INDEX "users_nodes.fk_users_has_nodes_users_idx" ON "users_nodes"("users_id"); -CREATE TABLE "users_roles"( - "users_id" INTEGER NOT NULL CHECK("users_id">=0), - "roles_id" INTEGER NOT NULL CHECK("roles_id">=0), - PRIMARY KEY("users_id","roles_id"), - CONSTRAINT "fk_users_has_roles_roles1" - FOREIGN KEY("roles_id") - REFERENCES "roles"("id"), - CONSTRAINT "fk_users_has_roles_users1" - FOREIGN KEY("users_id") - REFERENCES "users"("id") -); -CREATE INDEX "users_roles.fk_users_has_roles_roles1_idx" ON "users_roles"("roles_id"); -CREATE INDEX "users_roles.fk_users_has_roles_users1_idx" ON "users_roles"("users_id"); -COMMIT; diff --git a/library/persistence/implementation/sqlite/scripts/schema.sql b/library/persistence/implementation/sqlite/scripts/schema.sql new file mode 100644 index 0000000..0dffacb --- /dev/null +++ b/library/persistence/implementation/sqlite/scripts/schema.sql @@ -0,0 +1,36 @@ +PRAGMA foreign_keys = OFF; + +BEGIN; +CREATE TABLE "users"( + "uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0), + "name" VARCHAR(100) NOT NULL, + "password" VARCHAR(100) NOT NULL, + "salt" VARCHAR(100) NOT NULL, + "email" VARCHAR(250) NOT NULL, + "status" INTEGER, + "created" DATETIME NOT NULL, + "signed" DATETIME, + CONSTRAINT "name" + UNIQUE("name") +); + +CREATE TABLE "users_roles"( + "rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0), + "role" VARCHAR(100) NOT NULL, + CONSTRAINT "role" + UNIQUE("role") +); + +CREATE TABLE "nodes"( + "nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0), + "version" INTEGER, + "type" INTEGER, + "title" VARCHAR(255) NOT NULL, + "summary" TEXT NOT NULL, + "content" MEDIUMTEXT NOT NULL, + "author" INTEGER, + "publish" DATETIME, + "created" DATETIME NOT NULL, + "changed" DATETIME NOT NULL +); +COMMIT; diff --git a/library/persistence/implementation/sqlite/scripts/tables.sql b/library/persistence/implementation/sqlite/scripts/tables.sql deleted file mode 100644 index 4b1a4ba..0000000 --- a/library/persistence/implementation/sqlite/scripts/tables.sql +++ /dev/null @@ -1,14 +0,0 @@ -DROP TABLE IF EXISTS nodes; - -CREATE TABLE nodes -( - id smallint unsigned NOT NULL auto_increment, - publication_date date NOT NULL, #When the article was published - creation_date date NOT NULL, #When the article was created - modification_date date NOT NULL, #When the article was updated - title varchar(255) NOT NULL, #Full title of the article - summary text NOT NULL, #A short summary of the articule - content mediumtext NOT NULL, #The HTML content of the article - - PRIMARY KEY (ID) -); \ No newline at end of file diff --git a/library/persistence/implementation/sqlite/src/cms_node_storage_sqlite.e b/library/persistence/implementation/sqlite/src/cms_node_storage_sqlite.e index 6cafa70..94641a7 100644 --- a/library/persistence/implementation/sqlite/src/cms_node_storage_sqlite.e +++ b/library/persistence/implementation/sqlite/src/cms_node_storage_sqlite.e @@ -9,6 +9,67 @@ deferred class inherit CMS_NODE_STORAGE_SQL + redefine + nodes, recent_nodes + end +feature {NONE} -- Implementation + + db_handler: DATABASE_HANDLER + deferred + end + +feature -- Access + + nodes: LIST [CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + across nodes_iterator as ic loop + Result.force (ic.item) + end + end + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of recent `a_count' nodes with an offset of `lower'. + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (a_count) + across recent_nodes_iterator (a_lower, a_count) as c loop + Result.force (c.item) + end + end + +feature -- Access: iterator + + nodes_iterator: DATABASE_ITERATION_CURSOR [CMS_NODE] + -- List of nodes. + local + l_parameters: STRING_TABLE [ANY] + do + error_handler.reset + log.write_information (generator + ".nodes_iterator") + create l_parameters.make (0) + sql_query (select_nodes, l_parameters) + sql_post_execution + create Result.make (db_handler, agent fetch_node) + end + + recent_nodes_iterator (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [CMS_NODE] + -- The most recent `a_rows'. + local + l_parameters: STRING_TABLE [ANY] + l_query: STRING + do + -- FIXME: check implementation... + error_handler.reset + log.write_information (generator + ".recent_nodes_iterator") + create l_parameters.make (2) + l_parameters.put (a_rows, "rows") + l_parameters.put (a_lower, "offset") + create l_query.make_from_string (select_recent_nodes) + sql_query (l_query, l_parameters) + create Result.make (db_handler, agent fetch_node) + sql_post_execution + end end diff --git a/library/persistence/implementation/sqlite/src/cms_storage_sqlite.e b/library/persistence/implementation/sqlite/src/cms_storage_sqlite.e index 6fccade..5ab9659 100644 --- a/library/persistence/implementation/sqlite/src/cms_storage_sqlite.e +++ b/library/persistence/implementation/sqlite/src/cms_storage_sqlite.e @@ -8,6 +8,8 @@ class inherit CMS_STORAGE + + CMS_STORAGE_STORE_SQL CMS_USER_STORAGE_SQLITE @@ -18,85 +20,93 @@ inherit create make -feature {NONE} -- Initialization +--feature {NONE} -- Initialization - make (a_connection: DATABASE_CONNECTION) - -- - require - is_connected: a_connection.is_connected - do - connection := a_connection - log.write_information (generator + ".make_with_database is database connected? "+ a_connection.is_connected.out ) +-- make (a_connection: DATABASE_CONNECTION) +-- -- +-- require +-- is_connected: a_connection.is_connected +-- do +-- connection := a_connection +-- log.write_information (generator + ".make_with_database is database connected? "+ a_connection.is_connected.out ) - create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) +-- create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) - create error_handler.make --- error_handler.add_synchronization (db_handler.database_error_handler) - end +-- create error_handler.make +---- error_handler.add_synchronization (db_handler.database_error_handler) +-- end - db_handler: DATABASE_HANDLER +-- db_handler: DATABASE_HANDLER - connection: DATABASE_CONNECTION - -- Current database connection. +-- connection: DATABASE_CONNECTION +-- -- Current database connection. -feature -- Access: user +--feature -- Access: user - sql_post_execution - -- Post database execution. - do - error_handler.append (db_handler.database_error_handler) - if error_handler.has_error then - log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) - end - end +-- sql_post_execution +-- -- Post database execution. +-- do +-- error_handler.append (db_handler.database_error_handler) +-- if error_handler.has_error then +-- log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) +-- end +-- end - sql_begin_transaction - do - connection.begin_transaction - end +-- sql_begin_transaction +-- do +-- connection.begin_transaction +-- end - sql_commit_transaction - do - connection.commit - end +-- sql_commit_transaction +-- do +-- connection.commit +-- end - sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) - do - db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) - db_handler.execute_query - end +-- sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) +-- do +-- check_sql_query_validity (a_sql_statement, a_params) +-- db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) +-- db_handler.execute_query +-- end - sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) - do - db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) - db_handler.execute_change - end +-- sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) +-- do +-- check_sql_query_validity (a_sql_statement, a_params) +-- db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params)) +-- db_handler.execute_change +-- end - sql_rows_count: INTEGER - -- Number of rows for last sql execution. - do - Result := db_handler.count - end +-- sql_rows_count: INTEGER +-- -- Number of rows for last sql execution. +-- do +-- Result := db_handler.count +-- end - sql_after: BOOLEAN - -- Are there no more items to iterate over? - do - Result := db_handler.after - end +-- sql_start +-- -- Set the cursor on first element. +-- do +-- db_handler.start +-- end - sql_forth - -- Fetch next row from last sql execution, if any. - do - db_handler.forth - end +-- sql_after: BOOLEAN +-- -- Are there no more items to iterate over? +-- do +-- Result := db_handler.after +-- end - sql_item (a_index: INTEGER): detachable ANY - do - if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then - Result := l_item.item (a_index) - else - check has_item_at_index: False end - end - end +-- sql_forth +-- -- Fetch next row from last sql execution, if any. +-- do +-- db_handler.forth +-- end + +-- sql_item (a_index: INTEGER): detachable ANY +-- do +-- if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then +-- Result := l_item.item (a_index) +-- else +-- check has_item_at_index: False end +-- end +-- end end diff --git a/library/src/modules/node/node_module.e b/library/src/modules/node/node_module.e index a1b3c4c..fff007d 100644 --- a/library/src/modules/node/node_module.e +++ b/library/src/modules/node/node_module.e @@ -162,21 +162,23 @@ feature -- Handler l_user: CMS_USER l_node: CMS_NODE do - create {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE} r.make (req, res, a_api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_api) if attached a_api.user_by_name ("foo") as u then l_user := u else create l_user.make ("foo") l_user.set_password ("foobar#") - l_user.set_email ("jfiat@eiffel.com") + l_user.set_email ("test@example.com") a_api.new_user (l_user) end - create l_node.make ({STRING_32} "This is a content", {STRING_32} "And a summary", {STRING_32} "Nice title") - l_node.set_author (l_user) - a_api.new_node (l_node) + if a_api.nodes_count = 0 then + create l_node.make ({STRING_32} "This is a content", {STRING_32} "And a summary", {STRING_32} "Nice title") + l_node.set_author (l_user) + a_api.new_node (l_node) + end - create s.make_from_string ("

Sorry: listing the CMS nodes is not yet implemented.

") + create s.make_from_string ("

Nodes:

") if attached a_api.nodes as lst then across lst as ic @@ -191,7 +193,7 @@ feature -- Handler end r.set_main_content (s) - r.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet implemented
", Void), "highlighted") + r.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented
", Void), "highlighted") r.execute end diff --git a/library/src/persistence/cms_storage_null.e b/library/src/persistence/cms_storage_null.e index 00a1fcd..e54e5ac 100644 --- a/library/src/persistence/cms_storage_null.e +++ b/library/src/persistence/cms_storage_null.e @@ -7,13 +7,13 @@ class CMS_STORAGE_NULL inherit - CMS_STORAGE redefine default_create select default_create end + REFACTORING_HELPER rename default_create as default_create_rh @@ -70,11 +70,16 @@ feature -- User Nodes feature -- Change: user - save_user (a_user: CMS_USER) + new_user (a_user: CMS_USER) -- Add a new user `a_user'. do end + update_user (a_user: CMS_USER) + -- Update user `a_user'. + do + end + feature -- Access: roles and permissions user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE @@ -93,9 +98,13 @@ feature -- Change: roles and permissions do end - feature -- Access: node + nodes_count: INTEGER_64 + -- Count of nodes. + do + end + nodes: LIST[CMS_NODE] -- List of nodes. do @@ -156,11 +165,4 @@ feature -- Node do end -feature -- User - - new_user (a_user: CMS_USER) - -- Add a new user `a_user'. - do - end - end diff --git a/library/src/persistence/cms_storage_sql.e b/library/src/persistence/cms_storage_sql.e index b0266f1..e65b3bd 100644 --- a/library/src/persistence/cms_storage_sql.e +++ b/library/src/persistence/cms_storage_sql.e @@ -30,6 +30,58 @@ feature -- Execution feature -- Operation + check_sql_query_validity (a_sql_statement: READABLE_STRING_8; a_params: detachable STRING_TABLE [detachable ANY]) + local + l_sql_params: STRING_TABLE [READABLE_STRING_8] + i,j,n,m: INTEGER + s: STRING + do + create l_sql_params.make_caseless (0) + from + i := 1 + n := a_sql_statement.count + until + i > n + loop + i := a_sql_statement.index_of (':', i) + if i = 0 then + i := n -- exit + else + from + j := i + 1 + until + j > n or not (a_sql_statement[j].is_alpha_numeric or a_sql_statement[j] = '_') + loop + j := j + 1 + end + s := a_sql_statement.substring (i + 1, j - 1) + l_sql_params.force (s, s) + end + i := i + 1 + end + if a_params = Void then + if not l_sql_params.is_empty then + check False end + error_handler.add_custom_error (-1, "invalid query", "missing value for sql parameters") + end + else + across + a_params as ic + loop + if l_sql_params.has (ic.key) then + l_sql_params.remove (ic.key) + else + error_handler.add_custom_error (-1, "useless value", "value for unexpected parameter [" + ic.key + "]") + end + end + across + l_sql_params as ic + loop + error_handler.add_custom_error (-1, "invalid query", "missing value for sql parameter [" + ic.item + "]") + end + end + end + sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY]) deferred end @@ -45,6 +97,11 @@ feature -- Access deferred end + sql_start + -- Set the cursor on first element. + deferred + end + sql_after: BOOLEAN -- Are there no more items to iterate over? deferred diff --git a/library/src/persistence/node/cms_node_storage.e b/library/src/persistence/node/cms_node_storage.e index 4a42d95..d36733b 100644 --- a/library/src/persistence/node/cms_node_storage.e +++ b/library/src/persistence/node/cms_node_storage.e @@ -19,6 +19,11 @@ feature -- Error Handling feature -- Access + nodes_count: INTEGER_64 + -- Count of nodes. + deferred + end + nodes: LIST [CMS_NODE] -- List of nodes. deferred @@ -49,7 +54,7 @@ feature -- Change: Node -- Save node `a_node'. require no_id: not a_node.has_id - valid_user: attached a_node.author as l_author implies l_author.id > 0 + valid_user: attached a_node.author as l_author and then l_author.id > 0 deferred end diff --git a/library/src/persistence/node/cms_node_storage_sql.e b/library/src/persistence/node/cms_node_storage_sql.e index 229f3a5..7ea177d 100644 --- a/library/src/persistence/node/cms_node_storage_sql.e +++ b/library/src/persistence/node/cms_node_storage_sql.e @@ -16,7 +16,19 @@ inherit SHARED_LOGGER -feature -- Access +feature -- Access + + nodes_count: INTEGER_64 + -- Number of items nodes. + do + error_handler.reset + log.write_information (generator + ".nodes_count") + sql_query (select_nodes_count, Void) + if sql_rows_count = 1 then + Result := sql_read_integer_64 (1) + end + sql_post_execution + end nodes: LIST [CMS_NODE] -- List of nodes. @@ -29,6 +41,7 @@ feature -- Access from sql_query (select_nodes, Void) sql_post_execution + sql_start until sql_after loop @@ -56,6 +69,7 @@ feature -- Access l_parameters.put (a_lower, "offset") sql_query (select_recent_nodes, l_parameters) sql_post_execution + sql_start until sql_after loop @@ -99,18 +113,6 @@ feature -- Access sql_post_execution end - nodes_count: INTEGER - -- Number of items nodes. - do - error_handler.reset - log.write_information (generator + ".nodes_count") - sql_query (select_nodes_count, Void) - if sql_rows_count = 1 then - Result := sql_read_integer_32 (1) - end - sql_post_execution - end - last_inserted_node_id: INTEGER_64 -- Last insert node id. do @@ -137,16 +139,16 @@ feature -- Change: Node l_parameters.put (a_node.title, "title") l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.content, "content") - l_parameters.put (a_node.publication_date, "publication_date") - l_parameters.put (a_node.creation_date, "creation_date") - l_parameters.put (a_node.modification_date, "modification_date") + l_parameters.put (a_node.publication_date, "publish") + l_parameters.put (a_node.creation_date, "created") + l_parameters.put (a_node.modification_date, "changed") if attached a_node.author as l_author and then l_author.id > 0 then - l_parameters.put (l_author.id, "author_id") + l_parameters.put (l_author.id, "author") else - l_parameters.put (0, "author_id") + l_parameters.put (0, "author") end sql_change (sql_insert_node, l_parameters) sql_post_execution @@ -167,10 +169,6 @@ feature -- Change: Node create l_parameters.make (1) l_parameters.put (a_id, "id") sql_change (sql_delete_node, l_parameters) - sql_post_execution - - -- Delete from user nodes. FIXME: what is that ??? - sql_change (sql_delete_from_user_node, l_parameters) sql_post_execution end @@ -178,25 +176,28 @@ feature -- Change: Node -- Update node content `a_node'. local l_parameters: STRING_TABLE [detachable ANY] + now: DATE_TIME do + create now.make_now_utc error_handler.reset log.write_information (generator + ".update_node") create l_parameters.make (7) l_parameters.put (a_node.title, "title") l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.content, "content") - l_parameters.put (a_node.publication_date, "publication_date") - l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_node.publication_date, "publish") + l_parameters.put (now, "changed") l_parameters.put (a_node.id, "id") if attached a_node.author as l_author then - l_parameters.put (l_author.id, "id") - l_parameters.put (l_author.id, "editor") + l_parameters.put (l_author.id, "author") else - l_parameters.put (0, "id") - l_parameters.put (0, "editor") + l_parameters.put (0, "author") end sql_change (sql_update_node, l_parameters) sql_post_execution + if not error_handler.has_error then + a_node.set_modification_date (now) + end end update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) @@ -210,8 +211,8 @@ feature -- Change: Node log.write_information (generator + ".update_node_title") create l_parameters.make (3) l_parameters.put (a_title, "title") - l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") - l_parameters.put (a_node_id, "id") + l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") + l_parameters.put (a_node_id, "nid") sql_change (sql_update_node_title, l_parameters) sql_post_execution end @@ -227,8 +228,8 @@ feature -- Change: Node log.write_information (generator + ".update_node_summary") create l_parameters.make (3) l_parameters.put (a_summary, "summary") - l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") - l_parameters.put (a_node_id, "id") + l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") + l_parameters.put (a_node_id, "nid") sql_change (sql_update_node_summary, l_parameters) sql_post_execution end @@ -244,8 +245,8 @@ feature -- Change: Node log.write_information (generator + ".update_node_content") create l_parameters.make (3) l_parameters.put (a_content, "content") - l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") - l_parameters.put (a_node_id, "id") + l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") + l_parameters.put (a_node_id, "nid") sql_change (sql_update_node_content, l_parameters) sql_post_execution end @@ -257,49 +258,36 @@ feature {NONE} -- Queries Select_nodes: STRING = "select * from Nodes;" -- SQL Query to retrieve all nodes. - Select_node_by_id: STRING = "select * from Nodes where id =:id order by id desc, publication_date desc;" + Select_node_by_id: STRING = "select * from Nodes where nid =:nid order by nid desc, publish desc;" - Select_recent_nodes: STRING = "select * from Nodes order by id desc, publication_date desc LIMIT :rows OFFSET :offset ;" + Select_recent_nodes: STRING = "select * from Nodes order by nid desc, publish desc LIMIT :rows OFFSET :offset ;" - SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date, author_id) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date, :author_id);" + SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publish, created, changed, author) values (:title, :summary, :content, :publish, :created, :changed, :author);" -- SQL Insert to add a new node. - SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date, version = version + 1 where id=:id;" - -- SQL update node title. - - SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, modification_date=:modification_date, version = version + 1 where id=:id;" - -- SQL update node summary. - - SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date, version = version + 1 where id=:id;" - -- SQL node content. - - Slq_update_editor: STRING ="update nodes SET editor_id=:users_id where id=:nodes_id;" - -- SQL node content. - - SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, modification_date=:modification_date, version = version + 1, editor_id=:editor where id=:id;" + SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publish=:publish, changed=:changed, version = version + 1, author=:author where nid=:nid;" -- SQL node. - SQL_Delete_node: STRING = "delete from nodes where id=:id;" + SQL_Delete_node: STRING = "delete from nodes where nid=:nid;" - Sql_update_node_author: STRING = "update nodes SET author_id=:user_id where id=:id;" + Sql_update_node_author: STRING = "update nodes SET author=:author where nid=:nid;" - Sql_last_insert_node_id: STRING = "SELECT MAX(id) from nodes;" + SQL_Update_node_title: STRING ="update nodes SET title=:title, changed=:changed, version = version + 1 where nid=:nid;" + -- SQL update node title. + + SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, changed=:changed, version = version + 1 where nid=:nid;" + -- SQL update node summary. + + SQL_Update_node_content: STRING ="update nodes SET content=:content, changed=:changed, version = version + 1 where nid=:nid;" + -- SQL node content. + + Sql_last_insert_node_id: STRING = "SELECT MAX(nid) from nodes;" feature {NONE} -- Sql Queries: USER_ROLES collaborators, author - Sql_insert_users_nodes: STRING = "insert into users_nodes (users_id, nodes_id) values (:users_id, :nodes_id);" + Select_user_author: STRING = "SELECT * FROM Nodes INNER JOIN users ON nodes.author=users.uid and users.uid = :uid;" - select_node_collaborators: STRING = "SELECT * FROM Users INNER JOIN users_nodes ON users.id=users_nodes.users_id and users_nodes.nodes_id = :node_id;" - - Select_user_author: STRING = "SELECT * FROM Nodes INNER JOIN users ON nodes.author_id=users.id and users.id = :user_id;" - - Select_node_author: STRING = "SELECT * FROM Users INNER JOIN nodes ON nodes.author_id=users.id and nodes.id =:node_id;" - - Select_user_collaborator: STRING = "SELECT * FROM Nodes INNER JOIN users_nodes ON users_nodes.nodes_id = nodes.id and users_nodes.users_id = :user_id;" - - Select_exist_user_node: STRING= "Select Count(*) from Users_nodes where users_id=:user_id and nodes_id=:node_id;" - - sql_delete_from_user_node: STRING = "delete from users_nodes where nodes_id=:id" + Select_node_author: STRING = "SELECT * FROM Users INNER JOIN nodes ON nodes.author=users.uid and nodes.nid =:nid;" feature {NONE} -- Implementation @@ -309,24 +297,27 @@ feature {NONE} -- Implementation if attached sql_read_integer_64 (1) as l_id then Result.set_id (l_id) end - if attached sql_read_date_time (2) as l_publication_date then - Result.set_publication_date (l_publication_date) - end - if attached sql_read_date_time (3) as l_creation_date then - Result.set_creation_date (l_creation_date) - end - if attached sql_read_date_time (4) as l_modif_date then - Result.set_modification_date (l_modif_date) - end - if attached sql_read_string_32 (5) as l_title then + if attached sql_read_string_32 (4) as l_title then Result.set_title (l_title) end - if attached sql_read_string_32 (6) as l_summary then + if attached sql_read_string_32 (5) as l_summary then Result.set_summary (l_summary) end - if attached sql_read_string (7) as l_content then + if attached sql_read_string (6) as l_content then Result.set_content (l_content) end + if attached sql_read_date_time (8) as l_publication_date then + Result.set_publication_date (l_publication_date) + end + if attached sql_read_date_time (9) as l_creation_date then + Result.set_creation_date (l_creation_date) + end + if attached sql_read_date_time (10) as l_modif_date then + Result.set_modification_date (l_modif_date) + end + if attached sql_read_integer_64 (7) as l_author_id then + -- access to API ... + end end fetch_author: detachable CMS_USER diff --git a/library/src/persistence/user/cms_user_storage.e b/library/src/persistence/user/cms_user_storage.e index 6bfc9a7..5d548dd 100644 --- a/library/src/persistence/user/cms_user_storage.e +++ b/library/src/persistence/user/cms_user_storage.e @@ -36,7 +36,7 @@ feature -- Access deferred ensure same_id: Result /= Void implies Result.id = a_id - password: Result /= Void implies Result.password /= Void + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) end user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER @@ -46,7 +46,7 @@ feature -- Access deferred ensure same_name: Result /= Void implies a_name ~ Result.name - password: Result /= Void implies Result.password /= Void + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) end user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER @@ -54,7 +54,7 @@ feature -- Access deferred ensure same_email: Result /= Void implies a_email ~ Result.email - password: Result /= Void implies Result.password /= Void + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) end is_valid_credential (a_u, a_p: READABLE_STRING_32): BOOLEAN @@ -64,8 +64,17 @@ feature -- Access feature -- Change: user - save_user (a_user: CMS_USER) + new_user (a_user: CMS_USER) + -- New user `a_user'. + require + no_id: not a_user.has_id + deferred + end + + update_user (a_user: CMS_USER) -- Save user `a_user'. + require + has_id: a_user.has_id deferred end diff --git a/library/src/persistence/user/cms_user_storage_sql.e b/library/src/persistence/user/cms_user_storage_sql.e index a00d978..e6d20d6 100644 --- a/library/src/persistence/user/cms_user_storage_sql.e +++ b/library/src/persistence/user/cms_user_storage_sql.e @@ -47,6 +47,7 @@ feature -- Access: user from sql_query (select_users, Void) sql_post_execution + sql_start until sql_after loop @@ -66,7 +67,7 @@ feature -- Access: user error_handler.reset log.write_information (generator + ".user") create l_parameters.make (1) - l_parameters.put (a_id, "id") + l_parameters.put (a_id, "uid") sql_query (select_user_by_id, l_parameters) if sql_rows_count = 1 then Result := fetch_user @@ -136,20 +137,81 @@ feature -- Access: user feature -- Change: user - save_user (a_user: CMS_USER) + new_user (a_user: CMS_USER) -- Add a new user `a_user'. + local + l_parameters: STRING_TABLE [detachable ANY] + l_password_salt, l_password_hash: STRING + l_security: SECURITY_PROVIDER do if attached a_user.password as l_password and then attached a_user.email as l_email then sql_begin_transaction - new_user (a_user) + error_handler.reset + create l_security + l_password_salt := l_security.salt + l_password_hash := l_security.password_hash (l_password, l_password_salt) + + log.write_information (generator + ".new_user") + create l_parameters.make (4) + l_parameters.put (a_user.name, "name") + l_parameters.put (l_password_hash, "password") + l_parameters.put (l_password_salt, "salt") + l_parameters.put (l_email, "email") + l_parameters.put (create {DATE_TIME}.make_now_utc, "created") + + sql_change (sql_insert_user, l_parameters) + sql_post_execution + if not error_handler.has_error then + a_user.set_id (last_inserted_user_id) + sql_post_execution + end sql_commit_transaction else - debug ("refactor_fixme") - fixme ("maybe we should not always carry password, in this case, to implement the else part..") - end + -- set error + error_handler.add_custom_error (-1, "bad request" , "Missing password or email") + end + end + + update_user (a_user: CMS_USER) + -- Save user `a_user'. + local + l_parameters: STRING_TABLE [detachable ANY] + l_password_salt, l_password_hash: detachable READABLE_STRING_8 + l_security: SECURITY_PROVIDER + do + if attached a_user.password as l_password then + -- New password! + create l_security + l_password_salt := l_security.salt + l_password_hash := l_security.password_hash (l_password, l_password_salt) + else + -- Existing hashed password + l_password_hash := a_user.hashed_password + l_password_salt := user_salt (a_user.name) + end + if + l_password_hash /= Void and l_password_salt /= Void and + attached a_user.email as l_email + then + error_handler.reset + + log.write_information (generator + ".update_user") + create l_parameters.make (6) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_user.name, "name") + l_parameters.put (l_password_hash, "password") + l_parameters.put (l_password_salt, "salt") + l_parameters.put (l_email, "email") + l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") + + sql_change (sql_update_user, l_parameters) + sql_post_execution + else + -- set error + error_handler.add_custom_error (-1, "bad request" , "Missing password or email") end end @@ -193,44 +255,6 @@ feature {NONE} -- Implementation sql_post_execution end - new_user (a_user: CMS_USER) - -- Add a new user `a_user'. - require - no_id: not a_user.has_id - local - l_parameters: STRING_TABLE [detachable ANY] - l_password_salt, l_password_hash: STRING - l_security: SECURITY_PROVIDER - do - if - attached a_user.password as l_password and then - attached a_user.email as l_email - then - error_handler.reset - create l_security - l_password_salt := l_security.salt - l_password_hash := l_security.password_hash (l_password, l_password_salt) - - log.write_information (generator + ".new_user") - create l_parameters.make (4) - l_parameters.put (a_user.name, "username") - l_parameters.put (l_password_hash, "password") - l_parameters.put (l_password_salt, "salt") - l_parameters.put (l_email, "email") - - sql_change (sql_insert_user, l_parameters) - sql_post_execution - if not error_handler.has_error then - a_user.set_id (last_inserted_user_id) - sql_post_execution - end - else - -- set error - error_handler.add_custom_error (-1, "bad request" , "Missing password or email") - end - sql_post_execution - end - fetch_user: detachable CMS_USER local l_id: INTEGER_64 @@ -282,25 +306,27 @@ feature {NONE} -- Sql Queries: USER Select_users_count: STRING = "select count(*) from Users;" -- Number of users. - Sql_last_insert_user_id: STRING = "SELECT MAX(id) from Users;" + Sql_last_insert_user_id: STRING = "SELECT MAX(uid) from Users;" Select_users: STRING = "select * from Users;" -- List of users. - Select_user_by_id: STRING = "select * from Users where id =:id;" + Select_user_by_id: STRING = "select * from Users where uid =:uid;" -- Retrieve user by id if exists. - Select_user_by_name: STRING = "select * from Users where username =:name;" + Select_user_by_name: STRING = "select * from Users where name =:name;" -- Retrieve user by name if exists. Select_user_by_email: STRING = "select * from Users where email =:email;" -- Retrieve user by email if exists. - Select_salt_by_username: STRING = "select salt from Users where username =:name;" + Select_salt_by_username: STRING = "select salt from Users where name =:name;" -- Retrieve salt by username if exists. - SQL_Insert_user: STRING = "insert into users (username, password, salt, email) values (:username, :password, :salt, :email);" - -- SQL Insert to add a new node. + Sql_Insert_user: STRING = "insert into users (name, password, salt, email, created) values (:name, :password, :salt, :email, :created);" + -- SQL Insert to add a new node. + + sql_update_user: STRING = "update users SET name=:name, password=:password, salt=:salt, email=:email WHERE uid=:uid;" end diff --git a/library/src/service/cms_api.e b/library/src/service/cms_api.e index 75e26b3..1db79c3 100644 --- a/library/src/service/cms_api.e +++ b/library/src/service/cms_api.e @@ -88,12 +88,14 @@ feature -- Status Report feature -- Access: Node + nodes_count: INTEGER_64 + do + Result := storage.nodes_count + end + nodes: LIST [CMS_NODE] -- List of nodes. do - debug ("refactor_fixme") - fixme ("Implementation") - end Result := storage.nodes end @@ -174,12 +176,15 @@ feature -- Change User new_user (a_user: CMS_USER) -- Add a new user `a_user'. + require + no_id: not a_user.has_id + no_hashed_password: a_user.hashed_password = Void do if attached a_user.password as l_password and then attached a_user.email as l_email then - storage.save_user (a_user) + storage.new_user (a_user) else debug ("refactor_fixme") fixme ("Add error") @@ -187,6 +192,14 @@ feature -- Change User end end + update_user (a_user: CMS_USER) + -- Update user `a_user'. + require + has_id: a_user.has_id + do + storage.update_user (a_user) + end + feature -- Helpers html_encoded (a_string: READABLE_STRING_GENERAL): STRING_8