Extracted page support from cms_node_module, and add a proper CMS_PAGE_MODULE.
- now, the CMS_PAGE_MODULE has to be declared in the related CMS_SETUP via CMS_EXECUTION. (See demo for example) Improved the export facilities. Implemented blog and page export. Added import facilities. Implemented blog and page import. Improved node revision web interface (allow to edit a past revision, in order to restore it as latest revisionm i.e current). Removed specific tag from blog module, and reuse the taxonomy module for that purpose. Added WIKITEXT module that provide a WIKITEXT_FILTER, so now we can have wikitext content. - for now, no support for wiki links such as [[Foobar]].
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
<library name="cms_seo_module" location="..\..\modules\seo\seo-safe.ecf" readonly="false"/>
|
||||
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
|
||||
<library name="cms_wikitext_module" location="..\..\modules\wikitext\wikitext-safe.ecf" readonly="false"/>
|
||||
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false"/>
|
||||
<!--
|
||||
By default, commented, since it depends on specific environment settings.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"session_auth": { "location": "../../modules/session_auth" },
|
||||
"taxonomy": { "location": "../../modules/taxonomy" },
|
||||
"files": { "location": "../../modules/files" },
|
||||
"custom_block": { "location": "../../modules/custom_block" }
|
||||
"custom_block": { "location": "../../modules/custom_block" },
|
||||
"wikitext": { "location": "../../modules/wikitext" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,3 @@ CREATE TABLE node_revisions (
|
||||
CONSTRAINT Unique_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
|
||||
8
examples/demo/site/modules/node/scripts/page.sql
Normal file
8
examples/demo/site/modules/node/scripts/page.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
15
examples/demo/site/modules/wikitext/files/css/wikitext.css
Normal file
15
examples/demo/site/modules/wikitext/files/css/wikitext.css
Normal file
@@ -0,0 +1,15 @@
|
||||
#main code, #main e, #main eiffel {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
border: solid 1px #ccc;
|
||||
background-color: #fff;
|
||||
line-height: 1.3;
|
||||
padding: 10px;
|
||||
margin: 2px 0 2px 0;
|
||||
}
|
||||
#main code.inline, #main e.inline, #main eiffel.inline {
|
||||
display: inline-block;
|
||||
padding: 0 2px 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
18
examples/demo/site/modules/wikitext/files/scss/wikitext.scss
Normal file
18
examples/demo/site/modules/wikitext/files/scss/wikitext.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
#main {
|
||||
code, e, eiffel {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
border: solid 1px #ccc;
|
||||
background-color: #fff;
|
||||
line-height: 1.3;
|
||||
padding: 10px;
|
||||
margin: 2px 0 2px 0;
|
||||
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
padding: 0 2px 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,12 @@ feature -- CMS modules
|
||||
a_setup.register_module (create {CMS_OPENID_MODULE}.make)
|
||||
a_setup.register_module (create {CMS_SESSION_AUTH_MODULE}.make)
|
||||
|
||||
-- User
|
||||
a_setup.register_module (create {CMS_USER_PROFILE_MODULE}.make)
|
||||
|
||||
-- Nodes
|
||||
a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup))
|
||||
a_setup.register_module (create {CMS_PAGE_MODULE}.make)
|
||||
a_setup.register_module (create {CMS_BLOG_MODULE}.make)
|
||||
|
||||
-- Files
|
||||
@@ -69,6 +73,9 @@ feature -- CMS modules
|
||||
-- Taxonomy
|
||||
a_setup.register_module (create {CMS_TAXONOMY_MODULE}.make)
|
||||
|
||||
-- Wiki
|
||||
a_setup.register_module (create {WIKITEXT_MODULE}.make (a_setup))
|
||||
|
||||
-- Recent changes
|
||||
a_setup.register_module (create {CMS_RECENT_CHANGES_MODULE}.make)
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ feature -- Access: router
|
||||
|
||||
l_admin_cache_handler: CMS_ADMIN_CACHE_HANDLER
|
||||
l_admin_export_handler: CMS_ADMIN_EXPORT_HANDLER
|
||||
l_admin_import_handler: CMS_ADMIN_IMPORT_HANDLER
|
||||
|
||||
l_uri_mapping: WSF_URI_MAPPING
|
||||
do
|
||||
@@ -89,6 +90,10 @@ feature -- Access: router
|
||||
create l_admin_export_handler.make (a_api)
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/admin/export", l_admin_export_handler)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get_post)
|
||||
|
||||
create l_admin_import_handler.make (a_api)
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/admin/import", l_admin_import_handler)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get_post)
|
||||
|
||||
create l_user_handler.make (a_api)
|
||||
a_router.handle ("/admin/add/user", l_user_handler, a_router.methods_get_post)
|
||||
@@ -118,7 +123,9 @@ feature -- Security
|
||||
Result.force ("admin core caches")
|
||||
Result.force ("clear blocks cache")
|
||||
Result.force ("admin export")
|
||||
Result.force ("admin import")
|
||||
Result.force ("export core")
|
||||
Result.force ("import core")
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
@@ -154,6 +161,9 @@ feature -- Hooks
|
||||
|
||||
-- Per module export permission!
|
||||
create lnk.make ("Export", "admin/export")
|
||||
admin_lnk.extend (lnk)
|
||||
-- Per module import permission!
|
||||
create lnk.make ("Import", "admin/import")
|
||||
admin_lnk.extend (lnk)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ feature -- Execution
|
||||
l_response: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
l_exportation_parameters: CMS_EXPORT_PARAMETERS
|
||||
l_exportation: CMS_EXPORT_CONTEXT
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := exportation_web_form (l_response)
|
||||
@@ -65,15 +65,15 @@ feature -- Execution
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_export_all_data) then
|
||||
if attached fd.string_item ("folder") as l_folder then
|
||||
create l_exportation_parameters.make (api.site_location.extended ("export").extended (l_folder))
|
||||
create l_exportation.make (api.site_location.extended ("export").extended (l_folder))
|
||||
else
|
||||
create l_exportation_parameters.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss")))
|
||||
create l_exportation.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss")))
|
||||
end
|
||||
api.hooks.invoke_export_to (Void, l_exportation_parameters, l_response)
|
||||
api.hooks.invoke_export_to (Void, l_exportation, l_response)
|
||||
l_response.add_notice_message ("All data exported (if allowed)!")
|
||||
create s.make_empty
|
||||
across
|
||||
l_exportation_parameters.logs as ic
|
||||
l_exportation.logs as ic
|
||||
loop
|
||||
s.append (ic.item)
|
||||
s.append ("<br/>")
|
||||
|
||||
121
modules/admin/handler/cms_admin_import_handler.e
Normal file
121
modules/admin/handler/cms_admin_import_handler.e
Normal file
@@ -0,0 +1,121 @@
|
||||
note
|
||||
description: "[
|
||||
Administrate import functionality.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ADMIN_IMPORT_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
rename
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get,
|
||||
do_post
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_response: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := importation_web_form (l_response)
|
||||
create s.make_empty
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_response: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
l_importation: CMS_IMPORT_CONTEXT
|
||||
p: PATH
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := importation_web_form (l_response)
|
||||
f.process (l_response)
|
||||
if
|
||||
attached f.last_data as fd and then
|
||||
fd.is_valid
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_import_all_data) then
|
||||
if attached fd.string_item ("folder") as l_folder then
|
||||
create p.make_from_string (l_folder)
|
||||
if p.is_absolute then
|
||||
create l_importation.make (p)
|
||||
else
|
||||
create l_importation.make (api.site_location.extended ("import").extended (l_folder))
|
||||
end
|
||||
else
|
||||
create l_importation.make (api.site_location.extended ("import").extended ("default"))
|
||||
end
|
||||
api.hooks.invoke_import_from (Void, l_importation, l_response)
|
||||
l_response.add_notice_message ("All data imported (if allowed)!")
|
||||
create s.make_empty
|
||||
across
|
||||
l_importation.logs as ic
|
||||
loop
|
||||
s.append (ic.item)
|
||||
s.append ("<br/>")
|
||||
s.append_character ('%N')
|
||||
end
|
||||
l_response.add_notice_message (s)
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
end
|
||||
create s.make_empty
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
feature -- Widget
|
||||
|
||||
importation_web_form (a_response: CMS_RESPONSE): CMS_FORM
|
||||
local
|
||||
f_name: WSF_FORM_TEXT_INPUT
|
||||
but: WSF_FORM_SUBMIT_INPUT
|
||||
do
|
||||
create Result.make (a_response.url (a_response.location, Void), "import_all_data")
|
||||
Result.extend_raw_text ("Import CMS data from ")
|
||||
create f_name.make_with_text ("folder", "default")
|
||||
f_name.set_label ("Import folder name")
|
||||
f_name.set_description ("Folder name under 'imports' folder.")
|
||||
f_name.set_is_required (True)
|
||||
Result.extend (f_name)
|
||||
create but.make_with_text ("op", text_import_all_data)
|
||||
Result.extend (but)
|
||||
end
|
||||
|
||||
feature -- Interface text.
|
||||
|
||||
text_import_all_data: STRING_32 = "Import all data"
|
||||
|
||||
end
|
||||
@@ -283,7 +283,7 @@ feature -- Execution
|
||||
fd.report_error ("Installation failed for module " + m.name)
|
||||
end
|
||||
else
|
||||
fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value)
|
||||
fd.report_error ("Can not find associated module " + ic.item.as_string.url_encoded_value)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -599,7 +599,7 @@ feature -- Handler
|
||||
end
|
||||
elseif attached {WSF_STRING} req.form_parameter ("username") as l_username then
|
||||
if
|
||||
attached {CMS_USER} l_user_api.user_by_name (l_username) as l_user and then
|
||||
attached {CMS_USER} l_user_api.user_by_name (l_username.value) as l_user and then
|
||||
attached l_user.email as l_user_email
|
||||
then
|
||||
-- User exist create a new token and send a new email.
|
||||
|
||||
@@ -9,8 +9,7 @@ class
|
||||
inherit
|
||||
CMS_NODE
|
||||
redefine
|
||||
make_empty,
|
||||
import_node
|
||||
make_empty
|
||||
end
|
||||
|
||||
create
|
||||
@@ -24,23 +23,6 @@ feature {NONE} -- Initialization
|
||||
Precursor
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
import_node (a_node: CMS_NODE)
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor (a_node)
|
||||
if attached {CMS_BLOG} a_node as l_blog then
|
||||
if attached l_blog.tags as l_tags then
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
add_tag (ic.item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
content_type: READABLE_STRING_8
|
||||
@@ -60,11 +42,6 @@ feature -- Access: node
|
||||
-- Format associated with `content' and `summary'.
|
||||
-- For example: text, mediawiki, html, etc
|
||||
|
||||
feature -- Access: blog
|
||||
|
||||
tags: detachable ARRAYED_LIST [READABLE_STRING_32]
|
||||
-- Optional tags
|
||||
|
||||
feature -- Element change: node
|
||||
|
||||
set_content (a_content: like content; a_summary: like summary; a_format: like format)
|
||||
@@ -74,39 +51,5 @@ feature -- Element change: node
|
||||
format := a_format
|
||||
end
|
||||
|
||||
feature -- Element change: blog
|
||||
|
||||
add_tag (a_tag: READABLE_STRING_32)
|
||||
-- Set `parent' to `a_page'
|
||||
require
|
||||
not a_tag.is_whitespace
|
||||
local
|
||||
l_tags: like tags
|
||||
do
|
||||
l_tags := tags
|
||||
if l_tags = Void then
|
||||
create l_tags.make (1)
|
||||
tags := l_tags
|
||||
end
|
||||
l_tags.force (a_tag)
|
||||
end
|
||||
|
||||
set_tags_from_string (a_tags: READABLE_STRING_32)
|
||||
local
|
||||
t: STRING_32
|
||||
do
|
||||
tags := Void
|
||||
across
|
||||
a_tags.split (',') as ic
|
||||
loop
|
||||
t := ic.item
|
||||
t.left_adjust
|
||||
t.right_adjust
|
||||
if not t.is_whitespace then
|
||||
add_tag (t)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "API to handle nodes of type blog. Extends the node API."
|
||||
author: "Dario B<>sch <daboesch@student.ethz.ch"
|
||||
date: "$Date: 2015-05-21 14:46:00 +0100$"
|
||||
revision: "$Revision: 96616 $"
|
||||
|
||||
@@ -41,6 +40,11 @@ feature {NONE} -- Initialization
|
||||
else
|
||||
create {CMS_BLOG_STORAGE_NULL} blog_storage.make
|
||||
end
|
||||
|
||||
--| For test reasons this is 2, so we don't have to create a lot of blog entries.
|
||||
--| TODO: Set to bigger constant.
|
||||
entries_per_page := 2
|
||||
|
||||
-- initialize_node_types
|
||||
end
|
||||
|
||||
@@ -54,10 +58,8 @@ feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
feature -- Configuration of blog handlers
|
||||
|
||||
entries_per_page : NATURAL_32 = 2
|
||||
entries_per_page : NATURAL_32
|
||||
-- The numbers of posts that are shown on one page. If there are more post a pagination is generated
|
||||
--| For test reasons this is 2, so we don't have to create a lot of blog entries.
|
||||
--| TODO: Set to bigger constant.
|
||||
|
||||
feature -- Access node
|
||||
|
||||
@@ -88,7 +90,7 @@ feature -- Access node
|
||||
Result := nodes_to_blogs (blog_storage.blogs_limited (a_limit, a_offset))
|
||||
end
|
||||
|
||||
blogs_from_user_order_created_desc_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32) : LIST [CMS_BLOG]
|
||||
blogs_from_user_order_created_desc_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_BLOG]
|
||||
-- List of nodes ordered by creation date and limited by limit and offset
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
@@ -97,6 +99,15 @@ feature -- Access node
|
||||
Result := nodes_to_blogs (blog_storage.blogs_from_user_limited (a_user, a_limit, a_offset))
|
||||
end
|
||||
|
||||
blogs_by_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_BLOG]
|
||||
-- List of blogs with title `a_title` for user `a_user` and ordered by creation date .
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
do
|
||||
-- load all posts and add the authors to each post
|
||||
Result := nodes_to_blogs (blog_storage.blogs_from_user_with_title (a_user, a_title))
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
full_blog_node (a_blog: CMS_BLOG): CMS_BLOG
|
||||
@@ -112,6 +123,13 @@ feature -- Conversion
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Commit
|
||||
|
||||
save_blog (a_blog: CMS_BLOG)
|
||||
do
|
||||
node_api.save_node (a_blog)
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
nodes_to_blogs (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_BLOG]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Displays all posts (pages with type blog). It's possible to list posts by user."
|
||||
author: "Dario B<>sch <daboesch@student.ethz.ch>"
|
||||
date: "$Date: 2015-05-22 15:13:00 +0100 (lun., 18 mai 2015) $"
|
||||
revision: "$Revision 96616$"
|
||||
|
||||
@@ -24,8 +23,12 @@ inherit
|
||||
|
||||
CMS_HOOK_EXPORT
|
||||
|
||||
CMS_HOOK_IMPORT
|
||||
|
||||
CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
CMS_IMPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -34,8 +37,8 @@ feature {NONE} -- Initialization
|
||||
make
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Service to demonstrate new node for blog"
|
||||
package := "demo"
|
||||
description := "Blogging service"
|
||||
package := "content"
|
||||
add_dependency ({CMS_NODE_MODULE})
|
||||
end
|
||||
|
||||
@@ -51,14 +54,12 @@ feature {CMS_API} -- Module Initialization
|
||||
ct: CMS_BLOG_NODE_TYPE
|
||||
do
|
||||
Precursor (api)
|
||||
|
||||
if attached {CMS_NODE_API} api.module_api ({CMS_NODE_MODULE}) as l_node_api then
|
||||
create ct
|
||||
create blog_api.make (api, l_node_api)
|
||||
|
||||
node_api := l_node_api
|
||||
-- Depends on {CMS_NODE_MODULE}
|
||||
|
||||
--| For now, add all available formats to content type `ct'.
|
||||
across
|
||||
api.formats as ic
|
||||
@@ -67,12 +68,6 @@ feature {CMS_API} -- Module Initialization
|
||||
end
|
||||
l_node_api.add_node_type (ct)
|
||||
l_node_api.add_node_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct, l_node_api))
|
||||
|
||||
-- Add support for CMS_BLOG, which requires a storage extension to store the optional "tags" value
|
||||
-- For now, we only have extension based on SQL statement.
|
||||
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
|
||||
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_BLOG_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -148,6 +143,7 @@ feature -- Hooks
|
||||
a_hooks.subscribe_to_menu_system_alter_hook (Current)
|
||||
a_hooks.subscribe_to_response_alter_hook (Current)
|
||||
a_hooks.subscribe_to_export_hook (Current)
|
||||
a_hooks.subscribe_to_import_hook (Current)
|
||||
end
|
||||
|
||||
response_alter (a_response: CMS_RESPONSE)
|
||||
@@ -164,7 +160,7 @@ feature -- Hooks
|
||||
a_menu_system.primary_menu.extend (lnk)
|
||||
end
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_ctx: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
local
|
||||
@@ -175,21 +171,28 @@ feature -- Hooks
|
||||
lst: LIST [CMS_BLOG]
|
||||
do
|
||||
if
|
||||
a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string ("blog") end
|
||||
attached node_api as l_node_api and then
|
||||
attached l_node_api.node_type ("blog") as l_node_type and then
|
||||
( a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"export any node", "export blog">>) and then
|
||||
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
|
||||
attached blog_api as l_blog_api
|
||||
then
|
||||
lst := l_blog_api.blogs_order_created_desc
|
||||
a_export_parameters.log ("Exporting " + lst.count.out + " blogs")
|
||||
a_export_ctx.log ("Exporting " + lst.count.out + " [" + l_node_type.name + "] items...")
|
||||
create d.make_with_path (a_export_ctx.location.extended ("nodes").extended (l_node_type.name))
|
||||
if d.exists then
|
||||
d.recursive_delete
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
n := l_blog_api.full_blog_node (ic.item)
|
||||
a_export_parameters.log (n.content_type + " #" + n.id.out)
|
||||
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out).appended_with_extension ("json")
|
||||
a_export_ctx.log (n.content_type + " #" + n.id.out)
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out).appended_with_extension ("json")
|
||||
create d.make_with_path (p.parent)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
@@ -197,16 +200,16 @@ feature -- Hooks
|
||||
create f.make_with_path (p)
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (blog_node_to_json (n)))
|
||||
f.put_string (json_to_string (blog_node_to_json (n, l_blog_api)))
|
||||
f.close
|
||||
end
|
||||
-- Revisions.
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1
|
||||
attached l_node_api.node_revisions (n) as l_revisions and then
|
||||
l_revisions.count > 1
|
||||
then
|
||||
a_export_parameters.log (n.content_type + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
|
||||
a_export_ctx.log (n.content_type + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
@@ -218,7 +221,7 @@ feature -- Hooks
|
||||
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (blog_node_to_json (l_blog)))
|
||||
f.put_string (json_to_string (blog_node_to_json (l_blog, l_blog_api)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
@@ -229,8 +232,102 @@ feature -- Hooks
|
||||
end
|
||||
end
|
||||
|
||||
blog_node_to_json (a_blog: CMS_BLOG): JSON_OBJECT
|
||||
blog_node_to_json (a_blog: CMS_BLOG; a_blog_api: CMS_BLOG_API): JSON_OBJECT
|
||||
do
|
||||
Result := node_to_json (a_blog)
|
||||
Result := node_to_json (a_blog, a_blog_api.node_api)
|
||||
end
|
||||
|
||||
import_from (a_import_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_import_ctx: CMS_IMPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Import data identified by `a_import_id_list',
|
||||
-- or import all data if `a_import_id_list' is Void.
|
||||
local
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
s: STRING
|
||||
jp: JSON_PARSER
|
||||
loc: STRING
|
||||
do
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached {CMS_BLOG_NODE_TYPE} l_node_api.node_type ("blog") as l_node_type and then
|
||||
( a_import_id_list = Void
|
||||
or else across a_import_id_list as ic some ic.item.same_string ({CMS_BLOG_NODE_TYPE}.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"import any node", "import " + l_node_type.name>>) and then
|
||||
attached blog_api as l_blog_api
|
||||
then
|
||||
p := a_import_ctx.location.extended ("nodes").extended (l_node_type.name)
|
||||
create d.make_with_path (p)
|
||||
if d.exists and then d.is_readable then
|
||||
a_import_ctx.log ("Importing [" + l_node_type.name + "] items ..")
|
||||
across
|
||||
d.entries as ic
|
||||
loop
|
||||
if attached ic.item.extension as ext and then ext.same_string_general ("json") then
|
||||
create f.make_with_path (p.extended_path (ic.item))
|
||||
if f.exists and then f.is_access_readable then
|
||||
f.open_read
|
||||
from
|
||||
create s.make (0)
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_stream (1_024)
|
||||
s.append (f.last_string)
|
||||
end
|
||||
f.close
|
||||
create jp.make_with_string (s)
|
||||
jp.parse_content
|
||||
if jp.is_valid and then attached jp.parsed_json_object as j then
|
||||
if
|
||||
attached json_string_item (j, "type") as l_type and then
|
||||
l_type.same_string_general (l_node_type.name)
|
||||
then
|
||||
if attached json_to_node_blog (l_node_type, j, l_node_api) as l_blog then
|
||||
if l_blog.is_published then
|
||||
if attached l_blog.author as l_author then
|
||||
if
|
||||
attached l_blog_api.blogs_by_title (l_author, l_blog.title) as l_blogs and then
|
||||
not l_blogs.is_empty
|
||||
then
|
||||
-- Blog Already exists!
|
||||
-- FIXME/TODO
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (already exists for user #" + l_author.id.out + ")!")
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" imported for user #" + l_author.id.out + ".")
|
||||
l_blog_api.save_blog (l_blog)
|
||||
if attached {CMS_LOCAL_LINK} l_blog.link as l_link then
|
||||
loc := l_node_api.node_path (l_blog)
|
||||
if not l_link.location.starts_with_general ("node/") then
|
||||
l_blog_api.cms_api.set_path_alias (loc, l_link.location, False)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Author is unknown!)")
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Status is Not Published!)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_to_node_blog (a_node_type: CMS_BLOG_NODE_TYPE; j: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_BLOG
|
||||
do
|
||||
if attached json_to_node (a_node_type, j, a_node_api) as l_node then
|
||||
Result := a_node_type.new_node (l_node)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -9,11 +9,7 @@ class
|
||||
inherit
|
||||
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_BLOG]
|
||||
redefine
|
||||
content_type,
|
||||
populate_form,
|
||||
update_node,
|
||||
new_node,
|
||||
append_content_as_html_to
|
||||
content_type
|
||||
end
|
||||
|
||||
create
|
||||
@@ -24,76 +20,4 @@ feature -- Access
|
||||
content_type: CMS_BLOG_NODE_TYPE
|
||||
-- Associated content type.
|
||||
|
||||
feature -- form
|
||||
|
||||
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
|
||||
local
|
||||
ti: WSF_FORM_TEXT_INPUT
|
||||
s: STRING_32
|
||||
do
|
||||
Precursor (response, f, a_node)
|
||||
create ti.make ("tags")
|
||||
ti.set_label ("Tags")
|
||||
ti.set_size (70)
|
||||
if
|
||||
a_node /= Void and then
|
||||
attached {CMS_BLOG} a_node as a_blog and then
|
||||
attached a_blog.tags as l_tags
|
||||
then
|
||||
create s.make_empty
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
if not s.is_empty then
|
||||
s.append_character (',')
|
||||
end
|
||||
s.append (ic.item)
|
||||
end
|
||||
ti.set_text_value (s)
|
||||
end
|
||||
ti.set_is_required (False)
|
||||
f.extend (ti)
|
||||
end
|
||||
|
||||
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
|
||||
do
|
||||
Precursor (response, fd, a_node)
|
||||
if attached fd.string_item ("tags") as l_tags then
|
||||
if attached {CMS_BLOG} a_node as l_blog then
|
||||
l_blog.set_tags_from_string (l_tags)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): like content_type.new_node
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := Precursor (response, fd, a_node)
|
||||
if attached fd.string_item ("tags") as l_tags then
|
||||
Result.set_tags_from_string (l_tags)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Output
|
||||
|
||||
append_content_as_html_to (a_node: CMS_BLOG; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor (a_node, is_teaser, a_output, a_response)
|
||||
|
||||
if attached {CMS_BLOG} a_node as l_blog_post then
|
||||
if attached l_blog_post.tags as l_tags then
|
||||
a_output.append ("<div><strong>Tags:</strong> ")
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
a_output.append ("<span class=%"tag%">")
|
||||
a_output.append (cms_api.html_encoded (ic.item))
|
||||
a_output.append ("</span> ")
|
||||
end
|
||||
a_output.append ("</div>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
note
|
||||
description: "Storage extension for Blog nodes."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_NODE_STORAGE_SQL_BLOG_EXTENSION
|
||||
|
||||
inherit
|
||||
CMS_NODE_STORAGE_EXTENSION [CMS_BLOG]
|
||||
|
||||
CMS_PROXY_STORAGE_SQL
|
||||
rename
|
||||
make as make_proxy,
|
||||
sql_storage as node_storage
|
||||
redefine
|
||||
node_storage
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_node_api: CMS_NODE_API; a_sql_storage: CMS_NODE_STORAGE_SQL)
|
||||
do
|
||||
set_node_api (a_node_api)
|
||||
make_proxy (a_sql_storage)
|
||||
end
|
||||
|
||||
node_storage: CMS_NODE_STORAGE_SQL
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Access
|
||||
|
||||
content_type: STRING
|
||||
once
|
||||
Result := {CMS_BLOG_NODE_TYPE}.name
|
||||
end
|
||||
|
||||
feature -- Persistence
|
||||
|
||||
store (a_node: CMS_BLOG)
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_new_tags: detachable STRING_32
|
||||
l_previous_tags: detachable STRING_32
|
||||
l_update: BOOLEAN
|
||||
l_has_modif: BOOLEAN
|
||||
do
|
||||
if attached api as l_api then
|
||||
l_api.logger.put_information (generator + ".store", Void)
|
||||
end
|
||||
|
||||
error_handler.reset
|
||||
-- Check existing record, if any.
|
||||
if attached node_data (a_node) as d then
|
||||
l_update := d.revision = a_node.revision
|
||||
l_previous_tags := d.tags
|
||||
end
|
||||
if not has_error then
|
||||
if attached a_node.tags as l_tags and then not l_tags.is_empty then
|
||||
create l_new_tags.make (0)
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
if not l_new_tags.is_empty then
|
||||
l_new_tags.append_character (',')
|
||||
end
|
||||
l_new_tags.append (ic.item)
|
||||
end
|
||||
else
|
||||
l_new_tags := Void
|
||||
end
|
||||
|
||||
l_has_modif := l_has_modif or (l_new_tags /~ l_previous_tags)
|
||||
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
l_parameters.put (a_node.revision, "revision")
|
||||
l_parameters.force (l_new_tags, "tags")
|
||||
|
||||
if l_update then
|
||||
if l_has_modif then
|
||||
sql_modify (sql_update_node_data, l_parameters)
|
||||
end
|
||||
else
|
||||
if l_has_modif then
|
||||
sql_insert (sql_insert_node_data, l_parameters)
|
||||
else
|
||||
-- no page data, means everything is empty.
|
||||
-- FOR NOW: always record row
|
||||
-- sql_change (sql_insert_node_data, l_parameters)
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
end
|
||||
|
||||
load (a_node: CMS_BLOG)
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_tags: READABLE_STRING_32
|
||||
do
|
||||
if attached node_data (a_node) as d then
|
||||
l_tags := d.tags
|
||||
a_node.set_tags_from_string (l_tags)
|
||||
end
|
||||
end
|
||||
|
||||
delete_node (a_node: CMS_BLOG)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [ANY]
|
||||
do
|
||||
if a_node.has_id then
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_modify (sql_delete_node_data, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; tags: READABLE_STRING_32]
|
||||
-- Node extension data for node `a_node' as tuple.
|
||||
local
|
||||
l_parameters: STRING_TABLE [ANY]
|
||||
l_rev: INTEGER_64
|
||||
l_tags: detachable READABLE_STRING_32
|
||||
n: INTEGER
|
||||
do
|
||||
error_handler.reset
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
l_parameters.put (a_node.revision, "revision")
|
||||
sql_query (sql_select_node_data, l_parameters)
|
||||
if not has_error then
|
||||
if not sql_after then
|
||||
-- nid, revision, tags
|
||||
l_rev := sql_read_integer_64 (2)
|
||||
l_tags := sql_read_string_32 (3)
|
||||
if l_tags /= Void then
|
||||
Result := [l_rev, l_tags]
|
||||
end
|
||||
sql_forth
|
||||
if not sql_after then
|
||||
check unique_data: n = 0 end
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
ensure
|
||||
accepted_revision: Result /= Void implies Result.revision <= a_node.revision
|
||||
end
|
||||
|
||||
feature -- SQL
|
||||
|
||||
sql_select_node_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid=:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;"
|
||||
sql_insert_node_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
|
||||
sql_update_node_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
|
||||
sql_delete_node_data: STRING = "DELETE FROM blog_post_nodes WHERE nid=:nid;"
|
||||
|
||||
end
|
||||
@@ -44,4 +44,11 @@ feature -- Access
|
||||
deferred
|
||||
end
|
||||
|
||||
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of blogs from `a_user' with title `a_title` and ordered by creation date.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -44,4 +44,10 @@ feature -- Access
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
|
||||
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of blogs from `a_user' with title `a_title` and ordered by creation date.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -105,7 +105,7 @@ feature -- Access
|
||||
write_information_log (generator + ".blogs_from_user_limited")
|
||||
|
||||
from
|
||||
create l_parameters.make (2)
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_limit, "limit")
|
||||
l_parameters.put (a_offset, "offset")
|
||||
l_parameters.put (a_user.id, "user")
|
||||
@@ -122,6 +122,33 @@ feature -- Access
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
blogs_from_user_with_title (a_user: CMS_USER; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".blogs_from_user_with_title")
|
||||
|
||||
from
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_user.id, "user")
|
||||
l_parameters.put (a_title, "title")
|
||||
sql_query (sql_blogs_from_user_with_title, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature {NONE} -- Queries
|
||||
|
||||
sql_select_blog_count: STRING = "SELECT count(*) FROM nodes WHERE status != -1 AND type = %"blog%";"
|
||||
@@ -136,10 +163,12 @@ feature {NONE} -- Queries
|
||||
-- SQL Query to retrieve all nodes that are from the type "blog" ordered by descending creation date.
|
||||
|
||||
sql_blogs_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" ORDER BY created DESC LIMIT :limit OFFSET :offset ;"
|
||||
--- SQL Query to retrieve all node of type "blog" limited by limit and starting at offset
|
||||
--- SQL Query to retrieve all nodes of type "blog" limited by limit and starting at offset
|
||||
|
||||
sql_blogs_from_user_limited: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user ORDER BY created DESC LIMIT :limit OFFSET :offset ;"
|
||||
--- SQL Query to retrieve all node of type "blog" from author with id limited by limit + offset
|
||||
--- SQL Query to retrieve all nodes of type "blog" from author with id limited by limit + offset
|
||||
|
||||
sql_blogs_from_user_with_title: STRING = "SELECT * FROM nodes WHERE status != -1 AND type = %"blog%" AND author = :user AND title = :title ORDER BY created DESC;"
|
||||
--- SQL Query to retrieve all nodes of type "blog" from author with title .
|
||||
|
||||
end
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
CREATE TABLE blog_post_nodes(
|
||||
`nid` INTEGER NOT NULL CHECK("nid">=0),
|
||||
`revision` INTEGER NOT NULL,
|
||||
`tags` VARCHAR(255),
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
@@ -10,9 +10,6 @@ class
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
@@ -28,30 +25,6 @@ feature {NONE} -- Initialization
|
||||
-- error_handler.add_synchronization (a_node_storage.error_handler)
|
||||
end
|
||||
|
||||
initialize
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor
|
||||
initialize_node_types
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
-- Initialize content type system.
|
||||
local
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
-- Initialize node content types.
|
||||
create ct
|
||||
--| For now, add all available formats to content type `ct'.
|
||||
across
|
||||
cms_api.formats as ic
|
||||
loop
|
||||
ct.extend_format (ic.item)
|
||||
end
|
||||
add_node_type (ct)
|
||||
add_node_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct, Current))
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
node_storage: CMS_NODE_STORAGE_I
|
||||
@@ -301,45 +274,9 @@ feature -- Access: Node
|
||||
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
|
||||
end
|
||||
|
||||
feature -- Access: page/book outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
do
|
||||
Result := node_storage.children (a_node)
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Potential parent nodes for node `a_node'.
|
||||
-- Ensure no cycle exists.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
across node_storage.available_parents_for_node (a_node) as ic loop
|
||||
check distinct: not a_node.same_node (ic.item) end
|
||||
if not is_node_a_parent_of (a_node, ic.item) then
|
||||
Result.force (ic.item)
|
||||
end
|
||||
end
|
||||
ensure
|
||||
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
|
||||
end
|
||||
|
||||
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
|
||||
-- Is `a_node' a direct or indirect parent of node `a_child'?
|
||||
require
|
||||
distinct_nodes: not a_node.same_node (a_child)
|
||||
do
|
||||
if
|
||||
attached {CMS_PAGE} full_node (a_child) as l_child_page and then
|
||||
attached l_child_page.parent as l_parent
|
||||
then
|
||||
if l_parent.same_node (a_node) then
|
||||
Result := True
|
||||
else
|
||||
Result := is_node_a_parent_of (a_node, l_parent)
|
||||
end
|
||||
end
|
||||
Result := node_storage.nodes_of_type_with_title (a_node_type, a_title)
|
||||
end
|
||||
|
||||
feature -- Permission Scope: Node
|
||||
|
||||
@@ -27,9 +27,9 @@ inherit
|
||||
|
||||
CMS_TAXONOMY_HOOK
|
||||
|
||||
CMS_HOOK_EXPORT
|
||||
-- CMS_HOOK_EXPORT
|
||||
|
||||
CMS_EXPORT_NODE_UTILITIES
|
||||
-- CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
@@ -60,8 +60,6 @@ feature {CMS_API} -- Module Initialization
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
p1,p2: CMS_PAGE
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
l_node_api: like node_api
|
||||
l_node_storage: CMS_NODE_STORAGE_I
|
||||
do
|
||||
@@ -78,34 +76,6 @@ feature {CMS_API} -- Module Initialization
|
||||
-- Node API initialization
|
||||
create l_node_api.make_with_storage (a_api, l_node_storage)
|
||||
node_api := l_node_api
|
||||
|
||||
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
|
||||
-- For now, we only have extension based on SQL statement.
|
||||
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
|
||||
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
||||
|
||||
-- FIXME: the following code is mostly for test purpose/initialization, remove later
|
||||
if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then
|
||||
if attached a_api.user_api.user_by_id (1) as u then
|
||||
create ct
|
||||
p1 := ct.new_node (Void)
|
||||
p1.set_title ("Welcome")
|
||||
p1.set_content ("Welcome, you are using the ROC Eiffel CMS", Void, Void) -- Use default format
|
||||
p1.set_author (u)
|
||||
l_sql_node_storage.save_node (p1)
|
||||
|
||||
p2 := ct.new_node (Void)
|
||||
p2.set_title ("A new page example")
|
||||
p2.set_content ("This is the content of a page", Void, Void) -- Use default format
|
||||
p2.set_author (u)
|
||||
p2.set_parent (p1)
|
||||
l_sql_node_storage.save_node (p2)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- FIXME: maybe provide a default solution based on file system, when no SQL storage is available.
|
||||
-- IDEA: we could also have generic extension to node system, that handle generic addition field.
|
||||
end
|
||||
ensure then
|
||||
node_api_set: node_api /= Void
|
||||
end
|
||||
@@ -117,8 +87,7 @@ feature {CMS_API} -- Module management
|
||||
do
|
||||
Result := Precursor (a_api)
|
||||
if Result and attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
Result := l_sql_storage.sql_table_exists ("nodes") and
|
||||
l_sql_storage.sql_table_exists ("page_nodes")
|
||||
Result := l_sql_storage.sql_table_exists ("nodes")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -247,7 +216,7 @@ feature -- Hooks
|
||||
a_hooks.subscribe_to_menu_system_alter_hook (Current)
|
||||
a_hooks.subscribe_to_block_hook (Current)
|
||||
a_hooks.subscribe_to_response_alter_hook (Current)
|
||||
a_hooks.subscribe_to_export_hook (Current)
|
||||
-- a_hooks.subscribe_to_export_hook (Current)
|
||||
|
||||
-- Module specific hook, if available.
|
||||
a_hooks.subscribe_to_hook (Current, {CMS_RECENT_CHANGES_HOOK})
|
||||
@@ -427,94 +396,4 @@ feature -- Hooks
|
||||
end
|
||||
end
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
local
|
||||
l_node_type: CMS_CONTENT_TYPE
|
||||
n: CMS_NODE
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
lst: LIST [CMS_NODE]
|
||||
do
|
||||
if attached node_api as l_node_api then
|
||||
across
|
||||
l_node_api.node_types as types_ic
|
||||
loop
|
||||
l_node_type := types_ic.item
|
||||
if
|
||||
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
|
||||
l_node_type.name.same_string_general ("page") and then
|
||||
( a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
|
||||
-- For now, handle only page from this node module.
|
||||
lst := l_node_api.nodes_of_type (l_node_type)
|
||||
a_export_parameters.log ("Exporting " + lst.count.out + " nodes of type " + l_node_type.name)
|
||||
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
n := l_node_api.full_node (ic.item)
|
||||
a_export_parameters.log (l_node_type.name + " #" + n.id.out + " rev=" + n.revision.out)
|
||||
create f.make_with_path (p.extended (n.id.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
if attached {CMS_PAGE} n as l_page then
|
||||
f.put_string (json_to_string (page_node_to_json (l_page)))
|
||||
else
|
||||
f.put_string (json_to_string (node_to_json (n)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
-- Revisions.
|
||||
if attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1 then
|
||||
a_export_parameters.log (l_node_type.name + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name).extended (n.id.out)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
l_revisions as revs_ic
|
||||
loop
|
||||
n := revs_ic.item
|
||||
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
if attached {CMS_PAGE} n as l_page then
|
||||
f.put_string (json_to_string (page_node_to_json (l_page)))
|
||||
else
|
||||
f.put_string (json_to_string (node_to_json (n)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
page_node_to_json (a_page: CMS_PAGE): JSON_OBJECT
|
||||
local
|
||||
j: JSON_OBJECT
|
||||
do
|
||||
Result := node_to_json (a_page)
|
||||
if attached a_page.parent as l_parent_page then
|
||||
create j.make_empty
|
||||
j.put_string (l_parent_page.content_type, "type")
|
||||
j.put_integer (l_parent_page.id, "nid")
|
||||
Result.put (j, "parent")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11,11 +11,15 @@ class
|
||||
inherit
|
||||
CMS_EXPORT_JSON_UTILITIES
|
||||
|
||||
CMS_API_ACCESS
|
||||
|
||||
feature -- Access
|
||||
|
||||
node_to_json (n: CMS_NODE): JSON_OBJECT
|
||||
node_to_json (n: CMS_NODE; a_node_api: CMS_NODE_API): JSON_OBJECT
|
||||
local
|
||||
jo,j_author: JSON_OBJECT
|
||||
jo: JSON_OBJECT
|
||||
ja: JSON_ARRAY
|
||||
j, jterm: JSON_OBJECT
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.put_string (n.content_type, "type")
|
||||
@@ -27,10 +31,7 @@ feature -- Access
|
||||
put_date_into_json (n.publication_date, "publication_date", Result)
|
||||
Result.put_integer (n.status, "status")
|
||||
if attached n.author as u then
|
||||
create j_author.make
|
||||
j_author.put_integer (u.id, "uid")
|
||||
j_author.put_string (u.name, "name")
|
||||
Result.put (j_author, "author")
|
||||
Result.put (user_to_json (u), "author")
|
||||
end
|
||||
create jo.make_empty
|
||||
if attached n.format as l_format then
|
||||
@@ -44,12 +45,41 @@ feature -- Access
|
||||
end
|
||||
Result.put (jo, "data")
|
||||
if attached n.link as lnk then
|
||||
create jo.make_empty
|
||||
jo.put_string (lnk.title, "title")
|
||||
jo.put_string (lnk.location, "location")
|
||||
jo.put_integer (lnk.weight, "weight")
|
||||
Result.put (jo, "link")
|
||||
Result.put (link_to_json (lnk), "link")
|
||||
end
|
||||
|
||||
if attached {CMS_TAXONOMY_API} a_node_api.cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then
|
||||
if
|
||||
attached l_taxonomy_api.terms_of_content (n, Void) as l_tags and then
|
||||
not l_tags.is_empty
|
||||
then
|
||||
create ja.make (l_tags.count)
|
||||
across
|
||||
l_tags as ic
|
||||
loop
|
||||
create jterm.make_with_capacity (2)
|
||||
jterm.put_integer (ic.item.id, "id")
|
||||
jterm.put_string (ic.item.text, "text")
|
||||
ja.extend (jterm)
|
||||
end
|
||||
Result.put (ja, "tags")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
user_to_json (u: CMS_USER): JSON_OBJECT
|
||||
do
|
||||
create Result.make
|
||||
Result.put_integer (u.id, "uid")
|
||||
Result.put_string (u.name, "name")
|
||||
end
|
||||
|
||||
link_to_json (lnk: CMS_LOCAL_LINK): JSON_OBJECT
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.put_string (lnk.title, "title")
|
||||
Result.put_string (lnk.location, "location")
|
||||
Result.put_integer (lnk.weight, "weight")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,8 @@ class
|
||||
|
||||
inherit
|
||||
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE]
|
||||
rename
|
||||
make as make_for_node
|
||||
redefine
|
||||
content_type,
|
||||
append_content_as_html_to,
|
||||
@@ -19,11 +21,22 @@ inherit
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_type: like content_type; a_page_api: CMS_PAGE_API)
|
||||
do
|
||||
page_api := a_page_api
|
||||
make_for_node (a_type, a_page_api.node_api)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
content_type: CMS_PAGE_NODE_TYPE
|
||||
-- Associated content type.
|
||||
|
||||
page_api: CMS_PAGE_API
|
||||
-- Associated page API.
|
||||
|
||||
feature -- Forms ...
|
||||
|
||||
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
|
||||
@@ -122,7 +135,7 @@ feature -- Forms ...
|
||||
if
|
||||
nid > 0 and then
|
||||
attached l_node_api.node (nid) as l_node and then
|
||||
l_node_api.is_node_a_parent_of (l_node, l_parent_node)
|
||||
page_api.is_node_a_parent_of (l_node, l_parent_node)
|
||||
then
|
||||
fd.report_invalid_field ("select_parent_node", "Invalid parent due to cycle (node #" + nid.out + " is already a parent of node #" + l_parent_id.out)
|
||||
end
|
||||
@@ -144,7 +157,7 @@ feature -- Output
|
||||
lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
Precursor (a_node, is_teaser, a_output, a_response)
|
||||
|
||||
|
||||
if not is_teaser then
|
||||
l_node_api := node_api
|
||||
if
|
||||
@@ -170,7 +183,7 @@ feature -- Output
|
||||
a_output.append (a_response.link (l_parent_node.title, l_node_api.node_path (l_parent_node), Void))
|
||||
a_output.append ("</li>")
|
||||
end
|
||||
if attached l_node_api.children (a_node) as l_children then
|
||||
if attached page_api.children (a_node) as l_children then
|
||||
across
|
||||
l_children as ic
|
||||
loop
|
||||
|
||||
@@ -30,7 +30,15 @@ feature -- Execution
|
||||
location.ends_with_general ("/edit") and then
|
||||
node_api.has_permission_for_action_on_node ("edit", l_node, user)
|
||||
then
|
||||
edit_node (l_node, l_type, b)
|
||||
if
|
||||
attached {WSF_STRING} request.query_parameter ("revision") as p_rev and then
|
||||
p_rev.value.is_integer_64 and then
|
||||
attached node_api.revision_node (l_node.id, p_rev.value.to_integer_64) as l_rev_node
|
||||
then
|
||||
edit_node (l_rev_node, l_type, l_rev_node.revision < l_node.revision, b)
|
||||
else
|
||||
edit_node (l_node, l_type, False, b)
|
||||
end
|
||||
elseif
|
||||
location.ends_with_general ("/delete") and then
|
||||
node_api.has_permission_for_action_on_node ("delete", l_node, user)
|
||||
@@ -118,12 +126,15 @@ feature {NONE} -- Create a new node
|
||||
end
|
||||
|
||||
|
||||
edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
|
||||
edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; is_old_revision: BOOLEAN; b: STRING_8)
|
||||
local
|
||||
f: like new_edit_form
|
||||
fd: detachable WSF_FORM_DATA
|
||||
do
|
||||
f := new_edit_form (A_node, url (location, Void), "edit-" + a_type.name, a_type)
|
||||
if is_old_revision then
|
||||
add_warning_message ("You are editing old revision #" + a_node.revision.out + " !")
|
||||
end
|
||||
api.hooks.invoke_form_alter (f, fd, Current)
|
||||
if request.is_post_request_method then
|
||||
f.validation_actions.extend (agent edit_form_validate (?, b))
|
||||
@@ -135,6 +146,7 @@ feature {NONE} -- Create a new node
|
||||
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
|
||||
add_to_menu (create {CMS_LOCAL_LINK}.make ("Revisions", node_api.node_path (a_node) + "/revision"), primary_tabs)
|
||||
end
|
||||
|
||||
if attached redirection as l_location then
|
||||
|
||||
@@ -122,6 +122,7 @@ feature -- HTTP Methods
|
||||
if
|
||||
l_node /= Void and then
|
||||
l_rev > 0 and then
|
||||
l_rev < l_node.revision and then
|
||||
node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user)
|
||||
then
|
||||
l_node := node_api.revision_node (l_nid, l_rev)
|
||||
@@ -322,7 +323,12 @@ feature {NONE} -- Trash:Restore
|
||||
b.append ("<a href=%"")
|
||||
b.append (r.url (node_api.node_path (n) + "?revision=" + n.revision.out, Void))
|
||||
b.append ("%">")
|
||||
|
||||
if n.revision = l_node.revision then
|
||||
b.append ("Current ")
|
||||
else
|
||||
b.append ("Revision")
|
||||
end
|
||||
b.append (" #")
|
||||
b.append (n.revision.out)
|
||||
b.append (" : ")
|
||||
b.append (n.modification_date.out)
|
||||
@@ -331,6 +337,11 @@ feature {NONE} -- Trash:Restore
|
||||
b.append (" by ")
|
||||
b.append (r.link (l_author.name, "user/" + l_author.id.out, Void))
|
||||
end
|
||||
if node_api.has_permission_for_action_on_node ("edit revisions", l_node, api.user) then
|
||||
b.append (" (<a href=%"")
|
||||
b.append (r.url (node_api.node_path (n) + "/edit?revision=" + n.revision.out, Void))
|
||||
b.append ("%">edit</a>)")
|
||||
end
|
||||
b.append ("</li>")
|
||||
end
|
||||
b.append ("</ul>")
|
||||
|
||||
@@ -38,6 +38,7 @@ feature -- Execution
|
||||
local
|
||||
nid: INTEGER_64
|
||||
l_node: like node
|
||||
l_title: STRING_32
|
||||
do
|
||||
l_node := node
|
||||
if l_node = Void then
|
||||
@@ -62,8 +63,11 @@ feature -- Execution
|
||||
if revision > 0 then
|
||||
add_warning_message ("The revisions let you track differences between multiple versions of a post.")
|
||||
end
|
||||
if l_node /= Void and revision > 0 then
|
||||
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
|
||||
if l_node /= Void and then revision > 0 and then revision < l_node.revision then
|
||||
create l_title.make_from_string_general ("Revision #" + revision.out + " of %"")
|
||||
l_title.append (l_node.title)
|
||||
l_title.append_character ('"')
|
||||
set_title (l_title)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
106
modules/node/import/cms_import_node_utilities.e
Normal file
106
modules/node/import/cms_import_node_utilities.e
Normal file
@@ -0,0 +1,106 @@
|
||||
note
|
||||
description: "[
|
||||
Routines usefull during node exportation (see {CMS_HOOK_IMPORT}).
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_IMPORT_NODE_UTILITIES
|
||||
|
||||
inherit
|
||||
CMS_IMPORT_JSON_UTILITIES
|
||||
|
||||
CMS_API_ACCESS
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL; a_node_api: CMS_NODE_API): detachable CMS_USER
|
||||
-- CMS user named `a_name`.
|
||||
do
|
||||
Result := a_node_api.cms_api.user_api.user_by_name (a_name)
|
||||
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
|
||||
local
|
||||
l_node: CMS_PARTIAL_NODE
|
||||
do
|
||||
if
|
||||
attached json_string_item (j, "title") as l_title
|
||||
then
|
||||
create l_node.make_empty (a_node_type.name)
|
||||
l_node.set_title (l_title)
|
||||
Result := l_node
|
||||
if attached json_date_item (j, "creation_date") as dt then
|
||||
l_node.set_creation_date (dt)
|
||||
end
|
||||
if attached json_date_item (j, "modification_date") as dt then
|
||||
l_node.set_modification_date (dt)
|
||||
end
|
||||
if attached json_date_item (j, "publication_date") as dt then
|
||||
l_node.set_publication_date (dt)
|
||||
end
|
||||
if attached json_integer_item (j, "status") as l_status then
|
||||
inspect l_status
|
||||
when {CMS_NODE_API}.published then
|
||||
l_node.mark_published
|
||||
when {CMS_NODE_API}.not_published then
|
||||
l_node.mark_not_published
|
||||
when {CMS_NODE_API}.trashed then
|
||||
l_node.mark_trashed
|
||||
else
|
||||
end
|
||||
end
|
||||
if attached {JSON_OBJECT} j.item ("author") as j_author then
|
||||
l_node.set_author (json_to_user (j_author, a_node_api))
|
||||
end
|
||||
if attached {JSON_OBJECT} j.item ("data") as j_data then
|
||||
l_node.set_all_content (json_string_item (j_data, "content"), json_string_item (j_data, "summary"), json_string_8_item (j_data, "format"))
|
||||
end
|
||||
if
|
||||
attached {JSON_OBJECT} j.item ("link") as j_link and then
|
||||
attached json_to_local_link (j_link) as lnk
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_to_user (j_user: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_USER
|
||||
do
|
||||
if
|
||||
attached json_string_item (j_user, "name") as l_author_name
|
||||
then
|
||||
Result := user_by_name (l_author_name, a_node_api)
|
||||
end
|
||||
end
|
||||
|
||||
json_to_local_link (j_link: JSON_OBJECT): detachable CMS_LOCAL_LINK
|
||||
do
|
||||
if
|
||||
attached json_string_8_item (j_link, "location") as loc
|
||||
then
|
||||
create Result.make (json_string_item (j_link, "title"), loc)
|
||||
Result.set_weight (json_integer_item (j_link, "weight").to_integer_32)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -140,19 +140,22 @@ feature -- Access
|
||||
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
deferred
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Given the node `a_node', return the list of possible parent nodes id
|
||||
deferred
|
||||
ensure
|
||||
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of nodes of type `a_node_type' with title `a_title`.
|
||||
--| Redefine to optimize!
|
||||
do
|
||||
Result := nodes_of_type (a_node_type)
|
||||
from
|
||||
Result.start
|
||||
until
|
||||
Result.after
|
||||
loop
|
||||
if a_title.same_string (Result.item.title) then
|
||||
Result.forth
|
||||
else
|
||||
Result.remove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change: Node
|
||||
|
||||
@@ -75,19 +75,6 @@ feature -- Access: node
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
|
||||
feature -- Node
|
||||
|
||||
new_node (a_node: CMS_NODE)
|
||||
|
||||
@@ -13,7 +13,8 @@ inherit
|
||||
|
||||
CMS_NODE_STORAGE_I
|
||||
redefine
|
||||
nodes_of_type
|
||||
nodes_of_type,
|
||||
nodes_of_type_with_title
|
||||
end
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
@@ -277,53 +278,27 @@ feature -- Access
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature -- Access: outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]
|
||||
-- List of nodes of type `a_node_type' with title `a_title`.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".children")
|
||||
write_information_log (generator + ".nodes_of_type_with_title")
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_node_type.name, "node_type")
|
||||
l_parameters.put (a_title, "title")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_children_of_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): 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 + ".available_parents_for_node")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_available_parents_for_node, l_parameters)
|
||||
sql_query (sql_select_nodes_of_type_with_title, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
check expected_node_type: l_node.content_type.same_string (a_node_type.name) end
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
@@ -377,7 +352,7 @@ feature -- Change: Node
|
||||
sql_modify (sql_delete_node, l_parameters)
|
||||
sql_finalize
|
||||
|
||||
-- we remove node_revisions and pages.
|
||||
-- we remove node_revisions and potential extended nodes.
|
||||
-- Check: maybe we need a transaction.
|
||||
sql_modify (sql_delete_node_revisions, l_parameters)
|
||||
sql_finalize
|
||||
@@ -516,6 +491,10 @@ feature {NONE} -- Queries
|
||||
-- SQL Query to retrieve all nodes of type :node_type.
|
||||
--| note: {CMS_NODE_API}.trashed = -1
|
||||
|
||||
sql_select_nodes_of_type_with_title: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE status != -1 AND type=:node_type AND title=:title;"
|
||||
-- SQL Query to retrieve all nodes of type :node_type with title :title.
|
||||
--| note: {CMS_NODE_API}.trashed = -1
|
||||
|
||||
sql_select_node_revisions: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision < :revision ORDER BY node_revisions.revision DESC;"
|
||||
-- SQL query to get node revisions (missing the latest one).
|
||||
|
||||
@@ -557,22 +536,8 @@ feature {NONE} -- Queries
|
||||
Sql_last_insert_node_revision: STRING = "SELECT MAX(revision) FROM node_revisions;"
|
||||
Sql_last_insert_node_revision_for_nid: STRING = "SELECT MAX(revision) FROM node_revisions WHERE nid=:nid;"
|
||||
|
||||
sql_select_available_parents_for_node : STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
|
||||
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_select_children_of_node: STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
|
||||
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;"
|
||||
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fetch_node: detachable CMS_PARTIAL_NODE
|
||||
|
||||
@@ -28,10 +28,3 @@ CREATE TABLE node_revisions (
|
||||
CONSTRAINT Unique_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
|
||||
8
modules/node/site/scripts/page.sql
Normal file
8
modules/node/site/scripts/page.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER NOT NULL,
|
||||
`revision` INTEGER NOT NULL,
|
||||
`parent` INTEGER,
|
||||
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
|
||||
);
|
||||
|
||||
172
modules/node/submodules/page/cms_page_api.e
Normal file
172
modules/node/submodules/page/cms_page_api.e
Normal file
@@ -0,0 +1,172 @@
|
||||
note
|
||||
description: "API to handle nodes of type page. Extends the node API."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_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
|
||||
|
||||
---- l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
||||
|
||||
-- Create the node storage for type blog
|
||||
if attached storage.as_sql_storage as l_storage_sql then
|
||||
create {CMS_PAGE_STORAGE_SQL} page_storage.make (l_storage_sql)
|
||||
else
|
||||
create {CMS_PAGE_STORAGE_NULL} page_storage.make
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
end
|
||||
|
||||
initialize_node_types
|
||||
-- Initialize content type system.
|
||||
local
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
-- Initialize node content types.
|
||||
create ct
|
||||
page_content_type := ct
|
||||
--| For now, add all available formats to content type `ct'.
|
||||
across
|
||||
cms_api.formats as ic
|
||||
loop
|
||||
ct.extend_format (ic.item)
|
||||
end
|
||||
node_api.add_node_type (ct)
|
||||
node_api.add_node_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct, Current))
|
||||
end
|
||||
|
||||
feature {CMS_API_ACCESS, CMS_MODULE, CMS_API} -- Restricted access
|
||||
|
||||
node_api: CMS_NODE_API
|
||||
|
||||
page_content_type: CMS_PAGE_NODE_TYPE
|
||||
|
||||
feature -- Access
|
||||
|
||||
pages: LIST [CMS_PAGE]
|
||||
-- All pages.
|
||||
do
|
||||
Result := nodes_to_pages (node_api.nodes_of_type (page_content_type))
|
||||
end
|
||||
|
||||
pages_with_title (a_title: READABLE_STRING_GENERAL): LIST [CMS_PAGE]
|
||||
-- List of pages with title `a_title`.
|
||||
do
|
||||
Result := nodes_to_pages (node_api.nodes_of_type_with_title (page_content_type, a_title))
|
||||
end
|
||||
|
||||
feature -- Access: page/book outline
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
do
|
||||
Result := page_storage.children (a_node)
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Potential parent nodes for node `a_node'.
|
||||
-- Ensure no cycle exists.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
across page_storage.available_parents_for_node (a_node) as ic loop
|
||||
check distinct: not a_node.same_node (ic.item) end
|
||||
if not is_node_a_parent_of (a_node, ic.item) then
|
||||
Result.force (ic.item)
|
||||
end
|
||||
end
|
||||
ensure
|
||||
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
|
||||
end
|
||||
|
||||
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
|
||||
-- Is `a_node' a direct or indirect parent of node `a_child'?
|
||||
require
|
||||
distinct_nodes: not a_node.same_node (a_child)
|
||||
do
|
||||
if
|
||||
attached {CMS_PAGE} node_api.full_node (a_child) as l_child_page and then
|
||||
attached l_child_page.parent as l_parent
|
||||
then
|
||||
if l_parent.same_node (a_node) then
|
||||
Result := True
|
||||
else
|
||||
Result := is_node_a_parent_of (a_node, l_parent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
full_page_node (a_page: CMS_PAGE): CMS_PAGE
|
||||
-- If `a_page' is partial, return the full page node from `a_page',
|
||||
-- otherwise return directly `a_page'.
|
||||
require
|
||||
a_page_set: a_page /= Void
|
||||
do
|
||||
if attached {CMS_PAGE} node_api.full_node (a_page) as l_full_page then
|
||||
Result := l_full_page
|
||||
else
|
||||
Result := a_page
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Commit
|
||||
|
||||
save_page (a_page: CMS_PAGE)
|
||||
do
|
||||
node_api.save_node (a_page)
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
page_storage: CMS_PAGE_STORAGE_I
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
nodes_to_pages (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_PAGE]
|
||||
-- Convert list of nodes into a list of page when possible.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_PAGE]} Result.make (a_nodes.count)
|
||||
|
||||
if attached node_api as l_node_api then
|
||||
across
|
||||
a_nodes as ic
|
||||
loop
|
||||
if attached {CMS_PAGE} l_node_api.full_node (ic.item) as l_page then
|
||||
Result.force (l_page)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
411
modules/node/submodules/page/cms_page_module.e
Normal file
411
modules/node/submodules/page/cms_page_module.e
Normal file
@@ -0,0 +1,411 @@
|
||||
note
|
||||
description: "Node page implementation."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_MODULE
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as page_api
|
||||
redefine
|
||||
setup_hooks,
|
||||
initialize,
|
||||
install, is_installed,
|
||||
page_api
|
||||
end
|
||||
|
||||
CMS_HOOK_EXPORT
|
||||
|
||||
CMS_HOOK_IMPORT
|
||||
|
||||
CMS_EXPORT_NODE_UTILITIES
|
||||
|
||||
CMS_IMPORT_NODE_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Page service"
|
||||
package := "content"
|
||||
add_dependency ({CMS_NODE_MODULE})
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "page"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
p1,p2: CMS_PAGE
|
||||
ct: CMS_PAGE_NODE_TYPE
|
||||
do
|
||||
Precursor (a_api)
|
||||
if attached {CMS_NODE_API} a_api.module_api ({CMS_NODE_MODULE}) as l_node_api then
|
||||
node_api := l_node_api
|
||||
|
||||
create page_api.make (a_api, l_node_api)
|
||||
|
||||
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
|
||||
-- For now, we only have extension based on SQL statement.
|
||||
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
|
||||
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_node_api, l_sql_node_storage))
|
||||
|
||||
-- FIXME: the following code is mostly for test purpose/initialization, remove later
|
||||
if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then
|
||||
if attached a_api.user_api.user_by_id (1) as u then
|
||||
create ct
|
||||
p1 := ct.new_node (Void)
|
||||
p1.set_title ("Welcome")
|
||||
p1.set_content ("Welcome, you are using the ROC Eiffel CMS", Void, Void) -- Use default format
|
||||
p1.set_author (u)
|
||||
l_sql_node_storage.save_node (p1)
|
||||
|
||||
p2 := ct.new_node (Void)
|
||||
p2.set_title ("A new page example")
|
||||
p2.set_content ("This is the content of a page", Void, Void) -- Use default format
|
||||
p2.set_author (u)
|
||||
p2.set_parent (p1)
|
||||
l_sql_node_storage.save_node (p2)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- FIXME: maybe provide a default solution based on file system, when no SQL storage is available.
|
||||
-- IDEA: we could also have generic extension to node system, that handle generic addition field.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
is_installed (a_api: CMS_API): BOOLEAN
|
||||
-- Is Current module installed?
|
||||
do
|
||||
Result := Precursor (a_api)
|
||||
if Result and attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
Result := l_sql_storage.sql_table_exists ("page_nodes")
|
||||
end
|
||||
end
|
||||
|
||||
install (a_api: CMS_API)
|
||||
do
|
||||
-- Schema
|
||||
if attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
if attached a_api.module ({CMS_NODE_MODULE}) as l_node_module then
|
||||
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (l_node_module, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
|
||||
end
|
||||
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
|
||||
|
||||
page_api: detachable CMS_PAGE_API
|
||||
-- <Precursor>
|
||||
|
||||
node_api: detachable CMS_NODE_API
|
||||
|
||||
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_export_hook (Current)
|
||||
a_hooks.subscribe_to_import_hook (Current)
|
||||
end
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_ctx: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
local
|
||||
n: CMS_PAGE
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
lst: LIST [CMS_NODE]
|
||||
do
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached l_node_api.node_type ("page") as l_node_type and then
|
||||
( a_export_id_list = Void
|
||||
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
|
||||
attached page_api as l_page_api
|
||||
then
|
||||
lst := l_page_api.pages
|
||||
a_export_ctx.log ("Exporting " + lst.count.out + " notes of type `" + l_node_type.name + "`.")
|
||||
create d.make_with_path (a_export_ctx.location.extended ("nodes").extended (l_node_type.name))
|
||||
if d.exists then
|
||||
d.recursive_delete
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if attached {CMS_PAGE} ic.item as l_page_node then
|
||||
n := l_page_api.full_page_node (l_page_node)
|
||||
a_export_ctx.log (n.content_type + " #" + n.id.out)
|
||||
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out).appended_with_extension ("json")
|
||||
create d.make_with_path (p.parent)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
|
||||
create f.make_with_path (p)
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (page_node_to_json (n, l_page_api)))
|
||||
f.close
|
||||
end
|
||||
-- Revisions.
|
||||
if
|
||||
attached l_node_api.node_revisions (n) as l_revisions and then
|
||||
l_revisions.count > 1
|
||||
then
|
||||
a_export_ctx.log (n.content_type + " " + l_revisions.count.out + " revisions.")
|
||||
p := a_export_ctx.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
l_revisions as revs_ic
|
||||
loop
|
||||
if attached {CMS_PAGE} revs_ic.item as l_rev_page then
|
||||
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
|
||||
if not f.exists or else f.is_access_writable then
|
||||
f.open_write
|
||||
f.put_string (json_to_string (page_node_to_json (l_rev_page, l_page_api)))
|
||||
end
|
||||
f.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
page_node_to_json (a_page: CMS_PAGE; a_page_api: CMS_PAGE_API): JSON_OBJECT
|
||||
local
|
||||
j: JSON_OBJECT
|
||||
p: CMS_PAGE
|
||||
do
|
||||
Result := node_to_json (a_page, a_page_api.node_api)
|
||||
if attached a_page.parent as l_parent_page then
|
||||
p := a_page_api.full_page_node (l_parent_page)
|
||||
create j.make_empty
|
||||
j.put_string (p.content_type, "type")
|
||||
j.put_string (p.title, "title")
|
||||
j.put_integer (p.id, "nid")
|
||||
if attached p.link as lnk then
|
||||
j.put (link_to_json (lnk), "link")
|
||||
end
|
||||
Result.put (j, "parent")
|
||||
end
|
||||
end
|
||||
|
||||
import_from (a_import_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_import_ctx: CMS_IMPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Import data identified by `a_import_id_list',
|
||||
-- or import all data if `a_import_id_list' is Void.
|
||||
local
|
||||
p: PATH
|
||||
d: DIRECTORY
|
||||
f: PLAIN_TEXT_FILE
|
||||
s: STRING
|
||||
jp: JSON_PARSER
|
||||
loc: 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.
|
||||
do
|
||||
if
|
||||
attached node_api as l_node_api and then
|
||||
attached {CMS_PAGE_NODE_TYPE} l_node_api.node_type ({CMS_PAGE_NODE_TYPE}.name) as l_node_type and then
|
||||
attached page_api as l_page_api and then
|
||||
( a_import_id_list = Void
|
||||
or else across a_import_id_list as ic some ic.item.same_string (l_node_type.name) end
|
||||
)
|
||||
then
|
||||
if
|
||||
a_response.has_permissions (<<"import any node", "import " + l_node_type.name>>)
|
||||
then
|
||||
p := a_import_ctx.location.extended ("nodes").extended (l_node_type.name)
|
||||
create d.make_with_path (p)
|
||||
if d.exists and then d.is_readable then
|
||||
create l_parentable_list.make (0)
|
||||
create l_new_pages.make (0)
|
||||
a_import_ctx.log ("Importing [" + l_node_type.name + "] items ..")
|
||||
across
|
||||
d.entries as ic
|
||||
loop
|
||||
if attached ic.item.extension as ext and then ext.same_string_general ("json") then
|
||||
create f.make_with_path (p.extended_path (ic.item))
|
||||
if f.exists and then f.is_access_readable then
|
||||
f.open_read
|
||||
from
|
||||
create s.make (0)
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_stream (1_024)
|
||||
s.append (f.last_string)
|
||||
end
|
||||
f.close
|
||||
create jp.make_with_string (s)
|
||||
jp.parse_content
|
||||
if jp.is_valid and then attached jp.parsed_json_object as j then
|
||||
if
|
||||
attached json_string_item (j, "type") as l_type and then
|
||||
l_type.same_string_general (l_node_type.name)
|
||||
then
|
||||
if attached json_to_node_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
|
||||
if
|
||||
attached l_page_api.pages_with_title (l_page.title) as l_pages and then
|
||||
not l_pages.is_empty
|
||||
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
|
||||
end
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Author is unknown!)")
|
||||
end
|
||||
else
|
||||
a_import_ctx.log (l_node_type.name + " %"" + f.path.utf_8_name + "%" skipped (Status is Not Published!)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_to_node_page (a_node_type: CMS_PAGE_NODE_TYPE; j: JSON_OBJECT; a_node_api: CMS_NODE_API): detachable CMS_PAGE
|
||||
local
|
||||
p: detachable CMS_PAGE
|
||||
do
|
||||
if attached json_to_node (a_node_type, j, a_node_api) as l_node then
|
||||
Result := a_node_type.new_node (l_node)
|
||||
if
|
||||
attached {JSON_OBJECT} j.item ("parent") as j_parent and then
|
||||
attached json_string_item (j_parent, "title") as l_title
|
||||
then
|
||||
p := a_node_type.new_node_with_title (l_title, Void)
|
||||
if
|
||||
attached {JSON_OBJECT} j_parent.item ("link") as j_link and then
|
||||
attached json_to_local_link (j_link) as l_parent_lnk and then
|
||||
not l_parent_lnk.location.starts_with ("node/")
|
||||
then
|
||||
p.set_link (l_parent_lnk)
|
||||
else
|
||||
-- No link ..
|
||||
end
|
||||
if not Result.is_parent_of (p) then
|
||||
Result.set_parent (p)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_page_parent (a_page: CMS_PAGE; a_parent: CMS_PAGE; a_new_pages: STRING_TABLE [CMS_PAGE])
|
||||
require
|
||||
no_parent_set: a_page.parent = Void
|
||||
local
|
||||
lst: detachable LIST [CMS_PAGE]
|
||||
p: detachable CMS_PAGE
|
||||
do
|
||||
p := a_parent
|
||||
if attached page_api as l_page_api then
|
||||
if attached a_parent.link as l_parent_lnk then
|
||||
lst := l_page_api.pages_with_title (a_parent.title)
|
||||
across
|
||||
lst as ic
|
||||
until
|
||||
p.has_id
|
||||
loop
|
||||
if
|
||||
attached ic.item.link as lnk and then
|
||||
lnk.location.same_string_general (l_parent_lnk.location)
|
||||
then
|
||||
p := ic.item
|
||||
end
|
||||
end
|
||||
else
|
||||
lst := l_page_api.pages_with_title (a_parent.title)
|
||||
if lst.count = 1 then
|
||||
p := lst.first
|
||||
end
|
||||
end
|
||||
end
|
||||
if p.has_id then
|
||||
a_page.set_parent (p)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
31
modules/node/submodules/page/cms_page_storage_i.e
Normal file
31
modules/node/submodules/page/cms_page_storage_i.e
Normal file
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "Summary description for {CMS_PAGE_STORAGE_I}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- Children of node `a_node'.
|
||||
-- note: this is the partial version of the nodes.
|
||||
deferred
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- Given the node `a_node', return the list of possible parent nodes id
|
||||
deferred
|
||||
ensure
|
||||
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
|
||||
end
|
||||
|
||||
end
|
||||
31
modules/node/submodules/page/cms_page_storage_null.e
Normal file
31
modules/node/submodules/page/cms_page_storage_null.e
Normal file
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "Summary description for {CMS_PAGE_STORAGE_NULL}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
CMS_NODE_STORAGE_NULL
|
||||
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
|
||||
-- <Precursor>
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
|
||||
end
|
||||
|
||||
end
|
||||
86
modules/node/submodules/page/cms_page_storage_sql.e
Normal file
86
modules/node/submodules/page/cms_page_storage_sql.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "Access to the sql database for the page module"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_PAGE_STORAGE_SQL
|
||||
|
||||
inherit
|
||||
CMS_NODE_STORAGE_SQL
|
||||
|
||||
CMS_PAGE_STORAGE_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
children (a_node: CMS_NODE): detachable 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 + ".children")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_children_of_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
available_parents_for_node (a_node: CMS_NODE): 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 + ".available_parents_for_node")
|
||||
|
||||
from
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_node.id, "nid")
|
||||
sql_query (sql_select_available_parents_for_node, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after
|
||||
loop
|
||||
if attached fetch_node as l_node then
|
||||
Result.force (l_node)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Queries
|
||||
|
||||
sql_select_available_parents_for_node : STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
|
||||
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
sql_select_children_of_node: STRING = "[
|
||||
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
|
||||
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
|
||||
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
|
||||
]"
|
||||
|
||||
end
|
||||
15
modules/wikitext/site/files/css/wikitext.css
Normal file
15
modules/wikitext/site/files/css/wikitext.css
Normal file
@@ -0,0 +1,15 @@
|
||||
#main code, #main e, #main eiffel {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
border: solid 1px #ccc;
|
||||
background-color: #fff;
|
||||
line-height: 1.3;
|
||||
padding: 10px;
|
||||
margin: 2px 0 2px 0;
|
||||
}
|
||||
#main code.inline, #main e.inline, #main eiffel.inline {
|
||||
display: inline-block;
|
||||
padding: 0 2px 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
18
modules/wikitext/site/files/scss/wikitext.scss
Normal file
18
modules/wikitext/site/files/scss/wikitext.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
#main {
|
||||
code, e, eiffel {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
border: solid 1px #ccc;
|
||||
background-color: #fff;
|
||||
line-height: 1.3;
|
||||
padding: 10px;
|
||||
margin: 2px 0 2px 0;
|
||||
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
padding: 0 2px 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
modules/wikitext/wikitext-safe.ecf
Normal file
26
modules/wikitext/wikitext-safe.ecf
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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="wikitext" uuid="7A757B53-6145-4171-833D-27292F65E34C" library_target="wikitext">
|
||||
<target name="wikitext">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="all">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<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="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="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
|
||||
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>
|
||||
<library name="wikitext" location="$ISE_LIBRARY\contrib\library\text\parser\wikitext\wikitext-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
65
modules/wikitext/wikitext_filter.e
Normal file
65
modules/wikitext/wikitext_filter.e
Normal file
@@ -0,0 +1,65 @@
|
||||
note
|
||||
description: "Summary description for {WIKITEXT_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WIKITEXT_FILTER
|
||||
|
||||
inherit
|
||||
CONTENT_FILTER
|
||||
redefine
|
||||
help
|
||||
end
|
||||
|
||||
STRING_HANDLER
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING_8 = "wikitext_renderer"
|
||||
|
||||
title: STRING_8 = "Wikitext renderer"
|
||||
|
||||
help: STRING = "Wikitext rendered as HTML"
|
||||
|
||||
description: STRING_8 = "Render Wikitext as HTML."
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
filter (a_text: STRING_GENERAL)
|
||||
local
|
||||
wk: WIKI_CONTENT_TEXT
|
||||
utf: UTF_CONVERTER
|
||||
l_wikitext: STRING_8
|
||||
vis: WIKI_XHTML_GENERATOR
|
||||
html: STRING
|
||||
do
|
||||
if attached {STRING_8} a_text as s8 then
|
||||
l_wikitext := s8
|
||||
elseif attached {STRING_32} a_text as s32 then
|
||||
if s32.is_valid_as_string_8 then
|
||||
l_wikitext := s32.as_string_8
|
||||
else
|
||||
l_wikitext := utf.utf_32_string_to_utf_8_string_8 (s32)
|
||||
end
|
||||
else
|
||||
l_wikitext := utf.utf_32_string_to_utf_8_string_8 (a_text)
|
||||
end
|
||||
create wk.make_from_string (l_wikitext)
|
||||
if attached wk.structure as st then
|
||||
create html.make (l_wikitext.count)
|
||||
create vis.make (html)
|
||||
vis.code_aliases.extend ("eiffel")
|
||||
vis.code_aliases.extend ("e")
|
||||
st.process (vis)
|
||||
a_text.set_count (0)
|
||||
-- if attached {STRING_8} a_text as s8 then
|
||||
-- s8.wipe_out
|
||||
-- elseif attached {STRING_32} a_text as s32 then
|
||||
-- s32.wipe_out
|
||||
-- end
|
||||
a_text.append (html)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
32
modules/wikitext/wikitext_format.e
Normal file
32
modules/wikitext/wikitext_format.e
Normal file
@@ -0,0 +1,32 @@
|
||||
note
|
||||
description: "Summary description for {WIKITEXT_FORMAT}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WIKITEXT_FORMAT
|
||||
|
||||
inherit
|
||||
CONTENT_FORMAT
|
||||
redefine
|
||||
default_create
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
default_create
|
||||
do
|
||||
Precursor
|
||||
create filters.make (1)
|
||||
filters.force (create {WIKITEXT_FILTER})
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "wikitext"
|
||||
|
||||
title: STRING_8 = "Wikitext"
|
||||
|
||||
filters: ARRAYED_LIST [CONTENT_FILTER]
|
||||
|
||||
end
|
||||
80
modules/wikitext/wikitext_module.e
Normal file
80
modules/wikitext/wikitext_module.e
Normal file
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description: "CMS module that bring support for path aliases."
|
||||
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
|
||||
revision: "$Revision: 96616 $"
|
||||
|
||||
class
|
||||
WIKITEXT_MODULE
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as wikitext_api
|
||||
redefine
|
||||
initialize,
|
||||
setup_hooks
|
||||
end
|
||||
|
||||
CMS_HOOK_RESPONSE_ALTER
|
||||
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_setup: CMS_SETUP)
|
||||
-- Create Current module, disabled by default.
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Wikitext module"
|
||||
package := "filter"
|
||||
config := a_setup
|
||||
end
|
||||
|
||||
config: CMS_SETUP
|
||||
-- Node configuration.
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "wikitext"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
f: CMS_FORMAT
|
||||
do
|
||||
Precursor (api)
|
||||
|
||||
create f.make_from_format (create {WIKITEXT_FORMAT})
|
||||
api.formats.extend (f)
|
||||
-- FIXME!!!
|
||||
across
|
||||
api.content_types as ic
|
||||
loop
|
||||
ic.item.extend_format (f)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- Setup url dispatching for Current module.
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
do
|
||||
a_hooks.subscribe_to_response_alter_hook (Current)
|
||||
end
|
||||
|
||||
response_alter (a_response: CMS_RESPONSE)
|
||||
do
|
||||
a_response.add_style (a_response.url ("/module/" + name + "/files/css/wikitext.css", Void), Void)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -234,7 +234,7 @@ feature -- Hook: export
|
||||
subscribe_to_hook (h, {CMS_HOOK_EXPORT})
|
||||
end
|
||||
|
||||
invoke_export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
invoke_export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Invoke response alter hook for response `a_response'.
|
||||
local
|
||||
d: DIRECTORY
|
||||
@@ -254,7 +254,36 @@ feature -- Hook: export
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hook: import
|
||||
|
||||
subscribe_to_import_hook (h: CMS_HOOK_IMPORT)
|
||||
-- Add `h' as subscriber of import hooks CMS_HOOK_IMPORT.
|
||||
do
|
||||
subscribe_to_hook (h, {CMS_HOOK_IMPORT})
|
||||
end
|
||||
|
||||
invoke_import_from (a_import_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_import_ctx: CMS_IMPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Invoke response alter hook for response `a_response'.
|
||||
local
|
||||
d: DIRECTORY
|
||||
do
|
||||
if attached subscribers ({CMS_HOOK_IMPORT}) as lst then
|
||||
create d.make_with_path (a_import_ctx.location)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if attached {CMS_HOOK_IMPORT} ic.item as h then
|
||||
h.import_from (a_import_id_list, a_import_ctx, a_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ note
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_EXPORT_PARAMETERS
|
||||
CMS_EXPORT_CONTEXT
|
||||
|
||||
create
|
||||
make
|
||||
@@ -13,19 +13,19 @@ inherit
|
||||
|
||||
feature -- Hook
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_ctx: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Export data identified by `a_export_id_list',
|
||||
-- or export all data if `a_export_id_list' is Void.
|
||||
deferred
|
||||
end
|
||||
|
||||
-- export_identifiers: detachable ITERABLE [READABLE_STRING_32]
|
||||
-- export_identifiers: detachable ITERABLE [READABLE_STRING_GENERAL]
|
||||
-- -- Optional list of exportation ids, if any.
|
||||
-- do
|
||||
-- -- To redefine if needed.
|
||||
-- end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
23
src/hooks/import/cms_hook_import.e
Normal file
23
src/hooks/import/cms_hook_import.e
Normal file
@@ -0,0 +1,23 @@
|
||||
note
|
||||
description: "Summary description for {CMS_HOOK_IMPORT}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_HOOK_IMPORT
|
||||
|
||||
inherit
|
||||
CMS_HOOK
|
||||
|
||||
feature -- Hook
|
||||
|
||||
import_from (a_impot_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_import_ctx: CMS_IMPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- Import data identified by `a_import_id_list',
|
||||
-- or import all data if `a_import_id_list' is Void.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
43
src/hooks/import/cms_import_context.e
Normal file
43
src/hooks/import/cms_import_context.e
Normal file
@@ -0,0 +1,43 @@
|
||||
note
|
||||
description: "[
|
||||
Parameters used by CMS_HOOK_IMPORT subscribers.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_IMPORT_CONTEXT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_location: PATH)
|
||||
do
|
||||
location := a_location
|
||||
create logs.make (10)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
location: PATH
|
||||
-- Location of import folder.
|
||||
|
||||
feature -- Logs
|
||||
|
||||
logs: ARRAYED_LIST [READABLE_STRING_8]
|
||||
-- Associated importation logs.
|
||||
|
||||
log (m: READABLE_STRING_8)
|
||||
-- Add message `m' into `logs'.
|
||||
do
|
||||
logs.force (m)
|
||||
end
|
||||
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
57
src/hooks/import/cms_import_json_utilities.e
Normal file
57
src/hooks/import/cms_import_json_utilities.e
Normal file
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "[
|
||||
Usefull routines to import to JSON.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_IMPORT_JSON_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
json_string_item (j: JSON_OBJECT; a_name: READABLE_STRING_GENERAL): detachable STRING_32
|
||||
do
|
||||
if attached {JSON_STRING} j.item (a_name) as s then
|
||||
Result := s.unescaped_string_32
|
||||
end
|
||||
end
|
||||
|
||||
json_string_8_item (j: JSON_OBJECT; a_name: READABLE_STRING_GENERAL): detachable STRING_8
|
||||
do
|
||||
if attached {JSON_STRING} j.item (a_name) as s then
|
||||
Result := s.unescaped_string_8
|
||||
end
|
||||
end
|
||||
|
||||
json_integer_item (j: JSON_OBJECT; a_name: READABLE_STRING_GENERAL): INTEGER_64
|
||||
local
|
||||
s: READABLE_STRING_GENERAL
|
||||
do
|
||||
if attached {JSON_NUMBER} j.item (a_name) as i then
|
||||
Result := i.integer_64_item
|
||||
elseif attached {JSON_STRING} j.item (a_name) as js then
|
||||
s := js.unescaped_string_32
|
||||
if s.is_integer_64 then
|
||||
Result := s.to_integer_64
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json_date_item (j: JSON_OBJECT; a_name: READABLE_STRING_GENERAL): detachable DATE_TIME
|
||||
local
|
||||
hd: HTTP_DATE
|
||||
do
|
||||
if attached {JSON_NUMBER} j.item (a_name) as num then
|
||||
create hd.make_from_timestamp (num.integer_64_item)
|
||||
Result := hd.date_time
|
||||
elseif attached {JSON_STRING} j.item (a_name) as s then
|
||||
create hd.make_from_string (s.unescaped_string_32)
|
||||
Result := hd.date_time
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -38,7 +38,7 @@ feature -- Access
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User with name `a_name', if any.
|
||||
require
|
||||
a_name /= Void and then not a_name.is_empty
|
||||
@@ -284,6 +284,6 @@ feature -- New Temp User
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ feature -- Access: user
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User for the given name `a_name', if any.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
@@ -1347,6 +1347,6 @@ feature {NONE} -- SQL select
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -804,7 +804,7 @@ feature -- Environment/ modules and theme
|
||||
|
||||
feature -- Hook
|
||||
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
|
||||
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_ctx: CMS_EXPORT_CONTEXT; a_response: CMS_RESPONSE)
|
||||
-- <Precursor>.
|
||||
local
|
||||
p: PATH
|
||||
@@ -816,14 +816,14 @@ feature -- Hook
|
||||
do
|
||||
if attached a_response.has_permissions (<<"admin export", "export core">>) then
|
||||
if a_export_id_list = Void then -- Include everything
|
||||
p := a_export_parameters.location.extended ("core")
|
||||
p := a_export_ctx.location.extended ("core")
|
||||
create d.make_with_path (p)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
|
||||
-- path_aliases export.
|
||||
a_export_parameters.log ("Exporting path_aliases")
|
||||
a_export_ctx.log ("Exporting path_aliases")
|
||||
create jo.make_empty
|
||||
across storage.path_aliases as ic loop
|
||||
jo.put_string (ic.item, ic.key)
|
||||
@@ -835,7 +835,7 @@ feature -- Hook
|
||||
|
||||
-- custom_values export.
|
||||
if attached storage.custom_values as lst then
|
||||
a_export_parameters.log ("Exporting custom_values")
|
||||
a_export_ctx.log ("Exporting custom_values")
|
||||
create ja.make_empty
|
||||
across
|
||||
lst as ic
|
||||
@@ -857,7 +857,7 @@ feature -- Hook
|
||||
end
|
||||
|
||||
-- users export.
|
||||
a_export_parameters.log ("Exporting users")
|
||||
a_export_ctx.log ("Exporting users")
|
||||
create jo.make_empty
|
||||
|
||||
create jobj.make_empty
|
||||
@@ -1016,7 +1016,7 @@ feature {NONE} -- Implementation: current user
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -264,19 +264,9 @@ feature -- Filters
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'.
|
||||
local
|
||||
f: WSF_FILTER
|
||||
do
|
||||
api.logger.put_debug (generator + ".setup_filter", Void)
|
||||
|
||||
from
|
||||
f := filter
|
||||
until
|
||||
not attached f.next as l_next
|
||||
loop
|
||||
f := l_next
|
||||
end
|
||||
f.set_next (Current)
|
||||
append_filter (Current)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
@@ -331,7 +321,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -22,7 +22,7 @@ feature -- Access: user
|
||||
Result := storage.user_by_id (a_id)
|
||||
end
|
||||
|
||||
user_by_name (a_username: READABLE_STRING_32): detachable CMS_USER
|
||||
user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by name `a_user_name', if any.
|
||||
do
|
||||
Result := storage.user_by_name (a_username)
|
||||
@@ -407,6 +407,6 @@ feature -- Change Temp User
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user