Revisited hooks management, and added new CMS_HOOK_MANAGER. Added admin, and other link into navigation menu that goes into first sidebar. Fixed theme info, and template for sidebar ids. Better css class name for cms node content.
321 lines
9.2 KiB
Plaintext
321 lines
9.2 KiB
Plaintext
note
|
|
description: "CMS module bringing support for NODE management."
|
|
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
|
|
revision: "$Revision: 96616 $"
|
|
|
|
class
|
|
CMS_NODE_MODULE
|
|
|
|
inherit
|
|
CMS_MODULE
|
|
redefine
|
|
register_hooks,
|
|
initialize,
|
|
is_installed,
|
|
install,
|
|
module_api,
|
|
permissions
|
|
end
|
|
|
|
CMS_HOOK_MENU_SYSTEM_ALTER
|
|
|
|
CMS_HOOK_RESPONSE_ALTER
|
|
|
|
CMS_HOOK_BLOCK
|
|
|
|
CMS_RECENT_CHANGES_HOOK
|
|
|
|
create
|
|
make
|
|
|
|
feature {NONE} -- Initialization
|
|
|
|
make (a_setup: CMS_SETUP)
|
|
-- Create Current module, disabled by default.
|
|
do
|
|
version := "1.0"
|
|
description := "Service to manage content based on 'node'"
|
|
package := "core"
|
|
config := a_setup
|
|
end
|
|
|
|
config: CMS_SETUP
|
|
-- Node configuration.
|
|
|
|
feature -- Access
|
|
|
|
name: STRING = "node"
|
|
|
|
feature {CMS_API} -- Module Initialization
|
|
|
|
initialize (a_api: CMS_API)
|
|
-- <Precursor>
|
|
local
|
|
p1,p2: CMS_PAGE
|
|
ct: CMS_PAGE_NODE_TYPE
|
|
l_node_api: like node_api
|
|
l_node_storage: CMS_NODE_STORAGE_I
|
|
do
|
|
Precursor (a_api)
|
|
|
|
-- Storage initialization
|
|
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
|
|
create {CMS_NODE_STORAGE_SQL} l_node_storage.make (l_storage_sql)
|
|
else
|
|
-- FIXME: in case of NULL storage, should Current be disabled?
|
|
create {CMS_NODE_STORAGE_NULL} l_node_storage.make
|
|
end
|
|
|
|
-- Node API initialization
|
|
create l_node_api.make_with_storage (a_api, l_node_storage)
|
|
node_api := l_node_api
|
|
|
|
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
|
|
-- For now, we only have extension based on SQL statement.
|
|
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
|
|
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
|
|
|
-- FIXME: the following code is mostly for test purpose/initialization, remove later
|
|
if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then
|
|
if attached a_api.user_api.user_by_id (1) as u then
|
|
create ct
|
|
p1 := ct.new_node (Void)
|
|
p1.set_title ("Welcome")
|
|
p1.set_content ("Welcome, you are using the ROC Eiffel CMS", Void, Void) -- Use default format
|
|
p1.set_author (u)
|
|
l_sql_node_storage.save_node (p1)
|
|
|
|
p2 := ct.new_node (Void)
|
|
p2.set_title ("A new page example")
|
|
p2.set_content ("This is the content of a page", Void, Void) -- Use default format
|
|
p2.set_author (u)
|
|
p2.set_parent (p1)
|
|
l_sql_node_storage.save_node (p2)
|
|
end
|
|
end
|
|
else
|
|
-- FIXME: maybe provide a default solution based on file system, when no SQL storage is available.
|
|
-- IDEA: we could also have generic extension to node system, that handle generic addition field.
|
|
end
|
|
ensure then
|
|
node_api_set: node_api /= Void
|
|
end
|
|
|
|
feature {CMS_API} -- Module management
|
|
|
|
is_installed (a_api: CMS_API): BOOLEAN
|
|
-- Is Current module installed?
|
|
do
|
|
Result := Precursor (a_api)
|
|
if Result and attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
|
|
Result := l_sql_storage.sql_table_exists ("nodes") and
|
|
l_sql_storage.sql_table_exists ("page_nodes")
|
|
end
|
|
end
|
|
|
|
install (a_api: CMS_API)
|
|
do
|
|
-- Schema
|
|
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
|
|
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
|
|
end
|
|
Precursor {CMS_MODULE}(a_api)
|
|
end
|
|
|
|
feature {CMS_API} -- Access: API
|
|
|
|
module_api: detachable CMS_MODULE_API
|
|
-- <Precursor>
|
|
do
|
|
if attached node_api as l_api then
|
|
Result := l_api
|
|
else
|
|
-- Current is initialized, so node_api should be set.
|
|
check has_node_api: False end
|
|
end
|
|
end
|
|
|
|
node_api: detachable CMS_NODE_API
|
|
-- <Precursor>
|
|
|
|
feature -- Access
|
|
|
|
permissions: LIST [READABLE_STRING_8]
|
|
-- <Precursor>.
|
|
local
|
|
l_type_name: READABLE_STRING_8
|
|
do
|
|
Result := Precursor
|
|
Result.force ("create any node")
|
|
|
|
if attached node_api as l_node_api then
|
|
across
|
|
l_node_api.content_types as ic
|
|
loop
|
|
l_type_name := ic.item.name
|
|
if not l_type_name.is_whitespace then
|
|
Result.force ("create " + l_type_name)
|
|
|
|
Result.force ("view any " + l_type_name)
|
|
Result.force ("edit any " + l_type_name)
|
|
Result.force ("delete any " + l_type_name)
|
|
Result.force ("trash any " + l_type_name)
|
|
Result.force ("restore any " + l_type_name)
|
|
|
|
Result.force ("view revisions any " + l_type_name)
|
|
|
|
Result.force ("view own " + l_type_name)
|
|
Result.force ("edit own " + l_type_name)
|
|
Result.force ("delete own " + l_type_name)
|
|
Result.force ("trash own " + l_type_name)
|
|
Result.force ("restore own " + l_type_name)
|
|
|
|
Result.force ("view revisions own " + l_type_name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
feature -- Access: router
|
|
|
|
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
|
-- <Precursor>
|
|
do
|
|
if attached node_api as l_node_api then
|
|
configure_web (a_api, l_node_api, a_router)
|
|
end
|
|
end
|
|
|
|
configure_web (a_api: CMS_API; a_node_api: CMS_NODE_API; a_router: WSF_ROUTER)
|
|
local
|
|
l_node_handler: NODE_HANDLER
|
|
l_nodes_handler: NODES_HANDLER
|
|
l_uri_mapping: WSF_URI_MAPPING
|
|
l_trash_handler: TRASH_HANDLER
|
|
do
|
|
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
|
|
create l_node_handler.make (a_api, a_node_api)
|
|
create l_uri_mapping.make_trailing_slash_ignored ("/node", l_node_handler)
|
|
a_router.map (l_uri_mapping, a_router.methods_get_post)
|
|
|
|
a_router.handle ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
|
|
a_router.handle ("/node/{id}/revision", l_node_handler, a_router.methods_get)
|
|
a_router.handle ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
|
|
a_router.handle ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
|
|
a_router.handle ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
|
|
|
|
a_router.handle ("/node/{id}", l_node_handler, a_router.methods_get)
|
|
-- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post)
|
|
|
|
-- Nodes
|
|
create l_nodes_handler.make (a_api, a_node_api)
|
|
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
|
|
a_router.map (l_uri_mapping, a_router.methods_get)
|
|
|
|
-- Trash
|
|
create l_trash_handler.make (a_api, a_node_api)
|
|
create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
|
|
a_router.map (l_uri_mapping, a_router.methods_get)
|
|
|
|
end
|
|
|
|
feature -- Hooks
|
|
|
|
register_hooks (a_response: CMS_RESPONSE)
|
|
-- <Precursor>
|
|
do
|
|
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
|
|
a_response.hooks.subscribe_to_block_hook (Current)
|
|
a_response.hooks.subscribe_to_response_alter_hook (Current)
|
|
|
|
-- Module specific hook, if available.
|
|
a_response.hooks.subscribe_to_hook (Current, {CMS_RECENT_CHANGES_HOOK})
|
|
end
|
|
|
|
response_alter (a_response: CMS_RESPONSE)
|
|
-- <Precursor>
|
|
do
|
|
a_response.add_style (a_response.url ("/module/" + name + "/files/css/node.css", Void), Void)
|
|
end
|
|
|
|
block_list: ITERABLE [like {CMS_BLOCK}.name]
|
|
-- <Precursor>
|
|
do
|
|
Result := <<"node-info">>
|
|
end
|
|
|
|
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
|
local
|
|
-- b: CMS_CONTENT_BLOCK
|
|
do
|
|
-- create b.make (a_block_id, "Block::node", "This is a block test", Void)
|
|
-- a_response.add_block (b, "sidebar_second")
|
|
end
|
|
|
|
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
|
|
local
|
|
lnk: CMS_LOCAL_LINK
|
|
do
|
|
debug
|
|
create lnk.make ("List of nodes", "nodes")
|
|
a_menu_system.navigation_menu.extend (lnk)
|
|
end
|
|
create lnk.make ("Trash", "trash")
|
|
a_menu_system.navigation_menu.extend (lnk)
|
|
|
|
create lnk.make ("Create ..", "node")
|
|
a_menu_system.navigation_menu.extend (lnk)
|
|
end
|
|
|
|
populate_recent_changes (a_changes: CMS_RECENT_CHANGE_CONTAINER; a_sources: LIST [READABLE_STRING_8])
|
|
local
|
|
params: CMS_DATA_QUERY_PARAMETERS
|
|
ch: CMS_RECENT_CHANGE_ITEM
|
|
n: CMS_NODE
|
|
l_info: STRING_8
|
|
l_src: detachable READABLE_STRING_8
|
|
l_nodes: ITERABLE [CMS_NODE]
|
|
do
|
|
create params.make (0, a_changes.limit)
|
|
if attached node_api as l_node_api then
|
|
across
|
|
l_node_api.content_types as ic
|
|
loop
|
|
a_sources.force (ic.item.name)
|
|
end
|
|
l_src := a_changes.source
|
|
if attached a_changes.date as l_date then
|
|
l_nodes := l_node_api.recent_node_changes_before (params, l_date)
|
|
else
|
|
l_nodes := l_node_api.recent_node_changes_before (params, create {DATE_TIME}.make_now_utc)
|
|
end
|
|
across l_nodes as ic loop
|
|
n := ic.item
|
|
if l_src = Void or else l_src.is_case_insensitive_equal_general (n.content_type) then
|
|
n := l_node_api.full_node (n)
|
|
create ch.make (n.content_type, create {CMS_LOCAL_LINK}.make (n.title, "node/" + n.id.out), n.modification_date)
|
|
if n.creation_date ~ n.modification_date then
|
|
l_info := "new"
|
|
if not n.is_published then
|
|
l_info.append (" (unpublished)")
|
|
end
|
|
else
|
|
if n.is_trashed then
|
|
l_info := "trashed"
|
|
else
|
|
l_info := "updated"
|
|
if not n.is_published then
|
|
l_info.append (" (unpublished)")
|
|
end
|
|
end
|
|
end
|
|
ch.set_information (l_info)
|
|
ch.set_author (n.author)
|
|
a_changes.force (ch)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|