Compare commits

...

52 Commits

Author SHA1 Message Date
b8cfff487a Removed dependency from pagination to cms_data_query_parameters
TODO: review and fix any NATURAL_64 truncation.
2015-06-02 15:56:27 +02:00
af8f410684 Updated the CMS pagination component.
Harmonized count type to NATURAL_64 for recent_nodes (more to do later).
Fix get is active code.
2015-06-02 15:39:08 +02:00
cede341301 Merged remote-tracking branch 'jvelilla/roc_pg' into pagination
Renamed pagination related classes, and moved them to cms library under "support" cluster.
2015-05-31 22:43:19 +02:00
70d53b3ef1 Provide a default CMS_MODULE.is_installed: BOOLEAN implementation based on storage of custom value.
Now use CMS_MODULE.is_initialized: BOOLEAN as precondition of many routines.
Instantiation of node storage is now done in NODE_MODULE and not any more in CMS_NODE_API.
CMS_NODE_API can be instantiated only by NODE_MODULE.
2015-05-29 19:20:31 +02:00
jvelilla
f056b43ddc Updated CMS PAGINATION with the last Jocelyn's suggestion.
Added a node pagination helper, to build the html links and header related to
pagination.
2015-05-29 09:25:28 -03:00
jvelilla
957ca96bc5 Updated code based on comments. 2015-05-27 11:26:49 -03:00
jvelilla
ad9dd01f22 Update based on comments 2015-05-26 20:25:45 -03:00
jvelilla
323ac598d0 Updated code based on comments 2015-05-26 20:24:05 -03:00
b77c5cd93c Added abstraction of cms storage on file system. (mostly helpers features). 2015-05-22 23:04:09 +02:00
77f52388c1 Improved site_url and base_url interface and initialization.
Added CMS_CUSTOM_RESPONSE_MESSAGE interface to send easily simple response message.
Updated CMS_RESPONSE to use CMS_CUSTOM_RESPONSE_MESSAGE
2015-05-22 22:37:18 +02:00
jvelilla
306b39ab78 Updated code based on Jocelyn's suggestions. 2015-05-21 16:46:06 -03:00
jvelilla
53f3162b4a Inital page builder implementation to add paging support to cms_nodes. 2015-05-19 18:40:57 -03:00
036013a0a2 Use CMS local location (i.e relative) and not url for cms local links. 2015-05-19 22:02:56 +02:00
50da24d1af Added support for base_url (i.e the CMS can be hosted on the root, or sub folder).
Local paths are relative to cms site url (i.e no starting slash).
Favor CMS_RESPONSE.absolute_url and url .. instead of using directly WSF_REQUEST.absolute_script_url and script_url.
Handled unicode truncation issue for logger.
Code cleaning.
2015-05-19 13:50:39 +02:00
91457080fd Added support for base_url (i.e the CMS can be hosted on the root, or sub folder).
Local paths are relative to cms site url (i.e no starting slash).
Favor CMS_RESPONSE.absolute_url and url .. instead of using directly WSF_REQUEST.absolute_script_url and script_url.
Handled unicode truncation issue for logger.
Code cleaning.
2015-05-19 13:44:08 +02:00
51699f3bd3 updated db schema 2015-05-19 12:25:16 +02:00
jvelilla
77bb1fe123 Merge branch 'jvelilla-roc_trash' 2015-05-17 12:14:01 -03:00
jvelilla
e4e2d662b8 Renaming revert string to restore. 2015-05-15 13:10:12 -03:00
jvelilla
f0668e660e refactor rename NODE_HANLDER.do_restore instead of do_revert. 2015-05-15 11:34:21 -03:00
jvelilla
68fb21a4c1 Fixed typo 2015-05-15 11:01:59 -03:00
jvelilla
3b90d522f9 Refactor rename: using trashed_nodes instead of trash_nodes. 2015-05-15 10:44:04 -03:00
jvelilla
1c59a65983 Updated code based on Jocelyn suggestions. 2015-05-15 09:40:18 -03:00
jvelilla
9fbadac7ac Added trash feature: Remove or revert a node.
Added Handler to show the current trash nodes for a given user.
An admin can see all the trash nodes.
Updated storage to handle trash and revert nodes.
2015-05-14 11:07:15 -03:00
jvelilla
57bf5ad0dc Added delete option as a tab only if the current user has permissions to delete the
current resource
2015-05-13 15:17:19 -03:00
jvelilla
988f32c6c4 Merge branch 'master' of https://github.com/EiffelWebFramework/ROC into roc_trash
Conflicts:
	modules/node/handler/node_form_response.e
2015-05-13 12:38:25 -03:00
jvelilla
44d14c4100 delete with tabs 2015-05-13 12:27:02 -03:00
29ef17226b Added support for path_aliases.
Refactored CMS_MODULE.router (..): WSF_ROUTER design,
  to create only one router object of type CMS_ROUTER.
Added optional CMS_NODE.link: CMS_LOCAL_LINK
Reviewed permissions related to node module.
Refactor and add CMS_STORAGE_SQL(_BUILDER) abstractions
   for implementation relying only on SQL statements.
Factorized sql builder initialization (to work for sqlite and mysql storage builders).
Added CMS_RESPONSE.formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32
Added function "translation", but not implemented for now.
Updated indexing notes and comments.
Code cleaning.
2015-05-13 17:11:39 +02:00
9514f1de9c Updated SQLITE builder using GLOBAL_SETTINGS to map 0 to 0, by default 0 -> NULL
Updated CMS_NODE_API, with status, not_published, published and trashed.
Updated Form response to use permission scopes.
Updated sqlquery to retrieve user author.
Added logger info in cms_response
Updated CMS_NODE with a new status attribute.
Updated table nodes to support trashing (or soft deletes) of node using the new status field
Updated Sqlite builder to test different scenarios for users and roles.
Updated NODE_FORM_RESPONSE.edit_form feature to add a delete operation
  if there is a node ie node id >0 and the current user has delete permission on it.
Updated NODE_HANDLER.do_post to handle the operation "DELETE".
Updated queries to retrieve nodes filter by no logical deleted rows (ie. status is trashed).


Signed-off-by: jvelilla <javier.hector@gmail.com>
2015-05-12 22:02:23 +02:00
fdff2bef36 Fixed compilation of autotests suites for sqlite and mysql.
TODO: reintroduce tests for node management.
2015-05-04 23:17:06 +02:00
1603086905 Fixed node editing workflow (especially creation/updating). 2015-04-30 19:46:18 +02:00
99b2fa9fdb Commented line registering the MYSQL storage builder,
since it reguires to setup MYSQL environment variable and so on.
So by default, we use sqlite, easier to run out of the box.
2015-04-30 19:38:17 +02:00
e17011de97 for backward compatibility alias CMS_LAYOUT to CMS_ENVIRONMENT 2015-04-30 10:05:04 +02:00
17fe37aedd Added {CMS_RESPONSE}.formats: CMS_FORMATS 2015-04-30 09:50:47 +02:00
dd3688fab8 Now node and basic_auth modules are standalone cms modules (as .ecf library)
Moved to complete void-safety
Use port 9090 for demo by configuration.
2015-04-29 23:27:36 +02:00
7771a452cf Moved src/modules under modules cluster. 2015-04-29 23:08:45 +02:00
6ff7a6493c Simplify CMS_SERVICE initialization, and CMS server (launcher).
Renamed "layout" lib as "app_env" with APPLICATION_ENVIRONMENT interface.
  applied changed to callers.
Added CMS_THEME.has_region (a_name): BOOLEAN to know if a region is declared in a defined theme.
2015-04-29 23:01:42 +02:00
0eb2b70d0f Cleaned the node module, to remove for now the REST api attempt.
This will be redone with care once the web cms is ready.
2015-04-29 20:02:09 +02:00
c982f0ea9c Implemented view node by content type (no more hardcoded cases).
Added CMS_NODE_TYPE as descendant of CMS_CONTENT_TYPE,
  in case we have content which is not a node in the future.
  (probably useless, but for now, this extra abstraction is harmful)
Moved all node related code under node module cluster.
Applied comments from Javier Velilla.
Code cleaning.
2015-04-29 17:28:33 +02:00
e8bb3790ba Removed unused variables. 2015-04-27 18:50:05 +02:00
e206c1e133 Code cleaning. 2015-04-27 18:48:51 +02:00
5582dd9058 Merge branch 'jvelilla-nodes' into nodes 2015-04-27 18:44:35 +02:00
jvelilla
d54cd6032f Added comments 2015-04-24 17:55:32 -03:00
jvelilla
8b24c86ff1 Updated Authentication JS, still work in progress 2015-04-23 11:43:57 -03:00
jvelilla
fc4c2e76b6 Added login form with Javascript (example).
Updated feature and class comments.
2015-04-22 18:40:36 -03:00
a56338ad17 Added blog module as example, this is far from being a real blog module.
but this is an example about on to add a new content type, and support it.
Fixed new node form workflow.

The current state is not final, it requires many changes, but for now, it implements a node editing workflow.
2015-04-15 22:32:38 +02:00
f2bb061488 Added support for log stored in CMS_STORAGE.
Added support for custom value stored in CMS_STORAGE.
Added optional css classes addition to CMS_BLOCK output.
Refactored storage, to manage node from node module code only (or mostly).

TODO: improved view for a cms node, for now hardcoded.
2015-04-15 16:39:03 +02:00
2b25c23977 Added helper function to CMS_RESPONSE, to deal with permissions. 2015-04-14 16:11:04 +02:00
ea2b5b87d3 Added helper functions to get uri path for a node, and other related resources.
Added description to cms content type.
Fixed initialization of node module to create test bed nodes.
2015-04-14 16:07:09 +02:00
133c243126 Implemented CMS storage for user and nodes.
Implemented role and permission storage.
Introduced the CMS_PARTIAL_NODE and CMS_PARTIAL_USER.
Added support for node storage extension
  - storage of data specific to each node content type,
  - in addition to the core CMS_NODE)
  - For now, only implemented for SQL storage.
Note: in current version, CMS_PAGE support is hard coded in the core,
    (as opposed to be only supported by the node module.)
Commented/removed for now, the Web API code to update node summary, title, via REST request.
2015-04-14 11:25:02 +02:00
Jocelyn Fiat
734f661add Merge pull request #21 from jvelilla/roc_jv_09042015
Added missing descriptions/comments
2015-04-10 10:50:52 +02:00
jvelilla
e41b0631d6 Added missing descriptions 2015-04-09 22:45:37 -03:00
jvelilla
98621cb265 Merge branch 'jocelyn-jfiat' 2015-04-09 21:44:52 -03:00
177 changed files with 8753 additions and 2335 deletions

View File

@@ -2,31 +2,32 @@
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<target name="cms">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<mapping old_name="CMS_LAYOUT" new_name="CMS_ENVIRONMENT"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms_app_env" location=".\library\app_env\app_env-safe.ecf"/>
<library name="cms_model" location=".\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location=".\library\configuration\config-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="layout" location=".\library\layout\layout-safe.ecf"/>
<library name="cms_model" location=".\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty-safe.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="uri_template" location="$ISE_LIBRARY\contrib\library\text\parser\uri_template\uri_template-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

21
cms.ecf
View File

@@ -2,31 +2,32 @@
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<target name="cms">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" void_safety="none" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<mapping old_name="CMS_LAYOUT" new_name="CMS_ENVIRONMENT"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="config" location=".\library\configuration\config.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization.ecf" readonly="false"/>
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="layout" location=".\library\layout\layout.ecf"/>
<library name="cms_app_env" location=".\library\app_env\app_env.ecf"/>
<library name="cms_model" location=".\library\model\cms_model.ecf" readonly="false"/>
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="uri_template" location="$ISE_LIBRARY\contrib\library\text\parser\uri_template\uri_template.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -1,20 +1,24 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<description>Example/demo for Eiffel ROC CMS library</description>
<target name="common" abstract="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="none"/>
<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_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
<library name="cms_blog_module" location="modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="layout" location="..\..\library\layout\layout-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<!--
<library name="persistence_mysql" location="..\..\library\persistence\mysql\persistence_mysql-safe.ecf" readonly="false"/>
-->

View File

@@ -1,2 +1,2 @@
port=8099
#verbose=true
port=9090
#verbose=true

View File

@@ -0,0 +1,102 @@
note
description: "Summary description for {CMS_BLOG}."
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOG
inherit
CMS_NODE
redefine
make_empty,
import_node
end
create
make_empty,
make
feature {NONE} -- Initialization
make_empty
do
Precursor
end
feature -- Conversion
import_node (a_node: CMS_NODE)
-- <Precursor>
do
Precursor (a_node)
if attached {CMS_BLOG} a_node as l_blog then
-- l_blog
end
end
feature -- Access
content_type: READABLE_STRING_8
once
Result := {CMS_BLOG_NODE_TYPE}.name
end
feature -- Access: content
summary: detachable READABLE_STRING_8
-- A short summary of the node.
content: detachable READABLE_STRING_8
-- Content of the node.
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
tags: detachable ARRAYED_LIST [READABLE_STRING_32]
-- Optional tags
feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format)
do
content := a_content
summary := a_summary
format := a_format
end
add_tag (a_tag: READABLE_STRING_32)
-- Set `parent' to `a_page'
require
not a_tag.is_whitespace
local
l_tags: like tags
do
l_tags := tags
if l_tags = Void then
create l_tags.make (1)
tags := l_tags
end
l_tags.force (a_tag)
end
set_tags_from_string (a_tags: READABLE_STRING_32)
local
t: STRING_32
do
tags := Void
across
a_tags.split (',') as ic
loop
t := ic.item
t.left_adjust
t.right_adjust
if not t.is_whitespace then
add_tag (t)
end
end
end
end

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms_blog_module" uuid="C92F6E1E-2222-4414-9B6E-AA680E324D42" library_target="cms_blog_module">
<target name="cms_blog_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="cms" location="..\..\..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-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_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,112 @@
note
description: "Summary description for {CMS_BLOG_MODULE}."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
CMS_BLOG_MODULE
inherit
CMS_MODULE
redefine
register_hooks,
initialize,
install
end
CMS_HOOK_MENU_SYSTEM_ALTER
create
make
feature {NONE} -- Initialization
make
do
name := "Blog demo module"
version := "1.0"
description := "Service to demonstrate new node for blog"
package := "demo"
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
local
ct: CMS_BLOG_NODE_TYPE
do
Precursor (api)
if attached {CMS_NODE_API} api.module_api ({NODE_MODULE}) as l_node_api then
create ct
l_node_api.add_content_type (ct)
l_node_api.add_content_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct))
-- Add support for CMS_BLOG, which requires a storage extension to store the optional "tags" value
-- For now, we only have extension based on SQL statement.
if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_BLOG_EXTENSION}.make (l_sql_node_storage))
end
end
end
feature {CMS_API} -- Module management
install (api: CMS_API)
local
sql: STRING
do
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then
sql := "[
CREATE TABLE "blog_post_nodes"(
"nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0),
"revision" INTEGER,
"tags" VARCHAR(255)
);
]"
l_sql_storage.sql_execute_script (sql)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for blog module", generating_type)
end
end
Precursor (api)
end
end
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
a_router.handle_with_request_methods ("/blogs/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_blogs (?,?, a_api)), a_router.methods_get)
end
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
do
a_response.subscribe_to_menu_system_alter_hook (Current)
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
local
lnk: CMS_LOCAL_LINK
do
create lnk.make ("Blogs", "blogs/")
a_menu_system.primary_menu.extend (lnk)
end
feature -- Handler
handle_blogs (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: CMS_API)
local
r: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
do
create r.make (req, res, a_api)
r.set_main_content ("Blog module is in development ...")
r.execute
end
end

View File

@@ -0,0 +1,63 @@
note
description: "Summary description for {CMS_BLOG_NODE_TYPE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOG_NODE_TYPE
inherit
CMS_NODE_TYPE [CMS_BLOG]
redefine
default_create
end
feature {NONE} -- Initialization
default_create
do
Precursor
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (3)
available_formats.extend (create {PLAIN_TEXT_CONTENT_FORMAT})
available_formats.extend (create {FILTERED_HTML_CONTENT_FORMAT})
available_formats.extend (create {FULL_HTML_CONTENT_FORMAT})
end
feature -- Access
name: STRING = "blog"
-- Internal name.
title: STRING_32 = "Blog"
-- Human readable name.
description: STRING_32 = "Content published as a blog post."
-- Optional description
feature -- Access
available_formats: LIST [CONTENT_FORMAT]
-- Available formats for Current type.
feature -- Factory
new_node_with_title (a_title: READABLE_STRING_32; a_partial_node: detachable CMS_NODE): like new_node
-- New node with `a_title' and fill from partial `a_partial_node' if set.
do
create Result.make (a_title)
if a_partial_node /= Void then
Result.import_node (a_partial_node)
Result.set_title (a_title)
end
end
new_node (a_partial_node: detachable CMS_NODE): CMS_BLOG
-- New node based on partial `a_partial_node', or from none.
do
create Result.make_empty
if a_partial_node /= Void then
Result.import_node (a_partial_node)
end
end
end

View File

@@ -0,0 +1,109 @@
note
description: "Summary description for {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER
inherit
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_BLOG]
redefine
content_type,
populate_form,
update_node,
new_node,
append_html_output_to
end
create
make
feature -- Access
content_type: CMS_BLOG_NODE_TYPE
-- Associated content type.
feature -- form
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: WSF_FORM_TEXT_INPUT
s: STRING_32
do
Precursor (response, f, a_node)
create ti.make ("tags")
ti.set_label ("Tags")
ti.set_size (70)
if
a_node /= Void and then
attached {CMS_BLOG} a_node as a_blog and then
attached a_blog.tags as l_tags
then
create s.make_empty
across
l_tags as ic
loop
if not s.is_empty then
s.append_character (',')
end
s.append (ic.item)
end
ti.set_text_value (s)
end
ti.set_is_required (False)
f.extend (ti)
end
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
do
Precursor (response, fd, a_node)
if attached fd.string_item ("tags") as l_tags then
if attached {CMS_BLOG} a_node as l_blog then
l_blog.set_tags_from_string (l_tags)
end
end
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): like content_type.new_node
-- <Precursor>
do
Result := Precursor (response, fd, a_node)
if attached fd.string_item ("tags") as l_tags then
Result.set_tags_from_string (l_tags)
end
end
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
-- <Precursor>
local
s: STRING
do
Precursor (a_node, a_response)
if attached a_response.main_content as l_main_content then
s := l_main_content
else
create s.make_empty
end
if attached {CMS_BLOG} a_node as l_blog_post then
if attached l_blog_post.tags as l_tags then
s.append ("<div><strong>Tags:</strong> ")
across
l_tags as ic
loop
s.append ("<span class=%"tag%">")
s.append (a_response.html_encoded (ic.item))
s.append ("</span> ")
end
s.append ("</div>")
end
end
a_response.set_main_content (s)
end
end

View File

@@ -0,0 +1,115 @@
note
description: "Storage extension for Blog nodes."
date: "$Date$"
revision: "$Revision$"
class
CMS_NODE_STORAGE_SQL_BLOG_EXTENSION
inherit
CMS_NODE_STORAGE_EXTENSION [CMS_BLOG]
CMS_PROXY_STORAGE_SQL
rename
sql_storage as node_storage
redefine
node_storage
end
create
make
feature {NONE} -- Initialization
node_storage: CMS_NODE_STORAGE_SQL
-- <Precursor>
feature -- Access
content_type: STRING
once
Result := {CMS_BLOG_NODE_TYPE}.name
end
feature -- Persistence
store (a_node: CMS_BLOG)
local
l_parameters: STRING_TABLE [detachable ANY]
l_new_tags: detachable STRING_32
l_previous_tags: detachable STRING_32
l_update: BOOLEAN
do
error_handler.reset
if attached api as l_api then
l_api.logger.put_information (generator + ".store", Void)
end
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_blog_data, l_parameters)
if not has_error then
if sql_rows_count = 1 then
l_previous_tags := sql_read_string_32 (3)
l_update := True
end
if attached a_node.tags as l_tags and then not l_tags.is_empty then
create l_new_tags.make (0)
across
l_tags as ic
loop
if not l_new_tags.is_empty then
l_new_tags.append_character (',')
end
l_new_tags.append (ic.item)
end
else
l_new_tags := Void
end
l_parameters.put (l_new_tags, "tags")
if l_update and l_new_tags /~ l_previous_tags then
sql_change (sql_update_blog_data, l_parameters)
elseif l_new_tags /= Void then
sql_change (sql_insert_blog_data, l_parameters)
else
-- no blog data, means everything is empty.
end
end
end
load (a_node: CMS_BLOG)
local
l_parameters: STRING_TABLE [ANY]
n: INTEGER
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (sql_select_blog_data, l_parameters)
if not has_error then
n := sql_rows_count
if n = 1 then
-- nid, revision, parent
if
attached sql_read_string_32 (3) as l_tags and then
not l_tags.is_whitespace
then
-- FIXME: find a simple way to access the declared content types.
a_node.set_tags_from_string (l_tags)
end
else
check unique_data: n = 0 end
end
end
end
feature -- SQL
sql_select_blog_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid =:nid AND revision=:revision;"
sql_insert_blog_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
sql_update_blog_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
end

View File

@@ -7,7 +7,7 @@
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="standard">
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>

View File

@@ -10,7 +10,10 @@ class
inherit
CMS_MODULE
redefine
register_hooks
register_hooks,
initialize,
is_installed,
install
end
CMS_HOOK_MENU_SYSTEM_ALTER
@@ -30,14 +33,58 @@ feature {NONE} -- Initialization
package := "demo"
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
-- For now, we only have extension based on SQL statement.
-- if attached {CMS_NODE_STORAGE_SQL} l_node_api.node_storage as l_sql_node_storage then
-- l_sql_node_storage.register_node_storage_extension (create {CMS_NODE_STORAGE_SQL_PAGE_EXTENSION}.make (l_sql_node_storage))
-- end
end
feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed?
do
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)
local
sql: STRING
do
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("tb_demo") then
sql := "[
CREATE TABLE "tb_demo"(
"demo_id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("demo_id">=0),
"name" VARCHAR(100) NOT NULL,
"value" TEXT
);
]"
l_sql_storage.sql_execute_script (sql)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for demo module", generating_type)
end
end
api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
end
end
feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER
-- Node router.
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
create Result.make (2)
map_uri_template_agent (Result, "/demo/", agent handle_demo (?,?,a_api))
map_uri_template_agent (Result, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api))
map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
end
feature -- Hooks
@@ -63,9 +110,9 @@ feature -- Hooks
if a_block_id.is_case_insensitive_equal_general ("demo-info") then
if a_response.request.request_uri.starts_with ("/demo/") then
create m.make_with_title (a_block_id, "Demo", 2)
create lnk.make ("/demo/abc", a_response.url ("/demo/abc", Void))
create lnk.make ("demo: abc", "demo/abc")
m.extend (lnk)
create lnk.make ("/demo/123", a_response.url ("/demo/123", Void))
create lnk.make ("demo: 123", "demo/123")
m.extend (lnk)
create mb.make (m)
a_response.add_block (mb, "sidebar_second")
@@ -78,46 +125,20 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK
-- perms: detachable ARRAYED_LIST [READABLE_STRING_8]
do
create lnk.make ("Demo", "/demo/")
create lnk.make ("Demo", "demo/")
a_menu_system.primary_menu.extend (lnk)
end
feature -- Handler
initialize_module (a_api: CMS_API)
local
sql: STRING
do
if attached {CMS_STORAGE_SQL} a_api.storage as sql_db then
sql_db.sql_query ("select count(*) from tb_demo;", Void)
if sql_db.has_error then
-- Initialize db for demo module
sql := "[
CREATE TABLE "tb_demo"(
"did" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("did">=0),
"name" VARCHAR(100) NOT NULL,
"value" TEXT
);
]"
sql_db.reset_error
sql_db.sql_change (sql, Void)
if sql_db.has_error then
a_api.logger.put_error ("Could not initialize database for demo module", generating_type)
end
end
end
end
handle_demo,
handle_demo_entry (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: CMS_API)
local
r: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
do
initialize_module (a_api)
create r.make (req, res, a_api)
r.set_main_content ("NODE module does not yet implement %"" + req.path_info + "%" ...")
r.add_error_message ("NODE Module: not yet implemented")
r.set_main_content ("DEMO module does not yet implement %"" + req.path_info + "%" ...")
r.add_error_message ("DEMO Module: not yet implemented")
r.execute
end

View File

@@ -0,0 +1,28 @@
BEGIN;
CREATE TABLE "logs"(
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0),
"category" VARCHAR(255) NOT NULL,
"level" INTEGER NOT NULL CHECK("level">=1),
"uid" INTEGER,
"message" TEXT NOT NULL,
"info" TEXT,
"link" TEXT,
"date" DATETIME NOT NULL
);
CREATE TABLE "custom_values"(
"type" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"value" TEXT
);
CREATE TABLE "path_aliases"(
"pid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("pid">=0),
"source" VARCHAR(255) NOT NULL,
"alias" VARCHAR(255) NOT NULL,
"lang" VARCHAR(12)
);
COMMIT;

View File

@@ -0,0 +1,24 @@
BEGIN;
CREATE TABLE "nodes" (
"nid" INTEGER NOT NULL CHECK("nid" > 0) PRIMARY KEY AUTOINCREMENT UNIQUE,
"revision" INTEGER,
"type" TEXT NOT NULL,
"title" VARCHAR(255) NOT NULL,
"summary" TEXT,
"content" TEXT,
"format" VARCHAR(128),
"author" INTEGER,
"publish" DATETIME,
"created" DATETIME NOT NULL,
"changed" DATETIME NOT NULL,
"status" INTEGER
);
CREATE TABLE page_nodes(
"nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0),
"revision" INTEGER,
"parent" INTEGER
);
COMMIT;

View File

@@ -1,4 +1,5 @@
BEGIN;
CREATE TABLE "users"(
"uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0),
"name" VARCHAR(100) NOT NULL,
@@ -12,23 +13,22 @@ CREATE TABLE "users"(
UNIQUE("name")
);
CREATE TABLE "users_roles"(
CREATE TABLE "roles"(
"rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0),
"role" VARCHAR(100) NOT NULL,
CONSTRAINT "role"
UNIQUE("role")
"name" VARCHAR(100) NOT NULL,
CONSTRAINT "name"
UNIQUE("name")
);
CREATE TABLE "nodes"(
"nid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("nid">=0),
"version" INTEGER,
"type" INTEGER,
"title" VARCHAR(255) NOT NULL,
"summary" TEXT NOT NULL,
"content" MEDIUMTEXT NOT NULL,
"author" INTEGER,
"publish" DATETIME,
"created" DATETIME NOT NULL,
"changed" DATETIME NOT NULL
CREATE TABLE "users_roles"(
"uid" INTEGER NOT NULL CHECK("uid">=0),
"rid" INTEGER NOT NULL CHECK("rid">=0)
);
CREATE TABLE "role_permissions"(
"rid" INTEGER NOT NULL CHECK("rid">=0),
"permission" VARCHAR(255) NOT NULL,
"module" VARCHAR(255)
);
COMMIT;

View File

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

View File

@@ -37,6 +37,16 @@ ul.horizontal li {
#content {
margin-left: 20px;
}
#content #highlighted {
position: relative;
border: solid 1px #ddd;
background-color: #ffc;
width: 70%;
left: 15%;
right: 15%;
padding: 5px;
font-style: italic;
}
.sidebar {
padding: 5px;
@@ -51,3 +61,18 @@ ul.horizontal li {
width: 250px;
float: right;
}
#primary-tabs ul.horizontal {
list-style-type: none;
}
#primary-tabs ul.horizontal li {
display: inline;
padding: 2px 5px;
border: solid 1px #ccf;
}
#primary-tabs ul.horizontal li.active {
border-color: #99f #99f #ddd;
border-style: solid solid none;
border-width: 2px 1px 0;
padding: 2px 7px 1px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,307 @@
var ROC_AUTH = ROC_AUTH || { };
var loginURL = "/login";
var logoutURL = "/logoff";
var userAgent = navigator.userAgent.toLowerCase();
var firstLogIn = true;
ROC_AUTH.login = function() {
var form = document.forms[0];
var username = form.username.value;
var password = form.password.value;
//var host = form.host.value;
var origin = window.location.origin.concat(window.location.pathname);
var _login = function(){
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=origin;
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
};
ROC_AUTH.login_with_redirect = function() {
var form = document.forms[2];
var username = form.username.value;
var password = form.password.value;
var host = form.host.value;
var _login = function(){
var redirectURL = form.redirect && form.redirect.value || "";
$("#imgProgressRedirect").show();
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$("#imgProgressRedirect").hide();
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(loginURL), true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host.concat("/");
} else {
window.location=host.concat(redirectURL);
}
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
};
ROC_AUTH.getQueryParameterByName = function (name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
}
ROC_AUTH.logoff = function(callback){
var form = document.forms[0];
var host = form.host.value;
if (userAgent.indexOf("msie") != -1) {
document.execCommand("ClearAuthenticationCache");
}
else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest();
//Logout. Tell the server not to return the "WWW-Authenticate" header
request1.open("GET", host.concat(logoutURL) + "?prompt=false", true);
request1.send("");
request1.onreadystatechange = function(){
if (request1.readyState == 4) {
//Sign in with dummy credentials to clear the auth cache
request2.open("GET", host.concat(logoutURL), true, "logout", "logout");
request2.send("");
request2.onreadystatechange = function(){
if (request2.readyState == 4) {
if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);}
}
}
}
}
}
else {
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(logoutURL), true, "logout", "logout");
request.send("");
request.onreadystatechange = function(){
if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL);
}
}
}
};
ROC_AUTH.remove = function (id)
{
var element = document.getElementById(id);
element.outerHTML = "";
delete element;
return;
};
$(document).ready(function() {
if (typeof String.prototype.contains != 'function') {
String.prototype.contains = function (str){
return this.indexOf(str) != -1;
};
}
ROC_AUTH.progressive_loging();
});
ROC_AUTH.progressive_loging = function () {
ROC_AUTH.login_href();
};
$(document).keypress(function(e) {
if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) {
ROC_AUTH.login();
}
});
ROC_AUTH.OnOneClick = function(event) {
event.preventDefault();
if ( document.forms[0] === undefined ) {
ROC_AUTH.create_form();
}
return false;
};
ROC_AUTH.login_href = function() {
var els = document.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) {
var el = els[i];
if (el.href.contains("/basic_auth_login?destination")) {
loginURL = el.href;
var OneClick = el;
OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false);
}
}
};
ROC_AUTH.create_form = function() {
// Fetching HTML Elements in Variables by ID.
var createform = document.createElement('form'); // Create New Element Form
createform.setAttribute("action", ""); // Setting Action Attribute on Form
createform.setAttribute("method", "post"); // Setting Method Attribute on Form
$("body").append(createform);
var heading = document.createElement('h2'); // Heading of Form
heading.innerHTML = "Login Form ";
createform.appendChild(heading);
var line = document.createElement('hr'); // Giving Horizontal Row After Heading
createform.appendChild(line);
var linebreak = document.createElement('br');
createform.appendChild(linebreak);
var namelabel = document.createElement('label'); // Create Label for Name Field
namelabel.innerHTML = "Username : "; // Set Field Labels
createform.appendChild(namelabel);
var inputelement = document.createElement('input'); // Create Input Field for UserName
inputelement.setAttribute("type", "text");
inputelement.setAttribute("name", "username");
inputelement.setAttribute("required","required");
createform.appendChild(inputelement);
var linebreak = document.createElement('br');
createform.appendChild(linebreak);
var passwordlabel = document.createElement('label'); // Create Label for Password Field
passwordlabel.innerHTML = "Password : ";
createform.appendChild(passwordlabel);
var passwordelement = document.createElement('input'); // Create Input Field for Password.
passwordelement.setAttribute("type", "password");
passwordelement.setAttribute("name", "password");
passwordelement.setAttribute("id", "password");
passwordelement.setAttribute("required","required");
createform.appendChild(passwordelement);
var passwordbreak = document.createElement('br');
createform.appendChild(passwordbreak);
var submitelement = document.createElement('button'); // Append Submit Button
submitelement.setAttribute("type", "button");
submitelement.setAttribute("onclick", "ROC_AUTH.login();");
submitelement.innerHTML = "Sign In ";
createform.appendChild(submitelement);
};

View File

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

View File

@@ -41,6 +41,16 @@ ul.horizontal {
}
#content {
margin-left: 20px;
#highlighted {
position: relative;
border: solid 1px #ddd;
background-color: #ffc;
width: 70%;
left: 15%;
right: 15%;
padding: 5px;
font-style: italic;
}
}
.sidebar {
padding: 5px;
@@ -55,3 +65,19 @@ ul.horizontal {
float: right;
}
}
#primary-tabs {
ul.horizontal {
list-style-type: none;
li {
display: inline;
padding: 2px 5px;
border: solid 1px #ccf;
}
li.active {
border-color: #99f #99f #ddd;
border-style: solid solid none;
border-width: 2px 1px 0;
padding: 2px 7px 1px;
}
}
}

View File

@@ -1,14 +1,38 @@
{assign name="debug_enabled" value="True"/}
{if condition="$debug_enabled"}
<!-- start debug -->
{literal}
<style>
div.cms-debug>span {
position: absolute;
bottom: 5px;
right: 5px;
color: #ccc;
padding: 5px;
}
div.cms-debug:hover>span {
color: red;
}
div.cms-debug>span+ul {
display: none;
border: solid 2px red;
background-color: #ccc;
white-space: pre-wrap;
}
div.cms-debug:hover>span+ul {
display: block;
position: relative;
bottom: 5px;
left: 1%; right: 1%;
width: 98%;
}
</style>
{/literal}
<div class="cms-debug"><span>Show debug</span>
<ul>
{assign name="kpage" value="page"/}
{assign name="kregions" value="regions"/}
{foreach key="k" item="i" from="$page.variables"}
{unless condition="$k ~ $kpage"}
{unless condition="$k ~ $kregions"}
<li><strong>{$k/}</strong>={$i/}</li>
{/unless}
{/unless}
{assign name="kpage" value="page"/}{assign name="kregions" value="regions"/}{foreach key="k" item="i" from="$page.variables"}{unless condition="$k ~ $kpage"}{unless condition="$k ~ $kregions"}<li><strong>{$k/}</strong>={htmlentities}{$i/}{/htmlentities}</li>{/unless}{/unless}
{/foreach}
</ul>
</div>
<!-- end debug -->
{/if}

View File

@@ -1,10 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- EWF CMS -->
<link rel="stylesheet" href="{$site_url/}/theme/css/style.css">
<link rel="stylesheet" href="{$site_url/}/theme/css/node.css">
<script src="{$site_url/}/theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}/theme/js/roc_auth.js"></script>
<!-- bootstrap framework -->
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
@@ -70,8 +74,6 @@
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<!--
{include file="debug.tpl"/}
-->
</body>
</html>

View File

@@ -41,26 +41,24 @@ feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
-- Launcher
Precursor
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} service_options.make_from_file ("demo.ini")
initialize_cms (cms_setup)
-- CMS
initialize_cms
end
feature -- Service
cms_service: CMS_SERVICE
-- cms service.
-- cms service.
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
cms_service.execute (req, res)
end
feature -- Layout
layout: CMS_LAYOUT
-- cms layout.
feature {NONE} -- Launch operation
launcher: APPLICATION_LAUNCHER
@@ -91,7 +89,7 @@ feature {NONE} -- Launch operation
l_message.append ("The application crash without available information")
l_message.append ("%N%N")
end
-- send email shutdown
-- notify shutdown
write_debug_log (generator + ".launch shutdown")
end
rescue
@@ -101,31 +99,29 @@ feature {NONE} -- Launch operation
feature -- CMS Initialization
cms_setup: CMS_DEFAULT_SETUP
initialize_cms
local
l_setup: CMS_DEFAULT_SETUP
utf: UTF_CONVERTER
cms_env: CMS_ENVIRONMENT
do
-- Application Environment initialization
if attached execution_environment.arguments.separate_character_option_value ('d') as l_dir then
create layout.make_with_directory_name (l_dir)
create cms_env.make_with_directory_name (l_dir)
else
create layout.make_default
create cms_env.make_default
end
initialize_logger (layout)
write_debug_log (generator + ".cms_setup based directory %"" + utf.escaped_utf_32_string_to_utf_8_string_8 (layout.path.name) + "%"")
create Result.make (layout)
setup_storage (Result)
end
initialize_logger (cms_env)
initialize_cms (a_setup: CMS_SETUP)
local
cms: CMS_SERVICE
api: CMS_API
do
write_debug_log (generator + ".initialize_cms")
setup_modules (a_setup)
create api.make (a_setup)
create cms.make (api)
cms_service := cms
-- CMS Setup
write_debug_log (generator + ".initialize_cms / SETUP based directory=%"" + utf.escaped_utf_32_string_to_utf_8_string_8 (cms_env.path.name) + "%"")
create l_setup.make (cms_env)
-- CMS
write_debug_log (generator + ".initialize_cms / CMS")
setup_storage (l_setup)
setup_modules (l_setup)
create cms_service.make (l_setup)
end
feature -- CMS setup
@@ -135,22 +131,32 @@ feature -- CMS setup
local
m: CMS_MODULE
do
create {NODE_MODULE} m.make (a_setup)
m.enable
a_setup.register_module (m)
create {BASIC_AUTH_MODULE} m.make
if not a_setup.module_with_same_type_registered (m) then
m.enable
a_setup.register_module (m)
end
create {CMS_DEBUG_MODULE} m.make
m.enable
a_setup.register_module (m)
create {CMS_DEMO_MODULE} m.make
m.enable
a_setup.register_module (m)
create {CMS_BLOG_MODULE} m.make
m.enable
a_setup.register_module (m)
end
setup_storage (a_setup: CMS_SETUP)
-- Setup storage by declaring storage builder.
do
debug ("refactor_fixme")
to_implement ("To implement custom storage")
end
-- a_setup.storage_drivers.force (create {CMS_STORAGE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE_BUILDER}.make, "sqlite")
end

13
library/app_env/Readme.md Normal file
View File

@@ -0,0 +1,13 @@
Application Environment Library
===============================
Define a generic application environment to be re-used by different applications.
site/
doc/
logs/
www/
assets
template
theme
config/

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="app_env" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" library_target="app_env">
<description>Application Environment (layout, configuration, logger, database, ...)</description>
<target name="app_env">
<root all_classes="true"/>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="app_env" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" library_target="app_env">
<description>Application Environment (layout, configuration, logger, database, ...)</description>
<target name="app_env">
<root all_classes="true"/>
<option warning="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -1,6 +1,6 @@
note
description: "[
Application layout
Application environment (layout, ...)
Related to file system locations such as
- configuration locations
- application
@@ -15,7 +15,7 @@ note
revision: "$Revision: 96584 $"
class
APPLICATION_LAYOUT
APPLICATION_ENVIRONMENT
inherit
SHARED_EXECUTION_ENVIRONMENT

View File

@@ -0,0 +1,23 @@
note
description: "See {APPLICATION_ENVIRONMENT}."
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_LAYOUT
obsolete
"Use APPLICATION_ENVIRONMENT [April/2015]"
inherit
APPLICATION_ENVIRONMENT
create
make_default,
make_with_path,
make_with_directory_name
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {LOGGER}."
author: ""
description: "Object to log messages for a specific application. "
date: "$Date$"
revision: "$Revision$"
@@ -22,24 +21,35 @@ inherit
create
make,
make_with_environment,
make_with_layout
feature {NONE} -- Initialization
make
-- Initialize a logger object.
do
create log.make
end
make_with_layout (app: APPLICATION_LAYOUT)
make_with_environment (app: APPLICATION_ENVIRONMENT)
-- Initialize a logger object with an application environment `app'.
do
make
apply_layout (app)
apply_environment (app)
end
make_with_layout (app: APPLICATION_ENVIRONMENT)
-- Initialize a logger object with an application layout `app'.
obsolete
"Use make_with_environment"
do
make_with_environment (app)
end
feature -- Change
apply_layout (app: APPLICATION_LAYOUT)
apply_environment (app: APPLICATION_ENVIRONMENT)
do
initialize_logger (app, log)
end
@@ -51,38 +61,44 @@ feature {NONE} -- Internal
feature -- Logging
put_information (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at information level.
do
log.write_information (create {STRING}.make_from_separate (a_message))
end
put_error (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at error level.
do
log.write_error (create {STRING}.make_from_separate (a_message))
end
put_warning (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at warning level.
do
log.write_warning (create {STRING}.make_from_separate (a_message))
end
put_critical (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at critical level.
do
log.write_critical (create {STRING}.make_from_separate (a_message))
end
put_alert (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at alert level.
do
log.write_alert (create {STRING}.make_from_separate (a_message))
end
put_debug (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at debug level.
do
log.write_debug (create {STRING}.make_from_separate (a_message))
end
feature {NONE} -- Implementation
initialize_logger (app: APPLICATION_LAYOUT; a_log: like log)
initialize_logger (app: APPLICATION_ENVIRONMENT; a_log: like log)
local
l_log_writer_file: LOG_ROLLING_WRITER_FILE
l_log_writer: LOG_WRITER

View File

@@ -22,7 +22,7 @@ feature -- Access
register_log_writer (a_log_writer: LOG_WRITER)
-- -- Register the non-default log writer `a_log_writer'.
-- Register the non-default log writer `a_log_writer'.
do
logging.register_log_writer (a_log_writer)
end

View File

@@ -98,11 +98,11 @@ feature {NONE} -- Logger: separate implementation
feature {NONE} -- Implementation
initialize_logger (app: APPLICATION_LAYOUT)
initialize_logger (app: APPLICATION_ENVIRONMENT)
local
l_logger: LOGGER
do
create l_logger.make_with_layout (app)
create l_logger.make_with_environment (app)
set_logger_to (l_logger, logger_cell)
end

View File

@@ -1,13 +0,0 @@
Layout Library
==============
Define a generic layout to be re-used by different applications.
site/
doc/
logs/
www/
assets
template
theme
config/

View File

@@ -1,21 +1,3 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="layout" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" library_target="layout">
<target name="layout">
<root all_classes="true"/>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>
<redirection xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" location="..\app_env\app_env-safe.ecf">
</redirection>

View File

@@ -1,21 +1,3 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="layout" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" library_target="layout">
<target name="layout">
<root all_classes="true"/>
<option warning="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>
<redirection xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" uuid="7AE9E48B-5A15-43F8-B99A-04F4185DED6B" location="..\app_env\app_env.ecf">
</redirection>

View File

@@ -1,15 +0,0 @@
note
description: "[
Interface defining a CMS content type.
]"
status: "draft"
date: "$Date$"
revision: "$Revision$"
class
CMS_CONTENT_TYPE
note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -1,200 +0,0 @@
note
description: "[
CMS abstraction for CMS content entity, named "node".
]"
status: "draft"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_NODE
inherit
REFACTORING_HELPER
create
make,
make_empty
feature{NONE} -- Initialization
make_empty
-- Create empty node.
do
make ({STRING_32} "", {STRING_32} "", {STRING_32} "")
end
make (a_content: READABLE_STRING_32; a_summary: READABLE_STRING_32; a_title: READABLE_STRING_32)
-- Create current node with `a_content', `a_summary' and `a_title'.
local
l_time: DATE_TIME
do
create l_time.make_now_utc
set_content (a_content)
set_summary (a_summary)
set_title (a_title)
set_creation_date (l_time)
set_modification_date (l_time)
set_publication_date (l_time)
debug ("refactor_fixme")
fixme ("Remove default harcoded format")
end
set_format ("HTML")
debug ("refactor_fixme")
fixme ("Remove default harcoded content type")
end
set_content_type ("Page")
ensure
content_set: content = a_content
summary_set: summary = a_summary
title_set: title = a_title
end
feature -- Access
id: INTEGER_64 assign set_id
-- Unique id.
content: READABLE_STRING_32
-- Content of the node.
summary: READABLE_STRING_32
-- A short summary of the node.
title: READABLE_STRING_32
-- Full title of the node.
modification_date: DATE_TIME
-- When the node was updated.
creation_date: DATE_TIME
-- When the node was created.
publication_date: DATE_TIME
-- When the node was published.
publication_date_output: READABLE_STRING_32
-- Formatted output.
format: READABLE_STRING_32
-- Format associated with `body'.
-- For example: text, mediawiki, html, etc
content_type: READABLE_STRING_32
-- Associated content type name.
-- Page, Article, Blog, News, etc.
author: detachable CMS_USER
-- Author of current node.
feature -- status report
has_id: BOOLEAN
-- Has unique identifier?
do
Result := id > 0
end
feature -- Element change
set_content (a_content: like content)
-- Assign `content' with `a_content'.
do
content := a_content
ensure
content_assigned: content = a_content
end
set_summary (a_summary: like summary)
-- Assign `summary' with `a_summary'.
do
summary := a_summary
ensure
summary_assigned: summary = a_summary
end
set_title (a_title: like title)
-- Assign `title' with `a_title'.
do
title := a_title
ensure
title_assigned: title = a_title
end
set_modification_date (a_modification_date: like modification_date)
-- Assign `modification_date' with `a_modification_date'.
do
modification_date := a_modification_date
ensure
modification_date_assigned: modification_date = a_modification_date
end
set_creation_date (a_creation_date: like creation_date)
-- Assign `creation_date' with `a_creation_date'.
do
creation_date := a_creation_date
ensure
creation_date_assigned: creation_date = a_creation_date
end
set_publication_date (a_publication_date: like publication_date)
-- Assign `publication_date' with `a_publication_date'.
do
publication_date := a_publication_date
publication_date_output := publication_date.formatted_out ("yyyy/[0]mm/[0]dd")
ensure
publication_date_assigned: publication_date = a_publication_date
end
set_content_type (a_content_type: like content_type)
-- Assign `content_type' with `a_content_type'.
do
content_type := a_content_type
ensure
content_type_assigned: content_type = a_content_type
end
set_format (a_format: like format)
-- Assign `format' with `a_format'.
do
format := a_format
ensure
format_assigned: format = a_format
end
set_id (an_id: like id)
-- Assign `id' with `an_id'.
do
id := an_id
ensure
id_assigned: id = an_id
end
set_author (u: like author)
-- Assign 'author' with `u'
do
author := u
ensure
auther_set: author = u
end
-- add_collaborator (a_user: CMS_USER)
-- -- Add collaborator `a_user' to the collaborators list.
-- local
-- lst: like collaborators
-- do
-- lst := collaborators
-- if lst = Void then
-- create {ARRAYED_SET [CMS_USER]} lst.make (1)
-- collaborators := lst
-- end
-- lst.force (a_user)
-- end
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -25,15 +25,13 @@ create
feature {NONE} -- Initialization
make (a_title: detachable like title; a_location: like location)
make (a_title: detachable READABLE_STRING_GENERAL; a_location: like location)
-- Create current local link with optional title `a_title' and location `a_location'.
require
is_valid_local_location_argument: not a_location.starts_with_general ("/")
do
if a_title /= Void then
title := a_title
else
title := a_location
end
location := a_location
set_title (a_title)
end
feature -- Access
@@ -75,6 +73,16 @@ feature -- Status report
feature -- Element change
set_title (a_title: detachable READABLE_STRING_GENERAL)
-- Set `title' to `a_title' or `location'.
do
if a_title /= Void then
title := a_title.as_string_32
else
title := location.as_string_32
end
end
add_link (lnk: CMS_LINK)
-- <Precursor>
local

View File

@@ -62,6 +62,7 @@ feature -- Status report
end
has (lnk: CMS_LINK): BOOLEAN
-- Has the current Menu a link `lnk'.
do
across
items as ic
@@ -87,6 +88,7 @@ feature -- Element change
end
set_title (t: like title)
-- Set `title' with `t'.
do
title := t
end

View File

@@ -0,0 +1,127 @@
note
description: "Summary description for {CMS_LOG}."
date: "$Date$"
revision: "$Revision$"
class
CMS_LOG
create
make
feature {NONE} -- Initialization
make (a_category: like category; a_message: like message; a_level: like level; a_date: detachable like date)
do
category := a_category
message := a_message
set_level (a_level)
if a_date = Void then
create date.make_now_utc
else
date := a_date
end
end
make_with_id (a_id: like id; a_category: like category; a_message: like message; a_level: like level; a_date: detachable like date)
do
id := a_id
make (a_category, a_message, a_level, a_date)
end
feature -- Access
id: INTEGER
-- Unique identifier of Current.
category: READABLE_STRING_8
-- Associated title (optional).
message: READABLE_STRING_8
-- Log message
level: INTEGER
-- Severity level
level_name: STRING
do
Result := level_to_string (level)
end
info: detachable READABLE_STRING_8
link: detachable CMS_LINK
date: DATE_TIME
feature -- status report
has_id: BOOLEAN
do
Result := id > 0
end
feature -- Change
set_id (a_id: like id)
require
not has_id
do
id := a_id
end
set_level (a_level: like level)
do
if a_level = 0 then
level := level_notice
else
level := a_level
end
end
set_link (lnk: like link)
do
link := lnk
end
set_info (inf: like info)
do
info := inf
end
feature -- Constants
level_to_string (a_level: INTEGER): STRING
do
inspect a_level
when level_emergency then
Result := "emergency"
when level_alert then
Result := "alert"
when level_critical then
Result := "critical"
when level_error then
Result := "error"
when level_warning then
Result := "warning"
when level_notice then
Result := "notice"
when level_info then
Result := "info"
when level_debug then
Result := "debug"
else
Result := "level-" + a_level.out
end
end
level_emergency: INTEGER = 1
level_alert: INTEGER = 2
level_critical: INTEGER = 3
level_error: INTEGER = 4
level_warning: INTEGER = 5
level_notice: INTEGER = 6
level_info: INTEGER = 7
level_debug: INTEGER = 8
end

View File

@@ -0,0 +1,19 @@
note
description: "Partial CMS USER."
date: "$Date$"
revision: "$Revision$"
class
CMS_PARTIAL_USER
inherit
CMS_USER
create
make,
make_with_id -- MAYBE: export to CMS_STORAGE
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -71,6 +71,11 @@ feature -- Access
last_login_date: detachable DATE_TIME
-- User last login.
feature -- Roles
roles: detachable LIST [CMS_USER_ROLE]
-- If set, list of roles for current user.
feature -- Access: data
data: detachable STRING_TABLE [detachable ANY]
@@ -189,6 +194,14 @@ feature -- Change element
set_last_login_date (create {DATE_TIME}.make_now_utc)
end
feature -- Element change: roles
set_roles (lst: like roles)
-- Set `roles' to `lst'.
do
roles := lst
end
feature -- Change element: data
set_data_item (k: READABLE_STRING_GENERAL; d: like data_item)
@@ -217,6 +230,6 @@ invariant
id_or_name_set: id > 0 or else not name.is_whitespace
note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -22,15 +22,25 @@ create
feature {NONE} -- Initialization
make_with_id (a_id: like id; a_name: like name)
make_with_id (a_id: like id)
-- Create current role with role id `a_id'.
do
id := a_id
make (a_name)
name := {STRING_32} ""
initialize
end
make (a_name: like name)
-- Create current role with name `a_name'.
require
a_name_valid: not a_name.is_whitespace
do
name := a_name
initialize
end
initialize
do
create {ARRAYED_LIST [READABLE_STRING_8]} permissions.make (0)
end
@@ -42,10 +52,10 @@ feature -- Status report
Result := id > 0
end
has_permission (p: READABLE_STRING_8): BOOLEAN
has_permission (p: READABLE_STRING_GENERAL): BOOLEAN
-- Has permission `p'?
do
Result := across permissions as c some c.item.is_case_insensitive_equal (p) end
Result := across permissions as c some p.is_case_insensitive_equal (c.item) end
end
feature -- Access
@@ -53,7 +63,7 @@ feature -- Access
id: INTEGER
-- Unique id associated with Current role.
name: READABLE_STRING_8
name: READABLE_STRING_32
-- Name of Current role.
permissions: LIST [READABLE_STRING_8]
@@ -115,6 +125,6 @@ feature -- Permission change
end
note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {CMS_STORAGE_STORE_SQL}."
author: ""
description: "Storage based on Eiffel Store component."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
@@ -8,8 +7,6 @@ deferred class
CMS_STORAGE_STORE_SQL
inherit
CMS_STORAGE
CMS_STORAGE_SQL
feature {NONE} -- Initialization
@@ -25,7 +22,6 @@ feature {NONE} -- Initialization
create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection)
create error_handler.make
-- error_handler.add_synchronization (db_handler.database_error_handler)
end
feature -- Status report
@@ -55,21 +51,25 @@ feature -- Query
end
sql_begin_transaction
-- <Precursor>
do
connection.begin_transaction
end
sql_rollback_transaction
-- <Precursor>
do
connection.rollback
end
sql_commit_transaction
-- <Precursor>
do
connection.commit
end
sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute an sql query `a_sql_statement' with the params `a_params'.
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
@@ -78,6 +78,7 @@ feature -- Query
end
sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute an sql query change `a_sql_statement' with the params `a_params'.
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
@@ -109,6 +110,11 @@ feature -- Query
db_handler.forth
end
sql_valid_item_index (a_index: INTEGER): BOOLEAN
do
Result := attached {DB_TUPLE} db_handler.item as l_item and then l_item.valid_index (a_index)
end
sql_item (a_index: INTEGER): detachable ANY
do
if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then

View File

@@ -0,0 +1,16 @@
note
description: "[
Common ancestor for builders responsible to instantiate storage based
on Eiffel Store storage.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_STORAGE_STORE_SQL_BUILDER
inherit
CMS_STORAGE_SQL_BUILDER
end

View File

@@ -8,11 +8,11 @@
<setting name="console_application" value="true"/>
<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="..\..\app_env\app_env-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="layout" location="..\..\layout\layout-safe.ecf"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="model" location="..\..\model\cms_model-safe.ecf"/>
<library name="mysql" location="$ISE_LIBRARY\library\store\dbms\rdbms\mysql\mysql-safe.ecf"/>

View File

@@ -1,75 +0,0 @@
note
description: "Summary description for {CMS_NODE_STORAGE_MYSQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_NODE_STORAGE_MYSQL
inherit
CMS_NODE_STORAGE_SQL
redefine
nodes, recent_nodes
end
feature {NONE} -- Implementation
db_handler: DATABASE_HANDLER
deferred
end
feature -- Access
nodes: LIST [CMS_NODE]
-- List of nodes.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
across nodes_iterator as ic loop
Result.force (ic.item)
end
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (a_count)
across recent_nodes_iterator (a_lower, a_count) as c loop
Result.force (c.item)
end
end
feature -- Access: iterator
nodes_iterator: DATABASE_ITERATION_CURSOR [CMS_NODE]
-- List of nodes.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
log.write_information (generator + ".nodes_iterator")
create l_parameters.make (0)
sql_query (select_nodes, l_parameters)
create Result.make (db_handler, agent fetch_node)
sql_post_execution
end
recent_nodes_iterator (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [CMS_NODE]
-- The most recent `a_rows'.
local
l_parameters: STRING_TABLE [ANY]
l_query: STRING
do
-- FIXME: check implementation...
error_handler.reset
log.write_information (generator + ".recent_nodes_iterator")
create l_parameters.make (2)
l_parameters.put (a_rows, "rows")
l_parameters.put (a_lower, "offset")
create l_query.make_from_string (select_recent_nodes)
sql_query (l_query, l_parameters)
create Result.make (db_handler, agent fetch_node)
sql_post_execution
end
end

View File

@@ -7,13 +7,11 @@ class
CMS_STORAGE_MYSQL
inherit
CMS_STORAGE
CMS_STORAGE_STORE_SQL
CMS_USER_STORAGE_MYSQL
CMS_CORE_STORAGE_SQL_I
CMS_NODE_STORAGE_MYSQL
CMS_USER_STORAGE_SQL_I
REFACTORING_HELPER

View File

@@ -1,6 +1,6 @@
note
description: "[
Objects that ...
Interface responsible to instantiate CMS_STORAGE_MYSQL object.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
@@ -10,7 +10,9 @@ class
CMS_STORAGE_MYSQL_BUILDER
inherit
CMS_STORAGE_BUILDER
CMS_STORAGE_STORE_SQL_BUILDER
GLOBAL_SETTINGS
create
make
@@ -28,10 +30,16 @@ feature -- Factory
local
conn: DATABASE_CONNECTION
do
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.layout.application_config_path) as l_database_config then
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.application_config_path) as l_database_config then
create {DATABASE_CONNECTION_MYSQL} conn.login_with_connection_string (l_database_config.connection_string)
if conn.is_connected then
create Result.make (conn)
set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default.
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)
end
end
end
end
end

View File

@@ -1,14 +0,0 @@
note
description: "Summary description for {CMS_USER_STORAGE_MYSQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_USER_STORAGE_MYSQL
inherit
CMS_USER_STORAGE_SQL
end

View File

@@ -29,13 +29,13 @@ feature {NONE} -- Initialization
storage.new_user (default_user)
storage.new_user (custom_user ("u2", "p2", "e2"))
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_title (2,ll_node.id, "New Title")
check
attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary
end
end
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_title (2,ll_node.id, "New Title")
-- check
-- attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary
-- end
-- end
end
@@ -64,9 +64,10 @@ feature {NONE} -- Implementation
Result := custom_node ("Default content", "default summary", "Default")
end
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_PAGE
do
create Result.make (a_content, a_summary, a_title)
create Result.make (a_title)
Result.set_content (a_content, a_summary, Void)
end
end

View File

@@ -167,192 +167,192 @@ feature -- Test routines
assert ("Valid password", storage.is_valid_credential ("test", "password"))
end
test_recent_nodes_empty
do
assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty)
end
-- test_recent_nodes_empty
-- do
-- assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty)
-- end
test_recent_nodes
local
l_nodes: LIST[CMS_NODE]
l_node: CMS_NODE
do
storage.new_user (default_user)
across 1 |..| 10 as c loop
l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
end
l_nodes := storage.recent_nodes (0, 10)
assert ("10 recent nodes", l_nodes.count = 10)
assert ("First node id=10", l_nodes.first.id = 10)
assert ("Last node id=1", l_nodes.last.id = 1)
-- test_recent_nodes
-- local
-- l_nodes: LIST[CMS_NODE]
-- l_node: CMS_NODE
-- do
-- storage.new_user (default_user)
-- across 1 |..| 10 as c loop
-- l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- end
-- l_nodes := storage.recent_nodes (0, 10)
-- assert ("10 recent nodes", l_nodes.count = 10)
-- assert ("First node id=10", l_nodes.first.id = 10)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (5, 10)
assert ("5 recent nodes", l_nodes.count = 5)
assert ("First node id=5", l_nodes.first.id = 5)
assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (5, 10)
-- assert ("5 recent nodes", l_nodes.count = 5)
-- assert ("First node id=5", l_nodes.first.id = 5)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (9, 10)
assert ("1 recent nodes", l_nodes.count = 1)
assert ("First node id=1", l_nodes.first.id = 1)
assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (9, 10)
-- assert ("1 recent nodes", l_nodes.count = 1)
-- assert ("First node id=1", l_nodes.first.id = 1)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (10, 10)
assert ("Is empty", l_nodes.is_empty)
end
-- l_nodes := storage.recent_nodes (10, 10)
-- assert ("Is empty", l_nodes.is_empty)
-- end
test_node_does_not_exist
local
l_node: CMS_NODE
do
storage.new_user (default_user)
across 1 |..| 10 as c loop
l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
end
assert ("Not exist node id: 12", storage.node_by_id (12) = Void)
end
-- test_node_does_not_exist
-- local
-- l_node: CMS_NODE
-- do
-- storage.new_user (default_user)
-- across 1 |..| 10 as c loop
-- l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- end
-- assert ("Not exist node id: 12", storage.node_by_id (12) = Void)
-- end
test_node
local
l_node: CMS_NODE
do
storage.new_user (default_user)
across 1 |..| 10 as c loop
l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
end
assert ("Node id: 10", attached storage.node_by_id (10) as ll_node and then ll_node.title ~ "Title_10" )
end
-- test_node
-- local
-- l_node: CMS_NODE
-- do
-- storage.new_user (default_user)
-- across 1 |..| 10 as c loop
-- l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- end
-- assert ("Node id: 10", attached storage.node_by_id (10) as ll_node and then ll_node.title ~ "Title_10" )
-- end
test_update_node
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
l_node := ll_node.twin
l_node.set_content ("New Content")
l_node.set_summary ("New Summary")
l_node.set_title("New Title")
if attached storage.user_by_email (default_user.email) as l_user then
l_node.set_author (l_user)
storage.update_node (l_node)
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary))
end
end
end
-- test_update_node
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- l_node := ll_node.twin
-- l_node.set_content ("New Content")
-- l_node.set_summary ("New Summary")
-- l_node.set_title("New Title")
-- if attached storage.user_by_email (default_user.email) as l_user then
-- l_node.set_author (l_user)
-- storage.update_node (l_node)
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
-- end
test_update_node_title
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
storage.new_user (custom_user ("u2", "p2", "e2"))
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_title (2,ll_node.id, "New Title")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_title
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- storage.new_user (custom_user ("u2", "p2", "e2"))
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_title (2,ll_node.id, "New Title")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_update_node_summary
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
storage.new_user (custom_user ("u2", "p2", "e2"))
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_summary (2,ll_node.id, "New Summary")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
end
end
-- test_update_node_summary
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- storage.new_user (custom_user ("u2", "p2", "e2"))
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_summary (2,ll_node.id, "New Summary")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
test_update_node_content
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
storage.new_user (custom_user ("u2", "p2", "e2"))
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_content (2,ll_node.id, "New Content")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_content
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- storage.new_user (custom_user ("u2", "p2", "e2"))
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_content (2,ll_node.id, "New Content")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_update_node_title_by_author
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_title (1,ll_node.id, "New Title")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_title_by_author
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_title (1,ll_node.id, "New Title")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_update_node_summary_by_author
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_summary (1,ll_node.id, "New Summary")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
end
end
-- test_update_node_summary_by_author
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_summary (1,ll_node.id, "New Summary")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
test_update_node_content_by_author
local
l_node: CMS_NODE
do
l_node := custom_node ("Content", "Summary", "Title")
storage.new_user (default_user)
l_node.set_author (storage.user_by_email (default_user.email))
storage.new_node (l_node)
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
storage.update_node_content (1,ll_node.id, "New Content")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_content_by_author
-- local
-- l_node: CMS_NODE
-- do
-- l_node := custom_node ("Content", "Summary", "Title")
-- storage.new_user (default_user)
-- l_node.set_author (storage.user_by_email (default_user.email))
-- storage.new_node (l_node)
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_content (1,ll_node.id, "New Content")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_delete_node
local
l_node: CMS_NODE
l_user: like {CMS_NODE}.author
do
storage.new_user (custom_user ("test_delete", "testu", "email"))
l_user := storage.user_by_name ("test_delete")
across 1 |..| 10 as c loop
l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
l_node.set_author (l_user)
storage.new_node (l_node)
end
assert ("Exist node id: 10", attached storage.node_by_id (10) as ll_node and then ll_node.title ~ "Title_10" )
storage.delete_node_by_id (10)
assert ("Not exist node id: 10", storage.node_by_id (10) = Void)
end
-- test_delete_node
-- local
-- l_node: CMS_NODE
-- l_user: like {CMS_NODE}.author
-- do
-- storage.new_user (custom_user ("test_delete", "testu", "email"))
-- l_user := storage.user_by_name ("test_delete")
-- across 1 |..| 10 as c loop
-- l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)
-- l_node.set_author (l_user)
-- storage.new_node (l_node)
-- end
-- assert ("Exist node id: 10", attached storage.node_by_id (10) as ll_node and then ll_node.title ~ "Title_10" )
-- storage.delete_node_by_id (10)
-- assert ("Not exist node id: 10", storage.node_by_id (10) = Void)
-- end
end

View File

@@ -10,6 +10,7 @@
<library name="cms" location="..\..\..\..\cms-safe.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="model" location="..\..\..\model\cms_model-safe.ecf"/>
<library name="module_node" location="..\..\..\..\modules\node\node-safe.ecf"/>
<library name="persitence_mysql" location="..\persistence_mysql-safe.ecf" readonly="false"/>
<library name="process" location="$ISE_LIBRARY\library\process\process-safe.ecf"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
@@ -18,6 +19,7 @@
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
<exclude>/nodes$</exclude>
</file_rule>
</cluster>
</target>

View File

@@ -75,11 +75,11 @@ feature -- Test routines
connection.begin_transaction
u.set_email ("test@example.com")
assert ("Has user:", storage.user_by_email ("test@example.com") /= Void)
storage.new_node (default_node)
assert ("Has one node:", storage.nodes_count = 1)
-- storage.new_node (default_node)
-- assert ("Has one node:", storage.nodes_count = 1)
connection.rollback
assert ("Not has user:", storage.user_by_email ("test@example.com") = Void)
assert ("Has no node:", storage.nodes_count = 0)
-- assert ("Has no node:", storage.nodes_count = 0)
end
end

View File

@@ -41,8 +41,9 @@ feature {NONE} -- Fixture Factories: Nodes
Result := custom_node ("Default content", "default summary", "Default")
end
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_PAGE
do
create Result.make (a_content, a_summary, a_title)
create Result.make (a_title)
Result.set_content (a_content, a_summary, Void)
end
end

View File

@@ -8,11 +8,11 @@
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\..\app_env\app_env-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="layout" location="..\..\layout\layout-safe.ecf"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="model" location="..\..\model\cms_model-safe.ecf"/>
<library name="odbc" location="$ISE_LIBRARY\library\store\dbms\rdbms\odbc\odbc-safe.ecf"/>

View File

@@ -1,73 +0,0 @@
note
description: "Summary description for {CMS_NODE_STORAGE_SQLITE}."
author: ""
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_NODE_STORAGE_SQLITE
inherit
CMS_NODE_STORAGE_SQL
redefine
nodes, recent_nodes
end
feature {NONE} -- Implementation
db_handler: DATABASE_HANDLER
deferred
end
feature -- Access
nodes: LIST [CMS_NODE]
-- List of nodes.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
across nodes_iterator as ic loop
Result.force (ic.item)
end
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (a_count)
across recent_nodes_iterator (a_lower, a_count) as c loop
Result.force (c.item)
end
end
feature -- Access: iterator
nodes_iterator: DATABASE_ITERATION_CURSOR [CMS_NODE]
-- List of nodes.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".nodes_iterator")
create l_parameters.make (0)
sql_query (select_nodes, l_parameters)
create Result.make (db_handler, agent fetch_node)
end
recent_nodes_iterator (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [CMS_NODE]
-- The most recent `a_rows'.
local
l_parameters: STRING_TABLE [ANY]
l_query: STRING
do
-- FIXME: check implementation...
error_handler.reset
write_information_log (generator + ".recent_nodes_iterator")
create l_parameters.make (2)
l_parameters.put (a_rows, "rows")
l_parameters.put (a_lower, "offset")
create l_query.make_from_string (select_recent_nodes)
sql_query (l_query, l_parameters)
create Result.make (db_handler, agent fetch_node)
end
end

View File

@@ -1,5 +1,5 @@
note
description: "Summary description for {CMS_STORAGE_MYSQL}."
description: "Summary description for {CMS_STORAGE_SQLITE}."
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
@@ -7,13 +7,11 @@ class
CMS_STORAGE_SQLITE
inherit
CMS_STORAGE
CMS_STORAGE_STORE_SQL
CMS_USER_STORAGE_SQLITE
CMS_CORE_STORAGE_SQL_I
CMS_NODE_STORAGE_SQLITE
CMS_USER_STORAGE_SQL_I
REFACTORING_HELPER
@@ -27,5 +25,5 @@ feature -- Status report
do
Result := has_user
end
end

View File

@@ -1,6 +1,6 @@
note
description: "[
Objects that ...
Interface responsible to instantiate CMS_STORAGE_SQLITE object.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
@@ -10,7 +10,9 @@ class
CMS_STORAGE_SQLITE_BUILDER
inherit
CMS_STORAGE_BUILDER
CMS_STORAGE_STORE_SQL_BUILDER
GLOBAL_SETTINGS
create
make
@@ -27,65 +29,25 @@ feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_SQLITE
local
s: STRING
conn: DATABASE_CONNECTION
do
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.layout.application_config_path) as l_database_config then
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.application_config_path) as l_database_config then
s := "Driver=SQLite3 ODBC Driver;Database="
if attached l_database_config.database_name as db_name then
s.append (db_name)
end
s.append (";LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;")
create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (s))
--create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (l_database_config.connection_string))
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)
create {DATABASE_CONNECTION_ODBC} conn.login_with_connection_string (s)
if conn.is_connected then
create Result.make (conn)
set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default.
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)
end
end
end
end
end
initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL)
do
initialize_schema (a_setup, a_storage)
initialize_data (a_setup, a_storage)
end
initialize_schema (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL)
local
p: PATH
f: PLAIN_TEXT_FILE
sql: STRING
do
p := a_setup.layout.path.extended ("scripts").extended ("sqlite.sql")
create f.make_with_path (p)
if f.exists and then f.is_access_readable then
create sql.make (f.count)
f.open_read
from
f.start
until
f.exhausted or f.end_of_file
loop
f.read_stream_thread_aware (1_024)
sql.append (f.last_string)
end
f.close
a_storage.error_handler.reset
-- a_storage.sql_begin_transaction
a_storage.sql_change (sql, Void)
-- a_storage.sql_commit_transaction
end
end
initialize_data (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL)
local
u: CMS_USER
do
create u.make ("admin")
u.set_password ("#admin#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
end
end

View File

@@ -1,14 +0,0 @@
note
description: "Summary description for {CMS_USER_STORAGE_SQLITE}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_USER_STORAGE_SQLITE
inherit
CMS_USER_STORAGE_SQL
end

View File

@@ -167,107 +167,107 @@ feature -- Test routines
assert ("Valid password", storage.is_valid_credential ("test", "password"))
end
test_recent_nodes_empty
do
assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty)
end
-- test_recent_nodes_empty
-- do
-- assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty)
-- end
test_recent_nodes
local
l_nodes: LIST[CMS_NODE]
do
across 1 |..| 10 as c loop
storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
end
l_nodes := storage.recent_nodes (0, 10)
assert ("10 recent nodes", l_nodes.count = 10)
assert ("First node id=10", l_nodes.first.id = 10)
assert ("Last node id=1", l_nodes.last.id = 1)
-- test_recent_nodes
-- local
-- l_nodes: LIST[CMS_NODE]
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- l_nodes := storage.recent_nodes (0, 10)
-- assert ("10 recent nodes", l_nodes.count = 10)
-- assert ("First node id=10", l_nodes.first.id = 10)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (5, 10)
assert ("5 recent nodes", l_nodes.count = 5)
assert ("First node id=5", l_nodes.first.id = 5)
assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (5, 10)
-- assert ("5 recent nodes", l_nodes.count = 5)
-- assert ("First node id=5", l_nodes.first.id = 5)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (9, 10)
assert ("1 recent nodes", l_nodes.count = 1)
assert ("First node id=1", l_nodes.first.id = 1)
assert ("Last node id=1", l_nodes.last.id = 1)
-- l_nodes := storage.recent_nodes (9, 10)
-- assert ("1 recent nodes", l_nodes.count = 1)
-- assert ("First node id=1", l_nodes.first.id = 1)
-- assert ("Last node id=1", l_nodes.last.id = 1)
l_nodes := storage.recent_nodes (10, 10)
assert ("Is empty", l_nodes.is_empty)
end
-- l_nodes := storage.recent_nodes (10, 10)
-- assert ("Is empty", l_nodes.is_empty)
-- end
test_node_does_not_exist
do
across 1 |..| 10 as c loop
storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
end
assert ("Not exist node id: 12", storage.node_by_id (12) = Void)
end
-- test_node_does_not_exist
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("Not exist node id: 12", storage.node_by_id (12) = Void)
-- end
test_node
do
across 1 |..| 10 as c loop
storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
end
assert ("has nodes", storage.nodes.count > 5)
assert ("Node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
end
-- test_node
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("has nodes", storage.nodes.count > 5)
-- assert ("Node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
-- end
test_update_node
local
l_node: CMS_NODE
do
storage.new_node (custom_node ("Content", "Summary", "Title"))
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
l_node := ll_node.twin
l_node.set_content ("New Content")
l_node.set_summary ("New Summary")
l_node.set_title("New Title")
-- test_update_node
-- local
-- l_node: CMS_NODE
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- l_node := ll_node.twin
-- l_node.set_content ("New Content")
-- l_node.set_summary ("New Summary")
-- l_node.set_title("New Title")
-- storage.update_node (l_node)
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary))
end
end
---- storage.update_node (l_node)
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
test_update_node_title
do
storage.new_node (custom_node ("Content", "Summary", "Title"))
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_title (ll_node.id, "New Title")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_title
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_title (ll_node.id, "New Title")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_update_node_summary
do
storage.new_node (custom_node ("Content", "Summary", "Title"))
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_summary (ll_node.id, "New Summary")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
end
end
-- test_update_node_summary
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_summary (ll_node.id, "New Summary")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary))
-- end
-- end
test_update_node_content
do
storage.new_node (custom_node ("Content", "Summary", "Title"))
if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
-- storage.update_node_content (ll_node.id, "New Content")
assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
end
end
-- test_update_node_content
-- do
-- storage.new_node (custom_node ("Content", "Summary", "Title"))
-- if attached {CMS_NODE} storage.node_by_id (1) as ll_node then
---- storage.update_node_content (ll_node.id, "New Content")
-- assert ("Updated", attached {CMS_NODE} storage.node_by_id (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary)
-- end
-- end
test_delete_node
do
across 1 |..| 10 as c loop
storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
end
assert ("Exist node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
storage.delete_node_by_id (10)
assert ("Not exist node id: 10", storage.node_by_id (10) = Void)
end
-- test_delete_node
-- do
-- across 1 |..| 10 as c loop
-- storage.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out))
-- end
-- assert ("Exist node id: 10", attached storage.node_by_id (10) as l_node and then l_node.title ~ "Title_10" )
-- storage.delete_node_by_id (10)
-- assert ("Not exist node id: 10", storage.node_by_id (10) = Void)
-- end
end

View File

@@ -10,6 +10,7 @@
<library name="cms" location="..\..\..\..\cms-safe.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="model" location="..\..\..\model\cms_model-safe.ecf"/>
<library name="module_node" location="..\..\..\..\modules\node\node-safe.ecf"/>
<library name="persitence_sqlite" location="..\persistence_sqlite-safe.ecf" readonly="false"/>
<library name="process" location="$ISE_LIBRARY\library\process\process-safe.ecf"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
@@ -18,6 +19,7 @@
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
<exclude>/nodes$</exclude>
</file_rule>
</cluster>
</target>

View File

@@ -38,17 +38,18 @@ feature {NONE} -- Fixture Factory: Users
Result.set_email (a_email)
end
feature {NONE} -- Fixture Factories: Nodes
--feature {NONE} -- Fixture Factories: Nodes
default_node: CMS_NODE
do
Result := custom_node ("Default content", "default summary", "Default")
end
-- default_node: CMS_NODE
-- do
-- Result := custom_node ("Default content", "default summary", "Default")
-- end
custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE
do
create Result.make (a_content, a_summary, a_title)
end
-- custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_PAGE
-- do
-- create Result.make (a_title)
-- Result.set_content (a_content, a_summary, Void)
-- end
end

1
modules/README.md Normal file
View File

@@ -0,0 +1 @@
Location for contributed CMS modules.

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="basic_auth" uuid="05216FE9-20F3-45FB-AB2E-8FCD75A8AD7E" library_target="basic_auth">
<target name="basic_auth">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="basic_auth" uuid="05216FE9-20F3-45FB-AB2E-8FCD75A8AD7E" library_target="basic_auth">
<target name="basic_auth">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -37,12 +37,11 @@ feature {NONE} -- Initialization
feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER
-- Node router.
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
create Result.make (2)
configure_api_login (a_api, Result)
configure_api_logoff (a_api, Result)
configure_api_login (a_api, a_router)
configure_api_logoff (a_api, a_router)
end
feature -- Access: filter
@@ -110,9 +109,9 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK
do
if attached a_response.current_user (a_response.request) as u then
create lnk.make (u.name + " (Logout)", "/basic_auth_logoff?destination=" + a_response.request.request_uri)
create lnk.make (u.name + " (Logout)", "basic_auth_logoff?destination=" + a_response.request.request_uri)
else
create lnk.make ("Login", "/basic_auth_login?destination=" + a_response.request.request_uri)
create lnk.make ("Login", "basic_auth_login?destination=" + a_response.request.request_uri)
end
-- if not a_menu_system.primary_menu.has (lnk) then
lnk.set_weight (99)

View File

@@ -1,5 +1,7 @@
note
description: "Processes a HTTP request's BASIC authorization headers, putting the result into the execution variable user."
description: "[
Processes a HTTP request's BASIC authorization headers, putting the result into the execution variable user.
]"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
@@ -20,11 +22,12 @@ feature -- Basic operations
-- Execute the filter.
local
l_auth: HTTP_AUTHORIZATION
utf: UTF_CONVERTER
do
api.logger.put_debug (generator + ".execute ", Void)
create l_auth.make (req.http_authorization)
if attached req.raw_header_data as l_raw_data then
api.logger.put_debug (generator + ".execute " + l_raw_data, Void)
api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
end
-- A valid user
if

View File

@@ -51,7 +51,7 @@ feature -- HTTP Methods
api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void)
if attached req.query_parameter ("prompt") as l_prompt then
unset_current_user (req)
send_access_denied (res)
send_access_denied_message (res)
else
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
unset_current_user (req)

View File

@@ -0,0 +1,42 @@
note
description: "[
Interface defining a CMS content type.
]"
status: "draft"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_CONTENT_TYPE
inherit
CMS_API_ACCESS
feature -- Access
name: READABLE_STRING_8
-- Internal name.
deferred
end
title: READABLE_STRING_32
-- Human readable name.
deferred
end
description: detachable READABLE_STRING_32
-- Optional description
deferred
end
feature -- Access
available_formats: LIST [CONTENT_FORMAT]
-- Available formats for Current type.
deferred
end
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

354
modules/node/cms_node_api.e Normal file
View File

@@ -0,0 +1,354 @@
note
description: "[
API to manage CMS Nodes
]"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
CMS_NODE_API
inherit
CMS_MODULE_API
redefine
initialize
end
REFACTORING_HELPER
create {NODE_MODULE}
make_with_storage
feature {NONE} -- Initialization
make_with_storage (a_api: CMS_API; a_node_storage: CMS_NODE_STORAGE_I)
do
node_storage := a_node_storage
make (a_api)
end
initialize
-- <Precursor>
do
Precursor
initialize_node_types
end
initialize_node_types
-- Initialize content type system.
local
ct: CMS_PAGE_NODE_TYPE
do
create content_types.make (1)
create content_type_webform_managers.make (1)
create ct
add_content_type (ct)
add_content_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct))
end
feature {CMS_MODULE} -- Access nodes storage.
node_storage: CMS_NODE_STORAGE_I
feature -- Content type
content_types: ARRAYED_LIST [CMS_CONTENT_TYPE]
-- Available content types
node_types: ARRAYED_LIST [attached like node_type]
-- Node content types.
do
create Result.make (content_types.count)
across
content_types as ic
loop
if attached {like node_type} ic.item as l_node_type then
Result.extend (l_node_type)
end
end
end
add_content_type (a_type: CMS_CONTENT_TYPE)
-- Register content type `a_type'.
do
content_types.force (a_type)
end
content_type (a_name: READABLE_STRING_GENERAL): detachable CMS_CONTENT_TYPE
-- Content type named `a_named' if any.
do
across
content_types as ic
until
Result /= Void
loop
Result := ic.item
if not a_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
end
node_type (a_name: READABLE_STRING_GENERAL): detachable CMS_NODE_TYPE [CMS_NODE]
-- Content type named `a_named' if any.
do
across
content_types as ic
until
Result /= Void
loop
if
attached {like node_type} ic.item as l_node_type and then
a_name.is_case_insensitive_equal (l_node_type.name)
then
Result := l_node_type
end
end
end
node_type_for (a_node: CMS_NODE): like node_type
-- Content type for node `a_node' if any.
local
l_type_name: READABLE_STRING_8
do
l_type_name := a_node.content_type
Result := node_type (l_type_name)
end
feature -- Content type webform
content_type_webform_managers: ARRAYED_LIST [CMS_CONTENT_TYPE_WEBFORM_MANAGER]
-- Available content types
add_content_type_webform_manager (a_manager: CMS_CONTENT_TYPE_WEBFORM_MANAGER)
-- Register webform manager `a_manager'.
do
content_type_webform_managers.force (a_manager)
end
content_type_webform_manager (a_content_type: CMS_CONTENT_TYPE): detachable CMS_CONTENT_TYPE_WEBFORM_MANAGER
-- Web form manager for content type `a_content_type' if any.
local
l_type_name: READABLE_STRING_GENERAL
do
l_type_name := a_content_type.name
across
content_type_webform_managers as ic
until
Result /= Void
loop
Result := ic.item
if not l_type_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
end
node_type_webform_manager (a_node_type: CMS_CONTENT_TYPE): detachable CMS_NODE_TYPE_WEBFORM_MANAGER_I [CMS_NODE]
-- Web form manager for node type `a_node_type' if any.
local
l_type_name: READABLE_STRING_GENERAL
do
l_type_name := a_node_type.name
across
content_type_webform_managers as ic
until
Result /= Void
loop
if
attached {like node_type_webform_manager} ic.item as l_manager and then
l_type_name.is_case_insensitive_equal (l_manager.name)
then
Result := l_manager
end
end
end
feature -- URL
new_content_path (ct: detachable CMS_CONTENT_TYPE): STRING
-- URI path for new content of type `ct'
-- or URI of path for selection of new content possibilities if ct is Void.
do
if ct /= Void then
Result := "node/add/" + ct.name
else
Result := "node/"
end
end
node_link (a_node: CMS_NODE): CMS_LOCAL_LINK
-- CMS link for node `a_node'.
require
a_node.has_id
do
create Result.make (a_node.title, cms_api.path_alias (node_path (a_node)))
end
node_path (a_node: CMS_NODE): STRING
-- URI path for node `a_node'.
-- using the /node/{nid} url.
require
a_node.has_id
do
Result := "node/" + a_node.id.out
end
nodes_path: STRING
-- URI path for list of nodes.
do
Result := "nodes"
end
feature -- Access: Node
nodes_count: NATURAL_64
do
Result := node_storage.nodes_count
end
nodes: LIST [CMS_NODE]
-- List of nodes.
do
Result := node_storage.nodes
end
trashed_nodes (a_user: CMS_USER): LIST [CMS_NODE]
-- List of nodes with status in {CMS_NODE_API}.trashed.
-- if the current user is admin, it will retrieve all the trashed nodes
do
Result := node_storage.trashed_nodes (a_user.id)
end
recent_nodes (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_NODE]
-- List of the `a_rows' most recent nodes starting from `a_offset'.
do
Result := node_storage.recent_nodes (params.offset.to_integer_32, params.size.to_integer_32)
end
node (a_id: INTEGER_64): detachable CMS_NODE
-- Node by ID.
do
debug ("refactor_fixme")
fixme ("Check preconditions")
end
Result := full_node (node_storage.node_by_id (a_id))
end
full_node (a_node: detachable CMS_NODE): detachable CMS_NODE
-- If `a_node' is partial, return the full node from `a_node',
-- otherwise return directly `a_node'.
do
if attached {CMS_PARTIAL_NODE} a_node as l_partial_node then
if attached node_type_for (l_partial_node) as ct then
Result := ct.new_node (l_partial_node)
node_storage.fill_node (Result)
else
Result := l_partial_node
end
else
Result := a_node
end
-- Update link with aliasing.
if a_node /= Void and then a_node.has_id then
a_node.set_link (node_link (a_node))
end
-- Update partial user if needed.
if
Result /= Void and then
attached {CMS_PARTIAL_USER} Result.author as l_partial_author
then
if attached cms_api.user_api.user_by_id (l_partial_author.id) as l_author then
Result.set_author (l_author)
else
check
valid_author_id: False
end
end
end
end
is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN
-- Is the user `u' owner of the node `n'.
do
if attached node_storage.node_author (a_node.id) as l_author then
Result := u.same_as (l_author)
end
end
feature -- Permission Scope: Node
has_permission_for_action_on_node (a_action: READABLE_STRING_8; a_node: CMS_NODE; a_user: detachable CMS_USER; ): BOOLEAN
-- Has permission to execute action `a_action' on node `a_node', by eventual user `a_user'?
local
l_type_name: READABLE_STRING_8
do
l_type_name := a_node.content_type
Result := cms_api.user_has_permission (a_user, a_action + " any " + l_type_name)
if not Result and a_user /= Void then
if is_author_of_node (a_user, a_node) then
Result := cms_api.user_has_permission (a_user, a_action + " own " + l_type_name)
end
end
end
feature -- Change: Node
save_node (a_node: CMS_NODE)
-- Save `a_node'.
do
node_storage.save_node (a_node)
end
new_node (a_node: CMS_NODE)
-- Add a new node `a_node'
require
no_id: not a_node.has_id
do
node_storage.new_node (a_node)
end
delete_node (a_node: CMS_NODE)
-- Delete `a_node'.
do
if a_node.has_id then
node_storage.delete_node (a_node)
end
end
update_node (a_node: CMS_NODE)
-- Update node `a_node' data.
do
node_storage.update_node (a_node)
end
trash_node (a_node: CMS_NODE)
-- Trash node `a_node'.
--! remove the node from the storage.
do
node_storage.trash_node (a_node)
end
restore_node (a_node: CMS_NODE)
-- Restore node `a_node'.
-- From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
do
node_storage.restore_node (a_node)
end
feature -- Node status
Not_published: INTEGER = 0
-- The node is not published.
Published: INTEGER = 1
-- The node is published.
Trashed: INTEGER = -1
-- The node is trashed (soft delete), ready to be deleted/destroyed from storage.
end

View File

@@ -0,0 +1,30 @@
note
description: "[
Interface defining a CMS content type.
]"
status: "draft"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_NODE_TYPE [G -> CMS_NODE]
inherit
CMS_CONTENT_TYPE
feature -- Factory
new_node_with_title (a_title: READABLE_STRING_32; a_partial_node: detachable CMS_NODE): like new_node
-- New node with `a_title' and fill from partial `a_partial_node' if set.
deferred
end
new_node (a_partial_node: detachable CMS_NODE): G
-- New node based on partial `a_partial_node' if set.
deferred
end
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,268 @@
note
description: "[
CMS abstraction for CMS content entity, named "node".
]"
status: "draft"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_NODE
inherit
REFACTORING_HELPER
feature{NONE} -- Initialization
make_empty
-- Create empty node.
do
make ({STRING_32} "")
end
make (a_title: READABLE_STRING_32)
-- Create current node with `a_title'.
local
l_time: DATE_TIME
do
create l_time.make_now_utc
set_title (a_title)
set_creation_date (l_time)
set_modification_date (l_time)
set_publication_date (l_time)
mark_not_published
ensure
title_set: title = a_title
end
feature -- Conversion
import_node (a_node: CMS_NODE)
-- Import `a_node' into current node.
do
set_id (a_node.id)
set_revision (a_node.revision)
set_title (a_node.title)
set_creation_date (a_node.creation_date)
set_modification_date (a_node.modification_date)
set_publication_date (a_node.publication_date)
set_author (a_node.author)
set_content (
a_node.content,
a_node.summary,
a_node.format
)
set_status (a_node.status)
set_link (a_node.link)
end
feature -- Access
id: INTEGER_64 assign set_id
-- Unique id.
--| Should we use NATURAL_64 instead?
revision: INTEGER_64 assign set_revision
-- Revision value.
--| Note: for now version is not supported.
content_type: READABLE_STRING_8
-- Associated content type name.
-- Page, Article, Blog, News, etc.
deferred
end
status: INTEGER
-- Associated status for the current node.
-- default: {CMS_NODE_API}.Not_Published}
-- {CMS_NODE_API}.Published
-- {CMS_NODE_API}.Trashed
feature -- Access
title: READABLE_STRING_32
-- Full title of the node.
-- Required!
summary: detachable READABLE_STRING_8
-- A short summary of the node.
deferred
end
content: detachable READABLE_STRING_8
-- Content of the node.
deferred
end
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
deferred
end
feature -- Access: date
modification_date: DATE_TIME
-- When the node was updated.
creation_date: DATE_TIME
-- When the node was created.
publication_date: DATE_TIME
-- When the node was published.
publication_date_output: READABLE_STRING_32
-- Formatted output.
feature -- Access: author
author: detachable CMS_USER
-- Author of current node.
feature -- status report
has_id: BOOLEAN
-- Has unique identifier?
do
Result := id > 0
end
same_node (a_node: CMS_NODE): BOOLEAN
-- Is `a_node' same node as Current?
do
-- FIXME: if we introduce notion of revision, update this part!
Result := Current = a_node or id = a_node.id
ensure
valid_result: Result implies a_node.id = id
end
is_typed_as (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
-- Is current node of type `a_content_type' ?
do
Result := a_content_type.is_case_insensitive_equal (content_type)
end
feature -- Access: menu
link: detachable CMS_LOCAL_LINK
-- Associated menu link.
feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format)
-- Set `content', `summary' and `format' to `a_content', `a_summary' and `a_format'.
-- The `format' is associated with both `content' and `summary'
deferred
ensure
content_assigned: content = a_content
summary_assigned: summary = a_summary
format_assigned: format = a_format
end
set_title (a_title: like title)
-- Assign `title' with `a_title'.
do
title := a_title
if attached link as lnk then
lnk.set_title (a_title)
end
ensure
title_assigned: title = a_title
end
set_modification_date (a_modification_date: like modification_date)
-- Assign `modification_date' with `a_modification_date'.
do
modification_date := a_modification_date
ensure
modification_date_assigned: modification_date = a_modification_date
end
set_creation_date (a_creation_date: like creation_date)
-- Assign `creation_date' with `a_creation_date'.
do
creation_date := a_creation_date
ensure
creation_date_assigned: creation_date = a_creation_date
end
set_publication_date (a_publication_date: like publication_date)
-- Assign `publication_date' with `a_publication_date'.
do
publication_date := a_publication_date
publication_date_output := publication_date.formatted_out ("yyyy/[0]mm/[0]dd")
ensure
publication_date_assigned: publication_date = a_publication_date
end
set_id (a_id: like id)
-- Assign `id' with `a_id'.
do
id := a_id
ensure
id_assigned: id = a_id
end
set_revision (a_revision: like revision)
-- Assign `revision' with `a_revision'.
do
revision := a_revision
ensure
revision_assigned: revision = a_revision
end
set_author (u: like author)
-- Assign 'author' with `u'
do
author := u
ensure
auther_set: author = u
end
set_link (a_link: like link)
-- Set `link' to `a_link'.
do
link := a_link
end
feature -- Status change
mark_not_published
-- Set status to not_published.
do
set_status ({CMS_NODE_API}.not_published)
ensure
status_not_published: status = {CMS_NODE_API}.not_published
end
mark_published
-- Set status to published.
do
set_status ({CMS_NODE_API}.published)
ensure
status_published: status = {CMS_NODE_API}.published
end
mark_trashed
-- Set status to trashed.
do
set_status ({CMS_NODE_API}.trashed)
ensure
status_trash: status = {CMS_NODE_API}.trashed
end
feature {CMS_NODE_STORAGE_I} -- Access: status change.
set_status (a_status: like status)
-- Assign `status' with `a_status'.
do
status := a_status
ensure
status_set: status = a_status
end
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,85 @@
note
description: "Node object representing the CMS_NODE data in database."
date: "$Date$"
revision: "$Revision$"
class
CMS_PARTIAL_NODE
inherit
CMS_NODE
rename
make_empty as make_empty_node,
set_content as set_all_content
end
create
make_empty
feature {NONE} -- Initialization
make_empty (a_content_type: READABLE_STRING_8)
require
type_not_blank: not a_content_type.is_whitespace
do
content_type := a_content_type
make_empty_node
end
feature -- Access: code
content_type: READABLE_STRING_8
-- <Precursor>
feature -- Access: content
summary: detachable READABLE_STRING_8
-- A short summary of the node.
content: detachable READABLE_STRING_8
-- Content of the node.
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
feature -- Element change
set_all_content (a_content: like content; a_summary: like summary; a_format: like format)
-- <Precursor>
do
set_content (a_content)
set_summary (a_summary)
set_format (a_format)
end
set_content (a_content: like content)
-- Assign `content' with `a_content', and set the associated `format'.
do
content := a_content
ensure
content_assigned: content = a_content
end
set_summary (a_summary: like summary)
-- Assign `summary' with `a_summary'.
do
summary := a_summary
ensure
summary_assigned: summary = a_summary
end
set_format (a_format: like format)
-- Assign `format' with `a_format'.
do
format := a_format
ensure
format_assigned: format = a_format
end
invariant
note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,95 @@
note
description: "A page node."
date: "$Date$"
revision: "$Revision$"
class
CMS_PAGE
inherit
CMS_NODE
redefine
make_empty,
import_node
end
create
make_empty,
make
feature {NONE} -- Initialization
make_empty
do
Precursor
end
feature -- Conversion
import_node (a_node: CMS_NODE)
-- <Precursor>
do
Precursor (a_node)
if attached {CMS_PAGE} a_node as l_page then
set_parent (l_page.parent)
end
end
feature -- Access
content_type: READABLE_STRING_8
once
Result := {CMS_PAGE_NODE_TYPE}.name
end
feature -- Access: content
summary: detachable READABLE_STRING_8
-- A short summary of the node.
content: detachable READABLE_STRING_8
-- Content of the node.
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
parent: detachable CMS_PAGE
-- Eventual parent page.
--| Used to describe a book structure.
feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format)
do
content := a_content
summary := a_summary
format := a_format
end
set_parent (a_page: detachable CMS_PAGE)
-- Set `parent' to `a_page'
require
Current_is_not_parent_of_a_page: not is_parent_of (a_page)
do
parent := a_page
end
feature -- Status report
is_parent_of (a_page: detachable CMS_PAGE): BOOLEAN
-- Is Current page, a parent of `a_page' ?
do
if
a_page /= Void and then
attached a_page.parent as l_parent
then
if l_parent.same_node (a_page) then
Result := True
else
Result := is_parent_of (l_parent)
end
end
end
end

View File

@@ -0,0 +1,63 @@
note
description: "Summary description for {CMS_PAGE_NODE_TYPE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_PAGE_NODE_TYPE
inherit
CMS_NODE_TYPE [CMS_PAGE]
redefine
default_create
end
feature {NONE} -- Initialization
default_create
do
Precursor
create {ARRAYED_LIST [like available_formats.item]} available_formats.make (3)
available_formats.extend (create {PLAIN_TEXT_CONTENT_FORMAT})
available_formats.extend (create {FILTERED_HTML_CONTENT_FORMAT})
available_formats.extend (create {FULL_HTML_CONTENT_FORMAT})
end
feature -- Access
name: STRING = "page"
-- Internal name.
title: STRING_32 = "Page"
-- Human readable name.
description: STRING_32 = "Use basic pages for your content, such as an 'About us' page."
-- Optional description
feature -- Access
available_formats: LIST [CONTENT_FORMAT]
-- Available formats for Current type.
feature -- Factory
new_node_with_title (a_title: READABLE_STRING_32; a_partial_node: detachable CMS_NODE): like new_node
-- New node with `a_title' and fill from partial `a_partial_node' if set.
do
create Result.make (a_title)
if a_partial_node /= Void then
Result.import_node (a_partial_node)
Result.set_title (a_title)
end
end
new_node (a_partial_node: detachable CMS_NODE): CMS_PAGE
-- New node based on partial `a_partial_node', or from none.
do
create Result.make_empty
if a_partial_node /= Void then
Result.import_node (a_partial_node)
end
end
end

View File

@@ -0,0 +1,17 @@
note
description: "Summary description for {CMS_PARTIAL_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_PARTIAL_PAGE
inherit
CMS_PAGE
create
make_empty,
make
end

View File

@@ -0,0 +1,33 @@
note
description: "[
Html builder for content type `content_type'.
This is used to build webform and html output for a specific node, or node content type.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_CONTENT_TYPE_WEBFORM_MANAGER
inherit
CMS_API_ACCESS
feature {NONE} -- Initialization
make (a_type: like content_type)
do
content_type := a_type
end
feature -- Access
content_type: CMS_CONTENT_TYPE
-- Associated content type.
name: READABLE_STRING_8
-- Associated content type name.
do
Result := content_type.name
end
end

View File

@@ -0,0 +1,262 @@
note
description: "Summary description for {CMS_NODE_TYPE_WEBFORM_MANAGER}."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_NODE_TYPE_WEBFORM_MANAGER [G -> CMS_NODE]
inherit
CMS_NODE_TYPE_WEBFORM_MANAGER_I [G]
SHARED_WSF_PERCENT_ENCODER
feature -- Forms ...
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: WSF_FORM_TEXT_INPUT
fset: WSF_FORM_FIELD_SET
ta: WSF_FORM_TEXTAREA
tselect: WSF_FORM_SELECT
opt: WSF_FORM_SELECT_OPTION
l_uri: detachable READABLE_STRING_8
do
create ti.make ("title")
ti.set_label ("Title")
ti.set_size (70)
if a_node /= Void then
ti.set_text_value (a_node.title)
end
ti.set_is_required (True)
f.extend (ti)
f.extend_html_text ("<br/>")
create ta.make ("body")
ta.set_rows (10)
ta.set_cols (70)
if a_node /= Void then
ta.set_text_value (a_node.content)
end
-- ta.set_label ("Body")
ta.set_description ("This is the main content")
ta.set_is_required (False)
create fset.make
fset.set_legend ("Body")
fset.extend (ta)
fset.extend_html_text ("<br/>")
create tselect.make ("format")
tselect.set_label ("Body's format")
tselect.set_is_required (True)
across
content_type.available_formats as c
loop
create opt.make (c.item.name, c.item.title)
if attached c.item.html_help as f_help then
opt.set_description ("<ul>" + f_help + "</ul>")
end
tselect.add_option (opt)
end
if a_node /= Void and then attached a_node.format as l_format then
tselect.set_text_by_value (l_format)
end
fset.extend (tselect)
f.extend (fset)
-- Path alias
create ti.make ("path_alias")
ti.set_label ("Path")
ti.set_size (70)
if a_node /= Void and then a_node.has_id then
if attached a_node.link as lnk then
l_uri := lnk.location
else
l_uri := percent_encoder.percent_decoded_string (response.api.path_alias (response.node_api.node_path (a_node)))
end
ti.set_text_value (l_uri)
ti.set_description ("Optionally specify an alternative URL path by which this content can be accessed. For example, type 'about' when writing an about page. Use a relative path or the URL alias won't work.")
ti.set_validation_action (agent (fd: WSF_FORM_DATA; a_response: CMS_RESPONSE)
do
if
attached fd.string_item ("path_alias") as f_path_alias
then
if a_response.api.is_valid_path_alias (f_path_alias) then
-- Ok.
elseif f_path_alias.is_empty then
-- Ok
elseif f_path_alias.starts_with_general ("/") then
fd.report_invalid_field ("path_alias", "Path alias should not start with a slash '/' .")
elseif f_path_alias.has_substring ("://") then
fd.report_invalid_field ("path_alias", "Path alias should not be absolute url .")
else
-- TODO: implement full path alias validation
end
end
end(?, response)
)
end
if
attached f.fields_by_name ("title") as l_title_fields and then
attached l_title_fields.first as l_title_field
then
f.insert_after (ti, l_title_field)
else
f.extend (ti)
end
end
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
local
b: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT
do
if attached fd.integer_item ("id") as l_id and then l_id > 0 then
check a_node.id = l_id end
end
if attached fd.string_item ("title") as l_title then
a_node.set_title (l_title)
end
if attached fd.string_item ("body") as l_body then
b := l_body
end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
else
f := response.formats.default_format
end
if b /= Void then
a_node.set_content (b, Void, f.name) -- FIXME: summary
end
end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable CMS_NODE): G
-- <Precursor>
local
b: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT
l_node: detachable like new_node
do
if attached {like new_node} a_node as l_arg_node then
l_node := l_arg_node
else
l_node := content_type.new_node (a_node)
end
if attached fd.integer_item ("id") as l_id and then l_id > 0 then
if l_node /= Void then
check l_node.id = l_id end
else
if attached {like new_node} response.node_api.node (l_id) as n then
l_node := n
else
-- FIXME: Error
end
end
end
if attached fd.string_item ("title") as l_title then
if l_node = Void then
l_node := content_type.new_node (Void)
l_node.set_title (l_title)
else
l_node.set_title (l_title)
end
else
if l_node = Void then
l_node := content_type.new_node_with_title ("...", Void)
end
end
l_node.set_author (response.user)
if attached fd.string_item ("body") as l_body then
b := l_body
end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
else
f := response.formats.default_format
end
if b /= Void then
l_node.set_content (b, Void, f.name)
end
Result := l_node
end
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
-- <Precursor>
local
lnk: CMS_LOCAL_LINK
hdate: HTTP_DATE
s: STRING
node_api: CMS_NODE_API
do
node_api := a_response.node_api
a_response.add_variable (a_node, "node")
lnk := a_response.node_local_link (a_node, a_response.translation ("View", Void))
lnk.set_weight (1)
a_response.add_to_primary_tabs (lnk)
if a_node.status = {CMS_NODE_API}.trashed then
create lnk.make ("Trash", node_api.node_path (a_node) + "/trash")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
else
-- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status.
create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
if
a_node /= Void and then
a_node.id > 0 and then
attached node_api.node_type_for (a_node) as l_type and then
node_api.has_permission_for_action_on_node ("delete", a_node, a_response.current_user (a_response.request))
then
create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
create s.make_empty
s.append ("<div class=%"info%"> ")
if attached a_node.author as l_author then
s.append (" by ")
s.append (l_author.name)
end
if attached a_node.modification_date as l_modified then
s.append (" (modified: ")
create hdate.make_from_date_time (l_modified)
s.append (hdate.yyyy_mmm_dd_string)
s.append (")")
end
s.append ("</div>")
if attached a_node.content as l_content then
s.append ("<p class=%"content%">")
if attached node_api.cms_api.format (a_node.format) as f then
s.append (f.formatted_output (l_content))
else
s.append (a_response.formats.default_format.formatted_output (l_content))
end
s.append ("</p>")
end
a_response.set_title (a_node.title)
a_response.set_main_content (s)
end
end

View File

@@ -0,0 +1,67 @@
note
description: "[
Html builder for content type `content_type'.
This is used to build webform and html output for a specific node, or node content type.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_NODE_TYPE_WEBFORM_MANAGER_I [G -> CMS_NODE]
inherit
CMS_CONTENT_TYPE_WEBFORM_MANAGER
redefine
content_type
end
feature -- Access
content_type: CMS_NODE_TYPE [G]
-- Associated content type.
feature -- Query
has_valid_node_type (a_node: CMS_NODE): BOOLEAN
-- Accept `a_node' for Current operations.
do
Result := attached {like new_node} a_node
end
feature -- Forms ...
populate_form (response: NODE_RESPONSE; a_form: WSF_FORM; a_node: detachable CMS_NODE)
-- Fill the web form `a_form' with data from `a_node' if set.
require
a_node = Void or else has_valid_node_type (a_node)
deferred
end
feature -- Node ...
new_node (response: NODE_RESPONSE; a_form_data: WSF_FORM_DATA; a_node: detachable CMS_NODE): G
-- New typed node with data from `a_form_data', and eventually data from `a_node' if set.
require
a_node = Void or else has_valid_node_type (a_node)
deferred
--| Possible implementation:
--| Result := content_type.new_node (a_node)
end
update_node (response: NODE_RESPONSE; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE)
-- Update node `a_node' with data from `a_form_data'.
require
has_valid_node_type (a_node)
deferred
end
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
-- Append an html representation of `a_node' to response `a_response'.
require
has_valid_node_type (a_node)
deferred
end
end

View File

@@ -0,0 +1,184 @@
note
description: "Summary description for {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER
inherit
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE]
redefine
content_type,
append_html_output_to
end
create
make
feature -- Access
content_type: CMS_PAGE_NODE_TYPE
-- Associated content type.
feature -- Forms ...
-- fill_edit_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
-- local
-- ti: WSF_FORM_TEXT_INPUT
-- fset: WSF_FORM_FIELD_SET
-- ta: WSF_FORM_TEXTAREA
-- tselect: WSF_FORM_SELECT
-- opt: WSF_FORM_SELECT_OPTION
-- do
-- create ti.make ("title")
-- ti.set_label ("Title")
-- ti.set_size (70)
-- if a_node /= Void then
-- ti.set_text_value (a_node.title)
-- end
-- ti.set_is_required (True)
-- f.extend (ti)
-- f.extend_html_text ("<br/>")
-- create ta.make ("body")
-- ta.set_rows (10)
-- ta.set_cols (70)
-- if a_node /= Void then
-- ta.set_text_value (a_node.content)
-- end
---- ta.set_label ("Body")
-- ta.set_description ("This is the main content")
-- ta.set_is_required (False)
-- create fset.make
-- fset.set_legend ("Body")
-- fset.extend (ta)
-- fset.extend_html_text ("<br/>")
-- create tselect.make ("format")
-- tselect.set_label ("Body's format")
-- tselect.set_is_required (True)
-- across
-- content_type.available_formats as c
-- loop
-- create opt.make (c.item.name, c.item.title)
-- if attached c.item.html_help as f_help then
-- opt.set_description ("<ul>" + f_help + "</ul>")
-- end
-- tselect.add_option (opt)
-- end
-- if a_node /= Void and then attached a_node.format as l_format then
-- tselect.set_text_by_value (l_format)
-- end
-- fset.extend (tselect)
-- f.extend (fset)
-- end
-- change_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: like new_node)
-- local
-- b: detachable READABLE_STRING_8
-- f: detachable CONTENT_FORMAT
-- do
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then
-- check a_node.id = l_id end
-- end
-- if attached fd.string_item ("title") as l_title then
-- a_node.set_title (l_title)
-- end
-- if attached fd.string_item ("body") as l_body then
-- b := l_body
-- end
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- else
-- f := response.api.formats.default_format
-- end
-- if b /= Void then
-- a_node.set_content (b, Void, f.name) -- FIXME: summary
-- end
-- end
-- new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): CMS_PAGE
-- -- <Precursor>
-- local
-- b: detachable READABLE_STRING_8
-- f: detachable CONTENT_FORMAT
-- l_node: detachable like new_node
-- do
-- l_node := a_node
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then
-- if l_node /= Void then
-- check l_node.id = l_id end
-- else
-- if attached {like new_node} response.node_api.node (l_id) as n then
-- l_node := n
-- else
-- -- FIXME: Error
-- end
-- end
-- end
-- if attached fd.string_item ("title") as l_title then
-- if l_node = Void then
-- l_node := content_type.new_node (Void)
-- l_node.set_title (l_title)
-- else
-- l_node.set_title (l_title)
-- end
-- else
-- if l_node = Void then
-- l_node := content_type.new_node_with_title ("...", Void)
-- end
-- end
-- l_node.set_author (response.user)
-- if attached fd.string_item ("body") as l_body then
-- b := l_body
-- end
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- else
-- f := response.api.formats.default_format
-- end
-- if b /= Void then
-- l_node.set_content (b, Void, f.name)
-- end
-- Result := l_node
-- end
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
-- <Precursor>
local
s: STRING
do
Precursor (a_node, a_response)
if attached a_response.main_content as l_main_content then
s := l_main_content
else
create s.make_empty
end
if attached {CMS_PAGE} a_node as l_node_page then
if attached l_node_page.parent as l_parent_node then
s.append ("<div>Parent page is ")
s.append (a_response.link (l_parent_node.title + " (#" + l_parent_node.id.out + ")", a_response.node_api.node_path (l_parent_node), Void))
s.append ("</div>")
end
end
a_response.set_main_content (s)
end
end

View File

@@ -1,10 +1,10 @@
note
description: "Summary description for {NODE_HANDLER}."
description: "Node handler for the API."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
NODE_HANDLER
NODE_RESOURCE_HANDLER
inherit
CMS_NODE_HANDLER
@@ -69,6 +69,7 @@ feature -- HTTP Methods
l_id.is_integer and then
attached node_api.node (l_id.value.to_integer_64) as l_node
then
-- FIXME: there is a mix between CMS interface and API interface here.
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.add_variable (l_node, "node")
l_page.execute
@@ -107,7 +108,15 @@ feature -- HTTP Methods
end
else
-- New node
create u_node.make ("", "", "")
-- FIXME !!!
if attached {WSF_STRING} req.form_parameter ("type") as p_type then
if attached node_api.content_type (p_type.value) as ct then
end
create {CMS_PARTIAL_NODE} u_node.make_empty (p_type.url_encoded_value)
else
create {CMS_PARTIAL_NODE} u_node.make_empty ("")
end
update_node_from_data_form (req, u_node)
u_node.set_author (l_user)
node_api.new_node (u_node)
@@ -192,7 +201,7 @@ feature {NONE} -- Node
local
l_page: CMS_RESPONSE
do
if attached current_user_name (req) then
if api.user_has_permission (current_user (req), "create node") then
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.execute
else
@@ -205,16 +214,27 @@ feature -- {NONE} Form data
update_node_from_data_form (req: WSF_REQUEST; a_node: CMS_NODE)
-- Extract request form data and build a object
-- Node
local
l_title: detachable READABLE_STRING_32
l_summary, l_content, l_format: detachable READABLE_STRING_8
do
if attached {WSF_STRING} req.form_parameter ("title") as l_title then
a_node.set_title (l_title.value)
if attached {WSF_STRING} req.form_parameter ("title") as p_title then
l_title := p_title.value
a_node.set_title (l_title)
end
if attached {WSF_STRING} req.form_parameter ("summary") as l_summary then
a_node.set_summary (l_summary.value)
if attached {WSF_STRING} req.form_parameter ("summary") as p_summary then
l_summary := html_encoded (p_summary.value)
end
if attached {WSF_STRING} req.form_parameter ("content") as l_content then
a_node.set_content (l_content.value)
if attached {WSF_STRING} req.form_parameter ("content") as p_content then
l_content := html_encoded (p_content.value)
end
if attached {WSF_STRING} req.form_parameter ("format") as p_format then
l_format := p_format.url_encoded_value
end
if l_format = Void then
l_format := a_node.format
end
a_node.set_content (l_content, l_summary, l_format)
end
end

View File

@@ -4,7 +4,7 @@ note
revision: "$Revision: 96616 $"
class
NODES_HANDLER
NODE_RESOURCES_HANDLER
inherit
CMS_NODE_HANDLER
@@ -39,8 +39,6 @@ feature -- HTTP Methods
local
l_page: CMS_RESPONSE
s: STRING
l_user: CMS_USER
l_node: CMS_NODE
do
-- At the moment the template is hardcoded, but we can
-- get them from the configuration file and load them into

View File

@@ -0,0 +1,408 @@
note
description: "CMS Response handling node editing workflow using Web forms."
revision: "$Revision$"
class
NODE_FORM_RESPONSE
inherit
NODE_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api, a_node_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Execution
process
-- Computed response message.
local
b: STRING_8
f: like new_edit_form
fd: detachable WSF_FORM_DATA
nid: INTEGER_64
do
create b.make_empty
nid := node_id_path_parameter (request)
if
nid > 0 and then
attached node_api.node (nid) as l_node
then
if attached node_api.node_type_for (l_node) as l_type 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
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
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) + " saved")
else
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 ("<h1>")
b.append (translation ("Access denied", Void))
b.append ("</h1>")
end
else
set_title (translation ("Unknown node", Void))
end
elseif
attached {WSF_STRING} request.path_parameter ("type") as p_type and then
attached node_api.node_type (p_type.value) as l_type
then
if has_permissions (<<"create any", "create " + l_type.name>>) then
if attached l_type.new_node (Void) as l_node 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
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
f.process (Current)
fd := f.last_data
end
set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out)
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)
end
f.append_to_html (wsf_theme, b)
else
b.append ("<h1>")
b.append (translation ("Server error", Void))
b.append ("</h1>")
end
else
b.append ("<h1>")
b.append (translation ("Access denied", Void))
b.append ("</h1>")
end
else
set_title (translation ("Create new content ...", Void))
b.append ("<ul id=%"content-types%">")
across
node_api.node_types as ic
loop
if
attached ic.item as l_node_type and then
has_permissions (<<"create any", "create " + l_node_type.name>>)
then
b.append ("<li>" + link (l_node_type.name, "node/add/" + l_node_type.name, Void))
if attached l_node_type.description as d then
b.append ("<div class=%"description%">" + d + "</div>")
end
b.append ("</li>")
end
end
b.append ("</ul>")
end
set_main_content (b)
end
feature -- Form
edit_form_validate (fd: WSF_FORM_DATA; b: STRING)
local
l_preview: BOOLEAN
l_format: detachable CONTENT_FORMAT
do
l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview")
if l_preview then
b.append ("<strong>Preview</strong><div class=%"preview%">")
if attached fd.string_item ("format") as s_format and then attached api.format (s_format) as f_format then
l_format := f_format
end
if attached fd.string_item ("title") as l_title then
b.append ("<strong>Title:</strong><div class=%"title%">" + html_encoded (l_title) + "</div>")
end
if attached fd.string_item ("body") as l_body then
b.append ("<strong>Body:</strong><div class=%"body%">")
if l_format /= Void then
b.append (l_format.formatted_output (l_body))
else
b.append (html_encoded (l_body))
end
b.append ("</div>")
end
b.append ("</div>")
end
end
edit_form_submit (fd: WSF_FORM_DATA; a_node: detachable CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING)
local
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
debug ("cms")
across
fd as c
loop
b.append ("<li>" + html_encoded (c.key) + "=")
if attached c.item as v then
b.append (html_encoded (v.string_representation))
end
b.append ("</li>")
end
end
if a_node /= Void then
l_node := a_node
if l_node.has_id then
change_node (a_type, fd, a_node)
s := "modified"
else
change_node (a_type, fd, a_node)
l_node.set_author (user)
s := "created"
end
else
l_node := new_node (a_type, fd, Void)
l_node.set_author (user)
s := "created"
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 " + 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, Void))
end
add_success_message ("Node #" + l_node.id.out + " saved.")
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
if attached l_node.link as lnk then
set_redirection (lnk.location)
end
end
end
new_edit_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
th: WSF_FORM_HIDDEN_INPUT
do
create f.make (a_url, a_name)
create th.make ("node-id")
if a_node /= Void then
th.set_text_value (a_node.id.out)
else
th.set_text_value ("0")
end
f.extend (th)
populate_form (a_node_type, f, a_node)
f.extend_html_text ("<br/>")
create ts.make ("op")
ts.set_default_value ("Save")
f.extend (ts)
create ts.make ("op")
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 ("<br/>")
f.extend_html_text ("<legend>Are you sure you want to delete?</legend>")
-- TODO check if we need to check for has_permissions!!
if
a_node /= Void and then
a_node.id > 0
then
create ts.make ("op")
ts.set_default_value ("Delete")
fixme ("[
ts.set_default_value (translation ("Delete"))
]")
f.extend (ts)
fixme ("wsf_html: add support for HTML5 input attributes!!! ")
f.extend_html_text("<input type='submit' value='Cancel' formmethod='GET', formaction='/node/"+a_node.id.out+"'>" )
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 ("<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
create ts.make ("op")
ts.set_default_value ("Trash")
fixme ("[
ts.set_default_value (translation ("Trash"))
]")
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")
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'.
do
if attached node_api.node_type_webform_manager (a_content_type) as wf then
wf.populate_form (Current, a_form, a_node)
end
end
new_node (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form_data: WSF_FORM_DATA; a_node: detachable CMS_NODE): CMS_NODE
-- Node creation with form_data `a_form_data' for the given content type `a_content_type'
-- using optional `a_node' to get extra node data.
do
if attached node_api.node_type_webform_manager (a_content_type) as wf then
Result := wf.new_node (Current, a_form_data, a_node)
else
Result := a_content_type.new_node (a_node)
end
end
change_node (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form_data: WSF_FORM_DATA; a_node: CMS_NODE)
-- Update node `a_node' with form_data `a_form_data' for the given content type `a_content_type'.
do
if attached node_api.node_type_webform_manager (a_content_type) as wf then
wf.update_node (Current, a_form_data, a_node)
end
end
end

View File

@@ -0,0 +1,290 @@
note
description: "[
handler for CMS node in the CMS interface.
TODO: implement REST API.
]"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
NODE_HANDLER
inherit
CMS_NODE_HANDLER
WSF_URI_HANDLER
rename
execute as uri_execute,
new_mapping as new_uri_mapping
end
WSF_URI_TEMPLATE_HANDLER
rename
execute as uri_template_execute,
new_mapping as new_uri_template_mapping
select
new_uri_template_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get,
do_post,
do_put,
do_delete
end
REFACTORING_HELPER
create
make
feature -- execute
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute (req, res)
end
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute (req, res)
end
feature -- Query
node_id_path_parameter (req: WSF_REQUEST): INTEGER_64
-- Node id passed as path parameter for request `req'.
local
s: STRING
do
if attached {WSF_STRING} req.path_parameter ("id") as p_nid then
s := p_nid.value
if s.is_integer_64 then
Result := s.to_integer_64
end
end
end
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_node: detachable CMS_NODE
l_nid: INTEGER_64
edit_response: NODE_FORM_RESPONSE
view_response: NODE_VIEW_RESPONSE
do
if req.path_info.ends_with_general ("/edit") 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 ("/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)
if l_nid > 0 then
l_node := node_api.node (l_nid)
if l_node /= Void then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
view_response.execute
else
send_not_found (req, res)
end
else
-- redirect_to (req.absolute_script_url ("/node/"), res) -- New node.
-- send_bad_request (req, res)
create_new_node (req, res)
end
end
end
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
edit_response: NODE_FORM_RESPONSE
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)
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)
edit_response.execute
else
to_implement ("REST API")
send_not_implemented ("REST API not yet implemented", req, res)
end
end
do_put (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
do
to_implement ("REST API")
send_not_implemented ("REST API not yet implemented", req, res)
end
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
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 ("delete", l_node, current_user (req)) then
node_api.delete_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
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 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)
-- Handling error.
local
l_page: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.add_variable (req.absolute_script_url (req.path_info), "request")
if a_id /= Void and then a_id.is_integer then
-- resource not found
l_page.add_variable ("404", "code")
l_page.set_status_code (404)
else
-- bad request
l_page.add_variable ("400", "code")
l_page.set_status_code (400)
end
l_page.execute
end
feature {NONE} -- Node
create_new_node (req: WSF_REQUEST; res: WSF_RESPONSE)
local
edit_response: NODE_FORM_RESPONSE
do
if req.path_info.starts_with_general ("/node/") then
create edit_response.make (req, res, api, node_api)
edit_response.execute
elseif req.is_get_request_method then
redirect_to (req.absolute_script_url ("/node/"), res)
else
send_bad_request (req, res)
end
end
end

View File

@@ -0,0 +1,113 @@
note
description: "Generic CMS Response for a CMS NODE."
date: "$Date$"
revision: "$Revision$"
deferred class
NODE_RESPONSE
inherit
CMS_RESPONSE
rename
make as make_response
redefine
custom_prepare
end
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api)
do
node_api := a_node_api
make_response (req, res, a_api)
end
feature -- Access
node_api: CMS_NODE_API
-- Associated node API.
feature -- Generation
custom_prepare (page: CMS_HTML_PAGE)
do
if attached variables as l_variables then
across l_variables as c loop page.register_variable (c.item, c.key) end
end
end
feature -- Helpers
node_id_path_parameter (req: WSF_REQUEST): INTEGER_64
-- Node id passed as path parameter for request `req'.
local
s: STRING
do
if attached {WSF_STRING} req.path_parameter ("id") as p_nid then
s := p_nid.value
if s.is_integer_64 then
Result := s.to_integer_64
end
end
end
feature -- Helpers: cms link
user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do
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; 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)
end
node_html_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): like link
local
l_title: detachable READABLE_STRING_GENERAL
do
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
user_url (u: CMS_USER): like url
require
u_with_id: u.has_id
do
Result := url ("user/" + u.id.out, Void)
end
node_url (n: CMS_NODE): like url
require
n_with_id: n.has_id
do
Result := url (node_api.node_link (n).location, Void)
end
end

View File

@@ -0,0 +1,75 @@
note
description: "Summary description for {NODE_VIEW_RESPONSE}."
date: "$Date$"
revision: "$Revision$"
class
NODE_VIEW_RESPONSE
inherit
NODE_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api, a_node_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Access
node: detachable CMS_NODE
feature -- Element change
set_node (a_node: like node)
do
node := a_node
end
feature -- Execution
process
-- Computed response message.
local
b: STRING_8
nid: INTEGER_64
l_node: like node
do
l_node := node
if l_node = Void then
create b.make_empty
nid := node_id_path_parameter (request)
if nid > 0 then
l_node := node_api.node (nid)
end
end
if l_node /= Void then
if
attached node_api.node_type_for (l_node) as l_content_type and then
attached node_api.node_type_webform_manager (l_content_type) as l_manager
then
l_manager.append_html_output_to (l_node, Current)
end
else
set_main_content ("Missing node")
end
end
end

Some files were not shown because too many files have changed in this diff Show More