Improved taxonomy by supporting tags, multiple terms allowed, and required kind of vocabulary for specific content type.

Updated node web form, to support taxonomy editing if allowed (specific support for CMS_VOCABULARY.is_tags: BOOLEAN).
Added notion of required or optional module dependencies.
This commit is contained in:
2015-12-03 19:24:58 +01:00
parent f1f3c126dd
commit 20dfce1396
27 changed files with 1339 additions and 84 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]
-- <Precursor>
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

View File

@@ -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]
-- <Precursor>
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

View File

@@ -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]
-- <Precursor>
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

View File

@@ -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

View File

@@ -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
-- <Precursor>
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)
-- <Precursor>
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

View File

@@ -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
-- <Precursor>
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
-- <Precursor>
-- 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)
-- <Precursor>
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)
-- <Precursor>
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

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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)
);

View File

@@ -1,2 +1,3 @@
DROP TABLE IF EXISTS taxonomy_term;
DROP TABLE IF EXISTS taxonomy_hierarchy;
DROP TABLE IF EXISTS taxonomy_index;

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms_taxonomy_module" uuid="6FD848D1-5B07-46EE-B7F5-CFE2BB01479D" library_target="cms_taxonomy_module">
<target name="cms_taxonomy_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="cms" location="..\..\cms.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>