diff --git a/cms-safe.ecf b/cms-safe.ecf index 69affe6..91fdb85 100644 --- a/cms-safe.ecf +++ b/cms-safe.ecf @@ -19,6 +19,7 @@ + diff --git a/cms.ecf b/cms.ecf index 0e8620c..f642c2a 100644 --- a/cms.ecf +++ b/cms.ecf @@ -17,6 +17,7 @@ + diff --git a/examples/demo/modules/blog/cms_blog_module.e b/examples/demo/modules/blog/cms_blog_module.e index 7390683..b75fdcd 100644 --- a/examples/demo/modules/blog/cms_blog_module.e +++ b/examples/demo/modules/blog/cms_blog_module.e @@ -84,11 +84,10 @@ CREATE TABLE "blog_post_nodes"( feature -- Access: router - router (a_api: CMS_API): WSF_ROUTER - -- Node router. + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- do - create Result.make (1) - Result.handle_with_request_methods ("/blogs/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_blogs (?,?, a_api)), Result.methods_get) + a_router.handle_with_request_methods ("/blogs/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_blogs (?,?, a_api)), a_router.methods_get) end feature -- Hooks diff --git a/examples/demo/modules/demo/cms_demo_module.e b/examples/demo/modules/demo/cms_demo_module.e index 3867a16..e0f1ff1 100644 --- a/examples/demo/modules/demo/cms_demo_module.e +++ b/examples/demo/modules/demo/cms_demo_module.e @@ -80,12 +80,11 @@ CREATE TABLE "tb_demo"( feature -- Access: router - router (a_api: CMS_API): WSF_ROUTER - -- Node router. + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- do - create Result.make (2) - map_uri_template_agent (Result, "/demo/", agent handle_demo (?,?,a_api)) - map_uri_template_agent (Result, "/demo/{id}", agent handle_demo_entry (?,?,a_api)) + map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api)) + map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api)) end feature -- Hooks diff --git a/examples/demo/site/scripts/core.sql b/examples/demo/site/scripts/core.sql index 0c26f42..04dbec8 100644 --- a/examples/demo/site/scripts/core.sql +++ b/examples/demo/site/scripts/core.sql @@ -1,35 +1,5 @@ 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 "roles"( - "rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0), - "name" VARCHAR(100) NOT NULL, - CONSTRAINT "name" - UNIQUE("name") -); - -CREATE TABLE "users_roles"( - "uid" INTEGER NOT NULL CHECK("uid">=0), - "rid" INTEGER NOT NULL CHECK("rid">=0) -); - -CREATE TABLE "role_permissions"( - "rid" INTEGER NOT NULL CHECK("rid">=0), - "permission" VARCHAR(255) NOT NULL, - "module" VARCHAR(255) -); CREATE TABLE "logs"( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), @@ -48,4 +18,11 @@ CREATE TABLE "custom_values"( "value" VARCHAR(255) NOT NULL ); +CREATE TABLE "path_aliases"( + "pid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("pid">=0), + "source" VARCHAR(255) NOT NULL, + "alias" VARCHAR(255) NOT NULL, + "lang" VARCHAR(12) +); + COMMIT; diff --git a/examples/demo/site/scripts/user.sql b/examples/demo/site/scripts/user.sql new file mode 100644 index 0000000..707dbc2 --- /dev/null +++ b/examples/demo/site/scripts/user.sql @@ -0,0 +1,34 @@ +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 "roles"( + "rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0), + "name" VARCHAR(100) NOT NULL, + CONSTRAINT "name" + UNIQUE("name") +); + +CREATE TABLE "users_roles"( + "uid" INTEGER NOT NULL CHECK("uid">=0), + "rid" INTEGER NOT NULL CHECK("rid">=0) +); + +CREATE TABLE "role_permissions"( + "rid" INTEGER NOT NULL CHECK("rid">=0), + "permission" VARCHAR(255) NOT NULL, + "module" VARCHAR(255) +); + +COMMIT; diff --git a/library/model/src/link/cms_local_link.e b/library/model/src/link/cms_local_link.e index db41482..376ccac 100644 --- a/library/model/src/link/cms_local_link.e +++ b/library/model/src/link/cms_local_link.e @@ -28,12 +28,8 @@ feature {NONE} -- Initialization make (a_title: detachable like title; a_location: like location) -- Create current local link with optional title `a_title' and location `a_location'. do - if a_title /= Void then - title := a_title - else - title := a_location - end location := a_location + set_title (a_title) end feature -- Access @@ -75,6 +71,16 @@ feature -- Status report feature -- Element change + set_title (a_title: detachable like title) + -- Set `title' to `a_title' or `location'. + do + if a_title /= Void then + title := a_title + else + title := location + end + end + add_link (lnk: CMS_LINK) -- local diff --git a/library/persistence/implementation/store/cms_storage_store_sql.e b/library/persistence/implementation/store/cms_storage_store_sql.e index 3a8a4c0..acfec12 100644 --- a/library/persistence/implementation/store/cms_storage_store_sql.e +++ b/library/persistence/implementation/store/cms_storage_store_sql.e @@ -1,6 +1,5 @@ note - description: "Summary description for {CMS_STORAGE_STORE_SQL}." - author: "" + description: "Storage based on Eiffel Store component." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" @@ -8,9 +7,7 @@ deferred class CMS_STORAGE_STORE_SQL inherit - CMS_STORAGE - - CMS_STORAGE_SQL_I + CMS_STORAGE_SQL feature {NONE} -- Initialization @@ -25,7 +22,6 @@ feature {NONE} -- Initialization create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) create error_handler.make --- error_handler.add_synchronization (db_handler.database_error_handler) end feature -- Status report diff --git a/library/persistence/implementation/store/cms_storage_store_sql_builder.e b/library/persistence/implementation/store/cms_storage_store_sql_builder.e new file mode 100644 index 0000000..6fc4b15 --- /dev/null +++ b/library/persistence/implementation/store/cms_storage_store_sql_builder.e @@ -0,0 +1,16 @@ +note + description: "[ + Common ancestor for builders responsible to instantiate storage based + on Eiffel Store storage. + ]" + author: "$Author: jfiat $" + date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" + revision: "$Revision: 96616 $" + +deferred class + CMS_STORAGE_STORE_SQL_BUILDER + +inherit + CMS_STORAGE_SQL_BUILDER + +end diff --git a/library/persistence/mysql/src/cms_storage_mysql_builder.e b/library/persistence/mysql/src/cms_storage_mysql_builder.e index 4889f73..2249df3 100644 --- a/library/persistence/mysql/src/cms_storage_mysql_builder.e +++ b/library/persistence/mysql/src/cms_storage_mysql_builder.e @@ -1,6 +1,6 @@ note description: "[ - Objects that ... + Interface responsible to instantiate CMS_STORAGE_MYSQL object. ]" author: "$Author: jfiat $" date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" @@ -10,7 +10,9 @@ class CMS_STORAGE_MYSQL_BUILDER inherit - CMS_STORAGE_SQL_BUILDER + CMS_STORAGE_STORE_SQL_BUILDER + + GLOBAL_SETTINGS create make @@ -32,6 +34,12 @@ feature -- Factory create {DATABASE_CONNECTION_MYSQL} conn.login_with_connection_string (l_database_config.connection_string) if conn.is_connected then create Result.make (conn) + set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default. + if Result.is_available then + if not Result.is_initialized then + initialize (a_setup, Result) + end + end end end end diff --git a/library/persistence/sqlite/src/cms_storage_sqlite.e b/library/persistence/sqlite/src/cms_storage_sqlite.e index 78b9942..218b09e 100644 --- a/library/persistence/sqlite/src/cms_storage_sqlite.e +++ b/library/persistence/sqlite/src/cms_storage_sqlite.e @@ -1,5 +1,5 @@ note - description: "Summary description for {CMS_STORAGE_MYSQL}." + description: "Summary description for {CMS_STORAGE_SQLITE}." date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $" revision: "$Revision: 96596 $" diff --git a/library/persistence/sqlite/src/cms_storage_sqlite_builder.e b/library/persistence/sqlite/src/cms_storage_sqlite_builder.e index bb4dcf3..f59beee 100644 --- a/library/persistence/sqlite/src/cms_storage_sqlite_builder.e +++ b/library/persistence/sqlite/src/cms_storage_sqlite_builder.e @@ -1,6 +1,6 @@ note description: "[ - Objects that ... + Interface responsible to instantiate CMS_STORAGE_SQLITE object. ]" author: "$Author: jfiat $" date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" @@ -10,7 +10,7 @@ class CMS_STORAGE_SQLITE_BUILDER inherit - CMS_STORAGE_SQL_BUILDER + CMS_STORAGE_STORE_SQL_BUILDER GLOBAL_SETTINGS @@ -29,6 +29,7 @@ feature -- Factory storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_SQLITE local s: STRING + conn: DATABASE_CONNECTION do if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.application_config_path) as l_database_config then s := "Driver=SQLite3 ODBC Driver;Database=" @@ -36,77 +37,17 @@ feature -- Factory s.append (db_name) end s.append (";LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;") - create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (s)) - set_map_zero_null_value (False) - -- This way we map 0 to 0, instead of Null as default. - --create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (l_database_config.connection_string)) - if Result.is_available then - if not Result.is_initialized then - initialize (a_setup, Result) + create {DATABASE_CONNECTION_ODBC} conn.login_with_connection_string (s) + if conn.is_connected then + create Result.make (conn) + set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default. + if Result.is_available then + if not Result.is_initialized then + initialize (a_setup, Result) + end end end end end - initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL) - local - u: CMS_USER - l_anonymous_role, l_authenticated_role, r: CMS_USER_ROLE - l_roles: LIST [CMS_USER_ROLE] - do - --| Schema - a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql")) - - --| Roles - 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") - u.set_password ("istrator#") - u.set_email (a_setup.site_email) - a_storage.new_user (u) - - --| Node - -- FIXME: move that initialization to node module - l_anonymous_role.add_permission ("view any page") - a_storage.save_user_role (l_anonymous_role) - - 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) - - create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1) - l_roles.force (r) - - create u.make ("auth") - 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 diff --git a/modules/basic_auth/basic_auth_module.e b/modules/basic_auth/basic_auth_module.e index 1cb025c..3efe2c2 100644 --- a/modules/basic_auth/basic_auth_module.e +++ b/modules/basic_auth/basic_auth_module.e @@ -37,12 +37,11 @@ feature {NONE} -- Initialization feature -- Access: router - router (a_api: CMS_API): WSF_ROUTER - -- Node router. + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- do - create Result.make (2) - configure_api_login (a_api, Result) - configure_api_logoff (a_api, Result) + configure_api_login (a_api, a_router) + configure_api_logoff (a_api, a_router) end feature -- Access: filter diff --git a/modules/node/cms_node_api.e b/modules/node/cms_node_api.e index 9922722..8f0a8d9 100644 --- a/modules/node/cms_node_api.e +++ b/modules/node/cms_node_api.e @@ -176,6 +176,14 @@ feature -- URL end end + node_link (a_node: CMS_NODE): CMS_LOCAL_LINK + -- CMS link for node `a_node'. + require + a_node.has_id + do + create Result.make (a_node.title, cms_api.path_alias (node_path (a_node))) + end + node_path (a_node: CMS_NODE): STRING -- URI path for node `a_node'. -- using the /node/{nid} url. @@ -234,6 +242,11 @@ feature -- Access: Node Result := a_node end + -- Update link with aliasing. + if a_node /= Void and then a_node.has_id then + a_node.set_link (node_link (a_node)) + end + -- Update partial user if needed. if Result /= Void and then @@ -259,16 +272,17 @@ feature -- Access: Node 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'. + has_permission_for_action_on_node (a_action: READABLE_STRING_8; a_node: CMS_NODE; a_user: detachable CMS_USER; ): BOOLEAN + -- Has permission to execute action `a_action' on node `a_node', by eventual user `a_user'? + local + l_type_name: READABLE_STRING_8 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" + l_type_name := a_node.content_type + Result := cms_api.user_has_permission (a_user, a_action + " any " + l_type_name) + if not Result and a_user /= Void then + if is_author_of_node (a_user, a_node) then + Result := cms_api.user_has_permission (a_user, a_action + " own " + l_type_name) + end end end diff --git a/modules/node/content/cms_node.e b/modules/node/content/cms_node.e index 51808b2..e01a814 100644 --- a/modules/node/content/cms_node.e +++ b/modules/node/content/cms_node.e @@ -54,6 +54,7 @@ feature -- Conversion a_node.format ) set_status (a_node.status) + set_link (a_node.link) end feature -- Access @@ -142,6 +143,11 @@ feature -- status report Result := a_content_type.is_case_insensitive_equal (content_type) end +feature -- Access: menu + + link: detachable CMS_LOCAL_LINK + -- Associated menu link. + feature -- Element change set_content (a_content: like content; a_summary: like summary; a_format: like format) @@ -158,6 +164,9 @@ feature -- Element change -- Assign `title' with `a_title'. do title := a_title + if attached link as lnk then + lnk.set_title (a_title) + end ensure title_assigned: title = a_title end @@ -211,6 +220,14 @@ feature -- Element change auther_set: author = u end + set_link (a_link: like link) + -- Set `link' to `a_link'. + do + link := a_link + end + +feature -- Status change + mark_not_published -- Set status to not_published. do @@ -235,7 +252,6 @@ feature -- Element change status_trash: status = {CMS_NODE_API}.trashed end - feature {CMS_NODE_STORAGE_I} -- Access: status change. set_status (a_status: like status) diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index 88161a4..f571c26 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -65,6 +65,26 @@ feature -- Forms ... fset.extend (tselect) f.extend (fset) + + -- Path aliase + create ti.make ("path_alias") + ti.set_label ("Path") + ti.set_size (70) + if a_node /= Void and then a_node.has_id then + if attached a_node.link as lnk then + ti.set_text_value (lnk.location) + else + ti.set_text_value (response.api.path_alias (response.node_api.node_path (a_node))) + end + end + if + attached f.fields_by_name ("title") as l_title_fields and then + attached l_title_fields.first as l_title_field + then + f.insert_after (ti, l_title_field) + else + f.extend (ti) + end end update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE) @@ -160,8 +180,9 @@ feature -- Output node_api := a_response.node_api a_response.add_variable (a_node, "node") - create lnk.make ("View", node_api.node_path (a_node)) + create lnk.make (a_response.translation ("View", Void), a_response.node_local_link (a_node).location) lnk.set_weight (1) + a_response.add_to_primary_tabs (lnk) create lnk.make ("Edit", node_api.node_path (a_node) + "/edit") lnk.set_weight (2) diff --git a/modules/node/handler/cms_page_node_type_webform_manager.e b/modules/node/handler/cms_page_node_type_webform_manager.e index e538aa7..498f392 100644 --- a/modules/node/handler/cms_page_node_type_webform_manager.e +++ b/modules/node/handler/cms_page_node_type_webform_manager.e @@ -1,6 +1,5 @@ note description: "Summary description for {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}." - author: "" date: "$Date$" revision: "$Revision$" diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e index 8ade0b6..c01330a 100644 --- a/modules/node/handler/node_form_response.e +++ b/modules/node/handler/node_form_response.e @@ -37,7 +37,7 @@ feature -- Execution -- Computed response message. local b: STRING_8 - f: like edit_form + f: like new_edit_form fd: detachable WSF_FORM_DATA nid: INTEGER_64 do @@ -48,43 +48,44 @@ feature -- Execution attached node_api.node (nid) as l_node then if attached node_api.node_type_for (l_node) as l_type 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) + if node_api.has_permission_for_action_on_node ("edit", l_node, current_user (request)) then + f := new_edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) + invoke_form_alter (f, fd) if request.is_post_request_method then f.validation_actions.extend (agent edit_form_validate (?, b)) f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) f.process (Current) fd := f.last_data end + if l_node.has_id then + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("View", Void), node_url (l_node)), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), "/node/" + l_node.id.out + "/edit"), primary_tabs) + end if attached redirection as l_location then -- FIXME: Hack for now set_title (l_node.title) - add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs) - b.append (html_encoded (l_type.title) + " saved") else - set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out) - - add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs) - + set_title (formatted_string (translation ("Edit $1 #$2", Void), [l_type.title, l_node.id])) f.append_to_html (wsf_theme, b) end else - b.append ("

Access denied

") + b.append ("

") + b.append (translation ("Access denied", Void)) + b.append ("

") end else - set_title ("Unknown node") + set_title (translation ("Unknown node", Void)) end elseif attached {WSF_STRING} request.path_parameter ("type") as p_type and then attached node_api.node_type (p_type.value) as l_type then - if has_permission ("create " + l_type.name) then + if has_permissions (<<"create any", "create " + l_type.name>>) 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 := new_edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) + invoke_form_alter (f, fd) if request.is_post_request_method then f.validation_actions.extend (agent edit_form_validate (?, b)) f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) @@ -94,25 +95,31 @@ feature -- Execution set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out) - add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs) - add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs) + if l_node.has_id then + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("View", Void), node_url (l_node)), primary_tabs) + add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), "/node/" + l_node.id.out + "/edit"), primary_tabs) + end f.append_to_html (wsf_theme, b) else - b.append ("

Server error

") + b.append ("

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

") end else - b.append ("

Access denied

") + b.append ("

") + b.append (translation ("Access denied", Void)) + b.append ("

") end else - set_title ("Create new content ...") + set_title (translation ("Create new content ...", Void)) b.append ("
    ") across node_api.node_types as ic loop if attached ic.item as l_node_type and then - (has_permission ("create any") or has_permission ("create " + l_node_type.name)) + has_permissions (<<"create any", "create " + l_node_type.name>>) then b.append ("
  • " + link (l_node_type.name, "/node/add/" + l_node_type.name, Void)) if attached l_node_type.description as d then @@ -191,16 +198,25 @@ feature -- Form end node_api.save_node (l_node) if attached current_user (request) as u then - api.log ("node", "User %"" + user_link (u) + "%" " + s + " node " + link (a_type.name +" #" + l_node.id.out, "/node/" + l_node.id.out , Void), 0, node_local_link (l_node)) + api.log ("node", "User %"" + user_html_link (u) + "%" " + s + " node " + link (a_type.name +" #" + l_node.id.out, "/node/" + l_node.id.out , Void), 0, node_local_link (l_node)) else api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node)) end add_success_message ("Node #" + l_node.id.out + " saved.") + + if attached fd.string_item ("path_alias") as l_path_alias then + api.set_path_alias (node_api.node_path (l_node), l_path_alias, False) + l_node.set_link (create {CMS_LOCAL_LINK}.make (l_node.title, l_path_alias)) + else + l_node.set_link (node_api.node_link (l_node)) + end + set_redirection (node_url (l_node)) end end - edit_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM + new_edit_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_node_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM + -- Create a web form named `a_name' for node `a_node' (if set), using form action url `a_url', and for type of node `a_node_type'. local f: CMS_FORM ts: WSF_FORM_SUBMIT_INPUT @@ -216,8 +232,7 @@ feature -- Form end f.extend (th) - fill_edit_form (a_type, f, a_node) - + populate_form (a_node_type, f, a_node) f.extend_html_text ("
    ") create ts.make ("op") @@ -228,11 +243,15 @@ feature -- Form ts.set_default_value ("Preview") f.extend (ts) - if a_node /= Void and then a_node.id > 0 and then has_permission ("delete " + a_name) then + 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 + ts.set_default_value (translation ("Delete")) ]") f.extend (ts) end @@ -240,8 +259,18 @@ feature -- Form Result := f end + populate_form (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form: WSF_FORM; a_node: detachable CMS_NODE) + -- Fill the web form `a_form' with data from `a_node' if set, + -- and apply this to content type `a_content_type'. + do + if attached node_api.node_type_webform_manager (a_content_type) as wf then + wf.populate_form (Current, a_form, a_node) + end + end + new_node (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form_data: WSF_FORM_DATA; a_node: detachable CMS_NODE): CMS_NODE - -- + -- Node creation with form_data `a_form_data' for the given content type `a_content_type' + -- using optional `a_node' to get extra node data. do if attached node_api.node_type_webform_manager (a_content_type) as wf then Result := wf.new_node (Current, a_form_data, a_node) @@ -258,11 +287,4 @@ feature -- Form end end - fill_edit_form (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form: WSF_FORM; a_node: detachable CMS_NODE) - do - if attached node_api.node_type_webform_manager (a_content_type) as wf then - wf.populate_form (Current, a_form, a_node) - end - end - end diff --git a/modules/node/handler/node_handler.e b/modules/node/handler/node_handler.e index 0e6d8b9..de50f39 100644 --- a/modules/node/handler/node_handler.e +++ b/modules/node/handler/node_handler.e @@ -150,7 +150,7 @@ feature -- HTTP Methods l_id.is_integer and then attached node_api.node (l_id.integer_value) as l_node then - if api.user_has_permission (l_user, "delete " + node_api.permission_scope (l_user, l_node) + " " + l_node.content_type) then + if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then node_api.delete_node (l_node) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) else diff --git a/modules/node/handler/node_response.e b/modules/node/handler/node_response.e index 0bb7210..1bad113 100644 --- a/modules/node/handler/node_response.e +++ b/modules/node/handler/node_response.e @@ -51,36 +51,48 @@ feature -- Helpers end end -feature -- Helpers +feature -- Helpers: cms link - user_local_link (u: CMS_USER): CMS_LINK + user_local_link (u: CMS_USER): CMS_LOCAL_LINK do - create {CMS_LOCAL_LINK} Result.make (u.name, user_url (u)) + create Result.make (u.name, user_url (u)) end - node_local_link (n: CMS_NODE): CMS_LINK + node_local_link (n: CMS_NODE): CMS_LOCAL_LINK do - create {CMS_LOCAL_LINK} Result.make (n.title, node_url (n)) + if attached n.link as lnk then + Result := lnk + else + Result := node_api.node_link (n) + end end - user_link (u: CMS_USER): like link +feature -- Helpers: html link + + user_html_link (u: CMS_USER): like link do Result := link (u.name, "/user/" + u.id.out, Void) end - node_link (n: CMS_NODE): like link + node_html_link (n: CMS_NODE): like link do Result := link (n.title, "/node/" + n.id.out, Void) end +feature -- Helpers: URL + user_url (u: CMS_USER): like url + require + u_with_id: u.has_id do Result := url ("/user/" + u.id.out, Void) end node_url (n: CMS_NODE): like url + require + n_with_id: n.has_id do - Result := url ("/node/" + n.id.out, Void) + Result := url (node_api.node_link (n).location, Void) end end diff --git a/modules/node/handler/nodes_handler.e b/modules/node/handler/nodes_handler.e index 2619a90..18462d2 100644 --- a/modules/node/handler/nodes_handler.e +++ b/modules/node/handler/nodes_handler.e @@ -40,6 +40,7 @@ feature -- HTTP Methods l_page: CMS_RESPONSE s: STRING n: CMS_NODE + lnk: CMS_LOCAL_LINK do -- At the moment the template is hardcoded, but we can -- get them from the configuration file and load them into @@ -57,8 +58,10 @@ feature -- HTTP Methods lst as ic loop n := ic.item + lnk := node_api.node_link (n) s.append ("
  • ") - s.append (l_page.link (n.title + " (#" + n.id.out + ")", node_api.node_path (n), Void)) + s.append (l_page.link (lnk.title, lnk.location, Void)) +-- s.append (l_page.link (n.title + " (#" + n.id.out + ")", node_api.node_path (n), Void)) s.append ("
  • %N") end s.append ("
%N") diff --git a/modules/node/node_module.e b/modules/node/node_module.e index ba6ca2d..8df4eab 100644 --- a/modules/node/node_module.e +++ b/modules/node/node_module.e @@ -109,8 +109,8 @@ feature {CMS_API} -- Access: API feature -- Access: router - router (a_api: CMS_API): WSF_ROUTER - -- Node router. + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- local l_node_api: like node_api do @@ -119,8 +119,7 @@ feature -- Access: router create l_node_api.make (a_api) node_api := l_node_api end - create Result.make (2) - configure_web (a_api, l_node_api, Result) + configure_web (a_api, l_node_api, a_router) end configure_web (a_api: CMS_API; a_node_api: CMS_NODE_API; a_router: WSF_ROUTER) diff --git a/modules/node/persistence/cms_node_storage_i.e b/modules/node/persistence/cms_node_storage_i.e index 815593c..6090d95 100644 --- a/modules/node/persistence/cms_node_storage_i.e +++ b/modules/node/persistence/cms_node_storage_i.e @@ -107,6 +107,8 @@ feature -- Change: Node no_id: not a_node.has_id valid_user: attached a_node.author as l_author and then l_author.id > 0 deferred + ensure + has_id: a_node.has_id end update_node (a_node: CMS_NODE) diff --git a/modules/node/persistence/cms_node_storage_sql.e b/modules/node/persistence/cms_node_storage_sql.e index ebbf4a8..06ecadc 100644 --- a/modules/node/persistence/cms_node_storage_sql.e +++ b/modules/node/persistence/cms_node_storage_sql.e @@ -157,51 +157,6 @@ feature -- Change: Node sql_change (sql_delete_node, l_parameters) end --- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- -- FIXME: unused a_user_id ! --- error_handler.reset --- write_information_log (generator + ".update_node_title") --- create l_parameters.make (3) --- l_parameters.put (a_title, "title") --- 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) --- end - --- update_node_summary (a_user_id: Like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- -- FIXME: unused a_user_id ! --- error_handler.reset --- write_information_log (generator + ".update_node_summary") --- create l_parameters.make (3) --- l_parameters.put (a_summary, "summary") --- 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) --- end - --- update_node_content (a_user_id: Like {CMS_USER}.id;a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- -- FIXME: unused a_user_id ! --- error_handler.reset --- write_information_log (generator + ".update_node_content") --- create l_parameters.make (3) --- l_parameters.put (a_content, "content") --- 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) --- end - feature {NONE} -- Implementation store_node (a_node: CMS_NODE) @@ -273,9 +228,9 @@ feature {NONE} -- Queries -- 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, status 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, status 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, status, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);" -- SQL Insert to add a new node. diff --git a/src/configuration/cms_default_setup.e b/src/configuration/cms_default_setup.e index 6730fb5..9a16640 100644 --- a/src/configuration/cms_default_setup.e +++ b/src/configuration/cms_default_setup.e @@ -84,7 +84,7 @@ feature {NONE} -- Initialization initialize_modules -- Intialize core modules. local - m: CMS_MODULE +-- m: CMS_MODULE do -- Core -- create {BASIC_AUTH_MODULE} m.make diff --git a/src/modules/cms_debug_module.e b/src/modules/cms_debug_module.e index c851be6..9b51d1b 100644 --- a/src/modules/cms_debug_module.e +++ b/src/modules/cms_debug_module.e @@ -36,11 +36,10 @@ feature {NONE} -- Initialization feature -- Router - router (a_api: CMS_API): WSF_ROUTER - -- Router configuration. + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- do - create Result.make (1) - Result.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?))) + a_router.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?))) end feature -- Hooks configuration diff --git a/src/persistence/cms_storage_null.e b/src/persistence/cms_storage_null.e index 37ada00..17028e6 100644 --- a/src/persistence/cms_storage_null.e +++ b/src/persistence/cms_storage_null.e @@ -14,6 +14,11 @@ inherit default_create end + CMS_USER_STORAGE_NULL + undefine + default_create + end + REFACTORING_HELPER rename default_create as default_create_rh @@ -40,74 +45,34 @@ feature -- Status report Result := True end -feature -- Access: user +feature -- URL aliases - has_user: BOOLEAN - -- Has any user? + set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- . do end - users: LIST [CMS_USER] - do - create {ARRAYED_LIST [CMS_USER]} Result.make (0) - end - - user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- Replace eventual previous alias `a_previous_alias' with a new alias `a_alias' + -- on source `a_source'. do end - user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- Unalias `a_source' from `a_alias'. do end - user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Return eventual path alias associated with `a_source'. do end - is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Source path for alias `a_alias'. do end -feature -- Change: user - - new_user (a_user: CMS_USER) - -- Add a new user `a_user'. - do - a_user.set_id (1) - 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 - do - end - - user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE] - -- User roles for user `a_user'. - -- Note: anonymous and authenticated roles are not included. - do - create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0) - end - - user_roles: LIST [CMS_USER_ROLE] - do - create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0) - end - -feature -- Change: roles and permissions - - save_user_role (a_user_role: CMS_USER_ROLE) - do - end - - - feature -- Logs save_log (a_log: CMS_LOG) @@ -115,6 +80,8 @@ feature -- Logs do end +feature -- Custom + set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8) -- Save data `a_name:a_value' for type `a_type' (or default if none). do diff --git a/src/persistence/cms_storage_sql_builder.e b/src/persistence/cms_storage_sql_builder.e deleted file mode 100644 index d43ea79..0000000 --- a/src/persistence/cms_storage_sql_builder.e +++ /dev/null @@ -1,15 +0,0 @@ -note - description: "[ - Objects that ... - ]" - author: "$Author: jfiat $" - date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" - revision: "$Revision: 96542 $" - -deferred class - CMS_STORAGE_SQL_BUILDER - -inherit - CMS_STORAGE_BUILDER - -end diff --git a/src/persistence/core/cms_core_storage_i.e b/src/persistence/core/cms_core_storage_i.e index 6267dba..89926de 100644 --- a/src/persistence/core/cms_core_storage_i.e +++ b/src/persistence/core/cms_core_storage_i.e @@ -19,6 +19,34 @@ feature -- Error Handling deferred end +feature -- URL aliases + + set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- Alias `a_source' with `a_alias'. + deferred + end + + replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- Replace eventual previous alias `a_previous_alias' with a new alias `a_alias' + -- on source `a_source'. + deferred + end + + unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- Unalias `a_source' from `a_alias'. + deferred + end + + path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Return eventual path alias associated with `a_source'. + deferred + end + + source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Source path for alias `a_alias'. + deferred + end + feature -- Logs save_log (a_log: CMS_LOG) diff --git a/src/persistence/core/cms_core_storage_sql_i.e b/src/persistence/core/cms_core_storage_sql_i.e index 07a5910..c21305f 100644 --- a/src/persistence/core/cms_core_storage_sql_i.e +++ b/src/persistence/core/cms_core_storage_sql_i.e @@ -18,6 +18,123 @@ inherit SHARED_LOGGER +feature -- URL aliases + + set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (a_source, "source") + l_parameters.put (a_alias, "alias") + if attached source_of_path_alias (a_alias) as l_path then + if a_source.same_string (l_path) then + -- already up to date + else + error_handler.add_custom_error (0, "alias exists", "Path alias %"" + a_alias + "%" already exists!") + end + else + sql_change (sql_insert_path_alias, l_parameters) + end + end + + replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_previous_alias: detachable READABLE_STRING_8 + do + error_handler.reset + + if a_previous_alias = Void then + l_previous_alias := path_alias (a_source) + else + l_previous_alias := a_previous_alias + end + if + l_previous_alias /= Void and then + not a_alias.same_string (l_previous_alias) + then + create l_parameters.make (3) + l_parameters.put (a_source, "source") + l_parameters.put (l_previous_alias, "old") + l_parameters.put (a_alias, "alias") + + sql_change (sql_update_path_alias, l_parameters) + end + end + + unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + if attached source_of_path_alias (a_alias) as l_path then + if a_source.same_string (l_path) then + -- Found + create l_parameters.make (1) + l_parameters.put (a_alias, "alias") + sql_change (sql_delete_path_alias, l_parameters) + else + error_handler.add_custom_error (0, "alias mismatch", "Path alias %"" + a_alias + "%" is not related to source %"" + a_source + "%"!") + end + else + -- No such alias + end + end + + path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8 + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + create l_parameters.make (1) + l_parameters.put (a_source, "source") + sql_query (sql_select_path_source, l_parameters) + if not has_error then + if sql_rows_count = 1 then + Result := sql_read_string (1) + end + end + end + + source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8 + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + create l_parameters.make (1) + l_parameters.put (a_alias, "alias") + sql_query (sql_select_path_alias, l_parameters) + if not has_error then + if sql_rows_count = 1 then + Result := sql_read_string (1) + end + end + end + + sql_select_path_alias: STRING = "SELECT source FROM path_aliases WHERE alias=:alias ;" + -- SQL select path aliases. + + sql_select_path_source: STRING = "SELECT alias FROM path_aliases WHERE source=:source ORDER BY pid DESC LIMIT 1;" + -- SQL select latest path aliasing :source. + + sql_insert_path_alias: STRING = "INSERT INTO path_aliases (source, alias) VALUES (:source, :alias);" + -- SQL insert path alias. + + sql_update_path_alias: STRING = "UPDATE path_aliases SET alias=:alias WHERE source=:source AND alias=:old ;" + -- SQL update path alias. + + sql_delete_path_alias: STRING = "DELETE FROM path_aliases WHERE alias=:alias;" + -- SQL delete path alias + feature -- Logs save_log (a_log: CMS_LOG) diff --git a/src/persistence/cms_proxy_storage_sql.e b/src/persistence/sql/cms_proxy_storage_sql.e similarity index 100% rename from src/persistence/cms_proxy_storage_sql.e rename to src/persistence/sql/cms_proxy_storage_sql.e diff --git a/src/persistence/sql/cms_storage_sql.e b/src/persistence/sql/cms_storage_sql.e new file mode 100644 index 0000000..afd4793 --- /dev/null +++ b/src/persistence/sql/cms_storage_sql.e @@ -0,0 +1,15 @@ +note + description: "CMS Storage based on SQL statement." + date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" + revision: "$Revision: 96616 $" + +deferred class + CMS_STORAGE_SQL + +inherit + CMS_STORAGE + + CMS_STORAGE_SQL_I + + +end diff --git a/src/persistence/sql/cms_storage_sql_builder.e b/src/persistence/sql/cms_storage_sql_builder.e new file mode 100644 index 0000000..6000207 --- /dev/null +++ b/src/persistence/sql/cms_storage_sql_builder.e @@ -0,0 +1,85 @@ +note + description: "[ + Common ancestor for builders responsible to instantiate storage based + on SQL statement storage. + ]" + author: "$Author: jfiat $" + date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" + revision: "$Revision: 96542 $" + +deferred class + CMS_STORAGE_SQL_BUILDER + +inherit + CMS_STORAGE_BUILDER + +feature -- Initialization + + initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_SQL) + local + u: CMS_USER + l_anonymous_role, l_authenticated_role, r: CMS_USER_ROLE + l_roles: LIST [CMS_USER_ROLE] + do + --| 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 ("user.sql")) + + --| Roles + 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") + u.set_password ("istrator#") + u.set_email (a_setup.site_email) + a_storage.new_user (u) + + --| Node + -- FIXME: move that initialization to node module + l_anonymous_role.add_permission ("view any page") + a_storage.save_user_role (l_anonymous_role) + + l_authenticated_role.add_permission ("create page") + l_authenticated_role.add_permission ("view any page") + l_authenticated_role.add_permission ("edit any page") + l_authenticated_role.add_permission ("delete page") + l_authenticated_role.add_permission ("view own 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 any page") + a_storage.save_user_role (r) + + create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1) + l_roles.force (r) + + create u.make ("auth") + 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 diff --git a/src/persistence/cms_storage_sql_i.e b/src/persistence/sql/cms_storage_sql_i.e similarity index 100% rename from src/persistence/cms_storage_sql_i.e rename to src/persistence/sql/cms_storage_sql_i.e diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e new file mode 100644 index 0000000..7422618 --- /dev/null +++ b/src/persistence/user/cms_user_storage_null.e @@ -0,0 +1,79 @@ +note + description: "Summary description for {CMS_USER_STORAGE_NULL}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_USER_STORAGE_NULL + +inherit + CMS_USER_STORAGE_I + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + do + end + + users: LIST [CMS_USER] + do + create {ARRAYED_LIST [CMS_USER]} Result.make (0) + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + do + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + do + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + do + end + + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + do + end + +feature -- Change: user + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + a_user.set_id (1) + 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 + do + end + + user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE] + -- User roles for user `a_user'. + -- Note: anonymous and authenticated roles are not included. + do + create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0) + end + + user_roles: LIST [CMS_USER_ROLE] + do + create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0) + end + +feature -- Change: roles and permissions + + save_user_role (a_user_role: CMS_USER_ROLE) + do + end + +end diff --git a/src/service/cms_api.e b/src/service/cms_api.e index d9bbf54..655ed43 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -232,6 +232,66 @@ feature -- Query: API Result := l_api end +feature -- Path aliases + + set_path_alias (a_source, a_alias: READABLE_STRING_8; a_keep_previous: BOOLEAN) + -- Set `a_alias' as alias of `a_source', + -- and eventually unset previous alias if any. + local + l_continue: BOOLEAN + do + if attached storage.path_alias (a_source) as l_existing_alias then + if a_alias.same_string (l_existing_alias) then + -- Already aliased as expected + else + -- New alias + if a_keep_previous then + l_continue := True + else + storage.replace_path_alias (a_source, l_existing_alias, a_alias) + end + end + elseif a_alias.is_whitespace then + -- Ignore + elseif a_source.same_string (a_alias) then + -- No need for alias + else + l_continue := True + end + if l_continue then + storage.set_path_alias (a_source, a_alias) + end + end + + unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8) + do + storage.unset_path_alias (a_source, a_alias) + end + + path_alias (a_source: READABLE_STRING_8): READABLE_STRING_8 + -- Path alias associated with `a_source' or the source itself. + do + Result := a_source + if attached storage.path_alias (Result) as l_path then + Result := l_path + end + end + + source_of_path_alias (a_alias: READABLE_STRING_8): READABLE_STRING_8 + -- Resolved path for alias `a_alias'. + --| the CMS supports aliases for path, and then this function simply returns + --| the effective target path/url for this `a_alias'. + --| For instance: /articles/2015/may/this-is-an-article can be an alias to /node/123 + --| This function will return "/node/123". + --| If the alias is bad (i.e does not alias real path), then this function + --| returns the alias itself. + do + Result := a_alias + if attached storage.source_of_path_alias (Result) as l_path then + Result := l_path + end + end + feature -- Element Change: Error reset_error diff --git a/src/service/cms_module.e b/src/service/cms_module.e index 58b048d..adc48c6 100644 --- a/src/service/cms_module.e +++ b/src/service/cms_module.e @@ -66,10 +66,8 @@ feature {CMS_API} -- Module management feature -- Router - router (a_api: CMS_API): WSF_ROUTER - -- Router configuration. - require - is_enabled: is_enabled + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- Setup url dispatching for Current module. deferred end diff --git a/src/service/cms_service.e b/src/service/cms_service.e index 7153654..918d0ef 100644 --- a/src/service/cms_service.e +++ b/src/service/cms_service.e @@ -16,7 +16,9 @@ inherit undefine requires_proxy redefine - execute_default + execute_default, + create_router, + router end WSF_FILTERED_SERVICE @@ -82,10 +84,17 @@ feature {NONE} -- Initialization feature -- Settings: router + router: CMS_ROUTER + + create_router + -- Create `router'. + do + create router.make (api, 30) + end + setup_router -- local - l_module: CMS_MODULE l_api: like api l_router: like router do @@ -100,8 +109,7 @@ feature -- Settings: router across modules as ic loop - l_module := ic.item - l_router.import (l_module.router (l_api)) + ic.item.setup_router (l_router, l_api) end -- Configure files handler. configure_api_file_handler (l_router) diff --git a/src/service/path/cms_router.e b/src/service/path/cms_router.e new file mode 100644 index 0000000..fdc0452 --- /dev/null +++ b/src/service/path/cms_router.e @@ -0,0 +1,38 @@ +note + description: "Specific version of WSF_ROUTER for CMS component." + date: "$Date$" + revision: "$Revision$" + +class + CMS_ROUTER + +inherit + WSF_ROUTER + rename + make as make_router + redefine + path_to_dispatch + end + +create + make + +feature {NONE} -- Initialization + + make (a_api: CMS_API; a_capacity: INTEGER) + do + api := a_api + make_router (a_capacity) + end + + api: CMS_API + +feature {WSF_ROUTER_MAPPING} -- Dispatch helper + + path_to_dispatch (req: WSF_REQUEST): READABLE_STRING_8 + -- Path used by the router, to apply url dispatching of request `req'. + do + Result := api.source_of_path_alias (Precursor (req)) + end + +end diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 13db564..18d99d4 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -76,6 +76,26 @@ feature -- Access redirection: detachable READABLE_STRING_8 -- Location for eventual redirection. +feature -- Internationalization (i18n) + + translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32 + -- Translated text `a_text' according to expected context (lang, ...) + -- and adapt according to options eventually set by `opts'. + do + to_implement ("Implement i18n support [2015-may]") + Result := a_text.as_string_32 + end + + formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32 + -- Format `a_text' using arguments `args'. + --| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact" + local + l_formatter: CMS_STRING_FORMATTER + do + create l_formatter + Result := l_formatter.formatted_string (a_text, args) + end + feature -- API api: CMS_API @@ -185,12 +205,30 @@ feature -- Permission Result := user_has_permission (current_user (request), a_permission) end + has_permissions (a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN + -- Does current user has any of the permissions `a_permission_list' ? + do + Result := user_has_permissions (current_user (request), a_permission_list) + end + user_has_permission (a_user: detachable CMS_USER; a_permission: READABLE_STRING_GENERAL): BOOLEAN -- Does `a_user' has permission `a_permission' ? do Result := api.user_has_permission (a_user, a_permission) end + user_has_permissions (a_user: detachable CMS_USER; a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN + -- Does `a_user' has any of the permissions `a_permission_list' ? + do + across + a_permission_list as ic + until + Result + loop + Result := user_has_permission (a_user, ic.item) + end + end + feature -- Head customization add_additional_head_line (s: READABLE_STRING_8; a_allow_duplication: BOOLEAN) diff --git a/src/support/cms_string_formatter.e b/src/support/cms_string_formatter.e new file mode 100644 index 0000000..19f394b --- /dev/null +++ b/src/support/cms_string_formatter.e @@ -0,0 +1,23 @@ +note + description: "Format a text using arguments." + date: "$Date$" + revision: "$Revision$" + +class + CMS_STRING_FORMATTER + +feature -- Conversion + + formatted_string (a_string: READABLE_STRING_GENERAL; args: TUPLE): STRING_32 + do + Result := i18n_formatter.formatted_string (a_string, args) + end + +feature {NONE} -- Implementation + + i18n_formatter: I18N_STRING_FORMATTER + once + create Result + end + +end