From 77e2c28d182d0667a67c64125747064803808e69 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 19 Feb 2016 00:03:15 +0100 Subject: [PATCH] Added logs admin viewer. Added CMS_SETUP.is_debug: BOOLEAN (see cms.ini site.debug setting) --- examples/demo/site/config/cms.ini | 1 + library/model/src/log/cms_log.e | 5 +- modules/admin/cms_admin_module.e | 19 ++- modules/admin/handler/cms_admin_response.e | 1 + modules/admin/handler/logs/cms_logs_handler.e | 114 ++++++++++++++++++ src/configuration/cms_setup.e | 8 ++ src/persistence/cms_storage_null.e | 10 +- src/persistence/core/cms_core_storage_i.e | 9 +- src/persistence/core/cms_core_storage_sql_i.e | 87 ++++++++++++- src/service/cms_api.e | 27 ++++- src/service/cms_module_api.e | 7 +- src/service/response/cms_response.e | 7 ++ 12 files changed, 274 insertions(+), 21 deletions(-) create mode 100644 modules/admin/handler/logs/cms_logs_handler.e diff --git a/examples/demo/site/config/cms.ini b/examples/demo/site/config/cms.ini index c27d477..17aa909 100644 --- a/examples/demo/site/config/cms.ini +++ b/examples/demo/site/config/cms.ini @@ -6,6 +6,7 @@ root-dir=site/www [site] # General token that could be use for cookies, and related. id=_EIFFEL_CMS_ +#debug=true # Name of the site, for the title, and eventual message. name=Eiffel CMS diff --git a/library/model/src/log/cms_log.e b/library/model/src/log/cms_log.e index c50d932..10dcf7a 100644 --- a/library/model/src/log/cms_log.e +++ b/library/model/src/log/cms_log.e @@ -31,7 +31,7 @@ feature {NONE} -- Initialization feature -- Access - id: INTEGER + id: INTEGER_64 -- Unique identifier of Current. category: READABLE_STRING_8 @@ -124,4 +124,7 @@ feature -- Constants level_info: INTEGER = 7 level_debug: INTEGER = 8 +note + copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/modules/admin/cms_admin_module.e b/modules/admin/cms_admin_module.e index aafe5af..5054157 100644 --- a/modules/admin/cms_admin_module.e +++ b/modules/admin/cms_admin_module.e @@ -54,6 +54,7 @@ feature -- Access: router l_user_handler: CMS_USER_HANDLER l_role_handler: CMS_ROLE_HANDLER + l_admin_logs_handler: CMS_LOGS_HANDLER l_admin_cache_handler: CMS_ADMIN_CACHE_HANDLER l_admin_export_handler: CMS_ADMIN_EXPORT_HANDLER @@ -76,6 +77,11 @@ feature -- Access: router create l_uri_mapping.make_trailing_slash_ignored ("/admin/roles", l_roles_handler) a_router.map (l_uri_mapping, a_router.methods_get_post) + create l_admin_logs_handler.make (a_api) + create l_uri_mapping.make_trailing_slash_ignored ("/admin/logs", l_admin_logs_handler) + a_router.map (l_uri_mapping, a_router.methods_get) + + create l_admin_cache_handler.make (a_api) create l_uri_mapping.make_trailing_slash_ignored ("/admin/cache", l_admin_cache_handler) a_router.map (l_uri_mapping, a_router.methods_get_post) @@ -108,6 +114,7 @@ feature -- Security Result.force ("admin roles") Result.force ("admin modules") Result.force ("install modules") + Result.force ("view logs") Result.force ("admin core caches") Result.force ("clear blocks cache") Result.force ("admin export") @@ -148,18 +155,6 @@ feature -- Hooks -- Per module export permission! create lnk.make ("Export", "admin/export") admin_lnk.extend (lnk) - --- if --- a_response.has_permission ("access " + {CMS_ADMIN_MODULE}.name) -- Note: admin user has all permissions enabled by default. --- then --- lnk := admin_lnk --- lnk.set_title ("Admin") - --- a_menu_system.management_menu.extend (lnk) --- elseif admin_lnk.has_children then --- a_menu_system.management_menu.extend (admin_lnk) --- end --- admin_lnk.set_permission_arguments (<<"access " + {CMS_ADMIN_MODULE}.name>>) end end diff --git a/modules/admin/handler/cms_admin_response.e b/modules/admin/handler/cms_admin_response.e index 07e6226..8f7be4b 100644 --- a/modules/admin/handler/cms_admin_response.e +++ b/modules/admin/handler/cms_admin_response.e @@ -26,6 +26,7 @@ feature -- Process l_admin_links.force (["core", <<"admin users">>, local_link ("Users", "admin/users"), "View/Edit/Add Users"]) l_admin_links.force (["core", <<"admin roles">>, local_link ("Roles", "admin/roles"), "View/Edit/Add Roles"]) l_admin_links.force (["core", <<"admin modules">>, local_link ("Modules", "admin/modules"), "(un)Install modules"]) + l_admin_links.force (["core", <<"view logs">>, local_link ("Logs", "admin/logs"), "View logs"]) l_admin_links.force (["support", <<"admin cache">>, local_link ("Cache", "admin/cache"), "Clear caches"]) l_admin_links.force (["support", <<"admin export">>, local_link ("Export", "admin/export"), "Export CMS contents, and modules contents."]) create categories.make_caseless (3) diff --git a/modules/admin/handler/logs/cms_logs_handler.e b/modules/admin/handler/logs/cms_logs_handler.e new file mode 100644 index 0000000..1d3c3f9 --- /dev/null +++ b/modules/admin/handler/logs/cms_logs_handler.e @@ -0,0 +1,114 @@ +note + description: "[ + Handler for a CMS logs in the CMS interface. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_LOGS_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_logs: LIST [CMS_LOG] + l_log: CMS_LOG + r: CMS_RESPONSE + l_cat: detachable READABLE_STRING_8 + l_lower: INTEGER + l_count: INTEGER + b: STRING + do + if api.has_permission ("view logs") then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if attached {WSF_STRING} req.query_parameter ("category") as p_cat then + l_cat := p_cat.value + end + if attached {WSF_STRING} req.query_parameter ("lower") as p_lower and then p_lower.is_integer then + l_lower := p_lower.integer_value + end + if attached {WSF_STRING} req.query_parameter ("count") as p_count and then p_count.is_integer then + l_count := p_count.integer_value + end + + l_logs := api.logs (l_cat, l_lower, l_count) + create b.make (100) + b.append ("%N") + r.set_main_content (b) + r.set_page_title ("Logs ...") + r.set_title ("Logs") + else + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + end + r.execute + + end + +end diff --git a/src/configuration/cms_setup.e b/src/configuration/cms_setup.e index c19580d..02fc1c8 100644 --- a/src/configuration/cms_setup.e +++ b/src/configuration/cms_setup.e @@ -20,6 +20,9 @@ feature {NONE} -- Initialization do site_location := environment.path + -- Debug mode. + is_debug := attached string_8_item ("site.debug") as l_debug and then l_debug.is_case_insensitive_equal_general ("yes") + --| Site id, used to identified a site, this could be set to a uuid, or else site_id := string_8_item_or_default ("site.id", "_ROC_CMS_NO_ID_") @@ -259,6 +262,11 @@ feature -- Access: Site -- Optional path defining the front page. -- By default "" or "/". +feature -- Settings + + is_debug: BOOLEAN + -- Is debug mode enabled? + feature -- Query text_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 diff --git a/src/persistence/cms_storage_null.e b/src/persistence/cms_storage_null.e index 5156d25..89b330f 100644 --- a/src/persistence/cms_storage_null.e +++ b/src/persistence/cms_storage_null.e @@ -93,6 +93,14 @@ feature -- Logs do end + logs (a_category: detachable READABLE_STRING_GENERAL; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] + -- List of recent logs from `a_lower' to `a_lower+a_count'. + -- If `a_category' is set, filter to return only associated logs. + -- If `a_count' <= 0 then, return all logs. + do + create {ARRAYED_LIST [CMS_LOG]} Result.make (0) + end + feature -- Custom set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8) @@ -128,6 +136,6 @@ feature -- Custom end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/persistence/core/cms_core_storage_i.e b/src/persistence/core/cms_core_storage_i.e index 661fc46..b285e22 100644 --- a/src/persistence/core/cms_core_storage_i.e +++ b/src/persistence/core/cms_core_storage_i.e @@ -59,6 +59,13 @@ feature -- Logs deferred end + logs (a_category: detachable READABLE_STRING_GENERAL; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] + -- List of recent logs from `a_lower' to `a_lower+a_count'. + -- If `a_category' is set, filter to return only associated logs. + -- If `a_count' <= 0 then, return all logs. + deferred + end + feature -- Misc set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8) @@ -82,6 +89,6 @@ feature -- Misc end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/persistence/core/cms_core_storage_sql_i.e b/src/persistence/core/cms_core_storage_sql_i.e index c785fc3..f6a475f 100644 --- a/src/persistence/core/cms_core_storage_sql_i.e +++ b/src/persistence/core/cms_core_storage_sql_i.e @@ -206,9 +206,94 @@ feature -- Logs sql_finalize end + logs (a_category: detachable READABLE_STRING_GENERAL; a_lower: INTEGER; a_count: INTEGER): ARRAYED_LIST [CMS_LOG] + -- . + local + l_parameters: detachable STRING_TABLE [detachable ANY] + l_sql: READABLE_STRING_8 + do + error_handler.reset + create l_parameters.make (3) + if a_category /= Void then + l_parameters.put (a_category, "category") + l_sql := sql_select_categorized_logs + else + l_sql := sql_select_logs + end + if a_count > 0 then + l_parameters.put (a_lower, "offset") + l_parameters.put (a_count, "size") + check l_sql.ends_with_general (";") end + l_sql := l_sql.substring (1, l_sql.count - 1) -- Remove ';' + + "LIMIT :size OFFSET :offset ;" + end + + from + if a_count > 0 then + create Result.make (a_count) + else + create Result.make (10) + end + if l_parameters.is_empty then + l_parameters := Void + end + sql_query (l_sql, l_parameters) + sql_start + until + sql_after + loop + if attached fetch_log as l_log then + Result.force (l_log) + end + sql_forth + end + sql_finalize + end + + fetch_log: detachable CMS_LOG + -- SQL: 1:id, 2:category, 3:level, 4:uid, 5:message, 6:info, 7:link, 8:date + local + l_cat: detachable READABLE_STRING_8 + l_mesg: detachable READABLE_STRING_8 + l_level: INTEGER + l_date: detachable DATE_TIME + i: INTEGER + lnk: CMS_LOCAL_LINK + do + l_cat := sql_read_string (2) + l_mesg := sql_read_string (5) + l_level := sql_read_integer_32 (3) + l_date := sql_read_date_time (8) + + if l_cat = Void then + l_cat := "unknown" + end + if l_mesg = Void then + l_mesg := "" + end + + create Result.make (l_cat, l_mesg, l_level, l_date) + Result.set_id (sql_read_integer_64 (1)) + Result.set_info (sql_read_string (6)) + if attached sql_read_string_32 (7) as l_link_text then + -- Format: "[title](location)" + i := l_link_text.index_of ('(', 1) + if i > 0 then + create lnk.make (l_link_text.substring (2, i - 2), l_link_text.substring (i + 1, l_link_text.count - 1)) + Result.set_link (lnk) + end + end + 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. + sql_select_logs: STRING = "SELECT id, category, level, uid, message, info, link, date FROM logs ORDER by date DESC;" + -- SQL Insert to add a new node. + + sql_select_categorized_logs: STRING = "SELECT id, category, level, uid, message, info, link, date FROM logs WHERE category=:category ORDER by date DESC;" + -- 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) @@ -326,6 +411,6 @@ feature -- Misc note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 66b9c14..446eaab 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -170,6 +170,14 @@ feature -- Access storage: CMS_STORAGE -- Default persistence storage. +feature -- Settings + + is_debug: BOOLEAN + -- Is debug mode enabled? + do + Result := setup.is_debug + end + feature {NONE} -- Access: request request: WSF_REQUEST @@ -261,6 +269,14 @@ feature -- Status Report feature -- Logging + logs (a_category: detachable READABLE_STRING_8; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] + -- List of recent logs from `a_lower' to `a_lower+a_count'. + -- If `a_category' is set, filter to return only associated logs. + -- If `a_count' <= 0 then, return all logs. + do + Result := storage.logs (a_category, a_lower, a_count) + end + log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK) local l_log: CMS_LOG @@ -674,11 +690,16 @@ feature -- Environment/ module create {INI_CONFIG} Result.make_from_file (l_path) end end - if Result = Void and a_name /= Void then - -- Use sub config from default? - if attached {CONFIG_READER} module_configuration_by_name_in_location (a_module_name, a_dir, Void) as cfg then + if Result = Void then + if + a_name /= Void and then + attached {CONFIG_READER} module_configuration_by_name_in_location (a_module_name, a_dir, Void) as cfg + then + -- Use sub config from default. Result := cfg.sub_config (a_name) end + elseif Result.has_error then + log ("modules", "module configuration has error %"" + p.utf_8_name + "%"", {CMS_LOG}.level_error, Void) end end diff --git a/src/service/cms_module_api.e b/src/service/cms_module_api.e index 9632bc0..ff0ea8d 100644 --- a/src/service/cms_module_api.e +++ b/src/service/cms_module_api.e @@ -3,9 +3,12 @@ note date: "$Date: 2015-02-13 14:54:27 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96620 $" -deferred class +class CMS_MODULE_API +create + make + feature {NONE} -- Initialization make (a_api: CMS_API) @@ -67,6 +70,6 @@ feature -- Bridge to CMS API end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index c5d0953..a191d55 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -992,6 +992,13 @@ feature -- Message m.append (a_msg + "") end + add_debug_message (a_msg: READABLE_STRING_8) + do + if api.is_debug_enabled then + add_message (a_msg, "debug") + end + end + add_notice_message (a_msg: READABLE_STRING_8) do add_message (a_msg, "notice")