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:
2017-01-20 16:05:40 +01:00
parent 3bcfb0a44a
commit 2d698f604b
59 changed files with 1761 additions and 679 deletions

View File

@@ -9,8 +9,7 @@ class
inherit
CMS_NODE
redefine
make_empty,
import_node
make_empty
end
create
@@ -24,23 +23,6 @@ feature {NONE} -- Initialization
Precursor
end
feature -- Conversion
import_node (a_node: CMS_NODE)
-- <Precursor>
do
Precursor (a_node)
if attached {CMS_BLOG} a_node as l_blog then
if attached l_blog.tags as l_tags then
across
l_tags as ic
loop
add_tag (ic.item)
end
end
end
end
feature -- Access
content_type: READABLE_STRING_8
@@ -60,11 +42,6 @@ feature -- Access: node
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
feature -- Access: blog
tags: detachable ARRAYED_LIST [READABLE_STRING_32]
-- Optional tags
feature -- Element change: node
set_content (a_content: like content; a_summary: like summary; a_format: like format)
@@ -74,39 +51,5 @@ feature -- Element change: node
format := a_format
end
feature -- Element change: blog
add_tag (a_tag: READABLE_STRING_32)
-- Set `parent' to `a_page'
require
not a_tag.is_whitespace
local
l_tags: like tags
do
l_tags := tags
if l_tags = Void then
create l_tags.make (1)
tags := l_tags
end
l_tags.force (a_tag)
end
set_tags_from_string (a_tags: READABLE_STRING_32)
local
t: STRING_32
do
tags := Void
across
a_tags.split (',') as ic
loop
t := ic.item
t.left_adjust
t.right_adjust
if not t.is_whitespace then
add_tag (t)
end
end
end
end

View File

@@ -1,6 +1,5 @@
note
description: "API to handle nodes of type blog. Extends the node API."
author: "Dario B<>sch <daboesch@student.ethz.ch"
date: "$Date: 2015-05-21 14:46:00 +0100$"
revision: "$Revision: 96616 $"
@@ -41,6 +40,11 @@ feature {NONE} -- Initialization
else
create {CMS_BLOG_STORAGE_NULL} blog_storage.make
end
--| For test reasons this is 2, so we don't have to create a lot of blog entries.
--| TODO: Set to bigger constant.
entries_per_page := 2
-- initialize_node_types
end
@@ -54,10 +58,8 @@ feature {CMS_MODULE} -- Access nodes storage.
feature -- Configuration of blog handlers
entries_per_page : NATURAL_32 = 2
entries_per_page : NATURAL_32
-- The numbers of posts that are shown on one page. If there are more post a pagination is generated
--| For test reasons this is 2, so we don't have to create a lot of blog entries.
--| TODO: Set to bigger constant.
feature -- Access node
@@ -88,7 +90,7 @@ feature -- Access node
Result := nodes_to_blogs (blog_storage.blogs_limited (a_limit, a_offset))
end
blogs_from_user_order_created_desc_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32) : LIST [CMS_BLOG]
blogs_from_user_order_created_desc_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_BLOG]
-- List of nodes ordered by creation date and limited by limit and offset
require
has_id: a_user.has_id
@@ -97,6 +99,15 @@ feature -- Access node
Result := nodes_to_blogs (blog_storage.blogs_from_user_limited (a_user, a_limit, a_offset))
end
blogs_by_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_BLOG]
-- List of blogs with title `a_title` for user `a_user` and ordered by creation date .
require
has_id: a_user.has_id
do
-- load all posts and add the authors to each post
Result := nodes_to_blogs (blog_storage.blogs_from_user_with_title (a_user, a_title))
end
feature -- Conversion
full_blog_node (a_blog: CMS_BLOG): CMS_BLOG
@@ -112,6 +123,13 @@ feature -- Conversion
end
end
feature -- Commit
save_blog (a_blog: CMS_BLOG)
do
node_api.save_node (a_blog)
end
feature {NONE} -- Helpers
nodes_to_blogs (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_BLOG]

View File

@@ -1,6 +1,5 @@
note
description: "Displays all posts (pages with type blog). It's possible to list posts by user."
author: "Dario B<>sch <daboesch@student.ethz.ch>"
date: "$Date: 2015-05-22 15:13:00 +0100 (lun., 18 mai 2015) $"
revision: "$Revision 96616$"
@@ -24,8 +23,12 @@ inherit
CMS_HOOK_EXPORT
CMS_HOOK_IMPORT
CMS_EXPORT_NODE_UTILITIES
CMS_IMPORT_NODE_UTILITIES
create
make
@@ -34,8 +37,8 @@ feature {NONE} -- Initialization
make
do
version := "1.0"
description := "Service to demonstrate new node for blog"
package := "demo"
description := "Blogging service"
package := "content"
add_dependency ({CMS_NODE_MODULE})
end
@@ -51,14 +54,12 @@ feature {CMS_API} -- Module Initialization
ct: CMS_BLOG_NODE_TYPE
do
Precursor (api)
if attached {CMS_NODE_API} api.module_api ({CMS_NODE_MODULE}) as l_node_api then
create ct
create blog_api.make (api, l_node_api)
node_api := l_node_api
-- Depends on {CMS_NODE_MODULE}
--| For now, add all available formats to content type `ct'.
across
api.formats as ic
@@ -67,12 +68,6 @@ feature {CMS_API} -- Module Initialization
end
l_node_api.add_node_type (ct)
l_node_api.add_node_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct, l_node_api))
-- Add support for CMS_BLOG, which requires a storage extension to store the optional "tags" value
-- 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_BLOG_EXTENSION}.make (l_node_api, l_sql_node_storage))
end
end
end
@@ -148,6 +143,7 @@ feature -- Hooks
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_export_hook (Current)
a_hooks.subscribe_to_import_hook (Current)
end
response_alter (a_response: CMS_RESPONSE)
@@ -164,7 +160,7 @@ feature -- Hooks
a_menu_system.primary_menu.extend (lnk)
end
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
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
@@ -175,21 +171,28 @@ feature -- Hooks
lst: LIST [CMS_BLOG]
do
if
a_export_id_list = Void
or else across a_export_id_list as ic some ic.item.same_string ("blog") end
attached node_api as l_node_api and then
attached l_node_api.node_type ("blog") 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 blog">>) and then
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
attached blog_api as l_blog_api
then
lst := l_blog_api.blogs_order_created_desc
a_export_parameters.log ("Exporting " + lst.count.out + " blogs")
a_export_ctx.log ("Exporting " + lst.count.out + " [" + l_node_type.name + "] items...")
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
n := l_blog_api.full_blog_node (ic.item)
a_export_parameters.log (n.content_type + " #" + n.id.out)
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out).appended_with_extension ("json")
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
@@ -197,16 +200,16 @@ feature -- Hooks
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 (blog_node_to_json (n)))
f.put_string (json_to_string (blog_node_to_json (n, l_blog_api)))
f.close
end
-- Revisions.
if
attached node_api as l_node_api and then
attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1
attached l_node_api.node_revisions (n) as l_revisions and then
l_revisions.count > 1
then
a_export_parameters.log (n.content_type + " " + l_revisions.count.out + " revisions.")
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
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
@@ -218,7 +221,7 @@ feature -- Hooks
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 (blog_node_to_json (l_blog)))
f.put_string (json_to_string (blog_node_to_json (l_blog, l_blog_api)))
end
f.close
end
@@ -229,8 +232,102 @@ feature -- Hooks
end
end
blog_node_to_json (a_blog: CMS_BLOG): JSON_OBJECT
blog_node_to_json (a_blog: CMS_BLOG; a_blog_api: CMS_BLOG_API): JSON_OBJECT
do
Result := node_to_json (a_blog)
Result := node_to_json (a_blog, a_blog_api.node_api)
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: STRING
do
if
attached node_api as l_node_api and then
attached {CMS_BLOG_NODE_TYPE} l_node_api.node_type ("blog") as l_node_type and then
( a_import_id_list = Void
or else across a_import_id_list as ic some ic.item.same_string ({CMS_BLOG_NODE_TYPE}.name) end
)
then
if
a_response.has_permissions (<<"import any node", "import " + l_node_type.name>>) and then
attached blog_api as l_blog_api
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
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_blog (l_node_type, j, l_node_api) as l_blog then
if l_blog.is_published then
if attached l_blog.author as l_author then
if
attached l_blog_api.blogs_by_title (l_author, l_blog.title) as l_blogs and then
not l_blogs.is_empty
then
-- Blog 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
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" imported for user #" + l_author.id.out + ".")
l_blog_api.save_blog (l_blog)
if attached {CMS_LOCAL_LINK} l_blog.link as l_link then
loc := l_node_api.node_path (l_blog)
if not l_link.location.starts_with_general ("node/") then
l_blog_api.cms_api.set_path_alias (loc, l_link.location, False)
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
end
end
end
end
json_to_node_blog (a_node_type: CMS_BLOG_NODE_TYPE; j: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_BLOG
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)
end
end
end

View File

@@ -9,11 +9,7 @@ class
inherit
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_BLOG]
redefine
content_type,
populate_form,
update_node,
new_node,
append_content_as_html_to
content_type
end
create
@@ -24,76 +20,4 @@ feature -- Access
content_type: CMS_BLOG_NODE_TYPE
-- Associated content type.
feature -- form
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: WSF_FORM_TEXT_INPUT
s: STRING_32
do
Precursor (response, f, a_node)
create ti.make ("tags")
ti.set_label ("Tags")
ti.set_size (70)
if
a_node /= Void and then
attached {CMS_BLOG} a_node as a_blog and then
attached a_blog.tags as l_tags
then
create s.make_empty
across
l_tags as ic
loop
if not s.is_empty then
s.append_character (',')
end
s.append (ic.item)
end
ti.set_text_value (s)
end
ti.set_is_required (False)
f.extend (ti)
end
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
do
Precursor (response, fd, a_node)
if attached fd.string_item ("tags") as l_tags then
if attached {CMS_BLOG} a_node as l_blog then
l_blog.set_tags_from_string (l_tags)
end
end
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): like content_type.new_node
-- <Precursor>
do
Result := Precursor (response, fd, a_node)
if attached fd.string_item ("tags") as l_tags then
Result.set_tags_from_string (l_tags)
end
end
feature -- Output
append_content_as_html_to (a_node: CMS_BLOG; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
-- <Precursor>
do
Precursor (a_node, is_teaser, a_output, a_response)
if attached {CMS_BLOG} a_node as l_blog_post then
if attached l_blog_post.tags as l_tags then
a_output.append ("<div><strong>Tags:</strong> ")
across
l_tags as ic
loop
a_output.append ("<span class=%"tag%">")
a_output.append (cms_api.html_encoded (ic.item))
a_output.append ("</span> ")
end
a_output.append ("</div>")
end
end
end
end

View File

@@ -1,167 +0,0 @@
note
description: "Storage extension for Blog nodes."
date: "$Date$"
revision: "$Revision$"
class
CMS_NODE_STORAGE_SQL_BLOG_EXTENSION
inherit
CMS_NODE_STORAGE_EXTENSION [CMS_BLOG]
CMS_PROXY_STORAGE_SQL
rename
make as make_proxy,
sql_storage as node_storage
redefine
node_storage
end
create
make
feature {NONE} -- Initialization
make (a_node_api: CMS_NODE_API; a_sql_storage: CMS_NODE_STORAGE_SQL)
do
set_node_api (a_node_api)
make_proxy (a_sql_storage)
end
node_storage: CMS_NODE_STORAGE_SQL
-- <Precursor>
feature -- Access
content_type: STRING
once
Result := {CMS_BLOG_NODE_TYPE}.name
end
feature -- Persistence
store (a_node: CMS_BLOG)
-- <Precursor>.
local
l_parameters: STRING_TABLE [detachable ANY]
l_new_tags: detachable STRING_32
l_previous_tags: detachable STRING_32
l_update: BOOLEAN
l_has_modif: BOOLEAN
do
if attached api as l_api then
l_api.logger.put_information (generator + ".store", Void)
end
error_handler.reset
-- Check existing record, if any.
if attached node_data (a_node) as d then
l_update := d.revision = a_node.revision
l_previous_tags := d.tags
end
if not has_error then
if attached a_node.tags as l_tags and then not l_tags.is_empty then
create l_new_tags.make (0)
across
l_tags as ic
loop
if not l_new_tags.is_empty then
l_new_tags.append_character (',')
end
l_new_tags.append (ic.item)
end
else
l_new_tags := Void
end
l_has_modif := l_has_modif or (l_new_tags /~ l_previous_tags)
create l_parameters.make (3)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
l_parameters.force (l_new_tags, "tags")
if l_update then
if l_has_modif then
sql_modify (sql_update_node_data, l_parameters)
end
else
if l_has_modif then
sql_insert (sql_insert_node_data, l_parameters)
else
-- no page data, means everything is empty.
-- FOR NOW: always record row
-- sql_change (sql_insert_node_data, l_parameters)
end
end
sql_finalize
end
end
load (a_node: CMS_BLOG)
-- <Precursor>.
local
l_tags: READABLE_STRING_32
do
if attached node_data (a_node) as d then
l_tags := d.tags
a_node.set_tags_from_string (l_tags)
end
end
delete_node (a_node: CMS_BLOG)
-- <Precursor>
local
l_parameters: STRING_TABLE [ANY]
do
if a_node.has_id then
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_modify (sql_delete_node_data, l_parameters)
sql_finalize
end
end
feature {NONE} -- Implementation
node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; tags: READABLE_STRING_32]
-- Node extension data for node `a_node' as tuple.
local
l_parameters: STRING_TABLE [ANY]
l_rev: INTEGER_64
l_tags: detachable READABLE_STRING_32
n: INTEGER
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_node_data, l_parameters)
if not has_error then
if not sql_after then
-- nid, revision, tags
l_rev := sql_read_integer_64 (2)
l_tags := sql_read_string_32 (3)
if l_tags /= Void then
Result := [l_rev, l_tags]
end
sql_forth
if not sql_after then
check unique_data: n = 0 end
Result := Void
end
end
end
sql_finalize
ensure
accepted_revision: Result /= Void implies Result.revision <= a_node.revision
end
feature -- SQL
sql_select_node_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid=:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;"
sql_insert_node_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
sql_update_node_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
sql_delete_node_data: STRING = "DELETE FROM blog_post_nodes WHERE nid=:nid;"
end

View File

@@ -44,4 +44,11 @@ feature -- Access
deferred
end
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
-- List of blogs from `a_user' with title `a_title` and ordered by creation date.
require
has_id: a_user.has_id
deferred
end
end

View File

@@ -44,4 +44,10 @@ feature -- Access
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
-- List of blogs from `a_user' with title `a_title` and ordered by creation date.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
end

View File

@@ -105,7 +105,7 @@ feature -- Access
write_information_log (generator + ".blogs_from_user_limited")
from
create l_parameters.make (2)
create l_parameters.make (3)
l_parameters.put (a_limit, "limit")
l_parameters.put (a_offset, "offset")
l_parameters.put (a_user.id, "user")
@@ -122,6 +122,33 @@ feature -- Access
sql_finalize
end
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): 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 + ".blogs_from_user_with_title")
from
create l_parameters.make (2)
l_parameters.put (a_user.id, "user")
l_parameters.put (a_title, "title")
sql_query (sql_blogs_from_user_with_title, 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_blog_count: STRING = "SELECT count(*) FROM nodes WHERE status != -1 AND type = %"blog%";"
@@ -136,10 +163,12 @@ feature {NONE} -- Queries
-- SQL Query to retrieve all nodes that are from the type "blog" ordered by descending creation date.
sql_blogs_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC LIMIT :limit OFFSET :offset ;"
--- SQL Query to retrieve all node of type "blog" limited by limit and starting at offset
--- SQL Query to retrieve all nodes of type "blog" limited by limit and starting at offset
sql_blogs_from_user_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ORDER BY created DESC LIMIT :limit OFFSET :offset ;"
--- SQL Query to retrieve all node of type "blog" from author with id limited by limit + offset
--- SQL Query to retrieve all nodes of type "blog" from author with id limited by limit + offset
sql_blogs_from_user_with_title: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user AND title = :title ORDER BY created DESC;"
--- SQL Query to retrieve all nodes of type "blog" from author with title .
end

View File

@@ -1,6 +0,0 @@
CREATE TABLE blog_post_nodes(
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);