")
diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e
index c01330a..92ac76b 100644
--- a/modules/node/handler/node_form_response.e
+++ b/modules/node/handler/node_form_response.e
@@ -26,7 +26,7 @@ feature {NONE} -- Initialization
initialize
do
Precursor
- create {WSF_CMS_THEME} wsf_theme.make (Current, theme)
+ create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
@@ -48,7 +48,11 @@ feature -- Execution
attached node_api.node (nid) as l_node
then
if attached node_api.node_type_for (l_node) as l_type then
- if node_api.has_permission_for_action_on_node ("edit", l_node, current_user (request)) then
+ fixme ("refactor: process_edit, process_create porcess edit")
+ if
+ request.path_info.ends_with_general ("/edit") and then
+ node_api.has_permission_for_action_on_node ("edit", l_node, current_user (request))
+ then
f := new_edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type)
invoke_form_alter (f, fd)
if request.is_post_request_method then
@@ -58,8 +62,9 @@ feature -- Execution
fd := f.last_data
end
if l_node.has_id then
- add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("View", Void), node_url (l_node)), primary_tabs)
- add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), "/node/" + l_node.id.out + "/edit"), primary_tabs)
+ add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
@@ -70,6 +75,53 @@ feature -- Execution
set_title (formatted_string (translation ("Edit $1 #$2", Void), [l_type.title, l_node.id]))
f.append_to_html (wsf_theme, b)
end
+ elseif
+ request.path_info.ends_with_general ("/delete") and then
+ node_api.has_permission_for_action_on_node ("delete", l_node, current_user (request))
+ then
+ f := new_delete_form (l_node, url (request.path_info, Void), "delete-" + l_type.name, l_type)
+ invoke_form_alter (f, fd)
+ if request.is_post_request_method then
+ f.process (Current)
+ fd := f.last_data
+ end
+ if l_node.has_id then
+ add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs)
+ end
+
+ if attached redirection as l_location then
+ -- FIXME: Hack for now
+ set_title (l_node.title)
+ b.append (html_encoded (l_type.title) + " deleted")
+ else
+ set_title (formatted_string (translation ("Delete $1 #$2", Void), [l_type.title, l_node.id]))
+ f.append_to_html (wsf_theme, b)
+ end
+ elseif
+ request.path_info.ends_with_general ("/trash") and then
+ node_api.has_permission_for_action_on_node ("trash", l_node, current_user (request))
+ then
+ f := new_trash_form (l_node, url (request.path_info, Void), "trash-" + l_type.name, l_type)
+ invoke_form_alter (f, fd)
+ if request.is_post_request_method then
+ f.process (Current)
+ fd := f.last_data
+ end
+ if l_node.has_id then
+ add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", node_api.node_path (l_node) + "/Trash"), primary_tabs)
+ end
+
+ if attached redirection as l_location then
+ -- FIXME: Hack for now
+ set_title (l_node.title)
+ b.append (html_encoded (l_type.title) + " trashed")
+ else
+ set_title (formatted_string (translation ("Trash $1 #$2", Void), [l_type.title, l_node.id]))
+ f.append_to_html (wsf_theme, b)
+ end
else
b.append ("
")
b.append (translation ("Access denied", Void))
@@ -96,8 +148,8 @@ feature -- Execution
set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out)
if l_node.has_id then
- add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("View", Void), node_url (l_node)), primary_tabs)
- add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), "/node/" + l_node.id.out + "/edit"), primary_tabs)
+ add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
+ add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
end
f.append_to_html (wsf_theme, b)
@@ -121,7 +173,7 @@ feature -- Execution
attached ic.item as l_node_type and then
has_permissions (<<"create any", "create " + l_node_type.name>>)
then
- b.append (" " + link (l_node_type.name, "/node/add/" + l_node_type.name, Void))
+ b.append (" " + link (l_node_type.name, "node/add/" + l_node_type.name, Void))
if attached l_node_type.description as d then
b.append ("" + d + "
")
end
@@ -167,6 +219,7 @@ feature -- Form
l_preview: BOOLEAN
l_node: detachable CMS_NODE
s: STRING
+ l_path_alias: detachable READABLE_STRING_8
do
l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview")
if not l_preview then
@@ -198,20 +251,29 @@ feature -- Form
end
node_api.save_node (l_node)
if attached current_user (request) as u then
- api.log ("node", "User %"" + user_html_link (u) + "%" " + s + " node " + link (a_type.name +" #" + l_node.id.out, "/node/" + l_node.id.out , Void), 0, node_local_link (l_node))
+ api.log ("node",
+ "User %"" + user_html_link (u) + "%" " + s + " node " + node_html_link (l_node, a_type.name + " #" + l_node.id.out),
+ 0, node_local_link (l_node, Void)
+ )
else
- api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node))
+ api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node, Void))
end
add_success_message ("Node #" + l_node.id.out + " saved.")
- if attached fd.string_item ("path_alias") as l_path_alias then
+ if
+ attached fd.string_item ("path_alias") as f_path_alias and then
+ not f_path_alias.is_empty
+ then
+ l_path_alias := percent_encoder.partial_encoded_string (f_path_alias, <<'/'>>)
+ -- Path alias, are always from the root of the cms.
api.set_path_alias (node_api.node_path (l_node), l_path_alias, False)
l_node.set_link (create {CMS_LOCAL_LINK}.make (l_node.title, l_path_alias))
else
l_node.set_link (node_api.node_link (l_node))
end
-
- set_redirection (node_url (l_node))
+ if attached l_node.link as lnk then
+ set_redirection (lnk.location)
+ end
end
end
@@ -243,10 +305,25 @@ feature -- Form
ts.set_default_value ("Preview")
f.extend (ts)
+ Result := f
+ end
+
+
+ new_delete_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'.
+ local
+ f: CMS_FORM
+ ts: WSF_FORM_SUBMIT_INPUT
+ do
+ create f.make (a_url, a_name)
+
+ f.extend_html_text (" ")
+ f.extend_html_text ("Are you sure you want to delete? ")
+
+ -- TODO check if we need to check for has_permissions!!
if
a_node /= Void and then
- a_node.id > 0 and then
- has_permission ("delete " + a_name)
+ a_node.id > 0
then
create ts.make ("op")
ts.set_default_value ("Delete")
@@ -254,11 +331,52 @@ feature -- Form
ts.set_default_value (translation ("Delete"))
]")
f.extend (ts)
+ fixme ("wsf_html: add support for HTML5 input attributes!!! ")
+ f.extend_html_text(" " )
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'.
+ local
+ f: CMS_FORM
+ ts: WSF_FORM_SUBMIT_INPUT
+ do
+ create f.make (a_url, a_name)
+
+ f.extend_html_text (" ")
+ f.extend_html_text ("Are you sure you want to trash the current node? ")
+ if
+ a_node /= Void and then
+ a_node.id > 0
+ then
+ create ts.make ("op")
+ ts.set_default_value ("Trash")
+ fixme ("[
+ ts.set_default_value (translation ("Trash"))
+ ]")
+ f.extend (ts)
+ end
+ f.extend_html_text (" ")
+ f.extend_html_text ("Do you want to restore the current node? ")
+ if
+ a_node /= Void and then
+ a_node.id > 0
+ then
+ create ts.make ("op")
+ ts.set_default_value ("Restore")
+ fixme ("[
+ ts.set_default_value (translation ("Restore"))
+ ]")
+ f.extend (ts)
+ end
+ Result := f
+ end
+
+
populate_form (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form: WSF_FORM; a_node: detachable CMS_NODE)
-- Fill the web form `a_form' with data from `a_node' if set,
-- and apply this to content type `a_content_type'.
diff --git a/modules/node/handler/node_handler.e b/modules/node/handler/node_handler.e
index de50f39..88fb687 100644
--- a/modules/node/handler/node_handler.e
+++ b/modules/node/handler/node_handler.e
@@ -89,6 +89,14 @@ feature -- HTTP Methods
check valid_url: req.path_info.starts_with_general ("/node/") end
create edit_response.make (req, res, api, node_api)
edit_response.execute
+ elseif req.path_info.ends_with_general ("/delete") then
+ check valid_url: req.path_info.starts_with_general ("/node/") end
+ create edit_response.make (req, res, api, node_api)
+ edit_response.execute
+ elseif req.path_info.ends_with_general ("/trash") then
+ check valid_url: req.path_info.starts_with_general ("/node/") end
+ create edit_response.make (req, res, api, node_api)
+ edit_response.execute
else
-- Display existing node
l_nid := node_id_path_parameter (req)
@@ -116,14 +124,26 @@ feature -- HTTP Methods
do
fixme ("Refactor code: extract methods: edit_node and add_node")
if req.path_info.ends_with_general ("/edit") then
+ create edit_response.make (req, res, api, node_api)
+ edit_response.execute
+ elseif req.path_info.ends_with_general ("/delete") then
if
attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Delete")
then
do_delete (req, res)
- else
- create edit_response.make (req, res, api, node_api)
- edit_response.execute
+ end
+ elseif req.path_info.ends_with_general ("/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)
+ elseif
+ attached {WSF_STRING} req.form_parameter ("op") as l_op and then
+ l_op.value.same_string ("Restore")
+ then
+ do_restore (req, res)
end
elseif req.path_info.starts_with_general ("/node/add/") then
create edit_response.make (req, res, api, node_api)
@@ -174,6 +194,62 @@ feature -- HTTP Methods
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 from the database.
+ do
+ if attached current_user (req) as l_user then
+ if attached {WSF_STRING} req.path_parameter ("id") as l_id then
+ if
+ l_id.is_integer and then
+ attached node_api.node (l_id.integer_value) as l_node
+ then
+ if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then
+ node_api.trash_node (l_node)
+ res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
+ else
+ send_access_denied (req, res)
+ -- send_not_authorized ?
+ end
+ else
+ do_error (req, res, l_id)
+ end
+ else
+ (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute
+ end
+ else
+ send_access_denied (req, res)
+ end
+ end
+
+ do_restore (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
+ do
+ if attached current_user (req) as l_user then
+ if attached {WSF_STRING} req.path_parameter ("id") as l_id then
+ if
+ l_id.is_integer and then
+ attached node_api.node (l_id.integer_value) as l_node
+ then
+ if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then
+ node_api.restore_node (l_node)
+ res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
+ else
+ send_access_denied (req, res)
+ -- send_not_authorized ?
+ end
+ else
+ do_error (req, res, l_id)
+ end
+ else
+ (create {INTERNAL_SERVER_ERROR_CMS_RESPONSE}.make (req, res, api)).execute
+ end
+ else
+ send_access_denied (req, res)
+ end
+ end
+
feature -- Error
do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING)
diff --git a/modules/node/handler/node_response.e b/modules/node/handler/node_response.e
index 1bad113..6c444a5 100644
--- a/modules/node/handler/node_response.e
+++ b/modules/node/handler/node_response.e
@@ -53,30 +53,45 @@ feature -- Helpers
feature -- Helpers: cms link
- user_local_link (u: CMS_USER): CMS_LOCAL_LINK
+ user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do
- create Result.make (u.name, user_url (u))
+ if a_opt_title /= Void then
+ create Result.make (a_opt_title, user_url (u))
+ else
+ create Result.make (u.name, user_url (u))
+ end
end
- node_local_link (n: CMS_NODE): CMS_LOCAL_LINK
+ node_local_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do
if attached n.link as lnk then
Result := lnk
else
Result := node_api.node_link (n)
end
+ if a_opt_title /= Void and then not Result.title.same_string_general (a_opt_title) then
+ create Result.make (a_opt_title, Result.location)
+ end
end
feature -- Helpers: html link
user_html_link (u: CMS_USER): like link
do
- Result := link (u.name, "/user/" + u.id.out, Void)
+ Result := link (u.name, "user/" + u.id.out, Void)
end
- node_html_link (n: CMS_NODE): like link
+ node_html_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): like link
+ local
+ l_title: detachable READABLE_STRING_GENERAL
do
- Result := link (n.title, "/node/" + n.id.out, Void)
+ if a_opt_title /= Void then
+ l_title := a_opt_title
+ else
+ l_title := n.title
+ end
+ Result := link (l_title, node_api.node_path (n), Void)
+
end
feature -- Helpers: URL
@@ -85,7 +100,7 @@ feature -- Helpers: URL
require
u_with_id: u.has_id
do
- Result := url ("/user/" + u.id.out, Void)
+ Result := url ("user/" + u.id.out, Void)
end
node_url (n: CMS_NODE): like url
diff --git a/modules/node/handler/node_view_response.e b/modules/node/handler/node_view_response.e
index e62a316..aac253f 100644
--- a/modules/node/handler/node_view_response.e
+++ b/modules/node/handler/node_view_response.e
@@ -27,7 +27,7 @@ feature {NONE} -- Initialization
initialize
do
Precursor
- create {WSF_CMS_THEME} wsf_theme.make (Current, theme)
+ create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
diff --git a/modules/node/handler/nodes_handler.e b/modules/node/handler/nodes_handler.e
index 18462d2..e6f317c 100644
--- a/modules/node/handler/nodes_handler.e
+++ b/modules/node/handler/nodes_handler.e
@@ -37,22 +37,40 @@ feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
--
local
- l_page: CMS_RESPONSE
+ l_response: CMS_RESPONSE
s: STRING
n: CMS_NODE
lnk: CMS_LOCAL_LINK
+ l_page_helper: CMS_PAGINATION_GENERATOR
+ s_pager: STRING
+ l_count: NATURAL_64
do
- -- At the moment the template is hardcoded, but we can
+ -- At the moment the template are hardcoded, but we can
-- get them from the configuration file and load them into
-- the setup class.
- create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
- l_page.add_variable (node_api.nodes, "nodes")
+ l_count := node_api.nodes_count
+ create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
- -- NOTE: for development purposes we have the following hardcode output.
- create s.make_from_string ("Nodes:
")
- if attached node_api.nodes as lst then
+ 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")
+ 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
+ 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)
+ if l_page_helper.page_size > 25 then
+ s.append (s_pager)
+ 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
s.append ("%N")
across
lst as ic
@@ -60,16 +78,23 @@ feature -- HTTP Methods
n := ic.item
lnk := node_api.node_link (n)
s.append ("")
- s.append (l_page.link (lnk.title, lnk.location, Void))
--- s.append (l_page.link (n.title + " (#" + n.id.out + ")", node_api.node_path (n), Void))
+ s.append (l_response.link (lnk.title, lnk.location, Void))
+ debug
+ if attached node_api.content_type (n.content_type) as ct then
+ s.append ("")
+ s.append (html_encoded (ct.title))
+ s.append (" ")
+ end
+ end
s.append (" %N")
end
s.append (" %N")
end
+ -- Again the pager at the bottom, if needed
+ s.append (s_pager)
- l_page.set_main_content (s)
- l_page.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented ", Void), "highlighted")
- l_page.execute
+ l_response.set_main_content (s)
+ l_response.execute
end
end
diff --git a/modules/node/handler/trash_handler.e b/modules/node/handler/trash_handler.e
new file mode 100644
index 0000000..36af87d
--- /dev/null
+++ b/modules/node/handler/trash_handler.e
@@ -0,0 +1,80 @@
+note
+ description: "Request handler related to /trash "
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ TRASH_HANDLER
+
+
+inherit
+ CMS_NODE_HANDLER
+
+ WSF_URI_HANDLER
+ rename
+ new_mapping as new_uri_mapping
+ end
+
+ WSF_RESOURCE_HANDLER_HELPER
+ redefine
+ do_get
+ end
+
+ REFACTORING_HELPER
+
+create
+ make
+
+feature -- execute
+
+ execute (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Execute request handler
+ do
+ execute_methods (req, res)
+ end
+
+feature -- HTTP Methods
+
+ do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
+ --
+ local
+ l_page: CMS_RESPONSE
+ s: STRING
+ n: CMS_NODE
+ lnk: CMS_LOCAL_LINK
+ do
+ -- At the moment the template is hardcoded, but we can
+ -- get them from the configuration file and load them into
+ -- the setup class.
+
+ if attached current_user (req) as l_user then
+ create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
+
+ l_page.add_variable (node_api.trashed_nodes (l_user), "nodes")
+
+ -- NOTE: for development purposes we have the following hardcode output.
+ create s.make_from_string ("Nodes:
")
+ if attached node_api.trashed_nodes (l_user) as lst then
+ s.append ("%N")
+ across
+ lst as ic
+ loop
+ n := ic.item
+ lnk := node_api.node_link (n)
+ s.append ("")
+ s.append (l_page.link (lnk.title, lnk.location, Void))
+ s.append (" %N")
+ end
+ s.append (" %N")
+ end
+
+ l_page.set_main_content (s)
+ -- l_page.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented ", Void), "highlighted")
+ l_page.execute
+ else
+ create {FORBIDDEN_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
+ l_page.execute
+ end
+ end
+
+end
diff --git a/modules/node/node_module.e b/modules/node/node_module.e
index 8df4eab..1f495d3 100644
--- a/modules/node/node_module.e
+++ b/modules/node/node_module.e
@@ -9,14 +9,12 @@ class
inherit
CMS_MODULE
- rename
- module_api as node_api
redefine
register_hooks,
initialize,
is_installed,
install,
- node_api
+ module_api
end
CMS_HOOK_MENU_SYSTEM_ALTER
@@ -43,15 +41,26 @@ feature {NONE} -- Initialization
feature {CMS_API} -- Module Initialization
- initialize (api: CMS_API)
+ initialize (a_api: CMS_API)
--
local
p1,p2: CMS_PAGE
ct: CMS_PAGE_NODE_TYPE
l_node_api: like node_api
+ l_node_storage: CMS_NODE_STORAGE_I
do
- Precursor (api)
- create l_node_api.make (api)
+ Precursor (a_api)
+
+ -- Storage initialization
+ if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
+ create {CMS_NODE_STORAGE_SQL} l_node_storage.make (l_storage_sql)
+ else
+ -- FIXME: in case of NULL storage, should Current be disabled?
+ create {CMS_NODE_STORAGE_NULL} l_node_storage.make
+ end
+
+ -- 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.
@@ -61,7 +70,7 @@ feature {CMS_API} -- Module Initialization
-- 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 api.user_api.user_by_id (1) as u 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")
@@ -81,29 +90,42 @@ feature {CMS_API} -- Module Initialization
-- 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
feature {CMS_API} -- Module management
- is_installed (api: CMS_API): BOOLEAN
+ is_installed (a_api: CMS_API): BOOLEAN
-- Is Current module installed?
do
- if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
+ if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
Result := l_sql_storage.sql_table_exists ("nodes") and
l_sql_storage.sql_table_exists ("page_nodes")
end
end
- install (api: CMS_API)
+ install (a_api: CMS_API)
do
-- Schema
- if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
- l_sql_storage.sql_execute_file_script (api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql"))
+ if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
+ l_sql_storage.sql_execute_file_script (a_api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql"))
end
end
feature {CMS_API} -- Access: API
+ module_api: detachable CMS_MODULE_API
+ --
+ do
+ if attached node_api as l_api then
+ Result := l_api
+ else
+ -- Current is initialized, so node_api should be set.
+ check has_node_api: False end
+ end
+ end
+
node_api: detachable CMS_NODE_API
--
@@ -111,15 +133,10 @@ feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
--
- local
- l_node_api: like node_api
do
- l_node_api := node_api
- if l_node_api = Void then
- create l_node_api.make (a_api)
- node_api := l_node_api
+ if attached node_api as l_node_api then
+ configure_web (a_api, l_node_api, a_router)
end
- configure_web (a_api, l_node_api, a_router)
end
configure_web (a_api: CMS_API; a_node_api: CMS_NODE_API; a_router: WSF_ROUTER)
@@ -127,6 +144,7 @@ feature -- Access: router
l_node_handler: NODE_HANDLER
l_nodes_handler: NODES_HANDLER
l_uri_mapping: WSF_URI_MAPPING
+ l_trash_handler: TRASH_HANDLER
do
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
create l_node_handler.make (a_api, a_node_api)
@@ -135,6 +153,8 @@ feature -- Access: router
a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
+ a_router.handle_with_request_methods ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
+ a_router.handle_with_request_methods ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}", l_node_handler, a_router.methods_get)
-- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post)
@@ -143,6 +163,13 @@ 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_with_request_methods (l_uri_mapping, a_router.methods_get)
+
+ --Trash
+
+ create l_trash_handler.make (a_api, a_node_api)
+ create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
+ a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
+
end
feature -- Hooks
@@ -173,10 +200,13 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK
-- perms: detachable ARRAYED_LIST [READABLE_STRING_8]
do
- create lnk.make ("List of nodes", a_response.url ("/nodes", Void))
+ create lnk.make ("List of nodes", "nodes")
a_menu_system.primary_menu.extend (lnk)
- create lnk.make ("Create ..", a_response.url ("/node/", Void))
+ create lnk.make ("Trash", "trash")
+ a_menu_system.primary_menu.extend (lnk)
+
+ create lnk.make ("Create ..", "node")
a_menu_system.primary_menu.extend (lnk)
end
diff --git a/modules/node/persistence/cms_node_storage_i.e b/modules/node/persistence/cms_node_storage_i.e
index 6090d95..970c2bf 100644
--- a/modules/node/persistence/cms_node_storage_i.e
+++ b/modules/node/persistence/cms_node_storage_i.e
@@ -43,6 +43,8 @@ feature {NONE} -- Implementation
extended_store (a_node: CMS_NODE)
-- Store extended data from `a_node'.
+ require
+ not error_handler.has_error
do
if attached node_storage_extension (a_node) as ext then
ext.store_node (a_node)
@@ -51,16 +53,17 @@ feature {NONE} -- Implementation
extended_load (a_node: CMS_NODE)
-- Load extended data into `a_node'.
+ require
+ not error_handler.has_error
do
if attached node_storage_extension (a_node) as ext then
ext.load_node (a_node)
end
end
-
feature -- Access
- nodes_count: INTEGER_64
+ nodes_count: NATURAL_64
-- Count of nodes.
deferred
end
@@ -70,6 +73,11 @@ feature -- Access
deferred
end
+ trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes by user `a_user_id'.
+ deferred
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
deferred
@@ -108,7 +116,7 @@ feature -- Change: Node
valid_user: attached a_node.author as l_author and then l_author.id > 0
deferred
ensure
- has_id: a_node.has_id
+ has_id: not error_handler.has_error implies a_node.has_id
end
update_node (a_node: CMS_NODE)
@@ -135,32 +143,35 @@ feature -- Change: Node
deferred
end
--- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
--- -- Update node title to `a_title', node identified by id `a_node_id'.
--- -- The user `a_user_id' is an existing or new collaborator.
--- require
--- valid_node_id: a_node_id > 0
--- valid_user_id: a_user_id > 0
--- deferred
--- end
+ trash_node (a_node: CMS_NODE)
+ -- Trash `a_node'.
+ do
+ if a_node.has_id then
+ trash_node_by_id (a_node.id)
+ end
+ end
--- update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
--- -- Update node summary to `a_summary', node identified by id `a_node_id'.
--- -- The user `a_user_id' is an existing or new collaborator.
--- require
--- valid_id: a_node_id > 0
--- valid_user_id: a_user_id > 0
--- deferred
--- end
+ restore_node (a_node: CMS_NODE)
+ -- Restore `a_node'.
+ do
+ if a_node.has_id then
+ restore_node_by_id (a_node.id)
+ end
+ end
--- update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
--- -- Update node content to `a_content', node identified by id `a_node_id'.
--- -- The user `a_user_id' is an existing or new collaborator.
--- require
--- valid_id: a_node_id > 0
--- valid_user_id: a_user_id > 0
--- deferred
--- end
+ trash_node_by_id (a_id: INTEGER_64)
+ -- Trash node by id `a_id'.
+ require
+ valid_node_id: a_id > 0
+ deferred
+ end
+
+ restore_node_by_id (a_id: INTEGER_64)
+ -- Restore node by id `a_id'.
+ require
+ valid_node_id: a_id > 0
+ deferred
+ end
feature -- Helpers
diff --git a/modules/node/persistence/cms_node_storage_null.e b/modules/node/persistence/cms_node_storage_null.e
index 87559af..0bb571e 100644
--- a/modules/node/persistence/cms_node_storage_null.e
+++ b/modules/node/persistence/cms_node_storage_null.e
@@ -30,7 +30,7 @@ feature -- Error Handling
feature -- Access: node
- nodes_count: INTEGER_64
+ nodes_count: NATURAL_64
-- Count of nodes.
do
end
@@ -41,6 +41,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
+ trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes by user `a_user_id'.
+ do
+ create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of the `a_count' most recent nodes, starting from `a_lower'.
do
@@ -80,6 +86,16 @@ feature -- Node
do
end
+ trash_node_by_id (a_id: INTEGER_64)
+ --
+ do
+ end
+
+ restore_node_by_id (a_id: INTEGER_64)
+ --
+ do
+ end
+
-- update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- --
-- do
diff --git a/modules/node/persistence/cms_node_storage_sql.e b/modules/node/persistence/cms_node_storage_sql.e
index 756d61d..d14efeb 100644
--- a/modules/node/persistence/cms_node_storage_sql.e
+++ b/modules/node/persistence/cms_node_storage_sql.e
@@ -22,14 +22,14 @@ create
feature -- Access
- nodes_count: INTEGER_64
+ nodes_count: NATURAL_64
-- Number of items nodes.
do
error_handler.reset
write_information_log (generator + ".nodes_count")
sql_query (sql_select_nodes_count, Void)
if sql_rows_count = 1 then
- Result := sql_read_integer_64 (1)
+ Result := sql_read_natural_64 (1)
end
end
@@ -60,6 +60,36 @@ feature -- Access
-- end
end
+ trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
+ -- List of nodes.
+ local
+ l_parameters: STRING_TABLE [detachable ANY]
+ do
+ create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
+
+ error_handler.reset
+ write_information_log (generator + ".trash_nodes")
+
+ from
+ create l_parameters.make (1)
+ if a_user_id > 1 then
+ -- Not admin user
+ l_parameters.put (a_user_id, "author")
+ sql_query (sql_select_trash_nodes_by_author, l_parameters)
+ else
+ sql_query (sql_select_trash_nodes, Void)
+ end
+ sql_start
+ until
+ sql_after
+ loop
+ if attached fetch_node as l_node then
+ Result.force (l_node)
+ end
+ sql_forth
+ end
+ end
+
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
local
@@ -148,7 +178,7 @@ feature -- Change: Node
l_time: DATE_TIME
do
create l_time.make_now_utc
- write_information_log (generator + ".delete_node")
+ write_information_log (generator + ".delete_node {" + a_id.out + "}")
error_handler.reset
create l_parameters.make (1)
@@ -158,6 +188,40 @@ feature -- Change: Node
sql_change (sql_delete_node, l_parameters)
end
+
+ trash_node_by_id (a_id: INTEGER_64)
+ --
+ local
+ l_parameters: STRING_TABLE [ANY]
+ l_time: DATE_TIME
+ do
+ create l_time.make_now_utc
+ write_information_log (generator + ".trash_node {" + a_id.out + "}")
+
+ error_handler.reset
+ create l_parameters.make (1)
+ l_parameters.put (a_id, "nid")
+ sql_change (sql_trash_node, l_parameters)
+ end
+
+ restore_node_by_id (a_id: INTEGER_64)
+ --
+ local
+ l_parameters: STRING_TABLE [ANY]
+ l_time: DATE_TIME
+ do
+ create l_time.make_now_utc
+ write_information_log (generator + ".restore_node {" + a_id.out + "}")
+
+ error_handler.reset
+ create l_parameters.make (1)
+ l_parameters.put (l_time, "changed")
+ l_parameters.put ({CMS_NODE_API}.not_published, "status")
+ l_parameters.put (a_id, "nid")
+ sql_change (sql_restore_node, l_parameters)
+ end
+
+
feature {NONE} -- Implementation
store_node (a_node: CMS_NODE)
@@ -204,8 +268,14 @@ feature {NONE} -- Implementation
a_node.set_revision (1) -- New object.
end
end
- extended_store (a_node)
- sql_commit_transaction
+ if not error_handler.has_error then
+ extended_store (a_node)
+ end
+ if error_handler.has_error then
+ sql_rollback_transaction
+ else
+ sql_commit_transaction
+ end
end
feature -- Helpers
@@ -229,6 +299,14 @@ feature {NONE} -- Queries
-- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
+ sql_select_trash_nodes: STRING = "SELECT * FROM Nodes WHERE status = -1 ;"
+ -- SQL Query to retrieve all trahsed nodes.
+ --| note: {CMS_NODE_API}.trashed = -1
+
+ sql_select_trash_nodes_by_author: STRING = "SELECT * FROM Nodes WHERE status = -1 and author = :author ;"
+ -- SQL Query to retrieve all nodes by a given author.
+ --| note: {CMS_NODE_API}.trashed = -1
+
sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes WHERE nid =:nid ORDER BY revision DESC, publish DESC LIMIT 1;"
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes ORDER BY nid DESC, publish DESC LIMIT :rows OFFSET :offset ;"
@@ -244,6 +322,12 @@ feature {NONE} -- Queries
sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata.
+ sql_trash_node: STRING = "DELETE FROM nodes WHERE nid=:nid"
+ -- Physical deletion with free metadata.
+
+ sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
+ -- Restore node to {CMS_NODE_API}.not_publised.
+
-- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;"
-- sql_update_node_title: STRING ="UPDATE nodes SET title=:title, changed=:changed, revision = revision + 1 WHERE nid=:nid;"
diff --git a/package.iron b/package.iron
new file mode 100644
index 0000000..fd2052a
--- /dev/null
+++ b/package.iron
@@ -0,0 +1,31 @@
+package ROC_cms
+
+project
+ cms = "cms-safe.ecf"
+ cms = "cms.ecf"
+ app_env = "library/app_env/app_env-safe.ecf"
+ app_env = "library/app_env/app_env.ecf"
+ config = "library/configuration/config-safe.ecf"
+ config = "library/configuration/config.ecf"
+ app_env = "library/layout/layout-safe.ecf"
+ app_env = "library/layout/layout.ecf"
+ cms_model = "library/model/cms_model-safe.ecf"
+ cms_model = "library/model/cms_model.ecf"
+ persistence_mysql = "library/persistence/mysql/persistence_mysql-safe.ecf"
+ persistence_mysql = "library/persistence/mysql/persistence_mysql.ecf"
+ persistence_sqlite = "library/persistence/sqlite/persistence_sqlite-safe.ecf"
+ persistence_sqlite = "library/persistence/sqlite/persistence_sqlite.ecf"
+ basic_auth = "modules/basic_auth/basic_auth-safe.ecf"
+ basic_auth = "modules/basic_auth/basic_auth.ecf"
+ node = "modules/node/node-safe.ecf"
+ node = "modules/node/node.ecf"
+
+note
+ title: ROC CMS
+ description: CMS written with Eiffel
+ tags: cms, web, rest, api
+ license: Eiffel Forum v2
+ copyright: Jocelyn Fiat, Javier Velilla, Eiffel Software
+ link[source]: "Github" https://github.com/EiffelWebFramework/ROC.git
+
+end
diff --git a/src/configuration/cms_default_setup.e b/src/configuration/cms_default_setup.e
index 9a16640..64ec4b0 100644
--- a/src/configuration/cms_default_setup.e
+++ b/src/configuration/cms_default_setup.e
@@ -39,17 +39,21 @@ feature {NONE} -- Initialization
end
configure
+ local
+ l_url: like site_url
do
--| Site id, used to identified a site, this could be set to a uuid, or else
site_id := text_item_or_default ("site.id", "_EWF_CMS_NO_ID_")
-- Site url: optional, but ending with a slash
- site_url := string_8_item ("site_url")
- if attached site_url as l_url and then not l_url.is_empty then
- if l_url[l_url.count] /= '/' then
- site_url := l_url + "/"
+ l_url := string_8_item ("site_url")
+ if l_url /= Void and then not l_url.is_empty then
+ if l_url [l_url.count] /= '/' then
+ l_url := l_url + "/"
end
end
+ site_url := l_url
+
-- Site name
site_name := text_item_or_default ("site.name", "EWF::CMS")
diff --git a/src/configuration/cms_setup.e b/src/configuration/cms_setup.e
index 792f84e..1b53010 100644
--- a/src/configuration/cms_setup.e
+++ b/src/configuration/cms_setup.e
@@ -62,7 +62,7 @@ feature -- Access: Site
-- Mainly used for internal notification.
site_url: detachable READABLE_STRING_8
- -- Optional base url of the site.
+ -- Optional url of current CMS site.
front_page_path: detachable READABLE_STRING_8
-- Optional path defining the front page.
diff --git a/src/hooks/cms_hook_block_helper.e b/src/hooks/cms_hook_block_helper.e
index b36e12e..c0da7de 100644
--- a/src/hooks/cms_hook_block_helper.e
+++ b/src/hooks/cms_hook_block_helper.e
@@ -12,11 +12,12 @@ feature -- Factory
template_block (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK
-- Smarty content block for `a_block_id' in the context of `a_module' and `a_response'.
local
+ res: PATH
p: detachable PATH
do
- create p.make_from_string ("templates")
- p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
- p := a_response.module_resource_path (a_module, p)
+ create res.make_from_string ("templates")
+ res := res.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
+ p := a_response.module_resource_path (a_module, res)
if p /= Void then
if attached p.entry as e then
create Result.make (a_block_id, Void, p.parent, e)
diff --git a/src/kernel/form/cms_lower_upper_pager.e b/src/kernel/form/cms_lower_upper_pager.e
new file mode 100644
index 0000000..f46e19b
--- /dev/null
+++ b/src/kernel/form/cms_lower_upper_pager.e
@@ -0,0 +1,15 @@
+note
+ description: "Pager widget for ROC CMS."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ CMS_LOWER_UPPER_PAGER
+
+inherit
+ WSF_WIDGET_PAGER
+
+create
+ make
+
+end
diff --git a/src/modules/cms_debug_module.e b/src/modules/cms_debug_module.e
index 9b51d1b..3a25a35 100644
--- a/src/modules/cms_debug_module.e
+++ b/src/modules/cms_debug_module.e
@@ -39,7 +39,7 @@ feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
--
do
- a_router.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?)))
+ a_router.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?)), Void)
end
feature -- Hooks configuration
diff --git a/src/persistence/cms_data_query_parameters.e b/src/persistence/cms_data_query_parameters.e
new file mode 100644
index 0000000..f8c7475
--- /dev/null
+++ b/src/persistence/cms_data_query_parameters.e
@@ -0,0 +1,88 @@
+note
+ description: "[
+ Parameters associated with data query.
+ It could be query over http, or storage.
+ ]"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ CMS_DATA_QUERY_PARAMETERS
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_offset: NATURAL_64; a_size: NATURAL)
+ do
+ offset := a_offset
+ size := a_size
+ ensure
+ size_set: size = a_size
+ offset_set: offset = a_offset
+ end
+
+feature -- Access
+
+ size: NATURAL assign set_size
+ -- Number of items per page.
+
+ offset: NATURAL_64 assign set_offset
+ -- lower index of `items' pagination.
+
+ order_by: detachable READABLE_STRING_8
+ -- field to order by.
+
+ order_ascending: BOOLEAN
+ -- is ascending ordering?
+
+feature -- Element change
+
+ set_size (a_size: NATURAL)
+ -- Set `size' with `a_size'.
+ do
+ size := a_size
+ ensure
+ size_set: size = a_size
+ end
+
+ set_offset (a_offset: NATURAL_64)
+ -- Set offset with `a_offset'.
+ do
+ offset := a_offset
+ ensure
+ limit_set: offset = a_offset
+ end
+
+ set_ascending_order_by_field (a_field: detachable READABLE_STRING_8)
+ -- Pager with a order_by `a_field' asc.
+ do
+ if a_field /= Void then
+ order_by := a_field
+ order_ascending := True
+ else
+ order_by := Void
+ end
+ ensure
+ order_by_unset: a_field = Void implies order_by = Void
+ order_by_set: a_field /= Void implies attached order_by as l_order_by and then l_order_by.same_string (a_field)
+ asc_true: order_ascending
+ end
+
+ set_descending_order_by_field (a_field: detachable READABLE_STRING_8)
+ -- Pager sorting descending with field `a_field' if set, otherwise remove sorting.
+ do
+ if a_field /= Void then
+ order_by := a_field
+ order_ascending := False
+ else
+ order_by := Void
+ end
+ ensure
+ order_by_unset: a_field = Void implies order_by = Void
+ order_by_set: a_field /= Void implies attached order_by as l_order_by and then l_order_by.same_string (a_field)
+ asc_fasle: not order_ascending
+ end
+
+end
diff --git a/src/persistence/cms_storage.e b/src/persistence/cms_storage.e
index e3e880e..c45d9c1 100644
--- a/src/persistence/cms_storage.e
+++ b/src/persistence/cms_storage.e
@@ -39,6 +39,13 @@ feature -- Status report
deferred
end
+feature -- Basic operation
+
+ close
+ -- Close/disconnect current storage.
+ deferred
+ end
+
feature -- Error Handling
error_handler: ERROR_HANDLER
diff --git a/src/persistence/cms_storage_null.e b/src/persistence/cms_storage_null.e
index 17028e6..1b12b3b 100644
--- a/src/persistence/cms_storage_null.e
+++ b/src/persistence/cms_storage_null.e
@@ -45,6 +45,13 @@ feature -- Status report
Result := True
end
+feature -- Basic operation
+
+ close
+ --
+ do
+ end
+
feature -- URL aliases
set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
diff --git a/src/persistence/core/cms_core_storage_i.e b/src/persistence/core/cms_core_storage_i.e
index 89926de..ef87eba 100644
--- a/src/persistence/core/cms_core_storage_i.e
+++ b/src/persistence/core/cms_core_storage_i.e
@@ -1,6 +1,6 @@
note
description: "[
- Objects that ...
+ CMS Storage for core functionalities.
]"
author: "$Author$"
date: "$Date$"
diff --git a/src/persistence/sql/cms_storage_fs_i.e b/src/persistence/sql/cms_storage_fs_i.e
new file mode 100644
index 0000000..2cb025f
--- /dev/null
+++ b/src/persistence/sql/cms_storage_fs_i.e
@@ -0,0 +1,107 @@
+note
+ description: "Interface used to implement CMS Storage based on File system."
+ date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
+ revision: "$Revision: 96616 $"
+
+deferred class
+ CMS_STORAGE_FS_I
+
+inherit
+ SHARED_LOGGER
+
+feature {NONE} -- Initialization
+
+ make (a_location: PATH; a_api: CMS_API)
+ do
+ location := a_location
+ api := a_api
+ create error_handler.make
+ end
+
+feature -- Access
+
+ api: CMS_API
+ -- Associated CMS api.
+
+ location: PATH
+ -- File system location.
+
+feature -- Error handler
+
+ error_handler: ERROR_HANDLER
+ -- Error handler.
+
+ has_error: BOOLEAN
+ -- Last operation reported error.
+ do
+ Result := error_handler.has_error
+ end
+
+ reset_error
+ -- Reset errors.
+ do
+ error_handler.reset
+ end
+
+feature -- Helpers
+
+ save_to_file (a_text: STRING; opt_name: detachable READABLE_STRING_GENERAL)
+ local
+ s: READABLE_STRING_GENERAL
+ now: DATE_TIME
+ fut: WSF_FILE_UTILITIES [RAW_FILE]
+ do
+ create fut
+ if opt_name /= Void then
+ s := opt_name
+ else
+ create now.make_now_utc
+ s := date_to_yyyymmdd_hhmmss_string (now)
+ end
+ if attached fut.new_file (location.extended (s)) as f then
+ f.put_string (a_text)
+ f.close
+ else
+ error_handler.add_custom_error (0, "saving failure", Void)
+ end
+ end
+
+ date_to_yyyymmdd_hhmmss_string (d: DATE_TIME): STRING
+ local
+ i: INTEGER
+ do
+ create Result.make_empty
+ Result.append_integer (d.year)
+ Result.append_character ('-')
+ i := d.month
+ if i < 10 then
+ Result.append_integer (0)
+ end
+ Result.append_integer (i)
+ Result.append_character ('-')
+ i := d.day
+ if i < 10 then
+ Result.append_integer (0)
+ end
+ Result.append_integer (i)
+ Result.append_character ('_')
+ i := d.hour
+ if i < 10 then
+ Result.append_integer (0)
+ end
+ Result.append_integer (i)
+ Result.append_character ('-')
+ i := d.minute
+ if i < 10 then
+ Result.append_integer (0)
+ end
+ Result.append_integer (i)
+ Result.append_character ('-')
+ i := d.second
+ if i < 10 then
+ Result.append_integer (0)
+ end
+ Result.append_integer (i)
+ end
+
+end
diff --git a/src/persistence/sql/cms_storage_sql_builder.e b/src/persistence/sql/cms_storage_sql_builder.e
index 6000207..a1bec3b 100644
--- a/src/persistence/sql/cms_storage_sql_builder.e
+++ b/src/persistence/sql/cms_storage_sql_builder.e
@@ -40,6 +40,10 @@ feature -- Initialization
--| Node
-- FIXME: move that initialization to node module
+ -- TODO: should we move the initialization to an
+ --! external configuration file?
+ --! at the moment we only have 1 admin to the whole site.
+ --! is that ok?
l_anonymous_role.add_permission ("view any page")
a_storage.save_user_role (l_anonymous_role)
@@ -47,9 +51,11 @@ feature -- Initialization
l_authenticated_role.add_permission ("view any page")
l_authenticated_role.add_permission ("edit any page")
l_authenticated_role.add_permission ("delete page")
+ l_authenticated_role.add_permission ("trash page")
l_authenticated_role.add_permission ("view own page")
l_authenticated_role.add_permission ("edit own page")
l_authenticated_role.add_permission ("delete own page")
+ l_authenticated_role.add_permission ("trash own page")
a_storage.save_user_role (l_authenticated_role)
diff --git a/src/persistence/sql/cms_storage_sql_i.e b/src/persistence/sql/cms_storage_sql_i.e
index 7e91029..8f38ab0 100644
--- a/src/persistence/sql/cms_storage_sql_i.e
+++ b/src/persistence/sql/cms_storage_sql_i.e
@@ -217,6 +217,21 @@ feature -- Access
deferred
end
+ sql_read_natural_64 (a_index: INTEGER): NATURAL_64
+ -- Retrieved value at `a_index' position in `item'.
+ local
+ l_item: like sql_item
+ do
+ l_item := sql_item (a_index)
+ if attached {NATURAL_64} l_item as i then
+ Result := i
+ elseif attached {NATURAL_64_REF} l_item as l_value then
+ Result := l_value.item
+ else
+ Result := sql_read_integer_64 (a_index).to_natural_64
+ end
+ end
+
sql_read_integer_64 (a_index: INTEGER): INTEGER_64
-- Retrieved value at `a_index' position in `item'.
local
diff --git a/src/service/cms_api.e b/src/service/cms_api.e
index c245a43..97387fd 100644
--- a/src/service/cms_api.e
+++ b/src/service/cms_api.e
@@ -148,17 +148,15 @@ feature -- Permissions system
feature -- Query: module
- module (a_type: TYPE [detachable CMS_MODULE]): detachable CMS_MODULE
+ module (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE
-- Enabled module typed `a_type', if any.
--| usage: if attached module ({FOO_MODULE}) as mod then ...
local
- t: STRING_8
+-- t: STRING_8
l_type: TYPE [detachable CMS_MODULE]
do
- t := a_type.name
- if t.starts_with ("!") then
- t.remove_head (1)
- end
+-- t := type_name_without_annotation (a_type)
+
across
setup.modules as ic
until
@@ -171,8 +169,12 @@ feature -- Query: module
l_type := Result.generating_type
if a_type ~ l_type then
-- Found
- elseif t.same_string (l_type.name) then
+ elseif
+ attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type)
+ then
-- Found
+-- elseif t.same_string (type_name_without_annotation (l_type)) then
+-- -- Found
else
Result := Void
end
@@ -185,8 +187,11 @@ feature -- Query: module
module_api (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE_API
-- Enabled module API associated with module typed `a_type'.
do
- if attached {CMS_MODULE} module (a_type) as mod then
+ if attached module (a_type) as mod then
if mod.is_enabled then
+ if not mod.is_initialized then
+ mod.initialize (Current)
+ end
Result := mod.module_api
end
end
@@ -236,9 +241,16 @@ feature -- Query: API
feature -- Path aliases
+ is_valid_path_alias (a_alias: READABLE_STRING_8): BOOLEAN
+ do
+ Result := a_alias.is_empty or else not a_alias.starts_with_general ("/")
+ end
+
set_path_alias (a_source, a_alias: READABLE_STRING_8; a_keep_previous: BOOLEAN)
-- Set `a_alias' as alias of `a_source',
-- and eventually unset previous alias if any.
+ require
+ valid_alias: is_valid_path_alias (a_alias)
local
l_continue: BOOLEAN
do
@@ -283,8 +295,8 @@ feature -- Path aliases
-- Resolved path for alias `a_alias'.
--| the CMS supports aliases for path, and then this function simply returns
--| the effective target path/url for this `a_alias'.
- --| For instance: /articles/2015/may/this-is-an-article can be an alias to /node/123
- --| This function will return "/node/123".
+ --| For instance: articles/2015/may/this-is-an-article can be an alias to node/123
+ --| This function will return "node/123".
--| If the alias is bad (i.e does not alias real path), then this function
--| returns the alias itself.
do
@@ -310,6 +322,41 @@ feature {NONE}-- Implemenation
internal_user_api: detachable like user_api
-- Cached value for `user_api'.
+ type_name_without_annotation (a_type: TYPE [detachable ANY]): STRING
+ -- Type name for `a_type, without any annotation.
+ -- Used by `module' to search by type.
+ local
+ i,j,n: INTEGER
+ c: CHARACTER
+ do
+ create Result.make_from_string (a_type.name)
+ from
+ i := 1
+ n := Result.count
+ until
+ i > n
+ loop
+ c := Result[i]
+ if c = '!' or c = '?' then
+ Result.remove (i)
+ n := n - 1
+ elseif c.is_lower then
+ j := Result.index_of (' ', i + 1)
+ if j > 0 then
+ Result.remove_substring (i, j)
+ n := n - (j - i)
+ end
+ else
+ i := i + 1
+ end
+ end
+ if Result.starts_with ("!") or Result.starts_with ("?") then
+ Result.remove_head (1)
+ elseif Result.starts_with ("detachable ") then
+ Result.remove_head (11)
+ end
+ end
+
feature -- Environment
module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
diff --git a/src/service/cms_api_options.e b/src/service/cms_api_options.e
index 2ce3f29..ec00e2a 100644
--- a/src/service/cms_api_options.e
+++ b/src/service/cms_api_options.e
@@ -14,4 +14,10 @@ create
make,
make_from_manifest
+convert
+ make_from_manifest ({ ARRAY [TUPLE [key: STRING; value: detachable ANY]],
+ ARRAY [TUPLE [STRING_8, ARRAY [TUPLE [STRING_8, STRING_32]]]],
+ ARRAY [TUPLE [STRING_8, ARRAY [TUPLE [STRING_8, STRING_8]]]]
+ })
+
end
diff --git a/src/service/cms_service.e b/src/service/cms_execution.e
similarity index 76%
rename from src/service/cms_service.e
rename to src/service/cms_execution.e
index 918d0ef..2160cb6 100644
--- a/src/service/cms_service.e
+++ b/src/service/cms_execution.e
@@ -6,56 +6,56 @@ note
even for a specific handler.
]"
-class
- CMS_SERVICE
+deferred class
+ CMS_EXECUTION
inherit
- WSF_ROUTED_SKELETON_SERVICE
- rename
- execute as execute_service
+ WSF_FILTERED_ROUTED_SKELETON_EXECUTION
undefine
requires_proxy
redefine
+ create_router, router,
execute_default,
- create_router,
- router
- end
-
- WSF_FILTERED_SERVICE
-
- WSF_FILTER
- rename
- execute as execute_filter
+ filter_execute,
+ initialize
end
WSF_NO_PROXY_POLICY
- WSF_URI_HELPER_FOR_ROUTED_SERVICE
+ WSF_URI_HELPER_FOR_ROUTED_EXECUTION
- WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE
+ WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION
REFACTORING_HELPER
SHARED_LOGGER
-create
- make
+--create
+-- make
feature {NONE} -- Initialization
- make (a_setup: CMS_SETUP)
+ initialize
-- Build a CMS service with `a_api'
+ local
+ l_setup: CMS_SETUP
do
- create api.make (a_setup)
- initialize
+ l_setup := initial_cms_setup
+ setup_storage (l_setup)
+ setup_modules (l_setup)
+ create api.make (l_setup)
+ modules := setup.enabled_modules
+
+ initialize_cms
+ Precursor
end
- initialize
- -- Initialize various parts of the CMS service.
+ initialize_cms
do
- initialize_modules
- initialize_users
- initialize_mailer
+ write_debug_log (generator + ".initialize_cms")
+
+ -- CMS Initialization
+
-- initialize_router
-- initialize_filter: expanded here, for void-safety concern.
create_filter
@@ -71,20 +71,41 @@ feature {NONE} -- Initialization
only_enabled_modules: across modules as ic all ic.item.is_enabled end
end
- initialize_users
- -- Initialize users.
- do
+feature -- Factory
+
+ initial_cms_setup: CMS_SETUP
+ deferred
end
- initialize_mailer
- -- Initialize mailer engine.
+feature -- Access
+
+ api: CMS_API
+ -- API service.
+
+ setup: CMS_SETUP
+ -- CMS Setup.
do
- to_implement ("To Implement mailer")
+ Result := api.setup
+ end
+
+ modules: CMS_MODULE_COLLECTION
+ -- Configurator of possible modules.
+
+feature -- CMS setup
+
+ setup_modules (a_setup: CMS_SETUP)
+ -- Setup additional modules.
+ deferred
+ end
+
+ setup_storage (a_setup: CMS_SETUP)
+ deferred
end
feature -- Settings: router
router: CMS_ROUTER
+ --
create_router
-- Create `router'.
@@ -124,9 +145,9 @@ feature -- Settings: router
create l_root_handler.make (api)
create l_methods
l_methods.enable_get
- a_router.handle_with_request_methods ("/", l_root_handler, l_methods)
- a_router.handle_with_request_methods ("", l_root_handler, l_methods)
- map_uri_agent_with_request_methods ("/favicon.ico", agent handle_favicon, a_router.methods_head_get)
+ a_router.handle ("/", l_root_handler, l_methods)
+ a_router.handle ("", l_root_handler, l_methods)
+ map_uri_agent ("/favicon.ico", agent handle_favicon, a_router.methods_head_get)
end
configure_api_file_handler (a_router: WSF_ROUTER)
@@ -137,11 +158,11 @@ feature -- Settings: router
create fhdl.make_hidden_with_path (setup.theme_assets_location)
fhdl.disable_index
- fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
+ fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
do
execute_default (ia_req, ia_res)
end)
- a_router.handle_with_request_methods ("/theme/", fhdl, router.methods_GET)
+ a_router.handle ("/theme/", fhdl, router.methods_GET)
create fhdl.make_hidden_with_path (setup.environment.www_path)
@@ -150,17 +171,17 @@ feature -- Settings: router
do
execute_default (ia_req, ia_res)
end)
- a_router.handle_with_request_methods ("/", fhdl, router.methods_GET)
+ a_router.handle ("/", fhdl, router.methods_GET)
end
feature -- Execute Filter
- execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE)
+ filter_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
do
res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string)
res.put_header_line ("X-EWF-Server: CMS_v1.0")
- execute_service (req, res)
+ Precursor (req, res)
end
feature -- Filters
@@ -180,10 +201,10 @@ feature -- Filters
f.set_next (l_filter)
l_filter := f
- -- Error Filter
- create {CMS_ERROR_FILTER} f.make (api)
- f.set_next (l_filter)
- l_filter := f
+-- -- Error Filter
+-- create {CMS_ERROR_FILTER} f.make (api)
+-- f.set_next (l_filter)
+-- l_filter := f
-- Include filters from modules
l_api := api
@@ -223,20 +244,6 @@ feature -- Filters
f.set_next (Current)
end
-feature -- Access
-
- api: CMS_API
- -- API service.
-
- setup: CMS_SETUP
- -- CMS setup.
- do
- Result := api.setup
- end
-
- modules: CMS_MODULE_COLLECTION
- -- Configurator of possible modules.
-
feature -- Execution
handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE)
diff --git a/src/service/cms_module.e b/src/service/cms_module.e
index 356cb5f..1988de0 100644
--- a/src/service/cms_module.e
+++ b/src/service/cms_module.e
@@ -49,28 +49,32 @@ feature {CMS_API} -- Access: API
module_api: detachable CMS_MODULE_API
-- Eventual module api.
+ require
+ is_initialized: is_initialized
+ do
+ -- No API by default.
+ end
feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed?
do
- Result := is_enabled
- -- FIXME: implement proper installation status.
+ Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes")
end
install (api: CMS_API)
require
is_not_installed: not is_installed (api)
do
- -- Not Yet Supported
+ api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
end
uninstall (api: CMS_API)
require
is_installed: is_installed (api)
do
- -- Not Yet Supported
+ api.storage.set_custom_value ("is_initialized", "module-" + name, "no")
end
feature -- Router
diff --git a/src/service/cms_module_api.e b/src/service/cms_module_api.e
index e10c0c1..bd97ce9 100644
--- a/src/service/cms_module_api.e
+++ b/src/service/cms_module_api.e
@@ -1,6 +1,5 @@
note
description: "Summary description for {CMS_MODULE_API}."
- author: ""
date: "$Date: 2015-02-13 14:54:27 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96620 $"
diff --git a/src/service/cms_module_collection.e b/src/service/cms_module_collection.e
index 58ba768..42411f1 100644
--- a/src/service/cms_module_collection.e
+++ b/src/service/cms_module_collection.e
@@ -1,6 +1,5 @@
note
- description: "Summary description for {CMS_MODULE_COLLECTION}."
- author: ""
+ description: "Collection of CMS modules."
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
diff --git a/src/service/filter/cms_error_filter.e b/src/service/filter/cms_error_filter.e
index a07a3bc..ce85041 100644
--- a/src/service/filter/cms_error_filter.e
+++ b/src/service/filter/cms_error_filter.e
@@ -19,15 +19,19 @@ feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter
+ local
+ utf: UTF_CONVERTER
do
- fixme ("Check if it's ok to add new fetures CMS_API.has_error:BOOLEAN and CMS_API.error_description.")
+ debug ("refactor_fixme")
+ fixme ("Check if it's ok to add new features CMS_API.has_error:BOOLEAN and CMS_API.error_description.")
+ end
if not api.has_error then
api.logger.put_information (generator + ".execute with req: " + req.debug_output, Void)
if attached req.raw_header_data as l_header_data then
- api.logger.put_debug (generator + ".execute with req header: " + l_header_data, Void)
+ api.logger.put_debug (generator + ".execute with req header: " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_header_data), Void)
end
if attached req.raw_input_data as l_input_data then
- api.logger.put_debug (generator + ".execute with req input: " + l_input_data, Void)
+ api.logger.put_debug (generator + ".execute with req input: " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_input_data), Void)
end
execute_next (req, res)
else
diff --git a/src/service/handler/cms_handler.e b/src/service/handler/cms_handler.e
index 56517e4..ae2f3b9 100644
--- a/src/service/handler/cms_handler.e
+++ b/src/service/handler/cms_handler.e
@@ -36,6 +36,18 @@ feature -- Response helpers
-- res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (a_location))
end
+ send_bad_request_message (res: WSF_RESPONSE)
+ -- Send via `res' a bad request response.
+ do
+ res.send (create {CMS_CUSTOM_RESPONSE_MESSAGE}.make ({HTTP_STATUS_CODE}.bad_request))
+ end
+
+ send_not_found_message (res: WSF_RESPONSE)
+ -- Send via `res' a bad request response.
+ do
+ res.send (create {CMS_CUSTOM_RESPONSE_MESSAGE}.make ({HTTP_STATUS_CODE}.not_found))
+ end
+
send_access_denied_message (res: WSF_RESPONSE)
-- Send via `res' an access denied response.
do
diff --git a/src/service/misc/cms_request_util.e b/src/service/misc/cms_request_util.e
index e29bc61..f49337a 100644
--- a/src/service/misc/cms_request_util.e
+++ b/src/service/misc/cms_request_util.e
@@ -70,14 +70,4 @@ feature -- Media Type
end
end
-feature -- Absolute Host
-
- absolute_host (req: WSF_REQUEST; a_path:STRING): STRING
- do
- Result := req.absolute_script_url (a_path)
- if Result.last_index_of ('/', Result.count) = Result.count then
- Result.remove_tail (1)
- end
- end
-
end
diff --git a/src/service/misc/cms_url_utilities.e b/src/service/misc/cms_url_utilities.e
index 73feb92..89505a6 100644
--- a/src/service/misc/cms_url_utilities.e
+++ b/src/service/misc/cms_url_utilities.e
@@ -13,6 +13,7 @@ inherit
feature -- Core
site_url: READABLE_STRING_8
+ -- Absolute site URL of Current CMS site.
deferred
end
diff --git a/src/service/path/cms_router.e b/src/service/path/cms_router.e
index fdc0452..ce1982e 100644
--- a/src/service/path/cms_router.e
+++ b/src/service/path/cms_router.e
@@ -31,8 +31,22 @@ feature {WSF_ROUTER_MAPPING} -- Dispatch helper
path_to_dispatch (req: WSF_REQUEST): READABLE_STRING_8
-- Path used by the router, to apply url dispatching of request `req'.
+ local
+ l_path: STRING_8
do
- Result := api.source_of_path_alias (Precursor (req))
+ create l_path.make_from_string (Precursor (req))
+ if not l_path.is_empty and l_path [1] = '/' then
+ l_path.remove_head (1)
+ end
+ Result := api.source_of_path_alias (l_path)
+ if Result.is_empty then
+ Result := "/"
+ elseif Result [1] /= '/' then
+ create l_path.make (Result.count + 1)
+ l_path.append_character ('/')
+ l_path.append (Result)
+ Result := l_path
+ end
end
end
diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e
index 18d99d4..234a0c8 100644
--- a/src/service/response/cms_response.e
+++ b/src/service/response/cms_response.e
@@ -3,8 +3,8 @@ note
Generic CMS Response.
It builds the content to get process to render the output.
]"
- date: "$Date: 2015-02-16 20:14:19 +0100 (lun., 16 févr. 2015) $"
- revision: "$Revision: 96643 $"
+ date: "$Date: 2015-05-20 11:48:26 +0200 (mer., 20 mai 2015) $"
+ revision: "$Revision: 97327 $"
deferred class
CMS_RESPONSE
@@ -29,6 +29,7 @@ feature {NONE} -- Initialization
initialize
do
+ initialize_site_url
get_theme
create menu_system.make
initialize_block_region_settings
@@ -36,6 +37,35 @@ feature {NONE} -- Initialization
register_hooks
end
+ initialize_site_url
+ -- Initialize site and base url.
+ local
+ l_url: detachable STRING_8
+ i,j: INTEGER
+ do
+ --| WARNING: do not use `absolute_url' and `url', since it relies on site_url and base_url.
+ if attached setup.site_url as l_site_url and then not l_site_url.is_empty then
+ create l_url.make_from_string (l_site_url)
+ else
+ l_url := request.absolute_script_url ("/")
+ end
+ check is_not_empty: not l_url.is_empty end
+ if l_url [l_url.count] /= '/' then
+ l_url.append_character ('/')
+ end
+ site_url := l_url
+ i := l_url.substring_index ("://", 1)
+ if i > 0 then
+ j := l_url.index_of ('/', i + 3)
+ if j > 0 then
+ base_url := l_url.substring (j, l_url.count)
+ end
+ end
+ ensure
+ site_url_set: site_url /= Void
+ site_url_ends_with_slash: site_url.ends_with_general ("/")
+ end
+
register_hooks
local
l_module: CMS_MODULE
@@ -76,6 +106,15 @@ feature -- Access
redirection: detachable READABLE_STRING_8
-- Location for eventual redirection.
+ location: STRING_8
+ -- Associated cms local location.
+ do
+ create Result.make_from_string (request.percent_encoded_path_info)
+ if not Result.is_empty and then Result[1] = '/' then
+ Result.remove_head (1)
+ end
+ end
+
feature -- Internationalization (i18n)
translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32
@@ -121,9 +160,11 @@ feature -- Module
rp: PATH
ut: FILE_UTILITIES
do
+ -- Check first in selected theme folder.
rp := module_assets_theme_location (a_module)
Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then
+ -- And if not found, look into site/modules/$a_module.name/.... folders.
rp := module_assets_location (a_module)
Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then
@@ -163,16 +204,13 @@ feature -- URL utilities
end
end
- site_url: READABLE_STRING_8
- do
- Result := absolute_host (request, "")
- end
+ site_url: IMMUTABLE_STRING_8
+ -- Absolute site url.
- base_url: detachable READABLE_STRING_8
+ base_url: detachable IMMUTABLE_STRING_8
-- Base url if any.
--| Usually it is Void, but it could be
--| /project/demo/
- --| FIXME: for now, no way to change that. Always at the root "/"
feature -- Access: CMS
@@ -183,7 +221,7 @@ feature -- Access: CMS
front_page_url: READABLE_STRING_8
do
- Result := request.absolute_script_url ("/")
+ Result := absolute_url ("/", Void)
end
values: CMS_VALUE_TABLE
@@ -806,9 +844,9 @@ feature -- Theme
create l_info.make_default
end
if l_info.engine.is_case_insensitive_equal_general ("smarty") then
- create {SMARTY_CMS_THEME} theme.make (setup, l_info)
+ create {SMARTY_CMS_THEME} theme.make (setup, l_info, site_url)
else
- create {MISSING_CMS_THEME} theme.make (setup)
+ create {MISSING_CMS_THEME} theme.make (setup, l_info, site_url)
status_code := {HTTP_STATUS_CODE}.service_unavailable
to_implement ("Check how to add the Retry-after, http://tools.ietf.org/html/rfc7231#section-6.6.4 and http://tools.ietf.org/html/rfc7231#section-7.1.3")
end
@@ -834,7 +872,7 @@ feature -- Generation
lnk: CMS_LINK
do
-- Menu
- create {CMS_LOCAL_LINK} lnk.make ("Home", "/")
+ create {CMS_LOCAL_LINK} lnk.make ("Home", "")
lnk.set_weight (-10)
add_to_primary_menu (lnk)
invoke_menu_system_alter (menu_system)
@@ -920,8 +958,8 @@ feature -- Generation
end
-- Variables
- page.register_variable (request.absolute_script_url (""), "site_url")
- page.register_variable (request.absolute_script_url (""), "host") -- Same as `site_url'.
+ page.register_variable (absolute_url ("", Void), "site_url")
+ page.register_variable (absolute_url ("", Void), "host") -- Same as `site_url'.
page.register_variable (request.is_https, "is_https")
if attached current_user_name (request) as l_user then
page.register_variable (l_user, "user")
@@ -1029,6 +1067,9 @@ feature -- Generation
l_is_active: BOOLEAN
do
create qs.make_from_string (request.percent_encoded_path_info)
+ if qs.starts_with ("/") then
+ qs.remove_head (1)
+ end
l_is_active := qs.same_string (a_lnk.location)
if not l_is_active then
if attached request.query_string as l_query_string and then not l_query_string.is_empty then
@@ -1102,8 +1143,8 @@ feature {NONE} -- Execution
-- h.put_location (l_location)
response.redirect_now (l_location)
else
--- h.put_location (request.absolute_script_url (l_location))
- response.redirect_now (request.absolute_script_url (l_location))
+-- h.put_location (request.absolute_url (l_location, Void))
+ response.redirect_now (absolute_url (l_location, Void))
end
else
h.put_header_object (header)
diff --git a/src/service/response/error/bad_request_error_cms_response.e b/src/service/response/error/bad_request_error_cms_response.e
index 4c15239..002a3c3 100644
--- a/src/service/response/error/bad_request_error_cms_response.e
+++ b/src/service/response/error/bad_request_error_cms_response.e
@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
- page.register_variable (request.absolute_script_url (request.path_info), "request")
+ page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.bad_request)
page.register_variable (page.status_code.out, "code")
end
diff --git a/src/service/response/error/forbidden_error_cms_response.e b/src/service/response/error/forbidden_error_cms_response.e
index 5616c8b..62518e5 100644
--- a/src/service/response/error/forbidden_error_cms_response.e
+++ b/src/service/response/error/forbidden_error_cms_response.e
@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
- page.register_variable (request.absolute_script_url (request.path_info), "request")
+ page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.forbidden)
page.register_variable (page.status_code.out, "code")
end
diff --git a/src/service/response/error/internal_server_error_cms_response.e b/src/service/response/error/internal_server_error_cms_response.e
index 43eb930..f23a871 100644
--- a/src/service/response/error/internal_server_error_cms_response.e
+++ b/src/service/response/error/internal_server_error_cms_response.e
@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
- page.register_variable (request.absolute_script_url (request.path_info), "request")
+ page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
page.register_variable (page.status_code.out, "code")
end
diff --git a/src/service/response/error/not_found_error_cms_response.e b/src/service/response/error/not_found_error_cms_response.e
index 2f9ae36..2c7d63c 100644
--- a/src/service/response/error/not_found_error_cms_response.e
+++ b/src/service/response/error/not_found_error_cms_response.e
@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
- page.register_variable (request.absolute_script_url (request.path_info), "request")
+ page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_found)
page.register_variable (page.status_code.out, "code")
end
diff --git a/src/service/response/error/not_implemented_error_cms_response.e b/src/service/response/error/not_implemented_error_cms_response.e
index 159b9b4..e46e9fd 100644
--- a/src/service/response/error/not_implemented_error_cms_response.e
+++ b/src/service/response/error/not_implemented_error_cms_response.e
@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
- page.register_variable (request.absolute_script_url (request.path_info), "request")
+ page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
page.register_variable (page.status_code.out, "code")
end
diff --git a/src/service/response/message/cms_custom_response_message.e b/src/service/response/message/cms_custom_response_message.e
new file mode 100644
index 0000000..7c2f435
--- /dev/null
+++ b/src/service/response/message/cms_custom_response_message.e
@@ -0,0 +1,64 @@
+note
+ description: "Custom cms response message."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ CMS_CUSTOM_RESPONSE_MESSAGE
+
+inherit
+ CMS_RESPONSE_MESSAGE
+ redefine
+ send_payload_to
+ end
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_code: INTEGER)
+ -- Set `status_code' to `a_code'.
+ require
+ a_code_valid: a_code > 0
+ do
+ initialize
+ status_code := a_code
+ end
+
+feature -- Access
+
+ payload: detachable READABLE_STRING_8
+ -- Optional payload.
+
+feature -- Element change
+
+ set_status_code (a_code: INTEGER)
+ -- Set `status_code' to `a_code'.
+ require
+ a_code_valid: a_code > 0
+ do
+ status_code := a_code
+ end
+
+ set_payload (s: detachable READABLE_STRING_8)
+ -- Set `payload' to `s'.
+ do
+ if s /= Void then
+ payload := s
+ header.put_content_length (s.count)
+ else
+ end
+ end
+
+feature {WSF_RESPONSE} -- Output
+
+ send_payload_to (res: WSF_RESPONSE)
+ -- Send payload data to response `res'.
+ do
+ if attached payload as s then
+ res.put_string (s)
+ end
+ end
+
+end
diff --git a/src/support/cms_pagination_generator.e b/src/support/cms_pagination_generator.e
new file mode 100644
index 0000000..64ee463
--- /dev/null
+++ b/src/support/cms_pagination_generator.e
@@ -0,0 +1,284 @@
+note
+ description: "Pagination class to generate html pagination links and header summary."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ CMS_PAGINATION_GENERATOR
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_resource: READABLE_STRING_8; a_count: NATURAL_64; a_page_size: NATURAL)
+ -- Create an object with a pages of size `a_page_size'.
+ -- If `a_page_size' is zero, use default pagination size.
+ require
+ a_page_size > 0
+ do
+ create resource.make (a_resource)
+ set_page_size (a_page_size)
+ set_upper (a_count)
+ set_current_page_index (1)
+
+ maximum_ith_page_links := 7
+ page_parameter_id := "page"
+ size_parameter_id := "size"
+ set_first_text_id ("<<")
+ set_prev_text_id ("<")
+ set_next_text_id (">")
+ set_last_text_id (">>")
+ end
+
+feature -- Access
+
+ resource: URI_TEMPLATE
+ -- Resource associated with current pager.
+
+ page_size: NATURAL
+ -- Number of items per page.
+
+ upper: NATURAL_64
+ -- number of items.
+ -- if zero, no upper limit.
+
+ current_page_index: INTEGER
+ -- Current page index.
+
+ current_page_offset: NATURAL_64
+ -- Lower index - 1 for current page.
+ do
+ if current_page_index > 1 then
+ Result := (current_page_index - 1).to_natural_32 * page_size
+ else
+ Result := 0
+ end
+ end
+
+feature -- Status report
+
+ has_upper_limit: BOOLEAN
+ -- Upper limit known?
+ do
+ Result := upper > 0
+ end
+
+ pages_count: INTEGER
+ -- Number of pages.
+ -- If upper is
+ require
+ has_upper_limit: has_upper_limit
+ do
+ Result := (upper // page_size.as_natural_64).to_integer_32
+ if upper \\ page_size.to_natural_64 > 0 then
+ Result := Result + 1
+ end
+ end
+
+feature -- Parameters
+
+ page_parameter_id: STRING
+ -- Parameter id for page value.
+
+ size_parameter_id: STRING
+ -- Parameter id for size value.
+
+ maximum_ith_page_links: INTEGER
+ -- Maximum number of numeric ith page link.
+ -- ex: max = 6 gives "1 2 3 4 5 6 ... > >>".
+
+ label_first: IMMUTABLE_STRING_32
+ label_previous: IMMUTABLE_STRING_32
+ label_next: IMMUTABLE_STRING_32
+ label_last: IMMUTABLE_STRING_32
+
+feature -- Element change
+
+ set_page_size (a_size: NATURAL)
+ -- Set `page_size' to `a_size'.
+ do
+ page_size := a_size
+ end
+
+ set_upper (a_upper: NATURAL_64)
+ -- Set pages count, or upper limit `upper' to `a_size'.
+ do
+ upper := a_upper
+ end
+
+ set_current_page_index (a_page_index: like current_page_index)
+ -- Set Current page index to `a_page_index'.
+ do
+ current_page_index := a_page_index
+ end
+
+ set_page_parameter_id (a_id: READABLE_STRING_8)
+ -- Set "page" query parameter to `a_id'.
+ do
+ page_parameter_id := a_id
+ end
+
+ set_size_parameter_id (a_id: READABLE_STRING_8)
+ -- Set "size" query parameter to `a_id'.
+ do
+ size_parameter_id := a_id
+ end
+
+ get_setting_from_request (req: WSF_REQUEST)
+ -- Get various pager related settings from request `req' query paramenters.
+ -- Using `page_parameter_id' and `size_parameter_id' value for parameter names.
+ do
+ -- Size
+ if
+ attached {WSF_STRING} req.query_parameter (size_parameter_id) as l_size and then
+ attached l_size.value as l_value and then
+ l_value.is_natural
+ then
+ set_page_size (l_value.to_natural_32)
+ else
+ -- Keep default size
+ end
+
+ -- Page
+ if
+ attached {WSF_STRING} req.query_parameter (page_parameter_id) as l_page and then
+ l_page.is_integer
+ then
+ set_current_page_index (l_page.integer_value)
+ else
+ set_current_page_index (1)
+ end
+ end
+
+ set_first_text_id (s: READABLE_STRING_GENERAL)
+ -- Set label for "First" link to `s'.
+ -- default: "<<"
+ do
+ create label_first.make_from_string_general (s)
+ end
+
+ set_prev_text_id (s: READABLE_STRING_GENERAL)
+ -- Set label for "Prev" link to `s'.
+ -- default: "<"
+ do
+ create label_previous.make_from_string_general (s)
+ end
+
+ set_next_text_id (s: READABLE_STRING_GENERAL)
+ -- Set label for "Next" link to `s'.
+ -- default: ">"
+ do
+ create label_next.make_from_string_general (s)
+ end
+
+ set_last_text_id (s: READABLE_STRING_GENERAL)
+ -- Set label for "Last" link to `s'.
+ -- default: ">>"
+ do
+ create label_last.make_from_string_general (s)
+ end
+
+ set_maximum_ith_page_links (nb: INTEGER)
+ -- Set `maximum_ith_page_links' to `nb'.
+ do
+ maximum_ith_page_links := nb
+ end
+
+feature -- Conversion
+
+ pagination_links: ARRAYED_LIST [CMS_LOCAL_LINK]
+ -- CMS local links related to Current paginations.
+ local
+ lnk: CMS_LOCAL_LINK
+ tb: HASH_TABLE [detachable ANY, STRING_8]
+ curr, max: INTEGER
+ i,j: INTEGER
+ do
+ create Result.make (maximum_ith_page_links)
+
+ curr := current_page_index
+ if has_upper_limit then
+ max := pages_count
+ else
+ max := curr + page_size.to_integer_32
+ end
+
+ create tb.make (2)
+ tb.force (page_size, size_parameter_id)
+ tb.force (1, page_parameter_id)
+ if curr > 1 then
+ create lnk.make (label_first, resource.expanded_string (tb))
+ Result.force (lnk)
+ end
+
+ if curr > 1 then
+ tb.force (curr - 1, "page")
+ create lnk.make (label_previous, resource.expanded_string (tb))
+ Result.force (lnk)
+ end
+ from
+ if curr >= maximum_ith_page_links // 2 then
+ i := curr - (maximum_ith_page_links // 2)
+ else
+ i := 1
+ end
+ j := 0
+ until
+ j >= maximum_ith_page_links or (has_upper_limit and then i > max)
+ loop
+ tb.force (i, "page")
+ create lnk.make (i.out, resource.expanded_string (tb))
+ lnk.set_is_active (i = curr)
+ Result.force (lnk)
+ j := j + 1
+ i := i + 1
+ end
+ if not has_upper_limit or else i < max then
+ tb.force (i, "page")
+ create lnk.make ("...", resource.expanded_string (tb))
+ Result.force (lnk)
+ end
+
+ if curr < max then
+ tb.force (curr + 1, "page")
+ create lnk.make (label_next, resource.expanded_string (tb))
+ Result.force (lnk)
+ end
+
+ if upper > 0 and curr /= max then
+ tb.force (max, "page")
+ create lnk.make (label_last, resource.expanded_string (tb))
+ Result.force (lnk)
+ end
+ end
+
+feature -- Convertion
+
+ append_to_html (a_response: CMS_RESPONSE; a_output: STRING)
+ -- Append html pager to `a_output' in the context of `a_response'.
+ -- note: First, [Prev], [Next], Last.
+ local
+ lnk: CMS_LOCAL_LINK
+ do
+ a_output.append ("")
+ end
+
+end
diff --git a/src/theme/cms_theme.e b/src/theme/cms_theme.e
index d90c625..0bc2b69 100644
--- a/src/theme/cms_theme.e
+++ b/src/theme/cms_theme.e
@@ -1,7 +1,7 @@
note
description: "Abstract class describing a generic theme"
- date: "$Date: 2015-02-16 12:52:35 +0100 (lun., 16 févr. 2015) $"
- revision: "$Revision: 96630 $"
+ date: "$Date: 2015-05-20 11:48:26 +0200 (mer., 20 mai 2015) $"
+ revision: "$Revision: 97327 $"
deferred class
CMS_THEME
@@ -9,6 +9,8 @@ deferred class
inherit
CMS_ENCODERS
+ CMS_URL_UTILITIES
+
REFACTORING_HELPER
@@ -16,6 +18,12 @@ feature {NONE} -- Access
setup: CMS_SETUP
+ site_url: IMMUTABLE_STRING_8
+ -- Absolute URL for Current CMS site.
+
+ base_url: detachable IMMUTABLE_STRING_8
+ -- Optional base url of current CMS site.
+
feature -- Access
name: STRING
@@ -41,6 +49,31 @@ feature -- Status report
Result := across regions as ic some a_region_name.is_case_insensitive_equal (ic.item) end
end
+feature -- Element change
+
+ set_site_url (a_url: READABLE_STRING_8)
+ -- Set `site_url' to `a_url'.
+ require
+ a_url.ends_with_general ("/")
+ local
+ i,j: INTEGER
+ do
+ base_url := Void
+ if a_url [a_url.count] = '/' then
+ create site_url.make_from_string (a_url)
+ else
+ create site_url.make_from_string (a_url + "/")
+ end
+
+ i := a_url.substring_index ("://", 1)
+ if i > 0 then
+ j := a_url.index_of ('/', i + 3)
+ if j > 0 then
+ create base_url.make_from_string (a_url.substring (j, a_url.count))
+ end
+ end
+ end
+
feature -- Conversion
menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN; a_options: detachable CMS_HTML_OPTIONS): STRING_8
@@ -133,7 +166,11 @@ feature {NONE} -- Implementation
else
s.append ("")
end
- s.append ("" + html_encoded (lnk.title) + " ")
+ s.append ("")
+ s.append (html_encoded (lnk.title))
+ s.append (" ")
if
(lnk.is_expanded or lnk.is_collapsed) and then
attached lnk.children as l_children
diff --git a/src/theme/missing_theme/missing_cms_theme.e b/src/theme/missing_theme/missing_cms_theme.e
index b6c5ab9..bd767a5 100644
--- a/src/theme/missing_theme/missing_cms_theme.e
+++ b/src/theme/missing_theme/missing_cms_theme.e
@@ -18,15 +18,19 @@ create
feature {NONE} -- Initialization
- make (a_setup: like setup)
+ make (a_setup: like setup; a_info: like information; abs_site_url: READABLE_STRING_8)
do
setup := a_setup
+ information := a_info
+ set_site_url (abs_site_url)
ensure
setup_set: setup = a_setup
end
feature -- Access
+ information: CMS_THEME_INFORMATION
+
name: STRING = "missing theme"
regions: ARRAY [STRING]
diff --git a/src/theme/smarty_theme/smarty_cms_theme.e b/src/theme/smarty_theme/smarty_cms_theme.e
index 5f408e4..4ff9657 100644
--- a/src/theme/smarty_theme/smarty_cms_theme.e
+++ b/src/theme/smarty_theme/smarty_cms_theme.e
@@ -14,7 +14,7 @@ create
feature {NONE} -- Initialization
- make (a_setup: like setup; a_info: like information;)
+ make (a_setup: like setup; a_info: like information; abs_site_url: READABLE_STRING_8)
do
setup := a_setup
information := a_info
@@ -23,6 +23,7 @@ feature {NONE} -- Initialization
else
templates_directory := a_setup.theme_location
end
+ set_site_url (abs_site_url)
ensure
setup_set: setup = a_setup
information_set: information = a_info
diff --git a/modules/node/handler/wsf_cms_theme.e b/src/theme/wsf/cms_to_wsf_theme.e
similarity index 65%
rename from modules/node/handler/wsf_cms_theme.e
rename to src/theme/wsf/cms_to_wsf_theme.e
index 8ef9e3a..c3cf6cc 100644
--- a/modules/node/handler/wsf_cms_theme.e
+++ b/src/theme/wsf/cms_to_wsf_theme.e
@@ -1,10 +1,10 @@
note
- description: "Summary description for {WSF_CMS_THEME}."
+ description: "Theme from the EWF framework, but dedicated for the CMS."
date: "$Date$"
revision: "$Revision$"
class
- WSF_CMS_THEME
+ CMS_TO_WSF_THEME
inherit
WSF_THEME
@@ -25,13 +25,14 @@ feature -- Access
request: WSF_REQUEST
- response: detachable CMS_RESPONSE
+ response: CMS_RESPONSE
cms_theme: CMS_THEME
feature -- Element change
set_response (a_response: CMS_RESPONSE)
+ -- Set `response' to `a_response'.
do
response := a_response
end
@@ -39,20 +40,15 @@ feature -- Element change
feature -- Core
site_url: READABLE_STRING_8
+ -- CMS site url.
do
- if attached response as r then
- Result := r.site_url
- else
- Result := request.absolute_script_url ("")
- end
+ Result := response.site_url
end
base_url: detachable READABLE_STRING_8
-- Base url if any.
do
- if attached response as r then
- Result := r.base_url
- end
+ Result := response.base_url
end
end
diff --git a/src/theme/wsf/wsf_cms_theme.e b/src/theme/wsf/wsf_cms_theme.e
new file mode 100644
index 0000000..7d8d69a
--- /dev/null
+++ b/src/theme/wsf/wsf_cms_theme.e
@@ -0,0 +1,17 @@
+note
+ description: "Summary description for {WSF_CMS_THEME}."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ WSF_CMS_THEME
+
+obsolete "Use CMS_TO_WSF_THEME [2015-May]"
+
+inherit
+ CMS_TO_WSF_THEME
+
+create
+ make
+
+end
diff --git a/modules/node/handler/wsf_null_theme.e b/src/theme/wsf/wsf_null_theme.e
similarity index 100%
rename from modules/node/handler/wsf_null_theme.e
rename to src/theme/wsf/wsf_null_theme.e
diff --git a/tools/install.bat b/tools/install.bat
new file mode 100644
index 0000000..5770228
--- /dev/null
+++ b/tools/install.bat
@@ -0,0 +1,71 @@
+@echo off
+setlocal
+set TMP_EXCLUDE=%~dp0.install_ROC-copydir-exclude
+set COPYCMD= xcopy /EXCLUDE:%TMP_EXCLUDE% /I /E /Y
+set TMP_DIR=%~dp0..
+set SAFE_RMDIR=rd /q/s
+
+echo EIFGENs > %TMP_EXCLUDE%
+echo .git >> %TMP_EXCLUDE%
+echo .svn >> %TMP_EXCLUDE%
+
+set TMP_TARGET_DIR=%1
+if -%TMP_TARGET_DIR%- == -- goto ask_target_dir
+goto start
+
+:ask_target_dir
+echo Please provide a installation directory (target library)
+if -%ISE_LIBRARY%- == -- set ISE_LIBRARY=%EIFFEL_LIBRARY%
+if -%ISE_LIBRARY%- == -- set ISE_LIBRARY=%ISE_EIFFEL%
+if -%EIFFEL_LIBRARY%- == -- set EIFFEL_LIBRARY=%ISE_LIBRARY%
+echo 1: using $EIFFEL_LIBRARY=%EIFFEL_LIBRARY%
+echo 2: using $ISE_LIBRARY=%ISE_LIBRARY%
+echo 3: using current directory=%CD%\ewf
+CHOICE /C 123q /M " > selection:"
+if .%ERRORLEVEL%. == .1. goto use_eiffel_library
+if .%ERRORLEVEL%. == .2. goto use_ise_library
+if .%ERRORLEVEL%. == .3. goto use_current_dir
+echo No target directory were specified, you can pass it using the command line
+echo Usage: install_ewf {target_directory}
+echo Bye ...
+goto end
+
+:use_eiffel_library
+if -%EIFFEL_LIBRARY%- == -- goto use_ise_library
+set TMP_TARGET_DIR=%EIFFEL_LIBRARY%
+goto start
+
+:use_ise_library
+if -%ISE_LIBRARY%- == -- goto use_current_dir
+set TMP_TARGET_DIR=%ISE_LIBRARY%
+goto start
+
+:use_current_dir
+set TMP_TARGET_DIR=%CD%\ewf
+goto start
+
+:start
+set TMP_CONTRIB_DIR=%TMP_TARGET_DIR%\contrib
+set TMP_UNSTABLE_DIR=%TMP_TARGET_DIR%\unstable
+
+echo Install ROC as CMS ewf
+%SAFE_RMDIR% %TMP_UNSTABLE_DIR%\library\cms\library
+%SAFE_RMDIR% %TMP_UNSTABLE_DIR%\library\cms\src
+%SAFE_RMDIR% %TMP_UNSTABLE_DIR%\library\cms\doc
+%SAFE_RMDIR% %TMP_UNSTABLE_DIR%\library\cms\modules
+%SAFE_RMDIR% %TMP_UNSTABLE_DIR%\library\cms\examples
+
+%COPYCMD% %TMP_DIR%\library %TMP_UNSTABLE_DIR%\library\web\cms\library
+%COPYCMD% %TMP_DIR%\src %TMP_UNSTABLE_DIR%\library\web\cms\src
+%COPYCMD% %TMP_DIR%\doc %TMP_UNSTABLE_DIR%\library\web\cms\doc
+%COPYCMD% %TMP_DIR%\modules %TMP_UNSTABLE_DIR%\library\web\cms\modules
+%COPYCMD% %TMP_DIR%\examples %TMP_UNSTABLE_DIR%\library\web\cms\examples
+
+copy %TMP_DIR%\cms.ecf %TMP_UNSTABLE_DIR%\library\web\cms\cms.ecf
+copy %TMP_DIR%\cms-safe.ecf %TMP_UNSTABLE_DIR%\library\web\cms\cms-safe.ecf
+copy %TMP_DIR%\README.md %TMP_UNSTABLE_DIR%\library\web\cms\README.md
+copy %TMP_DIR%\package.iron %TMP_UNSTABLE_DIR%\library\web\cms\package.iron
+
+:end
+del %TMP_EXCLUDE%
+