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

@@ -0,0 +1,178 @@
note
description: "[
Comment object.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_COMMENT
inherit
ITERABLE [CMS_COMMENT]
undefine
is_equal
end
COMPARABLE
DEBUG_OUTPUT
undefine
is_equal
end
create
make
feature {NONE} -- Initialization
make
-- Create Current profile.
do
create {ARRAYED_LIST [CMS_COMMENT]} items.make (0)
create modification_date.make_now_utc
creation_date := modification_date
end
feature -- Access
id: INTEGER_64 assign set_id
-- Unique id.
--| Should we use NATURAL_64 instead?
content: detachable READABLE_STRING_32
format: detachable READABLE_STRING_8
author: detachable CMS_USER
author_name: detachable READABLE_STRING_32
-- Author name if no CMS user is associated.
creation_date: DATE_TIME
-- When the comment was created.
modification_date: DATE_TIME
-- When the comment was updated.
status: INTEGER
-- Status of Current comment.
parent: detachable CMS_COMMENT
entity: detachable CMS_CONTENT
-- Associated content.
feature -- Access
has_id: BOOLEAN
do
Result := id > 0
end
new_cursor: ITERATION_CURSOR [CMS_COMMENT]
-- Fresh cursor associated with current structure
do
Result := items.new_cursor
end
feature -- Optional
items: LIST [CMS_COMMENT]
extend (c: CMS_COMMENT)
do
items.extend (c)
end
count: INTEGER
do
Result := items.count
end
feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN
-- Is current object less than `other'?
do
Result := creation_date < other.creation_date
end
feature -- Status report
debug_output: STRING_32
-- <Precursor>
do
create Result.make_empty
Result.append_character ('#')
Result.append_integer_64 (id)
if attached content as l_content then
Result.append_character (' ')
Result.append_character ('%"')
Result.append (l_content.head (25))
if l_content.count > 25 then
Result.append ("...")
end
Result.append_character ('%"')
end
end
feature -- Change
set_id (a_id: like id)
require
a_id_positive: a_id > 0
do
id := a_id
end
set_content (s: like content)
do
content := s
end
set_format (f: like format)
do
format := f
end
set_author (u: detachable CMS_USER)
do
author := u
end
set_author_name (n: like author_name)
do
author_name := n
end
set_creation_date (dt: DATE_TIME)
do
creation_date := dt
end
set_modification_date (dt: DATE_TIME)
do
modification_date := dt
end
set_status (st: like status)
do
status := st
end
set_parent (p: detachable CMS_COMMENT)
do
parent := p
end
set_entity (e: like entity)
do
entity := e
end
;note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,207 @@
note
description: "API to handle user profiles."
date: "$Date$"
revision: "$Revision$"
class
CMS_COMMENTS_API
inherit
CMS_MODULE_API
redefine
initialize
end
REFACTORING_HELPER
create {CMS_COMMENTS_MODULE}
make
feature {NONE} -- Initialization
initialize
-- Create user profile api object with api `a_api' and storage `a_storage'.
do
Precursor
if attached storage.as_sql_storage as l_storage_sql then
create {CMS_COMMENTS_STORAGE_SQL} comments_storage.make (l_storage_sql)
else
create {CMS_COMMENTS_STORAGE_NULL} comments_storage.make
end
end
feature {CMS_MODULE} -- Access nodes storage.
comments_storage: CMS_COMMENTS_STORAGE_I
feature -- Access
comment (a_cid: INTEGER_64): detachable CMS_COMMENT
require
a_cid > 0
do
Result := comments_storage.comment (a_cid)
if Result /= Void then
Result := recursive_full_comment (Result)
end
end
comments_for (a_content: CMS_CONTENT): detachable LIST [CMS_COMMENT]
-- Comments for `a_content`.
require
valid_content: a_content.has_identifier
local
l_comment, l_parent: detachable CMS_COMMENT
tb: HASH_TABLE [CMS_COMMENT, INTEGER_64] -- indexed by "comment id".
do
if
attached comments_storage.comments_for (a_content) as l_flat_lst and then not l_flat_lst.is_empty and then
attached full_comments (l_flat_lst) as lst
then
create {ARRAYED_LIST [CMS_COMMENT]} Result.make (lst.count)
create tb.make (lst.count)
across
lst as ic
loop
l_comment := ic.item
tb.put (l_comment, l_comment.id)
if l_comment.parent = Void then
Result.extend (l_comment)
end
end
across
lst as ic
loop
l_comment := ic.item
l_parent := l_comment.parent
if attached {CMS_PARTIAL_COMMENT} l_parent as l_partial then
l_parent := tb.item (l_partial.id)
l_comment.set_parent (l_parent)
end
if l_parent /= Void then
l_parent.extend (l_comment)
end
end
sort_comments (Result)
end
end
full_comment (a_comment: CMS_COMMENT): CMS_COMMENT
-- If `a_comment' is partial, return the full object from `a_comment',
-- otherwise return directly `a_comment'.
require
a_comment_set: a_comment /= Void
local
l_comment: detachable CMS_COMMENT
do
if attached {CMS_PARTIAL_COMMENT} a_comment as l_partial_comment then
l_comment := comment (l_partial_comment.id)
if l_comment /= Void then
Result := l_comment
else
Result := l_partial_comment
end
else
Result := a_comment
end
-- Update partial user if needed.
if Result /= Void then
if attached {CMS_PARTIAL_USER} Result.author as l_partial_author then
if attached cms_api.user_api.user_by_id (l_partial_author.id) as l_author then
Result.set_author (l_author)
else
check
valid_author_id: False
end
end
end
end
end
full_comments (a_comments: LIST [CMS_COMMENT]): LIST [CMS_COMMENT]
-- Convert list of potentially partial comments into a list of comments when possible.
do
create {ARRAYED_LIST [CMS_COMMENT]} Result.make (a_comments.count)
across
a_comments as ic
loop
Result.force (full_comment (ic.item))
end
end
recursive_full_comment (a_comment: CMS_COMMENT): CMS_COMMENT
local
lst: LIST [CMS_COMMENT]
do
Result := full_comment (a_comment)
from
lst := a_comment.items
lst.start
until
lst.after
loop
lst.replace (recursive_full_comment (lst.item))
lst.forth
end
end
feature -- Change: profile
save_comment (a_comment: CMS_COMMENT)
-- Save `a_comment`.
do
comments_storage.save_comment (a_comment)
end
save_recursively_comment (a_comment: CMS_COMMENT)
do
save_comment (a_comment)
across
a_comment as ic
loop
ic.item.set_parent (a_comment)
save_recursively_comment (ic.item)
end
end
feature -- Helper
has_cycle_in_comments (a_comments: ITERABLE [CMS_COMMENT]; a_known_comments: detachable LIST [CMS_COMMENT]): BOOLEAN
local
lst: detachable LIST [CMS_COMMENT]
do
lst := a_known_comments
if lst = Void then
create {ARRAYED_LIST [CMS_COMMENT]} lst.make (0)
elseif across a_comments as ic some lst.has (ic.item) end then
Result := True
end
if not Result then
across
a_comments as ic
loop
lst.extend (ic.item)
Result := has_cycle_in_comments (ic.item, lst)
end
end
end
sort_comments (a_items: LIST [CMS_COMMENT])
require
no_cycle: not has_cycle_in_comments (a_items, Void)
local
l_sorter: QUICK_SORTER [CMS_COMMENT]
do
create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_COMMENT]})
l_sorter.sort (a_items)
across
a_items as ic
loop
sort_comments (ic.item.items)
end
end
end

View File

@@ -0,0 +1,96 @@
note
description: "[
Comments module.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_COMMENTS_MODULE
inherit
CMS_MODULE
rename
module_api as comments_api
redefine
setup_hooks,
initialize,
install,
comments_api
end
CMS_HOOK_RESPONSE_ALTER
create
make
feature {NONE} -- Initialization
make
do
version := "1.0"
description := "Comments"
package := "content"
end
feature -- Access
name: STRING = "comments"
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
create comments_api.make (api)
end
feature {CMS_API} -- Module management
install (a_api: CMS_API)
do
-- Schema
if attached a_api.storage.as_sql_storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
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
comments_api: detachable CMS_COMMENTS_API
-- <Precursor>
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_response_alter_hook (Current)
-- a_hooks.subscribe_to_form_alter_hook (Current)
end
-- form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_response: CMS_RESPONSE)
-- -- Hook execution on form `a_form' and its associated data `a_form_data',
-- -- for related response `a_response'.
-- do
-- end
response_alter (a_response: CMS_RESPONSE)
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/comments.css", Void), Void)
end
end

View File

@@ -0,0 +1,25 @@
note
description: "Summary description for {CMS_PARTIAL_COMMENT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_PARTIAL_COMMENT
inherit
CMS_COMMENT
create
make_with_id
feature {NONE} -- Initialization
make_with_id (a_cid: like id)
do
make
id := a_cid
end
end

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="comments_module" uuid="C1D39ECD-6993-456B-AD14-0CF94504340F" library_target="comments_module">
<target name="comments_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true">
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="comments_module" uuid="C1D39ECD-6993-456B-AD14-0CF94504340F" library_target="comments_module">
<target name="comments_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="none">
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="cms" location="..\..\cms.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,38 @@
note
description: "Interface for accessing comments from database."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_COMMENTS_STORAGE_I
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access
comment (a_cid: INTEGER_64): detachable CMS_COMMENT
require
a_cid > 0
deferred
end
comments_for (a_content: CMS_CONTENT): detachable LIST [CMS_COMMENT]
-- Comments for `a_content`.
require
has_id: a_content.has_identifier
deferred
end
feature -- Change
save_comment (a_comment: CMS_COMMENT)
-- Save `a_comment`.
deferred
end
end

View File

@@ -0,0 +1,46 @@
note
description: "Summary description for {CMS_COMMENTS_STORAGE_NULL}."
date: "$Date$"
revision: "$Revision$"
class
CMS_COMMENTS_STORAGE_NULL
inherit
CMS_COMMENTS_STORAGE_I
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
create error_handler.make
end
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
feature -- Access
comment (a_cid: INTEGER_64): detachable CMS_COMMENT
do
end
comments_for (a_content: CMS_CONTENT): detachable LIST [CMS_COMMENT]
-- Comments for `a_content`.
do
end
feature -- Change
save_comment (a_comment: CMS_COMMENT)
-- Save `a_comment`.
do
end
end

View File

@@ -0,0 +1,187 @@
note
description: "Interface for accessing user profile contents from SQL database."
date: "$Date: 2015-05-21 14:46:00 +0100$"
revision: "$Revision: 96616 $"
class
CMS_COMMENTS_STORAGE_SQL
inherit
CMS_COMMENTS_STORAGE_I
CMS_PROXY_STORAGE_SQL
create
make
feature -- Access
comment (a_cid: INTEGER_64): detachable CMS_COMMENT
-- Comment with id `a_cid`.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_cid, "cid")
sql_query (sql_select_comment_by_id, l_parameters)
sql_start
if not has_error and not sql_after then
Result := fetch_comment
end
sql_finalize
end
comments_for (a_content: CMS_CONTENT): detachable LIST [CMS_COMMENT]
-- Comments for content `a_content`.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_COMMENT]} Result.make (0)
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_content.identifier, "content_id")
from
sql_query (sql_select_comments_for_content, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_comment as obj then
Result.force (obj)
end
sql_forth
end
sql_finalize
end
feature -- Change
save_comment (a_comment: CMS_COMMENT)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (11)
l_parameters.put (a_comment.content, "content")
l_parameters.put (a_comment.format, "format")
if attached a_comment.author as u then
l_parameters.put (u.id, "author")
l_parameters.put (u.name, "author_name")
else
l_parameters.put (0, "author")
if attached a_comment.author_name as l_author_name then
l_parameters.put (l_author_name, "author_name")
else
l_parameters.put ("", "author_name")
end
end
l_parameters.put (a_comment.creation_date, "created")
l_parameters.put (a_comment.modification_date, "changed")
l_parameters.put (a_comment.status, "status")
if attached a_comment.parent as p then
l_parameters.put (p.id, "parent")
end
if attached a_comment.entity as l_entity then
l_parameters.put (l_entity.identifier, "entity")
l_parameters.put (l_entity.content_type, "entity_type")
else
l_parameters.put ("", "entity")
l_parameters.put ("", "entity_type")
end
sql_begin_transaction
if a_comment.has_id then
l_parameters.put (a_comment.id, "cid")
sql_modify (sql_update_comment, l_parameters)
else
sql_insert (sql_insert_comment, l_parameters)
if not has_error then
a_comment.set_id (last_inserted_comment_id)
end
end
if has_error then
sql_rollback_transaction
else
sql_commit_transaction
end
sql_finalize
end
feature {NONE} -- Implementation
fetch_comment: detachable CMS_COMMENT
local
cid, uid, pid, l_entity_id: INTEGER_64
l_entity: detachable CMS_PARTIAL_CONTENT
do
cid := sql_read_integer_64 (1)
if cid > 0 then
create Result.make
Result.set_id (cid)
Result.set_content (sql_read_string_32 (2))
Result.set_format (sql_read_string (3))
uid := sql_read_integer_64 (4)
if uid > 0 then
Result.set_author (create {CMS_PARTIAL_USER}.make_with_id (uid))
end
Result.set_author_name (sql_read_string_32 (5))
if attached sql_read_date_time (6) as dt then
Result.set_creation_date (dt)
end
if attached sql_read_date_time (7) as dt then
Result.set_modification_date (dt)
end
Result.set_status (sql_read_integer_32 (8))
pid := sql_read_integer_64 (9)
if pid > 0 then
Result.set_parent (create {CMS_PARTIAL_COMMENT}.make_with_id (pid))
end
l_entity_id := sql_read_integer_64 (10)
if
attached sql_read_string (11) as l_entity_type and
l_entity_id > 0
then
create l_entity.make_empty (l_entity_type)
l_entity.set_identifier (l_entity_id.out)
Result.set_entity (l_entity)
end
-- l_entity_type := sql_read_string_32 (11)
-- if l_entity_type /= Void then
-- Result.set_en
-- end
end
end
last_inserted_comment_id: INTEGER_64
-- Last insert comment id.
do
error_handler.reset
sql_query (Sql_last_inserted_comment_id, Void)
if not has_error and not sql_after then
Result := sql_read_integer_64 (1)
end
sql_finalize
end
feature {NONE} -- Queries
Sql_last_inserted_comment_id: STRING = "SELECT MAX(cid) FROM comments;"
sql_select_comment_by_id: STRING = "SELECT cid,content,format,author,author_name,created,changed,status,parent,entity,entity_type FROM comments WHERE cid=:cid;"
sql_select_comments_for_content: STRING = "SELECT cid,content,format,author,author_name,created,changed,status,parent,entity,entity_type FROM comments WHERE entity=:content_id;"
sql_insert_comment: STRING = "INSERT INTO comments (content,format,author,author_name,created,changed,status,parent,entity,entity_type) VALUES (:content,:format,:author,:author_name,:created,:changed,:status,:parent,:entity,:entity_type);"
sql_update_comment: STRING = "UPDATE comments SET content=:content,format=:format,author=:author,author_name=:author_name,created=:created,changed=:changed,status=:status,parent=:parent,entity=:entity,entity_type=:entity_type WHERE cid=:cid ;"
end

View File

@@ -0,0 +1,18 @@
div.comments-box div.title {
font-size: x-large;
}
div.comments-box ul.comments {
border-top: solid 1px #eee;
padding: 0 0 0 20px;
border-left: solid 5px #eee;
list-style-type: none;
}
div.comments-box ul.comments li.comment {
border-bottom: solid 1px #eee;
}
div.comments-box ul.comments li.comment > span.author {
font-weight: bold;
}
div.comments-box ul.comments li.comment > span.info {
font-style: italic;
}

View File

@@ -0,0 +1,17 @@
div.comments-box {
div.title {
font-size: x-large;
}
ul.comments {
border-top: solid 1px #eee;
padding: 0 0 0 20px;
border-left: solid 5px #eee;
list-style-type: none;
li.comment {
border-bottom: solid 1px #eee;
&>span.author { font-weight: bold; }
&>span.info { font-style: italic; }
}
}
}

View File

@@ -0,0 +1,13 @@
CREATE TABLE comments(
`cid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
`content` TEXT,
`format` VARCHAR(128),
`author` INTEGER,
`author_name` VARCHAR(255),
`created` DATETIME NOT NULL,
`changed` DATETIME NOT NULL,
`status` INTEGER,
`parent` INTEGER,
`entity` VARCHAR(255), /* Associated entity */
`entity_type` VARCHAR(255) /* Type of associated entity */
);