Merge branch 'blog_ewf_v1' into ewf_v1

This commit is contained in:
2015-06-16 21:07:40 +02:00
27 changed files with 1305 additions and 63 deletions

4
.gitignore vendored
View File

@@ -2,4 +2,6 @@ EIFGENs
*.swp
*.log*
*.rc
*.bak
*.bak
*.sqlite
Thumbs.db

View File

@@ -31,7 +31,13 @@ feature -- Conversion
do
Precursor (a_node)
if attached {CMS_BLOG} a_node as l_blog then
-- l_blog
if attached l_blog.tags as l_tags then
across
l_tags as ic
loop
add_tag (ic.item)
end
end
end
end
@@ -42,7 +48,7 @@ feature -- Access
Result := {CMS_BLOG_NODE_TYPE}.name
end
feature -- Access: content
feature -- Access: node
summary: detachable READABLE_STRING_8
-- A short summary of the node.
@@ -54,10 +60,12 @@ feature -- Access: content
-- 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
feature -- Element change: node
set_content (a_content: like content; a_summary: like summary; a_format: like format)
do
@@ -66,6 +74,8 @@ feature -- Element change
format := a_format
end
feature -- Element change: blog
add_tag (a_tag: READABLE_STRING_32)
-- Set `parent' to `a_page'
require

View File

@@ -0,0 +1,118 @@
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 $"
class
CMS_BLOG_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
-- Create the node storage for type blog
if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then
create {CMS_BLOG_STORAGE_SQL} blog_storage.make (l_storage_sql)
else
create {CMS_BLOG_STORAGE_NULL} blog_storage.make
end
-- initialize_node_types
end
feature {CMS_API_ACCESS, CMS_MODULE, CMS_API} -- Restricted access
node_api: CMS_NODE_API
feature {CMS_MODULE} -- Access nodes storage.
blog_storage: CMS_BLOG_STORAGE_I
feature -- Configuration of blog handlers
entries_per_page : NATURAL_32 = 2
-- 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
blogs_count: INTEGER_64
-- Number of nodes of type blog.
do
Result := blog_storage.blogs_count
end
blogs_count_from_user (a_user: CMS_USER): INTEGER_64
-- Number of nodes of type blog from user with `a_user_id'.
require
has_id: a_user.has_id
do
Result := blog_storage.blogs_count_from_user (a_user)
end
blogs_order_created_desc: LIST [CMS_BLOG]
-- List of nodes ordered by creation date (descending)
do
Result := nodes_to_blogs (blog_storage.blogs)
end
blogs_order_created_desc_limited (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_BLOG]
-- List of nodes ordered by creation date and limited by limit and offset
do
-- load all posts and add the authors to each post
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]
-- List of nodes ordered by creation date and limited by limit and offset
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_limited (a_user, a_limit, a_offset))
end
feature {NONE} -- Helpers
nodes_to_blogs (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_BLOG]
-- Convert list of nodes into a list of blog when possible.
do
create {ARRAYED_LIST [CMS_BLOG]} Result.make (a_nodes.count)
if attached node_api as l_node_api then
across
a_nodes as ic
loop
if attached {CMS_BLOG} l_node_api.full_node (ic.item) as l_blog then
Result.force (l_blog)
end
end
end
end
end

View File

@@ -20,6 +20,7 @@
<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"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>

View File

@@ -1,17 +1,21 @@
note
description: "Summary description for {CMS_BLOG_MODULE}."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
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$"
class
CMS_BLOG_MODULE
inherit
CMS_MODULE
rename
module_api as blog_api
redefine
register_hooks,
initialize,
install
install,
blog_api
end
CMS_HOOK_MENU_SYSTEM_ALTER
@@ -39,6 +43,10 @@ feature {CMS_API} -- Module Initialization
Precursor (api)
if attached {CMS_NODE_API} api.module_api ({NODE_MODULE}) as l_node_api then
create blog_api.make (api, l_node_api)
node_api := l_node_api
-- Depends on {NODE_MODULE}
create ct
l_node_api.add_content_type (ct)
l_node_api.add_content_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct))
@@ -75,12 +83,53 @@ CREATE TABLE "blog_post_nodes"(
end
end
feature {CMS_API} -- Access: API
blog_api: detachable CMS_BLOG_API
-- <Precursor>
node_api: detachable CMS_NODE_API
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
a_router.handle_with_request_methods ("/blogs/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_blogs (?,?, a_api)), a_router.methods_get)
if attached blog_api as l_blog_api then
configure_web (a_api, l_blog_api, a_router)
else
-- Issue with api/dependencies,
-- thus Current module should not be used!
-- thus no url mapping
end
end
configure_web (a_api: CMS_API; a_blog_api: CMS_BLOG_API; a_router: WSF_ROUTER)
-- Configure router mapping for web interface.
local
l_blog_handler: BLOG_HANDLER
l_blog_user_handler: BLOG_USER_HANDLER
l_uri_mapping: WSF_URI_MAPPING
do
-- TODO: for now, focused only on web interface, add REST api later. [2015-May-18]
create l_blog_handler.make (a_api, a_blog_api)
create l_blog_user_handler.make (a_api, a_blog_api)
-- Let the class BLOG_HANDLER handle the requests on "/blogs"
create l_uri_mapping.make_trailing_slash_ignored ("/blogs", l_blog_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
-- We can add a page number after /blogs/ to get older posts
a_router.handle_with_request_methods ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
-- If a user id is given route with blog user handler
--| FIXME: maybe /user/{user}/blogs/ would be better.
a_router.handle_with_request_methods ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
-- If a user id is given we also want to allow different pages
--| FIXME: what about /user/{user}/blogs/?page={page} ?
a_router.handle_with_request_methods ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
end
feature -- Hooks
@@ -94,19 +143,8 @@ feature -- Hooks
local
lnk: CMS_LOCAL_LINK
do
-- Add the link to the blog to the main menu
create lnk.make ("Blogs", "blogs/")
a_menu_system.primary_menu.extend (lnk)
end
feature -- Handler
handle_blogs (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: CMS_API)
local
r: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
do
create r.make (req, res, a_api)
r.set_main_content ("Blog module is in development ...")
r.execute
end
end

View File

@@ -17,10 +17,11 @@ feature {NONE} -- Initialization
default_create
do
Precursor
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (3)
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (4)
available_formats.extend (create {PLAIN_TEXT_CONTENT_FORMAT})
available_formats.extend (create {FILTERED_HTML_CONTENT_FORMAT})
available_formats.extend (create {FULL_HTML_CONTENT_FORMAT})
available_formats.extend (create {CMS_EDITOR_CONTENT_FORMAT})
end
feature -- Access

View File

@@ -0,0 +1,279 @@
note
description: "Request handler related to /blogs and /blogs/{page}. Displays all posts in the blog."
author: "Dario B<>sch <daboesch@student.ethz.ch>"
date: "$Date: 2015-05-18 13:49:00 +0100 (lun., 18 mai 2015) $"
revision: "$9661667$"
class
BLOG_HANDLER
inherit
CMS_BLOG_HANDLER
WSF_URI_HANDLER
rename
execute as uri_execute,
new_mapping as new_uri_mapping
end
WSF_URI_TEMPLATE_HANDLER
rename
execute as uri_template_execute,
new_mapping as new_uri_template_mapping
select
new_uri_template_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get
end
REFACTORING_HELPER
CMS_API_ACCESS
create
make
feature -- execute
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for any kind of mapping.
do
execute_methods (req, res)
end
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for URI mapping.
do
execute (req, res)
end
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for URI-template mapping.
do
execute (req, res)
end
feature -- Global Variables
page_number: NATURAL_32
-- Current page number.
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_page: CMS_RESPONSE
do
-- Read page number from path parameter.
page_number := page_number_path_parameter (req)
-- Responding with `main_content_html (l_page)'.
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.set_main_content (main_content_html (l_page))
l_page.execute
end
feature -- Query
posts: LIST [CMS_NODE]
-- Blog posts to display on given page ordered by date (descending).
do
Result := blog_api.blogs_order_created_desc_limited (
entries_per_page,
entries_per_page * (page_number - 1)
)
end
multiple_pages_needed : BOOLEAN
-- Return if more that one page is needed to display posts.
do
Result := entries_per_page < total_entries
end
pages_count: NATURAL_32
-- Number of pages needed to display all posts.
require
entries_per_page > 0
local
tmp: REAL_32
do
tmp := total_entries.to_real_32 / entries_per_page.to_real_32;
Result := tmp.ceiling.to_natural_32
end
page_number_path_parameter (req: WSF_REQUEST): NATURAL_32
-- Page number from path /blogs/{page}.
-- Unsigned integer since negative pages are not allowed.
local
s: STRING
do
Result := 1 -- default if not get variable is set
if attached {WSF_STRING} req.path_parameter ("page") as p_page then
s := p_page.value
if s.is_natural_32 then
if s.to_natural_32 > 1 then
Result := s.to_natural_32
end
end
end
end
total_entries: NATURAL_32
-- Total number of entries/posts.
do
Result := blog_api.blogs_count.to_natural_32
end
feature -- HTML Output
frozen main_content_html (page: CMS_RESPONSE): STRING
-- Content of the page as a html string.
do
create Result.make_empty
append_main_content_html_to (page, Result)
end
append_main_content_html_to (page: CMS_RESPONSE; a_output: STRING)
-- Append to `a_output, the content of the page as a html string.
local
n: CMS_NODE
lnk: CMS_LOCAL_LINK
do
-- Output the title. If more than one page, also output the current page number
append_page_title_html_to (a_output)
-- Get the posts from the current page (given by page number and entries per page)
-- Start list of posts
a_output.append ("<ul class=%"cms_blog_nodes%">%N")
across
posts as ic
loop
n := ic.item
lnk := blog_api.node_api.node_link (n)
a_output.append ("<li class=%"cms_type_"+ n.content_type +"%">")
-- Output the creation date
append_creation_date_html_to (n, a_output)
-- Output the author of the post
append_author_html_to (n, a_output)
-- Output the title of the post as a link (to the detail page)
append_title_html_to (n, page, a_output)
-- Output the summary of the post and a more link to the detail page
append_summary_html_to (n, page, a_output)
a_output.append ("</li>%N")
end
-- End of post list
a_output.append ("</ul>%N")
-- Pagination (older and newer links)
append_pagination_html_to (a_output)
end
append_page_title_html_to (a_output: STRING)
-- Append the title of the page as a html string to `a_output'.
-- It shows the current page number.
do
a_output.append ("<h2>Blog")
if multiple_pages_needed then
a_output.append (" (Page " + page_number.out + " of " + pages_count.out + ")")
end
a_output.append ("</h2>")
end
append_creation_date_html_to (n: CMS_NODE; a_output: STRING)
-- Append the creation date as a html string to `a_output'.
local
hdate: HTTP_DATE
do
if attached n.creation_date as l_modified then
create hdate.make_from_date_time (l_modified)
hdate.append_to_yyyy_mmm_dd_string (a_output)
a_output.append (" ")
end
end
append_author_html_to (n: CMS_NODE; a_output: STRING)
-- Append to `a_output', the author of node `n' as html link to author's posts.
do
if attached n.author as l_author then
a_output.append ("by ")
a_output.append ("<a class=%"blog_user_link%" href=%"/blogs/user/" + l_author.id.out + "%">" + l_author.name + "</a>")
end
end
append_title_html_to (n: CMS_NODE; page: CMS_RESPONSE; a_output: STRING)
-- Append to `a_output', the title of node `n' as html link to detail page.
local
lnk: CMS_LOCAL_LINK
do
lnk := blog_api.node_api.node_link (n)
a_output.append ("<span class=%"blog_title%">")
a_output.append (page.link (lnk.title, lnk.location, Void))
a_output.append ("</span>")
end
append_summary_html_to (n: CMS_NODE; page: CMS_RESPONSE; a_output: STRING)
-- returns a html string with the summary of the node and a link to the detail page
local
lnk: CMS_LOCAL_LINK
do
if attached n.summary as l_summary then
lnk := blog_api.node_api.node_link (n)
a_output.append ("<p class=%"blog_list_summary%">")
if attached api.format (n.format) as f then
a_output.append (f.formatted_output (l_summary))
else
a_output.append (page.formats.default_format.formatted_output (l_summary))
end
a_output.append ("<br />")
a_output.append (page.link ("See more...", lnk.location, Void))
a_output.append ("</p>")
end
end
append_pagination_html_to (a_output: STRING)
-- Append to `a_output' with the pagination links (if necessary).
local
tmp: NATURAL_32
do
if multiple_pages_needed then
a_output.append ("<div class=%"pagination%">")
-- If exist older posts show link to next page
if page_number < pages_count then
tmp := page_number + 1
a_output.append (" <a class=%"blog_older_posts%" href=%"" + base_path + "/page/" + tmp.out + "%"><< Older Posts</a> ")
end
-- Delimiter
if page_number < pages_count AND page_number > 1 then
a_output.append (" | ")
end
-- If exist newer posts show link to previous page
if page_number > 1 then
tmp := page_number -1
a_output.append (" <a class=%"blog_newer_posts%" href=%"" + base_path + "/page/" + tmp.out + "%">Newer Posts >></a> ")
end
a_output.append ("</div>")
end
end
base_path : STRING
-- the path to the page that lists all blogs
do
Result := "/blogs"
end
end

View File

@@ -0,0 +1,150 @@
note
description: "[
Request handler related to
/blogs/user/{id}/
or /blogs/user/{id}/page/{page}.
Displays all posts of the given 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$"
class
BLOG_USER_HANDLER
inherit
BLOG_HANDLER
redefine
do_get,
posts,
total_entries,
append_page_title_html_to,
base_path
end
create
make
feature -- Global Variables
user : detachable CMS_USER
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_error: NOT_FOUND_ERROR_CMS_RESPONSE
do
user := Void
if attached user_from_request (req) as l_user then
user := l_user
-- Output the results, similar as in the blog hanlder (but with other queries)
Precursor (req, res)
else
-- Throw a bad request error because the user is not valid
create l_error.make (req, res, api)
l_error.set_main_content ("<h1>Error</h1>User with id " + user_id_path_parameter (req).out + " doesn't exist!")
l_error.execute
end
end
feature -- Query
user_valid (req: WSF_REQUEST) : BOOLEAN
-- Returns true if a valid user id is given and a user with this id exists,
-- otherwise returns false.
local
user_id: INTEGER_32
do
user_id := user_id_path_parameter (req)
if user_id <= 0 then
-- Given user id is not valid
Result := False
else
--Check if user with user_id exists
Result := api.user_api.user_by_id (user_id) /= Void
end
end
user_from_request (req: WSF_REQUEST): detachable CMS_USER
-- Eventual user with given id in the path of request `req'.
local
uid: like user_id_path_parameter
do
uid := user_id_path_parameter (req)
if uid > 0 then
Result := api.user_api.user_by_id (uid)
else
-- Missing or invalid user id.
end
end
user_id_path_parameter (req: WSF_REQUEST): INTEGER_32
-- User id from path /blogs/{user}.
-- Unsigned integer since negative ids are not allowed.
-- If no valid id can be read it returns -1
local
s: STRING
do
Result := -1
if attached {WSF_STRING} req.path_parameter ("user") as l_user_id then
if l_user_id.is_integer then
Result := l_user_id.integer_value
end
end
end
posts: LIST [CMS_BLOG]
-- Blog posts to display on given page.
-- Filters out the posts of the current user.
do
if attached user as l_user then
Result := blog_api.blogs_from_user_order_created_desc_limited (l_user, entries_per_page, entries_per_page * (page_number - 1))
else
create {ARRAYED_LIST [CMS_BLOG]} Result.make (0)
end
end
total_entries : NATURAL_32
-- Returns the number of total entries/posts of the current user
do
if attached user as l_user then
Result := blog_api.blogs_count_from_user (l_user).to_natural_32
else
Result := Precursor
end
end
feature -- HTML Output
append_page_title_html_to (a_output: STRING)
-- Returns the title of the page as a html string. It shows the current page number and the name of the current user
do
a_output.append ("<h2>Posts from ")
if attached user as l_user then
a_output.append (l_user.name)
else
a_output.append ("unknown user")
end
if multiple_pages_needed then
a_output.append (" (Page " + page_number.out + " of " + pages_count.out + ")")
-- Get the posts from the current page (limited by entries per page)
end
a_output.append ("</h2>")
end
base_path : STRING
-- Path to page listing all blogs.
-- If user is logged in, include user id
do
if attached user as l_user then
Result := "/blogs/user/" + l_user.id.out
else
Result := precursor
end
end
end

View File

@@ -0,0 +1,23 @@
note
description: "Deferred request handler related to /blogs/... Has an own blog api."
author: "Dario B<>sch <daboesch@student.ethz.ch>"
date: "$Date: 2015-05-18 13:49:00 +0100 (lun., 18 mai 2015) $"
revision: "$9661667$"
deferred class
CMS_BLOG_HANDLER
inherit
CMS_MODULE_HANDLER [CMS_BLOG_API]
rename
module_api as blog_api
end
feature -- Access
entries_per_page: NATURAL_32
do
Result := blog_api.entries_per_page
end
end

View File

@@ -0,0 +1,43 @@
note
description: "Interface for accessing blog contents from the database."
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_BLOG_STORAGE_I
inherit
CMS_NODE_STORAGE_I
feature -- Access
blogs_count: INTEGER_64
-- Count of blog nodes
deferred
end
blogs_count_from_user (a_user: CMS_USER) : INTEGER_64
-- Number of nodes of type blog from `a_user'.
require
has_id: a_user.has_id
deferred
end
blogs: LIST [CMS_NODE]
-- List of nodes ordered by creation date (descending).
deferred
end
blogs_limited (limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_NODE]
-- List of posts ordered by creation date from offset to offset + limit.
deferred
end
blogs_from_user_limited (a_user: CMS_USER; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_NODE]
-- List of posts from `a_user' ordered by creation date from offset to offset + limit.
require
has_id: a_user.has_id
deferred
end
end

View File

@@ -0,0 +1,47 @@
note
description: "Summary description for {CMS_BLOG_STORAGE_NULL}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOG_STORAGE_NULL
inherit
CMS_NODE_STORAGE_NULL
CMS_BLOG_STORAGE_I
create
make
feature -- Access
blogs_count: INTEGER_64
-- Count of nodes.
do
end
blogs_count_from_user (a_user: CMS_USER) : INTEGER_64
-- <Precursor>
do
end
blogs: LIST [CMS_NODE]
-- <Precursor>
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
blogs_limited (limit: NATURAL_32; offset: NATURAL_32) : LIST [CMS_NODE]
-- <Precursor>
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
blogs_from_user_limited (a_user: CMS_USER; limit: NATURAL_32; offset: NATURAL_32): LIST [CMS_NODE]
-- <Precursor>
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
end

View File

@@ -0,0 +1,140 @@
note
description: "Access to the sql database for the blog module"
author: "Dario B<>sch <daboesch@student.ethz.ch>"
date: "$Date: 2015-05-21 14:46:00 +0100$"
revision: "$Revision: 96616 $"
class
CMS_BLOG_STORAGE_SQL
inherit
CMS_NODE_STORAGE_SQL
CMS_BLOG_STORAGE_I
create
make
feature -- Access
blogs_count: INTEGER_64
-- <Precursor>
do
error_handler.reset
write_information_log (generator + ".blogs_count")
sql_query (sql_select_blog_count, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
end
blogs_count_from_user (a_user: CMS_USER) : INTEGER_64
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".blogs_count_from_user")
create l_parameters.make (2)
l_parameters.put (a_user.id, "user")
sql_query (sql_select_blog_count_from_user, l_parameters)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
end
blogs: LIST [CMS_NODE]
-- <Precursor>
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".blogs")
from
sql_query (sql_select_blogs_order_created_desc, Void)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
blogs_limited (a_limit: NATURAL_32; a_offset: NATURAL_32): 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_limited")
from
create l_parameters.make (2)
l_parameters.put (a_limit, "limit")
l_parameters.put (a_offset, "offset")
sql_query (sql_blogs_limited, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
blogs_from_user_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32): 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_limited")
from
create l_parameters.make (2)
l_parameters.put (a_limit, "limit")
l_parameters.put (a_offset, "offset")
l_parameters.put (a_user.id, "user")
sql_query (sql_blogs_from_user_limited, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
feature {NONE} -- Queries
sql_select_blog_count: STRING = "SELECT count(*) FROM Nodes WHERE status != -1 AND type = %"blog%";"
-- Nodes count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_blog_count_from_user: STRING = "SELECT count(*) FROM Nodes WHERE status != -1 AND type = %"blog%" AND author = :user ;"
-- Nodes count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_blogs_order_created_desc: STRING = "SELECT * FROM Nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC;"
-- 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_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
end

View File

@@ -0,0 +1,25 @@
ul.cms_blog_nodes {
padding: 0;
margin: 0;
}
ul.cms_blog_nodes li.cms_type_blog {
list-style: none;
display: block;
margin-top: 20px;
padding-bottom: 20px;
border-bottom: 1px dotted black;
}
ul.cms_blog_nodes li.cms_type_blog .blog_title a {
color: black;
font-size: 18px;
text-decoration: none;
display: block;
margin: 6px 0;
}
ul.cms_blog_nodes li.cms_type_blog .blog_title a:hover {
color: #999;
}
ul.cms_blog_nodes li.cms_type_blog .blog_list_summary a {
margin-top: 20px;
display: block;
}

View File

@@ -3,13 +3,15 @@ ul.cms-nodes {
padding: 3px 3px 3px 3px;
border: solid 1px #ccc;
}
li.cms_type_page {
ul.cms-nodes li {
border-top: dotted 1px #ccc;
}
li.cms_type_page a::before {
content: "[page] ";
}
li.cms_type_page:first-child {
ul.cms-nodes li:first-child {
border-top: none;
}
ul.cms-nodes li.cms_type_page a::before {
content: "[page] ";
}
ul.cms-nodes li.cms_type_blog a::before {
content: "[blog] ";
}

View File

@@ -0,0 +1,30 @@
ul.cms_blog_nodes{
padding:0;
margin:0;
li.cms_type_blog{
list-style: none;
display: block;
margin-top:20px;
padding-bottom:20px;
border-bottom:1px dotted black;
.blog_title a{
color:black;
font-size:18px;
text-decoration: none;
display:block;
margin:6px 0;
&:hover{
color:#999;
}
}
.blog_list_summary a{
margin-top:20px;
display:block;
}
}
}

View File

@@ -1,14 +1,24 @@
ul.cms-nodes {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc;
}
li.cms_type_page {
a::before {
li{
border-top: dotted 1px #ccc;
&:first-child {
border-top: none;
}
}
li.cms_type_page a::before {
content: "[page] ";
}
border-top: dotted 1px #ccc;
&:first-child {
border-top: none;
li.cms_type_blog a::before {
content: "[blog] ";
}
}

View File

@@ -6,8 +6,12 @@
<!-- EWF CMS -->
<link rel="stylesheet" href="{$site_url/}theme/css/style.css">
<link rel="stylesheet" href="{$site_url/}theme/css/node.css">
<script src="{$site_url/}theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}theme/js/roc_auth.js"></script>
<!-- CMS Blog Module -->
<link rel="stylesheet" href="{$site_url/}theme/css/blog.css">
<script src="{$site_url/}theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}theme/js/roc_auth.js"></script>
<!-- bootstrap framework -->
<!-- Latest compiled and minified CSS -->

View File

@@ -17,10 +17,11 @@ feature {NONE} -- Initialization
default_create
do
Precursor
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (3)
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (4)
available_formats.extend (create {PLAIN_TEXT_CONTENT_FORMAT})
available_formats.extend (create {FILTERED_HTML_CONTENT_FORMAT})
available_formats.extend (create {FULL_HTML_CONTENT_FORMAT})
available_formats.extend (create {CMS_EDITOR_CONTENT_FORMAT})
end
feature -- Access

View File

@@ -17,11 +17,14 @@ feature -- Forms ...
local
ti: WSF_FORM_TEXT_INPUT
fset: WSF_FORM_FIELD_SET
ta: WSF_FORM_TEXTAREA
ta, sum: CMS_FORM_TEXTAREA
tselect: WSF_FORM_SELECT
opt: WSF_FORM_SELECT_OPTION
cms_format: CMS_EDITOR_CONTENT_FORMAT
l_uri: detachable READABLE_STRING_8
do
create cms_format
create ti.make ("title")
ti.set_label ("Title")
ti.set_size (70)
@@ -33,25 +36,47 @@ feature -- Forms ...
f.extend_html_text ("<br/>")
create ta.make ("body")
ta.set_rows (10)
ta.set_cols (70)
if a_node /= Void then
ta.set_text_value (a_node.content)
end
-- ta.set_label ("Body")
ta.set_description ("This is the main content")
ta.set_is_required (False)
create fset.make
fset.set_legend ("Body")
fset.extend (ta)
fset.extend_html_text ("<br/>")
-- Select field has to be initialized before textareas are replaced, because they depend on the selection of the field
create tselect.make ("format")
tselect.set_label ("Body's format")
tselect.set_is_required (True)
-- Main Content
create ta.make ("body")
ta.set_rows (10)
ta.set_cols (70)
ta.show_as_editor_if_selected (tselect, cms_format.name)
if a_node /= Void then
ta.set_text_value (a_node.content)
end
ta.set_label ("Content")
ta.set_description ("This is the main content")
ta.set_is_required (False)
-- Summary
create sum.make ("summary")
sum.set_rows (3)
sum.set_cols (70)
-- if cms_html is selected
sum.show_as_editor_if_selected (tselect, cms_format.name)
if a_node /= Void then
sum.set_text_value (a_node.summary)
end
sum.set_label ("Summary")
sum.set_description ("This is the summary")
sum.set_is_required (False)
create fset.make
fset.set_legend ("Body")
-- Add summary
fset.extend (sum)
fset.extend_html_text("<br />")
-- Add content (body)
fset.extend (ta)
fset.extend_html_text ("<br/>")
across
content_type.available_formats as c
loop
@@ -69,7 +94,7 @@ feature -- Forms ...
f.extend (fset)
-- Path alias
-- Path alias
create ti.make ("path_alias")
ti.set_label ("Path")
ti.set_size (70)
@@ -113,7 +138,7 @@ feature -- Forms ...
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
local
b: detachable READABLE_STRING_8
b,s: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT
do
if attached fd.integer_item ("id") as l_id and then l_id > 0 then
@@ -126,6 +151,12 @@ feature -- Forms ...
if attached fd.string_item ("body") as l_body then
b := l_body
end
-- Read out the summary field from the form data
if attached fd.string_item ("summary") as l_summary then
s := l_summary
end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
@@ -133,15 +164,19 @@ feature -- Forms ...
else
f := response.formats.default_format
end
-- Update node with summary and body content
if b /= Void then
a_node.set_content (b, Void, f.name) -- FIXME: summary
a_node.set_content (b, s, f.name)
end
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable CMS_NODE): G
-- <Precursor>
local
b: detachable READABLE_STRING_8
b,s: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT
l_node: detachable like new_node
do
@@ -175,9 +210,16 @@ feature -- Forms ...
end
l_node.set_author (response.user)
--Summary
if attached fd.string_item ("summary") as l_summary then
s := l_summary
end
--Content
if attached fd.string_item ("body") as l_body then
b := l_body
end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
@@ -185,8 +227,10 @@ feature -- Forms ...
else
f := response.formats.default_format
end
-- Update node with summary and content
if b /= Void then
l_node.set_content (b, Void, f.name)
l_node.set_content (b, s, f.name)
end
Result := l_node
end
@@ -243,6 +287,23 @@ feature -- Output
s.append (")")
end
s.append ("</div>")
-- We don't show the summary on the detail page, since its just a short view of the full content. Otherwise we would write the same thing twice.
-- The usage of the summary is to give a short overview in the list of nodes or for the meta tag "description"
-- if attached a_node.summary as l_summary then
-- s.append ("<p class=%"summary%">")
-- if attached node_api.cms_api.format (a_node.format) as f then
-- s.append (f.formatted_output (l_summary))
-- else
-- s.append (a_response.formats.default_format.formatted_output (l_summary))
-- end
-- s.append ("</p>")
-- end
if attached a_node.content as l_content then
s.append ("<p class=%"content%">")
if attached node_api.cms_api.format (a_node.format) as f then

View File

@@ -33,6 +33,7 @@ feature -- Access
end
end
nodes: LIST [CMS_NODE]
-- List of nodes.
do

View File

@@ -0,0 +1,31 @@
note
description: "HTML Content format editable with WYSIWYG editor."
date: "$Date$"
revision: "$Revision$"
class
CMS_EDITOR_CONTENT_FORMAT
inherit
CONTENT_FORMAT
redefine
default_create
end
feature {NONE} -- Initialization
default_create
do
Precursor
create filters.make (0)
end
feature -- Access
name: STRING = "cms_editor"
title: STRING_8 = "CMS HTML content"
filters: ARRAYED_LIST [CONTENT_FILTER]
end

View File

@@ -27,10 +27,11 @@ feature -- Access
once
-- Can we provide an external file to read the
-- supported formats?
create {ARRAYED_LIST [CONTENT_FORMAT]} Result.make (3)
create {ARRAYED_LIST [CONTENT_FORMAT]} Result.make (4)
Result.force (plain_text)
Result.force (full_html)
Result.force (filtered_html)
Result.force (cms_html)
end
default_format: CONTENT_FORMAT
@@ -43,6 +44,11 @@ feature -- Access
create Result
end
cms_html: CMS_EDITOR_CONTENT_FORMAT
once
create Result
end
full_html: FULL_HTML_CONTENT_FORMAT
once
create Result
@@ -53,7 +59,6 @@ feature -- Access
create Result
end
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,106 @@
note
description: "Class to import a WYSIWIG editor using javascript code"
author: "Dario B<>sch <daboesch@student.ethz.ch"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_EDITOR
feature -- Initialisation
load_assets : STRING
-- Loads all assest needed to show the editor
deferred
end
feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Javascript code that replaces a textarea with the editor. The editor instance should be saved in editor_variable
deferred
end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Javascript code that restores a textarea
deferred
end
javascript_textarea_to_editor(a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Javascript code to display the textarea as a WYSIWIG editor as soon as the document is loaded
do
Result := javascript_ready(javascript_replace_textarea (a_textarea))
end
javascript_textarea_to_editor_if_selected (a_textarea : WSF_FORM_TEXTAREA; a_select_field : WSF_FORM_SELECT; a_value : STRING) : STRING
-- Javascript code to display the textarea as a WYSIWIG editor if a_select_field has a_value
local
initial_replace_code, on_select_replace_code : STRING
do
-- Javascript that replaces the textarea if a_value is selected at load time
initial_replace_code := javascript_ready(javascript_if_selected(a_select_field, a_value, javascript_replace_textarea(a_textarea)))
-- Javascript code that replaces the textarea as soon as value is selected at a_select_field
on_select_replace_code := javascript_ready(
javascript_init_editor_variable(a_textarea) +
javascript_on_select(a_select_field, a_value,
-- If a_value is selected, replace textarea
javascript_replace_textarea(a_textarea),
-- Otherwise restore it
javascript_restore_textarea(a_textarea)
)
)
Result := initial_replace_code + " " + on_select_replace_code
end
javascript_init_editor_variable(a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Returns the javascript code that initializes a local variable to store the editor instance
do
Result := "var " + editor_variable(a_textarea) + "; "
end
javascript_if_selected(a_select_field : WSF_FORM_SELECT; a_value : STRING; a_code : STRING) : STRING
-- Javascript that executes a_code if a_value is selected at a_select_field
do
Result := "if($('#" + field_id(a_select_field) + "').val() == %"" + a_value + "%"){ " + a_code + " }"
end
javascript_ready (a_code : STRING) : STRING
-- Wraps the given javascript code with a ready statement, such that it's executed when the document has loaded
do
Result := "$(function() { " + a_code + " });"
end
javascript_on_select (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_then : STRING; a_else : STRING) : STRING
-- Javascript code that executes a_then if at the given select_field the given string value is selected, otherwise it executes a_else
do
Result := "$('#" + field_id(a_select_field) + "').change(function(){" +
javascript_if_selected(a_select_field, a_value, a_then) +
"else{" +
a_else +
"}" +
"});"
end
feature -- Helper
field_id(a_select_field : WSF_FORM_SELECT) : STRING
-- Returns the id of the given field
do
if attached a_select_field.css_id as a_id then
Result := a_id
else
Result := a_select_field.name + "-select"
end
end
editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Returns the variable name that stores the editor instance of the given textarea
do
Result := "editor_" + a_textarea.name
end
end

View File

@@ -0,0 +1,37 @@
note
description: "Summary description for {CMS_EDITOR_CKEDITOR}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_EDITOR_CKEDITOR
inherit
CMS_EDITOR
feature -- Initialisation
load_assets : STRING
-- <Precursor>
do
Result := "<script src=%"//cdn.ckeditor.com/4.4.7/standard/ckeditor.js%"></script>"
end
feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- <Precursor>
do
-- Replaces the textarea with an editor instance. Save the instance in a variable
Result := editor_variable(a_textarea) + " = CKEDITOR.replace( '" + a_textarea.name + "' );"
end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- <Precursor>
do
-- Replaces the textarea with an editor instance. Save the instance in a variable
Result := "if (" + editor_variable(a_textarea) + " != undefined) " + editor_variable(a_textarea) + ".destroy();"
end
end

View File

@@ -0,0 +1,77 @@
note
description: "Extends the WSF form textarea with features to add a WYSIWIG editor."
author: "Dario B<>sch <daboesch@student.ethz.ch"
date: "$Date: 2015-05-23 16:02:55 +0100 (Sat, 23 May 2015) $"
revision: "$Revision: 96616 $"
class
CMS_FORM_TEXTAREA
inherit
WSF_FORM_TEXTAREA
redefine
make,
append_item_to_html
end
CMS_EDITOR_CKEDITOR
create
make
feature -- Initialisation
make (a_name: like name)
-- <Precursor>
do
precursor(a_name)
-- By default we don't replace the textarea by an editor
editor := False;
end
feature -- Access
editor : BOOLEAN
-- True if the textarea should be replaced by the editor. Default is false.
format_field : detachable WSF_FORM_SELECT
-- Selection field for the format on that it depends, if the editor is shown or not.
condition_value : detachable STRING
feature -- Editor
show_as_editor
-- The textarea will be replaced by a wysiwyg editor
do
editor := True
end
show_as_editor_if_selected (a_select_field : WSF_FORM_SELECT; a_value : STRING)
-- Replaces the textarea only if a_select_field has a_value (or the value gets selected)
do
editor := True
format_field := a_select_field
condition_value := a_value
end
feature -- Conversion
append_item_to_html (a_theme: WSF_THEME; a_html: STRING_8)
do
-- Add javascript to replace textarea with editor
precursor(a_theme, a_html)
if editor then
a_html.append (load_assets)
a_html.append ("<script type=%"text/javascript%">");
if attached format_field as l_field and then attached condition_value as l_value then
a_html.append (javascript_textarea_to_editor_if_selected (Current, l_field, l_value))
else
a_html.append (javascript_textarea_to_editor (Current))
end
a_html.append ("</script>")
end
end
end

View File

@@ -287,7 +287,7 @@ feature -- Path aliases
do
Result := a_source
if attached storage.path_alias (Result) as l_path then
Result := l_path
Result := "/" + l_path
end
end

View File

@@ -43,7 +43,7 @@ feature {CMS_API} -- Module Initialization
feature -- Status
is_initialized: BOOLEAN
-- Is Current module initialized?
-- Is Current module initialized?
feature {CMS_API} -- Access: API