Extracted page support from cms_node_module, and add a proper CMS_PAGE_MODULE.
- now, the CMS_PAGE_MODULE has to be declared in the related CMS_SETUP via CMS_EXECUTION. (See demo for example) Improved the export facilities. Implemented blog and page export. Added import facilities. Implemented blog and page import. Improved node revision web interface (allow to edit a past revision, in order to restore it as latest revisionm i.e current). Removed specific tag from blog module, and reuse the taxonomy module for that purpose. Added WIKITEXT module that provide a WIKITEXT_FILTER, so now we can have wikitext content. - for now, no support for wiki links such as [[Foobar]].
This commit is contained in:
@@ -10,9 +10,6 @@ class
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
@@ -28,30 +25,6 @@ feature {NONE} -- Initialization
|
||||
-- error_handler.add_synchronization (a_node_storage.error_handler)
|
||||
end
|
||||
|
||||
initialize
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor
|
||||
initialize_node_types
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
-- Initialize content type system.
|
||||
local
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
-- Initialize node content types.
|
||||
create ct
|
||||
--| For now, add all available formats to content type `ct'.
|
||||
across
|
||||
cms_api.formats as ic
|
||||
loop
|
||||
ct.extend_format (ic.item)
|
||||
end
|
||||
add_node_type (ct)
|
||||
add_node_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct, Current))
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
node_storage: CMS_NODE_STORAGE_I
|
||||
@@ -301,45 +274,9 @@ feature -- Access: Node
|
||||
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
|
||||
end
|
||||
|
||||
feature -- Access: page/book outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
do
|
||||
Result := node_storage.children (a_node)
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Potential parent nodes for node `a_node'.
|
||||
-- Ensure no cycle exists.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
across node_storage.available_parents_for_node (a_node) as ic loop
|
||||
check distinct: not a_node.same_node (ic.item) end
|
||||
if not is_node_a_parent_of (a_node, ic.item) then
|
||||
Result.force (ic.item)
|
||||
end
|
||||
end
|
||||
ensure
|
||||
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
|
||||
end
|
||||
|
||||
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
|
||||
-- Is `a_node' a direct or indirect parent of node `a_child'?
|
||||
require
|
||||
distinct_nodes: not a_node.same_node (a_child)
|
||||
do
|
||||
if
|
||||
attached {CMS_PAGE} full_node (a_child) as l_child_page and then
|
||||
attached l_child_page.parent as l_parent
|
||||
then
|
||||
if l_parent.same_node (a_node) then
|
||||
Result := True
|
||||
else
|
||||
Result := is_node_a_parent_of (a_node, l_parent)
|
||||
end
|
||||
end
|
||||
Result := node_storage.nodes_of_type_with_title (a_node_type, a_title)
|
||||
end
|
||||
|
||||
feature -- Permission Scope: Node
|
||||
|
||||
@@ -27,9 +27,9 @@ inherit
|
||||
|
||||
CMS_TAXONOMY_HOOK
|
||||
|
||||
CMS_HOOK_EXPORT
|
||||
-- CMS_HOOK_EXPORT
|
||||
|
||||
CMS_EXPORT_NODE_UTILITIES
|
||||
-- CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
@@ -60,8 +60,6 @@ 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
|
||||
@@ -78,34 +76,6 @@ feature {CMS_API} -- Module Initialization
|
||||
-- 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
|
||||
@@ -117,8 +87,7 @@ feature {CMS_API} -- Module management
|
||||
do
|
||||
Result := Precursor (a_api)
|
||||
if Result and attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
Result := l_sql_storage.sql_table_exists ("nodes") and
|
||||
l_sql_storage.sql_table_exists ("page_nodes")
|
||||
Result := l_sql_storage.sql_table_exists ("nodes")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -247,7 +216,7 @@ feature -- Hooks
|
||||
a_hooks.subscribe_to_menu_system_alter_hook (Current)
|
||||
a_hooks.subscribe_to_block_hook (Current)
|
||||
a_hooks.subscribe_to_response_alter_hook (Current)
|
||||
a_hooks.subscribe_to_export_hook (Current)
|
||||
-- a_hooks.subscribe_to_export_hook (Current)
|
||||
|
||||
-- Module specific hook, if available.
|
||||
a_hooks.subscribe_to_hook (Current, {CMS_RECENT_CHANGES_HOOK})
|
||||
@@ -427,94 +396,4 @@ feature -- Hooks
|
||||
end
|
||||
end
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
local
|
||||
l_node_type: CMS_CONTENT_TYPE
|
||||
n: CMS_NODE
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
lst: LIST [CMS_NODE]
|
||||
do
|
||||
if attached node_api as l_node_api then
|
||||
across
|
||||
l_node_api.node_types as types_ic
|
||||
loop
|
||||
l_node_type := types_ic.item
|
||||
if
|
||||
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
|
||||
l_node_type.name.same_string_general ("page") and then
|
||||
( a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
|
||||
-- For now, handle only page from this node module.
|
||||
lst := l_node_api.nodes_of_type (l_node_type)
|
||||
a_export_parameters.log ("Exporting " + lst.count.out + " nodes of type " + l_node_type.name)
|
||||
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
n := l_node_api.full_node (ic.item)
|
||||
a_export_parameters.log (l_node_type.name + " #" + n.id.out + " rev=" + n.revision.out)
|
||||
create f.make_with_path (p.extended (n.id.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
if attached {CMS_PAGE} n as l_page then
|
||||
f.put_string (json_to_string (page_node_to_json (l_page)))
|
||||
else
|
||||
f.put_string (json_to_string (node_to_json (n)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
-- Revisions.
|
||||
if attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1 then
|
||||
a_export_parameters.log (l_node_type.name + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name).extended (n.id.out)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
l_revisions as revs_ic
|
||||
loop
|
||||
n := revs_ic.item
|
||||
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
if attached {CMS_PAGE} n as l_page then
|
||||
f.put_string (json_to_string (page_node_to_json (l_page)))
|
||||
else
|
||||
f.put_string (json_to_string (node_to_json (n)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
page_node_to_json (a_page: CMS_PAGE): JSON_OBJECT
|
||||
local
|
||||
j: JSON_OBJECT
|
||||
do
|
||||
Result := node_to_json (a_page)
|
||||
if attached a_page.parent as l_parent_page then
|
||||
create j.make_empty
|
||||
j.put_string (l_parent_page.content_type, "type")
|
||||
j.put_integer (l_parent_page.id, "nid")
|
||||
Result.put (j, "parent")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11,11 +11,15 @@ class
|
||||
inherit
|
||||
CMS_EXPORT_JSON_UTILITIES
|
||||
|
||||
CMS_API_ACCESS
|
||||
|
||||
feature -- Access
|
||||
|
||||
node_to_json (n: CMS_NODE): JSON_OBJECT
|
||||
node_to_json (n: CMS_NODE; a_node_api: CMS_NODE_API): JSON_OBJECT
|
||||
local
|
||||
jo,j_author: JSON_OBJECT
|
||||
jo: JSON_OBJECT
|
||||
ja: JSON_ARRAY
|
||||
j, jterm: JSON_OBJECT
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.put_string (n.content_type, "type")
|
||||
@@ -27,10 +31,7 @@ feature -- Access
|
||||
put_date_into_json (n.publication_date, "publication_date", Result)
|
||||
Result.put_integer (n.status, "status")
|
||||
if attached n.author as u then
|
||||
create j_author.make
|
||||
j_author.put_integer (u.id, "uid")
|
||||
j_author.put_string (u.name, "name")
|
||||
Result.put (j_author, "author")
|
||||
Result.put (user_to_json (u), "author")
|
||||
end
|
||||
create jo.make_empty
|
||||
if attached n.format as l_format then
|
||||
@@ -44,12 +45,41 @@ feature -- Access
|
||||
end
|
||||
Result.put (jo, "data")
|
||||
if attached n.link as lnk then
|
||||
create jo.make_empty
|
||||
jo.put_string (lnk.title, "title")
|
||||
jo.put_string (lnk.location, "location")
|
||||
jo.put_integer (lnk.weight, "weight")
|
||||
Result.put (jo, "link")
|
||||
Result.put (link_to_json (lnk), "link")
|
||||
end
|
||||
|
||||
if attached {CMS_TAXONOMY_API} a_node_api.cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then
|
||||
if
|
||||
attached l_taxonomy_api.terms_of_content (n, Void) as l_tags and then
|
||||
not l_tags.is_empty
|
||||
then
|
||||
create ja.make (l_tags.count)
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
create jterm.make_with_capacity (2)
|
||||
jterm.put_integer (ic.item.id, "id")
|
||||
jterm.put_string (ic.item.text, "text")
|
||||
ja.extend (jterm)
|
||||
end
|
||||
Result.put (ja, "tags")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
user_to_json (u: CMS_USER): JSON_OBJECT
|
||||
do
|
||||
create Result.make
|
||||
Result.put_integer (u.id, "uid")
|
||||
Result.put_string (u.name, "name")
|
||||
end
|
||||
|
||||
link_to_json (lnk: CMS_LOCAL_LINK): JSON_OBJECT
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.put_string (lnk.title, "title")
|
||||
Result.put_string (lnk.location, "location")
|
||||
Result.put_integer (lnk.weight, "weight")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,8 @@ class
|
||||
|
||||
inherit
|
||||
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE]
|
||||
rename
|
||||
make as make_for_node
|
||||
redefine
|
||||
content_type,
|
||||
append_content_as_html_to,
|
||||
@@ -19,11 +21,22 @@ inherit
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_type: like content_type; a_page_api: CMS_PAGE_API)
|
||||
do
|
||||
page_api := a_page_api
|
||||
make_for_node (a_type, a_page_api.node_api)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
content_type: CMS_PAGE_NODE_TYPE
|
||||
-- Associated content type.
|
||||
|
||||
page_api: CMS_PAGE_API
|
||||
-- Associated page API.
|
||||
|
||||
feature -- Forms ...
|
||||
|
||||
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
|
||||
@@ -122,7 +135,7 @@ feature -- Forms ...
|
||||
if
|
||||
nid > 0 and then
|
||||
attached l_node_api.node (nid) as l_node and then
|
||||
l_node_api.is_node_a_parent_of (l_node, l_parent_node)
|
||||
page_api.is_node_a_parent_of (l_node, l_parent_node)
|
||||
then
|
||||
fd.report_invalid_field ("select_parent_node", "Invalid parent due to cycle (node #" + nid.out + " is already a parent of node #" + l_parent_id.out)
|
||||
end
|
||||
@@ -144,7 +157,7 @@ feature -- Output
|
||||
lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
Precursor (a_node, is_teaser, a_output, a_response)
|
||||
|
||||
|
||||
if not is_teaser then
|
||||
l_node_api := node_api
|
||||
if
|
||||
@@ -170,7 +183,7 @@ feature -- Output
|
||||
a_output.append (a_response.link (l_parent_node.title, l_node_api.node_path (l_parent_node), Void))
|
||||
a_output.append ("</li>")
|
||||
end
|
||||
if attached l_node_api.children (a_node) as l_children then
|
||||
if attached page_api.children (a_node) as l_children then
|
||||
across
|
||||
l_children as ic
|
||||
loop
|
||||
|
||||
@@ -30,7 +30,15 @@ feature -- Execution
|
||||
location.ends_with_general ("/edit") and then
|
||||
node_api.has_permission_for_action_on_node ("edit", l_node, user)
|
||||
then
|
||||
edit_node (l_node, l_type, b)
|
||||
if
|
||||
attached {WSF_STRING} request.query_parameter ("revision") as p_rev and then
|
||||
p_rev.value.is_integer_64 and then
|
||||
attached node_api.revision_node (l_node.id, p_rev.value.to_integer_64) as l_rev_node
|
||||
then
|
||||
edit_node (l_rev_node, l_type, l_rev_node.revision < l_node.revision, b)
|
||||
else
|
||||
edit_node (l_node, l_type, False, b)
|
||||
end
|
||||
elseif
|
||||
location.ends_with_general ("/delete") and then
|
||||
node_api.has_permission_for_action_on_node ("delete", l_node, user)
|
||||
@@ -118,12 +126,15 @@ feature {NONE} -- Create a new node
|
||||
end
|
||||
|
||||
|
||||
edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
|
||||
edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; is_old_revision: BOOLEAN; b: STRING_8)
|
||||
local
|
||||
f: like new_edit_form
|
||||
fd: detachable WSF_FORM_DATA
|
||||
do
|
||||
f := new_edit_form (A_node, url (location, Void), "edit-" + a_type.name, a_type)
|
||||
if is_old_revision then
|
||||
add_warning_message ("You are editing old revision #" + a_node.revision.out + " !")
|
||||
end
|
||||
api.hooks.invoke_form_alter (f, fd, Current)
|
||||
if request.is_post_request_method then
|
||||
f.validation_actions.extend (agent edit_form_validate (?, b))
|
||||
@@ -135,6 +146,7 @@ feature {NONE} -- Create a new node
|
||||
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make ("Revisions", node_api.node_path (a_node) + "/revision"), primary_tabs)
|
||||
end
|
||||
|
||||
if attached redirection as l_location then
|
||||
|
||||
@@ -122,6 +122,7 @@ feature -- HTTP Methods
|
||||
if
|
||||
l_node /= Void and then
|
||||
l_rev > 0 and then
|
||||
l_rev < l_node.revision and then
|
||||
node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user)
|
||||
then
|
||||
l_node := node_api.revision_node (l_nid, l_rev)
|
||||
@@ -322,7 +323,12 @@ feature {NONE} -- Trash:Restore
|
||||
b.append ("<a href=%"")
|
||||
b.append (r.url (node_api.node_path (n) + "?revision=" + n.revision.out, Void))
|
||||
b.append ("%">")
|
||||
|
||||
if n.revision = l_node.revision then
|
||||
b.append ("Current ")
|
||||
else
|
||||
b.append ("Revision")
|
||||
end
|
||||
b.append (" #")
|
||||
b.append (n.revision.out)
|
||||
b.append (" : ")
|
||||
b.append (n.modification_date.out)
|
||||
@@ -331,6 +337,11 @@ feature {NONE} -- Trash:Restore
|
||||
b.append (" by ")
|
||||
b.append (r.link (l_author.name, "user/" + l_author.id.out, Void))
|
||||
end
|
||||
if node_api.has_permission_for_action_on_node ("edit revisions", l_node, api.user) then
|
||||
b.append (" (<a href=%"")
|
||||
b.append (r.url (node_api.node_path (n) + "/edit?revision=" + n.revision.out, Void))
|
||||
b.append ("%">edit</a>)")
|
||||
end
|
||||
b.append ("</li>")
|
||||
end
|
||||
b.append ("</ul>")
|
||||
|
||||
@@ -38,6 +38,7 @@ feature -- Execution
|
||||
local
|
||||
nid: INTEGER_64
|
||||
l_node: like node
|
||||
l_title: STRING_32
|
||||
do
|
||||
l_node := node
|
||||
if l_node = Void then
|
||||
@@ -62,8 +63,11 @@ feature -- Execution
|
||||
if revision > 0 then
|
||||
add_warning_message ("The revisions let you track differences between multiple versions of a post.")
|
||||
end
|
||||
if l_node /= Void and revision > 0 then
|
||||
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
|
||||
if l_node /= Void and then revision > 0 and then revision < l_node.revision then
|
||||
create l_title.make_from_string_general ("Revision #" + revision.out + " of %"")
|
||||
l_title.append (l_node.title)
|
||||
l_title.append_character ('"')
|
||||
set_title (l_title)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
106
modules/node/import/cms_import_node_utilities.e
Normal file
106
modules/node/import/cms_import_node_utilities.e
Normal file
@@ -0,0 +1,106 @@
|
||||
note
|
||||
description: "[
|
||||
Routines usefull during node exportation (see {CMS_HOOK_IMPORT}).
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_IMPORT_NODE_UTILITIES
|
||||
|
||||
inherit
|
||||
CMS_IMPORT_JSON_UTILITIES
|
||||
|
||||
CMS_API_ACCESS
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL; a_node_api: CMS_NODE_API): detachable CMS_USER
|
||||
-- CMS user named `a_name`.
|
||||
do
|
||||
Result := a_node_api.cms_api.user_api.user_by_name (a_name)
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
json_to_node (a_node_type: CMS_NODE_TYPE [CMS_NODE]; j: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_NODE
|
||||
local
|
||||
l_node: CMS_PARTIAL_NODE
|
||||
do
|
||||
if
|
||||
attached json_string_item (j, "title") as l_title
|
||||
then
|
||||
create l_node.make_empty (a_node_type.name)
|
||||
l_node.set_title (l_title)
|
||||
Result := l_node
|
||||
if attached json_date_item (j, "creation_date") as dt then
|
||||
l_node.set_creation_date (dt)
|
||||
end
|
||||
if attached json_date_item (j, "modification_date") as dt then
|
||||
l_node.set_modification_date (dt)
|
||||
end
|
||||
if attached json_date_item (j, "publication_date") as dt then
|
||||
l_node.set_publication_date (dt)
|
||||
end
|
||||
if attached json_integer_item (j, "status") as l_status then
|
||||
inspect l_status
|
||||
when {CMS_NODE_API}.published then
|
||||
l_node.mark_published
|
||||
when {CMS_NODE_API}.not_published then
|
||||
l_node.mark_not_published
|
||||
when {CMS_NODE_API}.trashed then
|
||||
l_node.mark_trashed
|
||||
else
|
||||
end
|
||||
end
|
||||
if attached {JSON_OBJECT} j.item ("author") as j_author then
|
||||
l_node.set_author (json_to_user (j_author, a_node_api))
|
||||
end
|
||||
if attached {JSON_OBJECT} j.item ("data") as j_data then
|
||||
l_node.set_all_content (json_string_item (j_data, "content"), json_string_item (j_data, "summary"), json_string_8_item (j_data, "format"))
|
||||
end
|
||||
if
|
||||
attached {JSON_OBJECT} j.item ("link") as j_link and then
|
||||
attached json_to_local_link (j_link) as lnk
|
||||
then
|
||||
l_node.set_link (lnk)
|
||||
end
|
||||
if attached {JSON_ARRAY} j.item ("tags") as j_tags and then j_tags.count > 0 then
|
||||
if attached {CMS_TAXONOMY_API} a_node_api.cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then
|
||||
across
|
||||
j_tags as ic
|
||||
loop
|
||||
if
|
||||
attached {JSON_OBJECT} ic.item as j_tag and then
|
||||
attached json_string_item (j_tag, "text") as l_tag_text
|
||||
then
|
||||
if attached l_taxonomy_api.term_by_text (l_tag_text, Void) as t then
|
||||
l_taxonomy_api.associate_term_with_content (t, l_node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_to_user (j_user: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_USER
|
||||
do
|
||||
if
|
||||
attached json_string_item (j_user, "name") as l_author_name
|
||||
then
|
||||
Result := user_by_name (l_author_name, a_node_api)
|
||||
end
|
||||
end
|
||||
|
||||
json_to_local_link (j_link: JSON_OBJECT): detachable CMS_LOCAL_LINK
|
||||
do
|
||||
if
|
||||
attached json_string_8_item (j_link, "location") as loc
|
||||
then
|
||||
create Result.make (json_string_item (j_link, "title"), loc)
|
||||
Result.set_weight (json_integer_item (j_link, "weight").to_integer_32)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -140,19 +140,22 @@ feature -- Access
|
||||
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
deferred
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Given the node `a_node', return the list of possible parent nodes id
|
||||
deferred
|
||||
ensure
|
||||
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of nodes of type `a_node_type' with title `a_title`.
|
||||
--| Redefine to optimize!
|
||||
do
|
||||
Result := nodes_of_type (a_node_type)
|
||||
from
|
||||
Result.start
|
||||
until
|
||||
Result.after
|
||||
loop
|
||||
if a_title.same_string (Result.item.title) then
|
||||
Result.forth
|
||||
else
|
||||
Result.remove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change: Node
|
||||
|
||||
@@ -75,19 +75,6 @@ feature -- Access: node
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
|
||||
feature -- Node
|
||||
|
||||
new_node (a_node: CMS_NODE)
|
||||
|
||||
@@ -13,7 +13,8 @@ inherit
|
||||
|
||||
CMS_NODE_STORAGE_I
|
||||
redefine
|
||||
nodes_of_type
|
||||
nodes_of_type,
|
||||
nodes_of_type_with_title
|
||||
end
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
@@ -277,53 +278,27 @@ feature -- Access
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of nodes of type `a_node_type' with title `a_title`.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".children")
|
||||
write_information_log (generator + ".nodes_of_type_with_title")
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_node_type.name, "node_type")
|
||||
l_parameters.put (a_title, "title")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_children_of_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".available_parents_for_node")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_available_parents_for_node, l_parameters)
|
||||
sql_query (sql_select_nodes_of_type_with_title, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
check expected_node_type: l_node.content_type.same_string (a_node_type.name) end
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
@@ -377,7 +352,7 @@ feature -- Change: Node
|
||||
sql_modify (sql_delete_node, l_parameters)
|
||||
sql_finalize
|
||||
|
||||
-- we remove node_revisions and pages.
|
||||
-- we remove node_revisions and potential extended nodes.
|
||||
-- Check: maybe we need a transaction.
|
||||
sql_modify (sql_delete_node_revisions, l_parameters)
|
||||
sql_finalize
|
||||
@@ -516,6 +491,10 @@ feature {NONE} -- Queries
|
||||
-- SQL Query to retrieve all nodes of type :node_type.
|
||||
--| note: {CMS_NODE_API}.trashed = -1
|
||||
|
||||
sql_select_nodes_of_type_with_title: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE status != -1 AND type=:node_type AND title=:title;"
|
||||
-- SQL Query to retrieve all nodes of type :node_type with title :title.
|
||||
--| note: {CMS_NODE_API}.trashed = -1
|
||||
|
||||
sql_select_node_revisions: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision < :revision ORDER BY node_revisions.revision DESC;"
|
||||
-- SQL query to get node revisions (missing the latest one).
|
||||
|
||||
@@ -557,22 +536,8 @@ feature {NONE} -- Queries
|
||||
Sql_last_insert_node_revision: STRING = "SELECT MAX(revision) FROM node_revisions;"
|
||||
Sql_last_insert_node_revision_for_nid: STRING = "SELECT MAX(revision) FROM node_revisions WHERE nid=:nid;"
|
||||
|
||||
sql_select_available_parents_for_node : STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
|
||||
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_select_children_of_node: STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
|
||||
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;"
|
||||
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fetch_node: detachable CMS_PARTIAL_NODE
|
||||
|
||||
@@ -28,10 +28,3 @@ CREATE TABLE node_revisions (
|
||||
CONSTRAINT Unique_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
|
||||
8
modules/node/site/scripts/page.sql
Normal file
8
modules/node/site/scripts/page.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
172
modules/node/submodules/page/cms_page_api.e
Normal file
172
modules/node/submodules/page/cms_page_api.e
Normal file
@@ -0,0 +1,172 @@
|
||||
note
|
||||
description: "API to handle nodes of type page. Extends the node API."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_API
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
rename
|
||||
make as make_with_cms_api
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API; a_node_api: CMS_NODE_API)
|
||||
-- (from CMS_MODULE_API)
|
||||
-- (export status {NONE})
|
||||
do
|
||||
node_api := a_node_api
|
||||
make_with_cms_api (a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor
|
||||
|
||||
---- l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
||||
|
||||
-- Create the node storage for type blog
|
||||
if attached storage.as_sql_storage as l_storage_sql then
|
||||
create {CMS_PAGE_STORAGE_SQL} page_storage.make (l_storage_sql)
|
||||
else
|
||||
create {CMS_PAGE_STORAGE_NULL} page_storage.make
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
-- Initialize content type system.
|
||||
local
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
-- Initialize node content types.
|
||||
create ct
|
||||
page_content_type := ct
|
||||
--| For now, add all available formats to content type `ct'.
|
||||
across
|
||||
cms_api.formats as ic
|
||||
loop
|
||||
ct.extend_format (ic.item)
|
||||
end
|
||||
node_api.add_node_type (ct)
|
||||
node_api.add_node_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct, Current))
|
||||
end
|
||||
|
||||
feature {CMS_API_ACCESS, CMS_MODULE, CMS_API} -- Restricted access
|
||||
|
||||
node_api: CMS_NODE_API
|
||||
|
||||
page_content_type: CMS_PAGE_NODE_TYPE
|
||||
|
||||
feature -- Access
|
||||
|
||||
pages: LIST [CMS_PAGE]
|
||||
-- All pages.
|
||||
do
|
||||
Result := nodes_to_pages (node_api.nodes_of_type (page_content_type))
|
||||
end
|
||||
|
||||
pages_with_title (a_title: READABLE_STRING_GENERAL): LIST [CMS_PAGE]
|
||||
-- List of pages with title `a_title`.
|
||||
do
|
||||
Result := nodes_to_pages (node_api.nodes_of_type_with_title (page_content_type, a_title))
|
||||
end
|
||||
|
||||
feature -- Access: page/book outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
do
|
||||
Result := page_storage.children (a_node)
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Potential parent nodes for node `a_node'.
|
||||
-- Ensure no cycle exists.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
across page_storage.available_parents_for_node (a_node) as ic loop
|
||||
check distinct: not a_node.same_node (ic.item) end
|
||||
if not is_node_a_parent_of (a_node, ic.item) then
|
||||
Result.force (ic.item)
|
||||
end
|
||||
end
|
||||
ensure
|
||||
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
|
||||
end
|
||||
|
||||
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
|
||||
-- Is `a_node' a direct or indirect parent of node `a_child'?
|
||||
require
|
||||
distinct_nodes: not a_node.same_node (a_child)
|
||||
do
|
||||
if
|
||||
attached {CMS_PAGE} node_api.full_node (a_child) as l_child_page and then
|
||||
attached l_child_page.parent as l_parent
|
||||
then
|
||||
if l_parent.same_node (a_node) then
|
||||
Result := True
|
||||
else
|
||||
Result := is_node_a_parent_of (a_node, l_parent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
full_page_node (a_page: CMS_PAGE): CMS_PAGE
|
||||
-- If `a_page' is partial, return the full page node from `a_page',
|
||||
-- otherwise return directly `a_page'.
|
||||
require
|
||||
a_page_set: a_page /= Void
|
||||
do
|
||||
if attached {CMS_PAGE} node_api.full_node (a_page) as l_full_page then
|
||||
Result := l_full_page
|
||||
else
|
||||
Result := a_page
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Commit
|
||||
|
||||
save_page (a_page: CMS_PAGE)
|
||||
do
|
||||
node_api.save_node (a_page)
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
page_storage: CMS_PAGE_STORAGE_I
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
nodes_to_pages (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_PAGE]
|
||||
-- Convert list of nodes into a list of page when possible.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_PAGE]} Result.make (a_nodes.count)
|
||||
|
||||
if attached node_api as l_node_api then
|
||||
across
|
||||
a_nodes as ic
|
||||
loop
|
||||
if attached {CMS_PAGE} l_node_api.full_node (ic.item) as l_page then
|
||||
Result.force (l_page)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
411
modules/node/submodules/page/cms_page_module.e
Normal file
411
modules/node/submodules/page/cms_page_module.e
Normal file
@@ -0,0 +1,411 @@
|
||||
note
|
||||
description: "Node page implementation."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_MODULE
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as page_api
|
||||
redefine
|
||||
setup_hooks,
|
||||
initialize,
|
||||
install, is_installed,
|
||||
page_api
|
||||
end
|
||||
|
||||
CMS_HOOK_EXPORT
|
||||
|
||||
CMS_HOOK_IMPORT
|
||||
|
||||
CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
CMS_IMPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Page service"
|
||||
package := "content"
|
||||
add_dependency ({CMS_NODE_MODULE})
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "page"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
p1,p2: CMS_PAGE
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
Precursor (a_api)
|
||||
if attached {CMS_NODE_API} a_api.module_api ({CMS_NODE_MODULE}) as l_node_api then
|
||||
node_api := l_node_api
|
||||
|
||||
create page_api.make (a_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
|
||||
end
|
||||
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 a_api.storage.as_sql_storage as l_sql_storage then
|
||||
Result := l_sql_storage.sql_table_exists ("page_nodes")
|
||||
end
|
||||
end
|
||||
|
||||
install (a_api: CMS_API)
|
||||
do
|
||||
-- Schema
|
||||
if attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
if attached a_api.module ({CMS_NODE_MODULE}) as l_node_module then
|
||||
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (l_node_module, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
|
||||
end
|
||||
if l_sql_storage.has_error then
|
||||
a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
|
||||
else
|
||||
Precursor {CMS_MODULE} (a_api)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
|
||||
page_api: detachable CMS_PAGE_API
|
||||
-- <Precursor>
|
||||
|
||||
node_api: detachable CMS_NODE_API
|
||||
|
||||
feature -- Access: router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
do
|
||||
a_hooks.subscribe_to_export_hook (Current)
|
||||
a_hooks.subscribe_to_import_hook (Current)
|
||||
end
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_ctx: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
local
|
||||
n: CMS_PAGE
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
lst: LIST [CMS_NODE]
|
||||
do
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached l_node_api.node_type ("page") as l_node_type and then
|
||||
( a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
|
||||
attached page_api as l_page_api
|
||||
then
|
||||
lst := l_page_api.pages
|
||||
a_export_ctx.log ("Exporting " + lst.count.out + " notes of type `" + l_node_type.name + "`.")
|
||||
create d.make_with_path (a_export_ctx.location.extended ("nodes").extended (l_node_type.name))
|
||||
if d.exists then
|
||||
d.recursive_delete
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if attached {CMS_PAGE} ic.item as l_page_node then
|
||||
n := l_page_api.full_page_node (l_page_node)
|
||||
a_export_ctx.log (n.content_type + " #" + n.id.out)
|
||||
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out).appended_with_extension ("json")
|
||||
create d.make_with_path (p.parent)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
|
||||
create f.make_with_path (p)
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (page_node_to_json (n, l_page_api)))
|
||||
f.close
|
||||
end
|
||||
-- Revisions.
|
||||
if
|
||||
attached l_node_api.node_revisions (n) as l_revisions and then
|
||||
l_revisions.count > 1
|
||||
then
|
||||
a_export_ctx.log (n.content_type + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
l_revisions as revs_ic
|
||||
loop
|
||||
if attached {CMS_PAGE} revs_ic.item as l_rev_page then
|
||||
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (page_node_to_json (l_rev_page, l_page_api)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
page_node_to_json (a_page: CMS_PAGE; a_page_api: CMS_PAGE_API): JSON_OBJECT
|
||||
local
|
||||
j: JSON_OBJECT
|
||||
p: CMS_PAGE
|
||||
do
|
||||
Result := node_to_json (a_page, a_page_api.node_api)
|
||||
if attached a_page.parent as l_parent_page then
|
||||
p := a_page_api.full_page_node (l_parent_page)
|
||||
create j.make_empty
|
||||
j.put_string (p.content_type, "type")
|
||||
j.put_string (p.title, "title")
|
||||
j.put_integer (p.id, "nid")
|
||||
if attached p.link as lnk then
|
||||
j.put (link_to_json (lnk), "link")
|
||||
end
|
||||
Result.put (j, "parent")
|
||||
end
|
||||
end
|
||||
|
||||
import_from (a_import_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_import_ctx: CMS_IMPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Import data identified by `a_import_id_list',
|
||||
-- or import all data if `a_import_id_list' is Void.
|
||||
local
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
s: STRING
|
||||
jp: JSON_PARSER
|
||||
loc: READABLE_STRING_8
|
||||
l_parentable_list: ARRAYED_LIST [TUPLE [page: CMS_PAGE; parent: CMS_PAGE]]
|
||||
l_new_pages: STRING_TABLE [CMS_PAGE] -- indexed by link location, if any.
|
||||
do
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached {CMS_PAGE_NODE_TYPE} l_node_api.node_type ({CMS_PAGE_NODE_TYPE}.name) as l_node_type and then
|
||||
attached page_api as l_page_api and then
|
||||
( a_import_id_list = Void
|
||||
or else across a_import_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"import any node", "import " + l_node_type.name>>)
|
||||
then
|
||||
p := a_import_ctx.location.extended ("nodes").extended (l_node_type.name)
|
||||
create d.make_with_path (p)
|
||||
if d.exists and then d.is_readable then
|
||||
create l_parentable_list.make (0)
|
||||
create l_new_pages.make (0)
|
||||
a_import_ctx.log ("Importing [" + l_node_type.name + "] items ..")
|
||||
across
|
||||
d.entries as ic
|
||||
loop
|
||||
if attached ic.item.extension as ext and then ext.same_string_general ("json") then
|
||||
create f.make_with_path (p.extended_path (ic.item))
|
||||
if f.exists and then f.is_access_readable then
|
||||
f.open_read
|
||||
from
|
||||
create s.make (0)
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_stream (1_024)
|
||||
s.append (f.last_string)
|
||||
end
|
||||
f.close
|
||||
create jp.make_with_string (s)
|
||||
jp.parse_content
|
||||
if jp.is_valid and then attached jp.parsed_json_object as j then
|
||||
if
|
||||
attached json_string_item (j, "type") as l_type and then
|
||||
l_type.same_string_general (l_node_type.name)
|
||||
then
|
||||
if attached json_to_node_page (l_node_type, j, l_node_api) as l_page then
|
||||
if l_page.is_published then
|
||||
if l_page.author = Void then
|
||||
-- FIXME!!!
|
||||
l_page.set_author (l_page_api.cms_api.user)
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" WARNING (Author is unknown!)")
|
||||
end
|
||||
if attached l_page.author as l_author then
|
||||
if
|
||||
attached l_page_api.pages_with_title (l_page.title) as l_pages and then
|
||||
not l_pages.is_empty
|
||||
then
|
||||
-- Page Already exists!
|
||||
-- FIXME/TODO
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (already exists for user #" + l_author.id.out + ")!")
|
||||
else
|
||||
if
|
||||
attached l_page.parent as l_parent and then
|
||||
not l_parent.has_id
|
||||
then
|
||||
l_parentable_list.extend ([l_page, l_parent])
|
||||
l_page.set_parent (Void)
|
||||
end
|
||||
l_page_api.save_page (l_page)
|
||||
l_new_pages.force (l_page, l_node_api.node_path (l_page))
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" imported as "+ l_page.id.out +" for user #" + l_author.id.out + ".")
|
||||
if attached {CMS_LOCAL_LINK} l_page.link as l_link then
|
||||
loc := l_node_api.node_path (l_page)
|
||||
if not l_link.location.starts_with_general ("node/") then
|
||||
l_page_api.cms_api.set_path_alias (loc, l_link.location, False)
|
||||
l_new_pages.force (l_page, l_link.location)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Author is unknown!)")
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Status is Not Published!)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
across
|
||||
l_parentable_list as ic
|
||||
loop
|
||||
if attached ic.item.page as l_page then
|
||||
update_page_parent (l_page, ic.item.parent, l_new_pages)
|
||||
if attached l_page.parent as l_parent then
|
||||
a_import_ctx.log (l_node_type.name + " #" + l_page.id.out + " assigned to parent #" + l_parent.id.out)
|
||||
l_page_api.save_page (l_page)
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " #" + l_page.id.out + " : unable to find parent!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_to_node_page (a_node_type: CMS_PAGE_NODE_TYPE; j: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_PAGE
|
||||
local
|
||||
p: detachable CMS_PAGE
|
||||
do
|
||||
if attached json_to_node (a_node_type, j, a_node_api) as l_node then
|
||||
Result := a_node_type.new_node (l_node)
|
||||
if
|
||||
attached {JSON_OBJECT} j.item ("parent") as j_parent and then
|
||||
attached json_string_item (j_parent, "title") as l_title
|
||||
then
|
||||
p := a_node_type.new_node_with_title (l_title, Void)
|
||||
if
|
||||
attached {JSON_OBJECT} j_parent.item ("link") as j_link and then
|
||||
attached json_to_local_link (j_link) as l_parent_lnk and then
|
||||
not l_parent_lnk.location.starts_with ("node/")
|
||||
then
|
||||
p.set_link (l_parent_lnk)
|
||||
else
|
||||
-- No link ..
|
||||
end
|
||||
if not Result.is_parent_of (p) then
|
||||
Result.set_parent (p)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_page_parent (a_page: CMS_PAGE; a_parent: CMS_PAGE; a_new_pages: STRING_TABLE [CMS_PAGE])
|
||||
require
|
||||
no_parent_set: a_page.parent = Void
|
||||
local
|
||||
lst: detachable LIST [CMS_PAGE]
|
||||
p: detachable CMS_PAGE
|
||||
do
|
||||
p := a_parent
|
||||
if attached page_api as l_page_api then
|
||||
if attached a_parent.link as l_parent_lnk then
|
||||
lst := l_page_api.pages_with_title (a_parent.title)
|
||||
across
|
||||
lst as ic
|
||||
until
|
||||
p.has_id
|
||||
loop
|
||||
if
|
||||
attached ic.item.link as lnk and then
|
||||
lnk.location.same_string_general (l_parent_lnk.location)
|
||||
then
|
||||
p := ic.item
|
||||
end
|
||||
end
|
||||
else
|
||||
lst := l_page_api.pages_with_title (a_parent.title)
|
||||
if lst.count = 1 then
|
||||
p := lst.first
|
||||
end
|
||||
end
|
||||
end
|
||||
if p.has_id then
|
||||
a_page.set_parent (p)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
31
modules/node/submodules/page/cms_page_storage_i.e
Normal file
31
modules/node/submodules/page/cms_page_storage_i.e
Normal file
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "Summary description for {CMS_PAGE_STORAGE_I}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
deferred
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Given the node `a_node', return the list of possible parent nodes id
|
||||
deferred
|
||||
ensure
|
||||
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
|
||||
end
|
||||
|
||||
end
|
||||
31
modules/node/submodules/page/cms_page_storage_null.e
Normal file
31
modules/node/submodules/page/cms_page_storage_null.e
Normal file
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "Summary description for {CMS_PAGE_STORAGE_NULL}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
CMS_NODE_STORAGE_NULL
|
||||
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
|
||||
end
|
||||
86
modules/node/submodules/page/cms_page_storage_sql.e
Normal file
86
modules/node/submodules/page/cms_page_storage_sql.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "Access to the sql database for the page module"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_STORAGE_SQL
|
||||
|
||||
inherit
|
||||
CMS_NODE_STORAGE_SQL
|
||||
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".children")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_children_of_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".available_parents_for_node")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_available_parents_for_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Queries
|
||||
|
||||
sql_select_available_parents_for_node : STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
|
||||
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_select_children_of_node: STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
|
||||
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user