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:
@@ -27,10 +27,6 @@ inherit
|
||||
|
||||
CMS_TAXONOMY_HOOK
|
||||
|
||||
-- CMS_HOOK_EXPORT
|
||||
|
||||
-- CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user