diff --git a/examples/demo/site/modules/taxonomy/files/css/taxonomy.css b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css index 41c87fa..dd216d0 100644 --- a/examples/demo/site/modules/taxonomy/files/css/taxonomy.css +++ b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css @@ -19,3 +19,8 @@ ul.taxonomy li:hover { border-bottom: solid 1px #66f; background-color: #ddf; } + +table.taxonomy td { + border: solid 1px #ccc; + padding: 2px; +} diff --git a/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss index f409395..2ceff02 100644 --- a/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss +++ b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss @@ -19,3 +19,9 @@ ul.taxonomy { } } } +table.taxonomy { + td { + border: solid 1px #ccc; + padding: 2px; + } +} diff --git a/examples/demo/site/themes/bootstrap/assets/css/style.css b/examples/demo/site/themes/bootstrap/assets/css/style.css index b16026b..5accf73 100644 --- a/examples/demo/site/themes/bootstrap/assets/css/style.css +++ b/examples/demo/site/themes/bootstrap/assets/css/style.css @@ -91,13 +91,10 @@ ul.horizontal li { padding: 5px 2px 5px 2px; } -ul.taxonomy-entities { - list-style-type: none; - padding: 0; +table.with_border thead td { + font-weight: bold; } -ul.taxonomy-entities li { - padding: 0; - margin-top: 5px; - margin-bottom: 10px; - border-top: dotted 1px #ccc; +table.with_border td { + border: solid 1px #ccc; + padding: 2px 5px 2px 5px; } diff --git a/examples/demo/site/themes/bootstrap/assets/scss/style.scss b/examples/demo/site/themes/bootstrap/assets/scss/style.scss index 1192673..90d2bb9 100644 --- a/examples/demo/site/themes/bootstrap/assets/scss/style.scss +++ b/examples/demo/site/themes/bootstrap/assets/scss/style.scss @@ -96,13 +96,12 @@ ul.horizontal { padding: 5px 2px 5px 2px; } -ul.taxonomy-entities { - list-style-type: none; - padding: 0; - li { - padding: 0; - margin-top: 5px; - margin-bottom: 10px; - border-top: dotted 1px #ccc; +table.with_border { + thead td { + font-weight: bold; + } + td { + border: solid 1px #ccc; + padding: 2px 5px 2px 5px; } } diff --git a/modules/admin/handler/cms_admin_cache_handler.e b/modules/admin/handler/cms_admin_cache_handler.e index 13dd922..5f53515 100644 --- a/modules/admin/handler/cms_admin_cache_handler.e +++ b/modules/admin/handler/cms_admin_cache_handler.e @@ -44,7 +44,7 @@ feature -- Execution create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) f := clear_cache_web_form (l_response) create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end @@ -70,7 +70,7 @@ feature -- Execution end end create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end diff --git a/modules/admin/handler/cms_admin_export_handler.e b/modules/admin/handler/cms_admin_export_handler.e index 8f21b30..8fd510e 100644 --- a/modules/admin/handler/cms_admin_export_handler.e +++ b/modules/admin/handler/cms_admin_export_handler.e @@ -44,7 +44,7 @@ feature -- Execution create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) f := exportation_web_form (l_response) create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end @@ -85,7 +85,7 @@ feature -- Execution end end create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end diff --git a/modules/admin/handler/cms_admin_modules_handler.e b/modules/admin/handler/cms_admin_modules_handler.e index 53ffb20..07e9d38 100644 --- a/modules/admin/handler/cms_admin_modules_handler.e +++ b/modules/admin/handler/cms_admin_modules_handler.e @@ -88,7 +88,7 @@ feature -- Execution 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) + f.append_to_html (r.wsf_theme, s) r.set_page_title ("Modules") r.set_main_content (s) r.execute @@ -133,7 +133,7 @@ feature -- Execution 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) + f.append_to_html (r.wsf_theme, s) r.set_page_title ("Modules") r.set_main_content (s) else diff --git a/modules/admin/handler/cms_admin_response.e b/modules/admin/handler/cms_admin_response.e index 3274fb5..fb7dea4 100644 --- a/modules/admin/handler/cms_admin_response.e +++ b/modules/admin/handler/cms_admin_response.e @@ -8,30 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Process process diff --git a/modules/admin/handler/role/cms_role_form_response.e b/modules/admin/handler/role/cms_role_form_response.e index b681549..7cdec58 100644 --- a/modules/admin/handler/role/cms_role_form_response.e +++ b/modules/admin/handler/role/cms_role_form_response.e @@ -8,32 +8,12 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end CMS_SHARED_SORTING_UTILITIES create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query role_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/role/cms_role_view_response.e b/modules/admin/handler/role/cms_role_view_response.e index 53b9038..a6887ae 100644 --- a/modules/admin/handler/role/cms_role_view_response.e +++ b/modules/admin/handler/role/cms_role_view_response.e @@ -8,31 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make - -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query role_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/user/cms_user_form_response.e b/modules/admin/handler/user/cms_user_form_response.e index 5209706..40847f0 100644 --- a/modules/admin/handler/user/cms_user_form_response.e +++ b/modules/admin/handler/user/cms_user_form_response.e @@ -7,32 +7,11 @@ class CMS_USER_FORM_RESPONSE inherit - CMS_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query user_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/user/cms_user_view_response.e b/modules/admin/handler/user/cms_user_view_response.e index 0568fcc..4cf3716 100644 --- a/modules/admin/handler/user/cms_user_view_response.e +++ b/modules/admin/handler/user/cms_user_view_response.e @@ -8,31 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make - -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query user_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index 453a272..6e5ae7b 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -100,6 +100,7 @@ feature -- Forms ... populate_form_with_taxonomy (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) local ti: detachable WSF_FORM_TEXT_INPUT + th: WSF_FORM_HIDDEN_INPUT w_set: WSF_FORM_FIELD_SET w_select: WSF_FORM_SELECT w_opt: WSF_FORM_SELECT_OPTION @@ -126,6 +127,9 @@ feature -- Forms ... l_vocs as vocs_ic loop voc := vocs_ic.item + create th.make_with_text ({STRING_32} "taxonomy_vocabularies[" + voc.id.out + "]", voc.name) + w_set.extend (th) + 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) @@ -139,7 +143,7 @@ feature -- Forms ... if voc.is_tags then w_voc_set.set_legend (response.translation (voc.name, Void)) - create ti.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + create ti.make ({STRING_32} "taxonomy_" + voc.id.out) w_voc_set.extend (ti) if voc.is_term_required then ti.enable_required @@ -186,7 +190,8 @@ feature -- Forms ... 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) + create w_cb.make_with_value ({STRING_32} "taxonomy_" + voc.id.out + "[]", t.text) + w_cb.set_title (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) @@ -196,7 +201,7 @@ feature -- Forms ... end end else - create w_select.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + create w_select.make ({STRING_32} "taxonomy_" + voc.id.out) w_voc_set.extend (w_select) if attached voc.description as l_desc then @@ -248,68 +253,81 @@ feature -- Forms ... l_text: READABLE_STRING_GENERAL l_found: BOOLEAN t: detachable CMS_TERM + vid: INTEGER_64 do if a_node /= Void and then a_node.has_id and then - attached fd.table_item ("taxonomy_terms") as fd_terms + attached fd.table_item ("taxonomy_vocabularies") as fd_vocs 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 + if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then + across + fd_vocs.values as ic + loop + vid := ic.key.to_integer_64 + l_voc_name := ic.item.string_representation + + if attached a_vocs.item_by_id (vid) as voc then + if attached fd.string_item ("taxonomy_" + vid.out) as l_string then + l_new_terms := a_taxonomy_api.splitted_string (l_string, ',') + elseif attached fd.table_item ("taxonomy_" + vid.out) as fd_terms then + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (fd_terms.count) + across + fd_terms as t_ic + loop + l_new_terms.force (t_ic.item.string_representation) + end + else + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (0) + end + + 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 - 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) + 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 - 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) + if not l_found then + -- Remove term + l_terms_to_remove.force (t_ic.item) end end across - l_new_terms as t_ic + l_terms_to_remove 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) + 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 diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e index f8da987..604bcb5 100644 --- a/modules/node/handler/node_form_response.e +++ b/modules/node/handler/node_form_response.e @@ -7,30 +7,10 @@ class inherit NODE_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api, a_node_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Execution process diff --git a/modules/node/handler/node_view_response.e b/modules/node/handler/node_view_response.e index 3a39f96..64b800b 100644 --- a/modules/node/handler/node_view_response.e +++ b/modules/node/handler/node_view_response.e @@ -8,30 +8,10 @@ class inherit NODE_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api, a_node_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Access node: detachable CMS_NODE diff --git a/modules/recent_changes/cms_recent_changes_module.e b/modules/recent_changes/cms_recent_changes_module.e index d685d4b..9715dff 100644 --- a/modules/recent_changes/cms_recent_changes_module.e +++ b/modules/recent_changes/cms_recent_changes_module.e @@ -284,7 +284,7 @@ feature -- Handler create l_submit.make_with_text ("op", "Filter") l_form.extend (l_submit) l_form.extend_html_text ("
") - l_form.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), l_content) + l_form.append_to_html (r.wsf_theme, l_content) end l_changes.reverse_sort diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 69534d8..677dbdf 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -52,7 +52,7 @@ feature -- Access node Result := taxonomy_storage.vocabularies (a_limit, a_offset) end - vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY -- Vocabulary associated with id `a_id'. require valid_id: a_id > 0 @@ -66,6 +66,27 @@ feature -- Access node Result := taxonomy_storage.vocabularies_for_type (a_type_name) end + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + do + Result := taxonomy_storage.types_associated_with_vocabulary (a_vocab) + end + + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies including `a_term'. + do + Result := taxonomy_storage.vocabularies_for_term (a_term) + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- Is `a_term' inside `a_vocab' ? + require + valid_term: a_term.has_id + valid_vocabulary: a_vocab.has_id + do + Result := taxonomy_storage.is_term_inside_vocabulary (a_term, a_vocab) + end + fill_vocabularies_with_terms (a_vocab: CMS_VOCABULARY) -- Fill `a_vocab' with associated terms. do @@ -125,19 +146,39 @@ feature -- Access node feature -- Write save_vocabulary (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. do reset_error - taxonomy_storage.save_vocabulary (a_voc) + taxonomy_storage.save_vocabulary (a_voc, False) error_handler.append (taxonomy_storage.error_handler) end - save_term (a_term: CMS_TERM; voc: CMS_VOCABULARY) + save_vocabulary_and_terms (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms. + do + reset_error + taxonomy_storage.save_vocabulary (a_voc, True) + error_handler.append (taxonomy_storage.error_handler) + end + + save_term (a_term: CMS_TERM; voc: detachable CMS_VOCABULARY) + -- Save `a_term' inside `voc' if set. do reset_error taxonomy_storage.save_term (a_term, voc) error_handler.append (taxonomy_storage.error_handler) end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + do + reset_error + taxonomy_storage.remove_term_from_vocabulary (t, 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 diff --git a/modules/taxonomy/cms_taxonomy_module.e b/modules/taxonomy/cms_taxonomy_module.e index bfbe5ea..0d3ddb5 100644 --- a/modules/taxonomy/cms_taxonomy_module.e +++ b/modules/taxonomy/cms_taxonomy_module.e @@ -109,6 +109,7 @@ feature -- Access: router do if attached taxonomy_api as l_taxonomy_api then configure_web (a_api, l_taxonomy_api, a_router) + configure_web_amin (a_api, l_taxonomy_api, a_router) else -- Issue with api/dependencies, -- thus Current module should not be used! @@ -120,9 +121,48 @@ feature -- Access: router -- Configure router mapping for web interface. local l_taxonomy_handler: TAXONOMY_HANDLER + l_voc_handler: TAXONOMY_VOCABULARY_HANDLER do create l_taxonomy_handler.make (a_api, a_taxonomy_api) a_router.handle ("/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get) + + create l_voc_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get) + a_router.handle ("/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get) + end + + configure_web_amin (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_TERM_ADMIN_HANDLER + l_voc_handler: TAXONOMY_VOCABULARY_ADMIN_HANDLER + do + a_router.handle ("/admin/taxonomy/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_admin_taxonomy (?, ?, a_api)), a_router.methods_get) + + create l_taxonomy_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/admin/taxonomy/term/", l_taxonomy_handler, a_router.methods_get_post) + a_router.handle ("/admin/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get_post) + + create l_voc_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/admin/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get_post) + a_router.handle ("/admin/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get_post) + end + +feature -- Handler + + handle_admin_taxonomy (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) + local + l_page: CMS_RESPONSE + lnk: CMS_LOCAL_LINK + do + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create lnk.make ("Admin Vocabularies", "admin/taxonomy/vocabulary/") + l_page.add_to_primary_tabs (lnk) + + create lnk.make ("Create terms", "admin/taxonomy/term/") + l_page.add_to_primary_tabs (lnk) + + l_page.execute end feature -- Hooks @@ -139,10 +179,14 @@ feature -- Hooks end menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + local + lnk: CMS_LOCAL_LINK do - -- Add the link to the taxonomy to the main menu --- create lnk.make ("Taxonomy", "taxonomy/") --- a_menu_system.primary_menu.extend (lnk) + -- Add the link to the taxonomy to the main menu + if a_response.has_permission ("admin taxonomy") then + create lnk.make ("Taxonomy", "admin/taxonomy/") + a_menu_system.management_menu.extend (lnk) + end end end diff --git a/modules/taxonomy/cms_term_collection.e b/modules/taxonomy/cms_term_collection.e index 5e882e7..dc96960 100644 --- a/modules/taxonomy/cms_term_collection.e +++ b/modules/taxonomy/cms_term_collection.e @@ -19,6 +19,7 @@ feature {NONE} -- Initialization make (nb: INTEGER) do create items.make (nb) + items.compare_objects end feature -- Access @@ -48,6 +49,21 @@ feature -- Status report Result := items.has (a_term) end + term_by_id (tid: INTEGER_64): detachable CMS_TERM + -- Term of id `tid' contained in Current, if any. + do + across + items as ic + until + Result /= Void + loop + Result := ic.item + if Result.id /= tid then + Result := Void + end + end + end + feature -- Element change wipe_out diff --git a/modules/taxonomy/cms_vocabulary_collection.e b/modules/taxonomy/cms_vocabulary_collection.e index b4c3028..b8ccf05 100644 --- a/modules/taxonomy/cms_vocabulary_collection.e +++ b/modules/taxonomy/cms_vocabulary_collection.e @@ -19,6 +19,7 @@ feature {NONE} -- Initialization make (nb: INTEGER) do create items.make (nb) + items.compare_objects end feature -- Access @@ -37,6 +38,21 @@ feature -- Access end end + item_by_id (a_id: INTEGER_64): detachable CMS_VOCABULARY + -- Vocabulary of id `a_id' contained in Current, if any. + do + across + items as ic + until + Result /= Void + loop + Result := ic.item + if Result.id /= a_id then + Result := Void + end + end + end + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_VOCABULARY] -- do diff --git a/modules/taxonomy/handler/taxonomy_term_admin_handler.e b/modules/taxonomy/handler/taxonomy_term_admin_handler.e new file mode 100644 index 0000000..9e97d36 --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_term_admin_handler.e @@ -0,0 +1,269 @@ +note + description: "[ + Request handler related to + /admin/taxonomy/term/{termid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_TERM_ADMIN_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, + do_post + 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_post (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: CMS_RESPONSE + tid: INTEGER_64 + s: STRING + f: CMS_FORM + t: detachable CMS_TERM + l_parents: detachable CMS_VOCABULARY_COLLECTION + do + if + attached {WSF_STRING} req.path_parameter ("termid") as p_termid and then + p_termid.is_integer + then + tid := p_termid.value.to_integer_64 + if tid > 0 then + t := taxonomy_api.term_by_id (tid) + end + end + + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if l_page.has_permission ("admin taxonomy") then + + if t = Void then + l_page.set_title ("New term ...") + create t.make ("") + else + l_page.set_title (t.text) + end + create s.make_empty + f := edit_form (t, l_page, req) + f.process (l_page) + if + attached f.last_data as fd and then + fd.is_valid + then + if attached fd.string_item ("op") as l_op and then l_op.same_string ("Save changes") then + if attached fd.string_item ("text") as l_text then + t.set_text (l_text) + l_page.set_title (t.text) + end + if attached fd.string_item ("description") as l_description then + t.set_description (l_description) + end + if attached fd.string_item ("weight") as l_weight and then l_weight.is_integer then + t.set_weight (l_weight.to_integer) + end + taxonomy_api.save_term (t, Void) + if taxonomy_api.has_error then + fd.report_error ("Term creation failed!") + else + l_page.add_success_message ("Term creation succeed.") + s.append ("
View term: ") + s.append (l_page.link (t.text, "admin/taxonomy/term/" + t.id.out, Void)) + s.append ("
") + + if + attached fd.table_item ("vocabularies") as voc_tb and then + attached taxonomy_api.vocabularies (0, 0) as l_vocabularies + then + l_parents := taxonomy_api.vocabularies_for_term (t) + across + voc_tb as vid_ic + until + taxonomy_api.has_error + loop + if attached l_vocabularies.item_by_id (vid_ic.item.string_representation.to_integer_64) as v then + if l_parents /= Void and then attached l_parents.item_by_id (v.id) as l_v then + -- Already as parent! + l_parents.remove (l_v) + else + taxonomy_api.save_term (t, v) + l_vocabularies.remove (v) + end + end + end + if l_parents /= Void then + across + l_parents as v_ic + until + taxonomy_api.has_error + loop + taxonomy_api.remove_term_from_vocabulary (t, v_ic.item) + end + end + end + -- l_page.set_redirection (l_page.location) + end + else + fd.report_error ("Invalid form data!") + end + end + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + l_page.execute + else + send_access_denied (req, res) + end + end + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + tid: INTEGER_64 + s: STRING + f: CMS_FORM + t: detachable CMS_TERM + do + if + attached {WSF_STRING} req.path_parameter ("termid") as p_termid and then + p_termid.is_integer + then + tid := p_termid.value.to_integer_64 + if tid > 0 then + t := taxonomy_api.term_by_id (tid) + end + end + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if l_page.has_permission ("admin taxonomy") then + if t = Void then + l_page.set_title ("Create term ...") + create t.make ("") + else + l_page.set_title (t.text) + end + create s.make_empty + f := edit_form (t, l_page, req) + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + l_page.execute + else + send_access_denied (req, res) + end + end + + edit_form (t: CMS_TERM; a_page: CMS_RESPONSE; req: WSF_REQUEST): CMS_FORM + local + f: CMS_FORM + voc: detachable CMS_VOCABULARY + w_tf: WSF_FORM_TEXT_INPUT + w_txt: WSF_FORM_TEXTAREA + w_set: WSF_FORM_FIELD_SET + w_cb: WSF_FORM_CHECKBOX_INPUT + l_parents: detachable CMS_VOCABULARY_COLLECTION + do + create f.make (req.percent_encoded_path_info, "taxonomy") + if t.has_id then + f.extend_html_text (a_page.link ("View associated entities", "taxonomy/term/" + t.id.out, Void)) + end + create w_tf.make_with_text ("text", t.text) + w_tf.set_is_required (True) + w_tf.set_label ("Text") + f.extend (w_tf) + + create w_txt.make ("description") + w_txt.set_label ("Description") + w_txt.set_rows (3) + w_txt.set_cols (60) + if attached t.description as l_desc then + w_txt.set_text_value (api.html_encoded (l_desc)) + end + w_txt.set_description ("Description of the terms; can be used by modules or administration.") + f.extend (w_txt) + + create w_tf.make_with_text ("weight", t.weight.out) + w_tf.set_label ("Weight") + w_tf.set_description ("Terms are sorted in ascending order by weight.") + f.extend (w_tf) + + if attached taxonomy_api.vocabularies (0, 0) as vocs then + if t.has_id then + l_parents := taxonomy_api.vocabularies_for_term (t) + end + create w_set.make + w_set.set_legend ("Associated vocabularies") + across + vocs as ic + loop + voc := ic.item + create w_cb.make_with_value ("vocabularies[]", ic.item.id.out) + w_cb.set_title (voc.name) + if + l_parents /= Void and then + across l_parents as p_ic some p_ic.item.id = ic.item.id end + then + w_cb.set_checked (True) + end + w_set.extend (w_cb) + end + if w_set.count > 0 then + f.extend (w_set) + end + end + + f.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("op", "Save changes")) + Result := f + end + + +end diff --git a/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e new file mode 100644 index 0000000..c3ba3ac --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e @@ -0,0 +1,414 @@ +note + description: "[ + Request handler related to + /admin/taxonomy/vocabulary/ + /admin/taxonomy/vocabulary/{vocid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_VOCABULARY_ADMIN_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, + do_post + 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_post (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: CMS_RESPONSE + voc: CMS_VOCABULARY + l_typename: READABLE_STRING_GENERAL + s: STRING + do + if not api.user_has_permission (current_user (req), "admin taxonomy") then + send_access_denied (req, res) + else + if attached {WSF_STRING} req.form_parameter ("op") as p_op then + if p_op.same_string ("New Vocabulary") then + if + attached {WSF_STRING} req.form_parameter ("vocabulary_name") as p_name and then + not p_name.is_empty + then + create voc.make (p_name.value) + taxonomy_api.save_vocabulary (voc) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if taxonomy_api.has_error then + l_page.add_error_message ("Vocabulary creation failed!") + else + l_page.add_success_message ("Vocabulary creation succeed!") + l_page.set_redirection ("admin/taxonomy/vocabulary/" + voc.id.out) + end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + elseif + p_op.same_string ("Save changes") and then + attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid and then p_vocid.is_integer and then + attached taxonomy_api.vocabulary (p_vocid.value.to_integer_64) as l_vocabulary + then + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create s.make_empty + l_page.add_notice_message ("Vocabulary " + l_vocabulary.id.out) + if attached {WSF_STRING} req.form_parameter ("name") as p_name then + l_vocabulary.set_name (p_name.value) + end + if attached {WSF_STRING} req.form_parameter ("description") as p_desc then + l_vocabulary.set_description (p_desc.value) + end + if attached {WSF_STRING} req.form_parameter ("weight") as p_weight and then p_weight.is_integer then + l_vocabulary.set_weight (p_weight.integer_value) + end + taxonomy_api.save_vocabulary (l_vocabulary) + if taxonomy_api.has_error then + l_page.add_error_message ("Could not save vocabulary") + elseif + attached {WSF_TABLE} req.form_parameter ("typenames") as typenames_table + then + across + typenames_table as ic + loop + l_typename := ic.item.string_representation + create voc.make_from_term (l_vocabulary) + voc.set_associated_content_type (l_typename, False, False, False) + l_page.add_notice_message ("Content type :" + api.html_encoded (l_typename)) + if attached {WSF_TABLE} req.form_parameter ({STRING_32} "vocabulary_" + l_typename.as_string_32) as opts then + across + opts as o_ic + loop + if o_ic.item.same_string ("tags") then + voc.set_is_tags (True) + elseif o_ic.item.same_string ("multiple") then + voc.allow_multiple_term (True) + elseif o_ic.item.same_string ("required") then + voc.set_is_term_required (True) + end + end + end + taxonomy_api.associate_vocabulary_with_type (voc, l_typename) + if taxonomy_api.has_error then + l_page.add_error_message ("Could not save vocabulary content type associations.") + end + end + end + if not taxonomy_api.has_error then + l_page.add_notice_message (l_page.link ({STRING_32} "Back to vocabulary %"" + l_vocabulary.name + "%"", "admin/taxonomy/vocabulary/" + l_vocabulary.id.out, Void)) + end + l_page.set_main_content (s) + else + create {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + end + end + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + tid: INTEGER_64 + do + if not api.user_has_permission (current_user (req), "admin taxonomy") then + send_access_denied (req, res) + else + if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then + if p_vocid.is_integer then + tid := p_vocid.value.to_integer_64 + end + end + if tid > 0 then + do_get_vocabulary (tid, req, res) + else + do_get_vocabularies (req, res) + end + end + end + + do_get_vocabulary (tid: INTEGER_64; req: WSF_REQUEST; res: WSF_RESPONSE) + -- + require + valid_tid: tid > 0 + local + l_page: CMS_RESPONSE + s: STRING + l_typename: detachable READABLE_STRING_8 + v: detachable CMS_VOCABULARY + l_typenames: detachable LIST [READABLE_STRING_32] + f: CMS_FORM + wtb: WSF_WIDGET_TABLE + wtb_row: WSF_WIDGET_TABLE_ROW + wtb_item: WSF_WIDGET_TABLE_ITEM + voc: detachable CMS_VOCABULARY + l_term: detachable CMS_TERM + tf_input: WSF_FORM_TEXT_INPUT + tf_text: WSF_FORM_TEXTAREA + tf_num: WSF_FORM_NUMBER_INPUT + w_set: WSF_FORM_FIELD_SET + w_cb: WSF_FORM_CHECKBOX_INPUT + sub: WSF_FORM_SUBMIT_INPUT + do + voc := taxonomy_api.vocabulary (tid) + if voc /= Void then + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + l_page.set_title (voc.name) + taxonomy_api.fill_vocabularies_with_terms (voc) + + create f.make (req.percent_encoded_path_info, "taxonomy") + + create tf_input.make_with_text ("name", voc.name) + f.extend (tf_input) + + create tf_text.make ("description") + tf_text.set_text_value (voc.description) + tf_text.set_description ("Description of the vocabulary; also used as intructions to present to the user when selecting terms.") + tf_text.set_rows (3) + f.extend (tf_text) + + create tf_num.make_with_text ("weight", voc.weight.out) + tf_num.set_label ("weight") + tf_num.set_description ("Items are displayed in ascending order by weight.") + f.extend (tf_num) + + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (2) + create wtb_item.make_with_text ("Text") + wtb_row.set_item (wtb_item, 1) + create wtb_item.make_with_text ("Description") + wtb_row.set_item (wtb_item, 2) + wtb.add_head_row (wtb_row) + across + voc as ic + loop + l_term := ic.item + + create wtb_row.make (3) + wtb.add_row (wtb_row) + + create wtb_item.make_with_text (l_page.link (ic.item.text, "admin/taxonomy/term/" + l_term.id.out, Void)) + wtb_row.set_item (wtb_item, 1) + if attached ic.item.description as l_desc then + create wtb_item.make_with_text (api.html_encoded (l_desc)) + else + create wtb_item.make_with_text ("") + end + wtb_row.set_item (wtb_item, 2) + end + if wtb.body_row_count > 0 then + f.extend (wtb) + else + f.extend_raw_text ("No terms.") + end + + create w_set.make + w_set.set_legend ("Content types") + f.extend (w_set) + + + l_typenames := taxonomy_api.types_associated_with_vocabulary (voc) + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (5) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_text ("Type"), 1) + create wtb_item.make_with_text ("Settings ...") + wtb_item.add_html_attribute ("colspan", "3") + wtb_row.set_item (wtb_item, 2) + wtb.add_head_row (wtb_row) + + across + api.content_types as ic + loop + create wtb_row.make (4) + wtb.add_row (wtb_row) + + l_typename := ic.item.name + create w_cb.make_with_value ("typenames[]", api.html_encoded (l_typename)) + w_cb.set_title (ic.item.name) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 1) + + v := Void + if + l_typenames /= Void and then + across l_typenames as tn_ic some l_typename.is_case_insensitive_equal (tn_ic.item) end + then + w_cb.set_checked (True) + if attached taxonomy_api.vocabularies_for_type (l_typename) as v_list then + across v_list as v_ic until v /= Void loop + if v_ic.item.id = voc.id then + v := v_ic.item + end + end + end + end + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "tags") + w_cb.set_title ("Tags") + w_cb.set_checked (v /= Void and then v.is_tags) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 2) + + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "multiple") + w_cb.set_title ("Multiple Select") + w_cb.set_checked (v /= Void and then v.multiple_terms_allowed) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 3) + + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "required") + w_cb.set_title ("Required") + w_cb.set_checked (v /= Void and then v.is_term_required) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 4) + end + if wtb.body_row_count > 0 then + w_set.extend (wtb) + end + + create sub.make_with_text ("op", "Save changes") + f.extend (sub) + + create s.make_empty + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + else + -- Responding with `main_content_html (l_page)'. + create {NOT_FOUND_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + end + + do_get_vocabularies (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + s: STRING + l_typenames: detachable LIST [READABLE_STRING_32] + f: CMS_FORM + wtb: WSF_WIDGET_TABLE + wtb_row: WSF_WIDGET_TABLE_ROW + wtb_item: WSF_WIDGET_TABLE_ITEM + voc: detachable CMS_VOCABULARY + tf_input: WSF_FORM_TEXT_INPUT + w_set: WSF_FORM_FIELD_SET + sub: WSF_FORM_SUBMIT_INPUT + do + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (3) + create wtb_item.make_with_text ("Name") + wtb_row.set_item (wtb_item, 1) + create wtb_item.make_with_text ("Type") + wtb_row.set_item (wtb_item, 2) + create wtb_item.make_with_text ("Operations") + wtb_row.set_item (wtb_item, 3) + wtb.add_head_row (wtb_row) + + if attached taxonomy_api.vocabularies (0, 0) as lst then + across + lst as ic + loop + voc := ic.item + create wtb_row.make (3) + wtb.add_row (wtb_row) + + create wtb_item.make_with_text (l_page.link (ic.item.name, "admin/taxonomy/vocabulary/" + ic.item.id.out, Void)) +-- if attached ic.item.description as l_desc then +-- s.append (" : ") +-- s.append (api.html_encoded (l_desc)) +-- s.append ("") +-- end + wtb_row.set_item (wtb_item, 1) + l_typenames := taxonomy_api.types_associated_with_vocabulary (voc) + if l_typenames /= Void then + create s.make_empty + across + l_typenames as types_ic + loop + if not s.is_empty then + s.append_character (',') + s.append_character (' ') + end + s.append (api.html_encoded (types_ic.item)) + end + create wtb_item.make_with_text (s) + wtb_row.set_item (wtb_item, 2) + end + + s := l_page.link ("edit", "admin/taxonomy/vocabulary/" + voc.id.out, Void) + create wtb_item.make_with_text (s) + wtb_row.set_item (wtb_item, 3) + end + end + create f.make (req.percent_encoded_path_info, "taxonomy") + f.set_method_post + + f.extend (wtb) + + create w_set.make + w_set.set_legend ("Create a new vocabulary") + create tf_input.make_with_text ("vocabulary_name", "") + tf_input.set_label ("Vocabulary name") + w_set.extend (tf_input) + create sub.make_with_text ("op", "New Vocabulary") + w_set.extend (sub) + f.extend (w_set) + + create s.make_empty + f.append_to_html (l_page.wsf_theme, s) + l_page.set_title ("Vocabularies") + l_page.set_main_content (s) + l_page.execute + end + +end diff --git a/modules/taxonomy/handler/taxonomy_vocabulary_handler.e b/modules/taxonomy/handler/taxonomy_vocabulary_handler.e new file mode 100644 index 0000000..6db90f4 --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_vocabulary_handler.e @@ -0,0 +1,129 @@ +note + description: "[ + Request handler related to + /taxonomy/vocabulary/ + /taxonomy/vocabulary/{vocid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_VOCABULARY_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 + tid: INTEGER_64 + s: STRING + do + if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then + if p_vocid.is_integer then + tid := p_vocid.value.to_integer_64 + end + end + if tid > 0 then + if attached taxonomy_api.vocabulary (tid) as voc then + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + l_page.set_title (voc.name) + taxonomy_api.fill_vocabularies_with_terms (voc) + + create s.make_empty + s.append ("
    ") + across + voc as ic + loop + s.append ("
  • ") + s.append (l_page.link (ic.item.text, "taxonomy/term/" + ic.item.id.out, Void)) + if attached ic.item.description as l_desc then + s.append (" : ") + s.append (api.html_encoded (l_desc)) + s.append ("") + end + s.append ("
  • ") + end + s.append ("
") + l_page.set_main_content (s) + else + -- Responding with `main_content_html (l_page)'. + create {NOT_FOUND_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + else + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create s.make_empty + if attached taxonomy_api.vocabularies (0, 0) as lst then + s.append ("
    ") + across + lst as ic + loop + s.append ("
  • ") + s.append (l_page.link (ic.item.name, "taxonomy/vocabulary/" + ic.item.id.out, Void)) + s.append ("
  • ") + end + s.append ("
") + else + s.append ("No vocabulary!") + end + l_page.set_main_content (s) + l_page.execute + end + end + +end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e index a915ebf..cfd7132 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e @@ -41,6 +41,24 @@ feature -- Access deferred end + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies including `a_term'. + deferred + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- Is `a_term' inside `a_vocab' ? + require + valid_term: a_term.has_id + valid_vocabulary: a_vocab.has_id + deferred + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + deferred + end + terms_count: INTEGER_64 -- Number of terms. deferred @@ -89,20 +107,28 @@ feature -- Access feature -- Store - save_vocabulary (a_voc: CMS_VOCABULARY) - -- Insert or update vocabulary `a_voc'. + save_vocabulary (a_voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. 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'. + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) + -- Insert or update term `t' as part of vocabulary `voc' if set. deferred ensure not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + require + t_has_id: t.has_id + deferred + 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 diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e index 2e663cd..f8336b6 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e @@ -53,6 +53,21 @@ feature -- Access do end + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- + do + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- + do + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- + do + end + terms_count: INTEGER_64 -- Number of terms. do @@ -85,18 +100,25 @@ feature -- Access feature -- Store - save_vocabulary (a_voc: CMS_VOCABULARY) - -- Insert or update vocabulary `a_voc'. + save_vocabulary (a_voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. do error_handler.add_custom_error (-1, "not implemented", "save_vocabulary") end - save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) -- do error_handler.add_custom_error (-1, "not implemented", "save_term") end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + do + error_handler.add_custom_error (-1, "not implemented", "remove_term_from_vocabulary") + 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") diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e index 7bb850b..15cad57 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e @@ -232,21 +232,41 @@ feature -- Access feature -- Store - save_vocabulary (voc: CMS_VOCABULARY) + save_vocabulary (voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + local + l_terms: CMS_TERM_COLLECTION do save_term (voc, create {CMS_VOCABULARY}.make_none) - across - voc.terms as ic - until - has_error - loop - save_term (ic.item, voc) + + if a_with_terms then + l_terms := terms (voc, 0, 0) + across + voc.terms as ic + until + has_error + loop + if attached l_terms.term_by_id (ic.item.id) as t then + -- Already contained. + -- Remove from `l_terms' to leave only terms to remove. + l_terms.remove (t) + else + save_term (ic.item, voc) + end + end + across + l_terms as ic + until + has_error + loop + remove_term_from_vocabulary (ic.item, voc) + end end end - save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) local l_parameters: STRING_TABLE [detachable ANY] + l_insert_voc: BOOLEAN do error_handler.reset @@ -255,6 +275,8 @@ feature -- Store l_parameters.put (t.description, "description") l_parameters.put (t.weight, "weight") + l_insert_voc := voc /= Void and then is_term_inside_vocabulary (t, voc) + sql_begin_transaction if t.has_id then l_parameters.put (t.id, "tid") @@ -263,9 +285,18 @@ feature -- Store sql_insert (sql_insert_term, l_parameters) t.set_id (last_inserted_term_id) end - if not has_error then + if + not has_error and + voc /= Void and then + not l_insert_voc + then + l_parameters.wipe_out l_parameters.put (t.id, "tid") - l_parameters.put (voc.id, "parent_tid") + if voc.has_id then + l_parameters.put (voc.id, "parent_tid") + else + l_parameters.put (0, "parent_tid") + end sql_insert (sql_insert_term_in_vocabulary, l_parameters) end if has_error then @@ -276,6 +307,19 @@ feature -- Store sql_finalize end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (t.id, "tid") + l_parameters.put (voc.id, "parent_tid") + sql_modify (sql_remove_term_from_vocabulary, l_parameters) + 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 @@ -376,6 +420,90 @@ feature -- Vocabulary and types end end + is_term_inside_vocabulary (a_term: CMS_TERM; a_voc: CMS_VOCABULARY): BOOLEAN + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (a_term.id, "tid") + l_parameters.put (a_voc.id, "parent_tid") + sql_query (sql_select_term_in_vocabulary, l_parameters) + sql_start + if not has_error or sql_after then + Result := sql_read_integer_64 (1) > 0 + end + sql_finalize + end + + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- + local + voc: detachable CMS_VOCABULARY + l_parameters: STRING_TABLE [detachable ANY] + l_parent_id: INTEGER_64 + l_ids: ARRAYED_LIST [INTEGER_64] + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_term.id, "tid") + sql_query (sql_select_vocabularies_for_term, l_parameters) + + create l_ids.make (1) + from + sql_start + until + sql_after or has_error + loop + l_parent_id := sql_read_integer_64 (1) + l_ids.force (l_parent_id) + sql_forth + end + sql_finalize + + if l_ids.count > 0 then + create Result.make (1) + across + l_ids as ic + loop + voc := vocabulary (ic.item) + if voc /= Void then + Result.force (voc) + end + end + if Result.count = 0 then + Result := Void + end + end + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (1) + l_parameters.put (a_vocab.id, "tid") + sql_query (sql_select_type_associated_with_vocabulary, l_parameters) + + create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (3) + from + sql_start + until + sql_after or has_error + loop + if attached sql_read_string_32 (1) as l_typename then + Result.force (l_typename) + end + sql_forth + end + sql_finalize + end + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) -- local @@ -396,10 +524,17 @@ feature -- Vocabulary and types 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) + if + attached vocabularies_for_type (a_type_name) as lst and then + across lst as ic some a_voc.id = ic.item.id end + then + sql_modify (sql_update_term_index, l_parameters) + else + sql_insert (sql_insert_term_index, l_parameters) + end + sql_finalize end @@ -464,6 +599,20 @@ feature {NONE} -- Queries ]" -- Terms under :parent_tid. + sql_select_vocabularies_for_term: STRING = "[ + SELECT parent + FROM taxonomy_hierarchy + WHERE tid = :tid + ; + ]" + + sql_select_term_in_vocabulary: STRING = "[ + SELECT count(*) + FROM taxonomy_hierarchy + WHERE tid = :tid and parent = :parent_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 @@ -505,6 +654,10 @@ feature {NONE} -- Queries VALUES (:tid, :parent_tid); ]" + sql_remove_term_from_vocabulary: STRING = "[ + DELETE FROM taxonomy_hierarchy WHERE tid=:tid AND parent=:parent_tid; + ]" + sql_select_terms_of_entity: STRING = "[ SELECT tid FROM taxonomy_index WHERE type=:type AND entity=:entity; ]" @@ -527,6 +680,19 @@ feature {NONE} -- Queries WHERE type=:type AND entity <= 0; ]" + sql_select_type_associated_with_vocabulary: STRING = "[ + SELECT type + FROM taxonomy_index + WHERE tid=:tid AND entity <= 0; + ]" + + sql_update_term_index: STRING = "[ + UPDATE taxonomy_index + SET entity=:entity + WHERE tid=:tid and type=:type + ; + ]" + sql_insert_term_index: STRING = "[ INSERT INTO taxonomy_index (tid, entity, type) VALUES (:tid, :entity, :type); diff --git a/modules/taxonomy/site/files/css/taxonomy.css b/modules/taxonomy/site/files/css/taxonomy.css index 41c87fa..dd216d0 100644 --- a/modules/taxonomy/site/files/css/taxonomy.css +++ b/modules/taxonomy/site/files/css/taxonomy.css @@ -19,3 +19,8 @@ ul.taxonomy li:hover { border-bottom: solid 1px #66f; background-color: #ddf; } + +table.taxonomy td { + border: solid 1px #ccc; + padding: 2px; +} diff --git a/modules/taxonomy/site/files/scss/taxonomy.scss b/modules/taxonomy/site/files/scss/taxonomy.scss index f409395..2ceff02 100644 --- a/modules/taxonomy/site/files/scss/taxonomy.scss +++ b/modules/taxonomy/site/files/scss/taxonomy.scss @@ -19,3 +19,9 @@ ul.taxonomy { } } } +table.taxonomy { + td { + border: solid 1px #ccc; + padding: 2px; + } +} diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 78a6252..d3a6e4e 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -15,7 +15,7 @@ inherit REFACTORING_HELPER - CMS_ENCODERS + CMS_REQUEST_UTIL create make @@ -407,7 +407,7 @@ feature {NONE} -- Hooks l_hooks: like hooks do l_hooks := hooks - register_hooks (l_hooks) + setup_core_hooks (l_hooks) across enabled_modules as ic loop @@ -436,7 +436,7 @@ feature -- Query: API feature -- Hooks - register_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + setup_core_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Register hooks associated with the cms core. do a_hooks.subscribe_to_export_hook (Current) diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 8746d00..ec7daeb 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -72,7 +72,6 @@ feature {NONE} -- Initialization l_module: CMS_MODULE l_enabled_modules: CMS_MODULE_COLLECTION do - api.register_hooks (hooks) l_enabled_modules := api.enabled_modules across l_enabled_modules as ic @@ -984,6 +983,26 @@ feature -- Theme end end +feature -- Theme helpers + + wsf_theme: WSF_THEME + -- WSF Theme from CMS `theme' for Current response. + local + t: like internal_wsf_theme + do + t := internal_wsf_theme + if t = Void then + create {CMS_TO_WSF_THEME} t.make (Current, theme) + internal_wsf_theme := t + end + Result := t + end + +feature {NONE} -- Theme helpers + + internal_wsf_theme: detachable WSF_THEME + -- Once per object for `wsf_theme'. + feature -- Element Change set_status_code (a_status: INTEGER)