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