Improved node management.

- List node by node types
- fixed the trash/restore/delete workflow
Added messaging module to send message to cms users (by email for now).
Added early protection for cache, export and import functionalities.
This commit is contained in:
2017-02-28 11:24:48 +01:00
parent dc84e79952
commit a341bd98eb
35 changed files with 1055 additions and 212 deletions

View File

@@ -124,6 +124,7 @@ feature -- Security
Result.force ("admin users")
Result.force ("admin roles")
Result.force ("admin modules")
Result.force ("admin cache")
Result.force ("admin core caches")
Result.force ("clear blocks cache")
Result.force ("admin export")
@@ -159,13 +160,16 @@ feature -- Hooks
-- Per module cache permission!
create lnk.make ("Cache", "admin/cache")
lnk.set_permission_arguments (<<"admin cache">>)
admin_lnk.extend (lnk)
-- Per module export permission!
create lnk.make ("Export", "admin/export")
lnk.set_permission_arguments (<<"admin export">>)
admin_lnk.extend (lnk)
-- Per module import permission!
create lnk.make ("Import", "admin/import")
lnk.set_permission_arguments (<<"admin import">>)
admin_lnk.extend (lnk)
end
end

View File

@@ -41,11 +41,15 @@ feature -- Execution
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
if api.has_permission ("admin cache") then
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
l_response.execute
end
@@ -55,23 +59,27 @@ feature -- Execution
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_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_clear_all_caches) then
api.hooks.invoke_clear_cache (Void, l_response)
l_response.add_notice_message ("Caches cleared (if allowed)!")
else
fd.report_error ("Invalid form data!")
if api.has_permission ("admin cache") then
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_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_clear_all_caches) then
api.hooks.invoke_clear_cache (Void, l_response)
l_response.add_notice_message ("Caches cleared (if allowed)!")
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)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end

View File

@@ -41,11 +41,15 @@ feature -- Execution
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
if api.has_permission ("admin export") then
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
l_response.execute
end
@@ -56,37 +60,41 @@ feature -- Execution
f: CMS_FORM
l_exportation: CMS_EXPORT_CONTEXT
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_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_export_all_data) then
if attached fd.string_item ("folder") as l_folder then
create l_exportation.make (api.site_location.extended ("export").extended (l_folder))
if api.has_permission ("admin export") then
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_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_export_all_data) then
if attached fd.string_item ("folder") as l_folder then
create l_exportation.make (api.site_location.extended ("export").extended (l_folder))
else
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, l_response)
l_response.add_notice_message ("All data exported (if allowed)!")
create s.make_empty
across
l_exportation.logs as ic
loop
s.append (ic.item)
s.append ("<br/>")
s.append_character ('%N')
end
l_response.add_notice_message (s)
else
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")))
fd.report_error ("Invalid form data!")
end
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.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
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end

View File

@@ -41,11 +41,15 @@ feature -- Execution
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)
if api.has_permission ("admin import") then
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)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
l_response.execute
end
@@ -57,43 +61,48 @@ feature -- Execution
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)
create l_importation.make (api.site_location.extended (import_folder_name).extended (l_folder))
if l_importation.location_exists then
l_response.add_notice_message ("Import all data (if permitted)!")
api.hooks.invoke_import_from (Void, l_importation, l_response)
create s.make_empty
across
l_importation.logs as ic
loop
s.append (ic.item)
s.append ("<br/>")
s.append_character ('%N')
if api.has_permission ("admin import") then
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)
create l_importation.make (api.site_location.extended (import_folder_name).extended (l_folder))
if l_importation.location_exists then
l_response.add_notice_message ("Import all data (if permitted)!")
api.hooks.invoke_import_from (Void, l_importation, l_response)
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
l_response.add_error_message ("Specified import folder is not found!")
fd.report_invalid_field ("folder", "Folder not found!")
end
l_response.add_notice_message (s)
else
l_response.add_error_message ("Specified import folder is not found!")
fd.report_invalid_field ("folder", "Folder not found!")
fd.report_error ("Invalid form data!")
end
else
fd.report_error ("Invalid form data!")
end
else
fd.report_error ("Invalid form data!")
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end

View File

@@ -66,6 +66,7 @@ feature -- HTTP Methods
s_pager: STRING
l_count: INTEGER
user_api: CMS_USER_API
l_display_name: READABLE_STRING_32
do
-- At the moment the template are hardcoded, but we can
-- get them from the configuration file and load them into
@@ -81,9 +82,9 @@ feature -- HTTP Methods
create s.make_empty
if l_count > 1 then
l_response.set_title ("Listing " + l_count.out + " Users")
l_response.set_title ("Listing " + l_count.out + " users")
else
l_response.set_title ("Listing " + l_count.out + " User")
l_response.set_title ("A single user")
end
create s_pager.make_empty
@@ -106,7 +107,13 @@ feature -- HTTP Methods
s.append ("<a href=%"")
s.append (req.absolute_script_url ("/admin/user/"+u.id.out))
s.append ("%">")
s.append (html_encoded (u.name))
l_display_name := user_api.user_display_name (u)
s.append (html_encoded (l_display_name))
if not l_display_name.same_string (u.name) then
s.append (" [")
s.append (html_encoded (u.name))
s.append ("]")
end
s.append ("</a>")
if attached user_api.user_roles (u) as l_roles and then not l_roles.is_empty then
s.append (" <span class=%"cms_roles%">(")

View File

@@ -10,7 +10,7 @@ ul.cms-users li:first-child {
border-top: none;
}
ul.cms-users li.cms_user a::before {
content: "[users] ";
content: "[user] ";
}
ul.cms-roles {
@@ -25,7 +25,7 @@ ul.cms-roles li:first-child {
border-top: none;
}
ul.cms-roles li.cms_role a::before {
content: "[roles] ";
content: "[role] ";
}
ul.cms-permissions {

View File

@@ -12,7 +12,7 @@ ul.cms-users {
}
li.cms_user a::before {
content: "[users] ";
content: "[user] ";
}
}
@@ -31,7 +31,7 @@ ul.cms-roles {
}
li.cms_role a::before {
content: "[roles] ";
content: "[role] ";
}
}

View File

@@ -0,0 +1,20 @@
<?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="messaging" uuid="939C9362-FD46-4B4F-BB36-A593089430AB" library_target="messaging">
<target name="messaging">
<root all_classes="true"/>
<option>
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,4 @@
.messaging-box fieldset {
overflow: scroll;
height: 250px;
}

View File

@@ -0,0 +1,6 @@
.messaging-box {
fieldset {
overflow:scroll;
height:250px;
}
}

View File

@@ -0,0 +1,17 @@
note
description: "API for the contact module."
date: "$Date$"
revision: "$Revision$"
class
CMS_MESSAGING_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create
make
end

View File

@@ -0,0 +1,381 @@
note
description: "[
Module that provides messenger functionality.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
CMS_MESSAGING_MODULE
inherit
CMS_MODULE
rename
module_api as messaging_api
redefine
setup_hooks,
-- install,
initialize,
permissions,
messaging_api
end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
REFACTORING_HELPER
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "Messaging module"
package := "messaging"
end
feature -- Access
name: STRING = "messaging"
-- <Precursor>
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
local
l_messaging_api: like messaging_api
do
Precursor (api)
create l_messaging_api.make (api)
messaging_api := l_messaging_api
end
feature {CMS_API} -- Access: API
messaging_api: detachable CMS_MESSAGING_API
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration.
local
m: WSF_URI_MAPPING
do
create m.make_trailing_slash_ignored ("/messaging", create {WSF_URI_AGENT_HANDLER}.make (agent handle_get_messaging (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get)
a_router.handle ("/messaging", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_messaging (a_api, ?, ?)), a_router.methods_put_post)
end
feature -- Security
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin messaging")
Result.force ("message any user")
Result.force ("use messaging")
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
end
feature -- Hooks
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
do
debug ("refactor_fixme")
fixme ("add messaging to menu")
end
end
new_html_messaging_form (a_response: CMS_RESPONSE; api: CMS_API): STRING
local
f: CMS_FORM
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/messaging.css", Void), Void)
-- TODO: use template to overwrite/customize
-- if attached smarty_template_block (Current, "messaging", api) as l_tpl_block then
-- across
-- a_response.values as tb
-- loop
-- l_tpl_block.set_value (tb.item, tb.key)
-- end
-- Result := l_tpl_block.to_html (a_response.theme)
-- else
f := new_messaging_form (a_response, api)
api.hooks.invoke_form_alter (f, f.last_data, a_response)
Result := "<div class=%"messaging-box%"><h1>Send message to ...</h1>" + f.to_html (a_response.wsf_theme) + "<br/></div>"
-- end
end
new_messaging_form (a_response: CMS_RESPONSE; api: CMS_API): CMS_FORM
local
f: CMS_FORM
f_name: WSF_FORM_TEXT_INPUT
f_msg: WSF_FORM_TEXTAREA
f_submit: WSF_FORM_SUBMIT_INPUT
f_user: WSF_FORM_CHECKBOX_INPUT
f_set: WSF_FORM_FIELD_SET
l_params: CMS_DATA_QUERY_PARAMETERS
nb: INTEGER
i: INTEGER
do
create f.make (a_response.url ("messaging", Void), "messaging-form")
if attached api.user as l_current_user then
nb := api.user_api.users_count
from
create f_set.make
f_set.set_legend ("Select users")
f.extend (f_set)
i := 0
until
i > nb
loop
create l_params.make (i.to_natural_64, 25)
if attached api.user_api.recent_users (l_params) as l_users then
across
l_users as ic
loop
if l_current_user.id = ic.item.id then
else
create f_user.make_with_value ("users[]", ic.item.id.out)
f_user.set_title (api.user_api.user_display_name (ic.item))
f_set.extend (f_user)
end
end
end
i := i + 25
end
create f_name.make ("title")
f_name.set_size (80)
f_name.set_label ("Title")
f_name.set_is_required (True)
f.extend (f_name)
create f_msg.make ("message")
f_msg.set_cols (80)
f_msg.set_rows (75)
f_msg.set_label ("Message")
f_msg.set_rows (5)
f_msg.set_is_required (True)
f.extend (f_msg)
create f_submit.make_with_text ("submit-op", "Send")
f.extend (f_submit)
end
Result := f
end
handle_get_messaging (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
if api.has_permission ("use messaging") or api.has_permission ("message any user") then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.values.force ("messaging", "messaging")
r.set_main_content (new_html_messaging_form (r, api))
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
r.execute
end
handle_post_messaging (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
e: CMS_EMAIL
l_emails: ARRAYED_LIST [CMS_EMAIL]
vars: STRING_TABLE [READABLE_STRING_8]
l_messaging_email_address: READABLE_STRING_8
s: STRING
l_uid: READABLE_STRING_32
f: like new_messaging_form
l_user: detachable CMS_USER
l_email_title: READABLE_STRING_8
l_email_messg: READABLE_STRING_8
do
if api.has_permission ("message any user") then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.add_style (r.url ("/module/" + name + "/files/css/messaging.css", Void), Void)
create s.make_empty
f := new_messaging_form (r, api)
f.process (r)
if attached f.last_data as fd then
if
not fd.has_error and then
attached fd.string_item ("title") as l_title and then
attached fd.string_item ("message") as l_message and then
attached fd.table_item ("users") as l_users
then
create l_emails.make (l_users.count)
s.append ("Send message %"")
s.append (r.html_encoded (l_title))
s.append ("%"")
s.append (" to users: <ul>")
across
l_users as ic
loop
if attached {WSF_STRING} ic.item as p_uid then
l_uid := p_uid.value
if l_uid.is_integer_64 then
l_user := api.user_api.user_by_id (l_uid.to_integer_64)
else
l_user := api.user_api.user_by_name (l_uid)
end
s.append ("<li>")
if l_user /= Void and then attached l_user.email as l_user_email then
s.append (r.html_encoded (api.user_api.user_display_name (l_user)))
s.append (" &lt;")
s.append (r.html_encoded (l_user_email))
s.append ("&gt;")
l_email_title := resolved_template_text (api, l_title, l_user)
l_email_messg := resolved_template_text (api, l_message, l_user)
e := api.new_email (l_user_email, l_email_title, l_email_messg)
s.append (" <pre>")
s.append (e.message)
s.append ("</pre>")
l_emails.force (e)
api.process_email (e)
if e.is_sent then
s.append (" successfully sent.")
else
s.append (" failure, not sent!")
end
else
s.append (r.html_encoded (p_uid.value))
s.append (" skipped!")
end
s.append ("</li>%N")
end
end
else
f.append_to_html (r.wsf_theme, s)
end
end
r.set_main_content (s)
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
r.execute
end
feature {NONE} -- Helpers
form_parameters_as_string (req: WSF_REQUEST): STRING
do
create Result.make_empty
across req.form_parameters as ic loop
Result.append (ic.item.key)
Result.append_character ('=')
Result.append_string (ic.item.string_representation)
Result.append_character ('%N')
end
end
feature {NONE} -- Contact Message
resolved_template_text (api: CMS_API; a_text: READABLE_STRING_GENERAL; a_target_user: detachable CMS_USER): STRING_8
local
smt: CMS_SMARTY_TEMPLATE_TEXT
utf: UTF_CONVERTER
do
create smt.make (utf.utf_32_string_to_utf_8_string_8 (a_text))
across
api.builtin_variables as vars_ic
loop
smt.set_value (vars_ic.item, vars_ic.key)
end
if a_target_user /= Void then
smt.set_value (a_target_user.name, "target_user_name")
smt.set_value (api.user_api.user_display_name (a_target_user), "target_user_profile_name")
smt.set_value (a_target_user.id.out, "target_user_id")
if attached a_target_user.email as l_email then
smt.set_value (l_email, "target_user_email")
end
end
Result := smt.string
end
-- email_html_message (a_message_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_html_encoded_values: STRING_TABLE [READABLE_STRING_8]): STRING
-- -- html message related to `a_message_id'.
-- local
-- res: PATH
-- p: detachable PATH
-- tpl: CMS_SMARTY_TEMPLATE_BLOCK
-- exp: CMS_STRING_EXPANDER [STRING_8]
-- do
-- write_debug_log (generator + ".email_html_message for [" + a_message_id + " ]")
-- create res.make_from_string ("templates")
-- res := res.extended ("email_").appended (a_message_id).appended_with_extension ("tpl")
-- p := a_response.api.module_theme_resource_location (Current, res)
-- if p /= Void then
-- if attached p.entry as e then
-- create tpl.make (a_message_id, Void, p.parent, e)
-- write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
-- else
-- create tpl.make (a_message_id, Void, p.parent, p)
-- write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
-- end
-- across
-- a_html_encoded_values as ic
-- loop
-- tpl.set_value (ic.item, ic.key)
-- end
-- Result := tpl.to_html (a_response.theme)
-- else
-- if a_message_id.is_case_insensitive_equal_general ("message") then
-- create Result.make_from_string (messaging_message_template)
-- elseif a_message_id.is_case_insensitive_equal_general ("notification") then
-- create Result.make_from_string (messaging_notification_message_template)
-- else
-- create Result.make_from_string (a_message_id)
-- across
-- a_html_encoded_values as ic
-- loop
-- Result.append ("<li>")
-- Result.append (html_encoded (ic.key))
-- Result.append (": ")
-- Result.append (ic.item) -- Already html encoded.
-- Result.append ("</li>%N")
-- end
-- end
-- create exp.make
-- across
-- a_html_encoded_values as ic
-- loop
-- exp.put (ic.item, ic.key)
-- end
-- exp.expand_string (Result)
-- write_debug_log (generator + ".email_html_message using built-in message:" + Result)
-- end
-- end
end

View File

@@ -153,6 +153,11 @@ feature -- Access: Node
Result := node_storage.nodes_count
end
nodes_of_type_count (a_content_type: CMS_CONTENT_TYPE): NATURAL_64
do
Result := node_storage.nodes_of_type_count (a_content_type)
end
nodes: LIST [CMS_NODE]
-- List of nodes.
do
@@ -179,6 +184,12 @@ feature -- Access: Node
Result := node_storage.recent_nodes (params.offset.to_integer_32, params.size.to_integer_32)
end
recent_nodes_of_type (a_content_type: CMS_CONTENT_TYPE; params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_NODE]
-- Most recent `a_content_type` nodes according to `params.offset' and `params.size'.
do
Result := node_storage.recent_nodes_of_type (a_content_type, params.offset.to_integer_32, params.size.to_integer_32)
end
recent_node_changes_before (params: CMS_DATA_QUERY_PARAMETERS; a_date: DATE_TIME): ITERABLE [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
do

View File

@@ -150,13 +150,13 @@ feature -- Access
Result.force ("restore own " + l_type_name)
Result.force ("view unpublished " + l_type_name)
Result.force ("view revisions own " + l_type_name)
Result.force ("export " + l_type_name)
end
end
Result.force ("view trash")
Result.force ("view own trash")
end
end
@@ -195,6 +195,7 @@ feature -- Access: router
create l_nodes_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
a_router.map (l_uri_mapping, a_router.methods_get)
a_router.handle ("/nodes/{type}", l_nodes_handler, a_router.methods_get)
-- Trash
create l_trash_handler.make (a_api, a_node_api)

View File

@@ -92,7 +92,15 @@ feature -- Status reports
do
Result := status = {CMS_NODE_API}.published
ensure
Result implies not is_trashed
Result implies not is_trashed and not is_not_published
end
is_not_published: BOOLEAN
-- Is Current not published?
do
Result := status = {CMS_NODE_API}.not_published
ensure
Result implies not is_published
end
is_trashed: BOOLEAN

View File

@@ -291,9 +291,12 @@ feature -- Output
a_response.add_to_primary_tabs (lnk)
if a_node.status = {CMS_NODE_API}.trashed then
create lnk.make ("Delete", l_node_api.node_path (a_node) + "/delete")
create lnk.make ("Restore", l_node_api.node_path (a_node) + "/trash")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
create lnk.make ("Delete", l_node_api.node_path (a_node) + "/delete")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
elseif a_node.has_id then
-- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status.
create lnk.make ("Edit", l_node_api.node_path (a_node) + "/edit")
@@ -320,7 +323,17 @@ feature -- Output
if is_teaser then
a_output.append (" cms-teaser")
end
a_output.append ("cms-node node-" + a_node.content_type + "%">")
a_output.append ("cms-node node-" + a_node.content_type)
if a_node.is_published then
a_output.append (" cms-status-published")
elseif a_node.is_trashed then
a_output.append (" cms-status-trashed")
elseif a_node.is_not_published then
a_output.append (" cms-status-unpublished")
else
a_output.append (" cms-status-" + a_node.status.out)
end
a_output.append ("%">")
a_output.append ("<div class=%"info%"> ")
if attached a_node.author as l_author then

View File

@@ -159,7 +159,6 @@ feature {NONE} -- Create a new node
end
end
delete_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
local
f: like new_edit_form
@@ -187,7 +186,7 @@ feature {NONE} -- Create a new node
f.append_to_html (wsf_theme, b)
end
else
--
b.append ("ERROR: node is not in the trash!")
end
end
@@ -381,7 +380,7 @@ feature -- Form
create f.make (a_url, a_name)
f.extend_html_text ("<br/>")
f.extend_html_text ("<legend>Are you sure you want to delete?</legend>")
f.extend_html_text ("<legend>Are you sure you want to delete? (impossible to undo)</legend>")
-- TODO check if we need to check for has_permissions!!
if
@@ -400,46 +399,42 @@ feature -- Form
ts.set_formmethod ("GET")
f.extend (ts)
end
f.extend_html_text ("<br/>")
f.extend_html_text ("<legend>Do you want to restore the current node?</legend>")
if
a_node /= Void and then
a_node.id > 0
then
create ts.make ("op")
ts.set_default_value ("Restore")
ts.set_formaction ("/node/"+a_node.id.out+"/delete")
ts.set_formmethod ("POST")
fixme ("[
ts.set_default_value (translation ("Restore"))
]")
f.extend (ts)
end
Result := f
end
new_trash_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_node_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM
-- Create a web form named `a_name' for node `a_node' (if set), using form action url `a_url', and for type of node `a_node_type'.
new_trash_form (a_node: CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_node_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM
-- Create a web form named `a_name' for node `a_node', using form action url `a_url', and for type of node `a_node_type'.
local
f: CMS_FORM
ts: WSF_FORM_SUBMIT_INPUT
do
create f.make (a_url, a_name)
f.set_method_post
f.extend_html_text ("<br/>")
f.extend_html_text ("<legend>Are you sure you want to trash the current node?</legend>")
if
a_node /= Void and then
a_node.id > 0
then
if a_node.is_trashed then
f.extend_html_text ("<legend>Are you sure you want to restore the current node?</legend>")
create ts.make ("op")
ts.set_default_value ("Trash")
ts.set_default_value ("Restore")
ts.set_formaction ("/node/" + a_node.id.out + "/trash")
ts.set_formmethod ("POST")
fixme ("[
ts.set_default_value (translation ("Trash"))
]")
f.extend (ts)
else
f.extend_html_text ("<legend>Are you sure you want to trash the current node?</legend>")
create ts.make ("op")
ts.set_default_value ("Trash")
ts.set_formaction ("/node/" + a_node.id.out + "/trash")
ts.set_formmethod ("POST")
fixme ("[
ts.set_default_value (translation ("Trash"))
]")
end
f.extend (ts)
Result := f
end

View File

@@ -174,18 +174,20 @@ feature -- HTTP Methods
l_op.value.same_string ("Delete")
then
do_delete (req, res)
elseif
attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Restore")
then
do_restore (req, res)
else
send_bad_request (req, res)
end
elseif req.percent_encoded_path_info.ends_with ("/trash") then
if
attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Trash")
then
do_trash (req, res)
if attached {WSF_STRING} req.form_parameter ("op") as l_op then
if l_op.is_case_insensitive_equal ("Trash") then
do_trash (req, res)
elseif l_op.is_case_insensitive_equal ("Restore") then
do_restore (req, res)
else
send_bad_request (req, res)
end
else
send_bad_request (req, res)
end
elseif req.percent_encoded_path_info.starts_with ("/node/add/") then
create edit_response.make (req, res, api, node_api)
@@ -206,6 +208,14 @@ feature -- HTTP Methods
send_not_implemented ("REST API not yet implemented", req, res)
end
process_node_creation (req: WSF_REQUEST; res: WSF_RESPONSE; a_user: CMS_USER)
do
to_implement ("REST API")
send_not_implemented ("REST API not yet implemented", req, res)
end
feature {NONE} -- Trash:Restore
do_trash (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Trash a node, soft delete.
do
@@ -233,14 +243,6 @@ feature -- HTTP Methods
end
end
process_node_creation (req: WSF_REQUEST; res: WSF_RESPONSE; a_user: CMS_USER)
do
to_implement ("REST API")
send_not_implemented ("REST API not yet implemented", req, res)
end
feature {NONE} -- Trash:Restore
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Delete a node from the database.
local
@@ -283,7 +285,7 @@ feature {NONE} -- Trash:Restore
then
if node_api.has_permission_for_action_on_node ("restore", l_node, l_user) then
node_api.restore_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("/" + node_api.node_path (l_node))))
else
send_access_denied (req, res)
-- send_not_authorized ?

View File

@@ -11,7 +11,16 @@ inherit
WSF_URI_HANDLER
rename
new_mapping as new_uri_mapping
new_mapping as new_uri_mapping,
execute as execute_uri
end
WSF_URI_TEMPLATE_HANDLER
rename
new_mapping as new_uri_template_mapping,
execute as execute_uri_template
select
new_uri_template_mapping
end
WSF_RESOURCE_HANDLER_HELPER
@@ -26,6 +35,16 @@ create
feature -- execute
execute_uri (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute (req, res)
end
execute_uri_template (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute (req, res)
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
@@ -39,30 +58,57 @@ feature -- HTTP Methods
local
l_response: CMS_RESPONSE
s: STRING
l_content_type: detachable CMS_CONTENT_TYPE
n: CMS_NODE
lnk: CMS_LOCAL_LINK
l_page_helper: CMS_PAGINATION_GENERATOR
s_pager: STRING
l_count: NATURAL_64
inc: BOOLEAN
l_include_trashed: BOOLEAN
lst: detachable ITERABLE [CMS_NODE]
do
-- At the moment the template are hardcoded, but we can
-- get them from the configuration file and load them into
-- the setup class.
l_count := node_api.nodes_count
if attached {WSF_STRING} req.path_parameter ("type") as p_node_type and then attached api.content_type (p_node_type.value) as ct then
l_content_type := api.content_type (p_node_type.value)
end
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
create s.make_empty
if l_count > 1 then
l_response.set_title ("Listing " + l_count.out + " nodes")
else
l_response.set_title ("Listing " + l_count.out + " node")
across
api.content_types as ic
loop
if attached {CMS_NODE_TYPE [CMS_NODE]} ic.item as l_note_type then
create lnk.make (l_note_type.name, "nodes/" + l_note_type.name)
if l_note_type = l_content_type then
lnk.set_is_active (True)
end
l_response.add_to_primary_tabs (lnk)
end
end
create s_pager.make_empty
create l_page_helper.make ("nodes/?page={page}&size={size}", node_api.nodes_count, 25) -- FIXME: Make this default page size a global CMS settings
if l_content_type /= Void then
l_count := node_api.nodes_of_type_count (l_content_type)
if l_count > 1 then
l_response.set_title ("Listing " + l_count.out + " " + l_content_type.name + " nodes")
else
l_response.set_title ("Listing " + l_count.out + " " + l_content_type.name + " node")
end
create l_page_helper.make ("nodes/" + l_content_type.name + "/?page={page}&size={size}", l_count, 25) -- FIXME: Make this default page size a global CMS settings
else
l_count := node_api.nodes_count
if l_count > 1 then
l_response.set_title ("Listing " + l_count.out + " nodes")
else
l_response.set_title ("Listing " + l_count.out + " node")
end
create l_page_helper.make ("nodes/?page={page}&size={size}", l_count, 25) -- FIXME: Make this default page size a global CMS settings
end
l_page_helper.get_setting_from_request (req)
if l_page_helper.has_upper_limit and then l_page_helper.pages_count > 1 then
l_page_helper.append_to_html (l_response, s_pager)
@@ -71,7 +117,12 @@ feature -- HTTP Methods
end
end
if attached node_api.recent_nodes (create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size)) as lst then
if l_content_type /= Void then
lst := node_api.recent_nodes_of_type (l_content_type, create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size))
else
lst := node_api.recent_nodes (create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size))
end
if lst /= Void then
if attached {WSF_STRING} req.query_parameter ("include_trash") as v and then v.is_case_insensitive_equal ("yes") then
l_include_trashed := l_response.has_permissions (<<"view trash", "view any trash">>)
end
@@ -80,7 +131,14 @@ feature -- HTTP Methods
lst as ic
loop
n := ic.item
if not n.is_trashed or else l_include_trashed then
inc := True
if not n.is_published then
inc := view_unpublished_node_permitted (n)
end
if inc and n.is_trashed then
inc := l_include_trashed
end
if inc then
lnk := node_api.node_link (n)
s.append ("<li class=%"cms_type_"+ n.content_type)
if not n.is_published then
@@ -131,4 +189,15 @@ feature -- HTTP Methods
l_response.execute
end
feature -- Helper
view_unpublished_node_permitted (n: CMS_NODE): BOOLEAN
do
if api.has_permission ("view unpublished " + n.content_type) then
Result := True
elseif attached api.user as u then
Result := u.same_as (n.author)
end
end
end

View File

@@ -100,6 +100,13 @@ feature -- Access
deferred
end
recent_nodes_of_type (a_node_type: CMS_CONTENT_TYPE; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- Recent `a_count` nodes of type `a_node_type` with an offset of `lower`.
deferred
ensure
across Result as ic all ic.item.is_typed_as (a_node_type.name) end
end
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
deferred
@@ -120,6 +127,12 @@ feature -- Access
deferred
end
nodes_of_type_count (a_node_type: CMS_CONTENT_TYPE): NATURAL_64
-- Count of nodes of type `a_node_type`.
do
Result := nodes_of_type (a_node_type).count.to_natural_64
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- List of nodes of type `a_node_type'.
--| Redefine to optimize!
@@ -137,7 +150,7 @@ feature -- Access
end
end
ensure
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
expected_type: across Result as ic all ic.item.is_typed_as (a_node_type.name) end
end
nodes_of_type_with_title (a_node_type: CMS_CONTENT_TYPE; a_title: READABLE_STRING_GENERAL): LIST [CMS_NODE]

View File

@@ -59,6 +59,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
recent_nodes_of_type (a_node_type: CMS_CONTENT_TYPE; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- Recent `a_count` nodes of type `a_node_type` with an offset of `lower`.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
do

View File

@@ -13,6 +13,7 @@ inherit
CMS_NODE_STORAGE_I
redefine
nodes_of_type_count,
nodes_of_type,
nodes_of_type_with_title
end
@@ -38,6 +39,22 @@ feature -- Access
sql_finalize
end
nodes_of_type_count (a_node_type: CMS_CONTENT_TYPE): NATURAL_64
-- Count of nodes of type `a_node_type`.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.force (a_node_type.name, "node_type")
sql_query (sql_select_nodes_of_type_count, l_parameters)
if not has_error and not sql_after then
Result := sql_read_natural_64 (1)
end
sql_finalize
end
nodes: LIST [CMS_NODE]
-- List of nodes.
do
@@ -145,6 +162,31 @@ feature -- Access
sql_finalize
end
recent_nodes_of_type (a_node_type: CMS_CONTENT_TYPE; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- Recent `a_count` nodes of type `a_node_type` with an offset of `lower`.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
from
create l_parameters.make (3)
l_parameters.put (a_node_type.name, "node_type")
l_parameters.put (a_count, "size")
l_parameters.put (a_lower, "offset")
sql_query (sql_select_recent_nodes_of_type, 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
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
local
@@ -270,7 +312,7 @@ feature -- Access
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
check expected_node_type: l_node.is_typed_as (a_node_type.name) end
Result.force (l_node)
end
sql_forth
@@ -474,6 +516,10 @@ feature {NONE} -- Queries
-- Nodes count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_nodes_of_type_count: STRING = "SELECT count(*) FROM nodes WHERE type=:node_type AND status != -1 ;"
-- Nodes of type `:node_type` count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE status != -1 ;"
-- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
@@ -503,6 +549,8 @@ feature {NONE} -- Queries
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes ORDER BY changed DESC, publish DESC LIMIT :size OFFSET :offset ;"
sql_select_recent_nodes_of_type: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE type=:node_type ORDER BY changed DESC, publish DESC LIMIT :size OFFSET :offset ;"
sql_select_recent_node_changes_before: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE changed <= :date ORDER BY changed DESC, nid DESC LIMIT :size OFFSET :offset ;"
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (:revision, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);"