Added support for user, user_roles, page, blog export and import.

Added basic support for comments, for now mainly viewing comments from database (no submission forms yet).
Added first simple wikitext filter (render wikitext content as xhtml).
Ensure response content type is text/html with utf-8 charset.
This commit is contained in:
2017-01-27 11:57:52 +01:00
parent 2d698f604b
commit 7c398a9f33
45 changed files with 2284 additions and 271 deletions

View File

@@ -27,10 +27,6 @@ inherit
CMS_TAXONOMY_HOOK
-- CMS_HOOK_EXPORT
-- CMS_EXPORT_NODE_UTILITIES
create
make

View File

@@ -19,7 +19,7 @@ feature -- Access
local
jo: JSON_OBJECT
ja: JSON_ARRAY
j, jterm: JSON_OBJECT
jterm: JSON_OBJECT
do
create Result.make_empty
Result.put_string (n.content_type, "type")

View File

@@ -351,17 +351,83 @@ feature -- Output
end
a_output.append ("</p>")
end
elseif attached a_node.content as l_content then
a_output.append ("<p class=%"content%">")
if attached cms_api.format (a_node.format) as f then
append_formatted_content_to (l_content, f, a_output)
else
append_formatted_content_to (l_content, cms_api.formats.default_format, a_output)
else
if attached a_node.content as l_content then
a_output.append ("<p class=%"content%">")
if attached cms_api.format (a_node.format) as f then
append_formatted_content_to (l_content, f, a_output)
else
append_formatted_content_to (l_content, cms_api.formats.default_format, a_output)
end
a_output.append ("</p>")
end
a_output.append ("</p>")
append_comments_as_html_to (a_node, a_output, a_response)
end
a_output.append ("</div>")
end
append_comments_as_html_to (a_node: G; a_output: STRING; a_response: detachable CMS_RESPONSE)
do
if attached {CMS_COMMENTS_API} cms_api.module_api ({CMS_COMMENTS_MODULE}) as l_comments_api then
if attached l_comments_api.comments_for (a_node) as l_comments and then not l_comments.is_empty then
a_output.append ("<div class=%"comments-box%"><div class=%"title%">Comments</div><ul class=%"comments%">")
across
l_comments as ic
loop
append_comment_as_html_to (ic.item, a_output, a_response)
end
a_output.append ("</ul></div>")
end
end
end
append_comment_as_html_to (a_comment: CMS_COMMENT; a_output: STRING; a_response: detachable CMS_RESPONSE)
local
l_ago: DATE_TIME_AGO_CONVERTER
do
a_output.append ("<li class=%"comment%">")
if attached a_comment.author as l_author then
a_output.append ("<span class=%"author%">")
a_output.append (cms_api.html_encoded (l_author.name))
a_output.append ("</span>")
elseif attached a_comment.author_name as l_author_name then
a_output.append ("<span class=%"author%">")
a_output.append (cms_api.html_encoded (l_author_name))
a_output.append ("</span>")
end
if attached a_comment.creation_date as dt then
a_output.append (" <span class=%"info%">(")
create l_ago.make
a_output.append (l_ago.smart_date_duration (dt))
a_output.append (" ")
a_output.append (l_ago.short_date (dt))
a_output.append (")</span>")
end
if attached a_comment.content as l_content then
a_output.append ("<div class=%"content%">")
if
attached a_comment.format as l_format and then
attached cms_api.format (l_format) as f
then
append_formatted_content_to (l_content, f, a_output)
else
append_formatted_content_to (l_content, cms_api.formats.default_format, a_output)
end
a_output.append ("</div>")
end
if attached a_comment.items as lst and then not lst.is_empty then
a_output.append ("<ul class=%"comments%">")
across
lst as ic
loop
append_comment_as_html_to (ic.item, a_output, a_response)
end
a_output.append ("</ul>")
end
a_output.append ("</li>")
end
end

View File

@@ -21,6 +21,53 @@ feature -- Access
Result := a_node_api.cms_api.user_api.user_by_name (a_name)
end
feature -- Comments helpers
import_comments_file_for_entity (fn: PATH; a_entity: CMS_CONTENT; api: CMS_API; a_import_ctx: CMS_IMPORT_CONTEXT)
local
s: STRING
jp: JSON_PARSER
f: RAW_FILE
l_comment: CMS_COMMENT
l_log: STRING_8
do
if attached {CMS_COMMENTS_API} api.module_api ({CMS_COMMENTS_MODULE}) as l_comments_api then
create f.make_with_path (fn)
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_value as j_comments then
if attached json_to_comments (j_comments, a_entity, l_comments_api) as l_comments then
across
l_comments as ic
loop
l_comment := ic.item
l_comments_api.save_recursively_comment (l_comment)
l_log := "comment #" + l_comment.id.out + " (count="+ l_comment.count.out +") %"" + f.path.utf_8_name + "%" imported"
l_log.append (" into " + a_entity.content_type)
if attached a_entity.identifier as l_id then
l_log.append (" #" + l_id)
end
l_log.append (" .")
a_import_ctx.log (l_log)
end
end
end
end
end
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
@@ -65,18 +112,36 @@ feature -- Conversion
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
apply_taxonomy_to_node (j: JSON_OBJECT; a_node: CMS_NODE; a_cms_api: CMS_API)
require
a_node.has_id
local
l_term: CMS_TERM
do
if attached {JSON_ARRAY} j.item ("tags") as j_tags and then j_tags.count > 0 then
if
attached {CMS_TAXONOMY_API} a_cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then
attached l_taxonomy_api.vocabularies_for_type (a_node.content_type) as l_voc_coll and then
attached l_voc_coll.item_by_name ("Tags") as l_voc
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_term := t
else
create l_term.make (l_tag_text)
l_taxonomy_api.save_term (l_term, l_voc)
end
if l_term.has_id then
l_taxonomy_api.associate_term_with_content (l_term, a_node)
end
end
end
@@ -103,4 +168,92 @@ feature -- Conversion
end
end
json_to_comments (j: JSON_VALUE; a_entity: CMS_CONTENT; a_comments_api: CMS_COMMENTS_API): detachable LIST [CMS_COMMENT]
do
if attached {JSON_ARRAY} j as j_array then
create {ARRAYED_LIST [CMS_COMMENT]} Result.make (j_array.count)
across
j_array as ic
loop
if
attached {JSON_OBJECT} ic.item as jo and then
attached json_to_comment (jo, a_entity, a_comments_api) as c
then
Result.extend (c)
end
end
end
end
json_to_comment (j: JSON_OBJECT; a_entity: CMS_CONTENT; a_comments_api: CMS_COMMENTS_API): detachable CMS_COMMENT
local
l_title, l_content: detachable READABLE_STRING_32
do
create Result.make
Result.set_entity (a_entity)
if attached {JSON_STRING} j.item ("title") as j_title then
l_title := j_title.unescaped_string_32
if l_title.is_whitespace then
l_title := Void
end
end
if attached {JSON_STRING} j.item ("content") as j_content then
l_content := j_content.unescaped_string_32
if l_content.is_whitespace then
l_content := Void
end
elseif attached {JSON_STRING} j.item ("body") as j_body then
l_content := j_body.unescaped_string_32
if l_content.is_whitespace then
l_content := Void
end
end
if l_content = Void then
if l_title /= Void then
Result.set_content (l_title)
end
elseif l_title = Void then
Result.set_content (l_content)
Result.set_format ("wikitext")
else
if l_content.starts_with (l_title) then
Result.set_content (l_content)
else
Result.set_content (l_title + {STRING_32} "%N%N" + l_content)
end
Result.set_format ("wikitext")
end
if attached {JSON_STRING} j.item ("format") as j_format then
Result.set_format (j_format.unescaped_string_8)
end
if attached {JSON_OBJECT} j.item ("author") as j_author then
if attached {JSON_STRING} j_author.item ("name") as j_author_name then
if attached a_comments_api.cms_api.user_api.user_by_name (j_author_name.unescaped_string_32) as u then
Result.set_author (u)
Result.set_author_name (u.name)
else
-- User unknown!
Result.set_author_name (j_author_name.unescaped_string_32)
end
end
end
if attached json_date_item (j, "date") as dt then
Result.set_modification_date (dt)
Result.set_creation_date (dt)
end
if
attached j.item ("comments") as j_comments and then
attached json_to_comments (j_comments, a_entity, a_comments_api) as lst
then
across
lst as ic
loop
Result.extend (ic.item)
end
end
end
end

View File

@@ -13,6 +13,7 @@
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_comments_module" location="..\..\modules\comments\comments-safe.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/>
<library name="cms_taxonomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>

View File

@@ -7,12 +7,12 @@
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" void_safety="none" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
<option warning="true" void_safety="none">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="cms_comments_module" location="..\..\modules\comments\comments.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes.ecf" readonly="false"/>
<library name="cms_taxonomy_module" location="..\..\modules\taxonomy\taxonomy.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>

View File

@@ -233,14 +233,13 @@ feature -- Hooks
-- Import data identified by `a_import_id_list',
-- or import all data if `a_import_id_list' is Void.
local
p: PATH
l_id: STRING_32
p, fp: 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.
l_entity: detachable CMS_PAGE
do
if
attached node_api as l_node_api and then
@@ -263,65 +262,60 @@ feature -- Hooks
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
l_id := ic.item.name
l_id.remove_tail (ext.count + 1)
fp := p.extended_path (ic.item)
if attached json_object_from_location (fp) 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
l_entity := json_to_node_page (l_node_type, j, l_node_api)
if l_entity /= Void then
if l_entity.is_published then
if l_entity.author = Void then
-- FIXME!!!
l_entity.set_author (l_page_api.cms_api.user)
a_import_ctx.log (l_node_type.name + " %"" + fp.utf_8_name + "%" WARNING (Author is unknown!)")
end
if attached l_entity.author as l_author then
if
attached l_page_api.pages_with_title (l_entity.title) as l_pages and then
not l_pages.is_empty
then
-- Page Already exists!
-- FIXME/TODO
l_entity := l_pages.first
a_import_ctx.log (l_node_type.name + " %"" + fp.utf_8_name + "%" skipped (already exists for user #" + l_author.id.out + ")!")
else
if
attached l_page_api.pages_with_title (l_page.title) as l_pages and then
not l_pages.is_empty
attached l_entity.parent as l_parent and then
not l_parent.has_id
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
l_parentable_list.extend ([l_entity, l_parent])
l_entity.set_parent (Void)
end
l_page_api.save_page (l_entity)
apply_taxonomy_to_node (j, l_entity, l_page_api.cms_api)
l_new_pages.force (l_entity, l_node_api.node_path (l_entity))
a_import_ctx.log (l_node_type.name + " %"" + fp.utf_8_name + "%" imported as "+ l_entity.id.out +" for user #" + l_author.id.out + ".")
if attached {CMS_LOCAL_LINK} l_entity.link as l_link then
loc := l_node_api.node_path (l_entity)
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_entity, l_link.location)
end
end
else
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Author is unknown!)")
end
if l_entity /= Void and then l_entity.has_id then
-- Support for comments
import_comments_file_for_entity (p.extended (l_id).extended ("comments.json"), l_entity, l_node_api.cms_api, a_import_ctx)
end
else
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Status is Not Published!)")
a_import_ctx.log (l_node_type.name + " %"" + fp.utf_8_name + "%" skipped (Author is unknown!)")
end
else
a_import_ctx.log (l_node_type.name + " %"" + fp.utf_8_name + "%" skipped (Status is Not Published!)")
end
end
end
@@ -331,14 +325,13 @@ feature -- Hooks
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
l_entity := ic.item.page
update_page_parent (l_entity, ic.item.parent, l_new_pages)
if attached l_entity.parent as l_parent then
a_import_ctx.log (l_node_type.name + " #" + l_entity.id.out + " assigned to parent #" + l_parent.id.out)
l_page_api.save_page (l_entity)
else
a_import_ctx.log (l_node_type.name + " #" + l_entity.id.out + " : unable to find parent!")
end
end
end