Compare commits

...

58 Commits

Author SHA1 Message Date
Jocelyn Fiat
53491274dc Merged CMS based on concurrent EWF (i.e ewf_v1) with blog branch. 2015-06-15 11:27:44 +02:00
21800e71d3 Removed obsolete calls.
Updated code to make it clear what is the resource, and what is the associated module resource path.
2015-06-11 23:03:34 +02:00
0fc1cb68ad Apply recent changes from EWF v1 2015-06-10 18:39:41 +02:00
0b8bee3404 favor EiffelThread for now, while waiting for SCOOP to be fully ready. 2015-06-10 11:06:58 +02:00
53a602d33c Removed CMS_SERVICE
Updated install.bat script
2015-06-10 09:43:04 +02:00
5578a9e622 Adapted ROC CMS to concurrent EWF.
Revisited the shared logger to reduced number of useless calls.
2015-06-09 19:42:37 +02:00
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
c871eae10e Renamed node_api and node_storage by blog_api and blog_storage in related CMS_BLOG_* classes. Mainly to avoid confusion with NODE_ classes.
Merged CMS_BLOG_CONFIG with CMS_BLOG_API.
In CMS_BLOG_API, prefer argument of type CMS_USER, rather than using directly user id.
Added a CMS_EDITOR_CONTENT_FORMAT for now, to be the format editable by the WYSIWYG editor.
Added CMS_MODULE.is_initialized: BOOLEAN to equip router, and module_api with expected preconditions.

Fixed typo, especially in log output.
Corrected a few routine names such as add_authors that should not be a function according to its name.
Converted various function returning html content, to procedure appending html content to an output string to minimize temporary string object creation.
Cosmetic: added spaces to make code easier to read, and indentation.
2015-05-27 19:00:32 +02: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
Dario Bösch
e35893fdb9 Integrated the CKEditor
#7 and #8: The class CMS_EDITOR generates javascript code that replaces a textarea with a wysiwyg editor. Only a few methods have to be implemented by the subclasses, for example by CMD_EDITOR_CKEDITOR. The class CMS_FORM_TEXTAREA extends WSF_FORM_TEXTAREA with features to include the javascript from CMS_EDITOR. The most complex usage is shown in CMS_NODE_TYPE_WEBFORM_MANAGER, where the textarea is only replaced if "full_html" is selected as the desired body format. This works dynamically on the browser side as soon as the user selects another format.
2015-05-23 21:11:39 +02: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
Dario Bösch
e8ff313c28 added some commments 2015-05-22 17:58:46 +02:00
Dario Bösch
0bd75e7c59 Added list of posts of a specific user
Similar to the blog handler the blog user handler routes /blogs/users/{id}. Pagination is implemented as well
2015-05-22 17:31:30 +02:00
Dario Bösch
9b169f70a7 page_number global in blog_handler 2015-05-22 15:43:29 +02:00
Dario Bösch
601b88ab36 blog handler optimized, blog user handler created 2015-05-22 15:34:32 +02:00
Dario Bösch
2b0e1a2b84 Improved structure of blog handler 2015-05-22 14:39:43 +02:00
Dario Bösch
db77c4024d created blog.scss, added link to blogs/{user}
Later we will list all posts of a user under the route blogs/{user}
2015-05-22 14:04:00 +02:00
Dario Bösch
261aeca300 Bugfix: Author was hidden
I had to add the authors to each post after getting the list. Made a helper feature add_authors.
2015-05-22 12:34:30 +02:00
Dario Bösch
027463a910 #3: added pagination links at bottom of the blog page 2015-05-22 12:02:33 +02:00
Dario Bösch
a4c50adefa #3: Calculate and show number of total pages 2015-05-22 11:48:20 +02:00
Dario Bösch
1f61126d22 Configuration File added
Created CMS_BLOG_CONFIG at moved the feature entries_per_page to this new class. The blog hander inherits from the config class
2015-05-22 11:30:54 +02:00
Dario Bösch
fb196735b6 #3: Routet page and limited entries
The blog module routes /blog/{page} and the blog handler limits the entries per page (given as a feature) and sets the offsets according to the given page number
2015-05-22 11:17:34 +02:00
jvelilla
306b39ab78 Updated code based on Jocelyn's suggestions. 2015-05-21 16:46:06 -03:00
Dario Bösch
802ad0626e Bugfix: wrong API in initialisation of blog module 2015-05-21 16:10:07 +02:00
Dario Bösch
470b1b2e05 Restructured Blog Module
All blog handlers and storage classes are detached from the nodes module. All files of the blog module are in the modules/blog folder
2015-05-21 16:04:58 +02:00
Dario Bösch
57c2a7bccd Removed the summary from the detail page 2015-05-21 14:12:01 +02:00
Dario Bösch
4dd980963a Moved filter of nodes of type blog to the node storage layer.
This is more efficient because the result set from the query will be smaller and it will be easier to implement the pagination
2015-05-21 14:06:08 +02:00
Dario Bösch
6a782e412d #2 Structure of list of posts (blog)Ordered the posts by creation date. For this, I added a field to the nodes storage and api. Show the creation date and author. Styled the post and added a more link to the detail page 2015-05-21 12:03:09 +02:00
Dario Bösch
0e0cd131a5 #1: Added the summary field to all nodes. It gets saved if we edit the node. On a node page the summary is shown first, then the main content. In the blog list the title and the summry is shown 2015-05-21 10:18:03 +02: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
Dario Bösch
202253e414 Added summary field in edit and add mode of a node 2015-05-19 17:28:37 +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
Dario Bösch
f72fcce440 Link fix 2015-05-18 15:40:47 +02:00
Dario Bösch
f48f09bfdf Bugfix (blog disappeared as create option) 2015-05-18 14:49:21 +02:00
Dario Bösch
35b186cec8 gitignore 2015-05-18 14:39:49 +02:00
Dario Bösch
c65265b025 added blog handler that lists all blog entries 2015-05-18 14:36:24 +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
108 changed files with 4208 additions and 827 deletions

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ EIFGENs
*.log* *.log*
*.rc *.rc
*.bak *.bak
*.sqlite
Thumbs.db

View File

@@ -19,10 +19,12 @@
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf" readonly="false"/> <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="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" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<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="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty-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="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="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" 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_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"/> <library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>

View File

@@ -17,12 +17,14 @@
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf" readonly="false"/> <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="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" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<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="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="cms_app_env" location=".\library\app_env\app_env.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="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="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="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="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" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf" readonly="false"/> <library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf" readonly="false"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/> <library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>

View File

@@ -7,10 +7,10 @@
<exclude>/CVS$</exclude> <exclude>/CVS$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional"> <option debug="true" 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"/> <debug name="dbglog" enabled="true"/>
</option> </option>
<setting name="concurrency" value="none"/> <setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/> <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_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
@@ -19,21 +19,39 @@
<library name="cms_demo_module" location="modules\demo\cms_demo_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="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="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"/>
-->
<library name="persistence_sqlite" location="..\..\library\persistence\sqlite\persistence_sqlite-safe.ecf" readonly="false"/> <library name="persistence_sqlite" location="..\..\library\persistence\sqlite\persistence_sqlite-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-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_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target> </target>
<target name="demo_any" extends="common"> <target name="demo_any" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/> <root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/> <library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/>
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/> <library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/>
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/> <library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\any\" recursive="true"/> <cluster name="launcher" location=".\launcher\any\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/> <cluster name="src" location=".\src\" recursive="true"/>
</target> </target>
<target name="demo_standalone" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<option debug="true">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone_none" extends="demo_standalone">
<setting name="concurrency" value="none"/>
</target>
<target name="demo_standalone_mt" extends="demo_standalone">
<setting name="concurrency" value="thread"/>
</target>
<target name="demo_standalone_scoop" extends="demo_standalone">
<setting name="concurrency" value="scoop"/>
</target>
<target name="demo_nino" extends="common"> <target name="demo_nino" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/> <root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/> <setting name="concurrency" value="none"/>
@@ -43,16 +61,18 @@
</target> </target>
<target name="demo_cgi" extends="common"> <target name="demo_cgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/> <root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/> <library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/> <cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/> <cluster name="src" location=".\src\" recursive="true"/>
</target> </target>
<target name="demo_libfcgi" extends="common"> <target name="demo_libfcgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/> <root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/> <library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/> <cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/> <cluster name="src" location=".\src\" recursive="true"/>
</target> </target>
<target name="demo" extends="demo_nino"> <target name="demo" extends="demo_standalone">
</target> </target>
</system> </system>

View File

@@ -8,10 +8,10 @@ note
revision: "$Revision: 36 $" revision: "$Revision: 36 $"
class class
APPLICATION_LAUNCHER APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit inherit
APPLICATION_LAUNCHER_I APPLICATION_LAUNCHER_I [G]
feature -- Custom feature -- Custom

View File

@@ -10,24 +10,28 @@ note
revision: "$Revision: 36 $" revision: "$Revision: 36 $"
deferred class deferred class
APPLICATION_LAUNCHER_I APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
inherit inherit
SHARED_EXECUTION_ENVIRONMENT SHARED_EXECUTION_ENVIRONMENT
feature -- Execution feature -- Execution
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
nature: like launcher_nature nature: like launcher_nature
do do
nature := launcher_nature nature := launcher_nature
if nature = Void or else nature = nature_nino then if nature = Void then
launch_nino (a_service, opts) launch_standalone (opts)
elseif nature = nature_standalone then
launch_standalone (opts)
elseif nature = nature_nino then
launch_nino (opts)
elseif nature = nature_cgi then elseif nature = nature_cgi then
launch_cgi (a_service, opts) launch_cgi (opts)
elseif nature = nature_libfcgi then elseif nature = nature_libfcgi then
launch_libfcgi (a_service, opts) launch_libfcgi (opts)
else else
-- bye bye -- bye bye
(create {EXCEPTIONS}).die (-1) (create {EXCEPTIONS}).die (-1)
@@ -43,7 +47,6 @@ feature {NONE} -- Access
--| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time. --| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time.
local local
p: PATH p: PATH
l_entry_name: READABLE_STRING_32
ext: detachable READABLE_STRING_32 ext: detachable READABLE_STRING_32
do do
create p.make_from_string (execution_environment.arguments.command_name) create p.make_from_string (execution_environment.arguments.command_name)
@@ -51,6 +54,9 @@ feature {NONE} -- Access
ext := l_entry.extension ext := l_entry.extension
end end
if ext /= Void then if ext /= Void then
if ext.same_string (nature_standalone) then
Result := nature_standalone
end
if ext.same_string (nature_nino) then if ext.same_string (nature_nino) then
Result := nature_nino Result := nature_nino
end end
@@ -61,39 +67,58 @@ feature {NONE} -- Access
Result := nature_libfcgi Result := nature_libfcgi
end end
end end
Result := default_nature
end
feature {NONE} -- standalone
nature_standalone: STRING = "standalone"
launch_standalone (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end end
feature {NONE} -- nino feature {NONE} -- nino
nature_nino: STRING = "nino" nature_nino: STRING = "nino"
launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch_nino (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
launcher: WSF_NINO_SERVICE_LAUNCHER launcher: WSF_NINO_SERVICE_LAUNCHER [G]
do do
create launcher.make_and_launch (a_service, opts) create launcher.make_and_launch (opts)
end end
feature {NONE} -- cgi feature {NONE} -- cgi
nature_cgi: STRING = "cgi" nature_cgi: STRING = "cgi"
launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch_cgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
launcher: WSF_CGI_SERVICE_LAUNCHER launcher: WSF_CGI_SERVICE_LAUNCHER [G]
do do
create launcher.make_and_launch (a_service, opts) create launcher.make_and_launch (opts)
end end
feature {NONE} -- libfcgi feature {NONE} -- libfcgi
nature_libfcgi: STRING = "libfcgi" nature_libfcgi: STRING = "libfcgi"
launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch_libfcgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER launcher: WSF_LIBFCGI_SERVICE_LAUNCHER [G]
do do
create launcher.make_and_launch (a_service, opts) create launcher.make_and_launch (opts)
end
feature -- Default
default_nature: STRING
do
Result := nature_standalone
end end

View File

@@ -8,10 +8,10 @@ note
revision: "$Revision: 36 $" revision: "$Revision: 36 $"
class class
APPLICATION_LAUNCHER APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit inherit
APPLICATION_LAUNCHER_I APPLICATION_LAUNCHER_I [G]
feature -- Custom feature -- Custom

View File

@@ -10,15 +10,15 @@ note
revision: "$Revision: 96596 $" revision: "$Revision: 96596 $"
deferred class deferred class
APPLICATION_LAUNCHER_I APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
feature -- Execution feature -- Execution
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
launcher: WSF_DEFAULT_SERVICE_LAUNCHER launcher: WSF_DEFAULT_SERVICE_LAUNCHER [G]
do do
create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts) create launcher.make_and_launch (opts)
end end
end end

View File

@@ -31,7 +31,13 @@ feature -- Conversion
do do
Precursor (a_node) Precursor (a_node)
if attached {CMS_BLOG} a_node as l_blog then if attached {CMS_BLOG} a_node as l_blog then
-- l_blog if attached l_blog.tags as l_tags then
across
l_tags as ic
loop
add_tag (ic.item)
end
end
end end
end end
@@ -42,7 +48,7 @@ feature -- Access
Result := {CMS_BLOG_NODE_TYPE}.name Result := {CMS_BLOG_NODE_TYPE}.name
end end
feature -- Access: content feature -- Access: node
summary: detachable READABLE_STRING_8 summary: detachable READABLE_STRING_8
-- A short summary of the node. -- A short summary of the node.
@@ -54,10 +60,12 @@ feature -- Access: content
-- Format associated with `content' and `summary'. -- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc -- For example: text, mediawiki, html, etc
feature -- Access: blog
tags: detachable ARRAYED_LIST [READABLE_STRING_32] tags: detachable ARRAYED_LIST [READABLE_STRING_32]
-- Optional tags -- Optional tags
feature -- Element change feature -- Element change: node
set_content (a_content: like content; a_summary: like summary; a_format: like format) set_content (a_content: like content; a_summary: like summary; a_format: like format)
do do
@@ -66,6 +74,8 @@ feature -- Element change
format := a_format format := a_format
end end
feature -- Element change: blog
add_tag (a_tag: READABLE_STRING_32) add_tag (a_tag: READABLE_STRING_32)
-- Set `parent' to `a_page' -- Set `parent' to `a_page'
require require

View File

@@ -0,0 +1,118 @@
note
description: "API to handle nodes of type blog. Extends the node API."
author: "Dario Bösch <daboesch@student.ethz.ch"
date: "$Date: 2015-05-21 14:46:00 +0100$"
revision: "$Revision: 96616 $"
class
CMS_BLOG_API
inherit
CMS_MODULE_API
rename
make as make_with_cms_api
redefine
initialize
end
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
make (a_api: CMS_API; a_node_api: CMS_NODE_API)
-- (from CMS_MODULE_API)
-- (export status {NONE})
do
node_api := a_node_api
make_with_cms_api (a_api)
end
initialize
-- <Precursor>
do
Precursor
-- Create the node storage for type blog
if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then
create {CMS_BLOG_STORAGE_SQL} blog_storage.make (l_storage_sql)
else
create {CMS_BLOG_STORAGE_NULL} blog_storage.make
end
-- initialize_node_types
end
feature {CMS_API_ACCESS, CMS_MODULE, CMS_API} -- Restricted access
node_api: CMS_NODE_API
feature {CMS_MODULE} -- Access nodes storage.
blog_storage: CMS_BLOG_STORAGE_I
feature -- Configuration of blog handlers
entries_per_page : NATURAL_32 = 2
-- The numbers of posts that are shown on one page. If there are more post a pagination is generated
--| For test reasons this is 2, so we don't have to create a lot of blog entries.
--| TODO: Set to bigger constant.
feature -- Access node
blogs_count: INTEGER_64
-- Number of nodes of type blog.
do
Result := blog_storage.blogs_count
end
blogs_count_from_user (a_user: CMS_USER): INTEGER_64
-- Number of nodes of type blog from user with `a_user_id'.
require
has_id: a_user.has_id
do
Result := blog_storage.blogs_count_from_user (a_user)
end
blogs_order_created_desc: LIST [CMS_BLOG]
-- List of nodes ordered by creation date (descending)
do
Result := nodes_to_blogs (blog_storage.blogs)
end
blogs_order_created_desc_limited (a_limit: NATURAL_32; a_offset: NATURAL_32): LIST [CMS_BLOG]
-- List of nodes ordered by creation date and limited by limit and offset
do
-- load all posts and add the authors to each post
Result := nodes_to_blogs (blog_storage.blogs_limited (a_limit, a_offset))
end
blogs_from_user_order_created_desc_limited (a_user: CMS_USER; a_limit: NATURAL_32; a_offset: NATURAL_32) : LIST [CMS_BLOG]
-- List of nodes ordered by creation date and limited by limit and offset
require
has_id: a_user.has_id
do
-- load all posts and add the authors to each post
Result := nodes_to_blogs (blog_storage.blogs_from_user_limited (a_user, a_limit, a_offset))
end
feature {NONE} -- Helpers
nodes_to_blogs (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_BLOG]
-- Convert list of nodes into a list of blog when possible.
do
create {ARRAYED_LIST [CMS_BLOG]} Result.make (a_nodes.count)
if attached node_api as l_node_api then
across
a_nodes as ic
loop
if attached {CMS_BLOG} l_node_api.full_node (ic.item) as l_blog then
Result.force (l_blog)
end
end
end
end
end

View File

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

View File

@@ -1,18 +1,21 @@
note note
description: "Summary description for {CMS_BLOG_MODULE}." description: "Displays all posts (pages with type blog). It's possible to list posts by user."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" author: "Dario Bösch <daboesch@student.ethz.ch>"
revision: "$Revision: 96616 $" date: "$Date: 2015-05-22 15:13:00 +0100 (lun., 18 mai 2015) $"
revision: "$Revision 96616$"
class class
CMS_BLOG_MODULE CMS_BLOG_MODULE
inherit inherit
CMS_MODULE CMS_MODULE
rename
module_api as blog_api
redefine redefine
register_hooks, register_hooks,
initialize, initialize,
is_installed, install,
install blog_api
end end
CMS_HOOK_MENU_SYSTEM_ALTER CMS_HOOK_MENU_SYSTEM_ALTER
@@ -40,6 +43,10 @@ feature {CMS_API} -- Module Initialization
Precursor (api) Precursor (api)
if attached {CMS_NODE_API} api.module_api ({NODE_MODULE}) as l_node_api then if attached {CMS_NODE_API} api.module_api ({NODE_MODULE}) as l_node_api then
create blog_api.make (api, l_node_api)
node_api := l_node_api
-- Depends on {NODE_MODULE}
create ct create ct
l_node_api.add_content_type (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)) l_node_api.add_content_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct))
@@ -53,12 +60,6 @@ feature {CMS_API} -- Module Initialization
feature {CMS_API} -- Module management 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) install (api: CMS_API)
local local
sql: STRING sql: STRING
@@ -78,17 +79,57 @@ CREATE TABLE "blog_post_nodes"(
api.logger.put_error ("Could not initialize database for blog module", generating_type) api.logger.put_error ("Could not initialize database for blog module", generating_type)
end end
end end
api.storage.set_custom_value ("is_initialized", "module-" + name, "yes") Precursor (api)
end end
end end
feature {CMS_API} -- Access: API
blog_api: detachable CMS_BLOG_API
-- <Precursor>
node_api: detachable CMS_NODE_API
feature -- Access: router feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Node router. -- <Precursor>
do do
create Result.make (1) if attached blog_api as l_blog_api then
Result.handle_with_request_methods ("/blogs/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_blogs (?,?, a_api)), Result.methods_get) configure_web (a_api, l_blog_api, a_router)
else
-- Issue with api/dependencies,
-- thus Current module should not be used!
-- thus no url mapping
end
end
configure_web (a_api: CMS_API; a_blog_api: CMS_BLOG_API; a_router: WSF_ROUTER)
-- Configure router mapping for web interface.
local
l_blog_handler: BLOG_HANDLER
l_blog_user_handler: BLOG_USER_HANDLER
l_uri_mapping: WSF_URI_MAPPING
do
-- TODO: for now, focused only on web interface, add REST api later. [2015-May-18]
create l_blog_handler.make (a_api, a_blog_api)
create l_blog_user_handler.make (a_api, a_blog_api)
-- Let the class BLOG_HANDLER handle the requests on "/blogs"
create l_uri_mapping.make_trailing_slash_ignored ("/blogs", l_blog_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
-- We can add a page number after /blogs/ to get older posts
a_router.handle_with_request_methods ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
-- If a user id is given route with blog user handler
--| FIXME: maybe /user/{user}/blogs/ would be better.
a_router.handle_with_request_methods ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
-- If a user id is given we also want to allow different pages
--| FIXME: what about /user/{user}/blogs/?page={page} ?
a_router.handle_with_request_methods ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
end end
feature -- Hooks feature -- Hooks
@@ -102,19 +143,8 @@ feature -- Hooks
local local
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
do do
create lnk.make ("Blogs", "/blogs/") -- Add the link to the blog to the main menu
create lnk.make ("Blogs", "blogs/")
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
end 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 end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -80,12 +80,11 @@ CREATE TABLE "tb_demo"(
feature -- Access: router feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Node router. -- <Precursor>
do do
create Result.make (2) map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api))
map_uri_template_agent (Result, "/demo/", agent handle_demo (?,?,a_api)) map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
map_uri_template_agent (Result, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
end end
feature -- Hooks feature -- Hooks
@@ -111,9 +110,9 @@ feature -- Hooks
if a_block_id.is_case_insensitive_equal_general ("demo-info") then if a_block_id.is_case_insensitive_equal_general ("demo-info") then
if a_response.request.request_uri.starts_with ("/demo/") then if a_response.request.request_uri.starts_with ("/demo/") then
create m.make_with_title (a_block_id, "Demo", 2) 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) m.extend (lnk)
create lnk.make ("/demo/123", a_response.url ("/demo/123", Void)) create lnk.make ("demo: 123", "demo/123")
m.extend (lnk) m.extend (lnk)
create mb.make (m) create mb.make (m)
a_response.add_block (mb, "sidebar_second") a_response.add_block (mb, "sidebar_second")
@@ -126,7 +125,7 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
-- perms: detachable ARRAYED_LIST [READABLE_STRING_8] -- perms: detachable ARRAYED_LIST [READABLE_STRING_8]
do do
create lnk.make ("Demo", "/demo/") create lnk.make ("Demo", "demo/")
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
end end
@@ -138,8 +137,8 @@ feature -- Handler
r: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE r: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
do do
create r.make (req, res, a_api) create r.make (req, res, a_api)
r.set_main_content ("NODE module does not yet implement %"" + req.path_info + "%" ...") r.set_main_content ("DEMO module does not yet implement %"" + req.path_info + "%" ...")
r.add_error_message ("NODE Module: not yet implemented") r.add_error_message ("DEMO Module: not yet implemented")
r.execute r.execute
end end

View File

@@ -23,7 +23,8 @@
"server": "localhost" "server": "localhost"
}, },
"logger": { "logger": {
"level":"debug", "level":"error",
"type":"stderr",
"backup_count":"4" "backup_count":"4"
}, },
"server": { "server": {

View File

@@ -1,35 +1,5 @@
BEGIN; BEGIN;
CREATE TABLE "users"(
"uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0),
"name" VARCHAR(100) NOT NULL,
"password" VARCHAR(100) NOT NULL,
"salt" VARCHAR(100) NOT NULL,
"email" VARCHAR(250) NOT NULL,
"status" INTEGER,
"created" DATETIME NOT NULL,
"signed" DATETIME,
CONSTRAINT "name"
UNIQUE("name")
);
CREATE TABLE "roles"(
"rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0),
"name" VARCHAR(100) NOT NULL,
CONSTRAINT "name"
UNIQUE("name")
);
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)
);
CREATE TABLE "logs"( CREATE TABLE "logs"(
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0),
@@ -45,7 +15,14 @@ CREATE TABLE "logs"(
CREATE TABLE "custom_values"( CREATE TABLE "custom_values"(
"type" VARCHAR(255) NOT NULL, "type" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL, "name" VARCHAR(255) NOT NULL,
"value" 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; COMMIT;

View File

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

View File

@@ -0,0 +1,34 @@
BEGIN;
CREATE TABLE "users"(
"uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("uid">=0),
"name" VARCHAR(100) NOT NULL,
"password" VARCHAR(100) NOT NULL,
"salt" VARCHAR(100) NOT NULL,
"email" VARCHAR(250) NOT NULL,
"status" INTEGER,
"created" DATETIME NOT NULL,
"signed" DATETIME,
CONSTRAINT "name"
UNIQUE("name")
);
CREATE TABLE "roles"(
"rid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("rid">=0),
"name" VARCHAR(100) NOT NULL,
CONSTRAINT "name"
UNIQUE("name")
);
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,25 @@
ul.cms_blog_nodes {
padding: 0;
margin: 0;
}
ul.cms_blog_nodes li.cms_type_blog {
list-style: none;
display: block;
margin-top: 20px;
padding-bottom: 20px;
border-bottom: 1px dotted black;
}
ul.cms_blog_nodes li.cms_type_blog .blog_title a {
color: black;
font-size: 18px;
text-decoration: none;
display: block;
margin: 6px 0;
}
ul.cms_blog_nodes li.cms_type_blog .blog_title a:hover {
color: #999;
}
ul.cms_blog_nodes li.cms_type_blog .blog_list_summary a {
margin-top: 20px;
display: block;
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

View File

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

View File

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

View File

@@ -4,10 +4,14 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- EWF CMS --> <!-- EWF CMS -->
<link rel="stylesheet" href="{$site_url/}/theme/css/style.css"> <link rel="stylesheet" href="{$site_url/}theme/css/style.css">
<link rel="stylesheet" href="{$site_url/}/theme/css/node.css"> <link rel="stylesheet" href="{$site_url/}theme/css/node.css">
<script src="{$site_url/}/theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}/theme/js/roc_auth.js"></script> <!-- CMS Blog Module -->
<link rel="stylesheet" href="{$site_url/}theme/css/blog.css">
<script src="{$site_url/}theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}theme/js/roc_auth.js"></script>
<!-- bootstrap framework --> <!-- bootstrap framework -->
<!-- Latest compiled and minified CSS --> <!-- Latest compiled and minified CSS -->

View File

@@ -16,11 +16,6 @@ inherit
initialize initialize
end end
WSF_SERVICE
redefine
execute
end
REFACTORING_HELPER REFACTORING_HELPER
SHARED_EXECUTION_ENVIRONMENT SHARED_EXECUTION_ENVIRONMENT
@@ -40,37 +35,26 @@ feature {NONE} -- Initialization
initialize initialize
-- Initialize current service. -- Initialize current service.
local
env: CMS_ENVIRONMENT
do do
-- Launcher
Precursor Precursor
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} service_options.make_from_file ("demo.ini") create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} service_options.make_from_file ("demo.ini")
create env.make_default
-- CMS initialize_logger (env)
initialize_cms
end
feature -- Service
cms_service: CMS_SERVICE
-- cms service.
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
cms_service.execute (req, res)
end end
feature {NONE} -- Launch operation feature {NONE} -- Launch operation
launcher: APPLICATION_LAUNCHER launcher: APPLICATION_LAUNCHER [EWF_ROC_SERVER_EXECUTION]
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local local
l_retry: BOOLEAN l_retry: BOOLEAN
l_message: STRING l_message: STRING
do do
if not l_retry then if not l_retry then
write_debug_log (generator + ".launch") launcher.launch (opts)
launcher.launch (a_service, opts)
else else
-- error hanling. -- error hanling.
create l_message.make (1024) create l_message.make (1024)
@@ -89,77 +73,12 @@ feature {NONE} -- Launch operation
l_message.append ("The application crash without available information") l_message.append ("The application crash without available information")
l_message.append ("%N%N") l_message.append ("%N%N")
end end
-- notify shutdown -- send email shutdown
write_debug_log (generator + ".launch shutdown")
end end
rescue rescue
l_retry := True l_retry := True
retry retry
end end
feature -- CMS Initialization
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 cms_env.make_with_directory_name (l_dir)
else
create cms_env.make_default
end
initialize_logger (cms_env)
-- 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
setup_modules (a_setup: CMS_SETUP)
-- Setup additional modules.
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
-- 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
end end

View File

@@ -0,0 +1,81 @@
note
description: "Summary description for {EWF_ROC_SERVER_EXECUTION}."
date: "$Date$"
revision: "$Revision$"
class
EWF_ROC_SERVER_EXECUTION
inherit
CMS_EXECUTION
redefine
initialize
end
REFACTORING_HELPER
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
end
initial_cms_setup: CMS_DEFAULT_SETUP
-- CMS setup.
local
l_env: CMS_ENVIRONMENT
do
if attached execution_environment.arguments.separate_character_option_value ('d') as l_dir then
create l_env.make_with_directory_name (l_dir)
else
create l_env.make_default
end
create Result.make (l_env)
end
feature -- CMS setup
setup_storage (a_setup: CMS_SETUP)
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
setup_modules (a_setup: CMS_SETUP)
-- Setup additional modules.
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
end

View File

@@ -30,6 +30,7 @@ feature -- Initialization
backup_count := 4 backup_count := 4
level := Log_debug level := Log_debug
location := Void location := Void
type := {STRING_32} "null"
ensure then ensure then
backup_count_set: backup_count = 4 backup_count_set: backup_count = 4
level_set: level = Log_debug level_set: level = Log_debug
@@ -48,6 +49,9 @@ feature -- Access
level: INTEGER level: INTEGER
-- Logger level. -- Logger level.
type: IMMUTABLE_STRING_32
-- Type of logging.
feature -- Element Change feature -- Element Change
set_location (a_location: detachable PATH) set_location (a_location: detachable PATH)
@@ -65,6 +69,15 @@ feature -- Element Change
set_location (create {PATH}.make_from_string (a_location)) set_location (create {PATH}.make_from_string (a_location))
end end
set_type_with_string (a_type: detachable READABLE_STRING_GENERAL)
do
if a_type /= Void and then not a_type.is_whitespace then
create type.make_from_string_general (a_type)
else
create type.make_from_string_general ("null")
end
end
set_backup_count (a_backup: NATURAL) set_backup_count (a_backup: NATURAL)
-- Set backup_count to `a_backup'. -- Set backup_count to `a_backup'.
do do

View File

@@ -32,7 +32,7 @@ feature {NONE} -- Initialization
create log.make create log.make
end end
make_with_environment (app: APPLICATION_ENVIRONMENT) make_with_environment (app: separate APPLICATION_ENVIRONMENT)
-- Initialize a logger object with an application environment `app'. -- Initialize a logger object with an application environment `app'.
do do
make make
@@ -49,7 +49,7 @@ feature {NONE} -- Initialization
feature -- Change feature -- Change
apply_environment (app: APPLICATION_ENVIRONMENT) apply_environment (app: separate APPLICATION_ENVIRONMENT)
do do
initialize_logger (app, log) initialize_logger (app, log)
end end
@@ -58,75 +58,107 @@ feature {NONE} -- Internal
log: LOGGING_FACILITY log: LOGGING_FACILITY
feature -- Settings
level: INTEGER
feature -- Logging feature -- Logging
put_debug (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at debug level.
do
if level >= log_debug then
log.write_debug (create {STRING}.make_from_separate (a_message))
end
end
put_information (a_message: separate READABLE_STRING_8) put_information (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at information level. -- Put message `a_message' to the log at information level.
do do
if level >= log_information then
log.write_information (create {STRING}.make_from_separate (a_message)) log.write_information (create {STRING}.make_from_separate (a_message))
end 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 end
put_warning (a_message: separate READABLE_STRING_8) put_warning (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at warning level. -- Put message `a_message' to the log at warning level.
do do
if level >= log_warning then
log.write_warning (create {STRING}.make_from_separate (a_message)) log.write_warning (create {STRING}.make_from_separate (a_message))
end end
end
put_error (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at error level.
do
if level >= log_error then
log.write_error (create {STRING}.make_from_separate (a_message))
end
end
put_critical (a_message: separate READABLE_STRING_8) put_critical (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at critical level. -- Put message `a_message' to the log at critical level.
do do
if level >= log_critical then
log.write_critical (create {STRING}.make_from_separate (a_message)) log.write_critical (create {STRING}.make_from_separate (a_message))
end end
end
put_alert (a_message: separate READABLE_STRING_8) put_alert (a_message: separate READABLE_STRING_8)
-- Put message `a_message' to the log at alert level. -- Put message `a_message' to the log at alert level.
do do
if level >= log_alert then
log.write_alert (create {STRING}.make_from_separate (a_message)) log.write_alert (create {STRING}.make_from_separate (a_message))
end 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 end
feature {NONE} -- Implementation feature {NONE} -- Implementation
initialize_logger (app: APPLICATION_ENVIRONMENT; a_log: like log) initialize_logger (app: separate APPLICATION_ENVIRONMENT; a_log: like log)
local local
l_log_writer_file: LOG_ROLLING_WRITER_FILE l_log_writer_file: LOG_ROLLING_WRITER_FILE
l_log_writer: LOG_WRITER l_log_writer: detachable LOG_WRITER
l_logs_path: detachable PATH l_logs_path: detachable PATH
l_logger_config: LOGGER_CONFIGURATION l_logger_config: LOGGER_CONFIGURATION
ut: FILE_UTILITIES ut: FILE_UTILITIES
p: PATH
l_name: IMMUTABLE_STRING_32
do do
l_logger_config := new_logger_level_configuration (app.application_config_path) create l_name.make_from_separate (app.name)
create p.make_from_separate (app.application_config_path)
-- l_name := app.name
-- p := app.application_config_path
l_logger_config := new_logger_level_configuration (p)
if l_logger_config.type.is_case_insensitive_equal_general ("file") then
l_logs_path := l_logger_config.location l_logs_path := l_logger_config.location
if l_logs_path = Void then if l_logs_path = Void then
l_logs_path := app.logs_path create l_logs_path.make_from_separate (app.logs_path)
end end
if ut.directory_path_exists (l_logs_path) then if ut.directory_path_exists (l_logs_path) then
create l_log_writer_file.make_at_location (l_logs_path.extended (app.name).appended_with_extension ("log")) create l_log_writer_file.make_at_location (l_logs_path.extended (l_name).appended_with_extension ("log"))
l_log_writer_file.set_max_file_size ({NATURAL_64} 1024 * 1204) l_log_writer_file.set_max_file_size ({NATURAL_64} 1024 * 1204)
l_log_writer_file.set_max_backup_count (l_logger_config.backup_count) l_log_writer_file.set_max_backup_count (l_logger_config.backup_count)
l_log_writer := l_log_writer_file l_log_writer := l_log_writer_file
else else
-- Should we create the directory anyway ? -- Should we create the directory anyway ?
create {LOG_WRITER_NULL} l_log_writer
end end
set_logger_level (l_log_writer, l_logger_config.level) elseif l_logger_config.type.is_case_insensitive_equal_general ("stderr") then
create {LOG_WRITER_STDERR} l_log_writer
end
if l_log_writer = Void then
create {LOG_WRITER_NULL} l_log_writer
set_logger_level (l_log_writer, log_notice)
else
set_logger_level (l_log_writer, 0) -- None
end
a_log.register_log_writer (l_log_writer) a_log.register_log_writer (l_log_writer)
end end
set_logger_level (a_log_writer: LOG_WRITER; a_priority: INTEGER) set_logger_level (a_log_writer: LOG_WRITER; a_priority: INTEGER)
-- Setup the logger level based on `a_priority' -- Setup the logger level based on `a_priority'
do do
level := a_priority
if a_priority = log_debug then if a_priority = log_debug then
a_log_writer.enable_debug_log_level a_log_writer.enable_debug_log_level
elseif a_priority = Log_emergency then elseif a_priority = Log_emergency then
@@ -167,6 +199,9 @@ feature {NONE} -- Implementation
attached l_parser.parsed_json_object as jv and then attached l_parser.parsed_json_object as jv and then
attached {JSON_OBJECT} jv.item ("logger") as l_logger attached {JSON_OBJECT} jv.item ("logger") as l_logger
then then
if attached {JSON_STRING} l_logger.item ("type") as l_type then
Result.set_type_with_string (l_type.item)
end
if attached {JSON_STRING} l_logger.item ("location") as l_location then if attached {JSON_STRING} l_logger.item ("location") as l_location then
Result.set_location_with_string (l_location.item) Result.set_location_with_string (l_location.item)
end end

View File

@@ -35,72 +35,113 @@ feature -- Logging
write_debug_log (m: READABLE_STRING_8) write_debug_log (m: READABLE_STRING_8)
do do
write_debug_log_to (m, logger) -- write_debug_log_to (m, logger)
end end
write_information_log (m: READABLE_STRING_8) write_information_log (m: READABLE_STRING_8)
do do
write_information_log_to (m, logger) -- write_information_log_to (m, logger)
end end
write_warning_log (m: READABLE_STRING_8) write_warning_log (m: READABLE_STRING_8)
do do
write_warning_log_to (m, logger) -- write_warning_log_to (m, logger)
end end
write_error_log (m: READABLE_STRING_8) write_error_log (m: READABLE_STRING_8)
do do
write_error_log_to (m, logger) -- write_error_log_to (m, logger)
end end
write_critical_log (m: READABLE_STRING_8) write_critical_log (m: READABLE_STRING_8)
do do
write_critical_log_to (m, logger) -- write_critical_log_to (m, logger)
end end
write_alert_log (m: READABLE_STRING_8) write_alert_log (m: READABLE_STRING_8)
do do
write_alert_log_to (m, logger) -- write_alert_log_to (m, logger)
end end
feature {NONE} -- Logger: separate implementation feature {NONE} -- Logger: separate implementation
write_debug_log_to (m: READABLE_STRING_8; a_log: like logger) write_debug_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_debug (m) a_log.put_debug (m)
end end
rescue
retried := True
retry
end
write_information_log_to (m: READABLE_STRING_8; a_log: like logger) write_information_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_information (m) a_log.put_information (m)
end end
rescue
retried := True
retry
end
write_warning_log_to (m: READABLE_STRING_8; a_log: like logger) write_warning_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_warning (m) a_log.put_warning (m)
end end
rescue
retried := True
retry
end
write_error_log_to (m: READABLE_STRING_8; a_log: like logger) write_error_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_error (m) a_log.put_error (m)
end end
rescue
retried := True
retry
end
write_critical_log_to (m: READABLE_STRING_8; a_log: like logger) write_critical_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_critical (m) a_log.put_critical (m)
end end
rescue
retried := True
retry
end
write_alert_log_to (m: READABLE_STRING_8; a_log: like logger) write_alert_log_to (m: READABLE_STRING_8; a_log: like logger)
local
retried: BOOLEAN
do do
if not retried then
a_log.put_alert (m) a_log.put_alert (m)
end end
rescue
retried := True
retry
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
initialize_logger (app: APPLICATION_ENVIRONMENT) initialize_logger (app: APPLICATION_ENVIRONMENT)
local local
l_logger: LOGGER l_logger: separate LOGGER
do do
create l_logger.make_with_environment (app) create l_logger.make_with_environment (app)
set_logger_to (l_logger, logger_cell) set_logger_to (l_logger, logger_cell)

View File

@@ -25,15 +25,13 @@ create
feature {NONE} -- Initialization 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'. -- 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 do
if a_title /= Void then
title := a_title
else
title := a_location
end
location := a_location location := a_location
set_title (a_title)
end end
feature -- Access feature -- Access
@@ -75,6 +73,16 @@ feature -- Status report
feature -- Element change 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) add_link (lnk: CMS_LINK)
-- <Precursor> -- <Precursor>
local local

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {CMS_STORAGE_STORE_SQL}." description: "Storage based on Eiffel Store component."
author: ""
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $" revision: "$Revision: 96616 $"
@@ -8,9 +7,7 @@ deferred class
CMS_STORAGE_STORE_SQL CMS_STORAGE_STORE_SQL
inherit inherit
CMS_STORAGE CMS_STORAGE_SQL
CMS_STORAGE_SQL_I
feature {NONE} -- Initialization feature {NONE} -- Initialization
@@ -25,7 +22,6 @@ feature {NONE} -- Initialization
create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection)
create error_handler.make create error_handler.make
-- error_handler.add_synchronization (db_handler.database_error_handler)
end end
feature -- Status report feature -- Status report
@@ -36,6 +32,15 @@ feature -- Status report
Result := connection.is_connected Result := connection.is_connected
end end
feature -- Basic operation
close
-- <Precursor>
-- Disconnect from SQL database.
do
connection.disconnect
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
db_handler: DATABASE_HANDLER db_handler: DATABASE_HANDLER

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

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
Objects that ... Interface responsible to instantiate CMS_STORAGE_MYSQL object.
]" ]"
author: "$Author: jfiat $" author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $" date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
@@ -10,7 +10,9 @@ class
CMS_STORAGE_MYSQL_BUILDER CMS_STORAGE_MYSQL_BUILDER
inherit inherit
CMS_STORAGE_SQL_BUILDER CMS_STORAGE_STORE_SQL_BUILDER
GLOBAL_SETTINGS
create create
make make
@@ -32,6 +34,12 @@ feature -- Factory
create {DATABASE_CONNECTION_MYSQL} conn.login_with_connection_string (l_database_config.connection_string) create {DATABASE_CONNECTION_MYSQL} conn.login_with_connection_string (l_database_config.connection_string)
if conn.is_connected then if conn.is_connected then
create Result.make (conn) 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 end
end end

View File

@@ -1,5 +1,5 @@
note 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) $" date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $" revision: "$Revision: 96596 $"

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
Objects that ... Interface responsible to instantiate CMS_STORAGE_SQLITE object.
]" ]"
author: "$Author: jfiat $" author: "$Author: jfiat $"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
@@ -10,7 +10,9 @@ class
CMS_STORAGE_SQLITE_BUILDER CMS_STORAGE_SQLITE_BUILDER
inherit inherit
CMS_STORAGE_SQL_BUILDER CMS_STORAGE_STORE_SQL_BUILDER
GLOBAL_SETTINGS
create create
make make
@@ -27,15 +29,28 @@ feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_SQLITE storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_SQLITE
local local
s: STRING s: STRING
conn: detachable DATABASE_CONNECTION
do do
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.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=" s := "Driver=SQLite3 ODBC Driver;Database="
if attached l_database_config.database_name as db_name then if attached l_database_config.database_name as db_name then
s.append (db_name) s.append (db_name)
end end
s.append (";LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;") s.append (";LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;")
create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (s)) if attached reuseable_connection.item as d then
--create Result.make (create {DATABASE_CONNECTION_ODBC}.login_with_connection_string (l_database_config.connection_string)) if s.same_string (d.name) then
conn := d.connection
end
end
if conn = Void or else not conn.is_connected then
create {DATABASE_CONNECTION_ODBC} conn.login_with_connection_string (s)
reuseable_connection.replace ([s, conn])
end
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 Result.is_available then
if not Result.is_initialized then if not Result.is_initialized then
initialize (a_setup, Result) initialize (a_setup, Result)
@@ -43,34 +58,11 @@ feature -- Factory
end end
end end
end end
end
initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_STORE_SQL) reuseable_connection: CELL [detachable TUPLE [name: STRING; connection: DATABASE_CONNECTION]]
local once
u: CMS_USER create Result.put (Void)
r: CMS_USER_ROLE
do
-- Schema
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql"))
-- Data
-- Users
create u.make ("admin")
u.set_password ("istrator#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
-- Roles
create r.make ("anonymous")
a_storage.save_user_role (r)
create r.make ("authenticated")
r.add_permission ("create page")
r.add_permission ("edit page")
a_storage.save_user_role (r)
-- Test custom value
a_storage.set_custom_value ("abc", "123", "test")
a_storage.set_custom_value ("abc", "OK", "test")
end end
end end

View File

@@ -37,12 +37,11 @@ feature {NONE} -- Initialization
feature -- Access: router feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Node router. -- <Precursor>
do do
create Result.make (2) configure_api_login (a_api, a_router)
configure_api_login (a_api, Result) configure_api_logoff (a_api, a_router)
configure_api_logoff (a_api, Result)
end end
feature -- Access: filter feature -- Access: filter
@@ -110,9 +109,9 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
do do
if attached a_response.current_user (a_response.request) as u then 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 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 end
-- if not a_menu_system.primary_menu.has (lnk) then -- if not a_menu_system.primary_menu.has (lnk) then
lnk.set_weight (99) lnk.set_weight (99)

View File

@@ -25,8 +25,10 @@ feature -- Basic operations
do do
api.logger.put_debug (generator + ".execute ", Void) api.logger.put_debug (generator + ".execute ", Void)
create l_auth.make (req.http_authorization) create l_auth.make (req.http_authorization)
debug
if attached req.raw_header_data as l_raw_data then 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 " + (create {UTF_CONVERTER}).escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
end
end end
-- A valid user -- A valid user
if if

View File

@@ -16,20 +16,21 @@ inherit
REFACTORING_HELPER REFACTORING_HELPER
create create {NODE_MODULE}
make make_with_storage
feature {NONE} -- Implementation 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 initialize
-- <Precursor> -- <Precursor>
do do
Precursor Precursor
if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then
create {CMS_NODE_STORAGE_SQL} node_storage.make (l_storage_sql)
else
create {CMS_NODE_STORAGE_NULL} node_storage.make
end
initialize_node_types initialize_node_types
end end
@@ -170,30 +171,38 @@ feature -- URL
-- or URI of path for selection of new content possibilities if ct is Void. -- or URI of path for selection of new content possibilities if ct is Void.
do do
if ct /= Void then if ct /= Void then
Result := "/node/add/" + ct.name Result := "node/add/" + ct.name
else else
Result := "/node/" Result := "node/"
end end
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 node_path (a_node: CMS_NODE): STRING
-- URI path for node `a_node'. -- URI path for node `a_node'.
-- using the /node/{nid} url. -- using the /node/{nid} url.
require require
a_node.has_id a_node.has_id
do do
Result := "/node/" + a_node.id.out Result := "node/" + a_node.id.out
end end
nodes_path: STRING nodes_path: STRING
-- URI path for list of nodes. -- URI path for list of nodes.
do do
Result := "/nodes" Result := "nodes"
end end
feature -- Access: Node feature -- Access: Node
nodes_count: INTEGER_64 nodes_count: NATURAL_64
do do
Result := node_storage.nodes_count Result := node_storage.nodes_count
end end
@@ -204,10 +213,17 @@ feature -- Access: Node
Result := node_storage.nodes Result := node_storage.nodes
end end
recent_nodes (a_offset, a_rows: INTEGER): LIST [CMS_NODE] 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'. -- List of the `a_rows' most recent nodes starting from `a_offset'.
do do
Result := node_storage.recent_nodes (a_offset, a_rows) Result := node_storage.recent_nodes (params.offset.to_integer_32, params.size.to_integer_32)
end end
node (a_id: INTEGER_64): detachable CMS_NODE node (a_id: INTEGER_64): detachable CMS_NODE
@@ -234,6 +250,11 @@ feature -- Access: Node
Result := a_node Result := a_node
end 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. -- Update partial user if needed.
if if
Result /= Void and then Result /= Void and then
@@ -249,6 +270,30 @@ feature -- Access: Node
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 feature -- Change: Node
save_node (a_node: CMS_NODE) save_node (a_node: CMS_NODE)
@@ -279,32 +324,31 @@ feature -- Change: Node
node_storage.update_node (a_node) node_storage.update_node (a_node)
end end
-- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) trash_node (a_node: CMS_NODE)
-- -- Update node title, with user identified by `a_id', with node id `a_node_id' and a new title `a_title'. -- Trash node `a_node'.
-- do --! remove the node from the storage.
-- debug ("refactor_fixme") do
-- fixme ("Check preconditions") node_storage.trash_node (a_node)
-- end end
-- node_storage.update_node_title (a_user_id, a_node_id, a_title)
-- end
-- update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- -- Update node summary, with user identified by `a_user_id', with node id `a_node_id' and a new summary `a_summary'.
-- do
-- debug ("refactor_fixme")
-- fixme ("Check preconditions")
-- end
-- node_storage.update_node_summary (a_user_id, a_node_id, a_summary)
-- end
-- update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) restore_node (a_node: CMS_NODE)
-- -- Update node content, with user identified by `a_user_id', with node id `a_node_id' and a new content `a_content'. -- Restore node `a_node'.
-- do -- From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
-- debug ("refactor_fixme") do
-- fixme ("Check preconditions") node_storage.restore_node (a_node)
-- end end
-- node_storage.update_node_content (a_user_id, a_node_id, a_content)
-- 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 end

View File

@@ -13,10 +13,6 @@ inherit
REFACTORING_HELPER REFACTORING_HELPER
--create
-- make,
-- make_empty
feature{NONE} -- Initialization feature{NONE} -- Initialization
make_empty make_empty
@@ -35,10 +31,7 @@ feature{NONE} -- Initialization
set_creation_date (l_time) set_creation_date (l_time)
set_modification_date (l_time) set_modification_date (l_time)
set_publication_date (l_time) set_publication_date (l_time)
mark_not_published
debug ("refactor_fixme")
fixme ("Remove default harcoded format")
end
ensure ensure
title_set: title = a_title title_set: title = a_title
end end
@@ -60,6 +53,8 @@ feature -- Conversion
a_node.summary, a_node.summary,
a_node.format a_node.format
) )
set_status (a_node.status)
set_link (a_node.link)
end end
feature -- Access feature -- Access
@@ -78,6 +73,12 @@ feature -- Access
deferred deferred
end 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 feature -- Access
title: READABLE_STRING_32 title: READABLE_STRING_32
@@ -142,6 +143,11 @@ feature -- status report
Result := a_content_type.is_case_insensitive_equal (content_type) Result := a_content_type.is_case_insensitive_equal (content_type)
end end
feature -- Access: menu
link: detachable CMS_LOCAL_LINK
-- Associated menu link.
feature -- Element change feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format) set_content (a_content: like content; a_summary: like summary; a_format: like format)
@@ -158,6 +164,9 @@ feature -- Element change
-- Assign `title' with `a_title'. -- Assign `title' with `a_title'.
do do
title := a_title title := a_title
if attached link as lnk then
lnk.set_title (a_title)
end
ensure ensure
title_assigned: title = a_title title_assigned: title = a_title
end end
@@ -211,6 +220,48 @@ feature -- Element change
auther_set: author = u auther_set: author = u
end 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 note
copyright: "2011-2015, 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

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

View File

@@ -9,16 +9,22 @@ deferred class
inherit inherit
CMS_NODE_TYPE_WEBFORM_MANAGER_I [G] CMS_NODE_TYPE_WEBFORM_MANAGER_I [G]
SHARED_WSF_PERCENT_ENCODER
feature -- Forms ... feature -- Forms ...
populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local local
ti: WSF_FORM_TEXT_INPUT ti: WSF_FORM_TEXT_INPUT
fset: WSF_FORM_FIELD_SET fset: WSF_FORM_FIELD_SET
ta: WSF_FORM_TEXTAREA ta, sum: CMS_FORM_TEXTAREA
tselect: WSF_FORM_SELECT tselect: WSF_FORM_SELECT
opt: WSF_FORM_SELECT_OPTION opt: WSF_FORM_SELECT_OPTION
cms_format: CMS_EDITOR_CONTENT_FORMAT
l_uri: detachable READABLE_STRING_8
do do
create cms_format
create ti.make ("title") create ti.make ("title")
ti.set_label ("Title") ti.set_label ("Title")
ti.set_size (70) ti.set_size (70)
@@ -30,25 +36,47 @@ feature -- Forms ...
f.extend_html_text ("<br/>") f.extend_html_text ("<br/>")
create ta.make ("body") -- Select field has to be initialized before textareas are replaced, because they depend on the selection of the field
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") create tselect.make ("format")
tselect.set_label ("Body's format") tselect.set_label ("Body's format")
tselect.set_is_required (True) tselect.set_is_required (True)
-- Main Content
create ta.make ("body")
ta.set_rows (10)
ta.set_cols (70)
ta.show_as_editor_if_selected (tselect, cms_format.name)
if a_node /= Void then
ta.set_text_value (a_node.content)
end
ta.set_label ("Content")
ta.set_description ("This is the main content")
ta.set_is_required (False)
-- Summary
create sum.make ("summary")
sum.set_rows (3)
sum.set_cols (70)
-- if cms_html is selected
sum.show_as_editor_if_selected (tselect, cms_format.name)
if a_node /= Void then
sum.set_text_value (a_node.summary)
end
sum.set_label ("Summary")
sum.set_description ("This is the summary")
sum.set_is_required (False)
create fset.make
fset.set_legend ("Body")
-- Add summary
fset.extend (sum)
fset.extend_html_text("<br />")
-- Add content (body)
fset.extend (ta)
fset.extend_html_text ("<br/>")
across across
content_type.available_formats as c content_type.available_formats as c
loop loop
@@ -65,11 +93,52 @@ feature -- Forms ...
fset.extend (tselect) fset.extend (tselect)
f.extend (fset) 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 end
update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE) update_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
local local
b: detachable READABLE_STRING_8 b,s: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT f: detachable CONTENT_FORMAT
do do
if attached fd.integer_item ("id") as l_id and then l_id > 0 then if attached fd.integer_item ("id") as l_id and then l_id > 0 then
@@ -82,6 +151,12 @@ feature -- Forms ...
if attached fd.string_item ("body") as l_body then if attached fd.string_item ("body") as l_body then
b := l_body b := l_body
end end
-- Read out the summary field from the form data
if attached fd.string_item ("summary") as l_summary then
s := l_summary
end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format 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 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
@@ -89,15 +164,19 @@ feature -- Forms ...
else else
f := response.formats.default_format f := response.formats.default_format
end end
-- Update node with summary and body content
if b /= Void then if b /= Void then
a_node.set_content (b, Void, f.name) -- FIXME: summary a_node.set_content (b, s, f.name)
end end
end end
new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable CMS_NODE): G new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable CMS_NODE): G
-- <Precursor> -- <Precursor>
local local
b: detachable READABLE_STRING_8 b,s: detachable READABLE_STRING_8
f: detachable CONTENT_FORMAT f: detachable CONTENT_FORMAT
l_node: detachable like new_node l_node: detachable like new_node
do do
@@ -131,9 +210,16 @@ feature -- Forms ...
end end
l_node.set_author (response.user) l_node.set_author (response.user)
--Summary
if attached fd.string_item ("summary") as l_summary then
s := l_summary
end
--Content
if attached fd.string_item ("body") as l_body then if attached fd.string_item ("body") as l_body then
b := l_body b := l_body
end end
if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
f := f_format 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 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
@@ -141,8 +227,10 @@ feature -- Forms ...
else else
f := response.formats.default_format f := response.formats.default_format
end end
-- Update node with summary and content
if b /= Void then if b /= Void then
l_node.set_content (b, Void, f.name) l_node.set_content (b, s, f.name)
end end
Result := l_node Result := l_node
end end
@@ -160,13 +248,32 @@ feature -- Output
node_api := a_response.node_api node_api := a_response.node_api
a_response.add_variable (a_node, "node") a_response.add_variable (a_node, "node")
create lnk.make ("View", node_api.node_path (a_node)) lnk := a_response.node_local_link (a_node, a_response.translation ("View", Void))
lnk.set_weight (1) lnk.set_weight (1)
a_response.add_to_primary_tabs (lnk) 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") create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
lnk.set_weight (2) lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk) 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 create s.make_empty
s.append ("<div class=%"info%"> ") s.append ("<div class=%"info%"> ")
if attached a_node.author as l_author then if attached a_node.author as l_author then
@@ -180,6 +287,23 @@ feature -- Output
s.append (")") s.append (")")
end end
s.append ("</div>") s.append ("</div>")
-- We don't show the summary on the detail page, since its just a short view of the full content. Otherwise we would write the same thing twice.
-- The usage of the summary is to give a short overview in the list of nodes or for the meta tag "description"
-- if attached a_node.summary as l_summary then
-- s.append ("<p class=%"summary%">")
-- if attached node_api.cms_api.format (a_node.format) as f then
-- s.append (f.formatted_output (l_summary))
-- else
-- s.append (a_response.formats.default_format.formatted_output (l_summary))
-- end
-- s.append ("</p>")
-- end
if attached a_node.content as l_content then if attached a_node.content as l_content then
s.append ("<p class=%"content%">") s.append ("<p class=%"content%">")
if attached node_api.cms_api.format (a_node.format) as f then if attached node_api.cms_api.format (a_node.format) as f then

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}." description: "Summary description for {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"

View File

@@ -26,7 +26,7 @@ feature {NONE} -- Initialization
initialize initialize
do do
Precursor Precursor
create {WSF_CMS_THEME} wsf_theme.make (Current, theme) create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end end
wsf_theme: WSF_THEME wsf_theme: WSF_THEME
@@ -37,7 +37,7 @@ feature -- Execution
-- Computed response message. -- Computed response message.
local local
b: STRING_8 b: STRING_8
f: like edit_form f: like new_edit_form
fd: detachable WSF_FORM_DATA fd: detachable WSF_FORM_DATA
nid: INTEGER_64 nid: INTEGER_64
do do
@@ -48,43 +48,96 @@ feature -- Execution
attached node_api.node (nid) as l_node attached node_api.node (nid) as l_node
then then
if attached node_api.node_type_for (l_node) as l_type then if attached node_api.node_type_for (l_node) as l_type then
if has_permission ("edit " + l_type.name) then fixme ("refactor: process_edit, process_create porcess edit")
f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) 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 if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b)) f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
f.process (Current) f.process (Current)
fd := f.last_data fd := f.last_data
end 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 if attached redirection as l_location then
-- FIXME: Hack for now -- FIXME: Hack for now
set_title (l_node.title) set_title (l_node.title)
add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs)
b.append (html_encoded (l_type.title) + " saved") b.append (html_encoded (l_type.title) + " saved")
else else
set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out) 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
add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs) if attached redirection as l_location then
add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs) -- 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) f.append_to_html (wsf_theme, b)
end end
else else
b.append ("<h1>Access denied</h1>") b.append ("<h1>")
b.append (translation ("Access denied", Void))
b.append ("</h1>")
end end
else else
set_title ("Unknown node") set_title (translation ("Unknown node", Void))
end end
elseif elseif
attached {WSF_STRING} request.path_parameter ("type") as p_type and then attached {WSF_STRING} request.path_parameter ("type") as p_type and then
attached node_api.node_type (p_type.value) as l_type attached node_api.node_type (p_type.value) as l_type
then then
if has_permission ("create " + l_type.name) then if has_permissions (<<"create any", "create " + l_type.name>>) then
if attached l_type.new_node (Void) as l_node then if attached l_type.new_node (Void) as l_node then
f := edit_form (l_node, url (request.path_info, Void), "edit-" + l_type.name, l_type) 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 if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b)) f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b)) f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
@@ -94,27 +147,33 @@ feature -- Execution
set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out) set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out)
add_to_menu (create {CMS_LOCAL_LINK}.make ("View", node_url (l_node)), primary_tabs) if l_node.has_id then
add_to_menu (create {CMS_LOCAL_LINK}.make ("Edit", "/node/" + l_node.id.out + "/edit"), primary_tabs) add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
end
f.append_to_html (wsf_theme, b) f.append_to_html (wsf_theme, b)
else else
b.append ("<h1>Server error</h1>") b.append ("<h1>")
b.append (translation ("Server error", Void))
b.append ("</h1>")
end end
else else
b.append ("<h1>Access denied</h1>") b.append ("<h1>")
b.append (translation ("Access denied", Void))
b.append ("</h1>")
end end
else else
set_title ("Create new content ...") set_title (translation ("Create new content ...", Void))
b.append ("<ul id=%"content-types%">") b.append ("<ul id=%"content-types%">")
across across
node_api.node_types as ic node_api.node_types as ic
loop loop
if if
attached ic.item as l_node_type and then attached ic.item as l_node_type and then
(has_permission ("create any") or has_permission ("create " + l_node_type.name)) has_permissions (<<"create any", "create " + l_node_type.name>>)
then then
b.append ("<li>" + link (l_node_type.name, "/node/add/" + l_node_type.name, Void)) b.append ("<li>" + link (l_node_type.name, "node/add/" + l_node_type.name, Void))
if attached l_node_type.description as d then if attached l_node_type.description as d then
b.append ("<div class=%"description%">" + d + "</div>") b.append ("<div class=%"description%">" + d + "</div>")
end end
@@ -160,6 +219,7 @@ feature -- Form
l_preview: BOOLEAN l_preview: BOOLEAN
l_node: detachable CMS_NODE l_node: detachable CMS_NODE
s: STRING s: STRING
l_path_alias: detachable READABLE_STRING_8
do do
l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview") l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview")
if not l_preview then if not l_preview then
@@ -191,16 +251,34 @@ feature -- Form
end end
node_api.save_node (l_node) node_api.save_node (l_node)
if attached current_user (request) as u then if attached current_user (request) as u then
api.log ("node", "User %"" + user_link (u) + "%" " + s + " node " + link (a_type.name +" #" + l_node.id.out, "/node/" + l_node.id.out , Void), 0, node_local_link (l_node)) api.log ("node",
"User %"" + user_html_link (u) + "%" " + s + " node " + node_html_link (l_node, a_type.name + " #" + l_node.id.out),
0, node_local_link (l_node, Void)
)
else else
api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node)) api.log ("node", "Anonymous " + s + " node " + a_type.name +" #" + l_node.id.out, 0, node_local_link (l_node, Void))
end end
add_success_message ("Node #" + l_node.id.out + " saved.") add_success_message ("Node #" + l_node.id.out + " saved.")
set_redirection (node_url (l_node))
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
end end
edit_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM 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 local
f: CMS_FORM f: CMS_FORM
ts: WSF_FORM_SUBMIT_INPUT ts: WSF_FORM_SUBMIT_INPUT
@@ -216,8 +294,7 @@ feature -- Form
end end
f.extend (th) f.extend (th)
fill_edit_form (a_type, f, a_node) populate_form (a_node_type, f, a_node)
f.extend_html_text ("<br/>") f.extend_html_text ("<br/>")
create ts.make ("op") create ts.make ("op")
@@ -231,8 +308,87 @@ feature -- Form
Result := f Result := f
end 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 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 do
if attached node_api.node_type_webform_manager (a_content_type) as wf then if attached node_api.node_type_webform_manager (a_content_type) as wf then
Result := wf.new_node (Current, a_form_data, a_node) Result := wf.new_node (Current, a_form_data, a_node)
@@ -249,11 +405,4 @@ feature -- Form
end end
end end
fill_edit_form (a_content_type: CMS_NODE_TYPE [CMS_NODE]; a_form: WSF_FORM; a_node: detachable CMS_NODE)
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
end end

View File

@@ -89,6 +89,14 @@ feature -- HTTP Methods
check valid_url: req.path_info.starts_with_general ("/node/") end check valid_url: req.path_info.starts_with_general ("/node/") end
create edit_response.make (req, res, api, node_api) create edit_response.make (req, res, api, node_api)
edit_response.execute 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 else
-- Display existing node -- Display existing node
l_nid := node_id_path_parameter (req) l_nid := node_id_path_parameter (req)
@@ -114,9 +122,29 @@ feature -- HTTP Methods
local local
edit_response: NODE_FORM_RESPONSE edit_response: NODE_FORM_RESPONSE
do do
fixme ("Refactor code: extract methods: edit_node and add_node")
if req.path_info.ends_with_general ("/edit") then if req.path_info.ends_with_general ("/edit") then
create edit_response.make (req, res, api, node_api) create edit_response.make (req, res, api, node_api)
edit_response.execute 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 elseif req.path_info.starts_with_general ("/node/add/") then
create edit_response.make (req, res, api, node_api) create edit_response.make (req, res, api, node_api)
edit_response.execute edit_response.execute
@@ -142,11 +170,12 @@ feature -- HTTP Methods
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if api.user_has_permission (l_user, "delete " + l_node.content_type) then if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then
node_api.delete_node (l_node) node_api.delete_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
send_access_denied (req, res) send_access_denied (req, res)
-- send_not_authorized ?
end end
else else
do_error (req, res, l_id) do_error (req, res, l_id)
@@ -165,6 +194,62 @@ feature -- HTTP Methods
send_not_implemented ("REST API not yet implemented", req, res) send_not_implemented ("REST API not yet implemented", req, res)
end 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 feature -- Error
do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING) do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: detachable WSF_STRING)

View File

@@ -51,36 +51,63 @@ feature -- Helpers
end end
end end
feature -- Helpers feature -- Helpers: cms link
user_local_link (u: CMS_USER): CMS_LINK user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do do
create {CMS_LOCAL_LINK} Result.make (u.name, user_url (u)) if a_opt_title /= Void then
create Result.make (a_opt_title, user_url (u))
else
create Result.make (u.name, user_url (u))
end
end end
node_local_link (n: CMS_NODE): CMS_LINK node_local_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do do
create {CMS_LOCAL_LINK} Result.make (n.title, node_url (n)) 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 end
user_link (u: CMS_USER): like link feature -- Helpers: html link
user_html_link (u: CMS_USER): like link
do do
Result := link (u.name, "/user/" + u.id.out, Void) Result := link (u.name, "user/" + u.id.out, Void)
end end
node_link (n: CMS_NODE): like link node_html_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): like link
local
l_title: detachable READABLE_STRING_GENERAL
do do
Result := link (n.title, "/node/" + n.id.out, Void) if a_opt_title /= Void then
l_title := a_opt_title
else
l_title := n.title
end end
Result := link (l_title, node_api.node_path (n), Void)
end
feature -- Helpers: URL
user_url (u: CMS_USER): like url user_url (u: CMS_USER): like url
require
u_with_id: u.has_id
do do
Result := url ("/user/" + u.id.out, Void) Result := url ("user/" + u.id.out, Void)
end end
node_url (n: CMS_NODE): like url node_url (n: CMS_NODE): like url
require
n_with_id: n.has_id
do do
Result := url ("/node/" + n.id.out, Void) Result := url (node_api.node_link (n).location, Void)
end end
end end

View File

@@ -27,7 +27,7 @@ feature {NONE} -- Initialization
initialize initialize
do do
Precursor Precursor
create {WSF_CMS_THEME} wsf_theme.make (Current, theme) create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end end
wsf_theme: WSF_THEME wsf_theme: WSF_THEME

View File

@@ -37,36 +37,64 @@ feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE) do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- <Precursor>
local local
l_page: CMS_RESPONSE l_response: CMS_RESPONSE
s: STRING s: STRING
n: CMS_NODE n: CMS_NODE
lnk: CMS_LOCAL_LINK
l_page_helper: CMS_PAGINATION_GENERATOR
s_pager: STRING
l_count: NATURAL_64
do do
-- At the moment the template is hardcoded, but we can -- At the moment the template are hardcoded, but we can
-- get them from the configuration file and load them into -- get them from the configuration file and load them into
-- the setup class. -- the setup class.
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) l_count := node_api.nodes_count
l_page.add_variable (node_api.nodes, "nodes")
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
-- NOTE: for development purposes we have the following hardcode output. create s.make_empty
create s.make_from_string ("<p>Nodes:</p>") if l_count > 1 then
if attached node_api.nodes as lst then l_response.set_title ("Listing " + l_count.out + " nodes")
else
l_response.set_title ("Listing " + l_count.out + " node")
end
create s_pager.make_empty
create l_page_helper.make ("nodes/?page={page}&size={size}", node_api.nodes_count, 25) -- FIXME: Make this default page size a global CMS settings
l_page_helper.get_setting_from_request (req)
if l_page_helper.has_upper_limit and then l_page_helper.pages_count > 1 then
l_page_helper.append_to_html (l_response, s_pager)
if l_page_helper.page_size > 25 then
s.append (s_pager)
end
end
if attached node_api.recent_nodes (create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size)) as lst then
s.append ("<ul class=%"cms-nodes%">%N") s.append ("<ul class=%"cms-nodes%">%N")
across across
lst as ic lst as ic
loop loop
n := ic.item n := ic.item
lnk := node_api.node_link (n)
s.append ("<li class=%"cms_type_"+ n.content_type +"%">") s.append ("<li class=%"cms_type_"+ n.content_type +"%">")
s.append (l_page.link (n.title + " (#" + n.id.out + ")", node_api.node_path (n), Void)) s.append (l_response.link (lnk.title, lnk.location, Void))
debug
if attached node_api.content_type (n.content_type) as ct then
s.append ("<span class=%"description%">")
s.append (html_encoded (ct.title))
s.append ("</span>")
end
end
s.append ("</li>%N") s.append ("</li>%N")
end end
s.append ("</ul>%N") s.append ("</ul>%N")
end end
-- Again the pager at the bottom, if needed
s.append (s_pager)
l_page.set_main_content (s) l_response.set_main_content (s)
l_page.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented<br/>", Void), "highlighted") l_response.execute
l_page.execute
end end
end end

View File

@@ -0,0 +1,80 @@
note
description: "Request handler related to /trash "
date: "$Date$"
revision: "$Revision$"
class
TRASH_HANDLER
inherit
CMS_NODE_HANDLER
WSF_URI_HANDLER
rename
new_mapping as new_uri_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get
end
REFACTORING_HELPER
create
make
feature -- execute
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_page: CMS_RESPONSE
s: STRING
n: CMS_NODE
lnk: CMS_LOCAL_LINK
do
-- At the moment the template is hardcoded, but we can
-- get them from the configuration file and load them into
-- the setup class.
if attached current_user (req) as l_user then
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.add_variable (node_api.trashed_nodes (l_user), "nodes")
-- NOTE: for development purposes we have the following hardcode output.
create s.make_from_string ("<p>Nodes:</p>")
if attached node_api.trashed_nodes (l_user) as lst then
s.append ("<ul class=%"cms-nodes%">%N")
across
lst as ic
loop
n := ic.item
lnk := node_api.node_link (n)
s.append ("<li class=%"cms_type_"+ n.content_type +"%">")
s.append (l_page.link (lnk.title, lnk.location, Void))
s.append ("</li>%N")
end
s.append ("</ul>%N")
end
l_page.set_main_content (s)
-- l_page.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented<br/>", Void), "highlighted")
l_page.execute
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
l_page.execute
end
end
end

View File

@@ -9,14 +9,12 @@ class
inherit inherit
CMS_MODULE CMS_MODULE
rename
module_api as node_api
redefine redefine
register_hooks, register_hooks,
initialize, initialize,
is_installed, is_installed,
install, install,
node_api module_api
end end
CMS_HOOK_MENU_SYSTEM_ALTER CMS_HOOK_MENU_SYSTEM_ALTER
@@ -43,15 +41,26 @@ feature {NONE} -- Initialization
feature {CMS_API} -- Module Initialization feature {CMS_API} -- Module Initialization
initialize (api: CMS_API) initialize (a_api: CMS_API)
-- <Precursor> -- <Precursor>
local local
p1,p2: CMS_PAGE p1,p2: CMS_PAGE
ct: CMS_PAGE_NODE_TYPE ct: CMS_PAGE_NODE_TYPE
l_node_api: like node_api l_node_api: like node_api
l_node_storage: CMS_NODE_STORAGE_I
do do
Precursor (api) Precursor (a_api)
create l_node_api.make (api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
create {CMS_NODE_STORAGE_SQL} l_node_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_NODE_STORAGE_NULL} l_node_storage.make
end
-- Node API initialization
create l_node_api.make_with_storage (a_api, l_node_storage)
node_api := l_node_api node_api := l_node_api
-- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id. -- Add support for CMS_PAGE, which requires a storage extension to store the optional "parent" id.
@@ -61,7 +70,7 @@ feature {CMS_API} -- Module Initialization
-- FIXME: the following code is mostly for test purpose/initialization, remove later -- FIXME: the following code is mostly for test purpose/initialization, remove later
if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then if l_sql_node_storage.sql_table_items_count ("page_nodes") = 0 then
if attached api.user_api.user_by_id (1) as u then if attached a_api.user_api.user_by_id (1) as u then
create ct create ct
p1 := ct.new_node (Void) p1 := ct.new_node (Void)
p1.set_title ("Welcome") p1.set_title ("Welcome")
@@ -81,46 +90,53 @@ feature {CMS_API} -- Module Initialization
-- FIXME: maybe provide a default solution based on file system, when no SQL storage is available. -- FIXME: maybe provide a default solution based on file system, when no SQL storage is available.
-- IDEA: we could also have generic extension to node system, that handle generic addition field. -- IDEA: we could also have generic extension to node system, that handle generic addition field.
end end
ensure then
node_api_set: node_api /= Void
end end
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN is_installed (a_api: CMS_API): BOOLEAN
-- Is Current module installed? -- Is Current module installed?
do do
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
Result := l_sql_storage.sql_table_exists ("nodes") and Result := l_sql_storage.sql_table_exists ("nodes") and
l_sql_storage.sql_table_exists ("page_nodes") l_sql_storage.sql_table_exists ("page_nodes")
end end
end end
install (api: CMS_API) install (a_api: CMS_API)
do do
-- Schema -- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql")) l_sql_storage.sql_execute_file_script (a_api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql"))
end end
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API
module_api: detachable CMS_MODULE_API
-- <Precursor>
do
if attached node_api as l_api then
Result := l_api
else
-- Current is initialized, so node_api should be set.
check has_node_api: False end
end
end
node_api: detachable CMS_NODE_API node_api: detachable CMS_NODE_API
-- <Precursor> -- <Precursor>
feature -- Access: router feature -- Access: router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Node router. -- <Precursor>
local
l_node_api: like node_api
do do
l_node_api := node_api if attached node_api as l_node_api then
if l_node_api = Void then configure_web (a_api, l_node_api, a_router)
create l_node_api.make (a_api)
node_api := l_node_api
end end
create Result.make (2)
configure_web (a_api, l_node_api, Result)
end end
configure_web (a_api: CMS_API; a_node_api: CMS_NODE_API; a_router: WSF_ROUTER) configure_web (a_api: CMS_API; a_node_api: CMS_NODE_API; a_router: WSF_ROUTER)
@@ -128,6 +144,7 @@ feature -- Access: router
l_node_handler: NODE_HANDLER l_node_handler: NODE_HANDLER
l_nodes_handler: NODES_HANDLER l_nodes_handler: NODES_HANDLER
l_uri_mapping: WSF_URI_MAPPING l_uri_mapping: WSF_URI_MAPPING
l_trash_handler: TRASH_HANDLER
do do
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29] -- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
create l_node_handler.make (a_api, a_node_api) create l_node_handler.make (a_api, a_node_api)
@@ -136,6 +153,8 @@ feature -- Access: router
a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post) a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post) a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}", l_node_handler, a_router.methods_get) a_router.handle_with_request_methods ("/node/{id}", l_node_handler, a_router.methods_get)
-- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post) -- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post)
@@ -144,6 +163,13 @@ feature -- Access: router
create l_nodes_handler.make (a_api, a_node_api) create l_nodes_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler) create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get) a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
--Trash
create l_trash_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
end end
feature -- Hooks feature -- Hooks
@@ -174,10 +200,13 @@ feature -- Hooks
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
-- perms: detachable ARRAYED_LIST [READABLE_STRING_8] -- perms: detachable ARRAYED_LIST [READABLE_STRING_8]
do do
create lnk.make ("List of nodes", a_response.url ("/nodes", Void)) create lnk.make ("List of nodes", "nodes")
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
create lnk.make ("Create ..", a_response.url ("/node/", Void)) create lnk.make ("Trash", "trash")
a_menu_system.primary_menu.extend (lnk)
create lnk.make ("Create ..", "node")
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
end end

View File

@@ -43,6 +43,8 @@ feature {NONE} -- Implementation
extended_store (a_node: CMS_NODE) extended_store (a_node: CMS_NODE)
-- Store extended data from `a_node'. -- Store extended data from `a_node'.
require
not error_handler.has_error
do do
if attached node_storage_extension (a_node) as ext then if attached node_storage_extension (a_node) as ext then
ext.store_node (a_node) ext.store_node (a_node)
@@ -51,16 +53,17 @@ feature {NONE} -- Implementation
extended_load (a_node: CMS_NODE) extended_load (a_node: CMS_NODE)
-- Load extended data into `a_node'. -- Load extended data into `a_node'.
require
not error_handler.has_error
do do
if attached node_storage_extension (a_node) as ext then if attached node_storage_extension (a_node) as ext then
ext.load_node (a_node) ext.load_node (a_node)
end end
end end
feature -- Access feature -- Access
nodes_count: INTEGER_64 nodes_count: NATURAL_64
-- Count of nodes. -- Count of nodes.
deferred deferred
end end
@@ -70,6 +73,11 @@ feature -- Access
deferred deferred
end end
trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
-- List of nodes by user `a_user_id'.
deferred
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'. -- List of recent `a_count' nodes with an offset of `lower'.
deferred deferred
@@ -107,6 +115,8 @@ feature -- Change: Node
no_id: not a_node.has_id no_id: not a_node.has_id
valid_user: attached a_node.author as l_author and then l_author.id > 0 valid_user: attached a_node.author as l_author and then l_author.id > 0
deferred deferred
ensure
has_id: not error_handler.has_error implies a_node.has_id
end end
update_node (a_node: CMS_NODE) update_node (a_node: CMS_NODE)
@@ -133,32 +143,35 @@ feature -- Change: Node
deferred deferred
end end
-- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) trash_node (a_node: CMS_NODE)
-- -- Update node title to `a_title', node identified by id `a_node_id'. -- Trash `a_node'.
-- -- The user `a_user_id' is an existing or new collaborator. do
-- require if a_node.has_id then
-- valid_node_id: a_node_id > 0 trash_node_by_id (a_node.id)
-- valid_user_id: a_user_id > 0 end
-- deferred end
-- end
-- update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) restore_node (a_node: CMS_NODE)
-- -- Update node summary to `a_summary', node identified by id `a_node_id'. -- Restore `a_node'.
-- -- The user `a_user_id' is an existing or new collaborator. do
-- require if a_node.has_id then
-- valid_id: a_node_id > 0 restore_node_by_id (a_node.id)
-- valid_user_id: a_user_id > 0 end
-- deferred end
-- end
-- update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) trash_node_by_id (a_id: INTEGER_64)
-- -- Update node content to `a_content', node identified by id `a_node_id'. -- Trash node by id `a_id'.
-- -- The user `a_user_id' is an existing or new collaborator. require
-- require valid_node_id: a_id > 0
-- valid_id: a_node_id > 0 deferred
-- valid_user_id: a_user_id > 0 end
-- deferred
-- end restore_node_by_id (a_id: INTEGER_64)
-- Restore node by id `a_id'.
require
valid_node_id: a_id > 0
deferred
end
feature -- Helpers feature -- Helpers

View File

@@ -30,7 +30,7 @@ feature -- Error Handling
feature -- Access: node feature -- Access: node
nodes_count: INTEGER_64 nodes_count: NATURAL_64
-- Count of nodes. -- Count of nodes.
do do
end end
@@ -41,6 +41,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0) create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end end
trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
-- List of nodes by user `a_user_id'.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of the `a_count' most recent nodes, starting from `a_lower'. -- List of the `a_count' most recent nodes, starting from `a_lower'.
do do
@@ -80,6 +86,16 @@ feature -- Node
do do
end end
trash_node_by_id (a_id: INTEGER_64)
-- <Precursor>
do
end
restore_node_by_id (a_id: INTEGER_64)
-- <Precursor>
do
end
-- update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) -- update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- -- <Precursor> -- -- <Precursor>
-- do -- do

View File

@@ -22,17 +22,18 @@ create
feature -- Access feature -- Access
nodes_count: INTEGER_64 nodes_count: NATURAL_64
-- Number of items nodes. -- Number of items nodes.
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".nodes_count") write_information_log (generator + ".nodes_count")
sql_query (sql_select_nodes_count, Void) sql_query (sql_select_nodes_count, Void)
if sql_rows_count = 1 then if sql_rows_count = 1 then
Result := sql_read_integer_64 (1) Result := sql_read_natural_64 (1)
end end
end end
nodes: LIST [CMS_NODE] nodes: LIST [CMS_NODE]
-- List of nodes. -- List of nodes.
do do
@@ -59,6 +60,36 @@ feature -- Access
-- end -- end
end end
trashed_nodes (a_user_id: INTEGER_64): LIST [CMS_NODE]
-- List of nodes.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".trash_nodes")
from
create l_parameters.make (1)
if a_user_id > 1 then
-- Not admin user
l_parameters.put (a_user_id, "author")
sql_query (sql_select_trash_nodes_by_author, l_parameters)
else
sql_query (sql_select_trash_nodes, Void)
end
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'. -- List of recent `a_count' nodes with an offset of `lower'.
local local
@@ -108,8 +139,8 @@ feature -- Access
error_handler.reset error_handler.reset
write_information_log (generator + ".node_author") write_information_log (generator + ".node_author")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_id, "node_id") l_parameters.put (a_id, "nid")
sql_query (select_node_author, l_parameters) sql_query (Select_user_author, l_parameters)
if sql_rows_count >= 1 then if sql_rows_count >= 1 then
Result := fetch_author Result := fetch_author
end end
@@ -144,59 +175,52 @@ feature -- Change: Node
-- Remove node by id `a_id'. -- Remove node by id `a_id'.
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME
do do
write_information_log (generator + ".delete_node") create l_time.make_now_utc
write_information_log (generator + ".delete_node {" + a_id.out + "}")
error_handler.reset error_handler.reset
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (l_time, "changed")
l_parameters.put ({CMS_NODE_API}.trashed, "status")
l_parameters.put (a_id, "nid") l_parameters.put (a_id, "nid")
sql_change (sql_delete_node, l_parameters) sql_change (sql_delete_node, l_parameters)
end end
-- update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- -- <Precursor>
-- local
-- l_parameters: STRING_TABLE [detachable ANY]
-- do
-- -- FIXME: unused a_user_id !
-- error_handler.reset
-- write_information_log (generator + ".update_node_title")
-- create l_parameters.make (3)
-- l_parameters.put (a_title, "title")
-- l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
-- l_parameters.put (a_node_id, "nid")
-- sql_change (sql_update_node_title, l_parameters)
-- end
-- update_node_summary (a_user_id: Like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) trash_node_by_id (a_id: INTEGER_64)
-- -- <Precursor> -- <Precursor>
-- local local
-- l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [ANY]
-- do l_time: DATE_TIME
-- -- FIXME: unused a_user_id ! do
-- error_handler.reset create l_time.make_now_utc
-- write_information_log (generator + ".update_node_summary") write_information_log (generator + ".trash_node {" + a_id.out + "}")
-- create l_parameters.make (3)
-- l_parameters.put (a_summary, "summary") error_handler.reset
-- l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") create l_parameters.make (1)
-- l_parameters.put (a_node_id, "nid") l_parameters.put (a_id, "nid")
-- sql_change (sql_update_node_summary, l_parameters) sql_change (sql_trash_node, l_parameters)
-- end end
restore_node_by_id (a_id: INTEGER_64)
-- <Precursor>
local
l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME
do
create l_time.make_now_utc
write_information_log (generator + ".restore_node {" + a_id.out + "}")
error_handler.reset
create l_parameters.make (1)
l_parameters.put (l_time, "changed")
l_parameters.put ({CMS_NODE_API}.not_published, "status")
l_parameters.put (a_id, "nid")
sql_change (sql_restore_node, l_parameters)
end
-- update_node_content (a_user_id: Like {CMS_USER}.id;a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- -- <Precursor>
-- local
-- l_parameters: STRING_TABLE [detachable ANY]
-- do
-- -- FIXME: unused a_user_id !
-- error_handler.reset
-- write_information_log (generator + ".update_node_content")
-- create l_parameters.make (3)
-- l_parameters.put (a_content, "content")
-- l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
-- l_parameters.put (a_node_id, "nid")
-- sql_change (sql_update_node_content, l_parameters)
-- end
feature {NONE} -- Implementation feature {NONE} -- Implementation
@@ -209,7 +233,7 @@ feature {NONE} -- Implementation
error_handler.reset error_handler.reset
write_information_log (generator + ".store_node") write_information_log (generator + ".store_node")
create l_parameters.make (8) create l_parameters.make (9)
l_parameters.put (a_node.content_type, "type") l_parameters.put (a_node.content_type, "type")
l_parameters.put (a_node.title, "title") l_parameters.put (a_node.title, "title")
l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.summary, "summary")
@@ -217,6 +241,7 @@ feature {NONE} -- Implementation
l_parameters.put (a_node.format, "format") l_parameters.put (a_node.format, "format")
l_parameters.put (a_node.publication_date, "publish") l_parameters.put (a_node.publication_date, "publish")
l_parameters.put (now, "changed") l_parameters.put (now, "changed")
l_parameters.put (a_node.status, "status")
if attached a_node.author as l_author then if attached a_node.author as l_author then
check valid_author: l_author.has_id end check valid_author: l_author.has_id end
l_parameters.put (l_author.id, "author") l_parameters.put (l_author.id, "author")
@@ -243,9 +268,15 @@ feature {NONE} -- Implementation
a_node.set_revision (1) -- New object. a_node.set_revision (1) -- New object.
end end
end end
if not error_handler.has_error then
extended_store (a_node) extended_store (a_node)
end
if error_handler.has_error then
sql_rollback_transaction
else
sql_commit_transaction sql_commit_transaction
end end
end
feature -- Helpers feature -- Helpers
@@ -260,24 +291,42 @@ feature -- Helpers
feature {NONE} -- Queries feature {NONE} -- Queries
sql_select_nodes_count: STRING = "SELECT count(*) from Nodes;" sql_select_nodes_count: STRING = "SELECT count(*) FROM Nodes WHERE status != -1 ;"
-- Nodes count (Published and not Published)
--| note: {CMS_NODE_API}.trashed = -1
sql_select_nodes: STRING = "SELECT * from Nodes;" sql_select_nodes: STRING = "SELECT * FROM Nodes WHERE status != -1 ;"
-- SQL Query to retrieve all nodes. -- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM Nodes WHERE nid =:nid ORDER BY revision desc, publish desc LIMIT 1;" sql_select_trash_nodes: STRING = "SELECT * FROM Nodes WHERE status = -1 ;"
-- SQL Query to retrieve all trahsed nodes.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM Nodes ORDER BY nid desc, publish desc LIMIT :rows OFFSET :offset ;" sql_select_trash_nodes_by_author: STRING = "SELECT * FROM Nodes WHERE status = -1 and author = :author ;"
-- SQL Query to retrieve all nodes by a given author.
--| note: {CMS_NODE_API}.trashed = -1
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :author);" sql_select_node_by_id: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes WHERE nid =:nid ORDER BY revision DESC, publish DESC LIMIT 1;"
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM Nodes ORDER BY nid DESC, publish DESC LIMIT :rows OFFSET :offset ;"
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (1, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);"
-- SQL Insert to add a new node. -- SQL Insert to add a new node.
sql_update_node : STRING = "UPDATE nodes SET revision = revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, author=:author WHERE nid=:nid;" sql_update_node : STRING = "UPDATE nodes SET revision = revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, status=:status, author=:author WHERE nid=:nid;"
-- FIXME: for now no revision inc.! -- FIXME: for now no revision inc.!
-- sql_update_node : STRING = "UPDATE nodes SET revision = revision + 1, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, revision = revision + 1, author=:author WHERE nid=:nid;" -- sql_update_node : STRING = "UPDATE nodes SET revision = revision + 1, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, revision = revision + 1, author=:author WHERE nid=:nid;"
-- SQL node. -- SQL node.
sql_delete_node: STRING = "DELETE FROM nodes WHERE nid=:nid;" sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata.
sql_trash_node: STRING = "DELETE FROM nodes WHERE nid=:nid"
-- Physical deletion with free metadata.
sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Restore node to {CMS_NODE_API}.not_publised.
-- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;" -- sql_update_node_author: STRING = "UPDATE nodes SET author=:author WHERE nid=:nid;"
@@ -294,7 +343,7 @@ feature {NONE} -- Queries
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM Nodes INNER JOIN users ON nodes.author=users.uid AND users.uid = :uid;" Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM Nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid;"
Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;" Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;"
@@ -335,6 +384,9 @@ feature {NONE} -- Implementation
if attached sql_read_date_time (11) as l_modif_date then if attached sql_read_date_time (11) as l_modif_date then
Result.set_modification_date (l_modif_date) Result.set_modification_date (l_modif_date)
end end
if attached sql_read_integer_32 (12) as l_status then
Result.set_status (l_status)
end
end end
end end

31
package.iron Normal file
View File

@@ -0,0 +1,31 @@
package ROC_cms
project
cms = "cms-safe.ecf"
cms = "cms.ecf"
app_env = "library/app_env/app_env-safe.ecf"
app_env = "library/app_env/app_env.ecf"
config = "library/configuration/config-safe.ecf"
config = "library/configuration/config.ecf"
app_env = "library/layout/layout-safe.ecf"
app_env = "library/layout/layout.ecf"
cms_model = "library/model/cms_model-safe.ecf"
cms_model = "library/model/cms_model.ecf"
persistence_mysql = "library/persistence/mysql/persistence_mysql-safe.ecf"
persistence_mysql = "library/persistence/mysql/persistence_mysql.ecf"
persistence_sqlite = "library/persistence/sqlite/persistence_sqlite-safe.ecf"
persistence_sqlite = "library/persistence/sqlite/persistence_sqlite.ecf"
basic_auth = "modules/basic_auth/basic_auth-safe.ecf"
basic_auth = "modules/basic_auth/basic_auth.ecf"
node = "modules/node/node-safe.ecf"
node = "modules/node/node.ecf"
note
title: ROC CMS
description: CMS written with Eiffel
tags: cms, web, rest, api
license: Eiffel Forum v2
copyright: Jocelyn Fiat, Javier Velilla, Eiffel Software
link[source]: "Github" https://github.com/EiffelWebFramework/ROC.git
end

View File

@@ -39,17 +39,21 @@ feature {NONE} -- Initialization
end end
configure configure
local
l_url: like site_url
do do
--| Site id, used to identified a site, this could be set to a uuid, or else --| Site id, used to identified a site, this could be set to a uuid, or else
site_id := text_item_or_default ("site.id", "_EWF_CMS_NO_ID_") site_id := text_item_or_default ("site.id", "_EWF_CMS_NO_ID_")
-- Site url: optional, but ending with a slash -- Site url: optional, but ending with a slash
site_url := string_8_item ("site_url") l_url := string_8_item ("site_url")
if attached site_url as l_url and then not l_url.is_empty then if l_url /= Void and then not l_url.is_empty then
if l_url [l_url.count] /= '/' then if l_url [l_url.count] /= '/' then
site_url := l_url + "/" l_url := l_url + "/"
end end
end end
site_url := l_url
-- Site name -- Site name
site_name := text_item_or_default ("site.name", "EWF::CMS") site_name := text_item_or_default ("site.name", "EWF::CMS")
@@ -84,7 +88,7 @@ feature {NONE} -- Initialization
initialize_modules initialize_modules
-- Intialize core modules. -- Intialize core modules.
local local
m: CMS_MODULE -- m: CMS_MODULE
do do
-- Core -- Core
-- create {BASIC_AUTH_MODULE} m.make -- create {BASIC_AUTH_MODULE} m.make

View File

@@ -62,7 +62,7 @@ feature -- Access: Site
-- Mainly used for internal notification. -- Mainly used for internal notification.
site_url: detachable READABLE_STRING_8 site_url: detachable READABLE_STRING_8
-- Optional base url of the site. -- Optional url of current CMS site.
front_page_path: detachable READABLE_STRING_8 front_page_path: detachable READABLE_STRING_8
-- Optional path defining the front page. -- Optional path defining the front page.

View File

@@ -12,11 +12,12 @@ feature -- Factory
template_block (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK template_block (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK
-- Smarty content block for `a_block_id' in the context of `a_module' and `a_response'. -- Smarty content block for `a_block_id' in the context of `a_module' and `a_response'.
local local
res: PATH
p: detachable PATH p: detachable PATH
do do
create p.make_from_string ("templates") create res.make_from_string ("templates")
p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") res := res.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
p := a_response.module_resource_path (a_module, p) p := a_response.module_resource_path (a_module, res)
if p /= Void then if p /= Void then
if attached p.entry as e then if attached p.entry as e then
create Result.make (a_block_id, Void, p.parent, e) create Result.make (a_block_id, Void, p.parent, e)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
note
description: "Pager widget for ROC CMS."
date: "$Date$"
revision: "$Revision$"
class
CMS_LOWER_UPPER_PAGER
inherit
WSF_WIDGET_PAGER
create
make
end

View File

@@ -36,11 +36,10 @@ feature {NONE} -- Initialization
feature -- Router feature -- Router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration. -- <Precursor>
do do
create Result.make (1) a_router.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?)), Void)
Result.handle ("/debug/", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_debug (a_api, ?, ?)))
end end
feature -- Hooks configuration feature -- Hooks configuration

View File

@@ -0,0 +1,88 @@
note
description: "[
Parameters associated with data query.
It could be query over http, or storage.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_DATA_QUERY_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_offset: NATURAL_64; a_size: NATURAL)
do
offset := a_offset
size := a_size
ensure
size_set: size = a_size
offset_set: offset = a_offset
end
feature -- Access
size: NATURAL assign set_size
-- Number of items per page.
offset: NATURAL_64 assign set_offset
-- lower index of `items' pagination.
order_by: detachable READABLE_STRING_8
-- field to order by.
order_ascending: BOOLEAN
-- is ascending ordering?
feature -- Element change
set_size (a_size: NATURAL)
-- Set `size' with `a_size'.
do
size := a_size
ensure
size_set: size = a_size
end
set_offset (a_offset: NATURAL_64)
-- Set offset with `a_offset'.
do
offset := a_offset
ensure
limit_set: offset = a_offset
end
set_ascending_order_by_field (a_field: detachable READABLE_STRING_8)
-- Pager with a order_by `a_field' asc.
do
if a_field /= Void then
order_by := a_field
order_ascending := True
else
order_by := Void
end
ensure
order_by_unset: a_field = Void implies order_by = Void
order_by_set: a_field /= Void implies attached order_by as l_order_by and then l_order_by.same_string (a_field)
asc_true: order_ascending
end
set_descending_order_by_field (a_field: detachable READABLE_STRING_8)
-- Pager sorting descending with field `a_field' if set, otherwise remove sorting.
do
if a_field /= Void then
order_by := a_field
order_ascending := False
else
order_by := Void
end
ensure
order_by_unset: a_field = Void implies order_by = Void
order_by_set: a_field /= Void implies attached order_by as l_order_by and then l_order_by.same_string (a_field)
asc_fasle: not order_ascending
end
end

View File

@@ -39,6 +39,13 @@ feature -- Status report
deferred deferred
end end
feature -- Basic operation
close
-- Close/disconnect current storage.
deferred
end
feature -- Error Handling feature -- Error Handling
error_handler: ERROR_HANDLER error_handler: ERROR_HANDLER

View File

@@ -14,6 +14,11 @@ inherit
default_create default_create
end end
CMS_USER_STORAGE_NULL
undefine
default_create
end
REFACTORING_HELPER REFACTORING_HELPER
rename rename
default_create as default_create_rh default_create as default_create_rh
@@ -40,74 +45,41 @@ feature -- Status report
Result := True Result := True
end end
feature -- Access: user feature -- Basic operation
has_user: BOOLEAN close
-- Has any user? -- <Precursor>
do do
end end
users: LIST [CMS_USER] feature -- URL aliases
do
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- <Precursor>.
do do
end end
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- Replace eventual previous alias `a_previous_alias' with a new alias `a_alias'
-- on source `a_source'.
do do
end end
user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- Unalias `a_source' from `a_alias'.
do do
end end
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8
-- Return eventual path alias associated with `a_source'.
do do
end end
feature -- Change: user source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8
-- Source path for alias `a_alias'.
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
do
a_user.set_id (1)
end
update_user (a_user: CMS_USER)
-- Update user `a_user'.
do do
end end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
do
end
user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE]
-- User roles for user `a_user'.
-- Note: anonymous and authenticated roles are not included.
do
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
end
user_roles: LIST [CMS_USER_ROLE]
do
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
do
end
feature -- Logs feature -- Logs
save_log (a_log: CMS_LOG) save_log (a_log: CMS_LOG)
@@ -115,6 +87,8 @@ feature -- Logs
do do
end end
feature -- Custom
set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8) set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8)
-- Save data `a_name:a_value' for type `a_type' (or default if none). -- Save data `a_name:a_value' for type `a_type' (or default if none).
do do

View File

@@ -1,15 +0,0 @@
note
description: "[
Objects that ...
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE_SQL_BUILDER
inherit
CMS_STORAGE_BUILDER
end

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
Objects that ... CMS Storage for core functionalities.
]" ]"
author: "$Author$" author: "$Author$"
date: "$Date$" date: "$Date$"
@@ -19,6 +19,34 @@ feature -- Error Handling
deferred deferred
end end
feature -- URL aliases
set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- Alias `a_source' with `a_alias'.
deferred
end
replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- Replace eventual previous alias `a_previous_alias' with a new alias `a_alias'
-- on source `a_source'.
deferred
end
unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- Unalias `a_source' from `a_alias'.
deferred
end
path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8
-- Return eventual path alias associated with `a_source'.
deferred
end
source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8
-- Source path for alias `a_alias'.
deferred
end
feature -- Logs feature -- Logs
save_log (a_log: CMS_LOG) save_log (a_log: CMS_LOG)

View File

@@ -18,6 +18,123 @@ inherit
SHARED_LOGGER SHARED_LOGGER
feature -- URL aliases
set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_source, "source")
l_parameters.put (a_alias, "alias")
if attached source_of_path_alias (a_alias) as l_path then
if a_source.same_string (l_path) then
-- already up to date
else
error_handler.add_custom_error (0, "alias exists", "Path alias %"" + a_alias + "%" already exists!")
end
else
sql_change (sql_insert_path_alias, l_parameters)
end
end
replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_previous_alias: detachable READABLE_STRING_8
do
error_handler.reset
if a_previous_alias = Void then
l_previous_alias := path_alias (a_source)
else
l_previous_alias := a_previous_alias
end
if
l_previous_alias /= Void and then
not a_alias.same_string (l_previous_alias)
then
create l_parameters.make (3)
l_parameters.put (a_source, "source")
l_parameters.put (l_previous_alias, "old")
l_parameters.put (a_alias, "alias")
sql_change (sql_update_path_alias, l_parameters)
end
end
unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
if attached source_of_path_alias (a_alias) as l_path then
if a_source.same_string (l_path) then
-- Found
create l_parameters.make (1)
l_parameters.put (a_alias, "alias")
sql_change (sql_delete_path_alias, l_parameters)
else
error_handler.add_custom_error (0, "alias mismatch", "Path alias %"" + a_alias + "%" is not related to source %"" + a_source + "%"!")
end
else
-- No such alias
end
end
path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_source, "source")
sql_query (sql_select_path_source, l_parameters)
if not has_error then
if sql_rows_count = 1 then
Result := sql_read_string (1)
end
end
end
source_of_path_alias (a_alias: READABLE_STRING_8): detachable READABLE_STRING_8
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_alias, "alias")
sql_query (sql_select_path_alias, l_parameters)
if not has_error then
if sql_rows_count = 1 then
Result := sql_read_string (1)
end
end
end
sql_select_path_alias: STRING = "SELECT source FROM path_aliases WHERE alias=:alias ;"
-- SQL select path aliases.
sql_select_path_source: STRING = "SELECT alias FROM path_aliases WHERE source=:source ORDER BY pid DESC LIMIT 1;"
-- SQL select latest path aliasing :source.
sql_insert_path_alias: STRING = "INSERT INTO path_aliases (source, alias) VALUES (:source, :alias);"
-- SQL insert path alias.
sql_update_path_alias: STRING = "UPDATE path_aliases SET alias=:alias WHERE source=:source AND alias=:old ;"
-- SQL update path alias.
sql_delete_path_alias: STRING = "DELETE FROM path_aliases WHERE alias=:alias;"
-- SQL delete path alias
feature -- Logs feature -- Logs
save_log (a_log: CMS_LOG) save_log (a_log: CMS_LOG)

View File

@@ -0,0 +1,107 @@
note
description: "Interface used to implement CMS Storage based on File system."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_STORAGE_FS_I
inherit
SHARED_LOGGER
feature {NONE} -- Initialization
make (a_location: PATH; a_api: CMS_API)
do
location := a_location
api := a_api
create error_handler.make
end
feature -- Access
api: CMS_API
-- Associated CMS api.
location: PATH
-- File system location.
feature -- Error handler
error_handler: ERROR_HANDLER
-- Error handler.
has_error: BOOLEAN
-- Last operation reported error.
do
Result := error_handler.has_error
end
reset_error
-- Reset errors.
do
error_handler.reset
end
feature -- Helpers
save_to_file (a_text: STRING; opt_name: detachable READABLE_STRING_GENERAL)
local
s: READABLE_STRING_GENERAL
now: DATE_TIME
fut: WSF_FILE_UTILITIES [RAW_FILE]
do
create fut
if opt_name /= Void then
s := opt_name
else
create now.make_now_utc
s := date_to_yyyymmdd_hhmmss_string (now)
end
if attached fut.new_file (location.extended (s)) as f then
f.put_string (a_text)
f.close
else
error_handler.add_custom_error (0, "saving failure", Void)
end
end
date_to_yyyymmdd_hhmmss_string (d: DATE_TIME): STRING
local
i: INTEGER
do
create Result.make_empty
Result.append_integer (d.year)
Result.append_character ('-')
i := d.month
if i < 10 then
Result.append_integer (0)
end
Result.append_integer (i)
Result.append_character ('-')
i := d.day
if i < 10 then
Result.append_integer (0)
end
Result.append_integer (i)
Result.append_character ('_')
i := d.hour
if i < 10 then
Result.append_integer (0)
end
Result.append_integer (i)
Result.append_character ('-')
i := d.minute
if i < 10 then
Result.append_integer (0)
end
Result.append_integer (i)
Result.append_character ('-')
i := d.second
if i < 10 then
Result.append_integer (0)
end
Result.append_integer (i)
end
end

View File

@@ -0,0 +1,15 @@
note
description: "CMS Storage based on SQL statement."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_STORAGE_SQL
inherit
CMS_STORAGE
CMS_STORAGE_SQL_I
end

View File

@@ -0,0 +1,91 @@
note
description: "[
Common ancestor for builders responsible to instantiate storage based
on SQL statement storage.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE_SQL_BUILDER
inherit
CMS_STORAGE_BUILDER
feature -- Initialization
initialize (a_setup: CMS_SETUP; a_storage: CMS_STORAGE_SQL)
local
u: CMS_USER
l_anonymous_role, l_authenticated_role, r: CMS_USER_ROLE
l_roles: LIST [CMS_USER_ROLE]
do
--| Schema
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql"))
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("user.sql"))
--| Roles
create l_anonymous_role.make ("anonymous")
a_storage.save_user_role (l_anonymous_role)
create l_authenticated_role.make ("authenticated")
a_storage.save_user_role (l_authenticated_role)
--| Users
create u.make ("admin")
u.set_password ("istrator#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
--| Node
-- FIXME: move that initialization to node module
-- TODO: should we move the initialization to an
--! external configuration file?
--! at the moment we only have 1 admin to the whole site.
--! is that ok?
l_anonymous_role.add_permission ("view any page")
a_storage.save_user_role (l_anonymous_role)
l_authenticated_role.add_permission ("create page")
l_authenticated_role.add_permission ("view any page")
l_authenticated_role.add_permission ("edit any page")
l_authenticated_role.add_permission ("delete page")
l_authenticated_role.add_permission ("trash page")
l_authenticated_role.add_permission ("view own page")
l_authenticated_role.add_permission ("edit own page")
l_authenticated_role.add_permission ("delete own page")
l_authenticated_role.add_permission ("trash own page")
a_storage.save_user_role (l_authenticated_role)
--|-------------------------------------------|--
--| For testing purpose, to be removed later. |--
--|-------------------------------------------|--
-- Roles, view role for testing.
create r.make ("view")
r.add_permission ("view any page")
a_storage.save_user_role (r)
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1)
l_roles.force (r)
create u.make ("auth")
u.set_password ("enticated#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
create u.make ("test")
u.set_password ("test#")
u.set_email (a_setup.site_email)
a_storage.new_user (u)
create u.make ("view")
u.set_password ("only#")
u.set_email (a_setup.site_email)
u.set_roles (l_roles)
a_storage.new_user (u)
end
end

View File

@@ -217,6 +217,21 @@ feature -- Access
deferred deferred
end end
sql_read_natural_64 (a_index: INTEGER): NATURAL_64
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {NATURAL_64} l_item as i then
Result := i
elseif attached {NATURAL_64_REF} l_item as l_value then
Result := l_value.item
else
Result := sql_read_integer_64 (a_index).to_natural_64
end
end
sql_read_integer_64 (a_index: INTEGER): INTEGER_64 sql_read_integer_64 (a_index: INTEGER): INTEGER_64
-- Retrieved value at `a_index' position in `item'. -- Retrieved value at `a_index' position in `item'.
local local

View File

@@ -0,0 +1,79 @@
note
description: "Summary description for {CMS_USER_STORAGE_NULL}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_USER_STORAGE_NULL
inherit
CMS_USER_STORAGE_I
feature -- Access: user
has_user: BOOLEAN
-- Has any user?
do
end
users: LIST [CMS_USER]
do
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
do
end
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
do
end
user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
do
end
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
do
end
feature -- Change: user
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
do
a_user.set_id (1)
end
update_user (a_user: CMS_USER)
-- Update user `a_user'.
do
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
do
end
user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE]
-- User roles for user `a_user'.
-- Note: anonymous and authenticated roles are not included.
do
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
end
user_roles: LIST [CMS_USER_ROLE]
do
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
do
end
end

View File

@@ -148,17 +148,15 @@ feature -- Permissions system
feature -- Query: module feature -- Query: module
module (a_type: TYPE [detachable CMS_MODULE]): detachable CMS_MODULE module (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE
-- Enabled module typed `a_type', if any. -- Enabled module typed `a_type', if any.
--| usage: if attached module ({FOO_MODULE}) as mod then ... --| usage: if attached module ({FOO_MODULE}) as mod then ...
local local
t: STRING_8 -- t: STRING_8
l_type: TYPE [detachable CMS_MODULE] l_type: TYPE [detachable CMS_MODULE]
do do
t := a_type.name -- t := type_name_without_annotation (a_type)
if t.starts_with ("!") then
t.remove_head (1)
end
across across
setup.modules as ic setup.modules as ic
until until
@@ -171,8 +169,12 @@ feature -- Query: module
l_type := Result.generating_type l_type := Result.generating_type
if a_type ~ l_type then if a_type ~ l_type then
-- Found -- Found
elseif t.same_string (l_type.name) then elseif
attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type)
then
-- Found -- Found
-- elseif t.same_string (type_name_without_annotation (l_type)) then
-- -- Found
else else
Result := Void Result := Void
end end
@@ -186,9 +188,14 @@ feature -- Query: module
-- Enabled module API associated with module typed `a_type'. -- Enabled module API associated with module typed `a_type'.
do do
if attached module (a_type) as mod then if attached module (a_type) as mod then
if mod.is_enabled then
if not mod.is_initialized then
mod.initialize (Current)
end
Result := mod.module_api Result := mod.module_api
end end
end end
end
module_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_MODULE module_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_MODULE
-- Enabled module named `a_name', if any. -- Enabled module named `a_name', if any.
@@ -232,6 +239,73 @@ feature -- Query: API
Result := l_api Result := l_api
end end
feature -- Path aliases
is_valid_path_alias (a_alias: READABLE_STRING_8): BOOLEAN
do
Result := a_alias.is_empty or else not a_alias.starts_with_general ("/")
end
set_path_alias (a_source, a_alias: READABLE_STRING_8; a_keep_previous: BOOLEAN)
-- Set `a_alias' as alias of `a_source',
-- and eventually unset previous alias if any.
require
valid_alias: is_valid_path_alias (a_alias)
local
l_continue: BOOLEAN
do
if attached storage.path_alias (a_source) as l_existing_alias then
if a_alias.same_string (l_existing_alias) then
-- Already aliased as expected
else
-- New alias
if a_keep_previous then
l_continue := True
else
storage.replace_path_alias (a_source, l_existing_alias, a_alias)
end
end
elseif a_alias.is_whitespace then
-- Ignore
elseif a_source.same_string (a_alias) then
-- No need for alias
else
l_continue := True
end
if l_continue then
storage.set_path_alias (a_source, a_alias)
end
end
unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
do
storage.unset_path_alias (a_source, a_alias)
end
path_alias (a_source: READABLE_STRING_8): READABLE_STRING_8
-- Path alias associated with `a_source' or the source itself.
do
Result := a_source
if attached storage.path_alias (Result) as l_path then
Result := "/" + l_path
end
end
source_of_path_alias (a_alias: READABLE_STRING_8): READABLE_STRING_8
-- Resolved path for alias `a_alias'.
--| the CMS supports aliases for path, and then this function simply returns
--| the effective target path/url for this `a_alias'.
--| For instance: articles/2015/may/this-is-an-article can be an alias to node/123
--| This function will return "node/123".
--| If the alias is bad (i.e does not alias real path), then this function
--| returns the alias itself.
do
Result := a_alias
if attached storage.source_of_path_alias (Result) as l_path then
Result := l_path
end
end
feature -- Element Change: Error feature -- Element Change: Error
reset_error reset_error
@@ -248,6 +322,41 @@ feature {NONE}-- Implemenation
internal_user_api: detachable like user_api internal_user_api: detachable like user_api
-- Cached value for `user_api'. -- Cached value for `user_api'.
type_name_without_annotation (a_type: TYPE [detachable ANY]): STRING
-- Type name for `a_type, without any annotation.
-- Used by `module' to search by type.
local
i,j,n: INTEGER
c: CHARACTER
do
create Result.make_from_string (a_type.name)
from
i := 1
n := Result.count
until
i > n
loop
c := Result[i]
if c = '!' or c = '?' then
Result.remove (i)
n := n - 1
elseif c.is_lower then
j := Result.index_of (' ', i + 1)
if j > 0 then
Result.remove_substring (i, j)
n := n - (j - i)
end
else
i := i + 1
end
end
if Result.starts_with ("!") or Result.starts_with ("?") then
Result.remove_head (1)
elseif Result.starts_with ("detachable ") then
Result.remove_head (11)
end
end
feature -- Environment feature -- Environment
module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER

View File

@@ -14,4 +14,10 @@ create
make, make,
make_from_manifest make_from_manifest
convert
make_from_manifest ({ ARRAY [TUPLE [key: STRING; value: detachable ANY]],
ARRAY [TUPLE [STRING_8, ARRAY [TUPLE [STRING_8, STRING_32]]]],
ARRAY [TUPLE [STRING_8, ARRAY [TUPLE [STRING_8, STRING_8]]]]
})
end end

View File

@@ -6,54 +6,56 @@ note
even for a specific handler. even for a specific handler.
]" ]"
class deferred class
CMS_SERVICE CMS_EXECUTION
inherit inherit
WSF_ROUTED_SKELETON_SERVICE WSF_FILTERED_ROUTED_SKELETON_EXECUTION
rename
execute as execute_service
undefine undefine
requires_proxy requires_proxy
redefine redefine
execute_default create_router, router,
end execute_default,
filter_execute,
WSF_FILTERED_SERVICE initialize
WSF_FILTER
rename
execute as execute_filter
end end
WSF_NO_PROXY_POLICY WSF_NO_PROXY_POLICY
WSF_URI_HELPER_FOR_ROUTED_SERVICE WSF_URI_HELPER_FOR_ROUTED_EXECUTION
WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION
REFACTORING_HELPER REFACTORING_HELPER
SHARED_LOGGER SHARED_LOGGER
create --create
make -- make
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_setup: CMS_SETUP)
-- Build a CMS service with `a_api'
do
create api.make (a_setup)
initialize initialize
-- Build a CMS service with `a_api'
local
l_setup: CMS_SETUP
do
l_setup := initial_cms_setup
setup_storage (l_setup)
setup_modules (l_setup)
create api.make (l_setup)
modules := setup.enabled_modules
initialize_cms
Precursor
end end
initialize initialize_cms
-- Initialize various parts of the CMS service.
do do
initialize_modules write_debug_log (generator + ".initialize_cms")
initialize_users
initialize_mailer -- CMS Initialization
-- initialize_router -- initialize_router
-- initialize_filter: expanded here, for void-safety concern. -- initialize_filter: expanded here, for void-safety concern.
create_filter create_filter
@@ -69,23 +71,51 @@ feature {NONE} -- Initialization
only_enabled_modules: across modules as ic all ic.item.is_enabled end only_enabled_modules: across modules as ic all ic.item.is_enabled end
end end
initialize_users feature -- Factory
-- Initialize users.
do initial_cms_setup: CMS_SETUP
deferred
end end
initialize_mailer feature -- Access
-- Initialize mailer engine.
api: CMS_API
-- API service.
setup: CMS_SETUP
-- CMS Setup.
do do
to_implement ("To Implement mailer") Result := api.setup
end
modules: CMS_MODULE_COLLECTION
-- Configurator of possible modules.
feature -- CMS setup
setup_modules (a_setup: CMS_SETUP)
-- Setup additional modules.
deferred
end
setup_storage (a_setup: CMS_SETUP)
deferred
end end
feature -- Settings: router feature -- Settings: router
router: CMS_ROUTER
-- <Precursor>
create_router
-- Create `router'.
do
create router.make (api, 30)
end
setup_router setup_router
-- <Precursor> -- <Precursor>
local local
l_module: CMS_MODULE
l_api: like api l_api: like api
l_router: like router l_router: like router
do do
@@ -100,8 +130,7 @@ feature -- Settings: router
across across
modules as ic modules as ic
loop loop
l_module := ic.item ic.item.setup_router (l_router, l_api)
l_router.import (l_module.router (l_api))
end end
-- Configure files handler. -- Configure files handler.
configure_api_file_handler (l_router) configure_api_file_handler (l_router)
@@ -116,9 +145,9 @@ feature -- Settings: router
create l_root_handler.make (api) create l_root_handler.make (api)
create l_methods create l_methods
l_methods.enable_get l_methods.enable_get
a_router.handle_with_request_methods ("/", l_root_handler, l_methods) a_router.handle ("/", l_root_handler, l_methods)
a_router.handle_with_request_methods ("", l_root_handler, l_methods) a_router.handle ("", l_root_handler, l_methods)
map_uri_agent_with_request_methods ("/favicon.ico", agent handle_favicon, a_router.methods_head_get) map_uri_agent ("/favicon.ico", agent handle_favicon, a_router.methods_head_get)
end end
configure_api_file_handler (a_router: WSF_ROUTER) configure_api_file_handler (a_router: WSF_ROUTER)
@@ -133,7 +162,7 @@ feature -- Settings: router
do do
execute_default (ia_req, ia_res) execute_default (ia_req, ia_res)
end) end)
a_router.handle_with_request_methods ("/theme/", fhdl, router.methods_GET) a_router.handle ("/theme/", fhdl, router.methods_GET)
create fhdl.make_hidden_with_path (setup.environment.www_path) create fhdl.make_hidden_with_path (setup.environment.www_path)
@@ -142,17 +171,17 @@ feature -- Settings: router
do do
execute_default (ia_req, ia_res) execute_default (ia_req, ia_res)
end) end)
a_router.handle_with_request_methods ("/", fhdl, router.methods_GET) a_router.handle ("/", fhdl, router.methods_GET)
end end
feature -- Execute Filter feature -- Execute Filter
execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE) filter_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter. -- Execute the filter.
do do
res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string) res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string)
res.put_header_line ("X-EWF-Server: CMS_v1.0") res.put_header_line ("X-EWF-Server: CMS_v1.0")
execute_service (req, res) Precursor (req, res)
end end
feature -- Filters feature -- Filters
@@ -172,10 +201,10 @@ feature -- Filters
f.set_next (l_filter) f.set_next (l_filter)
l_filter := f l_filter := f
-- Error Filter -- -- Error Filter
create {CMS_ERROR_FILTER} f.make (api) -- create {CMS_ERROR_FILTER} f.make (api)
f.set_next (l_filter) -- f.set_next (l_filter)
l_filter := f -- l_filter := f
-- Include filters from modules -- Include filters from modules
l_api := api l_api := api
@@ -215,20 +244,6 @@ feature -- Filters
f.set_next (Current) f.set_next (Current)
end end
feature -- Access
api: CMS_API
-- API service.
setup: CMS_SETUP
-- CMS setup.
do
Result := api.setup
end
modules: CMS_MODULE_COLLECTION
-- Configurator of possible modules.
feature -- Execution feature -- Execution
handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE) handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE)

View File

@@ -32,44 +32,57 @@ feature {CMS_API} -- Module Initialization
-- Initialize Current module with `api'. -- Initialize Current module with `api'.
require require
is_enabled: is_enabled is_enabled: is_enabled
is_not_initialized: not is_initialized
do do
-- Redefine to process specific module initialization. -- Redefine to process specific module initialization.
is_initialized := True
ensure
is_initialized: is_initialized
end end
feature -- Status
is_initialized: BOOLEAN
-- Is Current module initialized?
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API
module_api: detachable CMS_MODULE_API module_api: detachable CMS_MODULE_API
-- Eventual module api. -- Eventual module api.
require
is_initialized: is_initialized
do
-- No API by default.
end
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed? -- Is Current module installed?
do do
Result := is_enabled Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes")
-- FIXME: implement proper installation status.
end end
install (api: CMS_API) install (api: CMS_API)
require require
is_not_installed: not is_installed (api) is_not_installed: not is_installed (api)
do do
-- Not Yet Supported api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
end end
uninstall (api: CMS_API) uninstall (api: CMS_API)
require require
is_installed: is_installed (api) is_installed: is_installed (api)
do do
-- Not Yet Supported api.storage.set_custom_value ("is_initialized", "module-" + name, "no")
end end
feature -- Router feature -- Router
router (a_api: CMS_API): WSF_ROUTER setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration. -- Setup url dispatching for Current module.
require require
is_enabled: is_enabled is_initialized: is_initialized
deferred deferred
end end

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {CMS_MODULE_API}." description: "Summary description for {CMS_MODULE_API}."
author: ""
date: "$Date: 2015-02-13 14:54:27 +0100 (ven., 13 févr. 2015) $" date: "$Date: 2015-02-13 14:54:27 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96620 $" revision: "$Revision: 96620 $"

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {CMS_MODULE_COLLECTION}." description: "Collection of CMS modules."
author: ""
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $" date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $" revision: "$Revision: 96596 $"

View File

@@ -19,15 +19,19 @@ feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter -- Execute the filter
local
utf: UTF_CONVERTER
do do
fixme ("Check if it's ok to add new fetures CMS_API.has_error:BOOLEAN and CMS_API.error_description.") debug ("refactor_fixme")
fixme ("Check if it's ok to add new features CMS_API.has_error:BOOLEAN and CMS_API.error_description.")
end
if not api.has_error then if not api.has_error then
api.logger.put_information (generator + ".execute with req: " + req.debug_output, Void) api.logger.put_information (generator + ".execute with req: " + req.debug_output, Void)
if attached req.raw_header_data as l_header_data then if attached req.raw_header_data as l_header_data then
api.logger.put_debug (generator + ".execute with req header: " + l_header_data, Void) api.logger.put_debug (generator + ".execute with req header: " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_header_data), Void)
end end
if attached req.raw_input_data as l_input_data then if attached req.raw_input_data as l_input_data then
api.logger.put_debug (generator + ".execute with req input: " + l_input_data, Void) api.logger.put_debug (generator + ".execute with req input: " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_input_data), Void)
end end
execute_next (req, res) execute_next (req, res)
else else

View File

@@ -36,6 +36,18 @@ feature -- Response helpers
-- res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (a_location)) -- res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (a_location))
end end
send_bad_request_message (res: WSF_RESPONSE)
-- Send via `res' a bad request response.
do
res.send (create {CMS_CUSTOM_RESPONSE_MESSAGE}.make ({HTTP_STATUS_CODE}.bad_request))
end
send_not_found_message (res: WSF_RESPONSE)
-- Send via `res' a bad request response.
do
res.send (create {CMS_CUSTOM_RESPONSE_MESSAGE}.make ({HTTP_STATUS_CODE}.not_found))
end
send_access_denied_message (res: WSF_RESPONSE) send_access_denied_message (res: WSF_RESPONSE)
-- Send via `res' an access denied response. -- Send via `res' an access denied response.
do do

View File

@@ -70,14 +70,4 @@ feature -- Media Type
end end
end end
feature -- Absolute Host
absolute_host (req: WSF_REQUEST; a_path:STRING): STRING
do
Result := req.absolute_script_url (a_path)
if Result.last_index_of ('/', Result.count) = Result.count then
Result.remove_tail (1)
end
end
end end

View File

@@ -13,6 +13,7 @@ inherit
feature -- Core feature -- Core
site_url: READABLE_STRING_8 site_url: READABLE_STRING_8
-- Absolute site URL of Current CMS site.
deferred deferred
end end

View File

@@ -0,0 +1,52 @@
note
description: "Specific version of WSF_ROUTER for CMS component."
date: "$Date$"
revision: "$Revision$"
class
CMS_ROUTER
inherit
WSF_ROUTER
rename
make as make_router
redefine
path_to_dispatch
end
create
make
feature {NONE} -- Initialization
make (a_api: CMS_API; a_capacity: INTEGER)
do
api := a_api
make_router (a_capacity)
end
api: CMS_API
feature {WSF_ROUTER_MAPPING} -- Dispatch helper
path_to_dispatch (req: WSF_REQUEST): READABLE_STRING_8
-- Path used by the router, to apply url dispatching of request `req'.
local
l_path: STRING_8
do
create l_path.make_from_string (Precursor (req))
if not l_path.is_empty and l_path [1] = '/' then
l_path.remove_head (1)
end
Result := api.source_of_path_alias (l_path)
if Result.is_empty then
Result := "/"
elseif Result [1] /= '/' then
create l_path.make (Result.count + 1)
l_path.append_character ('/')
l_path.append (Result)
Result := l_path
end
end
end

View File

@@ -3,8 +3,8 @@ note
Generic CMS Response. Generic CMS Response.
It builds the content to get process to render the output. It builds the content to get process to render the output.
]" ]"
date: "$Date: 2015-02-16 20:14:19 +0100 (lun., 16 févr. 2015) $" date: "$Date: 2015-05-20 11:48:26 +0200 (mer., 20 mai 2015) $"
revision: "$Revision: 96643 $" revision: "$Revision: 97327 $"
deferred class deferred class
CMS_RESPONSE CMS_RESPONSE
@@ -29,6 +29,7 @@ feature {NONE} -- Initialization
initialize initialize
do do
initialize_site_url
get_theme get_theme
create menu_system.make create menu_system.make
initialize_block_region_settings initialize_block_region_settings
@@ -36,6 +37,35 @@ feature {NONE} -- Initialization
register_hooks register_hooks
end end
initialize_site_url
-- Initialize site and base url.
local
l_url: detachable STRING_8
i,j: INTEGER
do
--| WARNING: do not use `absolute_url' and `url', since it relies on site_url and base_url.
if attached setup.site_url as l_site_url and then not l_site_url.is_empty then
create l_url.make_from_string (l_site_url)
else
l_url := request.absolute_script_url ("/")
end
check is_not_empty: not l_url.is_empty end
if l_url [l_url.count] /= '/' then
l_url.append_character ('/')
end
site_url := l_url
i := l_url.substring_index ("://", 1)
if i > 0 then
j := l_url.index_of ('/', i + 3)
if j > 0 then
base_url := l_url.substring (j, l_url.count)
end
end
ensure
site_url_set: site_url /= Void
site_url_ends_with_slash: site_url.ends_with_general ("/")
end
register_hooks register_hooks
local local
l_module: CMS_MODULE l_module: CMS_MODULE
@@ -76,6 +106,35 @@ feature -- Access
redirection: detachable READABLE_STRING_8 redirection: detachable READABLE_STRING_8
-- Location for eventual redirection. -- Location for eventual redirection.
location: STRING_8
-- Associated cms local location.
do
create Result.make_from_string (request.percent_encoded_path_info)
if not Result.is_empty and then Result[1] = '/' then
Result.remove_head (1)
end
end
feature -- Internationalization (i18n)
translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32
-- Translated text `a_text' according to expected context (lang, ...)
-- and adapt according to options eventually set by `opts'.
do
to_implement ("Implement i18n support [2015-may]")
Result := a_text.as_string_32
end
formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32
-- Format `a_text' using arguments `args'.
--| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact"
local
l_formatter: CMS_STRING_FORMATTER
do
create l_formatter
Result := l_formatter.formatted_string (a_text, args)
end
feature -- API feature -- API
api: CMS_API api: CMS_API
@@ -101,9 +160,11 @@ feature -- Module
rp: PATH rp: PATH
ut: FILE_UTILITIES ut: FILE_UTILITIES
do do
-- Check first in selected theme folder.
rp := module_assets_theme_location (a_module) rp := module_assets_theme_location (a_module)
Result := rp.extended_path (a_resource) Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then if not ut.file_path_exists (Result) then
-- And if not found, look into site/modules/$a_module.name/.... folders.
rp := module_assets_location (a_module) rp := module_assets_location (a_module)
Result := rp.extended_path (a_resource) Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then if not ut.file_path_exists (Result) then
@@ -143,16 +204,13 @@ feature -- URL utilities
end end
end end
site_url: READABLE_STRING_8 site_url: IMMUTABLE_STRING_8
do -- Absolute site url.
Result := absolute_host (request, "")
end
base_url: detachable READABLE_STRING_8 base_url: detachable IMMUTABLE_STRING_8
-- Base url if any. -- Base url if any.
--| Usually it is Void, but it could be --| Usually it is Void, but it could be
--| /project/demo/ --| /project/demo/
--| FIXME: for now, no way to change that. Always at the root "/"
feature -- Access: CMS feature -- Access: CMS
@@ -163,7 +221,7 @@ feature -- Access: CMS
front_page_url: READABLE_STRING_8 front_page_url: READABLE_STRING_8
do do
Result := request.absolute_script_url ("/") Result := absolute_url ("/", Void)
end end
values: CMS_VALUE_TABLE values: CMS_VALUE_TABLE
@@ -185,12 +243,30 @@ feature -- Permission
Result := user_has_permission (current_user (request), a_permission) Result := user_has_permission (current_user (request), a_permission)
end end
has_permissions (a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
-- Does current user has any of the permissions `a_permission_list' ?
do
Result := user_has_permissions (current_user (request), a_permission_list)
end
user_has_permission (a_user: detachable CMS_USER; a_permission: READABLE_STRING_GENERAL): BOOLEAN user_has_permission (a_user: detachable CMS_USER; a_permission: READABLE_STRING_GENERAL): BOOLEAN
-- Does `a_user' has permission `a_permission' ? -- Does `a_user' has permission `a_permission' ?
do do
Result := api.user_has_permission (a_user, a_permission) Result := api.user_has_permission (a_user, a_permission)
end end
user_has_permissions (a_user: detachable CMS_USER; a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
-- Does `a_user' has any of the permissions `a_permission_list' ?
do
across
a_permission_list as ic
until
Result
loop
Result := user_has_permission (a_user, ic.item)
end
end
feature -- Head customization feature -- Head customization
add_additional_head_line (s: READABLE_STRING_8; a_allow_duplication: BOOLEAN) add_additional_head_line (s: READABLE_STRING_8; a_allow_duplication: BOOLEAN)
@@ -768,9 +844,9 @@ feature -- Theme
create l_info.make_default create l_info.make_default
end end
if l_info.engine.is_case_insensitive_equal_general ("smarty") then if l_info.engine.is_case_insensitive_equal_general ("smarty") then
create {SMARTY_CMS_THEME} theme.make (setup, l_info) create {SMARTY_CMS_THEME} theme.make (setup, l_info, site_url)
else else
create {MISSING_CMS_THEME} theme.make (setup) create {MISSING_CMS_THEME} theme.make (setup, l_info, site_url)
status_code := {HTTP_STATUS_CODE}.service_unavailable status_code := {HTTP_STATUS_CODE}.service_unavailable
to_implement ("Check how to add the Retry-after, http://tools.ietf.org/html/rfc7231#section-6.6.4 and http://tools.ietf.org/html/rfc7231#section-7.1.3") to_implement ("Check how to add the Retry-after, http://tools.ietf.org/html/rfc7231#section-6.6.4 and http://tools.ietf.org/html/rfc7231#section-7.1.3")
end end
@@ -796,7 +872,7 @@ feature -- Generation
lnk: CMS_LINK lnk: CMS_LINK
do do
-- Menu -- Menu
create {CMS_LOCAL_LINK} lnk.make ("Home", "/") create {CMS_LOCAL_LINK} lnk.make ("Home", "")
lnk.set_weight (-10) lnk.set_weight (-10)
add_to_primary_menu (lnk) add_to_primary_menu (lnk)
invoke_menu_system_alter (menu_system) invoke_menu_system_alter (menu_system)
@@ -882,8 +958,8 @@ feature -- Generation
end end
-- Variables -- Variables
page.register_variable (request.absolute_script_url (""), "site_url") page.register_variable (absolute_url ("", Void), "site_url")
page.register_variable (request.absolute_script_url (""), "host") -- Same as `site_url'. page.register_variable (absolute_url ("", Void), "host") -- Same as `site_url'.
page.register_variable (request.is_https, "is_https") page.register_variable (request.is_https, "is_https")
if attached current_user_name (request) as l_user then if attached current_user_name (request) as l_user then
page.register_variable (l_user, "user") page.register_variable (l_user, "user")
@@ -991,6 +1067,9 @@ feature -- Generation
l_is_active: BOOLEAN l_is_active: BOOLEAN
do do
create qs.make_from_string (request.percent_encoded_path_info) create qs.make_from_string (request.percent_encoded_path_info)
if qs.starts_with ("/") then
qs.remove_head (1)
end
l_is_active := qs.same_string (a_lnk.location) l_is_active := qs.same_string (a_lnk.location)
if not l_is_active then if not l_is_active then
if attached request.query_string as l_query_string and then not l_query_string.is_empty then if attached request.query_string as l_query_string and then not l_query_string.is_empty then
@@ -1064,8 +1143,8 @@ feature {NONE} -- Execution
-- h.put_location (l_location) -- h.put_location (l_location)
response.redirect_now (l_location) response.redirect_now (l_location)
else else
-- h.put_location (request.absolute_script_url (l_location)) -- h.put_location (request.absolute_url (l_location, Void))
response.redirect_now (request.absolute_script_url (l_location)) response.redirect_now (absolute_url (l_location, Void))
end end
else else
h.put_header_object (header) h.put_header_object (header)

View File

@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
page.register_variable (request.absolute_script_url (request.path_info), "request") page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.bad_request) page.set_status_code ({HTTP_STATUS_CODE}.bad_request)
page.register_variable (page.status_code.out, "code") page.register_variable (page.status_code.out, "code")
end end

View File

@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
page.register_variable (request.absolute_script_url (request.path_info), "request") page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.forbidden) page.set_status_code ({HTTP_STATUS_CODE}.forbidden)
page.register_variable (page.status_code.out, "code") page.register_variable (page.status_code.out, "code")
end end

View File

@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
page.register_variable (request.absolute_script_url (request.path_info), "request") page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) page.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
page.register_variable (page.status_code.out, "code") page.register_variable (page.status_code.out, "code")
end end

View File

@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
page.register_variable (request.absolute_script_url (request.path_info), "request") page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_found) page.set_status_code ({HTTP_STATUS_CODE}.not_found)
page.register_variable (page.status_code.out, "code") page.register_variable (page.status_code.out, "code")
end end

View File

@@ -20,7 +20,7 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
page.register_variable (request.absolute_script_url (request.path_info), "request") page.register_variable (absolute_url (request.path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_implemented) page.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
page.register_variable (page.status_code.out, "code") page.register_variable (page.status_code.out, "code")
end end

View File

@@ -0,0 +1,64 @@
note
description: "Custom cms response message."
date: "$Date$"
revision: "$Revision$"
class
CMS_CUSTOM_RESPONSE_MESSAGE
inherit
CMS_RESPONSE_MESSAGE
redefine
send_payload_to
end
create
make
feature {NONE} -- Initialization
make (a_code: INTEGER)
-- Set `status_code' to `a_code'.
require
a_code_valid: a_code > 0
do
initialize
status_code := a_code
end
feature -- Access
payload: detachable READABLE_STRING_8
-- Optional payload.
feature -- Element change
set_status_code (a_code: INTEGER)
-- Set `status_code' to `a_code'.
require
a_code_valid: a_code > 0
do
status_code := a_code
end
set_payload (s: detachable READABLE_STRING_8)
-- Set `payload' to `s'.
do
if s /= Void then
payload := s
header.put_content_length (s.count)
else
end
end
feature {WSF_RESPONSE} -- Output
send_payload_to (res: WSF_RESPONSE)
-- Send payload data to response `res'.
do
if attached payload as s then
res.put_string (s)
end
end
end

View File

@@ -0,0 +1,284 @@
note
description: "Pagination class to generate html pagination links and header summary."
date: "$Date$"
revision: "$Revision$"
class
CMS_PAGINATION_GENERATOR
create
make
feature {NONE} -- Initialization
make (a_resource: READABLE_STRING_8; a_count: NATURAL_64; a_page_size: NATURAL)
-- Create an object with a pages of size `a_page_size'.
-- If `a_page_size' is zero, use default pagination size.
require
a_page_size > 0
do
create resource.make (a_resource)
set_page_size (a_page_size)
set_upper (a_count)
set_current_page_index (1)
maximum_ith_page_links := 7
page_parameter_id := "page"
size_parameter_id := "size"
set_first_text_id ("<<")
set_prev_text_id ("<")
set_next_text_id (">")
set_last_text_id (">>")
end
feature -- Access
resource: URI_TEMPLATE
-- Resource associated with current pager.
page_size: NATURAL
-- Number of items per page.
upper: NATURAL_64
-- number of items.
-- if zero, no upper limit.
current_page_index: INTEGER
-- Current page index.
current_page_offset: NATURAL_64
-- Lower index - 1 for current page.
do
if current_page_index > 1 then
Result := (current_page_index - 1).to_natural_32 * page_size
else
Result := 0
end
end
feature -- Status report
has_upper_limit: BOOLEAN
-- Upper limit known?
do
Result := upper > 0
end
pages_count: INTEGER
-- Number of pages.
-- If upper is
require
has_upper_limit: has_upper_limit
do
Result := (upper // page_size.as_natural_64).to_integer_32
if upper \\ page_size.to_natural_64 > 0 then
Result := Result + 1
end
end
feature -- Parameters
page_parameter_id: STRING
-- Parameter id for page value.
size_parameter_id: STRING
-- Parameter id for size value.
maximum_ith_page_links: INTEGER
-- Maximum number of numeric ith page link.
-- ex: max = 6 gives "1 2 3 4 5 6 ... > >>".
label_first: IMMUTABLE_STRING_32
label_previous: IMMUTABLE_STRING_32
label_next: IMMUTABLE_STRING_32
label_last: IMMUTABLE_STRING_32
feature -- Element change
set_page_size (a_size: NATURAL)
-- Set `page_size' to `a_size'.
do
page_size := a_size
end
set_upper (a_upper: NATURAL_64)
-- Set pages count, or upper limit `upper' to `a_size'.
do
upper := a_upper
end
set_current_page_index (a_page_index: like current_page_index)
-- Set Current page index to `a_page_index'.
do
current_page_index := a_page_index
end
set_page_parameter_id (a_id: READABLE_STRING_8)
-- Set "page" query parameter to `a_id'.
do
page_parameter_id := a_id
end
set_size_parameter_id (a_id: READABLE_STRING_8)
-- Set "size" query parameter to `a_id'.
do
size_parameter_id := a_id
end
get_setting_from_request (req: WSF_REQUEST)
-- Get various pager related settings from request `req' query paramenters.
-- Using `page_parameter_id' and `size_parameter_id' value for parameter names.
do
-- Size
if
attached {WSF_STRING} req.query_parameter (size_parameter_id) as l_size and then
attached l_size.value as l_value and then
l_value.is_natural
then
set_page_size (l_value.to_natural_32)
else
-- Keep default size
end
-- Page
if
attached {WSF_STRING} req.query_parameter (page_parameter_id) as l_page and then
l_page.is_integer
then
set_current_page_index (l_page.integer_value)
else
set_current_page_index (1)
end
end
set_first_text_id (s: READABLE_STRING_GENERAL)
-- Set label for "First" link to `s'.
-- default: "<<"
do
create label_first.make_from_string_general (s)
end
set_prev_text_id (s: READABLE_STRING_GENERAL)
-- Set label for "Prev" link to `s'.
-- default: "<"
do
create label_previous.make_from_string_general (s)
end
set_next_text_id (s: READABLE_STRING_GENERAL)
-- Set label for "Next" link to `s'.
-- default: ">"
do
create label_next.make_from_string_general (s)
end
set_last_text_id (s: READABLE_STRING_GENERAL)
-- Set label for "Last" link to `s'.
-- default: ">>"
do
create label_last.make_from_string_general (s)
end
set_maximum_ith_page_links (nb: INTEGER)
-- Set `maximum_ith_page_links' to `nb'.
do
maximum_ith_page_links := nb
end
feature -- Conversion
pagination_links: ARRAYED_LIST [CMS_LOCAL_LINK]
-- CMS local links related to Current paginations.
local
lnk: CMS_LOCAL_LINK
tb: HASH_TABLE [detachable ANY, STRING_8]
curr, max: INTEGER
i,j: INTEGER
do
create Result.make (maximum_ith_page_links)
curr := current_page_index
if has_upper_limit then
max := pages_count
else
max := curr + page_size.to_integer_32
end
create tb.make (2)
tb.force (page_size, size_parameter_id)
tb.force (1, page_parameter_id)
if curr > 1 then
create lnk.make (label_first, resource.expanded_string (tb))
Result.force (lnk)
end
if curr > 1 then
tb.force (curr - 1, "page")
create lnk.make (label_previous, resource.expanded_string (tb))
Result.force (lnk)
end
from
if curr >= maximum_ith_page_links // 2 then
i := curr - (maximum_ith_page_links // 2)
else
i := 1
end
j := 0
until
j >= maximum_ith_page_links or (has_upper_limit and then i > max)
loop
tb.force (i, "page")
create lnk.make (i.out, resource.expanded_string (tb))
lnk.set_is_active (i = curr)
Result.force (lnk)
j := j + 1
i := i + 1
end
if not has_upper_limit or else i < max then
tb.force (i, "page")
create lnk.make ("...", resource.expanded_string (tb))
Result.force (lnk)
end
if curr < max then
tb.force (curr + 1, "page")
create lnk.make (label_next, resource.expanded_string (tb))
Result.force (lnk)
end
if upper > 0 and curr /= max then
tb.force (max, "page")
create lnk.make (label_last, resource.expanded_string (tb))
Result.force (lnk)
end
end
feature -- Convertion
append_to_html (a_response: CMS_RESPONSE; a_output: STRING)
-- Append html pager to `a_output' in the context of `a_response'.
-- note: First, [Prev], [Next], Last.
local
lnk: CMS_LOCAL_LINK
do
a_output.append ("<ul class=%"pagination%">%N")
across
pagination_links as ic
loop
lnk := ic.item
if lnk.is_active then
a_output.append ("<li class=%"active%">")
elseif lnk.title.same_string (label_previous) then
a_output.append ("<li class=%"previous%">")
elseif lnk.title.same_string (label_next) then
a_output.append ("<li class=%"next%">")
else
a_output.append ("<li>")
end
a_output.append (a_response.link (lnk.title, lnk.location, Void))
a_output.append ("</li>")
end
a_output.append ("</ul>")
end
end

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