From b8920ee8b3ce2a2110de879f374ff6d7ab135acb Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 23 Nov 2015 11:08:06 +0100 Subject: [PATCH 1/5] Added module administration from /admin/modules/ --- modules/admin/cms_admin_module.e | 12 + .../admin/handler/cms_admin_modules_handler.e | 320 ++++++++++++++++++ src/service/cms_api.e | 26 +- .../handler/cms_admin_install_handler.e | 3 +- 4 files changed, 355 insertions(+), 6 deletions(-) create mode 100644 modules/admin/handler/cms_admin_modules_handler.e diff --git a/modules/admin/cms_admin_module.e b/modules/admin/cms_admin_module.e index 8d83cc9..263e0eb 100644 --- a/modules/admin/cms_admin_module.e +++ b/modules/admin/cms_admin_module.e @@ -47,6 +47,8 @@ feature -- Access: router configure_web (a_api: CMS_API; a_router: WSF_ROUTER) local l_admin_handler: CMS_ADMIN_HANDLER + + l_modules_handler: CMS_ADMIN_MODULES_HANDLER l_users_handler: CMS_ADMIN_USERS_HANDLER l_roles_handler: CMS_ADMIN_ROLES_HANDLER @@ -62,6 +64,10 @@ feature -- Access: router create l_uri_mapping.make_trailing_slash_ignored ("/admin", l_admin_handler) a_router.map (l_uri_mapping, a_router.methods_get_post) + create l_modules_handler.make (a_api) + create l_uri_mapping.make_trailing_slash_ignored ("/admin/modules", l_modules_handler) + a_router.map (l_uri_mapping, a_router.methods_get_post) + create l_users_handler.make (a_api) create l_uri_mapping.make_trailing_slash_ignored ("/admin/users", l_users_handler) a_router.map (l_uri_mapping, a_router.methods_get_post) @@ -134,7 +140,13 @@ feature -- Hooks create lnk.make ("Admin", "admin") lnk.set_permission_arguments (<<"manage " + {CMS_ADMIN_MODULE}.name>>) a_menu_system.management_menu.extend (lnk) + end + + create lnk.make ("Module", "admin/modules") + lnk.set_permission_arguments (<<"manage module">>) + a_menu_system.management_menu.extend (lnk) + -- Per module cache permission! create lnk.make ("Cache", "admin/cache") a_menu_system.management_menu.extend (lnk) diff --git a/modules/admin/handler/cms_admin_modules_handler.e b/modules/admin/handler/cms_admin_modules_handler.e new file mode 100644 index 0000000..53ffb20 --- /dev/null +++ b/modules/admin/handler/cms_admin_modules_handler.e @@ -0,0 +1,320 @@ +note + description: "[ + Administrate modules. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_ADMIN_MODULES_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, do_post + end + + REFACTORING_HELPER + + CMS_SETUP_ACCESS + + CMS_ACCESS + +create + make + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + s: STRING + f: CMS_FORM + l_denied: BOOLEAN + do + if + attached {WSF_STRING} req.query_parameter ("op") as l_op and then l_op.same_string ("uninstall") and then + attached {WSF_TABLE} req.query_parameter ("module_uninstallation") as tb + then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if attached api.setup.string_8_item ("admin.installation_access") as l_access then + if l_access.is_case_insensitive_equal ("none") then + l_denied := True + elseif l_access.is_case_insensitive_equal ("permission") then + l_denied := not r.has_permission ("install modules") + end + else + l_denied := True + end + if l_denied then + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("You do not have permission to access CMS module uninstallation procedure!") + else + create s.make_empty + across + tb as ic + loop + if attached api.setup.modules.item_by_name (ic.item.string_representation) as l_module then + if api.is_module_installed (l_module) then + api.uninstall_module (l_module) + if api.is_module_installed (l_module) then + s.append ("

ERROR: Module " + l_module.name + " failed to be uninstalled!

") + else + s.append ("

Module " + l_module.name + " was successfully uninstalled.

") + end + else + s.append ("

Module " + l_module.name + " is not installed.

") + end + end + end + s.append (r.link ("Back to modules management", r.location, Void)) + r.set_main_content (s) + end + r.execute + else + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + f := modules_collection_web_form (r) + create s.make_empty + f.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), s) + r.set_page_title ("Modules") + r.set_main_content (s) + r.execute + end + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + s: STRING + f: CMS_FORM + l_denied: BOOLEAN + do + if attached {WSF_STRING} req.item ("op") as l_op then + if l_op.same_string ("Install modules") then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + + if attached api.setup.string_8_item ("admin.installation_access") as l_access then + if l_access.is_case_insensitive_equal ("none") then + l_denied := True + elseif l_access.is_case_insensitive_equal ("permission") then + l_denied := not r.has_permission ("install modules") + end + else + l_denied := True + end + if l_denied then + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("You do not have permission to access CMS module installation procedure!") + else + f := modules_collection_web_form (r) + if l_op.same_string ("Install modules") then + f.submit_actions.extend (agent on_installation_submit) + f.process (r) + elseif l_op.same_string ("uninstall") then + f.submit_actions.extend (agent on_uninstallation_submit) + f.process (r) + end + if + not attached f.last_data as l_data or else + not l_data.is_valid + then + r.add_error_message ("Error occurred.") + create s.make_empty + f.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), s) + r.set_page_title ("Modules") + r.set_main_content (s) + else + r.add_notice_message ("Operation on module(s) succeeded.") + r.set_redirection (r.location) + end + end + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + else + do_get (req, res) + end + end + + modules_collection_web_form (a_response: CMS_RESPONSE): CMS_FORM + local + mod: CMS_MODULE + f_cb: WSF_FORM_CHECKBOX_INPUT + w_tb: WSF_WIDGET_TABLE + w_row: WSF_WIDGET_TABLE_ROW + w_item: WSF_WIDGET_TABLE_ITEM + w_submit: WSF_FORM_SUBMIT_INPUT + w_set: WSF_FORM_FIELD_SET + + l_mods_to_install: ARRAYED_LIST [CMS_MODULE] + do + create Result.make (a_response.url (a_response.location, Void), "modules_collection") + create w_tb.make + w_tb.add_css_class ("modules_table") + create w_row.make (5) + create w_item.make_with_text ("Enabled ") + w_row.add_item (w_item) + create w_item.make_with_text ("Module") + w_row.add_item (w_item) + create w_item.make_with_text ("Version") + w_row.add_item (w_item) + create w_item.make_with_text ("Description") + w_row.add_item (w_item) + w_tb.add_head_row (w_row) + + create l_mods_to_install.make (0) + across + a_response.api.setup.modules as ic + loop + mod := ic.item + if not a_response.api.is_module_installed (mod) then + l_mods_to_install.extend (mod) + else + create w_row.make (5) + create f_cb.make ("module_" + mod.name) + f_cb.set_text_value (mod.name) + f_cb.set_checked (mod.is_enabled) + f_cb.set_is_readonly (True) + create w_item.make_with_content (f_cb) + w_row.add_item (w_item) + + create w_item.make_with_text (mod.name) + w_row.add_item (w_item) + + create w_item.make_with_text (mod.version) + w_row.add_item (w_item) + + if attached mod.description as l_desc then + create w_item.make_with_text (l_desc) + w_row.add_item (w_item) + else + create w_item.make_with_text ("") + w_row.add_item (w_item) + end + create w_item.make_with_text (a_response.link ("Uninstall", a_response.location + "?op=uninstall&module_uninstallation[]=" + mod.name, Void)) + w_row.add_item (w_item) + + w_tb.add_row (w_row) + end + end + create w_set.make + w_set.set_legend ("Installed modules") + w_set.extend (w_tb) +-- create w_submit.make ("op") +-- w_submit.set_text_value ("Save") +-- w_set.extend (w_submit) + Result.extend (w_set) + + Result.extend_html_text ("
") + + if not l_mods_to_install.is_empty then + create w_tb.make + w_tb.add_css_class ("modules_table") + create w_row.make (3) + create w_item.make_with_text ("Install ") + w_row.add_item (w_item) + create w_item.make_with_text ("Module") + w_row.add_item (w_item) + create w_item.make_with_text ("Description") + w_row.add_item (w_item) + w_tb.add_head_row (w_row) + across + l_mods_to_install as ic + loop + mod := ic.item + create w_row.make (3) + create f_cb.make ("module_installation[" + mod.name + "]") + f_cb.set_text_value (mod.name) + create w_item.make_with_content (f_cb) + w_row.add_item (w_item) + + create w_item.make_with_text (mod.name) + w_row.add_item (w_item) + + if attached mod.description as l_desc then + create w_item.make_with_text (l_desc) + w_row.add_item (w_item) + else + create w_item.make_with_text ("") + w_row.add_item (w_item) + end + w_tb.add_row (w_row) + end + create w_set.make + w_set.set_legend ("Available modules for installation") + w_set.extend (w_tb) + create w_submit.make ("op") + w_submit.set_text_value ("Install modules") + w_set.extend (w_submit) + Result.extend (w_set) + end + end + + on_installation_submit (fd: WSF_FORM_DATA) + local + l_mods: CMS_MODULE_COLLECTION + do + if attached {WSF_TABLE} fd.table_item ("module_installation") as tb and then not tb.is_empty then + l_mods := api.setup.modules + across + tb as ic + loop + if + attached {WSF_STRING} ic.item as l_mod_name and then + attached l_mods.item_by_name (l_mod_name.value) as m + then + api.install_module (m) + if not api.is_module_installed (m) then + fd.report_error ("Installation failed for module " + m.name) + end + else + fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value) + end + end + else + fd.report_error ("No module to install!") + end + end + + on_uninstallation_submit (fd: WSF_FORM_DATA) + local + l_mods: CMS_MODULE_COLLECTION + do + if attached {WSF_TABLE} fd.table_item ("module_uninstallation") as tb and then not tb.is_empty then + l_mods := api.setup.modules + across + tb as ic + loop + if + attached {WSF_STRING} ic.item as l_mod_name and then + attached l_mods.item_by_name (l_mod_name.value) as m + then + api.uninstall_module (m) + if api.is_module_installed (m) then + fd.report_error ("Un-Installation failed for module " + m.name) + end + else + fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value) + end + end + else + fd.report_error ("No module to uninstall!") + end + end + +end diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 18e73ce..8d1b450 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -107,7 +107,7 @@ feature {NONE} -- Initialize feature {CMS_ACCESS} -- Installation - install + install_all_modules -- Install CMS or uninstalled module which are enabled. local l_module: CMS_MODULE @@ -121,14 +121,30 @@ feature {CMS_ACCESS} -- Installation -- 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) - if l_module.is_enabled then - l_module.initialize (Current) - end + install_module (l_module) end end end + install_module (m: CMS_MODULE) + -- Install module `m'. + require + module_not_installed: not is_module_installed (m) + do + m.install (Current) + if m.is_enabled then + m.initialize (Current) + end + end + + uninstall_module (m: CMS_MODULE) + -- Uninstall module `m'. + require + module_installed: is_module_installed (m) + do + m.uninstall (Current) + end + feature -- Access setup: CMS_SETUP diff --git a/src/service/handler/cms_admin_install_handler.e b/src/service/handler/cms_admin_install_handler.e index 9b61fa2..f5f4b45 100644 --- a/src/service/handler/cms_admin_install_handler.e +++ b/src/service/handler/cms_admin_install_handler.e @@ -45,6 +45,7 @@ feature -- HTTP Methods lst: ARRAYED_LIST [CMS_MODULE] l_denied: BOOLEAN do + --| FIXME: improve the installer. create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if attached api.setup.string_8_item ("admin.installation_access") as l_access then if l_access.is_case_insensitive_equal ("none") then @@ -77,7 +78,7 @@ feature -- HTTP Methods lst.force (l_module) end end - api.install + api.install_all_modules across lst as ic loop From 3791ffacdce916dc0603d41686bf0b96334914ec Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 23 Nov 2015 15:27:04 +0100 Subject: [PATCH 2/5] Added first steps toward Taxonomy module. --- examples/demo/demo-safe.ecf | 4 +- examples/demo/src/demo_cms_execution.e | 4 + modules/taxonomy/cms_taxonomy_api.e | 84 ++++++ modules/taxonomy/cms_taxonomy_module.e | 161 +++++++++++ modules/taxonomy/cms_term.e | 99 +++++++ modules/taxonomy/cms_vocabulary.e | 63 ++++ modules/taxonomy/handler/taxonomy_handler.e | 81 ++++++ .../persistence/cms_taxonomy_storage_i.e | 64 ++++ .../persistence/cms_taxonomy_storage_null.e | 67 +++++ .../persistence/cms_taxonomy_storage_sql.e | 273 ++++++++++++++++++ .../taxonomy/site/files/scss/taxonomy.scss | 0 modules/taxonomy/taxonomy-safe.ecf | 26 ++ 12 files changed, 924 insertions(+), 2 deletions(-) create mode 100644 modules/taxonomy/cms_taxonomy_api.e create mode 100644 modules/taxonomy/cms_taxonomy_module.e create mode 100644 modules/taxonomy/cms_term.e create mode 100644 modules/taxonomy/cms_vocabulary.e create mode 100644 modules/taxonomy/handler/taxonomy_handler.e create mode 100644 modules/taxonomy/persistence/cms_taxonomy_storage_i.e create mode 100644 modules/taxonomy/persistence/cms_taxonomy_storage_null.e create mode 100644 modules/taxonomy/persistence/cms_taxonomy_storage_sql.e create mode 100644 modules/taxonomy/site/files/scss/taxonomy.scss create mode 100644 modules/taxonomy/taxonomy-safe.ecf diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index d1cf5a9..b8c67db 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -29,16 +29,16 @@ + - - + diff --git a/examples/demo/src/demo_cms_execution.e b/examples/demo/src/demo_cms_execution.e index bf3e1ae..a3286d9 100644 --- a/examples/demo/src/demo_cms_execution.e +++ b/examples/demo/src/demo_cms_execution.e @@ -66,6 +66,10 @@ feature -- CMS modules a_setup.register_module (m) create {CMS_BLOG_MODULE} m.make + a_setup.register_module (m) + + -- Taxonomy + create {CMS_TAXONOMY_MODULE} m.make a_setup.register_module (m) -- Recent changes diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e new file mode 100644 index 0000000..578f589 --- /dev/null +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -0,0 +1,84 @@ +note + description: "[ + API to handle taxonomy vocabularies and terms. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TAXONOMY_API + +inherit + CMS_MODULE_API + redefine + initialize + end + + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + initialize + -- + do + Precursor + + -- Create the node storage for type blog + if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then + create {CMS_TAXONOMY_STORAGE_SQL} taxonomy_storage.make (l_storage_sql) + else + create {CMS_TAXONOMY_STORAGE_NULL} taxonomy_storage.make + end + end + +feature {CMS_MODULE} -- Access nodes storage. + + taxonomy_storage: CMS_TAXONOMY_STORAGE_I + +feature -- Access node + + vocabulary_count: INTEGER_64 + -- Number of vocabulary. + do + Result := taxonomy_storage.vocabulary_count + end + + vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_VOCABULARY] + -- List of vocabularies ordered by weight and limited by limit and offset. + do + Result := taxonomy_storage.vocabularies (a_limit, a_offset) + end + + vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + -- Vocabulary associated with id `a_id'. + require + valid_id: a_id > 0 + do + Result := taxonomy_storage.vocabulary (a_id) + end + + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 + -- Number of terms from vocabulary `a_vocab'. + require + has_id: a_vocab.has_id + do + Result := taxonomy_storage.term_count_from_vocabulary (a_vocab) + end + + terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_TERM] + -- List of terms ordered by weight and limited by limit and offset. + require + has_id: a_vocab.has_id + do + Result := taxonomy_storage.terms (a_vocab, a_limit, a_offset) + end + + term_by_id (a_tid: INTEGER): detachable CMS_TERM + do + Result := taxonomy_storage.term_by_id (a_tid) + end + +end diff --git a/modules/taxonomy/cms_taxonomy_module.e b/modules/taxonomy/cms_taxonomy_module.e new file mode 100644 index 0000000..ace013b --- /dev/null +++ b/modules/taxonomy/cms_taxonomy_module.e @@ -0,0 +1,161 @@ +note + description: "[ + Taxonomy module managing vocabularies and terms. + ]" + date: "$Date: 2015-05-22 15:13:00 +0100 (lun., 18 mai 2015) $" + revision: "$Revision 96616$" + +class + CMS_TAXONOMY_MODULE + +inherit + CMS_MODULE + rename + module_api as taxonomy_api + redefine + register_hooks, + initialize, + install, + uninstall, + taxonomy_api + end + + CMS_HOOK_MENU_SYSTEM_ALTER + + CMS_HOOK_RESPONSE_ALTER + +create + make + +feature {NONE} -- Initialization + + make + do + version := "1.0" + description := "Taxonomy solution" + package := "core" +-- add_dependency ({CMS_NODE_MODULE}) + end + +feature -- Access + + name: STRING = "taxonomy" + +feature {CMS_API} -- Module Initialization + + initialize (api: CMS_API) + -- + do + Precursor (api) + create taxonomy_api.make (api) + end + +feature {CMS_API} -- Module management + + install (api: CMS_API) + local + sql: STRING + do + -- Schema + if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + if not l_sql_storage.sql_table_exists ("taxonomy_term") then + sql := "[ +CREATE TABLE taxonomy_term ( + `tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE, + `text` VARCHAR(255) NOT NULL, + `weight` INTEGER, + `description` TEXT, + `langcode` VARCHAR(12) +); +CREATE TABLE taxonomy_hierarchy ( + `tid` INTEGER NOT NULL, + `parent` INTEGER, + CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent) +); + ]" + l_sql_storage.sql_execute_script (sql, Void) + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for taxonomy module", generating_type) + end + end + Precursor (api) + end + end + + uninstall (api: CMS_API) + -- (export status {CMS_API}) + local + sql: STRING + do + if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + sql := "[ +DROP TABLE IF EXISTS taxonomy_term; +DROP TABLE IF EXISTS taxonomy_hierarchy; + ]" + l_sql_storage.sql_execute_script (sql, Void) + if l_sql_storage.has_error then + api.logger.put_error ("Could not remove database for taxonomy module", generating_type) + end + end + Precursor (api) + end + +feature {CMS_API} -- Access: API + + taxonomy_api: detachable CMS_TAXONOMY_API + -- + +feature -- Access: router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- + do + if attached taxonomy_api as l_taxonomy_api then + configure_web (a_api, l_taxonomy_api, a_router) + else + -- Issue with api/dependencies, + -- thus Current module should not be used! + -- thus no url mapping + end + end + + configure_web (a_api: CMS_API; a_taxonomy_api: CMS_TAXONOMY_API; a_router: WSF_ROUTER) + -- Configure router mapping for web interface. + local + l_taxonomy_handler: TAXONOMY_HANDLER + l_uri_mapping: WSF_URI_MAPPING + do + create l_taxonomy_handler.make (a_api, a_taxonomy_api) + -- Let the class BLOG_HANDLER handle the requests on "/taxonomys" + create l_uri_mapping.make_trailing_slash_ignored ("/taxonomy", l_taxonomy_handler) + a_router.map (l_uri_mapping, a_router.methods_get) + + -- We can add a page number after /taxonomys/ to get older posts + a_router.handle ("/taxonomy/{vocabulary}", l_taxonomy_handler, a_router.methods_get) + + -- If a user id is given route with taxonomy user handler + --| FIXME: maybe /user/{user}/taxonomys/ would be better. + a_router.handle ("/taxonomy/{vocabulary}/{termid}", l_taxonomy_handler, a_router.methods_get) + end + +feature -- Hooks + + register_hooks (a_response: CMS_RESPONSE) + do + a_response.hooks.subscribe_to_menu_system_alter_hook (Current) + a_response.hooks.subscribe_to_response_alter_hook (Current) + end + + response_alter (a_response: CMS_RESPONSE) + do + a_response.add_style (a_response.url ("/module/" + name + "/files/css/taxonomy.css", Void), Void) + end + + menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + do + -- Add the link to the taxonomy to the main menu +-- create lnk.make ("Taxonomy", "taxonomy/") +-- a_menu_system.primary_menu.extend (lnk) + end + +end diff --git a/modules/taxonomy/cms_term.e b/modules/taxonomy/cms_term.e new file mode 100644 index 0000000..659f869 --- /dev/null +++ b/modules/taxonomy/cms_term.e @@ -0,0 +1,99 @@ +note + description: "[ + Taxonomy vocabulary term. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TERM + +inherit + COMPARABLE + +create + make + +feature {NONE} -- Initialization + + make (a_id: INTEGER; a_text: READABLE_STRING_GENERAL) + do + id := a_id + set_text (a_text) + end + +feature -- Access + + id: INTEGER + -- Associated term id. + + text: IMMUTABLE_STRING_32 + -- Text for the term. + + description: detachable IMMUTABLE_STRING_32 + -- Optional description. + + parent_id: INTEGER + -- Optional parent term id. + + weight: INTEGER + -- Associated weight for ordering. + +feature -- Status report + + has_id: BOOLEAN + -- Has valid id? + do + Result := id > 0 + end + +feature -- Comparison + + is_less alias "<" (other: like Current): BOOLEAN + -- Is current object less than `other'? + do + if weight = other.weight then + if text.same_string (other.text) then + Result := id < other.id + else + Result := text < other.text + end + else + Result := weight < other.weight + end + end + +feature -- Element change + + set_id (a_id: INTEGER) + do + id := a_id + end + + set_text (a_text: READABLE_STRING_GENERAL) + do + create text.make_from_string_general (a_text) + end + + set_weight (w: like weight) + do + weight := w + end + + set_description (a_description: READABLE_STRING_GENERAL) + do + create description.make_from_string_general (a_description) + end + + set_parent (a_parent: CMS_TERM) + do + parent_id := a_parent.id + end + + set_parent_id (a_parent_id: INTEGER) + do + parent_id := a_parent_id + end + +end + diff --git a/modules/taxonomy/cms_vocabulary.e b/modules/taxonomy/cms_vocabulary.e new file mode 100644 index 0000000..e9cb979 --- /dev/null +++ b/modules/taxonomy/cms_vocabulary.e @@ -0,0 +1,63 @@ +note + description: "[ + Taxonomy vocabulary. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_VOCABULARY + +create + make, + make_from_term + +feature {NONE} -- Initialization + + make (a_tid: INTEGER; a_name: READABLE_STRING_GENERAL) + do + id := a_tid + set_name (a_name) + create {ARRAYED_LIST [CMS_TERM]} items.make (0) + end + + make_from_term (a_term: CMS_TERM) + do + make (a_term.id, a_term.text) + description := a_term.description + end + +feature -- Access + + id: INTEGER + + name: IMMUTABLE_STRING_32 + + description: detachable READABLE_STRING_32 + + items: LIST [CMS_TERM] + -- Collection of terms. + + has_id: BOOLEAN + do + Result := id > 0 + end + +feature -- Element change + + set_name (a_name: READABLE_STRING_GENERAL) + do + create name.make_from_string_general (a_name) + end + + sort + -- Sort `items' + local + l_sorter: QUICK_SORTER [CMS_TERM] + do + create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_TERM]}) + l_sorter.sort (items) + end + +end + diff --git a/modules/taxonomy/handler/taxonomy_handler.e b/modules/taxonomy/handler/taxonomy_handler.e new file mode 100644 index 0000000..bef7d11 --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_handler.e @@ -0,0 +1,81 @@ +note + description: "[ + Request handler related to + /taxonomy + /taxonomy/{vocabulary} + /taxonomy/{vocabulary}/{term} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_HANDLER + +inherit + CMS_MODULE_HANDLER [CMS_TAXONOMY_API] + rename + module_api as taxonomy_api + end + + 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 + + CMS_API_ACCESS + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for any kind of mapping. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI mapping. + do + execute (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI-template mapping. + do + execute (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + -- Read page number from path parameter. + + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + l_page.set_main_content ("Not Yet Implemented -- In Progress") + l_page.execute + end + +end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e new file mode 100644 index 0000000..cc3e6ef --- /dev/null +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e @@ -0,0 +1,64 @@ +note + description: "[ + Interface for accessing taxonomy data from storage. + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_TAXONOMY_STORAGE_I + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + deferred + end + +feature -- Access + + vocabulary_count: INTEGER_64 + -- Count of vocabularies. + deferred + end + + vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] + -- List of vocabularies ordered by weight from offset to offset + limit. + deferred + end + + vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + -- Vocabulary by id `a_id'. + require + valid_id: a_id > 0 + deferred + end + + terms_count: INTEGER_64 + -- Number of terms. + deferred + end + + term_by_id (tid: INTEGER): detachable CMS_TERM + -- Term associated with id `tid'. + deferred + ensure + Result /= Void implies Result.id = tid + end + + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 + -- Number of terms from vocabulary `a_vocab'. + require + has_id: a_vocab.has_id + deferred + end + + terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] + -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + require + has_id: a_vocab.has_id + deferred + end + + +end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e new file mode 100644 index 0000000..5795d4a --- /dev/null +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e @@ -0,0 +1,67 @@ +note + description: "Summary description for {CMS_TAXONOMY_STORAGE_NULL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_TAXONOMY_STORAGE_NULL + +inherit + CMS_TAXONOMY_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 + + vocabulary_count: INTEGER_64 + -- Count of vocabularies. + do + end + + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 + -- Number of terms from vocabulary `a_vocab'. + do + end + + vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] + -- List of vocabularies ordered by weight from offset to offset + limit. + do + create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) + end + + vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + -- Vocabulary by id `a_id'. + do + end + + terms_count: INTEGER_64 + -- Number of terms. + do + end + + term_by_id (tid: INTEGER): detachable CMS_TERM + -- Term associated with id `tid'. + do + end + + terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] + -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + do + create {ARRAYED_LIST [CMS_TERM]} Result.make (0) + end + +end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e new file mode 100644 index 0000000..da06ddf --- /dev/null +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e @@ -0,0 +1,273 @@ +note + description: "[ + Implementation of taxonomy storage using a SQL database. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TAXONOMY_STORAGE_SQL + +inherit + CMS_TAXONOMY_STORAGE_I + + CMS_PROXY_STORAGE_SQL + +create + make + +feature -- Access + + vocabulary_count: INTEGER_64 + -- Count of vocabularies. + do + error_handler.reset + sql_query (sql_select_vocabularies_count, Void) + if not has_error and not sql_after then + Result := sql_read_integer_64 (1) + end + sql_finalize + end + + vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] + -- List of vocabularies ordered by weight from offset to offset + limit. + do + create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) + end + + vocabulary (a_tid: INTEGER): detachable CMS_VOCABULARY + -- Vocabulary by id `a_tid'. + do + if attached term_by_id (a_tid) as t then + create Result.make_from_term (t) + end + end + + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 + -- Number of terms from vocabulary `a_vocab'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + create l_parameters.make (1) + l_parameters.put (a_vocab.id, "parent_tid") + sql_query (sql_select_vocabulary_terms_count, Void) + if not has_error and not sql_after then + Result := sql_read_integer_64 (1) + end + sql_finalize + end + + terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] + -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + local + l_parameters: STRING_TABLE [detachable ANY] + do + create {ARRAYED_LIST [CMS_TERM]} Result.make (0) + error_handler.reset + + create l_parameters.make (1) + l_parameters.put (a_vocab.id, "parent_tid") + from + sql_query (sql_select_terms, l_parameters) + sql_start + until + sql_after + loop + if attached fetch_term as l_term then + Result.force (l_term) + end + sql_forth + end + sql_finalize + end + + terms_count: INTEGER_64 + -- Number of terms. + do + error_handler.reset + sql_query (sql_select_terms_count, Void) + if not has_error and not sql_after then + Result := sql_read_integer_64 (1) + end + sql_finalize + end + + term_by_id (a_tid: INTEGER): detachable CMS_TERM + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (1) + l_parameters.put (a_tid, "tid") + sql_query (sql_select_term, l_parameters) + sql_start + if not has_error and not sql_after then + Result := fetch_term + end + sql_finalize + end + +feature -- Store + + save_term (t: CMS_TERM) + do + end + +-- blogs_count: INTEGER_64 +-- -- +-- do +-- error_handler.reset +-- write_information_log (generator + ".blogs_count") +-- sql_query (sql_select_blog_count, Void) +-- if not has_error and not sql_after then +-- Result := sql_read_integer_64 (1) +-- end +-- sql_finalize +-- end + +-- blogs_count_from_user (a_user: CMS_USER) : INTEGER_64 +-- -- +-- local +-- l_parameters: STRING_TABLE [detachable ANY] +-- do +-- error_handler.reset +-- write_information_log (generator + ".blogs_count_from_user") +-- create l_parameters.make (2) +-- l_parameters.put (a_user.id, "user") +-- sql_query (sql_select_blog_count_from_user, l_parameters) +-- if not has_error and not sql_after then +-- Result := sql_read_integer_64 (1) +-- end +-- sql_finalize +-- end + +-- blogs: LIST [CMS_NODE] +-- -- +-- do +-- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + +-- error_handler.reset +-- write_information_log (generator + ".blogs") + +-- from +-- sql_query (sql_select_blogs_order_created_desc, Void) +-- sql_start +-- until +-- sql_after +-- loop +-- if attached fetch_node as l_node then +-- Result.force (l_node) +-- end +-- sql_forth +-- end +-- sql_finalize +-- end + +-- blogs_limited (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_NODE] +-- -- +-- local +-- l_parameters: STRING_TABLE [detachable ANY] +-- do +-- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + +-- error_handler.reset +-- write_information_log (generator + ".blogs_limited") + +-- from +-- create l_parameters.make (2) +-- l_parameters.put (a_limit, "limit") +-- l_parameters.put (a_offset, "offset") +-- sql_query (sql_blogs_limited, l_parameters) +-- sql_start +-- until +-- sql_after +-- loop +-- if attached fetch_node as l_node then +-- Result.force (l_node) +-- end +-- sql_forth +-- end +-- sql_finalize +-- end + +-- blogs_from_user_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_NODE] +-- -- +-- local +-- l_parameters: STRING_TABLE [detachable ANY] +-- do +-- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) + +-- error_handler.reset +-- write_information_log (generator + ".blogs_from_user_limited") + +-- from +-- create l_parameters.make (2) +-- l_parameters.put (a_limit, "limit") +-- l_parameters.put (a_offset, "offset") +-- l_parameters.put (a_user.id, "user") +-- sql_query (sql_blogs_from_user_limited, l_parameters) +-- sql_start +-- until +-- sql_after +-- loop +-- if attached fetch_node as l_node then +-- Result.force (l_node) +-- end +-- sql_forth +-- end +-- sql_finalize +-- end + +feature {NONE} -- Queries + + fetch_term: detachable CMS_TERM + local + tid: INTEGER + l_text: detachable READABLE_STRING_32 + do + tid := sql_read_integer_32 (1) + l_text := sql_read_string_32 (2) + if tid > 0 and l_text /= Void then + create Result.make (tid, l_text) + Result.set_weight (sql_read_integer_32 (3)) + if attached sql_read_string_32 (4) as l_desc then + Result.set_description (l_desc) + end + end + end + + sql_select_terms_count: STRING = "SELECT count(*) FROM taxonomy_term ;" + -- Number of terms. + + sql_select_vocabularies_count: STRING = "SELECT count(*) FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = 0;" + -- Number of terms without parent. + + sql_select_vocabulary_terms_count: STRING = "SELECT count(*) FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = :parent_tid;" + -- Number of terms under :parent_tid. + + sql_select_vocabularies: STRING = "SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = 0;" + -- Terms without parent. + + sql_select_terms: STRING = "SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = :parent_tid;" + -- Terms under :parent_tid. + + sql_select_term: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE tid = :tid;" + -- Term with tid :tid . + +-- sql_select_blog_count_from_user: STRING = "SELECT count(*) FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ;" +-- -- Nodes count (Published and not Published) +-- --| note: {CMS_NODE_API}.trashed = -1 + +-- sql_select_blogs_order_created_desc: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC;" +-- -- SQL Query to retrieve all nodes that are from the type "blog" ordered by descending creation date. + +-- sql_blogs_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC LIMIT :limit OFFSET :offset ;" +-- --- SQL Query to retrieve all node of type "blog" limited by limit and starting at offset + +-- sql_blogs_from_user_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ORDER BY created DESC LIMIT :limit OFFSET :offset ;" +-- --- SQL Query to retrieve all node of type "blog" from author with id limited by limit + offset + + +end diff --git a/modules/taxonomy/site/files/scss/taxonomy.scss b/modules/taxonomy/site/files/scss/taxonomy.scss new file mode 100644 index 0000000..e69de29 diff --git a/modules/taxonomy/taxonomy-safe.ecf b/modules/taxonomy/taxonomy-safe.ecf new file mode 100644 index 0000000..e4813e9 --- /dev/null +++ b/modules/taxonomy/taxonomy-safe.ecf @@ -0,0 +1,26 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + + + + From 1d4ce37ebf67856a9f63588bee43b6b87cac88a5 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 23 Nov 2015 18:03:55 +0100 Subject: [PATCH 3/5] Added CMS_STORAGE.as_sql_storage: detachable CMS_STORAGE_SQL_I to ease development based on SQL database. --- modules/blog/cms_blog_api.e | 2 +- modules/blog/cms_blog_module.e | 2 +- modules/node/cms_node_module.e | 6 +++--- modules/oauth20/cms_oauth_20_module.e | 6 +++--- modules/openid/cms_openid_module.e | 4 ++-- modules/taxonomy/cms_taxonomy_api.e | 2 +- modules/taxonomy/site/scripts/install.sql | 13 +++++++++++++ modules/taxonomy/site/scripts/uninstall.sql | 2 ++ src/persistence/cms_storage.e | 13 +++++++++++++ 9 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 modules/taxonomy/site/scripts/install.sql create mode 100644 modules/taxonomy/site/scripts/uninstall.sql diff --git a/modules/blog/cms_blog_api.e b/modules/blog/cms_blog_api.e index 8c63dcd..695289d 100644 --- a/modules/blog/cms_blog_api.e +++ b/modules/blog/cms_blog_api.e @@ -36,7 +36,7 @@ feature {NONE} -- Initialization Precursor -- Create the node storage for type blog - if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then + if attached storage.as_sql_storage as l_storage_sql then create {CMS_BLOG_STORAGE_SQL} blog_storage.make (l_storage_sql) else create {CMS_BLOG_STORAGE_NULL} blog_storage.make diff --git a/modules/blog/cms_blog_module.e b/modules/blog/cms_blog_module.e index 0daf7cc..ec38c1e 100644 --- a/modules/blog/cms_blog_module.e +++ b/modules/blog/cms_blog_module.e @@ -83,7 +83,7 @@ feature {CMS_API} -- Module management sql: STRING do -- Schema - if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + if attached api.storage.as_sql_storage as l_sql_storage then if not l_sql_storage.sql_table_exists ("blog_post_nodes") then sql := "[ CREATE TABLE blog_post_nodes( diff --git a/modules/node/cms_node_module.e b/modules/node/cms_node_module.e index b6fd250..5da64a3 100644 --- a/modules/node/cms_node_module.e +++ b/modules/node/cms_node_module.e @@ -63,7 +63,7 @@ feature {CMS_API} -- Module Initialization Precursor (a_api) -- Storage initialization - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then + if attached a_api.storage.as_sql_storage as l_storage_sql then create {CMS_NODE_STORAGE_SQL} l_node_storage.make (l_storage_sql) else -- FIXME: in case of NULL storage, should Current be disabled? @@ -111,7 +111,7 @@ feature {CMS_API} -- Module management -- Is Current module installed? do Result := Precursor (a_api) - if Result and attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + if Result and attached a_api.storage.as_sql_storage as l_sql_storage then Result := l_sql_storage.sql_table_exists ("nodes") and l_sql_storage.sql_table_exists ("page_nodes") end @@ -120,7 +120,7 @@ feature {CMS_API} -- Module management install (a_api: CMS_API) do -- Schema - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + if attached a_api.storage.as_sql_storage as l_sql_storage then l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void) end Precursor {CMS_MODULE}(a_api) diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 321f451..0474636 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -72,7 +72,7 @@ feature {CMS_API} -- Module Initialization Precursor (a_api) -- Storage initialization - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then + if attached a_api.storage.as_sql_storage as l_storage_sql then create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) else -- FIXME: in case of NULL storage, should Current be disabled? @@ -93,7 +93,7 @@ feature {CMS_API} -- Module management l_consumers: LIST [STRING] do -- Schema - if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + if attached api.storage.as_sql_storage as l_sql_storage then if not l_sql_storage.sql_table_exists ("oauth2_consumers") then --| Schema l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void) @@ -112,7 +112,7 @@ feature {CMS_API} -- Module management else from l_sql_storage.sql_start - create {ARRAYED_LIST[STRING]} l_consumers.make (2) + create {ARRAYED_LIST [STRING]} l_consumers.make (2) until l_sql_storage.sql_after loop diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index 988085b..f098c50 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -74,7 +74,7 @@ feature {CMS_API} -- Module Initialization Precursor (a_api) -- Storage initialization - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then + if attached a_api.storage.as_sql_storage as l_storage_sql then create {CMS_OPENID_STORAGE_SQL} l_openid_storage.make (l_storage_sql) else -- FIXME: in case of NULL storage, should Current be disabled? @@ -93,7 +93,7 @@ feature {CMS_API} -- Module management install (api: CMS_API) do -- Schema - if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + if attached api.storage.as_sql_storage as l_sql_storage then if not l_sql_storage.sql_table_exists ("openid_consumers") then --| Schema l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers.sql")), Void) diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 578f589..76f571e 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -27,7 +27,7 @@ feature {NONE} -- Initialization Precursor -- Create the node storage for type blog - if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then + if attached storage.as_sql_storage as l_storage_sql then create {CMS_TAXONOMY_STORAGE_SQL} taxonomy_storage.make (l_storage_sql) else create {CMS_TAXONOMY_STORAGE_NULL} taxonomy_storage.make diff --git a/modules/taxonomy/site/scripts/install.sql b/modules/taxonomy/site/scripts/install.sql new file mode 100644 index 0000000..54b2130 --- /dev/null +++ b/modules/taxonomy/site/scripts/install.sql @@ -0,0 +1,13 @@ +CREATE TABLE taxonomy_term ( + `tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE, + `text` VARCHAR(255) NOT NULL, + `weight` INTEGER, + `description` TEXT, + `langcode` VARCHAR(12) +); + +CREATE TABLE taxonomy_hierarchy ( + `tid` INTEGER NOT NULL, + `parent` INTEGER, + CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent) +); diff --git a/modules/taxonomy/site/scripts/uninstall.sql b/modules/taxonomy/site/scripts/uninstall.sql new file mode 100644 index 0000000..1fc3a53 --- /dev/null +++ b/modules/taxonomy/site/scripts/uninstall.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS taxonomy_term; +DROP TABLE IF EXISTS taxonomy_hierarchy; diff --git a/src/persistence/cms_storage.e b/src/persistence/cms_storage.e index c45d9c1..f49ccbd 100644 --- a/src/persistence/cms_storage.e +++ b/src/persistence/cms_storage.e @@ -27,6 +27,16 @@ feature -- Access api: detachable CMS_API assign set_api -- Associated CMS API. +feature -- Conversion + + as_sql_storage: detachable CMS_STORAGE_SQL_I + -- SQL based variant of `Current' if possible. + do + if attached {CMS_STORAGE_SQL_I} Current as st then + Result := st + end + end + feature -- Status report is_available: BOOLEAN @@ -59,4 +69,7 @@ feature -- Element change api := a_api end +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end From f1f3c126dd4838560482703ed5d32e4e04c3443c Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 23 Nov 2015 18:05:53 +0100 Subject: [PATCH 4/5] Use module site files system for the (un)install SQL scripts. Changed CMS_TERM.id type to INTEGER_64 . Removed CMS_TERM.parent_id . Implemented CMS_TERM saving. --- modules/taxonomy/cms_taxonomy_module.e | 32 +--- modules/taxonomy/cms_term.e | 19 +- modules/taxonomy/cms_vocabulary.e | 4 +- .../persistence/cms_taxonomy_storage_i.e | 12 +- .../persistence/cms_taxonomy_storage_null.e | 12 +- .../persistence/cms_taxonomy_storage_sql.e | 169 +++++------------- 6 files changed, 78 insertions(+), 170 deletions(-) diff --git a/modules/taxonomy/cms_taxonomy_module.e b/modules/taxonomy/cms_taxonomy_module.e index ace013b..3ec79d5 100644 --- a/modules/taxonomy/cms_taxonomy_module.e +++ b/modules/taxonomy/cms_taxonomy_module.e @@ -53,30 +53,12 @@ feature {CMS_API} -- Module Initialization feature {CMS_API} -- Module management install (api: CMS_API) - local - sql: STRING do -- Schema if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then - if not l_sql_storage.sql_table_exists ("taxonomy_term") then - sql := "[ -CREATE TABLE taxonomy_term ( - `tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE, - `text` VARCHAR(255) NOT NULL, - `weight` INTEGER, - `description` TEXT, - `langcode` VARCHAR(12) -); -CREATE TABLE taxonomy_hierarchy ( - `tid` INTEGER NOT NULL, - `parent` INTEGER, - CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent) -); - ]" - l_sql_storage.sql_execute_script (sql, Void) - if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for taxonomy module", generating_type) - end + l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install").appended_with_extension ("sql")), Void) + if l_sql_storage.has_error then + api.logger.put_error ("Could not install database for taxonomy module", generating_type) end Precursor (api) end @@ -84,15 +66,9 @@ CREATE TABLE taxonomy_hierarchy ( uninstall (api: CMS_API) -- (export status {CMS_API}) - local - sql: STRING do if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then - sql := "[ -DROP TABLE IF EXISTS taxonomy_term; -DROP TABLE IF EXISTS taxonomy_hierarchy; - ]" - l_sql_storage.sql_execute_script (sql, Void) + l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("uninstall").appended_with_extension ("sql")), Void) if l_sql_storage.has_error then api.logger.put_error ("Could not remove database for taxonomy module", generating_type) end diff --git a/modules/taxonomy/cms_term.e b/modules/taxonomy/cms_term.e index 659f869..789dffb 100644 --- a/modules/taxonomy/cms_term.e +++ b/modules/taxonomy/cms_term.e @@ -16,7 +16,7 @@ create feature {NONE} -- Initialization - make (a_id: INTEGER; a_text: READABLE_STRING_GENERAL) + make (a_id: INTEGER_64; a_text: READABLE_STRING_GENERAL) do id := a_id set_text (a_text) @@ -24,7 +24,7 @@ feature {NONE} -- Initialization feature -- Access - id: INTEGER + id: INTEGER_64 -- Associated term id. text: IMMUTABLE_STRING_32 @@ -33,9 +33,6 @@ feature -- Access description: detachable IMMUTABLE_STRING_32 -- Optional description. - parent_id: INTEGER - -- Optional parent term id. - weight: INTEGER -- Associated weight for ordering. @@ -65,7 +62,7 @@ feature -- Comparison feature -- Element change - set_id (a_id: INTEGER) + set_id (a_id: INTEGER_64) do id := a_id end @@ -85,15 +82,5 @@ feature -- Element change create description.make_from_string_general (a_description) end - set_parent (a_parent: CMS_TERM) - do - parent_id := a_parent.id - end - - set_parent_id (a_parent_id: INTEGER) - do - parent_id := a_parent_id - end - end diff --git a/modules/taxonomy/cms_vocabulary.e b/modules/taxonomy/cms_vocabulary.e index e9cb979..3400f36 100644 --- a/modules/taxonomy/cms_vocabulary.e +++ b/modules/taxonomy/cms_vocabulary.e @@ -14,7 +14,7 @@ create feature {NONE} -- Initialization - make (a_tid: INTEGER; a_name: READABLE_STRING_GENERAL) + make (a_tid: INTEGER_64; a_name: READABLE_STRING_GENERAL) do id := a_tid set_name (a_name) @@ -29,7 +29,7 @@ feature {NONE} -- Initialization feature -- Access - id: INTEGER + id: INTEGER_64 name: IMMUTABLE_STRING_32 diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e index cc3e6ef..7a28ad3 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e @@ -27,7 +27,7 @@ feature -- Access deferred end - vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY -- Vocabulary by id `a_id'. require valid_id: a_id > 0 @@ -39,7 +39,7 @@ feature -- Access deferred end - term_by_id (tid: INTEGER): detachable CMS_TERM + term_by_id (tid: INTEGER_64): detachable CMS_TERM -- Term associated with id `tid'. deferred ensure @@ -60,5 +60,13 @@ feature -- Access deferred end +feature -- Store + + save_term (t: CMS_TERM) + -- Insert or update term `t'. + deferred + ensure + not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void + end end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e index 5795d4a..461278d 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e @@ -43,7 +43,7 @@ feature -- Access create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) end - vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY -- Vocabulary by id `a_id'. do end @@ -53,7 +53,7 @@ feature -- Access do end - term_by_id (tid: INTEGER): detachable CMS_TERM + term_by_id (tid: INTEGER_64): detachable CMS_TERM -- Term associated with id `tid'. do end @@ -64,4 +64,12 @@ feature -- Access create {ARRAYED_LIST [CMS_TERM]} Result.make (0) end +feature -- Store + + save_term (t: CMS_TERM) + -- Insert or update term `t'. + do + error_handler.add_custom_error (-1, "not implemented", "") + end + end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e index da06ddf..0d2d92b 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e @@ -35,7 +35,7 @@ feature -- Access create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) end - vocabulary (a_tid: INTEGER): detachable CMS_VOCABULARY + vocabulary (a_tid: INTEGER_64): detachable CMS_VOCABULARY -- Vocabulary by id `a_tid'. do if attached term_by_id (a_tid) as t then @@ -93,7 +93,7 @@ feature -- Access sql_finalize end - term_by_id (a_tid: INTEGER): detachable CMS_TERM + term_by_id (a_tid: INTEGER_64): detachable CMS_TERM local l_parameters: STRING_TABLE [detachable ANY] do @@ -112,122 +112,51 @@ feature -- Access feature -- Store save_term (t: CMS_TERM) + local + l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset + + create l_parameters.make (5) + l_parameters.put (t.text, "text") + l_parameters.put (t.description, "description") + l_parameters.put (t.weight, "weight") + + sql_begin_transaction + if t.has_id then + l_parameters.put (t.id, "tid") + sql_modify (sql_update_term, l_parameters) + else + sql_insert (sql_insert_term, l_parameters) + t.set_id (last_inserted_term_id) + end + if has_error then + sql_rollback_transaction + else + sql_commit_transaction + end + sql_finalize end --- blogs_count: INTEGER_64 --- -- --- do --- error_handler.reset --- write_information_log (generator + ".blogs_count") --- sql_query (sql_select_blog_count, Void) --- if not has_error and not sql_after then --- Result := sql_read_integer_64 (1) --- end --- sql_finalize --- end - --- blogs_count_from_user (a_user: CMS_USER) : INTEGER_64 --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- error_handler.reset --- write_information_log (generator + ".blogs_count_from_user") --- create l_parameters.make (2) --- l_parameters.put (a_user.id, "user") --- sql_query (sql_select_blog_count_from_user, l_parameters) --- if not has_error and not sql_after then --- Result := sql_read_integer_64 (1) --- end --- sql_finalize --- end - --- blogs: LIST [CMS_NODE] --- -- --- do --- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) - --- error_handler.reset --- write_information_log (generator + ".blogs") - --- from --- sql_query (sql_select_blogs_order_created_desc, Void) --- sql_start --- until --- sql_after --- loop --- if attached fetch_node as l_node then --- Result.force (l_node) --- end --- sql_forth --- end --- sql_finalize --- end - --- blogs_limited (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_NODE] --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) - --- error_handler.reset --- write_information_log (generator + ".blogs_limited") - --- from --- create l_parameters.make (2) --- l_parameters.put (a_limit, "limit") --- l_parameters.put (a_offset, "offset") --- sql_query (sql_blogs_limited, l_parameters) --- sql_start --- until --- sql_after --- loop --- if attached fetch_node as l_node then --- Result.force (l_node) --- end --- sql_forth --- end --- sql_finalize --- end - --- blogs_from_user_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_NODE] --- -- --- local --- l_parameters: STRING_TABLE [detachable ANY] --- do --- create {ARRAYED_LIST [CMS_NODE]} Result.make (0) - --- error_handler.reset --- write_information_log (generator + ".blogs_from_user_limited") - --- from --- create l_parameters.make (2) --- l_parameters.put (a_limit, "limit") --- l_parameters.put (a_offset, "offset") --- l_parameters.put (a_user.id, "user") --- sql_query (sql_blogs_from_user_limited, l_parameters) --- sql_start --- until --- sql_after --- loop --- if attached fetch_node as l_node then --- Result.force (l_node) --- end --- sql_forth --- end --- sql_finalize --- end - feature {NONE} -- Queries + last_inserted_term_id: INTEGER_64 + -- Last insert term id. + do + error_handler.reset + sql_query (Sql_last_inserted_term_id, Void) + if not has_error and not sql_after then + Result := sql_read_integer_64 (1) + end + sql_finalize + end + fetch_term: detachable CMS_TERM local - tid: INTEGER + tid: INTEGER_64 l_text: detachable READABLE_STRING_32 do - tid := sql_read_integer_32 (1) + tid := sql_read_integer_64 (1) l_text := sql_read_string_32 (2) if tid > 0 and l_text /= Void then create Result.make (tid, l_text) @@ -256,18 +185,18 @@ feature {NONE} -- Queries sql_select_term: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE tid = :tid;" -- Term with tid :tid . --- sql_select_blog_count_from_user: STRING = "SELECT count(*) FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ;" --- -- Nodes count (Published and not Published) --- --| note: {CMS_NODE_API}.trashed = -1 + Sql_last_inserted_term_id: STRING = "SELECT MAX(tid) FROM taxonomy_term;" --- sql_select_blogs_order_created_desc: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC;" --- -- SQL Query to retrieve all nodes that are from the type "blog" ordered by descending creation date. - --- sql_blogs_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC LIMIT :limit OFFSET :offset ;" --- --- SQL Query to retrieve all node of type "blog" limited by limit and starting at offset - --- sql_blogs_from_user_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ORDER BY created DESC LIMIT :limit OFFSET :offset ;" --- --- SQL Query to retrieve all node of type "blog" from author with id limited by limit + offset + sql_insert_term: STRING = "[ + INSERT INTO taxonomy_terms (tid, text, weight, description, langcode) + VALUES (:tid, :text, :weight, :description, null); + ]" + sql_update_term: STRING = "[ + UPDATE taxonomy_terms + SET tid=:tid, text=:text, weight=:weight, description=:description, langcode=null + WHERE tid=:tid; + ]" + end From 20dfce139653b93be0fc86e4d68ec1e72e5338c0 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 3 Dec 2015 19:24:58 +0100 Subject: [PATCH 5/5] Improved taxonomy by supporting tags, multiple terms allowed, and required kind of vocabulary for specific content type. Updated node web form, to support taxonomy editing if allowed (specific support for CMS_VOCABULARY.is_tags: BOOLEAN). Added notion of required or optional module dependencies. --- examples/demo/install_modules.bat | 1 + .../modules/taxonomy/files/css/taxonomy.css | 21 ++ .../modules/taxonomy/files/scss/taxonomy.scss | 21 ++ .../site/modules/taxonomy/scripts/install.sql | 24 ++ .../modules/taxonomy/scripts/uninstall.sql | 3 + modules/node/cms_node_module.e | 3 + .../handler/cms_node_type_webform_manager.e | 256 ++++++++++++- modules/node/handler/node_form_response.e | 4 +- modules/node/node-safe.ecf | 1 + modules/node/node.ecf | 1 + modules/taxonomy/cms_taxonomy_api.e | 138 ++++++- modules/taxonomy/cms_taxonomy_module.e | 23 +- modules/taxonomy/cms_term.e | 28 +- modules/taxonomy/cms_term_collection.e | 92 +++++ modules/taxonomy/cms_vocabulary.e | 107 ++++-- modules/taxonomy/cms_vocabulary_collection.e | 98 +++++ .../persistence/cms_taxonomy_storage_i.e | 67 +++- .../persistence/cms_taxonomy_storage_null.e | 60 ++- .../persistence/cms_taxonomy_storage_sql.e | 342 +++++++++++++++++- modules/taxonomy/site/files/css/taxonomy.css | 21 ++ .../taxonomy/site/files/scss/taxonomy.scss | 21 ++ modules/taxonomy/site/scripts/install.sql | 11 + modules/taxonomy/site/scripts/uninstall.sql | 1 + modules/taxonomy/taxonomy.ecf | 26 ++ src/configuration/cms_setup.e | 18 +- src/persistence/sql/cms_storage_sql_i.e | 9 +- src/service/cms_module.e | 26 +- 27 files changed, 1339 insertions(+), 84 deletions(-) create mode 100644 examples/demo/site/modules/taxonomy/files/css/taxonomy.css create mode 100644 examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss create mode 100644 examples/demo/site/modules/taxonomy/scripts/install.sql create mode 100644 examples/demo/site/modules/taxonomy/scripts/uninstall.sql create mode 100644 modules/taxonomy/cms_term_collection.e create mode 100644 modules/taxonomy/cms_vocabulary_collection.e create mode 100644 modules/taxonomy/site/files/css/taxonomy.css create mode 100644 modules/taxonomy/taxonomy.ecf diff --git a/examples/demo/install_modules.bat b/examples/demo/install_modules.bat index c3def68..98de61d 100644 --- a/examples/demo/install_modules.bat +++ b/examples/demo/install_modules.bat @@ -12,3 +12,4 @@ set ROC_CMS_DIR=%~dp0 %ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\google_search --dir %ROC_CMS_DIR% +%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR% diff --git a/examples/demo/site/modules/taxonomy/files/css/taxonomy.css b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css new file mode 100644 index 0000000..41c87fa --- /dev/null +++ b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css @@ -0,0 +1,21 @@ +ul.taxonomy { + font-size: 80%; + list-style-type: none; + font-style: italic; + margin: 0; +} +ul.taxonomy li { + padding: 2px; + margin-right: 3px; + display: inline-block; + border: none; +} +ul.taxonomy li a:hover { + text-decoration: none; +} +ul.taxonomy li:hover { + padding: 1px; + border-top: solid 1px #66f; + border-bottom: solid 1px #66f; + background-color: #ddf; +} diff --git a/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss new file mode 100644 index 0000000..f409395 --- /dev/null +++ b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss @@ -0,0 +1,21 @@ +ul.taxonomy { + font-size: 80%; + list-style-type: none; + font-style: italic; + margin: 0; + li { + a:hover { + text-decoration: none; + } + padding: 2px; + margin-right: 3px; + display: inline-block; + border: none; + &:hover { + padding: 1px; + border-top: solid 1px #66f; + border-bottom: solid 1px #66f; + background-color: #ddf; + } + } +} diff --git a/examples/demo/site/modules/taxonomy/scripts/install.sql b/examples/demo/site/modules/taxonomy/scripts/install.sql new file mode 100644 index 0000000..2eaf9a2 --- /dev/null +++ b/examples/demo/site/modules/taxonomy/scripts/install.sql @@ -0,0 +1,24 @@ + +CREATE TABLE taxonomy_term ( + `tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE, + `text` VARCHAR(255) NOT NULL, + `weight` INTEGER, + `description` TEXT, + `langcode` VARCHAR(12) +); + +CREATE TABLE taxonomy_hierarchy ( + `tid` INTEGER NOT NULL, + `parent` INTEGER, + CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent) +); + +/* Associate tid with unique (type,entity) + * for instance: "page" + "$nid" -> "tid" + */ +CREATE TABLE taxonomy_index ( + `tid` INTEGER NOT NULL, + `entity` VARCHAR(255), + `type` VARCHAR(255) NOT NULL, + CONSTRAINT PK_tid_entity_type PRIMARY KEY (tid,entity,type) +); diff --git a/examples/demo/site/modules/taxonomy/scripts/uninstall.sql b/examples/demo/site/modules/taxonomy/scripts/uninstall.sql new file mode 100644 index 0000000..0efae92 --- /dev/null +++ b/examples/demo/site/modules/taxonomy/scripts/uninstall.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS taxonomy_term; +DROP TABLE IF EXISTS taxonomy_hierarchy; +DROP TABLE IF EXISTS taxonomy_index; diff --git a/modules/node/cms_node_module.e b/modules/node/cms_node_module.e index 5da64a3..0ea6f2d 100644 --- a/modules/node/cms_node_module.e +++ b/modules/node/cms_node_module.e @@ -41,6 +41,9 @@ feature {NONE} -- Initialization description := "Service to manage content based on 'node'" package := "core" config := a_setup + -- Optional dependencies, mainly for information. + put_dependency ({CMS_RECENT_CHANGES_MODULE}, False) + put_dependency ({CMS_TAXONOMY_MODULE}, False) end config: CMS_SETUP diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index 4876844..05ea3dd 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -48,8 +48,8 @@ feature -- Forms ... if a_node /= Void then ta.set_text_value (a_node.content) end - ta.set_label ("Content") - ta.set_description ("This is the main content") + ta.set_label (response.translation ("Content", Void)) + ta.set_description (response.translation ("This is the main content", Void)) ta.set_is_required (False) -- Summary @@ -61,8 +61,8 @@ feature -- Forms ... if a_node /= Void then sum.set_text_value (a_node.summary) end - sum.set_label ("Summary") - sum.set_description ("Text displayed in short view.") + sum.set_label (response.translation ("Summary", Void)) + sum.set_description (response.translation ("Text displayed in short view.", Void)) sum.set_is_required (False) create fset.make @@ -93,9 +93,230 @@ feature -- Forms ... f.extend (fset) -- Path alias + populate_form_with_taxonomy (response, f, a_node) populate_form_with_path_alias (response, f, a_node) end + populate_form_with_taxonomy (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) + local + ti: detachable WSF_FORM_TEXT_INPUT + w_set: WSF_FORM_FIELD_SET + w_select: WSF_FORM_SELECT + w_opt: WSF_FORM_SELECT_OPTION + w_cb: WSF_FORM_CHECKBOX_INPUT + w_voc_set: WSF_FORM_FIELD_SET + s: STRING_32 + voc: CMS_VOCABULARY + t: detachable CMS_TERM + l_terms: detachable CMS_TERM_COLLECTION + l_has_edit_permission: BOOLEAN + do + if + attached {CMS_TAXONOMY_API} response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then + attached l_taxonomy_api.vocabularies_for_type (content_type.name) as l_vocs and then not l_vocs.is_empty + then + + l_has_edit_permission := response.has_permissions (<<"update any taxonomy", "update " + content_type.name + " taxonomy">>) + + -- Handle Taxonomy fields, if any associated with `content_type'. + create w_set.make + w_set.add_css_class ("taxonomy") + l_vocs.sort + across + l_vocs as vocs_ic + loop + voc := vocs_ic.item + l_terms := Void + if a_node /= Void and then a_node.has_id then + l_terms := l_taxonomy_api.terms_of_entity (a_node.content_type, a_node.id.out, voc) + if l_terms /= Void then + l_terms.sort + end + end + create w_voc_set.make + w_set.extend (w_voc_set) + + if voc.is_tags then + w_voc_set.set_legend (response.translation (voc.name, Void)) + + create ti.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + w_voc_set.extend (ti) + if voc.is_term_required then + ti.enable_required + end + if attached voc.description as l_desc then + ti.set_description (response.html_encoded (response.translation (l_desc, Void))) + else + ti.set_description (response.html_encoded (response.translation (voc.name, Void))) + end + ti.set_size (70) + if l_terms /= Void then + create s.make_empty + across + l_terms as ic + loop + t := ic.item + if not s.is_empty then + s.append_character (',') + s.append_character (' ') + end + if ic.item.text.has (' ') then + s.append_character ('"') + s.append (t.text) + s.append_character ('"') + else + s.append (t.text) + end + end + ti.set_text_value (s) + end + if not l_has_edit_permission then + ti.set_is_readonly (True) + end + else + l_taxonomy_api.fill_vocabularies_with_terms (voc) + if not voc.terms.is_empty then + if voc.multiple_terms_allowed then + if attached voc.description as l_desc then + w_voc_set.set_legend (response.html_encoded (l_desc)) + else + w_voc_set.set_legend (response.html_encoded (voc.name)) + end + across + voc as voc_terms_ic + loop + t := voc_terms_ic.item + create w_cb.make_with_value ({STRING_32} "taxonomy_terms[" + voc.name + "]", t.text) + w_voc_set.extend (w_cb) + if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then + w_cb.set_checked (True) + end + if not l_has_edit_permission then + w_cb.set_is_readonly (True) + end + end + else + create w_select.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + w_voc_set.extend (w_select) + + if attached voc.description as l_desc then + w_select.set_description (response.html_encoded (l_desc)) + else + w_select.set_description (response.html_encoded (voc.name)) + end + w_voc_set.set_legend (response.html_encoded (voc.name)) + + across + voc as voc_terms_ic + loop + t := voc_terms_ic.item + create w_opt.make (response.html_encoded (t.text), response.html_encoded (t.text)) + w_select.add_option (w_opt) + + if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then + w_opt.set_is_selected (True) + end + end + if not l_has_edit_permission then + w_select.set_is_readonly (True) + end + end + end + end + end + + f.submit_actions.extend (agent taxonomy_submit_action (response, l_taxonomy_api, l_vocs, a_node, ?)) + + if + attached f.fields_by_name ("title") as l_title_fields and then + attached l_title_fields.first as l_title_field + then + f.insert_after (w_set, l_title_field) + else + f.extend (w_set) + end + end + end + + taxonomy_submit_action (a_response: CMS_RESPONSE; a_taxonomy_api: CMS_TAXONOMY_API; a_vocs: CMS_VOCABULARY_COLLECTION; a_node: detachable CMS_NODE fd: WSF_FORM_DATA) + require + vocs_not_empty: not a_vocs.is_empty + local + l_voc_name: READABLE_STRING_32 + l_terms_to_remove: ARRAYED_LIST [CMS_TERM] + l_new_terms: LIST [READABLE_STRING_32] + l_text: READABLE_STRING_GENERAL + l_found: BOOLEAN + t: detachable CMS_TERM + do + if + a_node /= Void and then a_node.has_id and then + attached fd.table_item ("taxonomy_terms") as fd_terms + then + across + fd_terms.values as ic + loop + if attached {WSF_STRING} ic.item as l_string then + l_voc_name := ic.key + l_new_terms := a_taxonomy_api.splitted_string (l_string.value, ',') + if attached a_vocs.item_by_name (l_voc_name) as voc then + if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then + create l_terms_to_remove.make (0) + if attached a_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, voc) as l_existing_terms then + across + l_existing_terms as t_ic + loop + l_text := t_ic.item.text + from + l_found := False + l_new_terms.start + until + l_new_terms.after + loop + if l_new_terms.item.same_string_general (l_text) then + -- Already associated with term `t_ic.text'. + l_found := True + l_new_terms.remove + else + l_new_terms.forth + end + end + if not l_found then + -- Remove term + l_terms_to_remove.force (t_ic.item) + end + end + across + l_terms_to_remove as t_ic + loop + a_taxonomy_api.unassociate_term_from_entity (t_ic.item, content_type.name, a_node.id.out) + end + end + across + l_new_terms as t_ic + loop + t := a_taxonomy_api.term_by_text (t_ic.item, voc) + if + t = Void and voc.is_tags + then + -- Create new term! + create t.make (t_ic.item) + a_taxonomy_api.save_term (t, voc) + if a_taxonomy_api.has_error then + t := Void + end + end + if t /= Void then + a_taxonomy_api.associate_term_with_entity (t, content_type.name, a_node.id.out) + end + end + end + end + end + end + end + end + populate_form_with_path_alias (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) local ti: WSF_FORM_TEXT_INPUT @@ -316,6 +537,33 @@ feature -- Output s.append ("") + if + attached {CMS_TAXONOMY_API} a_response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then + attached l_taxonomy_api.vocabularies_for_type (content_type.name) as vocs and then not vocs.is_empty + then + vocs.sort + across + vocs as ic + loop + if + attached l_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, ic.item) as l_terms and then + not l_terms.is_empty + then + s.append ("
    ") + s.append (a_response.html_encoded (ic.item.name)) + s.append (": ") + across + l_terms as t_ic + loop + s.append ("
  • ") + a_response.append_link_to_html (t_ic.item.text, "taxonomy/term/" + t_ic.item.id.out, Void, s) + s.append ("
  • ") + end + s.append ("
%N") + end + end + end + -- We don't show the summary on the detail page, since its just a short view of the full content. Otherwise we would write the same thing twice. -- The usage of the summary is to give a short overview in the list of nodes or for the meta tag "description" diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e index 4b68eca..25f5c34 100644 --- a/modules/node/handler/node_form_response.e +++ b/modules/node/handler/node_form_response.e @@ -117,7 +117,7 @@ feature {NONE} -- Create a new node hooks.invoke_form_alter (f, fd, Current) 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, a_type, b)) + f.submit_actions.put_front (agent edit_form_submit (?, l_node, a_type, b)) f.process (Current) fd := f.last_data end @@ -147,7 +147,7 @@ feature {NONE} -- Create a new node hooks.invoke_form_alter (f, fd, Current) if request.is_post_request_method then f.validation_actions.extend (agent edit_form_validate (?, b)) - f.submit_actions.extend (agent edit_form_submit (?, a_node, a_type, b)) + f.submit_actions.put_front (agent edit_form_submit (?, a_node, a_type, b)) f.process (Current) fd := f.last_data end diff --git a/modules/node/node-safe.ecf b/modules/node/node-safe.ecf index f024e33..00ea6fc 100644 --- a/modules/node/node-safe.ecf +++ b/modules/node/node-safe.ecf @@ -14,6 +14,7 @@ + diff --git a/modules/node/node.ecf b/modules/node/node.ecf index 3fe2d71..bfbfead 100644 --- a/modules/node/node.ecf +++ b/modules/node/node.ecf @@ -14,6 +14,7 @@ + diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 76f571e..f0b3ea7 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -46,7 +46,7 @@ feature -- Access node Result := taxonomy_storage.vocabulary_count end - vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_VOCABULARY] + vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION -- List of vocabularies ordered by weight and limited by limit and offset. do Result := taxonomy_storage.vocabularies (a_limit, a_offset) @@ -60,6 +60,26 @@ feature -- Access node Result := taxonomy_storage.vocabulary (a_id) end + vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies associated with content type `a_type_name'. + do + Result := taxonomy_storage.vocabularies_for_type (a_type_name) + end + + fill_vocabularies_with_terms (a_vocab: CMS_VOCABULARY) + -- Fill `a_vocab' with associated terms. + do + reset_error + a_vocab.terms.wipe_out + if attached terms (a_vocab, 0, 0) as lst then + across + lst as ic + loop + a_vocab.extend (ic.item) + end + end + end + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 -- Number of terms from vocabulary `a_vocab'. require @@ -68,7 +88,14 @@ feature -- Access node Result := taxonomy_storage.term_count_from_vocabulary (a_vocab) end - terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_TERM] + terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION + -- Terms related to `(a_type_name,a_entity)', and if `a_vocabulary' is set + -- constrain to be part of `a_vocabulary'. + do + Result := taxonomy_storage.terms_of_entity (a_type_name, a_entity, a_vocabulary) + end + + terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION -- List of terms ordered by weight and limited by limit and offset. require has_id: a_vocab.has_id @@ -81,4 +108,111 @@ feature -- Access node Result := taxonomy_storage.term_by_id (a_tid) end + term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM + -- Term with text `a_term_text', included in vocabulary `a_vocabulary' if provided. + do + Result := taxonomy_storage.term_by_text (a_term_text, a_vocabulary) + end + +feature -- Write + + save_vocabulary (a_voc: CMS_VOCABULARY) + do + reset_error + taxonomy_storage.save_vocabulary (a_voc) + error_handler.append (taxonomy_storage.error_handler) + end + + save_term (a_term: CMS_TERM; voc: CMS_VOCABULARY) + do + reset_error + taxonomy_storage.save_term (a_term, voc) + error_handler.append (taxonomy_storage.error_handler) + end + + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Associate term `a_term' with `(a_type_name, a_entity)'. + do + reset_error + taxonomy_storage.associate_term_with_entity (a_term, a_type_name, a_entity) + error_handler.append (taxonomy_storage.error_handler) + end + + unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Unassociate term `a_term' from `(a_type_name, a_entity)'. + do + reset_error + taxonomy_storage.unassociate_term_from_entity (a_term, a_type_name, a_entity) + error_handler.append (taxonomy_storage.error_handler) + end + + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Associate vocabulary `a_voc' with type `a_type_name'. + require + existing_term: a_voc.has_id + do + reset_error + taxonomy_storage.associate_vocabulary_with_type (a_voc, a_type_name) + error_handler.append (taxonomy_storage.error_handler) + end + + unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Un-associate vocabulary `a_voc' from type `a_type_name'. + require + existing_term: a_voc.has_id + do + reset_error + taxonomy_storage.unassociate_vocabulary_with_type (a_voc, a_type_name) + error_handler.append (taxonomy_storage.error_handler) + end + +feature -- Helpers + + splitted_string (s: READABLE_STRING_32; sep: CHARACTER): LIST [READABLE_STRING_32] + -- Splitted string from `s' with separator `sep', and support '"..."' wrapping. + local + i,j,n,b: INTEGER + t: STRING_32 + do + create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (1) + Result.compare_objects + from + i := 1 + b := 1 + n := s.count + create t.make_empty + until + i > n + loop + if s[i].is_space then + if not t.is_empty then + t.append_character (s[i]) + end + elseif s[i] = sep then + t.left_adjust + t.right_adjust + if t.count > 2 and t.starts_with_general ("%"") and t.ends_with_general ("%"") then + t.remove_head (1) + t.remove_tail (1) + end + Result.force (t) + create t.make_empty + elseif s[i] = '"' then + j := s.index_of ('"', i + 1) + if j > 0 then + t.append (s.substring (i, j)) + end + i := j + else + t.append_character (s[i]) + end + i := i + 1 + end + if not t.is_empty then + t.left_adjust + t.right_adjust + Result.force (t) + end + end + end diff --git a/modules/taxonomy/cms_taxonomy_module.e b/modules/taxonomy/cms_taxonomy_module.e index 3ec79d5..77a1249 100644 --- a/modules/taxonomy/cms_taxonomy_module.e +++ b/modules/taxonomy/cms_taxonomy_module.e @@ -17,7 +17,8 @@ inherit initialize, install, uninstall, - taxonomy_api + taxonomy_api, + permissions end CMS_HOOK_MENU_SYSTEM_ALTER @@ -41,6 +42,16 @@ feature -- Access name: STRING = "taxonomy" + permissions: LIST [READABLE_STRING_8] + -- List of permission ids, used by this module, and declared. + do + Result := Precursor + Result.force ("admin taxonomy") + Result.force ("update any taxonomy") + Result.force ("update page taxonomy") -- related to node module + Result.force ("update blog taxonomy") -- related to blog module + end + feature {CMS_API} -- Module Initialization initialize (api: CMS_API) @@ -53,6 +64,9 @@ feature {CMS_API} -- Module Initialization feature {CMS_API} -- Module management install (api: CMS_API) + local + voc: CMS_VOCABULARY + l_taxonomy_api: like taxonomy_api do -- Schema if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then @@ -61,6 +75,13 @@ feature {CMS_API} -- Module management api.logger.put_error ("Could not install database for taxonomy module", generating_type) end Precursor (api) + + create l_taxonomy_api.make (api) + create voc.make ("Tags") + voc.set_description ("Enter comma separated tags.") + l_taxonomy_api.save_vocabulary (voc) + voc.set_is_tags (True) + l_taxonomy_api.associate_vocabulary_with_type (voc, "page") end end diff --git a/modules/taxonomy/cms_term.e b/modules/taxonomy/cms_term.e index 789dffb..aadb153 100644 --- a/modules/taxonomy/cms_term.e +++ b/modules/taxonomy/cms_term.e @@ -11,14 +11,25 @@ class inherit COMPARABLE + DEBUG_OUTPUT + undefine + is_equal + end + create - make + make, + make_with_id feature {NONE} -- Initialization - make (a_id: INTEGER_64; a_text: READABLE_STRING_GENERAL) + make_with_id (a_id: INTEGER_64; a_text: READABLE_STRING_GENERAL) do id := a_id + make (a_text) + end + + make (a_text: READABLE_STRING_GENERAL) + do set_text (a_text) end @@ -44,6 +55,19 @@ feature -- Status report Result := id > 0 end + debug_output: STRING_32 + -- String that should be displayed in debugger to represent `Current'. + do + create Result.make_empty + Result.append_character ('#') + Result.append (id.out) + Result.append_character (' ') + Result.append (text) + Result.append_character (' ') + Result.append ("weight=") + Result.append_integer (weight) + end + feature -- Comparison is_less alias "<" (other: like Current): BOOLEAN diff --git a/modules/taxonomy/cms_term_collection.e b/modules/taxonomy/cms_term_collection.e new file mode 100644 index 0000000..5e882e7 --- /dev/null +++ b/modules/taxonomy/cms_term_collection.e @@ -0,0 +1,92 @@ +note + description: "[ + Collection of CMS terms (see Taxonomy). + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TERM_COLLECTION + +inherit + ITERABLE [CMS_TERM] + +create + make + +feature {NONE} -- Initialization + + make (nb: INTEGER) + do + create items.make (nb) + end + +feature -- Access + + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_TERM] + -- + do + Result := items.new_cursor + end + + count: INTEGER + -- Number of terms. + do + Result := items.count + end + +feature -- Status report + + is_empty: BOOLEAN + do + Result := count = 0 + end + + has (a_term: CMS_TERM): BOOLEAN + -- Has `a_term'? + do + Result := items.has (a_term) + end + +feature -- Element change + + wipe_out + -- Remove all items. + do + items.wipe_out + ensure + empty: count = 0 + end + + force, extend (a_term: CMS_TERM) + -- Add term `a_term'; + do + if not has (a_term) then + items.force (a_term) + end + end + + remove (a_term: CMS_TERM) + -- Remove term `a_term'. + do + items.prune_all (a_term) + end + + sort + -- Sort `items' + local + l_sorter: QUICK_SORTER [CMS_TERM] + do + create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_TERM]}) + l_sorter.sort (items) + end + +feature {NONE} -- Implementation + + items: ARRAYED_LIST [CMS_TERM] + -- List of terms. + +;note + copyright: "2011-2015, 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/modules/taxonomy/cms_vocabulary.e b/modules/taxonomy/cms_vocabulary.e index 3400f36..4ab45a9 100644 --- a/modules/taxonomy/cms_vocabulary.e +++ b/modules/taxonomy/cms_vocabulary.e @@ -8,55 +8,120 @@ note class CMS_VOCABULARY +inherit + CMS_TERM + rename + text as name, + set_text as set_name + redefine + make + end + + ITERABLE [CMS_TERM] + undefine + is_equal + end + create make, - make_from_term + make_with_id, + make_from_term, + make_none feature {NONE} -- Initialization - make (a_tid: INTEGER_64; a_name: READABLE_STRING_GENERAL) + make_none do - id := a_tid - set_name (a_name) - create {ARRAYED_LIST [CMS_TERM]} items.make (0) + make ("") + end + + make (a_name: READABLE_STRING_GENERAL) + do + Precursor (a_name) + create terms.make (0) end make_from_term (a_term: CMS_TERM) do - make (a_term.id, a_term.text) + make_with_id (a_term.id, a_term.text) description := a_term.description + set_weight (a_term.weight) end feature -- Access - id: INTEGER_64 - - name: IMMUTABLE_STRING_32 - - description: detachable READABLE_STRING_32 - - items: LIST [CMS_TERM] + terms: CMS_TERM_COLLECTION -- Collection of terms. - has_id: BOOLEAN + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_TERM] + -- do - Result := id > 0 + Result := terms.new_cursor + end + +feature -- Status report + + associated_content_type: detachable READABLE_STRING_GENERAL + -- Associated content type, if any. + + is_tags: BOOLEAN + -- New terms accepted (as tags), in the context of `associated_content_type'? + + multiple_terms_allowed: BOOLEAN + -- Accepts multiple terms, in the context of `associated_content_type'? + + is_term_required: BOOLEAN + -- At least one term is required, in the context of `associated_content_type'? + +feature -- Element change + + set_is_tags (b: BOOLEAN) + -- Set `is_tags' to `b'. + do + is_tags := b + end + + allow_multiple_term (b: BOOLEAN) + -- Set `multiple_terms_allowed' to `b'. + do + multiple_terms_allowed := b + end + + set_is_term_required (b: BOOLEAN) + -- Set `is_term_required' to `b'. + do + is_term_required := b + end + + set_associated_content_type (a_type: detachable READABLE_STRING_GENERAL; a_is_tags, a_multiple, a_is_required: BOOLEAN) + -- If `a_type' is set, define `associated_content_type' and related options, + -- otherwise reset `associated_content_type'. + do + if a_type = Void then + associated_content_type := Void + set_is_tags (False) + allow_multiple_term (False) + set_is_term_required (False) + else + associated_content_type := a_type + set_is_tags (a_is_tags) + allow_multiple_term (a_multiple) + set_is_term_required (a_is_required) + end end feature -- Element change - set_name (a_name: READABLE_STRING_GENERAL) + force, extend (a_term: CMS_TERM) + -- Add `a_term' to the vocabulary terms `terms'. do - create name.make_from_string_general (a_name) + terms.force (a_term) end sort -- Sort `items' - local - l_sorter: QUICK_SORTER [CMS_TERM] do - create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_TERM]}) - l_sorter.sort (items) + terms.sort end end diff --git a/modules/taxonomy/cms_vocabulary_collection.e b/modules/taxonomy/cms_vocabulary_collection.e new file mode 100644 index 0000000..b4c3028 --- /dev/null +++ b/modules/taxonomy/cms_vocabulary_collection.e @@ -0,0 +1,98 @@ +note + description: "[ + Collection of CMS vocabularies (see Taxonomy). + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_VOCABULARY_COLLECTION + +inherit + ITERABLE [CMS_VOCABULARY] + +create + make + +feature {NONE} -- Initialization + + make (nb: INTEGER) + do + create items.make (nb) + end + +feature -- Access + + item_by_name (a_voc_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY + -- Vocabulary from current collection associated with name `a_voc_name', if any. + do + across + items as ic + until + Result /= Void + loop + if ic.item.name.is_case_insensitive_equal_general (a_voc_name) then + Result := ic.item + end + end + end + + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_VOCABULARY] + -- + do + Result := items.new_cursor + end + + count: INTEGER + -- Number of vocabularies. + do + Result := items.count + end + +feature -- Status report + + is_empty: BOOLEAN + do + Result := count = 0 + end + + has (a_vocabulary: CMS_VOCABULARY): BOOLEAN + -- Has `a_vocabulary'? + do + Result := items.has (a_vocabulary) + end + +feature -- Element change + + force, extend (a_vocabulary: CMS_VOCABULARY) + -- Add vocabulary `a_vocabulary'; + do + if not has (a_vocabulary) then + items.force (a_vocabulary) + end + end + + remove (a_vocabulary: CMS_VOCABULARY) + -- Remove vocabulary `a_vocabulary'. + do + items.prune_all (a_vocabulary) + end + + sort + -- Sort `items' + local + l_sorter: QUICK_SORTER [CMS_VOCABULARY] + do + create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_VOCABULARY]}) + l_sorter.sort (items) + end + +feature {NONE} -- Implementation + + items: ARRAYED_LIST [CMS_VOCABULARY] + -- List of vocabularies. + +;note + copyright: "2011-2015, 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/modules/taxonomy/persistence/cms_taxonomy_storage_i.e b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e index 7a28ad3..559d0d7 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e @@ -22,8 +22,8 @@ feature -- Access deferred end - vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] - -- List of vocabularies ordered by weight from offset to offset + limit. + vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION + -- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'. deferred end @@ -34,6 +34,13 @@ feature -- Access deferred end + vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies associated with content type `a_type_name'. + require + valid_type_name: not a_type_name.is_whitespace + deferred + end + terms_count: INTEGER_64 -- Number of terms. deferred @@ -46,6 +53,13 @@ feature -- Access Result /= Void implies Result.id = tid end + term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM + -- Term with text `a_term_text', included in vocabulary `a_vocabulary' if provided. + deferred + ensure + Result /= Void implies a_term_text.same_string (Result.text) + end + term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64 -- Number of terms from vocabulary `a_vocab'. require @@ -53,20 +67,61 @@ feature -- Access deferred end - terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] - -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION + -- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'. require has_id: a_vocab.has_id deferred end + terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION + -- Terms related to `(a_type_name,a_entity)', and if `a_vocabulary' is set + -- constrain to be part of `a_vocabulary'. + deferred + end + feature -- Store - save_term (t: CMS_TERM) - -- Insert or update term `t'. + save_vocabulary (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc'. + deferred + ensure + not error_handler.has_error implies a_voc.has_id and then vocabulary (a_voc.id) /= Void + end + + save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Insert or update term `t' as part of vocabulary `voc'. deferred ensure not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Associate term `a_term' with `(a_type_name, a_entity)'. + require + existing_term: a_term.has_id + deferred + end + + unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Unassociate term `a_term' from `(a_type_name, a_entity)'. + require + existing_term: a_term.has_id + deferred + end + + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Associate vocabulary `a_voc' with type `a_type_name'. + require + existing_term: a_voc.has_id + deferred + end + + unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Un-associate vocabulary `a_voc' from type `a_type_name'. + require + existing_term: a_voc.has_id + deferred + end + end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e index 461278d..bd8f05d 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e @@ -37,10 +37,10 @@ feature -- Access do end - vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] - -- List of vocabularies ordered by weight from offset to offset + limit. + vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION + -- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'. do - create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) + create Result.make (0) end vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY @@ -48,6 +48,11 @@ feature -- Access do end + vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION + -- + do + end + terms_count: INTEGER_64 -- Number of terms. do @@ -58,18 +63,55 @@ feature -- Access do end - terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] - -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM + do + end + + terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION + -- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'. + do + create Result.make (0) + end + + terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION + -- Terms related to `(a_type_name,a_entity)'. do - create {ARRAYED_LIST [CMS_TERM]} Result.make (0) end feature -- Store - save_term (t: CMS_TERM) - -- Insert or update term `t'. + save_vocabulary (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc'. do - error_handler.add_custom_error (-1, "not implemented", "") + error_handler.add_custom_error (-1, "not implemented", "save_vocabulary") + end + + save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + -- + do + error_handler.add_custom_error (-1, "not implemented", "save_term") + end + + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + do + error_handler.add_custom_error (-1, "not implemented", "associate_term_with_entity") + end + + unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + do + error_handler.add_custom_error (-1, "not implemented", "unassociate_term_from_entity") + end + + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Associate vocabulary `a_voc' with type `a_type_name'. + do + error_handler.add_custom_error (-1, "not implemented", "associate_vocabulary_with_type") + end + + unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- Un-associate vocabulary `a_voc' from type `a_type_name'. + do + error_handler.add_custom_error (-1, "not implemented", "unassociate_vocabulary_with_type") end end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e index 0d2d92b..5ed17dc 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e @@ -29,10 +29,28 @@ feature -- Access sql_finalize end - vocabularies (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_VOCABULARY] - -- List of vocabularies ordered by weight from offset to offset + limit. + vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION + -- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'. + local + l_parameters: STRING_TABLE [detachable ANY] do - create {ARRAYED_LIST [CMS_VOCABULARY]} Result.make (0) + create Result.make (0) + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (0, "parent_tid") + from + sql_query (sql_select_terms, l_parameters) + sql_start + until + sql_after + loop + if attached fetch_term as l_term then + Result.force (create {CMS_VOCABULARY}.make_from_term (l_term)) + end + sql_forth + end + sql_finalize end vocabulary (a_tid: INTEGER_64): detachable CMS_VOCABULARY @@ -58,18 +76,21 @@ feature -- Access sql_finalize end - terms (a_vocab: CMS_VOCABULARY; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_TERM] - -- List of terms from vocabulary `a_vocab' ordered by weight from offset to offset + limit. + terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION + -- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'. local l_parameters: STRING_TABLE [detachable ANY] do - create {ARRAYED_LIST [CMS_TERM]} Result.make (0) + create Result.make (0) error_handler.reset - create l_parameters.make (1) + create l_parameters.make (3) l_parameters.put (a_vocab.id, "parent_tid") +-- l_parameters.put (a_limit, "limit") +-- l_parameters.put (a_offset, "offset") from sql_query (sql_select_terms, l_parameters) +-- sql_query (sql_select_terms_with_range, l_parameters) sql_start until sql_after @@ -109,9 +130,89 @@ feature -- Access sql_finalize end + term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (1) + l_parameters.put (a_term_text, "text") + if a_vocabulary /= Void then + l_parameters.put (a_vocabulary.id, "parent_tid") + sql_query (sql_select_vocabulary_term_by_text, l_parameters) + else + sql_query (sql_select_term_by_text, l_parameters) + end + sql_start + if not has_error and not sql_after then + Result := fetch_term + end + sql_finalize + end + + terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_tids: ARRAYED_LIST [INTEGER_64] + tid: INTEGER_64 + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_type_name, "type") + l_parameters.put (a_entity , "entity") + if a_vocabulary /= Void then + l_parameters.put (a_vocabulary.id , "parent_tid") + sql_query (sql_select_vocabulary_terms_of_entity, l_parameters) + else + sql_query (sql_select_terms_of_entity, l_parameters) + end + + create l_tids.make (0) + from + sql_start + until + sql_after or has_error + loop + tid := sql_read_integer_64 (1) + if tid > 0 then + l_tids.force (tid) + end + sql_forth + end + sql_finalize + if not l_tids.is_empty then + create Result.make (l_tids.count) + across + l_tids as ic + loop + if + ic.item > 0 and then + attached term_by_id (ic.item) as t + then + Result.force (t) + end + end + end + end + feature -- Store - save_term (t: CMS_TERM) + save_vocabulary (voc: CMS_VOCABULARY) + do + save_term (voc, create {CMS_VOCABULARY}.make_none) + across + voc.terms as ic + until + has_error + loop + save_term (ic.item, voc) + end + end + + save_term (t: CMS_TERM; voc: CMS_VOCABULARY) local l_parameters: STRING_TABLE [detachable ANY] do @@ -130,6 +231,11 @@ feature -- Store sql_insert (sql_insert_term, l_parameters) t.set_id (last_inserted_term_id) end + if not has_error then + l_parameters.put (t.id, "tid") + l_parameters.put (voc.id, "parent_tid") + sql_insert (sql_insert_term_in_vocabulary, l_parameters) + end if has_error then sql_rollback_transaction else @@ -138,6 +244,148 @@ feature -- Store sql_finalize end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Associate term `a_term' with `(a_type_name, a_entity)'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_term.id, "tid") + l_parameters.put (a_entity, "entity") + l_parameters.put (a_type_name, "type") + + sql_insert (sql_insert_term_index, l_parameters) + sql_finalize + end + + unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) + -- Unassociate term `a_term' from `(a_type_name, a_entity)'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_term.id, "tid") + l_parameters.put (a_entity, "entity") + l_parameters.put (a_type_name, "type") + + sql_modify (sql_delete_term_index, l_parameters) + sql_finalize + end + +feature -- Vocabulary and types + + mask_is_tags: INTEGER = 0b0001 -- 1 + mask_multiple_terms: INTEGER = 0b0010 -- 2 + mask_is_required: INTEGER = 0b0100 -- 4 + + vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION + -- + -- note: vocabularies are not filled with associated terms. + local + voc: detachable CMS_VOCABULARY + l_parameters: STRING_TABLE [detachable ANY] + l_data: ARRAYED_LIST [TUPLE [tid: INTEGER_64; entity: INTEGER_64]] + tid, ent: INTEGER_64 + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_type_name, "type") + sql_query (sql_select_vocabularies_for_type, l_parameters) + + create l_data.make (0) + from + sql_start + until + sql_after or has_error + loop + tid := sql_read_integer_64 (1) + if attached sql_read_string_32 (2) as s and then s.is_integer_64 then + ent := s.to_integer_64 + else + ent := 0 + end + if ent > 0 then + -- Vocabulary index should have 0 or negative value for `entity'! + check zero_or_negative_entity_value: False end + else + ent := - ent + if tid > 0 then + l_data.force ([tid, ent]) + end + end + sql_forth + end + sql_finalize + if not l_data.is_empty then + create Result.make (l_data.count) + across + l_data as ic + loop + tid := ic.item.tid + ent := ic.item.entity + check ic.item.tid > 0 end + + if + attached term_by_id (tid) as t + then + create voc.make_from_term (t) + --| 1: mask 0001: New terms allowed (i.e tags) + --| 2: mask 0010: Allow multiple tags + --| 4: mask 0100: At least one tag is required + voc.set_associated_content_type (a_type_name, ent & mask_is_tags = mask_is_tags, ent & mask_multiple_terms = mask_multiple_terms, ent & mask_is_required = mask_is_required) + Result.force (voc) + end + end + end + end + + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + i: INTEGER + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_voc.id, "tid") + if a_voc.is_tags then + i := i | mask_is_tags + end + if a_voc.is_term_required then + i := i | mask_multiple_terms + end + if a_voc.multiple_terms_allowed then + i := i | mask_is_required + end + l_parameters.put ((- i).out, "entity") + + l_parameters.put (a_type_name, "type") + + sql_insert (sql_insert_term_index, l_parameters) + sql_finalize + end + + unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (a_voc.id, "tid") + l_parameters.put (a_type_name, "type") + + sql_insert (sql_delete_vocabulary_index, l_parameters) + sql_finalize + end + feature {NONE} -- Queries last_inserted_term_id: INTEGER_64 @@ -159,7 +407,7 @@ feature {NONE} -- Queries tid := sql_read_integer_64 (1) l_text := sql_read_string_32 (2) if tid > 0 and l_text /= Void then - create Result.make (tid, l_text) + create Result.make_with_id (tid, l_text) Result.set_weight (sql_read_integer_32 (3)) if attached sql_read_string_32 (4) as l_desc then Result.set_description (l_desc) @@ -176,27 +424,85 @@ feature {NONE} -- Queries sql_select_vocabulary_terms_count: STRING = "SELECT count(*) FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = :parent_tid;" -- Number of terms under :parent_tid. - sql_select_vocabularies: STRING = "SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = 0;" - -- Terms without parent. - - sql_select_terms: STRING = "SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = :parent_tid;" + sql_select_terms: STRING = "[ + SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description + FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid + WHERE taxonomy_hierarchy.parent = :parent_tid + ORDER BY taxonomy_term.weight ASC ; + ]" -- Terms under :parent_tid. - sql_select_term: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE tid = :tid;" + sql_select_terms_with_range: STRING = "[ + SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description + FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid + WHERE taxonomy_hierarchy.parent = :parent_tid + ORDER BY taxonomy_term.weight ASC LIMIT :limit OFFSET :offset + ; + ]" + -- Terms under :parent_tid, and :limit, :offset + + sql_select_term: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE tid=:tid;" -- Term with tid :tid . + sql_select_term_by_text: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE text=:text;" + -- Term with text :text . + + sql_select_vocabulary_term_by_text: STRING = "[ + SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description + FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid + WHERE taxonomy_hierarchy.parent=:parent_tid AND taxonomy_term.text=:text + ; + ]" + -- Term with text :text and with parent :parent_tid + Sql_last_inserted_term_id: STRING = "SELECT MAX(tid) FROM taxonomy_term;" sql_insert_term: STRING = "[ - INSERT INTO taxonomy_terms (tid, text, weight, description, langcode) - VALUES (:tid, :text, :weight, :description, null); + INSERT INTO taxonomy_term (text, weight, description, langcode) + VALUES (:text, :weight, :description, null); ]" sql_update_term: STRING = "[ - UPDATE taxonomy_terms + UPDATE taxonomy_term SET tid=:tid, text=:text, weight=:weight, description=:description, langcode=null WHERE tid=:tid; ]" - + + sql_insert_term_in_vocabulary: STRING = "[ + INSERT INTO taxonomy_hierarchy (tid, parent) + VALUES (:tid, :parent_tid); + ]" + + sql_select_terms_of_entity: STRING = "[ + SELECT tid FROM taxonomy_index WHERE type=:type AND entity=:entity; + ]" + + sql_select_vocabulary_terms_of_entity: STRING = "[ + SELECT taxonomy_index.tid + FROM taxonomy_index INNER JOIN taxonomy_hierarchy ON taxonomy_index.tid=taxonomy_hierarchy.tid + WHERE taxonomy_hierarchy.parent=:parent_tid AND taxonomy_index.type=:type AND taxonomy_index.entity=:entity; + ]" + + sql_select_vocabularies_for_type: STRING = "[ + SELECT tid, entity + FROM taxonomy_index + WHERE type=:type AND entity <= 0; + ]" + + sql_insert_term_index: STRING = "[ + INSERT INTO taxonomy_index (tid, entity, type) + VALUES (:tid, :entity, :type); + ]" + + sql_delete_term_index: STRING = "[ + DELETE FROM taxonomy_index WHERE tid=:tid AND entity=:entity AND type=:type + ; + ]" + + sql_delete_vocabulary_index: STRING = "[ + DELETE FROM taxonomy_index WHERE tid=:tid AND type=:type + ; + ]" + end diff --git a/modules/taxonomy/site/files/css/taxonomy.css b/modules/taxonomy/site/files/css/taxonomy.css new file mode 100644 index 0000000..41c87fa --- /dev/null +++ b/modules/taxonomy/site/files/css/taxonomy.css @@ -0,0 +1,21 @@ +ul.taxonomy { + font-size: 80%; + list-style-type: none; + font-style: italic; + margin: 0; +} +ul.taxonomy li { + padding: 2px; + margin-right: 3px; + display: inline-block; + border: none; +} +ul.taxonomy li a:hover { + text-decoration: none; +} +ul.taxonomy li:hover { + padding: 1px; + border-top: solid 1px #66f; + border-bottom: solid 1px #66f; + background-color: #ddf; +} diff --git a/modules/taxonomy/site/files/scss/taxonomy.scss b/modules/taxonomy/site/files/scss/taxonomy.scss index e69de29..f409395 100644 --- a/modules/taxonomy/site/files/scss/taxonomy.scss +++ b/modules/taxonomy/site/files/scss/taxonomy.scss @@ -0,0 +1,21 @@ +ul.taxonomy { + font-size: 80%; + list-style-type: none; + font-style: italic; + margin: 0; + li { + a:hover { + text-decoration: none; + } + padding: 2px; + margin-right: 3px; + display: inline-block; + border: none; + &:hover { + padding: 1px; + border-top: solid 1px #66f; + border-bottom: solid 1px #66f; + background-color: #ddf; + } + } +} diff --git a/modules/taxonomy/site/scripts/install.sql b/modules/taxonomy/site/scripts/install.sql index 54b2130..2eaf9a2 100644 --- a/modules/taxonomy/site/scripts/install.sql +++ b/modules/taxonomy/site/scripts/install.sql @@ -1,3 +1,4 @@ + CREATE TABLE taxonomy_term ( `tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE, `text` VARCHAR(255) NOT NULL, @@ -11,3 +12,13 @@ CREATE TABLE taxonomy_hierarchy ( `parent` INTEGER, CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent) ); + +/* Associate tid with unique (type,entity) + * for instance: "page" + "$nid" -> "tid" + */ +CREATE TABLE taxonomy_index ( + `tid` INTEGER NOT NULL, + `entity` VARCHAR(255), + `type` VARCHAR(255) NOT NULL, + CONSTRAINT PK_tid_entity_type PRIMARY KEY (tid,entity,type) +); diff --git a/modules/taxonomy/site/scripts/uninstall.sql b/modules/taxonomy/site/scripts/uninstall.sql index 1fc3a53..0efae92 100644 --- a/modules/taxonomy/site/scripts/uninstall.sql +++ b/modules/taxonomy/site/scripts/uninstall.sql @@ -1,2 +1,3 @@ DROP TABLE IF EXISTS taxonomy_term; DROP TABLE IF EXISTS taxonomy_hierarchy; +DROP TABLE IF EXISTS taxonomy_index; diff --git a/modules/taxonomy/taxonomy.ecf b/modules/taxonomy/taxonomy.ecf new file mode 100644 index 0000000..6fa259f --- /dev/null +++ b/modules/taxonomy/taxonomy.ecf @@ -0,0 +1,26 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + + + + diff --git a/src/configuration/cms_setup.e b/src/configuration/cms_setup.e index 979646e..93e3602 100644 --- a/src/configuration/cms_setup.e +++ b/src/configuration/cms_setup.e @@ -132,14 +132,16 @@ feature {NONE} -- Implementation: update until not a_module.is_enabled loop - if - attached a_collection.item (ic.item) as mod and then - mod.is_enabled - then - update_module_status_within (mod, a_collection) - else - --| dependency not found or disabled - a_module.disable + if ic.item.is_required then + if + attached a_collection.item (ic.item.module_type) as mod and then + mod.is_enabled + then + update_module_status_within (mod, a_collection) + else + --| dependency not found or disabled + a_module.disable + end end end end diff --git a/src/persistence/sql/cms_storage_sql_i.e b/src/persistence/sql/cms_storage_sql_i.e index 4ddb2f3..16f5444 100644 --- a/src/persistence/sql/cms_storage_sql_i.e +++ b/src/persistence/sql/cms_storage_sql_i.e @@ -236,16 +236,22 @@ feature -- Access sql_start -- Set the cursor on first element. + require + no_error: not has_error deferred end sql_after: BOOLEAN -- Are there no more items to iterate over? + require + no_error: not has_error deferred end sql_forth -- Fetch next row from last sql execution, if any. + require + no_error: not has_error deferred end @@ -255,6 +261,7 @@ feature -- Access sql_item (a_index: INTEGER): detachable ANY require + no_error: not has_error valid_index: sql_valid_item_index (a_index) deferred end @@ -446,7 +453,7 @@ feature {NONE} -- Implementation across l_removals as ic loop - Result.remove_substring (ic.item.start_index - j, ic.item.end_index - j) + Result.remove_substring (ic.item.start_index - j - a_start_index + 1, ic.item.end_index - j - a_start_index + 1) j := j + ic.item.end_index - ic.item.start_index + 1 end -- a_offset.replace (a_offset.item j) diff --git a/src/service/cms_module.e b/src/service/cms_module.e index 8c87807..8d65e6d 100644 --- a/src/service/cms_module.e +++ b/src/service/cms_module.e @@ -29,8 +29,8 @@ feature -- Access version: STRING -- Version of the module? - dependencies: detachable LIST [TYPE [CMS_MODULE]] - -- Optional dependencies. + dependencies: detachable LIST [TUPLE [module_type: TYPE [CMS_MODULE]; is_required: BOOLEAN]] + -- Optional declaration for dependencies. permissions: LIST [READABLE_STRING_8] -- List of permission ids, used by this module, and declared. @@ -55,16 +55,22 @@ feature {CMS_API} -- Module Initialization end add_dependency (a_type: TYPE [CMS_MODULE]) - -- Add dependency using type of module `a_type'. - local - deps: like dependencies + -- Add required dependency using type of module `a_type'. do - deps := dependencies - if deps = Void then - create {ARRAYED_LIST [TYPE [CMS_MODULE]]} deps.make (1) - dependencies := deps + put_dependency (a_type, True) + end + + put_dependency (a_type: TYPE [CMS_MODULE]; is_required: BOOLEAN) + -- Add required or optional dependency using type of module `a_type', based on `is_required' value. + local + lst: like dependencies + do + lst := dependencies + if lst = Void then + create {ARRAYED_LIST [TUPLE [module_type: TYPE [CMS_MODULE]; is_required: BOOLEAN]]} lst.make (1) + dependencies := lst end - deps.force (a_type) + lst.force ([a_type, is_required]) end feature -- Status