From f2bb061488532285dcdd73df6cd5b8b19c9092de Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 15 Apr 2015 16:39:03 +0200 Subject: [PATCH] Added support for log stored in CMS_STORAGE. Added support for custom value stored in CMS_STORAGE. Added optional css classes addition to CMS_BLOCK output. Refactored storage, to manage node from node module code only (or mostly). TODO: improved view for a cms node, for now hardcoded. --- examples/demo/modules/demo/cms_demo_module.e | 2 +- examples/demo/site/scripts/core.sql | 18 ++ examples/demo/site/scripts/node.sql | 3 +- .../themes/bootstrap/assets/css/style.css | 25 ++ .../themes/bootstrap/assets/scss/style.scss | 16 ++ library/model/src/log/cms_log.e | 127 +++++++++++ .../store/cms_storage_store_sql.e | 2 +- .../mysql/src/cms_node_storage_mysql.e | 79 ------- .../persistence/mysql/src/cms_storage_mysql.e | 6 +- .../mysql/src/cms_storage_mysql_builder.e | 2 +- .../sqlite/src/cms_node_storage_sqlite.e | 77 ------- .../sqlite/src/cms_storage_sqlite.e | 8 +- .../sqlite/src/cms_storage_sqlite_builder.e | 7 +- .../sqlite/src/cms_user_storage_sqlite.e | 14 -- src/kernel/content/cms_block.e | 31 +++ src/kernel/content/cms_content_block.e | 2 +- src/kernel/content/cms_menu_block.e | 19 +- src/kernel/content/format/cms_formats.e | 3 +- .../handler/basic_auth_logoff_handler.e | 2 +- src/modules/cms_module.e | 5 + .../modules/node}/cms_content_type.e | 14 +- src/modules/node/cms_node_api.e | 68 +++++- .../node/content_type/cms_page_content_type.e | 29 +++ .../cms_content_type_webform_manager.e | 44 ++++ .../cms_page_content_type_webform_manager.e | 160 +++++++++++++ src/modules/node/handler/node_form_response.e | 214 ++++++++++++++++++ src/modules/node/handler/node_handler.e | 183 ++++++++++----- src/modules/node/handler/node_response.e | 86 +++++++ src/modules/node/handler/wsf_cms_theme.e | 59 +++++ src/modules/node/handler/wsf_null_theme.e | 30 +++ src/modules/node/node_module.e | 47 ++-- src/persistence/cms_proxy_storage_sql.e | 6 +- src/persistence/cms_storage.e | 21 +- src/persistence/cms_storage_builder.e | 1 + src/persistence/cms_storage_null.e | 73 +----- .../persistence/cms_storage_sql_builder.e | 11 +- ...{cms_storage_sql.e => cms_storage_sql_i.e} | 12 +- src/persistence/core/cms_core_storage_i.e | 47 ++++ src/persistence/core/cms_core_storage_sql_i.e | 138 +++++++++++ ...ms_node_storage.e => cms_node_storage_i.e} | 5 +- src/persistence/node/cms_node_storage_null.e | 106 +++++++++ src/persistence/node/cms_node_storage_sql.e | 44 ++-- .../cms_node_storage_sql_page_extension.e | 1 + ...ms_user_storage.e => cms_user_storage_i.e} | 4 +- ...storage_sql.e => cms_user_storage_sql_i.e} | 10 +- src/service/cms_api.e | 70 +++++- src/service/handler/cms_handler.e | 29 ++- src/service/logger/cms_logger.e | 12 +- src/service/response/cms_response.e | 28 ++- .../bad_request_error_cms_response.e | 0 .../error/forbidden_error_cms_response.e | 38 ++++ .../internal_server_error_cms_response.e | 0 .../not_found_error_cms_response.e | 0 .../not_implemented_error_cms_response.e | 0 .../cms_forbidden_response_message.e | 0 .../cms_redirection_response_message.e | 0 .../cms_unauthorized_response_message.e | 0 src/theme/cms_html_options.e | 50 ++++ src/theme/cms_theme.e | 19 +- 59 files changed, 1693 insertions(+), 414 deletions(-) create mode 100644 library/model/src/log/cms_log.e delete mode 100644 library/persistence/mysql/src/cms_node_storage_mysql.e delete mode 100644 library/persistence/sqlite/src/cms_node_storage_sqlite.e delete mode 100644 library/persistence/sqlite/src/cms_user_storage_sqlite.e rename {library/model/src/content => src/modules/node}/cms_content_type.e (64%) create mode 100644 src/modules/node/handler/cms_content_type_webform_manager.e create mode 100644 src/modules/node/handler/cms_page_content_type_webform_manager.e create mode 100644 src/modules/node/handler/node_form_response.e create mode 100644 src/modules/node/handler/node_response.e create mode 100644 src/modules/node/handler/wsf_cms_theme.e create mode 100644 src/modules/node/handler/wsf_null_theme.e rename library/persistence/mysql/src/cms_user_storage_mysql.e => src/persistence/cms_storage_sql_builder.e (51%) rename src/persistence/{cms_storage_sql.e => cms_storage_sql_i.e} (96%) create mode 100644 src/persistence/core/cms_core_storage_i.e create mode 100644 src/persistence/core/cms_core_storage_sql_i.e rename src/persistence/node/{cms_node_storage.e => cms_node_storage_i.e} (97%) create mode 100644 src/persistence/node/cms_node_storage_null.e rename src/persistence/user/{cms_user_storage.e => cms_user_storage_i.e} (97%) rename src/persistence/user/{cms_user_storage_sql.e => cms_user_storage_sql_i.e} (99%) rename src/service/response/{ => error}/bad_request_error_cms_response.e (100%) create mode 100644 src/service/response/error/forbidden_error_cms_response.e rename src/service/response/{ => error}/internal_server_error_cms_response.e (100%) rename src/service/response/{ => error}/not_found_error_cms_response.e (100%) rename src/service/response/{ => error}/not_implemented_error_cms_response.e (100%) rename src/service/response/{ => message}/cms_forbidden_response_message.e (100%) rename src/service/response/{ => message}/cms_redirection_response_message.e (100%) rename src/service/response/{ => message}/cms_unauthorized_response_message.e (100%) create mode 100644 src/theme/cms_html_options.e diff --git a/examples/demo/modules/demo/cms_demo_module.e b/examples/demo/modules/demo/cms_demo_module.e index f038073..347c48c 100644 --- a/examples/demo/modules/demo/cms_demo_module.e +++ b/examples/demo/modules/demo/cms_demo_module.e @@ -88,7 +88,7 @@ feature -- Handler local sql: STRING do - if attached {CMS_STORAGE_SQL} a_api.storage as sql_db then + if attached {CMS_STORAGE_SQL_I} a_api.storage as sql_db then sql_db.sql_query ("select count(*) from tb_demo;", Void) if sql_db.has_error then -- Initialize db for demo module diff --git a/examples/demo/site/scripts/core.sql b/examples/demo/site/scripts/core.sql index f1b4ba0..0c26f42 100644 --- a/examples/demo/site/scripts/core.sql +++ b/examples/demo/site/scripts/core.sql @@ -1,4 +1,5 @@ BEGIN; + CREATE TABLE "users"( "uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0), "name" VARCHAR(100) NOT NULL, @@ -30,4 +31,21 @@ CREATE TABLE "role_permissions"( "module" VARCHAR(255) ); +CREATE TABLE "logs"( + "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), + "category" VARCHAR(255) NOT NULL, + "level" INTEGER NOT NULL CHECK("level">=1), + "uid" INTEGER, + "message" TEXT NOT NULL, + "info" TEXT, + "link" TEXT, + "date" DATETIME NOT NULL +); + +CREATE TABLE "custom_values"( + "type" VARCHAR(255) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "value" VARCHAR(255) NOT NULL +); + COMMIT; diff --git a/examples/demo/site/scripts/node.sql b/examples/demo/site/scripts/node.sql index 6dc2792..85aa51b 100644 --- a/examples/demo/site/scripts/node.sql +++ b/examples/demo/site/scripts/node.sql @@ -5,8 +5,9 @@ CREATE TABLE "nodes"( "revision" INTEGER, "type" TEXT NOT NULL, "title" VARCHAR(255) NOT NULL, - "summary" TEXT NOT NULL, + "summary" TEXT, "content" MEDIUMTEXT NOT NULL, + "format" VARCHAR(255), "author" INTEGER, "publish" DATETIME, "created" DATETIME NOT NULL, diff --git a/examples/demo/site/themes/bootstrap/assets/css/style.css b/examples/demo/site/themes/bootstrap/assets/css/style.css index 05182b7..45ffaa5 100644 --- a/examples/demo/site/themes/bootstrap/assets/css/style.css +++ b/examples/demo/site/themes/bootstrap/assets/css/style.css @@ -37,6 +37,16 @@ ul.horizontal li { #content { margin-left: 20px; } +#content #highlighted { + position: relative; + border: solid 1px #ddd; + background-color: #ffc; + width: 70%; + left: 15%; + right: 15%; + padding: 5px; + font-style: italic; +} .sidebar { padding: 5px; @@ -51,3 +61,18 @@ ul.horizontal li { width: 250px; float: right; } + +#primary-tabs ul.horizontal { + list-style-type: none; +} +#primary-tabs ul.horizontal li { + display: inline; + padding: 2px 5px; + border: solid 1px #ccf; +} +#primary-tabs ul.horizontal li.active { + border-color: #99f #99f #ddd; + border-style: solid solid none; + border-width: 2px 1px 0; + padding: 2px 7px 1px; +} diff --git a/examples/demo/site/themes/bootstrap/assets/scss/style.scss b/examples/demo/site/themes/bootstrap/assets/scss/style.scss index 3783a7a..a6b5475 100644 --- a/examples/demo/site/themes/bootstrap/assets/scss/style.scss +++ b/examples/demo/site/themes/bootstrap/assets/scss/style.scss @@ -65,3 +65,19 @@ ul.horizontal { float: right; } } +#primary-tabs { + ul.horizontal { + list-style-type: none; + li { + display: inline; + padding: 2px 5px; + border: solid 1px #ccf; + } + li.active { + border-color: #99f #99f #ddd; + border-style: solid solid none; + border-width: 2px 1px 0; + padding: 2px 7px 1px; + } + } +} diff --git a/library/model/src/log/cms_log.e b/library/model/src/log/cms_log.e new file mode 100644 index 0000000..c50d932 --- /dev/null +++ b/library/model/src/log/cms_log.e @@ -0,0 +1,127 @@ +note + description: "Summary description for {CMS_LOG}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_LOG + +create + make + +feature {NONE} -- Initialization + + make (a_category: like category; a_message: like message; a_level: like level; a_date: detachable like date) + do + category := a_category + message := a_message + set_level (a_level) + if a_date = Void then + create date.make_now_utc + else + date := a_date + end + end + + make_with_id (a_id: like id; a_category: like category; a_message: like message; a_level: like level; a_date: detachable like date) + do + id := a_id + make (a_category, a_message, a_level, a_date) + end + +feature -- Access + + id: INTEGER + -- Unique identifier of Current. + + category: READABLE_STRING_8 + -- Associated title (optional). + + message: READABLE_STRING_8 + -- Log message + + level: INTEGER + -- Severity level + + level_name: STRING + do + Result := level_to_string (level) + end + + info: detachable READABLE_STRING_8 + + link: detachable CMS_LINK + + date: DATE_TIME + +feature -- status report + + has_id: BOOLEAN + do + Result := id > 0 + end + +feature -- Change + + set_id (a_id: like id) + require + not has_id + do + id := a_id + end + + set_level (a_level: like level) + do + if a_level = 0 then + level := level_notice + else + level := a_level + end + end + + set_link (lnk: like link) + do + link := lnk + end + + set_info (inf: like info) + do + info := inf + end + +feature -- Constants + + level_to_string (a_level: INTEGER): STRING + do + inspect a_level + when level_emergency then + Result := "emergency" + when level_alert then + Result := "alert" + when level_critical then + Result := "critical" + when level_error then + Result := "error" + when level_warning then + Result := "warning" + when level_notice then + Result := "notice" + when level_info then + Result := "info" + when level_debug then + Result := "debug" + else + Result := "level-" + a_level.out + end + end + + level_emergency: INTEGER = 1 + level_alert: INTEGER = 2 + level_critical: INTEGER = 3 + level_error: INTEGER = 4 + level_warning: INTEGER = 5 + level_notice: INTEGER = 6 + level_info: INTEGER = 7 + level_debug: INTEGER = 8 + +end diff --git a/library/persistence/implementation/store/cms_storage_store_sql.e b/library/persistence/implementation/store/cms_storage_store_sql.e index edfbac2..09c1150 100644 --- a/library/persistence/implementation/store/cms_storage_store_sql.e +++ b/library/persistence/implementation/store/cms_storage_store_sql.e @@ -10,7 +10,7 @@ deferred class inherit CMS_STORAGE - CMS_STORAGE_SQL + CMS_STORAGE_SQL_I feature {NONE} -- Initialization diff --git a/library/persistence/mysql/src/cms_node_storage_mysql.e b/library/persistence/mysql/src/cms_node_storage_mysql.e deleted file mode 100644 index ee0302a..0000000 --- a/library/persistence/mysql/src/cms_node_storage_mysql.e +++ /dev/null @@ -1,79 +0,0 @@ -note - description: "Summary description for {CMS_NODE_STORAGE_MYSQL}." - author: "" - date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" - revision: "$Revision: 96542 $" - -deferred class - CMS_NODE_STORAGE_MYSQL - -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 - if attached ic.item as l_node then - Result.force (l_node) - end - 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 ic loop - if attached ic.item as l_node then - Result.force (l_node) - end - end - end - -feature -- Access: iterator - - nodes_iterator: DATABASE_ITERATION_CURSOR [detachable CMS_NODE] - -- List of nodes. - local - l_parameters: STRING_TABLE [ANY] - do - error_handler.reset - write_information_log (generator + ".nodes_iterator") - create l_parameters.make (0) - sql_query (sql_select_nodes, l_parameters) - create Result.make (db_handler, agent fetch_node) - sql_post_execution - end - - recent_nodes_iterator (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [detachable CMS_NODE] - -- The most recent `a_rows'. - local - l_parameters: STRING_TABLE [ANY] - l_query: STRING - do - -- FIXME: check implementation... - error_handler.reset - write_information_log (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 (sql_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/mysql/src/cms_storage_mysql.e b/library/persistence/mysql/src/cms_storage_mysql.e index c30ad02..2cc5535 100644 --- a/library/persistence/mysql/src/cms_storage_mysql.e +++ b/library/persistence/mysql/src/cms_storage_mysql.e @@ -7,13 +7,11 @@ class CMS_STORAGE_MYSQL inherit - CMS_STORAGE - CMS_STORAGE_STORE_SQL - CMS_USER_STORAGE_MYSQL + CMS_CORE_STORAGE_SQL_I - CMS_NODE_STORAGE_MYSQL + CMS_USER_STORAGE_SQL_I REFACTORING_HELPER diff --git a/library/persistence/mysql/src/cms_storage_mysql_builder.e b/library/persistence/mysql/src/cms_storage_mysql_builder.e index 585fc81..943e1af 100644 --- a/library/persistence/mysql/src/cms_storage_mysql_builder.e +++ b/library/persistence/mysql/src/cms_storage_mysql_builder.e @@ -10,7 +10,7 @@ class CMS_STORAGE_MYSQL_BUILDER inherit - CMS_STORAGE_BUILDER + CMS_STORAGE_SQL_BUILDER create make diff --git a/library/persistence/sqlite/src/cms_node_storage_sqlite.e b/library/persistence/sqlite/src/cms_node_storage_sqlite.e deleted file mode 100644 index 60d6d54..0000000 --- a/library/persistence/sqlite/src/cms_node_storage_sqlite.e +++ /dev/null @@ -1,77 +0,0 @@ -note - description: "Summary description for {CMS_NODE_STORAGE_SQLITE}." - author: "" - date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" - revision: "$Revision: 96616 $" - -deferred class - CMS_NODE_STORAGE_SQLITE - -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 - if attached ic.item as l_node then - Result.force (l_node) - end - 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 ic loop - if attached ic.item as l_node then - Result.force (l_node) - end - end - end - -feature -- Access: iterator - - nodes_iterator: DATABASE_ITERATION_CURSOR [detachable CMS_NODE] - -- List of nodes. - local - l_parameters: STRING_TABLE [ANY] - do - error_handler.reset - write_information_log (generator + ".nodes_iterator") - create l_parameters.make (0) - sql_query (sql_select_nodes, l_parameters) - create Result.make (db_handler, agent fetch_node) - end - - recent_nodes_iterator (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [detachable CMS_NODE] - -- The most recent `a_rows'. - local - l_parameters: STRING_TABLE [ANY] - l_query: STRING - do - -- FIXME: check implementation... - error_handler.reset - write_information_log (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 (sql_select_recent_nodes) - sql_query (l_query, l_parameters) - create Result.make (db_handler, agent fetch_node) - end - -end diff --git a/library/persistence/sqlite/src/cms_storage_sqlite.e b/library/persistence/sqlite/src/cms_storage_sqlite.e index 6444c7c..78b9942 100644 --- a/library/persistence/sqlite/src/cms_storage_sqlite.e +++ b/library/persistence/sqlite/src/cms_storage_sqlite.e @@ -7,13 +7,11 @@ class CMS_STORAGE_SQLITE inherit - CMS_STORAGE - CMS_STORAGE_STORE_SQL - CMS_USER_STORAGE_SQLITE + CMS_CORE_STORAGE_SQL_I - CMS_NODE_STORAGE_SQLITE + CMS_USER_STORAGE_SQL_I REFACTORING_HELPER @@ -27,5 +25,5 @@ feature -- Status report do Result := has_user end - + end diff --git a/library/persistence/sqlite/src/cms_storage_sqlite_builder.e b/library/persistence/sqlite/src/cms_storage_sqlite_builder.e index efdad97..01b4241 100644 --- a/library/persistence/sqlite/src/cms_storage_sqlite_builder.e +++ b/library/persistence/sqlite/src/cms_storage_sqlite_builder.e @@ -10,7 +10,7 @@ class CMS_STORAGE_SQLITE_BUILDER inherit - CMS_STORAGE_BUILDER + CMS_STORAGE_SQL_BUILDER create make @@ -66,6 +66,11 @@ feature -- Factory r.add_permission ("create page") r.add_permission ("edit page") a_storage.save_user_role (r) + + -- Test custom value + + a_storage.set_custom_value ("abc", "123", "test") + a_storage.set_custom_value ("abc", "OK", "test") end end diff --git a/library/persistence/sqlite/src/cms_user_storage_sqlite.e b/library/persistence/sqlite/src/cms_user_storage_sqlite.e deleted file mode 100644 index 68ccb3a..0000000 --- a/library/persistence/sqlite/src/cms_user_storage_sqlite.e +++ /dev/null @@ -1,14 +0,0 @@ -note - description: "Summary description for {CMS_USER_STORAGE_SQLITE}." - author: "" - date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" - revision: "$Revision: 96542 $" - -deferred class - CMS_USER_STORAGE_SQLITE - -inherit - CMS_USER_STORAGE_SQL - - -end diff --git a/src/kernel/content/cms_block.e b/src/kernel/content/cms_block.e index 6ac240b..ac300f2 100644 --- a/src/kernel/content/cms_block.e +++ b/src/kernel/content/cms_block.e @@ -20,6 +20,9 @@ feature -- Access deferred end + html_options: detachable CMS_HTML_OPTIONS + -- Optional addition html options. + feature -- status report is_enabled: BOOLEAN @@ -31,6 +34,34 @@ feature -- status report deferred end +feature -- Element change + + add_css_class (a_class: READABLE_STRING_8) + -- Add css class `a_class'. + local + opts: like html_options + do + opts := html_options + if opts = Void then + create opts + html_options := opts + end + opts.add_css_class (a_class) + end + + remove_css_class (a_class: READABLE_STRING_GENERAL) + -- Remove css class `a_class'. + local + opts: like html_options + do + opts := html_options + if opts = Void then + create opts + html_options := opts + end + opts.remove_css_class (a_class) + end + feature -- Conversion to_html (a_theme: CMS_THEME): STRING_8 diff --git a/src/kernel/content/cms_content_block.e b/src/kernel/content/cms_content_block.e index b084a89..bf2ef2a 100644 --- a/src/kernel/content/cms_content_block.e +++ b/src/kernel/content/cms_content_block.e @@ -46,7 +46,7 @@ feature -- Access feature -- Status report - is_raw: BOOLEAN + is_raw: BOOLEAN assign set_is_raw -- Is raw? -- If True, do not get wrapped it with block specific div diff --git a/src/kernel/content/cms_menu_block.e b/src/kernel/content/cms_menu_block.e index d3a5cf3..0b4a306 100644 --- a/src/kernel/content/cms_menu_block.e +++ b/src/kernel/content/cms_menu_block.e @@ -31,9 +31,10 @@ feature -- Access feature -- Status report - is_horizontal: BOOLEAN + is_horizontal: BOOLEAN assign set_is_horizontal + -- Is horizontal layout for the menu? - is_raw: BOOLEAN = False + is_raw: BOOLEAN assign set_is_raw -- feature -- Element change @@ -52,11 +53,23 @@ feature -- Element change title := a_title end + set_is_horizontal (b: BOOLEAN) + -- Set `is_horizontal' to `b'. + do + is_horizontal := b + end + + set_is_raw (b: BOOLEAN) + -- Set `is_raw' to `b'. + do + is_raw := b + end + feature -- Conversion to_html (a_theme: CMS_THEME): STRING_8 do - Result := a_theme.menu_html (menu, is_horizontal) + Result := a_theme.menu_html (menu, is_horizontal, html_options) end end diff --git a/src/kernel/content/format/cms_formats.e b/src/kernel/content/format/cms_formats.e index b4711c7..d6697e1 100644 --- a/src/kernel/content/format/cms_formats.e +++ b/src/kernel/content/format/cms_formats.e @@ -9,9 +9,8 @@ class feature -- Access - item (a_name: detachable READABLE_STRING_GENERAL): CONTENT_FORMAT + item (a_name: detachable READABLE_STRING_GENERAL): detachable CONTENT_FORMAT do - Result := default_format if a_name /= Void then across all_formats as c diff --git a/src/modules/basic_auth/handler/basic_auth_logoff_handler.e b/src/modules/basic_auth/handler/basic_auth_logoff_handler.e index 432376e..40ca330 100644 --- a/src/modules/basic_auth/handler/basic_auth_logoff_handler.e +++ b/src/modules/basic_auth/handler/basic_auth_logoff_handler.e @@ -51,7 +51,7 @@ feature -- HTTP Methods api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) if attached req.query_parameter ("prompt") as l_prompt then unset_current_user (req) - send_access_denied (res) + send_access_denied_message (res) else create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) unset_current_user (req) diff --git a/src/modules/cms_module.e b/src/modules/cms_module.e index 2964057..58b048d 100644 --- a/src/modules/cms_module.e +++ b/src/modules/cms_module.e @@ -36,6 +36,11 @@ feature {CMS_API} -- Module Initialization -- Redefine to process specific module initialization. end +feature {CMS_API} -- Access: API + + module_api: detachable CMS_MODULE_API + -- Eventual module api. + feature {CMS_API} -- Module management is_installed (api: CMS_API): BOOLEAN diff --git a/library/model/src/content/cms_content_type.e b/src/modules/node/cms_content_type.e similarity index 64% rename from library/model/src/content/cms_content_type.e rename to src/modules/node/cms_content_type.e index fd5e175..675a470 100644 --- a/library/model/src/content/cms_content_type.e +++ b/src/modules/node/cms_content_type.e @@ -26,10 +26,22 @@ feature -- Access deferred end +feature -- Access + + available_formats: LIST [CONTENT_FORMAT] + -- Available formats for Current type. + deferred + end + feature -- Factory + new_node_with_title (a_title: READABLE_STRING_32; a_partial_node: detachable CMS_NODE): like new_node + -- New node with `a_title' and fill from partial `a_partial_node' if set. + deferred + end + new_node (a_partial_node: detachable CMS_NODE): CMS_NODE - -- New node based on partial `a_partial_node', or from none. + -- New node based on partial `a_partial_node' if set. deferred end diff --git a/src/modules/node/cms_node_api.e b/src/modules/node/cms_node_api.e index 51f11f0..c2ca691 100644 --- a/src/modules/node/cms_node_api.e +++ b/src/modules/node/cms_node_api.e @@ -25,16 +25,30 @@ feature {NONE} -- Implementation -- do Precursor + if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then + create {CMS_NODE_STORAGE_SQL} node_storage.make (l_storage_sql) + else + create {CMS_NODE_STORAGE_NULL} node_storage.make + end initialize_content_types end initialize_content_types -- Initialize content type system. + local + ct: CMS_PAGE_CONTENT_TYPE do create content_types.make (1) - add_content_type (create {CMS_PAGE_CONTENT_TYPE}) + create content_type_webform_managers.make (1) + create ct + add_content_type (ct) + add_content_type_webform_manager (create {CMS_PAGE_CONTENT_TYPE_WEBFORM_MANAGER}.make (ct)) end +feature {NODE_MODULE} -- Access nodes storage. + + node_storage: CMS_NODE_STORAGE_I + feature -- Content type content_types: ARRAYED_LIST [CMS_CONTENT_TYPE] @@ -59,6 +73,30 @@ feature -- Content type end end +feature -- Content type webform + + content_type_webform_managers: ARRAYED_LIST [CMS_CONTENT_TYPE_WEBFORM_MANAGER] + -- Available content types + + add_content_type_webform_manager (a_type: CMS_CONTENT_TYPE_WEBFORM_MANAGER) + do + content_type_webform_managers.force (a_type) + end + + content_type_webform_manager (a_name: READABLE_STRING_GENERAL): detachable CMS_CONTENT_TYPE_WEBFORM_MANAGER + do + across + content_type_webform_managers as ic + until + Result /= Void + loop + Result := ic.item + if not a_name.is_case_insensitive_equal (Result.name) then + Result := Void + end + end + end + feature -- URL new_content_path (ct: detachable CMS_CONTENT_TYPE): STRING @@ -91,19 +129,19 @@ feature -- Access: Node nodes_count: INTEGER_64 do - Result := storage.nodes_count + Result := node_storage.nodes_count end nodes: LIST [CMS_NODE] -- List of nodes. do - Result := storage.nodes + Result := node_storage.nodes end recent_nodes (a_offset, a_rows: INTEGER): LIST [CMS_NODE] -- List of the `a_rows' most recent nodes starting from `a_offset'. do - Result := storage.recent_nodes (a_offset, a_rows) + Result := node_storage.recent_nodes (a_offset, a_rows) end node (a_id: INTEGER_64): detachable CMS_NODE @@ -112,7 +150,7 @@ feature -- Access: Node debug ("refactor_fixme") fixme ("Check preconditions") end - Result := full_node (storage.node_by_id (a_id)) + Result := full_node (node_storage.node_by_id (a_id)) end full_node (a_node: detachable CMS_NODE): detachable CMS_NODE @@ -122,7 +160,7 @@ feature -- Access: Node if attached {CMS_PARTIAL_NODE} a_node as l_partial_node then if attached content_type (l_partial_node.content_type) as ct then Result := ct.new_node (l_partial_node) - storage.fill_node (Result) + node_storage.fill_node (Result) else Result := l_partial_node end @@ -147,26 +185,32 @@ feature -- Access: Node feature -- Change: Node + save_node (a_node: CMS_NODE) + -- Save `a_node'. + do + node_storage.save_node (a_node) + end + new_node (a_node: CMS_NODE) -- Add a new node `a_node' require no_id: not a_node.has_id do - storage.new_node (a_node) + node_storage.new_node (a_node) end delete_node (a_node: CMS_NODE) -- Delete `a_node'. do if a_node.has_id then - storage.delete_node (a_node) + node_storage.delete_node (a_node) end end update_node (a_node: CMS_NODE) -- Update node `a_node' data. do - storage.update_node (a_node) + node_storage.update_node (a_node) end -- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) @@ -175,7 +219,7 @@ feature -- Change: Node -- debug ("refactor_fixme") -- fixme ("Check preconditions") -- end --- storage.update_node_title (a_user_id, a_node_id, a_title) +-- node_storage.update_node_title (a_user_id, a_node_id, a_title) -- end -- update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) @@ -184,7 +228,7 @@ feature -- Change: Node -- debug ("refactor_fixme") -- fixme ("Check preconditions") -- end --- storage.update_node_summary (a_user_id, a_node_id, a_summary) +-- node_storage.update_node_summary (a_user_id, a_node_id, a_summary) -- end -- update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) @@ -193,7 +237,7 @@ feature -- Change: Node -- debug ("refactor_fixme") -- fixme ("Check preconditions") -- end --- storage.update_node_content (a_user_id, a_node_id, a_content) +-- node_storage.update_node_content (a_user_id, a_node_id, a_content) -- end diff --git a/src/modules/node/content_type/cms_page_content_type.e b/src/modules/node/content_type/cms_page_content_type.e index 588dda3..b9c198b 100644 --- a/src/modules/node/content_type/cms_page_content_type.e +++ b/src/modules/node/content_type/cms_page_content_type.e @@ -9,6 +9,20 @@ class inherit CMS_CONTENT_TYPE + redefine + default_create + end + +feature {NONE} -- Initialization + + default_create + do + Precursor + create {ARRAYED_LIST [like available_formats.item]} available_formats.make (3) + available_formats.extend (create {PLAIN_TEXT_CONTENT_FORMAT}) + available_formats.extend (create {FILTERED_HTML_CONTENT_FORMAT}) + available_formats.extend (create {FULL_HTML_CONTENT_FORMAT}) + end feature -- Access @@ -21,8 +35,23 @@ feature -- Access description: STRING_32 = "Use basic pages for your content, such as an 'About us' page." -- Optional description +feature -- Access + + available_formats: LIST [CONTENT_FORMAT] + -- Available formats for Current type. + feature -- Factory + new_node_with_title (a_title: READABLE_STRING_32; a_partial_node: detachable CMS_NODE): like new_node + -- New node with `a_title' and fill from partial `a_partial_node' if set. + do + create Result.make (a_title) + if a_partial_node /= Void then + Result.import_node (a_partial_node) + Result.set_title (a_title) + end + end + new_node (a_partial_node: detachable CMS_NODE): CMS_PAGE -- New node based on partial `a_partial_node', or from none. do diff --git a/src/modules/node/handler/cms_content_type_webform_manager.e b/src/modules/node/handler/cms_content_type_webform_manager.e new file mode 100644 index 0000000..2c982f2 --- /dev/null +++ b/src/modules/node/handler/cms_content_type_webform_manager.e @@ -0,0 +1,44 @@ +note + description: "Summary description for {CMS_CONTENT_TYPE_WEBFORM_MANAGER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_CONTENT_TYPE_WEBFORM_MANAGER + +feature {NONE} -- Initialization + + make (a_type: like content_type) + do + content_type := a_type + end + +feature -- Access + + content_type: CMS_CONTENT_TYPE + -- Associated content type. + + name: READABLE_STRING_8 + do + Result := content_type.name + end + +feature -- Forms ... + + fill_edit_form (response: NODE_RESPONSE; a_form: WSF_FORM; a_node: detachable CMS_NODE) + deferred + end + + new_node (response: NODE_RESPONSE; a_form_data: WSF_FORM_DATA; a_node: detachable CMS_NODE): CMS_NODE + deferred +-- Result := content_type.new_node (a_node) + end + + change_node (response: NODE_RESPONSE; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE) + require + a_node.has_id + deferred + end + +end diff --git a/src/modules/node/handler/cms_page_content_type_webform_manager.e b/src/modules/node/handler/cms_page_content_type_webform_manager.e new file mode 100644 index 0000000..11ecb79 --- /dev/null +++ b/src/modules/node/handler/cms_page_content_type_webform_manager.e @@ -0,0 +1,160 @@ +note + description: "Summary description for {CMS_PAGE_CONTENT_TYPE_WEBFORM_MANAGER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_PAGE_CONTENT_TYPE_WEBFORM_MANAGER + +inherit + CMS_CONTENT_TYPE_WEBFORM_MANAGER + redefine + content_type + end + +create + make + +feature -- Access + + content_type: CMS_PAGE_CONTENT_TYPE + -- Associated content type. + +feature -- Forms ... + + fill_edit_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) + local + ti: WSF_FORM_TEXT_INPUT + fset: WSF_FORM_FIELD_SET + ta: WSF_FORM_TEXTAREA + tselect: WSF_FORM_SELECT + opt: WSF_FORM_SELECT_OPTION + do + create ti.make ("title") + ti.set_label ("Title") + ti.set_size (70) + if a_node /= Void then + ti.set_text_value (a_node.title) + end + ti.set_is_required (True) + f.extend (ti) + + f.extend_html_text ("
") + + create ta.make ("body") + ta.set_rows (10) + ta.set_cols (70) + if a_node /= Void then + ta.set_text_value (a_node.content) + end +-- ta.set_label ("Body") + ta.set_description ("This is the main content") + ta.set_is_required (False) + + create fset.make + fset.set_legend ("Body") + fset.extend (ta) + + fset.extend_html_text ("
") + + create tselect.make ("format") + tselect.set_label ("Body's format") + tselect.set_is_required (True) + across + content_type.available_formats as c + loop + create opt.make (c.item.name, c.item.title) + if attached c.item.html_help as f_help then + opt.set_description ("
    " + f_help + "
") + end + tselect.add_option (opt) + end + if a_node /= Void and then attached a_node.format as l_format then + tselect.set_text_by_value (l_format) + end + + fset.extend (tselect) + + f.extend (fset) + + end + + change_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: like new_node) + local + b: detachable READABLE_STRING_8 + f: detachable CONTENT_FORMAT + do + if attached fd.integer_item ("id") as l_id and then l_id > 0 then + check a_node.id = l_id end + end + if attached fd.string_item ("title") as l_title then + a_node.set_title (l_title) + end + + if attached fd.string_item ("body") as l_body then + b := l_body + end + if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then + f := f_format + elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then + f := f_format + else + f := response.api.formats.default_format + end + if b /= Void then + a_node.set_content (b, Void, f.name) -- FIXME: summary + end + end + + new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): CMS_PAGE + -- + local + b: detachable READABLE_STRING_8 + f: detachable CONTENT_FORMAT + l_node: detachable like new_node + do + l_node := a_node + if attached fd.integer_item ("id") as l_id and then l_id > 0 then + if l_node /= Void then + check l_node.id = l_id end + else + if attached {like new_node} response.node_api.node (l_id) as n then + l_node := n + else + -- FIXME: Error + end + end + end + if attached fd.string_item ("title") as l_title then + if l_node = Void then + l_node := content_type.new_node (Void) + l_node.set_title (l_title) + else + l_node.set_title (l_title) + end + else + if l_node = Void then + l_node := content_type.new_node_with_title ("...", Void) + end + end + l_node.set_author (response.user) + + if attached fd.string_item ("body") as l_body then + b := l_body + end + if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then + f := f_format + elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then + f := f_format + else + f := response.api.formats.default_format + end + if b /= Void then + l_node.set_content (b, Void, f.name) + end + Result := l_node + end + + +end diff --git a/src/modules/node/handler/node_form_response.e b/src/modules/node/handler/node_form_response.e new file mode 100644 index 0000000..7569eeb --- /dev/null +++ b/src/modules/node/handler/node_form_response.e @@ -0,0 +1,214 @@ +note + description: "Summary description for {NODE_FORM_RESPONSE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + NODE_FORM_RESPONSE + +inherit + NODE_RESPONSE + redefine + make, + initialize + end + +create + make + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) + do + create {WSF_NULL_THEME} wsf_theme.make + Precursor (req, res, a_api, a_node_api) + end + + initialize + do + Precursor + create {WSF_CMS_THEME} wsf_theme.make (Current, theme) + end + + wsf_theme: WSF_THEME + +feature -- Execution + + process + -- Computed response message. + local + b: STRING_8 + f: like edit_form + fd: detachable WSF_FORM_DATA + nid: INTEGER_64 + do + create b.make_empty + nid := node_id_path_parameter (request) + if + nid > 0 and then + attached node_api.node (nid) as l_node + then + if attached node_api.content_type (l_node.content_type) as l_type then + if has_permission ("edit " + l_type.name) then + f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) + 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 + + set_title ("Edit #" + 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) + + f.append_to_html (wsf_theme, b) + else + b.append ("

Access denied

") + end + else + set_title ("Unknown node") + end + else + set_title ("Create new content ...") + b.append ("
    ") + across + node_api.content_types as c + loop + if has_permission ("create " + c.item.name) then + b.append ("
  • " + link (c.item.name, "/node/add/" + c.item.name, Void)) + if attached c.item.description as d then + b.append ("
    " + d + "
    ") + end + b.append ("
  • ") + end + end + b.append ("
") + end + set_main_content (b) + end + +feature -- Form + + edit_form_validate (fd: WSF_FORM_DATA; b: STRING) + local + l_preview: BOOLEAN + l_format: detachable CONTENT_FORMAT + do + l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview") + if l_preview then + b.append ("Preview
") + if attached fd.string_item ("format") as s_format and then attached api.format (s_format) as f_format then + l_format := f_format + end + if attached fd.string_item ("title") as l_title then + b.append ("Title:
" + html_encoded (l_title) + "
") + end + if attached fd.string_item ("body") as l_body then + b.append ("Body:
") + if l_format /= Void then + b.append (l_format.formatted_output (l_body)) + else + b.append (html_encoded (l_body)) + end + b.append ("
") + end + b.append ("
") + end + end + + edit_form_submit (fd: WSF_FORM_DATA; a_node: detachable CMS_NODE; a_type: CMS_CONTENT_TYPE; b: STRING) + local + l_preview: BOOLEAN + l_node: detachable CMS_NODE + s: STRING + do + l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview") + if not l_preview then + debug ("cms") + across + fd as c + loop + b.append ("
  • " + html_encoded (c.key) + "=") + if attached c.item as v then + b.append (html_encoded (v.string_representation)) + end + b.append ("
  • ") + end + end + if a_node /= Void then + l_node := a_node + change_node (a_type, fd, a_node) + s := "modified" + else + l_node := new_node (a_type, fd, Void) + s := "created" + 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)) + 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.") + 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_CONTENT_TYPE): CMS_FORM + local + f: CMS_FORM + ts: WSF_FORM_SUBMIT_INPUT + th: WSF_FORM_HIDDEN_INPUT + do + create f.make (a_url, a_name) + + create th.make ("node-id") + if a_node /= Void then + th.set_text_value (a_node.id.out) + else + th.set_text_value ("0") + end + f.extend (th) + + fill_edit_form (a_type, f, a_node) + + f.extend_html_text ("
    ") + + create ts.make ("op") + ts.set_default_value ("Save") + f.extend (ts) + + create ts.make ("op") + ts.set_default_value ("Preview") + f.extend (ts) + + Result := f + end + + new_node (a_content_type: CMS_CONTENT_TYPE; a_form_data: WSF_FORM_DATA; a_node: detachable CMS_NODE): CMS_NODE + do + if attached node_api.content_type_webform_manager (a_content_type.name) as wf then + Result := wf.new_node (Current, a_form_data, a_node) + else + Result := a_content_type.new_node (a_node) + end + end + + change_node (a_content_type: CMS_CONTENT_TYPE; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE) + do + if attached node_api.content_type_webform_manager (a_content_type.name) as wf then + wf.change_node (Current, a_form_data, a_node) + end + end + + fill_edit_form (a_content_type: CMS_CONTENT_TYPE; a_form: WSF_FORM; a_node: detachable CMS_NODE) + do + if attached node_api.content_type_webform_manager (a_content_type.name) as wf then + wf.fill_edit_form (Current, a_form, a_node) + end + end + +end diff --git a/src/modules/node/handler/node_handler.e b/src/modules/node/handler/node_handler.e index 5eb9cb3..8059961 100644 --- a/src/modules/node/handler/node_handler.e +++ b/src/modules/node/handler/node_handler.e @@ -1,5 +1,5 @@ note - description: "CMS handler for a node." + description: "handler for CMS node in the CMS interface." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" @@ -64,16 +64,34 @@ feature -- HTTP Methods l_page: CMS_RESPONSE s: STRING hdate: HTTP_DATE + lnk: CMS_LOCAL_LINK + l_node: detachable CMS_NODE + l_nid: INTEGER_64 + edit_response: NODE_FORM_RESPONSE do - -- Existing node - if attached {WSF_STRING} req.path_parameter ("id") as l_id then - if - l_id.is_integer and then - attached {CMS_NODE} node_api.node (l_id.value.to_integer_64) as l_node - then - -- FIXME: there is a mix between CMS interface and API interface here. + if req.path_info.ends_with_general ("/edit") then + create edit_response.make (req, res, api, node_api) + edit_response.execute + else + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer then + l_nid := l_id.value.to_integer_64 + end + end + if l_nid > 0 then + l_node := node_api.node (l_nid) + end + if l_node /= Void then + -- FIXME: allow a per content type display here! create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) l_page.add_variable (l_node, "node") + create lnk.make ("View", node_api.node_path (l_node)) + lnk.set_weight (1) + l_page.add_to_primary_tabs (lnk) + create lnk.make ("Edit", node_api.node_path (l_node) + "/edit") + lnk.set_weight (2) + l_page.add_to_primary_tabs (lnk) create s.make_empty s.append ("
    ") @@ -90,7 +108,12 @@ feature -- HTTP Methods s.append ("
    ") if attached l_node.content as l_content then s.append ("

    ") - s.append (api.formats.item (l_node.format).formatted_output (l_content)) + if attached api.format (l_node.format) as f then + s.append (f.formatted_output (l_content)) + else + s.append (api.formats.default_format.formatted_output (l_content)) + end + s.append ("

    ") end if attached {CMS_PAGE} l_node as l_node_page then @@ -103,74 +126,66 @@ feature -- HTTP Methods l_page.set_title (l_node.title) l_page.set_main_content (s) l_page.execute + elseif l_nid > 0 then --| i.e: l_node = Void + send_not_found (req, res) else - do_error (req, res, l_id) + send_bad_request (req, res) + -- FIXME: should not be accepted + -- Factory + -- create_new_node (req, res) end - else - -- Factory - new_node (req, res) end end do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- local - u_node: CMS_NODE + edit_response: NODE_FORM_RESPONSE do - to_implement ("Check user permissions!!!") - if attached current_user (req) as l_user then - if attached {WSF_STRING} req.path_parameter ("id") as l_id then - if - l_id.is_integer and then - attached node_api.node (l_id.value.to_integer_64) as l_node - then - if attached {WSF_STRING} req.form_parameter ("method") as l_method then - if l_method.is_case_insensitive_equal ("DELETE") then - do_delete (req, res) - elseif l_method.is_case_insensitive_equal ("PUT") then - do_put (req, res) - else - (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute + if req.path_info.ends_with_general ("/edit") then + create edit_response.make (req, res, api, node_api) + edit_response.execute + else + to_implement ("Check user permissions!!!") + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if + l_id.is_integer and then + attached node_api.node (l_id.value.to_integer_64) as l_node + then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("DELETE") then + do_delete (req, res) + elseif l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + process_node_update (req, res, l_user, l_node) + -- Accept this, even if this is not proper usage of POST + -- (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute + end end + else + do_error (req, res, l_id) end else - do_error (req, res, l_id) + process_node_creation (req, res, l_user) end else - -- New node - -- FIXME !!! - if attached {WSF_STRING} req.form_parameter ("type") as p_type then - if attached node_api.content_type (p_type.value) as ct then - end - create {CMS_PARTIAL_NODE} u_node.make_empty (p_type.url_encoded_value) - else - create {CMS_PARTIAL_NODE} u_node.make_empty ("") - end - - update_node_from_data_form (req, u_node) - u_node.set_author (l_user) - node_api.new_node (u_node) - redirect_to (req.absolute_script_url (""), res) + send_access_denied (req, res) end - else - send_access_denied (res) end end do_put (req: WSF_REQUEST; res: WSF_RESPONSE) -- do - if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached node_api.node (l_id.value.to_integer_64) as l_node then - update_node_from_data_form (req, l_node) - l_node.set_author (l_user) - node_api.update_node (l_node) - redirect_to (req.absolute_script_url (""), res) + process_node_update (req, res, l_user, l_node) else do_error (req, res, l_id) end @@ -178,21 +193,25 @@ feature -- HTTP Methods (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute end else - send_access_denied (res) + send_access_denied (req, res) end end do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) -- do - if attached current_user_name (req) then + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached node_api.node (l_id.integer_value) as l_node then - node_api.delete_node (l_node) - res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) + if api.user_has_permission (l_user, "delete " + l_node.content_type) then + node_api.delete_node (l_node) + res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) + else + send_access_denied (req, res) + end else do_error (req, res, l_id) end @@ -200,20 +219,65 @@ feature -- HTTP Methods (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute end else - send_access_denied (res) + send_access_denied (req, res) + end + end + + process_node_creation (req: WSF_REQUEST; res: WSF_RESPONSE; a_user: CMS_USER) + local + u_node: CMS_NODE + do + -- New node + -- FIXME !!! + if + attached {WSF_STRING} req.form_parameter ("type") as p_type and then + attached node_api.content_type (p_type.value) as ct -- should be string 8 value. + then + if api.user_has_permission (a_user, "create " + ct.name) then + u_node := ct.new_node (Void) + -- create {CMS_PARTIAL_NODE} u_node.make_empty (p_type.url_encoded_value) + update_node_from_data_form (req, u_node) + u_node.set_author (a_user) + node_api.new_node (u_node) + if attached {WSF_STRING} req.item ("destination") as p_destination then + redirect_to (req.absolute_script_url (p_destination.url_encoded_value), res) + else + redirect_to (req.absolute_script_url (""), res) + end + else + send_access_denied (req, res) + end + else + do_error (req, res, Void) + end + end + + process_node_update (req: WSF_REQUEST; res: WSF_RESPONSE; a_user: CMS_USER; a_node: CMS_NODE) + do + if api.user_has_permission (a_user, "modify " + a_node.content_type) then + update_node_from_data_form (req, a_node) + a_node.set_author (a_user) + node_api.update_node (a_node) + if attached {WSF_STRING} req.item ("destination") as p_destination then + redirect_to (req.absolute_script_url (p_destination.url_encoded_value), res) + else + redirect_to (req.absolute_script_url (""), res) + end + else + send_access_denied (req, res) end end feature -- Error - do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING) -- Handling error. local l_page: CMS_RESPONSE do create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) l_page.add_variable (req.absolute_script_url (req.path_info), "request") - if a_id.is_integer then + if a_id /= Void and then a_id.is_integer then -- resource not found l_page.add_variable ("404", "code") l_page.set_status_code (404) @@ -227,7 +291,7 @@ feature -- Error feature {NONE} -- Node - new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + create_new_node (req: WSF_REQUEST; res: WSF_RESPONSE) local l_page: CMS_RESPONSE do @@ -235,7 +299,7 @@ feature {NONE} -- Node create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) l_page.execute else - send_access_denied (res) + send_access_denied (req, res) end end @@ -267,4 +331,5 @@ feature -- {NONE} Form data a_node.set_content (l_content, l_summary, l_format) end + end diff --git a/src/modules/node/handler/node_response.e b/src/modules/node/handler/node_response.e new file mode 100644 index 0000000..8779f8c --- /dev/null +++ b/src/modules/node/handler/node_response.e @@ -0,0 +1,86 @@ +note + description: "Summary description for {NODE_RESPONSE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + NODE_RESPONSE + +inherit + CMS_RESPONSE + rename + make as make_response + redefine + custom_prepare + end + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) + do + node_api := a_node_api + make_response (req, res, a_api) + end + +feature -- Access + + node_api: CMS_NODE_API + +feature -- Generation + + custom_prepare (page: CMS_HTML_PAGE) + do + if attached variables as l_variables then + across l_variables as c loop page.register_variable (c.item, c.key) end + end + end + +feature -- Helpers + + node_id_path_parameter (req: WSF_REQUEST): INTEGER_64 + -- Node id passed as path parameter for request `req'. + local + s: STRING + do + if attached {WSF_STRING} req.path_parameter ("id") as p_nid then + s := p_nid.value + if s.is_integer_64 then + Result := s.to_integer_64 + end + end + end + +feature -- Helpers + + user_local_link (u: CMS_USER): CMS_LINK + do + create {CMS_LOCAL_LINK} Result.make (u.name, user_url (u)) + end + + node_local_link (n: CMS_NODE): CMS_LINK + do + create {CMS_LOCAL_LINK} Result.make (n.title, node_url (n)) + end + + user_link (u: CMS_USER): like link + do + Result := link (u.name, "/user/" + u.id.out, Void) + end + + node_link (n: CMS_NODE): like link + do + Result := link (n.title, "/node/" + n.id.out, Void) + end + + user_url (u: CMS_USER): like url + do + Result := url ("/user/" + u.id.out, Void) + end + + node_url (n: CMS_NODE): like url + do + Result := url ("/node/" + n.id.out, Void) + end + +end diff --git a/src/modules/node/handler/wsf_cms_theme.e b/src/modules/node/handler/wsf_cms_theme.e new file mode 100644 index 0000000..34d27da --- /dev/null +++ b/src/modules/node/handler/wsf_cms_theme.e @@ -0,0 +1,59 @@ +note + description: "Summary description for {WSF_CMS_THEME}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_CMS_THEME + +inherit + WSF_THEME + +create + make + +feature {NONE} -- Initialization + + make (res: CMS_RESPONSE; a_cms_theme: CMS_THEME) + do + request := res.request + cms_theme := a_cms_theme + set_response (res) + end + +feature -- Access + + request: WSF_REQUEST + + response: detachable CMS_RESPONSE + + cms_theme: CMS_THEME + +feature -- Element change + + set_response (a_response: CMS_RESPONSE) + do + response := a_response + end + +feature -- Core + + site_url: READABLE_STRING_8 + do + if attached response as r then + Result := r.site_url + else + Result := request.absolute_script_url ("") + end + end + + base_url: detachable READABLE_STRING_8 + -- Base url if any. + do + if attached response as r then + Result := r.base_url + end + end + +end diff --git a/src/modules/node/handler/wsf_null_theme.e b/src/modules/node/handler/wsf_null_theme.e new file mode 100644 index 0000000..15de942 --- /dev/null +++ b/src/modules/node/handler/wsf_null_theme.e @@ -0,0 +1,30 @@ +note + description: " Null theme for void-safety purpose." + date: "$Date$" + revision: "$Revision$" + +class + WSF_NULL_THEME + +inherit + WSF_THEME + +create + make + +feature {NONE} -- Initialization + + make + do + end + +feature -- Core + + site_url: STRING = "" + + base_url: detachable READABLE_STRING_8 + -- Base url if any. + do + end + +end diff --git a/src/modules/node/node_module.e b/src/modules/node/node_module.e index a972a48..200d92e 100644 --- a/src/modules/node/node_module.e +++ b/src/modules/node/node_module.e @@ -9,11 +9,14 @@ class inherit CMS_MODULE + rename + module_api as node_api redefine register_hooks, initialize, is_installed, - install + install, + node_api end CMS_HOOK_MENU_SYSTEM_ALTER @@ -45,31 +48,38 @@ feature {CMS_API} -- Module Initialization local p1,p2: CMS_PAGE ct: CMS_PAGE_CONTENT_TYPE + l_node_api: like node_api do Precursor (api) - if attached {CMS_NODE_STORAGE_SQL} api.storage as l_sql_storage then - l_sql_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_sql_storage)) - if l_sql_storage.sql_table_items_count ("page_nodes") = 0 then - -- Data - -- FIXME: for test purpose, remove later + create l_node_api.make (api) + node_api := l_node_api + + -- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id. + -- For now, we only have extension based on SQL statement. + if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then + l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_sql_node_storage)) + + -- FIXME: the following code is mostly for test purpose/initialization, remove later + if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then if attached api.user_api.user_by_id (1) as u then create ct p1 := ct.new_node (Void) p1.set_title ("Welcome") - p1.set_content ("Welcome, you are using the ROC Eiffel CMS", "Welcome Eiffel ROC user", Void) -- Use default format + p1.set_content ("Welcome, you are using the ROC Eiffel CMS", Void, Void) -- Use default format p1.set_author (u) - api.storage.save_node (p1) + l_sql_node_storage.save_node (p1) p2 := ct.new_node (Void) p2.set_title ("A new page example") - p2.set_content ("This is the content of a page", "This is a new page", Void) -- Use default format + p2.set_content ("This is the content of a page", Void, Void) -- Use default format p2.set_author (u) p2.set_parent (p1) - api.storage.save_node (p2) + l_sql_node_storage.save_node (p2) end end else -- FIXME: maybe provide a default solution based on file system, when no SQL storage is available. + -- IDEA: we could also have generic extension to node system, that handle generic addition field. end end @@ -78,7 +88,7 @@ feature {CMS_API} -- Module management is_installed (api: CMS_API): BOOLEAN -- Is Current module installed? do - if attached {CMS_STORAGE_SQL} api.storage as l_sql_storage then + if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then Result := l_sql_storage.sql_table_exists ("nodes") and l_sql_storage.sql_table_exists ("page_nodes") end @@ -87,19 +97,28 @@ feature {CMS_API} -- Module management install (api: CMS_API) do -- Schema - if attached {CMS_STORAGE_SQL} api.storage as l_sql_storage then + if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then l_sql_storage.sql_execute_file_script (api.setup.layout.path.extended ("scripts").extended (name).appended_with_extension ("sql")) end end +feature {CMS_API} -- Access: API + + node_api: detachable CMS_NODE_API + -- + feature -- Access: router router (a_api: CMS_API): WSF_ROUTER -- Node router. local - l_node_api: CMS_NODE_API + l_node_api: like node_api do - create l_node_api.make (a_api) + l_node_api := node_api + if l_node_api = Void then + create l_node_api.make (a_api) + node_api := l_node_api + end create Result.make (2) configure_cms (a_api, l_node_api, Result) -- configure_api (a_api, l_node_api, Result) diff --git a/src/persistence/cms_proxy_storage_sql.e b/src/persistence/cms_proxy_storage_sql.e index 6f87aea..212cfa8 100644 --- a/src/persistence/cms_proxy_storage_sql.e +++ b/src/persistence/cms_proxy_storage_sql.e @@ -1,5 +1,5 @@ note - description: "Proxy on a {CMS_STORAGE_SQL} interface." + description: "Proxy on a {CMS_STORAGE_SQL_I} interface." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" @@ -7,7 +7,7 @@ class CMS_PROXY_STORAGE_SQL inherit - CMS_STORAGE_SQL + CMS_STORAGE_SQL_I create make @@ -19,7 +19,7 @@ feature {NONE} -- Initialization sql_storage := a_sql_storage end - sql_storage: CMS_STORAGE_SQL + sql_storage: CMS_STORAGE_SQL_I feature -- Access diff --git a/src/persistence/cms_storage.e b/src/persistence/cms_storage.e index 8463272..e3e880e 100644 --- a/src/persistence/cms_storage.e +++ b/src/persistence/cms_storage.e @@ -10,9 +10,9 @@ deferred class CMS_STORAGE inherit - CMS_USER_STORAGE + CMS_CORE_STORAGE_I - CMS_NODE_STORAGE + CMS_USER_STORAGE_I SHARED_LOGGER @@ -52,21 +52,4 @@ feature -- Element change api := a_api end -feature -- Misc - --- set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: READABLE_STRING_8) --- -- Save data `a_name:a_value' for type `a_type' --- deferred --- end - --- custom_value (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable TABLE_ITERABLE [READABLE_STRING_8, STRING_8] --- -- Data for name `a_name' and type `a_type'. --- deferred --- end - --- custom_value_names_where (a_where_key, a_where_value: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable LIST [READABLE_STRING_8] --- -- Names where custom value has item `a_where_key' same as `a_where_value' for type `a_type'. --- deferred --- end - end diff --git a/src/persistence/cms_storage_builder.e b/src/persistence/cms_storage_builder.e index 3f79c98..3200547 100644 --- a/src/persistence/cms_storage_builder.e +++ b/src/persistence/cms_storage_builder.e @@ -12,6 +12,7 @@ deferred class feature -- Factory storage (a_setup: CMS_SETUP): detachable CMS_STORAGE + -- CMS Storage object based on CMS setup `a_setup'. deferred end diff --git a/src/persistence/cms_storage_null.e b/src/persistence/cms_storage_null.e index 0c1da17..01b6bd2 100644 --- a/src/persistence/cms_storage_null.e +++ b/src/persistence/cms_storage_null.e @@ -120,80 +120,29 @@ feature -- Change: roles and permissions do end -feature -- Access: node - nodes_count: INTEGER_64 - -- Count of nodes. + +feature -- Logs + + save_log (a_log: CMS_LOG) + -- Save `a_log'. do end - nodes: LIST[CMS_NODE] - -- List of nodes. - do - create {ARRAYED_LIST [CMS_NODE]} Result.make (0) - end - - recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] - -- List of the `a_count' most recent nodes, starting from `a_lower'. - do - create {ARRAYED_LIST [CMS_NODE]} Result.make (0) - end - - node_by_id (a_id: INTEGER_64): detachable CMS_NODE - -- + 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 end - node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER - -- Node's author. if any. + unset_custom_value (a_name: READABLE_STRING_8; a_type: detachable READABLE_STRING_8) + -- Delete data `a_name' for type `a_type' (or default if none). do end - node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] - -- Possible list of node's collaborator. - do - create {ARRAYED_LIST [CMS_USER]} Result.make (0) - end - -feature -- Node - - new_node (a_node: CMS_NODE) - -- Add a new node + custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32 + -- Data for name `a_name' and type `a_type' (or default if none). do end - delete_node_by_id (a_id: INTEGER_64) - -- - do - end - - update_node (a_node: CMS_NODE) - -- - do - end - - update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) - -- - do - end - - update_node_summary (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) - -- - do - end - - update_node_content (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) - -- - do - end - - -feature -- Helpers - - fill_node (a_node: CMS_NODE) - -- Fill `a_node' with extra information from database. - -- i.e: specific to each content type data. - do - end end diff --git a/library/persistence/mysql/src/cms_user_storage_mysql.e b/src/persistence/cms_storage_sql_builder.e similarity index 51% rename from library/persistence/mysql/src/cms_user_storage_mysql.e rename to src/persistence/cms_storage_sql_builder.e index 9f8bfc2..d43ea79 100644 --- a/library/persistence/mysql/src/cms_user_storage_mysql.e +++ b/src/persistence/cms_storage_sql_builder.e @@ -1,14 +1,15 @@ note - description: "Summary description for {CMS_USER_STORAGE_MYSQL}." - author: "" + 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_USER_STORAGE_MYSQL + CMS_STORAGE_SQL_BUILDER inherit - CMS_USER_STORAGE_SQL - + CMS_STORAGE_BUILDER end diff --git a/src/persistence/cms_storage_sql.e b/src/persistence/cms_storage_sql_i.e similarity index 96% rename from src/persistence/cms_storage_sql.e rename to src/persistence/cms_storage_sql_i.e index 752a3a3..afae7f7 100644 --- a/src/persistence/cms_storage_sql.e +++ b/src/persistence/cms_storage_sql_i.e @@ -1,11 +1,10 @@ note - description: "Summary description for {CMS_STORAGE_SQL}." - author: "" + description: "Interface used to implement 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 + CMS_STORAGE_SQL_I feature -- Access @@ -161,6 +160,7 @@ feature -- Helper sql_query ("SELECT count(*) FROM :tbname ;", l_params) Result := not has_error -- FIXME: find better solution + reset_error end sql_table_items_count (a_table_name: READABLE_STRING_8): INTEGER_64 @@ -252,7 +252,7 @@ feature -- Access elseif attached {BOOLEAN_REF} l_item as l_boolean_ref then Result := l_boolean_ref.item.out else - check is_string: False end + check is_string_nor_null: l_item = Void end end end @@ -269,7 +269,7 @@ feature -- Access if attached sql_read_string (a_index) as s8 then Result := s8.to_string_32 -- FIXME: any escape? else - check is_string_32: False end + check is_string_nor_null: l_item = Void end end end end @@ -283,7 +283,7 @@ feature -- Access if attached {DATE_TIME} l_item as dt then Result := dt else --- check is_date_time: False end + check is_date_time_nor_null: l_item = Void end end end diff --git a/src/persistence/core/cms_core_storage_i.e b/src/persistence/core/cms_core_storage_i.e new file mode 100644 index 0000000..6267dba --- /dev/null +++ b/src/persistence/core/cms_core_storage_i.e @@ -0,0 +1,47 @@ +note + description: "[ + Objects that ... + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_CORE_STORAGE_I + +inherit + SHARED_LOGGER + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + deferred + end + +feature -- Logs + + save_log (a_log: CMS_LOG) + -- Save `a_log'. + deferred + end + +feature -- Misc + + 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). + deferred + end + + unset_custom_value (a_name: READABLE_STRING_8; a_type: detachable READABLE_STRING_8) + -- Delete data `a_name' for type `a_type' (or default if none). + deferred + end + + custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32 + -- Data for name `a_name' and type `a_type' (or default if none). + deferred + end + + +end diff --git a/src/persistence/core/cms_core_storage_sql_i.e b/src/persistence/core/cms_core_storage_sql_i.e new file mode 100644 index 0000000..07a5910 --- /dev/null +++ b/src/persistence/core/cms_core_storage_sql_i.e @@ -0,0 +1,138 @@ +note + description: "[ + Objects that ... + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_CORE_STORAGE_SQL_I + +inherit + CMS_CORE_STORAGE_I + + CMS_STORAGE_SQL_I + + REFACTORING_HELPER + + SHARED_LOGGER + +feature -- Logs + + save_log (a_log: CMS_LOG) + -- Save `a_log'. + local + l_parameters: STRING_TABLE [detachable ANY] + now: DATE_TIME + s32: STRING_32 + do + create now.make_now_utc + error_handler.reset + + create l_parameters.make (8) + l_parameters.put (a_log.category, "category") + l_parameters.put (a_log.level, "level") + l_parameters.put (0, "uid") -- Unsupported for now + l_parameters.put (a_log.message, "message") + l_parameters.put (a_log.info, "info") + if attached a_log.link as lnk then + create s32.make_empty + s32.append_character ('[') + s32.append_string_general (lnk.location) + s32.append_character (']') + s32.append_character ('(') + s32.append (lnk.title) + s32.append_character (')') + l_parameters.put (s32, "link") + else + l_parameters.put (Void, "link") + end + l_parameters.put (now, "date") + sql_change (sql_insert_log, l_parameters) + end + + sql_insert_log: STRING = "INSERT INTO logs (category, level, uid, message, info, link, date) VALUES (:category, :level, :uid, :message, :info, :link, :date);" + -- SQL Insert to add a new node. + +feature -- Misc + + set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (3) + if a_type /= Void then + l_parameters.put (a_type, "type") + else + l_parameters.put (a_type, "default") + end + l_parameters.put (a_name, "name") + l_parameters.put (a_value, "value") + if attached custom_value (a_name, a_type) as l_value then + if a_value.same_string (l_value) then + -- already up to date + else + sql_change (sql_update_custom_value, l_parameters) + end + else + sql_change (sql_insert_custom_value, l_parameters) + end + end + + unset_custom_value (a_name: READABLE_STRING_8; a_type: detachable READABLE_STRING_8) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (3) + if a_type /= Void then + l_parameters.put (a_type, "type") + else + l_parameters.put (a_type, "default") + end + l_parameters.put (a_name, "name") + sql_change (sql_delete_custom_value, l_parameters) + end + + custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32 + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + if a_type /= Void then + l_parameters.put (a_type, "type") + else + l_parameters.put (a_type, "default") + end + l_parameters.put (a_name, "name") + sql_query (sql_select_custom_value, l_parameters) + if not has_error then + if sql_rows_count = 1 then + Result := sql_read_string_32 (1) + end + end + end + + sql_select_custom_value: STRING = "SELECT value FROM custom_values WHERE type=:type AND name=:name;" + -- SQL Insert to add a new custom value. + + sql_insert_custom_value: STRING = "INSERT INTO custom_values (type, name, value) VALUES (:type, :name, :value);" + -- SQL Insert to add a new custom value. + + sql_update_custom_value : STRING = "UPDATE custom_values SET value=:value WHERE type=:type AND name=:name;" + -- SQL Update to modify a custom value. + + sql_delete_custom_value: STRING = "DELETE FROM custom_values WHERE type=:type AND name=:name;" + -- SQL delete custom value; + + +end diff --git a/src/persistence/node/cms_node_storage.e b/src/persistence/node/cms_node_storage_i.e similarity index 97% rename from src/persistence/node/cms_node_storage.e rename to src/persistence/node/cms_node_storage_i.e index 3b10581..058654d 100644 --- a/src/persistence/node/cms_node_storage.e +++ b/src/persistence/node/cms_node_storage_i.e @@ -1,11 +1,10 @@ note - description: "Summary description for {CMS_NODE_STORAGE}." - author: "" + description: "Summary description for {CMS_NODE_STORAGE_I}." date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" revision: "$Revision: 96542 $" deferred class - CMS_NODE_STORAGE + CMS_NODE_STORAGE_I inherit SHARED_LOGGER diff --git a/src/persistence/node/cms_node_storage_null.e b/src/persistence/node/cms_node_storage_null.e new file mode 100644 index 0000000..87559af --- /dev/null +++ b/src/persistence/node/cms_node_storage_null.e @@ -0,0 +1,106 @@ +note + description: "[ + Objects that ... + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +class + CMS_NODE_STORAGE_NULL + +inherit + CMS_NODE_STORAGE_I + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + create error_handler.make + end + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + +feature -- Access: node + + nodes_count: INTEGER_64 + -- Count of nodes. + do + end + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + end + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of the `a_count' most recent nodes, starting from `a_lower'. + do + create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + end + + node_by_id (a_id: INTEGER_64): detachable CMS_NODE + -- + do + end + + node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER + -- Node's author. if any. + do + end + + node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] + -- Possible list of node's collaborator. + do + create {ARRAYED_LIST [CMS_USER]} Result.make (0) + end + +feature -- Node + + new_node (a_node: CMS_NODE) + -- Add a new node + do + end + + delete_node_by_id (a_id: INTEGER_64) + -- + do + end + + update_node (a_node: CMS_NODE) + -- + do + end + +-- update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) +-- -- +-- do +-- end + +-- update_node_summary (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) +-- -- +-- do +-- end + +-- update_node_content (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) +-- -- +-- do +-- end + +feature -- Helpers + + fill_node (a_node: CMS_NODE) + -- Fill `a_node' with extra information from database. + -- i.e: specific to each content type data. + do + end + +end diff --git a/src/persistence/node/cms_node_storage_sql.e b/src/persistence/node/cms_node_storage_sql.e index cead57d..f606ea5 100644 --- a/src/persistence/node/cms_node_storage_sql.e +++ b/src/persistence/node/cms_node_storage_sql.e @@ -5,18 +5,23 @@ note date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" -deferred class +class CMS_NODE_STORAGE_SQL inherit - CMS_NODE_STORAGE + CMS_PROXY_STORAGE_SQL - CMS_STORAGE_SQL + CMS_NODE_STORAGE_I + + CMS_STORAGE_SQL_I REFACTORING_HELPER SHARED_LOGGER +create + make + feature -- Access nodes_count: INTEGER_64 @@ -146,7 +151,7 @@ feature -- Change: Node error_handler.reset create l_parameters.make (1) - l_parameters.put (a_id, "id") + l_parameters.put (a_id, "nid") sql_change (sql_delete_node, l_parameters) end @@ -201,7 +206,6 @@ feature {NONE} -- Implementation local l_parameters: STRING_TABLE [detachable ANY] now: DATE_TIME - is_new: BOOLEAN do create now.make_now_utc error_handler.reset @@ -212,6 +216,7 @@ feature {NONE} -- Implementation 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.format, "format") l_parameters.put (a_node.publication_date, "publish") l_parameters.put (now, "changed") if attached a_node.author as l_author then @@ -222,16 +227,14 @@ feature {NONE} -- Implementation end sql_begin_transaction if a_node.has_id then - is_new := True -- Update - l_parameters.put (a_node.id, "id") + l_parameters.put (a_node.id, "nid") sql_change (sql_update_node, l_parameters) if not error_handler.has_error then a_node.set_revision (a_node.revision + 1) -- FIXME: Should be incremented by one, in same transaction...but check! a_node.set_modification_date (now) end else - is_new := False -- Store new node l_parameters.put (a_node.creation_date, "created") sql_change (sql_insert_node, l_parameters) @@ -263,14 +266,14 @@ feature {NONE} -- Queries sql_select_nodes: STRING = "SELECT * from Nodes;" -- SQL Query to retrieve all nodes. - sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, author, publish, created, changed FROM Nodes WHERE nid =:nid ORDER BY revision desc, publish desc LIMIT 1;" + sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed 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, author, publish, created, changed FROM Nodes ORDER BY nid desc, publish desc LIMIT :rows OFFSET :offset ;" + sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM Nodes ORDER BY nid desc, publish desc LIMIT :rows OFFSET :offset ;" - sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, publish, created, changed, author) VALUES (1, :type, :title, :summary, :content, :publish, :created, :changed, :author);" + sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :author);" -- SQL Insert to add a new node. - sql_update_node : STRING = "UPDATE nodes SET revision = revision + 1, type=:type, title=:title, summary=:summary, content=:content, publish=:publish, changed=:changed, revision = revision + 1, author=:author WHERE nid=:nid;" + sql_update_node : STRING = "UPDATE nodes SET revision = revision + 1, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, revision = revision + 1, author=:author WHERE nid=:nid;" -- SQL node. sql_delete_node: STRING = "DELETE FROM nodes WHERE nid=:nid;" @@ -292,7 +295,7 @@ feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Select_user_author: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM Nodes INNER JOIN users ON nodes.author=users.uid AND users.uid = :uid;" - Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;" + Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;" feature {NONE} -- Implementation @@ -316,18 +319,21 @@ feature {NONE} -- Implementation 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 + if attached sql_read_string (7) as l_format then + Result.set_format (l_format) + end + if attached sql_read_integer_64 (8) as l_author_id then + Result.set_author (create {CMS_PARTIAL_USER}.make_with_id (l_author_id)) + end + if attached sql_read_date_time (9) 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 + if attached sql_read_date_time (10) 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 + if attached sql_read_date_time (11) 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 - Result.set_author (create {CMS_PARTIAL_USER}.make_with_id (l_author_id)) - end end end diff --git a/src/persistence/node/cms_node_storage_sql_page_extension.e b/src/persistence/node/cms_node_storage_sql_page_extension.e index a6d70d2..961d233 100644 --- a/src/persistence/node/cms_node_storage_sql_page_extension.e +++ b/src/persistence/node/cms_node_storage_sql_page_extension.e @@ -90,6 +90,7 @@ feature -- Persistence l_parent_id /= a_node.id and then attached node_storage.node_by_id (l_parent_id) as l_parent then + -- FIXME: find a simple way to access the declared content types. create ct a_node.set_parent (ct.new_node (l_parent)) else diff --git a/src/persistence/user/cms_user_storage.e b/src/persistence/user/cms_user_storage_i.e similarity index 97% rename from src/persistence/user/cms_user_storage.e rename to src/persistence/user/cms_user_storage_i.e index a399a62..9586383 100644 --- a/src/persistence/user/cms_user_storage.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -1,11 +1,11 @@ note - description: "Summary description for {CMS_USER_STORAGE}." + description: "Summary description for {CMS_USER_STORAGE_I}." author: "" date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" revision: "$Revision: 96542 $" deferred class - CMS_USER_STORAGE + CMS_USER_STORAGE_I inherit SHARED_LOGGER diff --git a/src/persistence/user/cms_user_storage_sql.e b/src/persistence/user/cms_user_storage_sql_i.e similarity index 99% rename from src/persistence/user/cms_user_storage_sql.e rename to src/persistence/user/cms_user_storage_sql_i.e index cf87ae9..54c26a0 100644 --- a/src/persistence/user/cms_user_storage_sql.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -1,16 +1,15 @@ note - description: "Summary description for {CMS_USER_STORAGE_SQL}." - author: "" + description: "Summary description for {CMS_USER_STORAGE_SQL_I}." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" deferred class - CMS_USER_STORAGE_SQL + CMS_USER_STORAGE_SQL_I inherit - CMS_USER_STORAGE + CMS_USER_STORAGE_I - CMS_STORAGE_SQL + CMS_STORAGE_SQL_I REFACTORING_HELPER @@ -34,6 +33,7 @@ feature -- Access: user if sql_rows_count = 1 then Result := sql_read_integer_32 (1) end + error_handler.reset end users: LIST [CMS_USER] diff --git a/src/service/cms_api.e b/src/service/cms_api.e index e036ab7..b20ce57 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -47,6 +47,10 @@ feature {NONE} -- Initialize setup.enabled_modules as ic loop l_module := ic.item + -- FIXME: should we initialize first, and then install + -- or the reverse, or merge installation and initialization + -- and leave the responsability to the module to know + -- if this is installed or not... if not l_module.is_installed (Current) then l_module.install (Current) end @@ -72,6 +76,11 @@ feature -- Formats create Result end + format (a_format_name: detachable READABLE_STRING_GENERAL): detachable CONTENT_FORMAT + do + Result := formats.item (a_format_name) + end + feature -- Status Report has_error: BOOLEAN @@ -86,6 +95,47 @@ feature -- Status Report Result := error_handler.as_string_representation end +feature -- Logging + + log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK) + local + l_log: CMS_LOG + m: STRING + do + create l_log.make (a_category, a_message, a_level, Void) + if a_link /= Void then + l_log.set_link (a_link) + end + storage.save_log (l_log) + + create m.make_from_string ("[" + a_category + "] ") + m.append (a_message) + if a_link /= Void then + m.append (" [" + url_encoded (a_link.title) + "]("+ a_link.location +")") + end + + inspect a_level + when {CMS_LOG}.level_emergency then + logger.put_alert (m, Void) + when {CMS_LOG}.level_alert then + logger.put_alert (m, Void) + when {CMS_LOG}.level_critical then + logger.put_critical (m, Void) + when {CMS_LOG}.level_error then + logger.put_error (m, Void) + when {CMS_LOG}.level_warning then + logger.put_warning (m, Void) + when {CMS_LOG}.level_notice then + logger.put_information (m, Void) + when {CMS_LOG}.level_info then + logger.put_information (m, Void) + when {CMS_LOG}.level_debug then + logger.put_debug (m, Void) + else + logger.put_debug (m, Void) + end + end + feature -- Permissions system user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN @@ -114,6 +164,16 @@ feature -- Query: module Result := Void end end + ensure + Result /= Void implies (Result.is_enabled and Result.generating_type ~ a_type) + end + + module_api (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE_API + -- Enabled module API associated with module typed `a_type'. + do + if attached module (a_type) as mod then + Result := mod.module_api + end end module_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_MODULE @@ -133,7 +193,15 @@ feature -- Query: module end end ensure - Result /= Void implies Result.name.is_case_insensitive_equal_general (a_name) + Result /= Void implies (Result.is_enabled and Result.name.is_case_insensitive_equal_general (a_name)) + end + + module_api_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_MODULE_API + -- Enabled module API associated with module named `a_name'. + do + if attached module_by_name (a_name) as mod then + Result := mod.module_api + end end feature -- Query: API diff --git a/src/service/handler/cms_handler.e b/src/service/handler/cms_handler.e index e90c572..4df49cb 100644 --- a/src/service/handler/cms_handler.e +++ b/src/service/handler/cms_handler.e @@ -36,10 +36,37 @@ feature -- Response helpers -- res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (a_location)) end - send_access_denied (res: WSF_RESPONSE) + send_access_denied_message (res: WSF_RESPONSE) -- Send via `res' an access denied response. do res.send (create {CMS_FORBIDDEN_RESPONSE_MESSAGE}.make) end + send_access_denied (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Forbidden response. + local + r: CMS_RESPONSE + do + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + + send_not_found (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Send via `res' a not found response. + local + r: CMS_RESPONSE + do + create {NOT_FOUND_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + + send_bad_request (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Send via `res' a bad request response. + local + r: CMS_RESPONSE + do + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + end diff --git a/src/service/logger/cms_logger.e b/src/service/logger/cms_logger.e index 08afe3a..399411b 100644 --- a/src/service/logger/cms_logger.e +++ b/src/service/logger/cms_logger.e @@ -9,7 +9,11 @@ deferred class feature -- Logging - put_information (a_message: READABLE_STRING_8; a_data: detachable ANY) + put_critical (a_message: READABLE_STRING_8; a_data: detachable ANY) + deferred + end + + put_alert (a_message: READABLE_STRING_8; a_data: detachable ANY) deferred end @@ -21,11 +25,7 @@ feature -- Logging deferred end - put_critical (a_message: READABLE_STRING_8; a_data: detachable ANY) - deferred - end - - put_alert (a_message: READABLE_STRING_8; a_data: detachable ANY) + put_information (a_message: READABLE_STRING_8; a_data: detachable ANY) deferred end diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 90f0c34..d364f02 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -162,9 +162,14 @@ feature -- Access: CMS -- Associated values indexed by string name. +feature -- User access + + user: detachable CMS_USER + do + Result := current_user (request) + end feature -- Permission - -- FIXME: to be implemented has_permissions and has_permission. has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN -- Does current user has permission `a_permission' ? @@ -442,6 +447,9 @@ feature -- Blocks do if attached primary_tabs as m and then not m.is_empty then create Result.make (m) + Result.is_horizontal := True + Result.set_is_raw (True) + Result.add_css_class ("tabs") end end @@ -459,7 +467,7 @@ feature -- Blocks s: STRING l_hb: STRING do - create s.make_from_string (theme.menu_html (primary_menu, True)) + create s.make_from_string (theme.menu_html (primary_menu, True, Void)) create l_hb.make_empty create Result.make ("header", Void, l_hb, Void) Result.set_is_raw (True) @@ -469,7 +477,15 @@ feature -- Blocks do create Result.make_empty Result.append ("
    ") - Result.append (theme.menu_html (primary_menu, True)) + Result.append (theme.menu_html (primary_menu, True, Void)) + Result.append ("
    ") + end + + horizontal_primary_tabs_html: STRING + do + create Result.make_empty + Result.append ("
    ") + Result.append (theme.menu_html (primary_tabs, True, Void)) Result.append ("
    ") end @@ -652,6 +668,11 @@ feature -- Menu: change add_to_menu (lnk, primary_menu) end + add_to_primary_tabs (lnk: CMS_LINK) + do + add_to_menu (lnk, primary_tabs) + end + add_to_menu (lnk: CMS_LINK; m: CMS_MENU) do -- if attached {CMS_LOCAL_LINK} lnk as l_local then @@ -872,6 +893,7 @@ feature -- Generation -- Menu... page.register_variable (horizontal_primary_menu_html, "primary_nav") + page.register_variable (horizontal_primary_tabs_html, "primary_tabs") -- Page related if attached page_title as l_page_title then diff --git a/src/service/response/bad_request_error_cms_response.e b/src/service/response/error/bad_request_error_cms_response.e similarity index 100% rename from src/service/response/bad_request_error_cms_response.e rename to src/service/response/error/bad_request_error_cms_response.e diff --git a/src/service/response/error/forbidden_error_cms_response.e b/src/service/response/error/forbidden_error_cms_response.e new file mode 100644 index 0000000..5616c8b --- /dev/null +++ b/src/service/response/error/forbidden_error_cms_response.e @@ -0,0 +1,38 @@ +note + description: "Summary description for {FORBIDDEN_ERROR_CMS_RESPONSE}." + date: "$Date: 2014-11-13 19:34:00 +0100 (jeu., 13 nov. 2014) $" + revision: "$Revision: 96086 $" + +class + FORBIDDEN_ERROR_CMS_RESPONSE + +inherit + + CMS_RESPONSE + redefine + custom_prepare + end + +create + make + +feature -- Generation + + custom_prepare (page: CMS_HTML_PAGE) + do + page.register_variable (request.absolute_script_url (request.path_info), "request") + page.set_status_code ({HTTP_STATUS_CODE}.forbidden) + page.register_variable (page.status_code.out, "code") + end + +feature -- Execution + + process + -- Computed response message. + do + set_title ("Forbidden") + set_page_title ("Forbidden") + set_main_content ("Access denied for resource " + request.request_uri + ".") + end +end + diff --git a/src/service/response/internal_server_error_cms_response.e b/src/service/response/error/internal_server_error_cms_response.e similarity index 100% rename from src/service/response/internal_server_error_cms_response.e rename to src/service/response/error/internal_server_error_cms_response.e diff --git a/src/service/response/not_found_error_cms_response.e b/src/service/response/error/not_found_error_cms_response.e similarity index 100% rename from src/service/response/not_found_error_cms_response.e rename to src/service/response/error/not_found_error_cms_response.e diff --git a/src/service/response/not_implemented_error_cms_response.e b/src/service/response/error/not_implemented_error_cms_response.e similarity index 100% rename from src/service/response/not_implemented_error_cms_response.e rename to src/service/response/error/not_implemented_error_cms_response.e diff --git a/src/service/response/cms_forbidden_response_message.e b/src/service/response/message/cms_forbidden_response_message.e similarity index 100% rename from src/service/response/cms_forbidden_response_message.e rename to src/service/response/message/cms_forbidden_response_message.e diff --git a/src/service/response/cms_redirection_response_message.e b/src/service/response/message/cms_redirection_response_message.e similarity index 100% rename from src/service/response/cms_redirection_response_message.e rename to src/service/response/message/cms_redirection_response_message.e diff --git a/src/service/response/cms_unauthorized_response_message.e b/src/service/response/message/cms_unauthorized_response_message.e similarity index 100% rename from src/service/response/cms_unauthorized_response_message.e rename to src/service/response/message/cms_unauthorized_response_message.e diff --git a/src/theme/cms_html_options.e b/src/theme/cms_html_options.e new file mode 100644 index 0000000..33f9e71 --- /dev/null +++ b/src/theme/cms_html_options.e @@ -0,0 +1,50 @@ +note + description: "Options for any html item during CMS theme output." + date: "$Date$" + revision: "$Revision$" + +class + CMS_HTML_OPTIONS + +feature -- Access + + css_classes: detachable ARRAYED_LIST [READABLE_STRING_8] + -- Optional additional css classes. + +feature -- Element change + + add_css_class (a_class: READABLE_STRING_8) + -- Add css class `a_class'. + local + l_css_classes: like css_classes + do + l_css_classes := css_classes + if l_css_classes = Void then + create l_css_classes.make (1) + css_classes := l_css_classes + end + l_css_classes.force (a_class) + end + + remove_css_class (a_class: READABLE_STRING_GENERAL) + -- Remove css class `a_class'. + local + l_css_classes: like css_classes + do + l_css_classes := css_classes + if l_css_classes /= Void then + from + l_css_classes.start + until + l_css_classes.after + loop + if a_class.is_case_insensitive_equal (l_css_classes.item) then + l_css_classes.remove + l_css_classes.finish + end + l_css_classes.forth + end + end + end + +end diff --git a/src/theme/cms_theme.e b/src/theme/cms_theme.e index 74eca9e..8093433 100644 --- a/src/theme/cms_theme.e +++ b/src/theme/cms_theme.e @@ -35,12 +35,27 @@ feature -- Access feature -- Conversion - menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 + menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN; a_options: detachable CMS_HTML_OPTIONS): STRING_8 + local + cl: STRING do debug ("refactor_fixme") fixme ("Refactor HTML code to use the new Bootstrap theme template") end - create Result.make_from_string ("
    ") + create cl.make_from_string ("menu") + if a_options /= Void and then attached a_options.css_classes as lst then + across + lst as ic + loop + cl.append_character (' ') + cl.append (ic.item) + end + end + create Result.make_from_string ("
    ") if is_horizontal then Result.append ("
      %N") else