Compare commits

...

120 Commits

Author SHA1 Message Date
0813abe0bb Fixed ROC CMS library compilation. 2015-12-18 15:29:43 +01:00
1094acb3ec Removed unused local variable 2015-12-16 21:05:57 +01:00
e7c9a54f3f Removed unused local. 2015-12-16 21:03:15 +01:00
bbbdac12c8 Moved taxonomy html generation to CMS_TAXONOMY_API. 2015-12-16 21:03:03 +01:00
jvelilla
22528315cb Removed unneeded file. 2015-12-16 16:01:02 +01:00
jvelilla
090a48eb85 Updated class CMS_TOKEN_GENERATOR.
Remove once in sha1 feature.
Updated encoded_base_64 to base_64
2015-12-16 16:01:01 +01:00
jvelilla
e05c4dca3a Fixed typos
Renamed class CMS_SESSION_CONSTANT to CMS_SESSION_CONSTANTS
Removed unneeded classes and files.
Update SQL implementation.
2015-12-16 16:00:59 +01:00
jvelilla
2255fcc0f6 Added Module Session Authentication with Cookies.
Updated Demo example with the Module Session (Authentication with Cookies)
Fixed little issue with SQL query in OpenID module.
2015-12-16 16:00:58 +01:00
e50fb6959e Moved taxonomy integration for web form inside CMS_TAXONOMY_API.
Moved a few helpers routine from CMS_RESPONSE to CMS_API.
Added CMS_CONTENT.identifier: detachable READABLE_STRING_32 .
2015-12-16 15:59:22 +01:00
jvelilla
3b88c746a1 Removed unneeded file. 2015-12-16 10:43:21 -03:00
jvelilla
fa8ef44a4a Merge branch 'jvelilla-roc_login_session' 2015-12-16 10:11:55 -03:00
jvelilla
068943734f Updated class CMS_TOKEN_GENERATOR.
Remove once in sha1 feature.
Updated encoded_base_64 to base_64
2015-12-16 10:03:35 -03:00
jvelilla
089179e60e Fixed typos
Renamed class CMS_SESSION_CONSTANT to CMS_SESSION_CONSTANTS
Removed unneeded classes and files.
Update SQL implementation.
2015-12-15 15:32:31 -03:00
jvelilla
c25590c9cd Added Module Session Authentication with Cookies.
Updated Demo example with the Module Session (Authentication with Cookies)
Fixed little issue with SQL query in OpenID module.
2015-12-13 18:19:25 -03:00
23d266497b Made the SQL storage more flexible with INTEGER_32, by allowing to retrieve INTEGER_64 and convert to INTEGER_32 if value can be converted to integer 32. 2015-12-10 11:26:28 +01:00
ce8de442e9 Implemented taxonomy administration pages
- create term, vocabulary, add or remove term from vocabularies, ...
Fixed content editing related to taxonomy  (especially with multiple terms vs tags).
Fixed various SQL storage issue related to taxonomy and vocabularies.
Added CMS_RESPONSE.wsf_theme as helper.
2015-12-10 11:21:20 +01:00
e3ae564746 Removed an obsolete call to CMS_RESPONSE.hooks . 2015-12-07 22:08:37 +01:00
b0626d5250 Use + instead of concat(..) in javascript. 2015-12-07 21:36:24 +01:00
276dcc6fcd Added back CMS_MODULE.register_modules (CMS_RESPONSE) as obsolete, to avoid breaking existing modules.
Note: all module SHOULD migrate to new hook setup!
2015-12-07 21:24:48 +01:00
6313007fbf Refactored and update CMS hooks design. (Move from CMS_RESPONSE to CMS_API).
Moved content_types and content_type_webform_managers from CMS_RESPONSE to CMS_API.
Updated the way to output content (node, ...) to html page.
   See CMS_CONTENT_TYPE_WEBFORM_MANAGER.append_cointent_as_html_to (...).
   Added notion of "teaser" (short version of the content), as opposed to full content.
One can use CMS_API.html_encoder ... when possible, same for `formats', ...
Added bridge from CMS_MODULE_API to CMS_API's encoders.
Added new CMS_TAXONOMY_HOOK used to retrieve list of content associated with a specific term.
Moved up to CMS_RESPONSE a few features which was available only in specific descendants.

Added /taxonomy/term/{termid} implementation.
2015-12-07 18:21:40 +01:00
ecbcb6a5cb Added notion of CMS_CONTENT as ancestor of CMS_NODE.
Moved CMS_CONTENT_TYPE to core library.
Added basic and limited taxonomy query /taxonomy/term/{termid} .
2015-12-03 23:01:31 +01:00
a5c117e46e Merge branch 'taxonomy' 2015-12-03 19:26:43 +01:00
20dfce1396 Improved taxonomy by supporting tags, multiple terms allowed, and required kind of vocabulary for specific content type.
Updated node web form, to support taxonomy editing if allowed (specific support for CMS_VOCABULARY.is_tags: BOOLEAN).
Added notion of required or optional module dependencies.
2015-12-03 19:24:58 +01:00
jvelilla
1bfc4a6741 Merge branch 'jvelilla-roc_delete_trash' 2015-12-03 06:51:37 -03:00
jvelilla
3fdbcb2eef Merge branch 'roc_delete_trash' of https://github.com/jvelilla/ROC into jvelilla-roc_delete_trash 2015-12-03 06:50:59 -03:00
jvelilla
a11a93c285 Update code to use CMS_API.unset_path_alias
Updated CMS_NODE_STORAGE_SQL renamed sql_restore_node as sql_update_note_status, updated
related code.
2015-12-02 16:14:54 -03:00
jvelilla
fade19bbee Added precondition to NODE_FORM_RESPONSE.new_delete_form
Added transaction support to CMS_NODE_STORAGE_SQL.delete_node_base
2015-12-02 12:10:03 -03:00
jvelilla
d10612f94b Made test.ecf compilable. 2015-12-02 10:56:18 -03:00
jvelilla
9da8b8a025 Fixed: delete-trash a node.
Added code to remove path_aliase when we delete a node.
2015-12-01 19:20:16 -03:00
f1f3c126dd Use module site files system for the (un)install SQL scripts.
Changed CMS_TERM.id type to INTEGER_64 .
Removed CMS_TERM.parent_id .
Implemented CMS_TERM saving.
2015-11-23 18:05:53 +01:00
1d4ce37ebf Added CMS_STORAGE.as_sql_storage: detachable CMS_STORAGE_SQL_I to ease development based on SQL database. 2015-11-23 18:03:55 +01:00
Jocelyn Fiat
10102e80fa Fixed a few grammar and style errors. 2015-11-23 16:57:49 +01:00
3791ffacdc Added first steps toward Taxonomy module. 2015-11-23 15:27:04 +01:00
b8920ee8b3 Added module administration from /admin/modules/ 2015-11-23 11:08:06 +01:00
2cf2b1da8c Merge branch 'master' of https://github.com/EiffelWebFramework/ROC 2015-11-17 22:19:34 +01:00
17ae27df40 Updated ROC CMS documentation.
Cosmetic, comments, typo.
2015-11-17 22:18:02 +01:00
Jocelyn Fiat
a976b1e21a Create doc/readme.md 2015-11-13 15:53:45 +01:00
04df6b85f0 Removed unused local variables. 2015-11-12 18:48:37 +01:00
79d30ee3a7 Added export of core data, such as users, path_aliases, custom_values.
Added export of node revisions.
2015-11-12 18:19:06 +01:00
a5973c9c8a Added exportation solution via CMS_HOOK_EXPORT.
Updated blocks settings for demo example project.
2015-11-10 10:30:10 +01:00
420051cd14 Redesigned hooks system (moving from CMS_RESPONSE to CMS_API).
Indeed, hooks does not require RESPONSE interface,
   and should be setup before, at the system level (i.e CMS_API)
Added exportation solution via CMS_HOOK_EXPORT.
Updated blocks settings for demo example project.
2015-11-09 21:07:02 +01:00
6b3ff6f980 Fixed list item computation for ini file, especially with included ini file. 2015-11-02 21:07:26 +01:00
360855a558 Fixed recent_changes module to allow alias of recent_changes blocks.
Factorized code in CMS_RESPONSE by reusing add_block.
2015-11-02 18:54:30 +01:00
6aaec0be9f Fixed table item computation for ini file, especially with included ini file. 2015-11-02 18:10:27 +01:00
cb6d13b5f7 Updated gcse library to use unique own uuid in ecf file. 2015-11-02 14:38:36 +01:00
951c977892 Use relative paths from google_search ecf files to other local cms library .ecf files. 2015-11-02 14:33:14 +01:00
2a4ebfa12e Updated google search libary ecf files to have better void-safe .ecf file. 2015-11-02 14:22:41 +01:00
955852747a Include google_search engine in example/demo installation script. 2015-11-02 11:18:45 +01:00
23fe22cad1 Google search module:
- cleanup unwanted file
- fixed bad indentation in html template.
2015-11-02 11:16:54 +01:00
da4b36869a Include the launcher during installation (install.bat). 2015-11-02 10:50:27 +01:00
5624892ebc Updated code related to cache management in CMS core and modules. 2015-11-02 10:50:05 +01:00
jvelilla
e40a7969fa Merge branch 'jvelilla-roc_gcse' 2015-10-26 10:51:51 -03:00
jvelilla
67fdd357df Merge branch 'master' of https://github.com/EiffelWebFramework/ROC into roc_gcse
Conflicts:
	examples/demo/demo-safe.ecf
2015-10-23 16:48:25 -03:00
5b0ab76434 Fixed more unicode issues, or being more flexible when loading from database. 2015-10-20 19:02:04 +02:00
a84f86d7a2 Addressed various unicode related issues.
Note this is using recent changes from text_filter library.
2015-10-20 18:49:40 +02:00
3f4e70b98c Updated roc tools, and associated scripts.
Also include Eiffel Store ODBC persistence.
2015-10-20 11:29:16 +02:00
782e9397a3 Added missing sql_finalize which is used to cleanup as early as possible the last statement when it is not needed anymore. 2015-10-19 23:33:17 +02:00
f51ddc9796 Extracted launcher code into cms/launcher/... libraries.
(mostly to help new project based on ROC CMS).
Renamed and simplified the roc cms server launcher, and the related cms execution.
Updated cms.ini and extract blocks related management into blocks.ini.
Added debug clauses for cms sqlite3 storage.
2015-10-19 22:50:48 +02:00
a260bbc2c5 Removed obsolete usage of {TYPE}.attempt from CMS_MODULE_COLLECTION. 2015-10-19 20:50:47 +02:00
eb5ae32e46 Added persistence support for Eiffel sqlite3 wrapper.
Updated existing persistency solution to be more generic to any db solution.
2015-10-19 11:24:22 +02:00
7fcacad5eb Use extended type support from EiffelStore to handle STRING_32, and other extended types. 2015-10-19 11:20:04 +02:00
7c99f2dc83 Added recent_changes.ecf 2015-10-18 20:47:33 +02:00
57430193e3 Fixed compilation of CMS_FILE_BLOCK. 2015-10-18 19:24:43 +02:00
62bf58ce6d Added support for block options for the feed aggregator blocks.
Updated weight for primary_tabs block.
2015-10-17 00:17:59 +02:00
23c395513b Fixed handling of block and optional block.
- All blocks behavior can be specified and overwritten via the configuaration.
  - And optional block are not displayed by default.
2015-10-16 23:46:18 +02:00
d2d86ecdf2 Added notion of block options, declared in cms.ini as
[blocks]
    {block_id}.options[name]=value
    {block_id}.options[size]=123
2015-10-16 23:11:44 +02:00
e90f82387f Added notion of alias block, to provide a way to include a block content in mutiple regions. 2015-10-16 23:02:47 +02:00
05472abdd7 Improved block condition "path:..." by allowing wildchar.
Added weight data to CMS_BLOCK to be able to sort the block lists,
  and thus order the display of blocks.
Set negative weight for various core block, so that they appear first as expected.
The weight can be set and overwritten in cms.ini , by pref  blocks.{block_id}.weight=integer_weight.
2015-10-16 17:22:22 +02:00
jvelilla
193760b34f Remove unneeded file. 2015-10-14 11:57:06 -03:00
jvelilla
9263f31521 Renamed module name to google_search (custom_search)
Clean code.
Updated google custom search to handle quota limit and no query submit.
Updated encoding issues for input searches: like "void safe" and "void + safe".
2015-10-14 11:51:59 -03:00
jvelilla
454d92f85b Merge https://github.com/EiffelWebFramework/ROC into roc_gcse 2015-10-14 11:45:33 -03:00
788cf3738d Fixed compilation of CMS_BLOCK_LOCATION_CONDITION (not used for now). 2015-10-14 14:20:55 +02:00
jvelilla
0e63c14613 Added Module Custom Search
Added Google custom search library
Added HTTP client extension libaray
Updated demo example to use the Module Custom Search
2015-10-13 10:23:30 -03:00
f6185612b2 Fixed compilation issue. 2015-10-12 19:42:09 +02:00
d37f45d958 Include block caches clearing during "clear_cache" hook invocation. 2015-10-12 19:39:41 +02:00
872f2a177d For now, only clear feed aggregation cache if clear all cache is requested. 2015-10-12 19:22:37 +02:00
3ed2f410d9 Added feeds.json example in feed_aggregator module folder. 2015-10-12 19:15:09 +02:00
1b451ef142 Moved the feeds.json example in associated module files. 2015-10-12 19:12:42 +02:00
50146985de Added CMS_HOOK_CACHE, and admin cache.
Prepared evolution of feed module, by allowing json object to list feeds locations.
   The associated key will be used to identify the location, and have category filter by location.
2015-10-12 19:03:12 +02:00
8cdf9ba973 Updated demo feed info. 2015-10-12 19:00:46 +02:00
8044f7d52b Implemented feed aggregation filtering based on categories. 2015-10-12 15:53:50 +02:00
ed24eb7c94 Updated theme example, to have only 2 feeds. 2015-10-09 19:47:49 +02:00
43d6b4a197 Committed module files installed for demo example. 2015-10-09 19:45:21 +02:00
dffd06e331 Implemented a basic block caching system.
- for block {block_id}, to have a cache with 3600 seconds of expiration,
    declare in the cms.ini
     [blocks]
     {block_id}.expiration=3600

Added support for size in feed aggregation with new field "size"
2015-10-09 19:38:57 +02:00
463105f29f Added feed aggregation module.
Redesigned the CMS_BLOCK system,
   - added condition attribute. It can be set via configuration file
     with
     [blocks]
      {blockid}.region={region_name}
      {blockid}.conditions[]=is_front
      {blockid}.conditions[]=path:location-path/foo/bar
   - For backward compatibility, the CMS will check only conditions for block name prefixed by "?".
Improved the configuration library to support list and table properties.
Updated theme for now, to include the feed examples.
Added "cache" classes, to ease caching of html output for instance. (TODO: improve by providing a cache manager).
2015-10-08 13:56:31 +02:00
abebd00a4f Added feed aggregation module.
Improved the CMS Block system to support condition.
2015-10-05 16:04:24 +02:00
ec53a2682b Updated notification mailer, to always store output messages.
Fixed CMS_RESPONSE, and specific error response, to return expected status code.
2015-09-28 10:47:57 +02:00
jvelilla
7b2e6ab7b4 Merge branch 'jvelilla-roc_jv_issues' 2015-09-15 14:35:35 -03:00
jvelilla
87f4de1264 Merge branch 'roc_jv_issues' of https://github.com/jvelilla/ROC into roc_jv_issues 2015-09-15 14:30:02 -03:00
jvelilla
ed614a662c Updated CMS node and blog to remove extension data.
Comments: minor update.

Updated CMS_NODE_STORAGE_I API.
Delete a node using a node as formal parameter instead of node id.
Clean code and update log information.

Added precondition to delete node to accept nodes with a valid a id.

Added missing assertions tag names.
2015-09-15 14:28:33 -03:00
jvelilla
d54ad59e5f Added missing assertions tag names. 2015-09-15 14:10:51 -03:00
jvelilla
9173ef2ded Added precondition to delete node to accept nodes with a valid a id. 2015-09-15 11:40:13 -03:00
jvelilla
ad9e908dc2 Updated CMS_NODE_STORAGE_I API.
Delete a node using a node as formal parameter instead of node id.
Clean code and update log information.
2015-09-15 10:42:30 -03:00
jvelilla
4584917877 Comments: minor update. 2015-09-15 09:02:38 -03:00
jvelilla
f7d68d09e4 Updated CMS node and blog to remove extension data. 2015-09-15 08:54:43 -03:00
f9ecd4956f Keep the until date in the form data, so that new filter will remember the until date. 2015-09-09 23:12:52 +02:00
18e159ad3c Simplified the add child mechanism, by using a query parameter ?parent=nid
(instead of specific node/{nid}/add_child/page url)
Fixed implementation of `CMS_NODE_API.is_node_a_parent_of (..)',
  and improved parent validity checking against cycle.
2015-09-09 23:04:08 +02:00
438259033a Renamed link "Trash" to "Move to trash". 2015-09-09 22:10:13 +02:00
62e74ea6cd Refactor notion of trash and delete.
- Trash a node now does a soft delete (move to trash container).
 - Delete a node now remove it from the storage (no undo).

Signed-off-by: jvelilla <javier.hector@gmail.com>
2015-09-09 22:05:23 +02:00
32a409b7e9 Refactored add child feature, to have all the page specific code in CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER (and not in the general node code).
Note that ideally PAGE could be a separated page node module (as it is for blog).
Added listing of children for each "page".
2015-09-09 21:58:16 +02:00
jvelilla
eb70af6f19 CMS_NODE_API.available_parents_for_node
- Fixed comment.
     - added check to know if there are potencial cycles
     - added postcondition to ensure the list of potencial parent will not produce a cycle.

CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER.update_node
     - Updated code to handle unassing parent from a node when the user
     - submit a -1 value.

NODE_FORM_RESPONSE.edit_form_validation
      - Added validation for node_parent, check if the input is valid and then
      - if exist a valid node in the list of available parents for the current node.

Signed-off-by: jvelilla <javier.hector@gmail.com>
2015-09-09 16:53:37 +02:00
5f4eb2cf10 Correct implementation of node extension save operation. 2015-09-08 20:44:00 +02:00
88bc52fffb Updated roc script, install module for the examples, and updated demo site folder. 2015-09-08 16:03:36 +02:00
jvelilla
544e6540ed Add child page support.
- Add support to create a new node as a child of an existing node.
   - Update or add a parent for a given node.
Fix delete and trash behavior: when a node is not published only the
   - author or admin see it.

Fix Node and Page node Revisions.
2015-09-08 15:57:17 +02:00
2431d7af6c Improved the recent changes modules:
- extracted from populate_recent_changes, the recent_changes_sources that enables to filter early.
Added author_name to the CMS_RECENT_CHANGE_ITEM to support author which is not related to any CMS_USER.
Implemented the simple filtering on source and add parameters size and date.
2015-09-03 14:47:17 +02:00
jvelilla
0d55bd67a2 Updated Basic Auth Module: remove the escaping from the logout message 2015-09-01 12:56:26 -03:00
jvelilla
e1727cc445 Merge branch 'jvelilla-roc_logout' 2015-09-01 11:50:37 -03:00
jvelilla
634a078282 Updated Loging form:
Display invalid credentials message inside the primary-tabs div.
Added support to submit the form using <Enter>.
2015-09-01 11:41:50 -03:00
jvelilla
2f65a084ac Updated logout message. Change message and added a div to make it easier style the site 2015-09-01 11:15:25 -03:00
jvelilla
13df6fd593 Added Logout message 2015-09-01 10:55:28 -03:00
ad4f020d0e The NULL storage may look into the CMS configuration file.
This allows to run the CMS without any database.
2015-08-21 19:16:31 +02:00
7a13b47131 Fixed cms library compilation.
CMS_FILE_BLOCK was missing `is_empty' implementation.
2015-08-19 12:34:35 +02:00
923089baa1 Better js code to apply CKEditor.replace, mainly to select only textarea.name=$name.
code cleaning.
2015-08-18 19:24:18 +02:00
cfec9dc7f8 Do not include empty blocks, this way, we avoid empty sidebars if not needed. 2015-08-18 10:58:31 +02:00
b5e7d5d201 Require "view recent changes" permission to see the recent changes. 2015-08-17 17:40:55 +02:00
e1bdcb965c Added permission arguments to "trash" and "Create" links.
Added blocks configuration settings via the cms.ini and "blocks" section
   ex: [blocks]
       navigation.region=sidebar_first
   This enables the site to change default block location, and even hides it easily, if the theme does not include associated region name.
2015-08-13 00:44:16 +02:00
0061afcbe8 use "deleted" instead of "trashed" . 2015-08-12 19:03:40 +02:00
6a9bc8aa42 Updated admin and recent_changes module permissions declaration. 2015-08-12 19:00:17 +02:00
1d7d79d69e Cleaned up hooks related code, and always go via CMS_RESPONSE.hooks 2015-08-12 17:50:23 +02:00
46014da3d8 Added recent_changes module.
Revisited hooks management, and added new CMS_HOOK_MANAGER.
Added admin, and other link into navigation menu that goes into first sidebar.
Fixed theme info, and template for sidebar ids.
Better css class name for cms node content.
2015-08-12 17:30:07 +02:00
244 changed files with 14824 additions and 1550 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<target name="cms">
<root all_classes="true"/>
<file_rule>
@@ -14,14 +14,16 @@
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="cms_app_env" location=".\library\app_env\app_env-safe.ecf"/>
<library name="cms_model" location=".\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_config" location=".\library\configuration\config-safe.ecf"/>
<library name="cms_model" location=".\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="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="kmp_matcher" location="$ISE_LIBRARY\library\text\regexp\kmp_matcher\kmp_matcher-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
<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"/>
@@ -30,7 +32,6 @@
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -15,14 +15,16 @@
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.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_config" location=".\library\configuration\config.ecf"/>
<library name="cms_model" location=".\library\model\cms_model.ecf" readonly="false"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="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="kmp_matcher" location="$ISE_LIBRARY\library\text\regexp\kmp_matcher\kmp_matcher.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email.ecf"/>
<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"/>
@@ -31,7 +33,6 @@
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -1,93 +0,0 @@
CMS Concepts
============
>Current implemented concepts
##### Table of Contents
1. [**Theme**](#theme)
2. [**Regions**](#regions)
- [**Default Page Layout**](#page_layout)
- [**Regions Holds blocks**](#regions_blocks)
3. [**Blocks**](#blocks)
4. [**Modules**](#modules)
5. [**Hooks**](#hooks)
<a name="theme"/>
Theme
-----
In a CMS , a theme is a collection of templates files (HTML, CSS, Images, etc ) that determine how a CMS web site looks. The goal of a theme is to let you change the look and feel of the site.
Eiffel CMS is inspired by Drupal, and use the same default region names as default drupal theme.
#### Important Classes
* [CMS_THEME] (/library/src/theme/cms_theme.e): Abstraction defining the interface of a CMS theme.
* [SMARTY_CMS_THEME] (/library/src/theme/smarty_theme/smarty_cms_theme.e): Theme implemented using the [Eiffel Smarty library] (https://github.com/eiffelhub/template-smarty).
* [CMS_TEMPLATE] (/library/src/theme/cms_template.e): Template Abstraction that contains theme, variables needed by template when rendering page as html. At the moment there is only one implementation SMARTY_CMS_PAGE_TEMPLATE. At the moment there is only one implementation [SMARTY_CMS_PAGE_TEMPLATE] (/library/src/theme/smarty_theme/smarty_cms_page_template.e).
<a name="regions"/>
Regions
-------
The layout of a CMS web page has predefined area called **regions**. The Eiffel CMS uses the same default regions as Drupal, so let's see them in the following image.
<a name="page_layout"/>
![default page layout](http://themery.com/sites/default/files/figure-15-10.png)
```
regions[page_top] = Top
regions[header] = Header
regions[content] = Content
regions[highlighted] = Highlighted
regions[help] = Help
regions[footer] = Footer
regions[first_sidebar] = first sidebar
regions[second_sidebar] = second sidebar
regions[page_bottom] = Bottom
```
<a name="regions_blocks"/>
**A Region holds blocks**
**What goes inside regions?**
Generally, regions hold smaller piece of content called blocks. Blocks hold chunks of content, like the user login form, navigation menu or the information for the footer.
Regions are defined in a configuration file theme.info.
<a name="blocks"/>
CMS_BLOCK
---------
**What is a cms block?**
Blocks are chunk of content that can be created to display whatever you want, and then can be placed in various resgions in your template (theme) layout.
#### Important Classes
* [CMS_BLOCK] (/library/src/kernel/content/cms_block.e): The deferred class CMS_BLOCK provides an abstraction to describe content to be placed inside Regions.
* [CMS_CONTENT_BLOCK] (/library/src/kernel/content/cms_content_block.e): The class CMS_CONTENT_BLOCK describe how to provide generic content.
* [CMS_MENU_BLOCK](/library/src/kernel/content/cms_menu_block.e): The class CMS_MENU_BLOCK describe how to provides a menu of navigational links.
* [CMS_SMARTY_TEMPLATE_BLOCK] (/library/src/kernel/content/cms_smarty_templateblock.e) The class CMS_SMARTY_TEMPLATE_BLOCK describe how to use a CMS block with smarty template file content.
<a name="modules"/>
CMS_MODULES
-----------
**What is a cms module?**
Modules are piece of code that adds one or more features to your web site.
Modules can be plugged and combined to provide a web site customized to your needs. There are modules for many purposes, for example Administratiton, Basic Authentication, etc.
#### Important Classes
* [CMS_MODULE] (/library/src/modules/cms_module.e): The deferred class CMS_MODULE provides an abstraction to describe a generic module that add features to your web site.
* [CMS_RESPONSE](/library/src/service/response/cms_response.e). The deferred class CMS_RESPONSE provide an abstraction to builds the content to get process to render the output.
<a name="hooks">
CMS_HOOK
--------
Hooks is a mechanism which provide a way for modules to interact with each other and extending blocks of the current CMS.
* [CMS_HOOK] (/library/src/hooks/cms_hook.e): The deferred class CMS_HOOK is a marker interface for CMS Hook
* [CMS_HOOK_AUTO_REGISTER] (/library/src/hooks/cms_hook_auto_register.e): The deferred class provides an abstraction that when inheriting from this class, the declared hooks are automatically registered, otherwise, each descendant has to add it to the cms service itself.
* [CMS_HOOK_BLOCK](/library/src/hooks/cms_hook_block.e): The class CMS_HOOK_BLOCK describe a hook providing a way to alter a generic block.
* [CMS_HOOK_FORM_ALTER](/library/src/hooks/cms_hook_form_alter.e): The class CMS_HOOK_FORM_ATLER describe a hook providing a way to alter a form.
* [CMS_HOOK_MENU_ALTER](/library/src/hooks/cms_hook_menu_alter.e): The class CMS_HOOK_MENU_ATLER describe a hook providing a way to alter a menu.
* [CMS_HOOK_MENU_SYSTEM_ALTER](/library/src/hooks/cms_hook_menu_system_alter.e): The class CMS_HOOK_MENU_SYSTEM_ALTER describe a hook providing a way to alter the CMS menu system.
* [CMS_HOOK_VALUE_TABLE_ALTER](/library/src/hooks/cms_hook_value_table_alter.e):: The class CMS_HOOK_VALUE_TABLE_ALTER describe a hook providing a way to alter the value table for a response.

View File

BIN
doc/img_diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

396
doc/readme.md Normal file
View File

@@ -0,0 +1,396 @@
ROC CMS Documentation
=====================
[TOC]
## Overview
**ROC CMS** stands for "REST On CMS", however, until now, no particular focus was done on the REST API approach, and so far a more pragmatic approach dominated.
Part of the design is inspired by Drupal (blocks, hooks, Role-based access control, ...), and other parts related to Eiffel. Priorities, modules and related have been driven by concrete need, in order to fulfill the https://eiffel.org/ websites. Also a contribution (as student projects, or others) helped build various modules or functionality.
Currently, **ROC CMS** is a library or **framework** that provides components, tools and resources to build a CMS (Content Management System). It is not currently a CMS product, one can install and customize without any code.
Thus, it will be interesting for people willing to build a website using **Eiffel**. This will enable to reuse other Eiffel components, better integration with other Eiffel projects, and of course benefit from all the goodies of the Eiffel technologies (Eiffel language, DbC, re-usability, portability, IDE, debugger,...).
It depends on the **Eiffel Web Framework** (known as "Eiffel Web" or "EWF"), and thus can be executed as standalone, or CGI, libFCGI mode on Apache2 for instance, and on any Windows or Linux platform).
The main notions are:
- CMS Execution
- CMS APIs
- CMS Response
- CMS Modules
- CMS Hooks
- CMS Theme, blocks, links, ...
Those points will be described later in appropriated sections.
## Setup
The ROC CMS source is available either with the latest EiffelStudio release under the locations:
- $ISE_LIBRARY\unstable\library\web\cms
- or from github project https://github.com/EiffelWebFramework/ROC branch v0 for now.
```
git clone https://github.com/EiffelWebFramework/ROC -b v0
```
Note that if you use the source code from the github repository, you will need to use the latest release of EiffelStudio as it relieѕ on recent version of various libraries such as EWF, sqlite3, ....
And using the "master" branch, even the trunk version of EiffelStudio libraries. So for now, we encourage you to use the ROC CMS shipped with your EiffelStudio.
Once you have the source code, you should compile project <code>cms/example/demo/demo-safe.ecf</code> target "demo_standalone".
```
# from Command line
cd example
cd demo
ec -config demo-safe.ecf -c_compile -finalize
cp ./EIFGENs/demo_standalone/F_Code/demo.exe demo.exe
demo.exe
# or launch EiffelStudio, and open that project, compile and execute it inside the debugger for instance.
````
This demo includes all the official ROC CMS modules, files, and use libsqlite3 as default storage engine. So you should be able to execute it easily. The **standalone** target is configured to listen on port 9090 by default. (Mostly to avoid conflict on other app that my listen on port 80 or 8080).
In the directory <code>site</code> you will find all the expected files that should be in the root directory.
* config/ : it contains the various configuration files, especially the **cms.ini**.
* modules/ : files associated with each installed ROC CMS module.
* scripts/ : common scripts used mainly to initialize SQL databases.
* themes/ : folder containing the available ROC CMS themes.
* files/ : folder containing files available from the ROC CMS app.
* And also demo.ini that contains the settings for the web launcher, (in our case, the standalone Eiffel server), such as port_number.
Now that you know how to compile, execute, and see the related configuration files, let's describes the main notions of the ROC CMS, first from
* an admin point of view (dev using ROC CMS to build its site),
* and then from a developer point of view (in case you want to contribute to ROC CMS).
## Usage
### Main entries
As a CMS administrator, you will need to setup your CMS application (here the demo example). For this purpose, the main entry points are the CMS_EXECUTION interface, and then the <code>site/</code> files (configuration, themes, templates, ...).
### CMS initialization/Execution
The `CMS_EXECUTION` interface is deferred, and your CMS application needs to inherit from it and define `setup_storage`, `initial_cms_setup` and `setup_modules`. See for instance `DEMO_CMS_EXECUTION`.
So, the descendant of `CMS_EXECUTION` (`DEMO_CMS_EXECUTION` in the example), is creating the `CMS_SETUP`, declares the available **storage** builders (for persistency), and declares the available **modules**.
#### Persistence/Storage
Depending on the **configuration**, the CMS engine will instantiate and use a specific **CMS_STORAGE** (the default is based on `Eiffel sqlite3`, otherwise `EiffelStore+MySQL` and `EiffelStore+ODBC` are available). The storage solution is used to implement the persistence layer, and thus store and load CMS data to disk, or database.
The CMS provides, for now, storage based on
* EiffelStore + MySQL
* EiffelStore + ODBC (could be used for MySQL, sqlite, SQLserver, ...)
* Eiffel sqlite3 : that one is the default storage, since it is convenient for testing, but it is recommended to use EiffelStore+MySQL in production CMS site.
A typical implementation of <code>setup_storage</code> is:
```eiffel
setup_storage (a_setup: CMS_SETUP)
do
a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE3_BUILDER}.make, "sqlite3")
-- a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
end
```
And the CMS decides which storage should be used. It depends on the application configuration. See the **configuration** section.
Those data could be user information (login, email, password, ...), custom values, logs, emails, path aliases, ... and any data modules may need to store (for instance node content, for the `node` module.)
#### Modules
The `setup_module` is used to declare available **modules** (instances of `CMS_MODULE` effective types).
The modular design provides a simple way to extend or alter the CMS functionalities/behaviors.
Most of the CMS features are implemented by modules, and each module relies on the core of the CMS core.
This **core** contains the `CMS_API`, `CMS_USER_API`, and various internal mechanisms such as mailer, logger, ...
Use `setup_module (a_setup: CMS_SETUP)` to customize the `CMS_SETUP` object created by `initial_cms_setup`.
For your convenience, ROC CMS provides a `CMS_DEFAULT_SETUP` that import configuration from `site/config/cms.ini`
So far, what you need to remember is `CMS_EXECUTION` class and descendants are used to set up the ROC CMS application, for storage, modules, and also how to load configuration.
Note that a module can have 3 states:
- not installed,
- installed and enabled,
- installed and disabled.
At first, to install the modules, open your browser at location `https://hostname:port/admin/install` and click the associated button.
(Note: for new module addition, you also need to install them, using the same link, in the future, there will be a proper module management interface, in the admin front-end.)
To enable or disable a module, you will need to use the `cms.ini` configuration file, please see the **configuration** section.
Existing modules:
- **admin**: basic administration pages, to manage modules, roles, permissions, users, caches, ... (note: it is still very basic, and need effort to improve it.)
- authentication modules based on **auth**:
- **basic_auth**: account signing using basic HTTP Authorization solution
- **oauth20**: sign using a thirdparty OAuth2.0 account (such as Google, Facebook, github, ...)
- **openid**: sign using an OpenID account.
- **node**: the base of node management, include **Page** content type.
- **blog**: extends the **node** module with a **blog** content type.
- **recent_changes**: compute recent changes of CMS (integration with **node** management, and any modules that implement the `CMS_RECENT_CHANGES_HOOK`).
- **feed_aggregator**: aggregate one or many feeds (rss, atom, ...), and provide associated pages or blocks.
- **google_search**: provides search facilities using the Google Custom Search API.
### Configuration
When `CMS_DEFAULT_SETUP` is used, the CMS configuration is loaded from `site/config/cms.ini`.
That file contains a few sections:
- **site**: to set the `name`, `email` and the name of the `theme`. (See "Themes" section pour information.)
- **layout**: the application layout (or environment) can precise the `root-dir`, `themes-dir`, `modules-dir`. If not defined, the values are computed from Current working directory.
- **mailer**: the CMS can send email notification for various reasons, such as new users, or reset password functionalities, ... In this section, you can use
- `smtp` settings to precise an SMTP server (+ port),
- or `sendmail` to use an external script using the sendmail usage,
- or just an `output` file such as @stderr, or a path to a file on disk.
- **modules**: used to enable or disable modules.
- `*=on` -> modules are enabled by default
- `*=off` -> modules are disabled by default
- Note the default value is `on`
- For each module, this can be overwritten with `module_name=on|off`
- **blocks**: settings for blocks (See Themes, Blocks sections for more information on the block). A few parameters are available to customize blocks. The general form is `block-name.param=value` (note that "foo.bar" is a value block name.)
- `block-name.region`: assign the block `block-name` to a specific region. A block can be assigned to **only one region**.
- `block-name.title`: used to overwrite the block title (with <none> , the title is hidden).
- `block-name.weight`: used to order blocks in the same region (blocks with lower weight goes first).
- `block-name.expiration`: used to provide a basic cache system based on expiration. The value is a number of seconds before the cache expires (-1: never expires, 0: never cache, n: cache expires after `n` seconds).
- `block-name.condition`, or `block-name.conditions[]`: include `block-name` only under specific condition(s). The condition can be
- `is_front`: which is True only for the front page, usually at url "/"
- `path:foo/bar`, `path:foo/*/bar`: True only for CMS location matching the patterns after "path:"
- `<none>`: related block is disabled.
- *note: There can be multiple conditions processed as any of the conditions (i.e: "or").*
- `block-name.options[varname]: pass a table of options `varname => value` to the related block. This can be used to pass parameters for block builder (for instance, recent_changes modules accept parameters "size" to know how many changes should be included.)
- To be able to include a block content into multiple region, it is possible to use aliases feature. For instance `&aliases[new_block]=block-name`, in this case, a `new_block` is declared, and it has same content as `block-name`, on this alias, the parameters `region, condition(s), title, weight` are supported, but not `options[]`.
- **admin**: various admin related settings such as
- `installation_access` which accepts 3 values: "all", "none" or "permission", to precise who has access to the modules installation page; either "all" for anyone, "none" to disable installation of new modules, or "permission" to use the CMS permissions solution to determine if the current user can install a new module.
Then, the configuration `cms.ini` can also define other parameters, and sections, that may be used by specific modules.
Note it is also possible to include another ini file with instruction `@include=path-to-file.ini`.
Check the `example/demo/site/cms.ini` for example.
### User management
The CMS core includes the notion of user, via interface `CMS_USER`, which has an id, a name, a password, ... and profile. Without any module, the CMS does not include any mean to authenticate, but still the CMS has the support for user management, and permissions system for current user. To be able to sign into the CMS, the site should include the module `auth`, and one or many of:
- `basic_auth`: authentication using the HTTP Authorization header.
- `oauth20`: being able to sign with an OAuth2.0 account (such as Google, Facebook, ...)
- `OpenID`: being able to sign with an OpenID account.
Whatever authentication solution is used, when a user is signed-in, there is an instance of `CMS_USER` representing the associated CMS user account.
There is a predefined user `admin` who is the administrator of the CMS, and by definition, this **admin** has all the permissions. It is initialized by default with username `admin` and password `istrator#`.
The access control is role-based permissions system. This means, a user can have one or many *roles*, and each *role* includes a list of *permissions*.
There are two built-in roles:
- **anonymous**: when no user is signed in (typically anonymous visitors).
- **authenticated**: when a user is signed in the CMS.
With those 2 built-ins roles, and any custom role the admin will create, it is possible to give specific permissions, to a group of users.
The CMS core defines a few permissions, and each module can also define their own permissions, for instance: "view any page", "create page", "edit page", "delete page", "clear cache", "install modules", ... (when the administrator is signed-in, go to url `/admin/role/1/edit` to see all the available permissions).
### Modules
A module is the way to extend the CMS engine.
First via the inherited `CMS_MODULE` interface that enables a module to:
- have a custom `install` and `uninstall` procedure by redefining the related routines.
- add its **routes** via `setup_router`. (i.e associated url or template of url with a specific request handler).
- register itself to hooks via `register_hooks`.
- declare new permissions by redefining `permissions`.
- provide a specific module api by redefining `module_api`.
- add its **filters** by redefining `filters`.
Using the `hooks` system, a module can be deeply integrated with the CMS engine, and even alter behaviors (for instance, add link, add css, javascript, ...). See related developer documentation on hooks.
It is simple to create your own modules (check the developer documentation).
The ROC CMS library provides a few modules for now, for instance: basic_auth, oauth20, openid, node, blog, feed_aggregator, recent_changes, google_search, ... and others (the list keeps growing...).
**Reminder**: to include a module to your CMS site, you need to
- include the associated .ecf file in your CMS site .ecf file.
- and also declare them in your descendant of CMS_EXECUTION.
- copy the eventual resources, configuration, ... files in the corresponding `site`.
Note: a tool **roc** is under development to ease such operations, for now it only copies needed files from module to site location. In the future, it should also update .ecf files, associated CMS_EXECUTION effective class.
### Themes
When talking about CMS, a major topic is how a request is rendered in a web browser. Here comes the notion of **theme** which is a collection of templates, accepting various values as input (including the content of the blocks), and renders as an html5 page. It also includes various assets such as css, javascript, icons, images, ...
The ROC CMS theming is inspired by Drupal, with the notion of **region** and **block**.
Note: for now, there is no simple "theme" module or similar, and the common way to start your CMS site is to copy an existing project such as the one available with the demo example (i.e: copying the source code, but also the `site` folder).
Currently the default theme of the demo example `SMARTY_CMS_THEME` is based on Eiffel **smarty** template library (Check [smarty doc](https://svn.eiffel.com/eiffelstudio/trunk/Src/contrib/library/text/template/smarty/README.md) for syntax and functionalities).
The layout of a CMS web page has predefined area called **regions**. The Eiffel CMS uses the same default regions as Drupal, so let's see them in the following image.
```
+----------------------------------------------------------+
| Page_top |
+----------------------------------------------------------+
| Header |
+---------------+-------------------------+----------------+
| | Highlighted | |
| Sidebar_first +-------------------------+ Sidebar_second |
| | Help | |
| +-------------------------+ |
| | | |
| | Content | |
| | | |
+---------------+-------------------------+----------------+
| Footer |
+----------------------------------------------------------+
| Page_bottom |
+----------------------------------------------------------+
```
The regions available for a theme, are defined in a configuration file `theme.info` located in the theme directory. For example:
```
name=default_theme
engine=smarty
version=0.1
regions[page_top] = Top
regions[header] = Header
regions[content] = Content
regions[highlighted] = Highlighted
regions[help] = Help
regions[footer] = Footer
regions[sidebar_first] = first sidebar
regions[sidebar_second] = second sidebar
regions[page_bottom] = Bottom
```
Note: the value for each region is the human readable region name.
Note the regions may be disposed with other layout (two sidebars on the left, or right, ... and so on), responsive design or not, and so on. But on the CMS side, a *block* can be inserted into a *region*, and depending if the region is included in the theme, the related block content will be displayed or not.
To sort *block* inside a region, the CMS is using the `weight` property (that can be set via code, and/or overridden via configuration, i.e: `cms.ini`).
This is how a site can support many themes, using the region as content holders, and theme for the layout and style.
Internally the block contents are stored in the values associated with each region.
The theme also has access to specific `values` such as
- `site_url`: the absolute url of the CMS website.
- `host`: the host name.
- `is_https`: True if the connection is using https://
- `user`: contains the username of the signed user, if any.
- `site_title`: site title.
- `page_title`: per page title.
- and also `page: CMS_HTML_PAGE` which represents the CMS page to render with the theme.
- `page` provides values via expression, such as `$page.type`, `$page.is_front`, `$page.is_https`, `$page.title`, ...
- and also a smart expression for region via `$page.region_xyz` for region `xyz` if any, ... (note the region are also available with expression like `$region_xyz` or `$page.region_xyz` ...)
==Note for developers: internally, the deferred class `CMS_RESPONSE` provides an abstraction to render the response for the request using the **theme**, in fact, the theme is controlled by the CMS_RESPONSE implementation (to set value, build expected theme, and finally render as html).==
### Blocks
As previously said, a region holds smaller piece of content called blocks.
Blocks hold chunks of content, like the user login form, navigation menu, information for the footer, or anything provided by each module.
For instance the `feed_aggregator` module provides a block to display the latest elements of a aggregated feed.
Currently there are different kind of `CMS_BLOCK`:
- `CMS_CONTENT_BLOCK`: it holds a simple text to render as it is on the page.
- `CMS_MENU_BLOCK`: it holds a `CMS_MENU` as a collection of `CMS_LINK` generally used to hold a menu, or set of links such as navigation or management menus.
- `CMS_SMARTY_TEMPLATE_BLOCK`: it holds a simple text to render as it is in the page.
Internally, there are two other kinds of block:
- `CMS_ALIAS_BLOCK`: being the alias of another block, but with specific properties.
- `CMS_CACHE_BLOCK`: there is a simple cache solution for blocks, based on expiration. See the configuration section to know how to define the expiration for a block.
For now, creating a block is only possible via block, an evolution of ROC CMS should allow the administrator to add new block without coding.
### Persistence
The persistence or storage layer is used by the CMS to store custom values, path aliases, logs, emails, user information, but it is also used by module (unless a module wants to use its own persistence solution, disk, cloud, ...).
Currently, there are only SQL based implementations of that `CMS_STORAGE`, but nothing prevents to implement it with other solutions (plain text file, NoSQL db, ...).
The current implementation are using either:
- EiffelStore + MySQL: recommended for production, however Eiffel MySQL requires to configure your environment by setting, for instance MYSQL variable on Windows, and MYSQLINC on Linux.
- EiffelStore + ODBC: via ODBC, there is a large range of available database (MySQL, SQLite, SQLserver, ...), but it requires to set up your environment (for instance install sqliteODBC driver to use SQLite database).
- Eiffel sqlite3 wrapper: it is very convenient for development, but maybe not recommended for production websites. It does not require any environment setup, so this is a simple solution to build tests for instance.
In practice, how to use a storage or another?
The project needs to include the expected storage, the following instructions explains how to include sqlite3, EiffelStore+ODBC and EiffelStore+MYSQL storage.
1. First the associated .ecf file need to be included in your project file (.ecf)
For instance
```xml
<library name="persistence_sqlite3" location="$ISE_LIBRARY\unstable\library\web\cms\library\persistence\sqlite3\sqlite3-safe.ecf"/>
<library name="persistence_store_odbc" location="$ISE_LIBRARY\unstable\library\web\cms\library\persistence\store_odbc\store_odbc-safe.ecf"/>
<library name="persistence_store_mysql" location="$ISE_LIBRARY\unstable\library\web\cms\library\persistence\store_mysql\store_mysql-safe.ecf"/>
```
2. Then in the descendant of `CMS_EXECUTION`, in the demo `DEMO_CMS_EXECUTION`, see the code of `setup_storage`:
```eiffel
setup_storage (a_setup: CMS_SETUP)
do
a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE3_BUILDER}.make, "sqlite3")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
end
```
3. And finally, in the configuration file `site/config/demo.json` (in fact, the executable name + ".json"), define the driver and environment of the datasource. For instance the following code defines **sqlite3** as default CMS storage, and environment *sqlite3* that defines the path of SQLite database as "site/database.sqlite3". Note the way to declare sqlite with ODBC, mysql with ODBC, or mysql directly with EiffelStore.
```json
{
"database": {
"datasource": {
"driver": "sqlite3",
"environment": "sqlite3",
},
"environments": {
"sqlite3": {
"connection_string":"Database=./site/database.sqlite3;"
},
"odbc-sqlite": {
"connection_string":"Driver=SQLite3 ODBC Driver;Database=./site/database.sqlite;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;"
},
"odbc-mysql": {
"connection_string":"Driver=mysql ODBC Driver;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;"
},
"mysql": {
"connection_string":"Driver=mysql;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;"
}
}
}
}
```
To use EiffelStore+MySQL, just change the "driver" to be "mysql" and "environment" to "mysql". The connection string for server database defines the credentials with "Uid" and "Pwd".
### How to run the CMS site?
As any Eiffel Web application (EWF), it can be executed as
- **standalone**: using Eiffel standalone httpd server included in the "standalone" connector, and then no setup is needed.
- **CGI** or **libFCGI** server: using, for instance, Apache2. Please refer to the Eiffel Web Framework documentation.
### Conclusion
At this point, you know enough to build and administrate a ROC CMS site.
However, for a real site, it is likely that you will need to build your own modules, you will learn how doing that in the Developer Documentation.
***
## Developper Documentation
This diagram shows the main interfaces, they will be described in this documentation, but for now, it introduces those class names.
![Diagram](img_diagram.png)
### CMS APIs
An instance of CMS_API is available either via argument, or via attribute / function of various CMS components.
It provides routine specific to the ROC CMS engine (access to setup, modules, logs, custom values, ...).
### CMS Hooks
Hooks is a mechanism which provides a way for modules to interact with each other and extending blocks of the current CMS.
- [CMS_HOOK](../library/src/hooks/cms_hook.e): deferred class CMS_HOOK is a marker interface for CMS Hook
- [CMS_HOOK_AUTO_REGISTER](../library/src/hooks/cms_hook_auto_register.e): when inheriting from this deferred class, the declared hooks are automatically registered (note only the CMS core hooks are supported, as opposed to hook a module may propose). Otherwise, each descendant has to register itself to the associated hook manager.
- [CMS_HOOK_BLOCK](../library/src/hooks/cms_hook_block.e): it provides a way to declare and build blocks.
- [CMS_HOOK_FORM_ALTER](../library/src/hooks/cms_hook_form_alter.e): it provides a way to alter a web form `CMS_FORM`.
- [CMS_HOOK_MENU_ALTER](../library/src/hooks/cms_hook_menu_alter.e): it provides a way to alter a menu, and thus add or remove a link. This is how a module can add a link into a specific `CMS_MENU`.
- [CMS_HOOK_MENU_SYSTEM_ALTER](../library/src/hooks/cms_hook_menu_system_alter.e): similar to CMS_HOOK_MENU_ALTER, but on built-in menu, such as management, navigation menus, and other.
- [CMS_HOOK_VALUE_TABLE_ALTER](../library/src/hooks/cms_hook_value_table_alter.e): it provides a way to alter the values table for a response (i.e: inserting custom values, or even override existing values).
- [CMS_HOOK_EXPORT](../library/src/hooks/cms_hook_export.e): it provides a simple export solution for each module. Typically used to archive data associated with a module, for instance for backup purpose. In the future, a `CMS_HOOK_IMPORT` should also be available, and it would allow importing data exported by `CMS_HOOK_EXPORT`.
- and for more hooks ... please check descendants of `CMS_HOOK`.
### Custom Module
How to build a new module?
A module is usually developed as an Eiffel library, and provide one or many implementations of `CMS_MODULE`.
It has to set or implement:
- **name**: a unique name identifying the module
- **description**: a human text to describe the purpose of the module, it will mainly be used by the administration front-end.
- **package**: put the current module into a package, mainly for admin front-end.
- **version**: version information
- **dependencies**: defines dependencies on other modules.
- **permissions**: defines permissions used by the modules (mainly for admin front-end)
- **setup_router**: associate routes with request handlers (declare various url or url template and associated request handler).
- **filters**: similar to routers setup, but for WSF Filters (See EWF documentation for more details).
- **register_hooks**: register current module with various hooks if needed.
A module can also redefine `install` and `uninstall`. This could be used during installation to create new database tables, or anything needed by the module, or clean similar resources when being uninstalled.
In addition, a module can also implement `module_api: detachable CMS_MODULE_API` in order to be integrated easily with other modules (see for instance the CMS_NODE_API defined in **node** module).
Please have a look at the [tutorial](tutorial.md) page.
## References
For the interface references, please have a look at the [ROC CMS source code](https://github.com/EiffelWebFramework/ROC).
***
*(last modified: Nov/17/2015 by Jocelyn.)*

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<description>Example/demo for Eiffel ROC CMS library</description>
<target name="common" abstract="true">
<root class="DEMO_CMS_SERVER" feature="make_and_launch"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
@@ -12,44 +13,51 @@
</option>
<setting name="concurrency" value="thread"/>
<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">
<option>
<assertions precondition="true" postcondition="true" supplier_precondition="true"/>
</option>
</library>
<library name="cms_admin_module" location="..\..\modules\admin\admin-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/>
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
<library name="cms_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
<library name="cms_google_search_module" location="..\..\modules\google_search\google_search-safe.ecf" readonly="false" use_application_options="true"/>
<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_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
<library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/>
<library name="cms_admin_module" location="..\..\modules\admin\admin-safe.ecf" readonly="false"/>
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/>
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false">
<option>
<assertions/>
</option>
</library>
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/>
<!--
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-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"/>
</target>
<target name="demo_any" extends="common">
<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="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="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
<library name="any_launcher" location="..\..\launcher\any-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</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"/>
<setting name="concurrency" value="scoop"/>
<variable name="httpd_ssl_disabled" value="true"/>
<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"/>
<library name="standalone_launcher" location="..\..\launcher\standalone-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone_none" extends="demo_standalone">
@@ -61,25 +69,14 @@
<target name="demo_standalone_scoop" extends="demo_standalone">
<setting name="concurrency" value="scoop"/>
</target>
<target name="demo_nino" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_cgi" extends="common">
<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"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<library name="cgi_launcher" location="..\..\launcher\cgi-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_libfcgi" extends="common">
<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"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<library name="libfcgi_launcher" location="..\..\launcher\libfcgi-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo" extends="demo_standalone">

View File

@@ -1,9 +1,15 @@
setlocal
set ROC_CMD=%~dp0..\..\tools\roc.exe
set ROC_CMD=call %~dp0..\..\tools\roc.bat
set ROC_CMS_DIR=%~dp0
%ROC_CMD% install --module ..\..\modules\admin --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\google_search --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR%

View File

@@ -10,7 +10,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks,
setup_hooks,
initialize,
install
end
@@ -85,15 +85,15 @@ feature -- Access: router
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_response.subscribe_to_menu_system_alter_hook (Current)
a_response.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_block_hook (Current)
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
do
Result := <<"demo-info">>
Result := <<"?demo-info">>
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -103,8 +103,8 @@ feature -- Hooks
m: CMS_MENU
lnk: CMS_LOCAL_LINK
do
if a_block_id.is_case_insensitive_equal_general ("demo-info") then
if a_response.request.request_uri.starts_with ("/demo/") then
if a_block_id.same_string ("demo-info") then
if a_response.location.starts_with_general ("demo/") then
create m.make_with_title (a_block_id, "Demo", 2)
create lnk.make ("demo: abc", "demo/abc")
m.extend (lnk)

View File

@@ -0,0 +1,25 @@
### Blocks settings
#navigation.region=sidebar_first
#navigation.condition=is_front
management.conditions[]=path:admin*
management.conditions[]=is_front
#Feeds
feed.news.weight=3
feed.news.region=feed_news
feed.news.region=content
feed.news.condition=is_front
feed.forum.weight=2
feed.forum.region=feed_forum
feed.forum.region=content
feed.forum.condition=is_front
feed.forum.options[size]=5
#Updates
recent_changes.region=content
recent_changes.condition=is_front
recent_changes.title=Updates
recent_changes.options[size]=4

View File

@@ -9,9 +9,9 @@ email=your@email.com
theme=bootstrap
[mailer]
smtp=localhost:25
#smtp=localhost:25
#sendmail=/usr/bin/sendmail
#output=@stderr
output=@stderr
[modules]
# Module status
@@ -20,17 +20,11 @@ smtp=localhost:25
# Default is "on"
# for each module, this can be overwritten with
# module_name= on or off
*=off
admin=on
auth=on
basic_auth=on
blog=on
debug=on
demo=on
node=on
oauth20=on
openid=on
*=all
[blocks]
@include=blocks.ini
[admin]
# CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none)
installation_access=permission
installation_access=all

View File

@@ -1,10 +1,15 @@
{
"database": {
"datasource": {
"driver": "odbc",
"environment": "odbc-sqlite"
"driver": "sqlite3",
"environment": "sqlite3",
"-driver": "odbc",
"-environment": "odbc-sqlite"
},
"environments": {
"sqlite3": {
"connection_string":"Database=./site/database.sqlite3;"
},
"odbc-sqlite": {
"connection_string":"Driver=SQLite3 ODBC Driver;Database=./site/database.sqlite;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;"
},

View File

@@ -8,10 +8,10 @@
</head>
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>You have required a new password at <a href="$host">ROC CMS</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p><a href="$link">$link</a></p>
</body>
</html>
</html>

View File

@@ -8,11 +8,11 @@
</head>
<body>
<p>You have request a new activation token at<a href="...">ROC CMS</a></p>
<p>You have request a new activation token at <a href="$host">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
</html>

View File

@@ -7,7 +7,7 @@
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Welcome to<a href="...">ROC CMS</a></p>
<p>Welcome to<a href="$host">ROC CMS</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
</html>

View File

@@ -25,5 +25,5 @@
</p>
</div>
</div>
{/unless}
</div>
{/unless}
</div>

View File

@@ -11,7 +11,7 @@ ROC_AUTH.login = function() {
var username = form.username.value;
var password = form.password.value;
//var host = form.host.value;
var origin = window.location.origin.concat(window.location.pathname);
var origin = window.location.origin + window.location.pathname;
var _login = function(){
@@ -25,7 +25,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
}
}else{
@@ -49,7 +49,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
}
}
@@ -93,7 +93,7 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}else{
@@ -122,8 +122,8 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$("#imgProgressRedirect").hide();
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
@@ -322,4 +322,4 @@ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword;
}
}

View File

@@ -5,12 +5,12 @@
<div>
<form name="cms_basic_auth" action method="POST">
<div>
<input type="text" name="username" required>
<input type="text" name="username" id="username" required>
<label>Username</label>
</div>
<div>
<input type="password" name="password" required>
<input type="password" name="password" id="password" required>
<label>Password</label>
</div>
@@ -25,10 +25,5 @@
</p>
</div>
</div>
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
{/unless}
</div>

View File

@@ -0,0 +1,28 @@
{
"ids": ["news", "forum"],
"feeds": {
"news": {
"title": "Eiffel related posts",
"expiration": "21600",
"size": 5,
"locations": [
"https://bertrandmeyer.com/feed/",
"https://room.eiffel.com/blog/feed",
"https://room.eiffel.com/article/feed",
"https://room.eiffel.com/library/feed"
]
, "categories": ["Eiffel"]
,"option_description": "enabled"
},
"forum": {
"title": "Eiffel Forum",
"expiration": "21600",
"size": 5,
"locations": [
"https://groups.google.com/forum/feed/eiffel-users/msgs/atom.xml?num=15",
"http://stackoverflow.com/feeds/tag?tagnames=eiffel&sort=newest"
]
,"option_description": "enabled"
}
}
}

View File

@@ -0,0 +1,53 @@
div.feed ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
div.feed li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
}
div.feed li a {
font-weight: bold;
}
div.feed li .date {
font-weight: bold;
font-size: small;
}
div.feed li .category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
div.feed li .description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
div.feed li:hover {
margin-bottom: 23px;
}
div.feed li:hover .description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
div.feed li:hover:last-child {
margin-bottom: 28px;
}
div.feed li .description::after {
content: "...";
}

View File

@@ -0,0 +1,54 @@
div.feed {
ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
a {
font-weight: bold;
}
.date {
font-weight: bold;
font-size: small;
}
.category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
.description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
&:hover {
margin-bottom: 23px;
.description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
&:last-child {
margin-bottom: 28px;
}
}
.description::after { content: "..."; }
}
}

View File

@@ -0,0 +1,6 @@
{
"gcse": {
"cx":"",
"secret_key":""
}
}

View File

@@ -0,0 +1,40 @@
<section>
<header>
<h2>Results for <kbd>{$result.current_page.search_terms/}</kbd></h2>
</header>
<!-- list of results -->
<ol start="{$result.current_page.start_index/}">
<!-- Item result -->
{foreach from="$result.items" item="item"}
<li>
<article>
<header>
<h3>
<cite>
<a href="{$item.link/}">{$item.title/}</a>
</cite>
</h3>
</header>
<blockquote cite="{$item.link/}">
<p>{$item.html_snippet/}</p>
<footer>
<p><abbr title="Uniform Resource Locator">Source</abbr> <a href="{$item.link/}">{$item.display_link/}</a></p>
</footer>
</blockquote>
</article>
</li>
{/foreach}
</ol>
<ul class="cms-page-links">
{if isset="$result.previous_page"}
<li><a href="{$site_url/}gcse/?q={$result.previous_page.search_terms/}&amp;start={$result.previous_page.start_index/}&amp;num={$result.previous_page.count/}">Previous</a></li>
{/if}
{if isset="$result.next_page"}
<li><a href="{$site_url/}gcse/?q={$result.next_page.search_terms/}&amp;start={$result.next_page.start_index/}&amp;num={$result.next_page.count/}">Next</a></li>
{/if}
</ul>
</section>

View File

@@ -0,0 +1,11 @@
CREATE TABLE session_auth (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` TEXT NOT NULL,
`created` DATETIME NOT NULL,
CONSTRAINT `uid`
UNIQUE(`uid`),
CONSTRAINT `access_token`
UNIQUE(`access_token`)
);

View File

@@ -0,0 +1,37 @@
<div class="primary-tabs">
{unless isset="$user"}
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div>
<div>
<form name="cms_session_auth" action="{$site_url/}account/login-with-session" method="POST">
<div>
<input type="text" name="username" id="username" required value="{$username/}">
<label>Username</label>
</div>
<div>
<input type="password" name="password" id="password" required >
<label>Password</label>
</div>
<button type="submit">Login</button>
</form>
</div>
</div>
<div>
<div>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div>
{/unless}
{if isset=$error}
<div>
<div>
<p>
<strong>{$error/}
</p>
</div>
</div>
{/if}
</div>

View File

@@ -0,0 +1,26 @@
ul.taxonomy {
font-size: 80%;
list-style-type: none;
font-style: italic;
margin: 0;
}
ul.taxonomy li {
padding: 2px;
margin-right: 3px;
display: inline-block;
border: none;
}
ul.taxonomy li a:hover {
text-decoration: none;
}
ul.taxonomy li:hover {
padding: 1px;
border-top: solid 1px #66f;
border-bottom: solid 1px #66f;
background-color: #ddf;
}
table.taxonomy td {
border: solid 1px #ccc;
padding: 2px;
}

View File

@@ -0,0 +1,27 @@
ul.taxonomy {
font-size: 80%;
list-style-type: none;
font-style: italic;
margin: 0;
li {
a:hover {
text-decoration: none;
}
padding: 2px;
margin-right: 3px;
display: inline-block;
border: none;
&:hover {
padding: 1px;
border-top: solid 1px #66f;
border-bottom: solid 1px #66f;
background-color: #ddf;
}
}
}
table.taxonomy {
td {
border: solid 1px #ccc;
padding: 2px;
}
}

View File

@@ -0,0 +1,24 @@
CREATE TABLE taxonomy_term (
`tid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
`text` VARCHAR(255) NOT NULL,
`weight` INTEGER,
`description` TEXT,
`langcode` VARCHAR(12)
);
CREATE TABLE taxonomy_hierarchy (
`tid` INTEGER NOT NULL,
`parent` INTEGER,
CONSTRAINT PK_tid_parent PRIMARY KEY (tid,parent)
);
/* Associate tid with unique (type,entity)
* for instance: "page" + "$nid" -> "tid"
*/
CREATE TABLE taxonomy_index (
`tid` INTEGER NOT NULL,
`entity` VARCHAR(255),
`type` VARCHAR(255) NOT NULL,
CONSTRAINT PK_tid_entity_type PRIMARY KEY (tid,entity,type)
);

View File

@@ -0,0 +1,3 @@
DROP TABLE IF EXISTS taxonomy_term;
DROP TABLE IF EXISTS taxonomy_hierarchy;
DROP TABLE IF EXISTS taxonomy_index;

View File

@@ -51,16 +51,24 @@ ul.horizontal li {
.sidebar {
padding: 5px;
margin: 3px;
border: solid 1px #ccc;
/* border: solid 1px #ccc; */
}
.sidebar#sidebar_first {
width: 250px;
float: left;
position: fixed;
top: 45px;
left: 0;
bottom: 0;
width: 200px;
border-right: solid 1px #ddd;
}
.sidebar#sidebar_second {
width: 250px;
float: right;
}
.sidebar + .main {
margin-left: 200px;
}
#primary-tabs ul.horizontal {
list-style-type: none;
@@ -82,3 +90,11 @@ ul.horizontal li {
border: solid 1px red;
padding: 5px 2px 5px 2px;
}
table.with_border thead td {
font-weight: bold;
}
table.with_border td {
border: solid 1px #ccc;
padding: 2px 5px 2px 5px;
}

View File

@@ -0,0 +1,8 @@
$(document).ready(function() {
$('#gcse_search_form').submit(function() {
window.open('', 'formpopup', 'width=600,height=600,resizeable,scrollbars');
this.target = 'formpopup';
});
});

View File

@@ -55,15 +55,24 @@ ul.horizontal {
.sidebar {
padding: 5px;
margin: 3px;
border: solid 1px #ccc;
/* border: solid 1px #ccc; */
&#sidebar_first {
width: 250px;
float: left;
position: fixed;
top: 45px;
left: 0;
bottom: 0;
width: 200px;
border-right: solid 1px #ddd;
}
&#sidebar_second {
width: 250px;
float: right;
}
&+.main {
margin-left: 200px;
}
}
#primary-tabs {
ul.horizontal {
@@ -86,3 +95,13 @@ ul.horizontal {
border: solid 1px red;
padding: 5px 2px 5px 2px;
}
table.with_border {
thead td {
font-weight: bold;
}
td {
border: solid 1px #ccc;
padding: 2px 5px 2px 5px;
}
}

View File

@@ -5,9 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- EWF CMS -->
<link rel="stylesheet" href="{$site_url/}theme/css/style.css">
<!-- jQuery dep -->
<script src="{$site_url/}theme/js/jquery-1.10.2.min.js"></script>
<script src="{$site_url/}theme/js/popup_search.js"></script>
{if isset="$head"}{$head/}{/if}
{if isset="$styles"}{$styles/}{/if}
@@ -36,7 +37,17 @@
{$page.primary_nav/}
{/if}
</div>
<!-- Page search -->
<div class="row">
<div class="col-md-2 col-md-offset-9">
<form action="{$site_url/}gcse" class="search-form" id="gcse_search_form">
<div class="form-group has-feedback">
<input type="search" class="form-control" name="q" id="gcse_search" placeholder="search">
<span class="glyphicon glyphicon-search form-control-feedback"></span>
</div>
</form>
</div>
</div>
<!-- General Page Content -->
<div id='content' class='row-fluid'>
<!-- Left Sidebar sidebar_first -->
@@ -49,7 +60,7 @@
{/unless}
<!-- Highlighted, Help, Content -->
<div class='span8 main'>
<div id='main' class='span8 main'>
<!-- Highlighted Section -->
{unless isempty="$page.region_highlighted"}
<div id="highlighted">{$page.region_highlighted/}</div>
@@ -62,9 +73,15 @@
<!-- Main Content Section -->
{unless isempty="$page_title"}<h1 class="page-title">{$page_title/}</h1>{/unless}
{$page.region_content/}
{if condition="$page.is_front"}
{if isset="$page.region_feed_news"}
<div class="column" style="width: 45%; float: left">{$page.region_feed_news/}</div>
{/if}
{if isset="$page.region_feed_forum"}
<div class="column" style="width: 45%; float: left">{$page.region_feed_forum/}</div>
{/if}
{/if}
</div>
</div>
</div>

View File

@@ -8,7 +8,7 @@ regions[content] = Content
regions[highlighted] = Highlighted
regions[help] = Help
regions[footer] = Footer
regions[first_sidebar] = first sidebar
regions[second_sidebar] = second sidebar
regions[sidebar_first] = first sidebar
regions[sidebar_second] = second sidebar
regions[page_bottom] = Bottom
navigation=default_nav
navigation=default_nav

View File

@@ -1,31 +1,21 @@
note
description: "Summary description for {EWF_ROC_SERVER_EXECUTION}."
description: "[
CMS Execution for the demo server.
]"
date: "$Date$"
revision: "$Revision$"
class
EWF_ROC_SERVER_EXECUTION
DEMO_CMS_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
@@ -39,17 +29,17 @@ feature {NONE} -- Initialization
create Result.make (l_env)
end
feature -- CMS setup
feature -- CMS storage
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_SQLITE3_BUILDER}.make, "sqlite3")
-- a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
end
feature -- CMS modules
setup_modules (a_setup: CMS_SETUP)
-- Setup additional modules.
local
@@ -76,6 +66,18 @@ feature -- CMS setup
a_setup.register_module (m)
create {CMS_BLOG_MODULE} m.make
a_setup.register_module (m)
-- Taxonomy
create {CMS_TAXONOMY_MODULE} m.make
a_setup.register_module (m)
-- Recent changes
create {CMS_RECENT_CHANGES_MODULE} m.make
a_setup.register_module (m)
-- Recent changes
create {FEED_AGGREGATOR_MODULE} m.make
a_setup.register_module (m)
-- Miscellanious
@@ -84,6 +86,12 @@ feature -- CMS setup
create {CMS_DEMO_MODULE} m.make
a_setup.register_module (m)
create {GOOGLE_CUSTOM_SEARCH_MODULE} m.make
a_setup.register_module (m)
create {CMS_SESSION_AUTH_MODULE} m.make
a_setup.register_module (m)
end
end

View File

@@ -0,0 +1,18 @@
note
description: "[
DEMO application server.
]"
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
class
DEMO_CMS_SERVER
inherit
ROC_CMS_LAUNCHER [DEMO_CMS_EXECUTION]
create
make_and_launch
end

10
launcher/README.txt Normal file
View File

@@ -0,0 +1,10 @@
Collection of ROC CMS launcher ready to use.
- Include any-safe.ecf to use any of cgi, libfcgi or standalone connector.
- Include standalone-safe.ecf to use standalone connector.
- Include libfcgi-safe.ecf to use libfcgi connector.
- Include cgi-safe.ecf to use cgi connector.
In application, the root class need to inherit from ROC_CMS_LAUNCHER with adapted CMS_EXECUTION
descendant.

18
launcher/any-safe.ecf Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="launcher" uuid="6FDCA393-AFC3-436B-A58A-870923967C86" library_target="launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
</target>
<target name="launcher" extends="common">
<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="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="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\any\" recursive="true"/>
</target>
</system>

19
launcher/any.ecf Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="launcher" uuid="6FDCA393-AFC3-436B-A58A-870923967C86" library_target="launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<option void_safety="none" />
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\cms.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
</target>
<target name="launcher" extends="common">
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi.ecf"/>
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi.ecf"/>
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\any\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,19 @@
note
description: "[
Effective class for APPLICATION_LAUNCHER_I
You can put modification in this class
]"
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
revision: "$Revision: 36 $"
class
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit
APPLICATION_LAUNCHER_I [G]
feature -- Custom
end

View File

@@ -0,0 +1,127 @@
note
description: "[
Specific application launcher
DO NOT EDIT THIS CLASS
you can customize APPLICATION_LAUNCHER
]"
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
revision: "$Revision: 36 $"
deferred class
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
inherit
SHARED_EXECUTION_ENVIRONMENT
feature -- Execution
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
nature: like launcher_nature
do
nature := launcher_nature
if nature = Void then
launch_standalone (opts)
elseif nature = nature_standalone then
launch_standalone (opts)
elseif nature = nature_nino then
launch_nino (opts)
elseif nature = nature_cgi then
launch_cgi (opts)
elseif nature = nature_libfcgi then
launch_libfcgi (opts)
else
-- bye bye
(create {EXCEPTIONS}).die (-1)
end
end
feature {NONE} -- Access
launcher_nature: detachable READABLE_STRING_8
-- Initialize the launcher nature
-- either cgi, libfcgi, or nino.
--| We could extend with more connector if needed.
--| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time.
local
p: PATH
ext: detachable READABLE_STRING_32
do
create p.make_from_string (execution_environment.arguments.command_name)
if attached p.entry as l_entry then
ext := l_entry.extension
end
if ext /= Void then
if ext.same_string (nature_standalone) then
Result := nature_standalone
end
if ext.same_string (nature_nino) then
Result := nature_nino
end
if ext.same_string (nature_cgi) then
Result := nature_cgi
end
if ext.same_string (nature_libfcgi) or else ext.same_string ("fcgi") then
Result := nature_libfcgi
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
feature {NONE} -- nino
nature_nino: STRING = "nino"
launch_nino (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_NINO_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end
feature {NONE} -- cgi
nature_cgi: STRING = "cgi"
launch_cgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_CGI_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end
feature {NONE} -- libfcgi
nature_libfcgi: STRING = "libfcgi"
launch_libfcgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end
feature -- Default
default_nature: STRING
do
Result := nature_standalone
end
end

15
launcher/cgi-safe.ecf Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="cgi_launcher" uuid="0FE4F1D0-BB70-4C7F-A66E-B27F1D718109" library_target="cgi_launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
</target>
<target name="cgi_launcher" extends="common">
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\default\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,19 @@
note
description: "[
Effective class for APPLICATION_LAUNCHER_I
You can put modification in this class
]"
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
revision: "$Revision: 36 $"
class
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit
APPLICATION_LAUNCHER_I [G]
feature -- Custom
end

View File

@@ -0,0 +1,26 @@
note
description: "[
Specific application launcher
DO NOT EDIT THIS CLASS
you can customize APPLICATION_LAUNCHER
]"
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
deferred class
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
feature -- Execution
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_DEFAULT_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end
end

15
launcher/libfcgi-safe.ecf Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libfcgi_launcher" uuid="04D7D1EA-059B-4024-B0DE-BBB57AB2D00C" library_target="libfcgi_launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
</target>
<target name="libfcgi_launcher" extends="common">
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\default\" recursive="true"/>
</target>
</system>

16
launcher/libfcgi.ecf Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libfcgi_launcher" uuid="04D7D1EA-059B-4024-B0DE-BBB57AB2D00C" library_target="libfcgi_launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<option void_safety="none" />
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\cms.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
</target>
<target name="libfcgi_launcher" extends="common">
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\default\" recursive="true"/>
</target>
</system>

View File

@@ -1,12 +1,12 @@
note
description: "[
application service
Reusable ROC CMS launcher.
]"
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
class
EWF_ROC_SERVER
ROC_CMS_LAUNCHER [G -> CMS_EXECUTION create make end]
inherit
WSF_LAUNCHABLE_SERVICE
@@ -37,16 +37,27 @@ feature {NONE} -- Initialization
-- Initialize current service.
local
env: CMS_ENVIRONMENT
l_app_name: detachable READABLE_STRING_32
do
Precursor
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} service_options.make_from_file ("demo.ini")
create env.make_default
l_app_name := optional_application_name
if l_app_name = Void then
l_app_name := env.name
end
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} service_options.make_from_file (l_app_name + ".ini")
initialize_logger (env)
end
optional_application_name: detachable READABLE_STRING_32
-- Optional application name.
--| Redefine if needed.
do
end
feature {NONE} -- Launch operation
launcher: APPLICATION_LAUNCHER [EWF_ROC_SERVER_EXECUTION]
launcher: APPLICATION_LAUNCHER [G]
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
@@ -70,7 +81,7 @@ feature {NONE} -- Launch operation
l_message.append ("%N%N")
end
else
l_message.append ("The application crash without available information")
l_message.append ("The application crashed without information.")
l_message.append ("%N%N")
end
-- send email shutdown

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="standalone_launcher" uuid="F42660A9-26C2-466B-A63C-C7823C808BE7" library_target="standalone_launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\cms-safe.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
</target>
<target name="standalone_launcher" extends="common">
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\default\" recursive="true"/>
</target>
</system>

17
launcher/standalone.ecf Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="standalone_launcher" uuid="F42660A9-26C2-466B-A63C-C7823C808BE7" library_target="standalone_launcher">
<target name="common" abstract="true">
<root all_classes="true"/>
<option void_safety="none" />
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\cms.ecf"/>
<library name="cms_app_env" location="..\library\app_env\app_env.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
</target>
<target name="standalone_launcher" extends="common">
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone.ecf"/>
<cluster name="src" location=".\" recursive="false"/>
<cluster name="launcher" location=".\default\" recursive="true"/>
</target>
</system>

View File

@@ -33,11 +33,53 @@ feature -- Query
end
end
resolved_text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k',
-- and expanded values to resolved variables ${varname}.
do
if attached text_list_item (k) as lst then
from
lst.start
until
lst.after
loop
lst.replace (resolved_expression (lst.item))
lst.forth
end
end
end
resolved_text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k',
-- and expanded values to resolved variables ${varname}.
do
if attached text_table_item (k) as tb then
from
tb.start
until
tb.after
loop
tb.replace (resolved_expression (tb.item_for_iteration), tb.key_for_iteration)
tb.forth
end
end
end
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with key `k'.
deferred
end
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'.
deferred
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
deferred
end
integer_item (k: READABLE_STRING_GENERAL): INTEGER
-- Integer item associated with key `k'.
deferred
@@ -109,7 +151,7 @@ feature -- Duplication
end
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -119,14 +119,47 @@ feature -- Access: Config Reader
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with key `k'.
local
obj: like item
do
obj := item (k)
if attached {READABLE_STRING_32} obj as s32 then
Result := s32
elseif attached {READABLE_STRING_8} obj as s then
Result := utf.utf_8_string_8_to_escaped_string_32 (s)
Result := value_to_string_32 (item (k))
end
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'.
do
if attached {LIST [READABLE_STRING_8]} item (k) as l_list then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_list.count)
Result.compare_objects
across
l_list as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32)
else
Result := Void
end
end
end
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
do
if attached {STRING_TABLE [READABLE_STRING_8]} item (k) as l_list then
create {STRING_TABLE [READABLE_STRING_32]} Result.make (l_list.count)
Result.compare_objects
across
l_list as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32, ic.key)
else
Result := Void
end
end
end
end
@@ -226,6 +259,15 @@ feature -- Access
feature {NONE} -- Implementation
value_to_string_32 (obj: detachable ANY): detachable STRING_32
do
if attached {READABLE_STRING_32} obj as s32 then
Result := s32
elseif attached {READABLE_STRING_8} obj as s then
Result := utf.utf_8_string_8_to_escaped_string_32 (s)
end
end
item_from_values (a_values: STRING_TABLE [ANY]; k: READABLE_STRING_GENERAL): detachable ANY
local
i,j: INTEGER
@@ -400,7 +442,17 @@ feature {NONE} -- Implementation
j := k.index_of (']', i + 1)
if j = i + 1 then -- ends_with "[]"
k.keep_head (i - 1)
if attached {LIST [STRING_8]} items.item (k) as l_list then
if
a_section_prefix /= Void and then
attached {LIST [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_list
then
lst := l_list
elseif
attached last_section_name as l_section_prefix and then
attached {LIST [STRING_8]} items.item (l_section_prefix + {STRING_32} "." + k) as l_list
then
lst := l_list
elseif attached {LIST [STRING_8]} items.item (k) as l_list then
lst := l_list
else
create {ARRAYED_LIST [STRING_8]} lst.make (1)
@@ -414,7 +466,17 @@ feature {NONE} -- Implementation
sk.left_adjust
sk.right_adjust
k.keep_head (i - 1)
if attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
if
a_section_prefix /= Void and then
attached {STRING_TABLE [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_table
then
tb := l_table
elseif
attached last_section_name as l_section_prefix and then
attached {STRING_TABLE [STRING_8]} items.item (l_section_prefix + {STRING_32} "." + k) as l_table
then
tb := l_table
elseif attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
tb := l_table
else
create tb.make (1)
@@ -460,7 +522,7 @@ feature {NONE} -- Implementation
invariant
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -63,10 +63,46 @@ feature -- Access: Config Reader
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with query `k'.
do
if attached {JSON_STRING} item (k) as l_string then
Result := l_string.unescaped_string_32
elseif attached {JSON_NUMBER} item (k) as l_number then
Result := l_number.item
Result := value_to_string_32 (item (k))
end
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'.
do
if attached {JSON_ARRAY} item (k) as l_array then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_array.count)
Result.compare_objects
across
l_array as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32)
else
Result := Void
end
end
end
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
do
if attached {JSON_OBJECT} item (k) as obj then
create {STRING_TABLE [READABLE_STRING_32]} Result.make (obj.count)
Result.compare_objects
across
obj as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32, ic.key.item)
else
Result := Void
end
end
end
end
@@ -105,6 +141,15 @@ feature -- Access
feature {NONE} -- Implementation
value_to_string_32 (v: detachable ANY): detachable STRING_32
do
if attached {JSON_STRING} v as l_string then
Result := l_string.unescaped_string_32
elseif attached {JSON_NUMBER} v as l_number then
Result := l_number.item
end
end
object_json_value (a_object: JSON_OBJECT; a_query: READABLE_STRING_32): detachable JSON_VALUE
-- Item associated with query `a_query' from object `a_object' if any.
local
@@ -163,7 +208,7 @@ feature {NONE} -- JSON
end
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -21,6 +21,18 @@ feature -- Test
create {INI_CONFIG} cfg.make_from_string ("[
foo = bar
collection[] = a
collection[] = b
collection[] = c
collection[] = 1
collection[] = 2
collection[] = 3
table[a] = 1
table[b] = 2
table[c] = 3
table[d] = test
[first]
abc = 1
def = and so on
@@ -58,6 +70,21 @@ feature -- Test
assert ("has_item (second.is)", cfg.has_item ("second.is"))
assert ("item (second.is)", attached cfg.text_item ("second.is") as v and then v.same_string_general ("2"))
assert ("has_item (collection)", cfg.has_item ("collection"))
assert ("item (collection)", attached cfg.text_list_item ("collection") as lst and then (
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
)
)
assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test"
)
)
if attached cfg.sub_config ("second") as cfg_second then
assert ("has_item (is)", cfg_second.has_item ("is"))
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))
@@ -141,7 +168,9 @@ feature -- Test
"is": 2,
"the": 3,
"end": 4
}
},
"collection": ["a", "b", "c", 1, 2, 3],
"table": { "a": 1, "b": 2, "c": 3, "d" : "test" }
}
]")
@@ -164,6 +193,21 @@ feature -- Test
assert ("item (second.the)", attached cfg.text_item ("second.the") as v and then v.same_string_general ("3"))
assert ("item (second.end)", attached cfg.text_item ("second.end") as v and then v.same_string_general ("4"))
assert ("has_item (collection)", cfg.has_item ("collection"))
assert ("item (collection)", attached cfg.text_list_item ("collection") as lst and then (
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
)
)
assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test"
)
)
if attached cfg.sub_config ("second") as cfg_second then
assert ("has_item (is)", cfg_second.has_item ("is"))
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))

View File

@@ -28,7 +28,6 @@ feature {NONE} -- Initialization
-- Initialize service.
do
admin_email := parameters.admin_email
create {NOTIFICATION_SMTP_MAILER} mailer.make (parameters.smtp_server)
set_successful
end

4
library/gcse/Readme.md Normal file
View File

@@ -0,0 +1,4 @@
Google Custom Search Engine Eiffel Lbrary
Based on https://developers.google.com/custom-search/json-api/v1/using_rest

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
<target name="gcse">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_client_extension" location="..\http_client_extension\http_client_extension-safe.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="gcse" location=".\src\" recursive="true"/>
</target>
</system>

21
library/gcse/gcse.ecf Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
<target name="gcse">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http_client_extension" location="..\http_client_extension\http_client_extension.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<cluster name="gcse" location=".\src\" recursive="true"/>
</target>
</system>

10
library/gcse/license.lic Normal file
View File

@@ -0,0 +1,10 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR} Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"

236
library/gcse/src/gcse_api.e Normal file
View File

@@ -0,0 +1,236 @@
note
description: "[
Simple API to call Google Custome Search Engine
Example call:
GET https://www.googleapis.com/customsearch/v1?key=INSERT_YOUR_API_KEY&cx=017576662512468239146:omuauf_lfve&q=lectures
]"
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
revision: "$Revision: 97973 $"
EIS: "name=Google Custom Search Engine", "src=https://developers.google.com/custom-search/json-api/v1/using_rest", "protocol=uri"
class
GCSE_API
create
make
feature {NONE} -- Initialization
make (a_query_parameters: GCSE_QUERY_PARAMETERS)
-- Create an object GCSE with query_parameters `a_query_parameters'
do
query_parameter := a_query_parameters
ensure
query_parameters_set: query_parameter = a_query_parameters
end
feature -- Access
base_uri: STRING_8 = "https://www.googleapis.com/customsearch/v1"
-- Google custom search base URI.
query_parameter: GCSE_QUERY_PARAMETERS
-- Google custom search parameters.
last_result: detachable GCSE_RESPONSE
-- Search results.
feature -- Status Reports
errors: detachable LIST [READABLE_STRING_8]
-- optional list of error messages.
feature -- API
search
-- Search
local
l_parser: JSON_PARSER
l_gcse_response: detachable GCSE_RESPONSE
do
-- Data format for the response.
-- At the moment we are using the default value: json
-- but it's possible to define atom response using the alt parameter.
last_result := Void
if attached get as l_response then
create l_gcse_response
l_gcse_response.set_status (l_response.status)
l_gcse_response.set_status_nessage (l_response.status_message)
if attached l_response.body as l_body then
create l_parser.make_with_string (l_body)
l_parser.parse_content
if l_response.status = 200 and then l_parser.is_parsed and then attached {JSON_OBJECT} l_parser.parsed_json_object as jv then
-- Queries
if attached {JSON_OBJECT} jv.item (queries_key) as jqueries then
-- Next Page
if attached {GCSE_PAGE} query_page (next_page_key, jqueries) as l_page then
l_gcse_response.set_next_page (l_page)
end
-- Current Page
if attached {GCSE_PAGE} query_page (request_key, jqueries) as l_page then
l_gcse_response.set_current_page (l_page)
end
-- Previous Page
if attached {GCSE_PAGE} query_page (previous_page_key, jqueries) as l_page then
l_gcse_response.set_previous_page (l_page)
end
end
if attached {JSON_ARRAY} jv.item (items_key) as jitems then
across jitems as ic loop
if attached{JSON_OBJECT} ic.item as j_item then
l_gcse_response.add_item (item (j_item))
end
end
end
else
put_error (l_body)
end
else
put_error (l_response.status.out)
end
else
put_error ("unknown")
end
last_result := l_gcse_response
end
feature {NONE} -- REST API
get: detachable RESPONSE
-- Reading Data.
local
l_request: REQUEST
do
create l_request.make ("GET", new_uri)
Result := l_request.execute
end
feature {NONE} -- Implementation
new_uri: STRING_8
-- new uri (BaseUri?key=secret_value&cx=a_cx_id&q=a_query
-- ?key=INSERT_YOUR_API_KEY&cx=017576662512468239146:omuauf_lfve&q=lectures
-- full template BaseUri?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&
-- safe={safe?}&cx={cx?}&cref={cref?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&
-- googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&
-- siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&
-- orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&
-- searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&
-- imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
do
create Result.make_from_string (base_uri)
Result.append ("?key=")
Result.append (query_parameter.secret)
Result.append ("&cx=")
Result.append (query_parameter.cx)
Result.append ("&q=")
Result.append (query_parameter.query)
-- num
if attached query_parameter.num as l_num then
Result.append ("&num=")
Result.append (l_num)
end
if attached query_parameter.start as l_start then
Result.append ("&start=")
Result.append (l_start)
end
end
put_error (a_message: READABLE_STRING_GENERAL)
-- put error message `a_message'.
local
l_errors: like errors
utf: UTF_CONVERTER
do
l_errors := errors
if l_errors = Void then
create {ARRAYED_LIST [STRING]} l_errors.make (1)
errors := l_errors
end
l_errors.force (utf.utf_32_string_to_utf_8_string_8 (a_message))
end
item (a_item: JSON_OBJECT): GCSE_PAGE_ITEM
-- Google Result Metadata Item.
do
create Result
if attached {JSON_STRING} a_item.item ("kind") as l_kind then
Result.set_kind (l_kind.item)
end
if attached {JSON_STRING} a_item.item ("title") as l_title then
Result.set_title (l_title.item)
end
if attached {JSON_STRING} a_item.item ("htmlTitle") as l_htmltitle then
Result.set_html_title (l_htmltitle.unescaped_string_32)
end
if attached {JSON_STRING} a_item.item ("link") as l_link then
Result.set_link (l_link.item)
end
if attached {JSON_STRING} a_item.item ("displayLink") as l_display_link then
Result.set_display_link (l_display_link.item)
end
if attached {JSON_STRING} a_item.item ("snippet") as l_snippet then
Result.set_snippet (l_snippet.unescaped_string_8)
end
if attached {JSON_STRING} a_item.item ("htmlSnippet") as l_html_snippet then
Result.set_html_snippet (l_html_snippet.unescaped_string_32)
end
if attached {JSON_STRING} a_item.item ("formattedUrl") as l_formatted_url then
Result.set_formatted_url (l_formatted_url.item)
end
end
query_page (a_page_key: JSON_STRING; a_queries: JSON_OBJECT): detachable GCSE_PAGE
-- Google result medata query. Return a query page based for a query with page key `a_page_key', if any.
do
if
attached {JSON_ARRAY} a_queries.item (a_page_key) as jquerypage and then
jquerypage.count > 0 and then
attached {JSON_OBJECT} jquerypage.i_th (1) as jpage
then
create Result
if attached {JSON_STRING} jpage.item ("title") as l_title then
Result.set_title (l_title.item)
end
if attached {JSON_STRING} jpage.item ("totalResults") as l_results then
Result.set_total_results (l_results.item.to_integer)
end
if attached {JSON_STRING} jpage.item ("searchTerms") as l_search_terms then
Result.set_search_terms (l_search_terms.item)
end
-- TODO check if we should use INTEGER_64
if attached {JSON_NUMBER} jpage.item ("count") as l_count then
Result.set_count (l_count.integer_64_item.as_integer_32)
end
if attached {JSON_NUMBER} jpage.item ("startIndex") as l_index then
Result.set_start_index (l_index.integer_64_item.as_integer_32)
end
end
end
feature {NONE} -- JSON Keys
queries_key: STRING = "queries"
next_page_key: STRING = "nextPage"
request_key: STRING = "request"
previous_page_key: STRING = "previousPage"
items_key: STRING = "items"
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,118 @@
note
description: "Represent metadata describing the query for the current set of results."
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
revision: "$Revision: 97973 $"
class
GCSE_PAGE
inherit
DEBUG_OUTPUT
feature -- Access
search_terms: detachable STRING_8
-- search term
title: detachable STRING_8
-- Search title.
total_results: INTEGER
-- Search total results.
count: INTEGER
-- Rows per page.
start_index: INTEGER
-- Page index.
feature -- Element change
set_search_terms (a_search_terms: like search_terms)
-- Assign `search_terms' with `a_search_terms'.
do
search_terms := a_search_terms
ensure
search_terms_assigned: search_terms = a_search_terms
end
feature -- Change element
set_title (a_title: like title)
-- Set title with `a_title'
do
title := a_title
ensure
title_set: title = a_title
end
set_total_results (a_total_results: like total_results)
-- Set total_results with `a_total_results'.
do
total_results := a_total_results
ensure
total_results_set: total_results = a_total_results
end
set_count (a_count: like count)
-- Set count with `a_count'.
do
count := a_count
ensure
count_set: count = a_count
end
set_start_index (a_start_index: like start_index)
-- Set start_index with `a_start_index'.
do
start_index := a_start_index
ensure
start_index_set: start_index = a_start_index
end
feature -- Status report
debug_output: STRING_8
-- <Precursor>
do
create Result.make_from_string ("%NPage details%N")
if attached title as l_title then
Result.append ("Title:")
Result.append (l_title)
Result.append_character ('%N')
end
if attached search_terms as l_search_tearm then
Result.append ("Search Tearm:")
Result.append (l_search_tearm)
Result.append_character ('%N')
end
Result.append ("Count:")
Result.append (count.out)
Result.append_character ('%N')
Result.append ("Total Result:")
Result.append (count.out)
Result.append_character ('%N')
Result.append ("Count:")
Result.append (total_results.out)
Result.append_character ('%N')
Result.append ("Start index:")
Result.append (start_index.out)
Result.append_character ('%N')
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,208 @@
note
description: "Represent a search result, include the URL, title and text snippets that describe the result"
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
revision: "$Revision: 97973 $"
class
GCSE_PAGE_ITEM
inherit
DEBUG_OUTPUT
feature -- Access
html_formatted_url: detachable STRING_8
-- Html formatted url of this result
formatted_url: detachable STRING_8
-- Formatted url of this result
cache_id: detachable STRING_8
-- Cache id of this result
html_snippet: detachable STRING_8
-- Html snippet of this request
snippet: detachable STRING_8
-- Snippet of this result
display_link: detachable STRING_8
-- Display link of this result
link: detachable STRING_8
-- link of this result
html_title: detachable STRING_8
-- html title of result
title: detachable STRING_8
-- title of result.
kind: detachable STRING_8
-- Kind of actual search result.
page_map: detachable GCSE_PAGE_MAP
-- Page map
--! Not supported for now.
feature -- Element change
set_html_formatted_url (a_html_formatted_url: like html_formatted_url)
-- Assign `html_formatted_url' with `a_html_formatted_url'.
do
html_formatted_url := a_html_formatted_url
ensure
html_formatted_url_assigned: html_formatted_url = a_html_formatted_url
end
set_formatted_url (a_formatted_url: like formatted_url)
-- Assign `formatted_url' with `a_formatted_url'.
do
formatted_url := a_formatted_url
ensure
formatted_url_assigned: formatted_url = a_formatted_url
end
set_cache_id (a_cache_id: like cache_id)
-- Assign `cache_id' with `a_cache_id'.
do
cache_id := a_cache_id
ensure
cache_id_assigned: cache_id = a_cache_id
end
set_html_snippet (a_html_snippet: like html_snippet)
-- Assign `html_snippet' with `a_html_snippet'.
do
html_snippet := a_html_snippet
ensure
html_snippet_assigned: html_snippet = a_html_snippet
end
set_snippet (a_snippet: like snippet)
-- Assign `snippet' with `a_snippet'.
do
snippet := a_snippet
ensure
snippet_assigned: snippet = a_snippet
end
set_display_link (a_display_link: like display_link)
-- Assign `display_link' with `a_display_link'.
do
display_link := a_display_link
ensure
display_link_assigned: display_link = a_display_link
end
set_link (a_link: like link)
-- Assign `link' with `a_link'.
do
link := a_link
ensure
link_assigned: link = a_link
end
set_html_title (a_html_title: like html_title)
-- Assign `html_title' with `a_html_title'.
do
html_title := a_html_title
ensure
html_title_assigned: html_title = a_html_title
end
set_title (a_title: like title)
-- Assign `title' with `a_title'.
do
title := a_title
ensure
title_assigned: title = a_title
end
set_kind (a_kind: like kind)
-- Assign `kind' with `a_kind'.
do
kind := a_kind
ensure
kind_assigned: kind = a_kind
end
set_page_map (a_map: like page_map)
-- Assign `kind' with `a_kind'.
do
page_map := a_map
ensure
page_map_assigned: page_map = a_map
end
feature -- Output
debug_output: STRING_8
-- <Precursor>
do
create Result.make_from_string ("%NPage Item details%N")
if attached title as l_title then
Result.append ("Title:")
Result.append (l_title)
Result.append_character ('%N')
end
if attached kind as l_kind then
Result.append ("Kind:")
Result.append (l_kind)
Result.append_character ('%N')
end
if attached html_title as l_html_title then
Result.append ("Html title:")
Result.append (l_html_title)
Result.append_character ('%N')
end
if attached link as l_link then
Result.append ("Link:")
Result.append (l_link)
Result.append_character ('%N')
end
if attached display_link as l_display_link then
Result.append ("Display link:")
Result.append (l_display_link)
Result.append_character ('%N')
end
if attached snippet as l_snippet then
Result.append ("Snippet:")
Result.append (l_snippet)
Result.append_character ('%N')
end
if attached html_snippet as l_html_snippet then
Result.append ("Html snippet:")
Result.append (l_html_snippet)
Result.append_character ('%N')
end
if attached cache_id as l_cache_id then
Result.append ("Cache_id:")
Result.append (l_cache_id)
Result.append_character ('%N')
end
if attached formatted_url as l_formatted_url then
Result.append ("Formatted url:")
Result.append (l_formatted_url)
Result.append_character ('%N')
end
if attached html_formatted_url as l_html_formatted_url then
Result.append ("Html formatted url:")
Result.append (l_html_formatted_url)
Result.append_character ('%N')
end
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,41 @@
note
description: "[
Represent a google page map
"pagemap": {
"cse_image": [
{
"src": "https://www.eiffel.org/portal/files/userpictures/picture-40.jpg"
}
],
"cse_thumbnail": [
{
"width": "81",
"height": "61",
"src": "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRnC-RKzps6BFItx_MLYBVskFI7U6u0y3VJBInomPYEF5sO6gkip94mLw"
}
]
}
]"
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
revision: "$Revision: 97973 $"
EIS: "name=PageMaps", "src=https://developers.google.com/custom-search/docs/structured_data#pagemaps", "protocol=url"
class
GCSE_PAGE_MAP
feature -- Access
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,238 @@
note
description: "[
Represent google custom search parameters
Example url template
"template": "https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&cref={cref?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
]"
optional_parameters: "[
Optional parameters
c2coff string Enables or disables Simplified and Traditional Chinese Search.
The default value for this parameter is 0 (zero), meaning that the feature is enabled. Supported values are:
1: Disabled
0: Enabled (default)
cr string Restricts search results to documents originating in a particular country.
You may use Boolean operators in the cr parameter's value.
Google Search determines the country of a document by analyzing:
the top-level domain (TLD) of the document's URL
the geographic location of the Web server's IP address
See the Country Parameter Values page for a list of valid values for this parameter.
cref string The URL of a linked custom search engine specification to use for this request.
Does not apply for Google Site Search
If both cx and cref are specified, the cx value is used
cx string The custom search engine ID to use for this request.
If both cx and cref are specified, the cx value is used.
dateRestrict string Restricts results to URLs based on date. Supported values include:
d[number]: requests results from the specified number of past days.
w[number]: requests results from the specified number of past weeks.
m[number]: requests results from the specified number of past months.
y[number]: requests results from the specified number of past years.
exactTerms string Identifies a phrase that all documents in the search results must contain.
excludeTerms string Identifies a word or phrase that should not appear in any documents in the search results.
fileType string Restricts results to files of a specified extension. A list of file types indexable by Google can be found in Webmaster Tools Help Center.
filter string Controls turning on or off the duplicate content filter.
See Automatic Filtering for more information about Google's search results filters. Note that host crowding filtering applies only to multi-site searches.
By default, Google applies filtering to all search results to improve the quality of those results.
Acceptable values are:
"0": Turns off duplicate content filter.
"1": Turns on duplicate content filter.
gl string Geolocation of end user.
The gl parameter value is a two-letter country code. The gl parameter boosts search results whose country of origin matches the parameter value. See the Country Codes page for a list of valid values.
Specifying a gl parameter value should lead to more relevant results. This is particularly true for international customers and, even more specifically, for customers in English- speaking countries other than the United States.
googlehost string The local Google domain (for example, google.com, google.de, or google.fr) to use to perform the search.
highRange string
Specifies the ending value for a search range.
Use lowRange and highRange to append an inclusive search range of lowRange...highRange to the query.
hl string Sets the user interface language.
Explicitly setting this parameter improves the performance and the quality of your search results.
See the Interface Languages section of Internationalizing Queries and Results Presentation for more information, and Supported Interface Languages for a list of supported languages.
hq string Appends the specified query terms to the query, as if they were combined with a logical AND operator.
imgColorType string Returns black and white, grayscale, or color images: mono, gray, and color.
Acceptable values are:
"color": color
"gray": gray
"mono": mono
imgDominantColor string Returns images of a specific dominant color.
Acceptable values are:
"black": black
"blue": blue
"brown": brown
"gray": gray
"green": green
"pink": pink
"purple": purple
"teal": teal
"white": white
"yellow": yellow
imgSize string Returns images of a specified size.
Acceptable values are:
"huge": huge
"icon": icon
"large": large
"medium": medium
"small": small
"xlarge": xlarge
"xxlarge": xxlarge
imgType string Returns images of a type.
Acceptable values are:
"clipart": clipart
"face": face
"lineart": lineart
"news": news
"photo": photo
linkSite string Specifies that all search results should contain a link to a particular URL
lowRange string Specifies the starting value for a search range.
Use lowRange and highRange to append an inclusive search range of lowRange...highRange to the query.
lr string Restricts the search to documents written in a particular language (e.g., lr=lang_ja).
Acceptable values are:
"lang_ar": Arabic
"lang_bg": Bulgarian
"lang_ca": Catalan
"lang_cs": Czech
"lang_da": Danish
"lang_de": German
"lang_el": Greek
"lang_en": English
"lang_es": Spanish
"lang_et": Estonian
"lang_fi": Finnish
"lang_fr": French
"lang_hr": Croatian
"lang_hu": Hungarian
"lang_id": Indonesian
"lang_is": Icelandic
"lang_it": Italian
"lang_iw": Hebrew
"lang_ja": Japanese
"lang_ko": Korean
"lang_lt": Lithuanian
"lang_lv": Latvian
"lang_nl": Dutch
"lang_no": Norwegian
"lang_pl": Polish
"lang_pt": Portuguese
"lang_ro": Romanian
"lang_ru": Russian
"lang_sk": Slovak
"lang_sl": Slovenian
"lang_sr": Serbian
"lang_sv": Swedish
"lang_tr": Turkish
"lang_zh-CN": Chinese (Simplified)
"lang_zh-TW": Chinese (Traditional)
orTerms string Provides additional search terms to check for in a document, where each document in the search results must contain at least one of the additional search terms.
relatedSite string Specifies that all search results should be pages that are related to the specified URL.
rights string Filters based on licensing. Supported values include: cc_publicdomain, cc_attribute, cc_sharealike, cc_noncommercial, cc_nonderived, and combinations of these.
safe string Search safety level.
Acceptable values are:
"high": Enables highest level of SafeSearch filtering.
"medium": Enables moderate SafeSearch filtering.
"off": Disables SafeSearch filtering. (default)
searchType string Specifies the search type: image. If unspecified, results are limited to webpages.
Acceptable values are:
"image": custom image search.
siteSearch string Specifies all search results should be pages from a given site.
siteSearchFilter string Controls whether to include or exclude results from the site named in the siteSearch parameter.
Acceptable values are:
"e": exclude
"i": include
sort string The sort expression to apply to the results.
]"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
EIS: "GCSE parameters", "src=https://developers.google.com/custom-search/json-api/v1/reference/cse/list", "protocol=URI"
class
GCSE_QUERY_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_secret_key, a_cx, a_query: READABLE_STRING_8)
-- Create an object GCSE_QUERY_PARAMETERS with secret key `a_secret_key' and a custom search engine id `a_cx'.
-- and query `a_query'.
do
-- TODO
-- At the moment the API only use cx as Google Custom Search id.
-- Custom search engine ID - Use either cx or cref to specify the custom search engine you want to use to perform this search
secret := a_secret_key
cx := a_cx
query := a_query
ensure
secret_set: secret.same_string (a_secret_key)
cx_set: cx.same_string (a_cx)
query_set: query.same_string (a_query)
end
feature -- Access : Required Parameters
secret: READABLE_STRING_8
-- Required. The shared key between your site and Google Custom Search Engine.
cx: READABLE_STRING_8
-- Custom search engine id to perform this search.
query: READABLE_STRING_8
-- Search query, query parameter to specify your search expression.
feature -- Optional Parameters
num : detachable STRING_8
-- Number of search results to return.
-- Valid values are integers between 1 and 10, inclusive.
start: detachable STRING_8
-- The index of the first result to return.
feature -- Change Elements
set_num (a_num: READABLE_STRING_8)
require
is_number: a_num.is_integer
valid_range: a_num.to_integer >= 1 and then a_num.to_integer <= 10
do
num := a_num
ensure
num_set: num = a_num
valid_rage_set: attached num as l_num and then l_num.to_integer >= 1 and then l_num.to_integer <= 10
end
set_start (a_start: READABLE_STRING_8)
require
is_number: a_start.is_integer
valid_start: a_start.to_integer >= 1
do
start := a_start
ensure
start_set: start = a_start
valid_start_set: attached start as l_start and then l_start.to_integer >= 1
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,115 @@
note
description: "[
Represent search request metadata
URL: search template used for the current results.
Queries: current, next and previous page.
Context
Search infromation
Items: array of actual search results.
]"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
class
GCSE_RESPONSE
--! TODO
--! All suppport for for url, context and search information.
feature -- Access
current_page: detachable GCSE_PAGE
-- Metadata describing the query for the current set of results.
-- This role is always present in the response.
-- It is always an array with just one element.
next_page: detachable GCSE_PAGE
-- Metadata describing the query to use for the next page of results.
-- This role is not present if the current results are the last page. Note: This API returns up to the first 100 results only.
-- When present, it is always a array with just one element.
previous_page: detachable GCSE_PAGE
-- Metadata describing the query to use for the previous page of results.
-- Not present if the current results are the first page.
-- When present, it is always a array with just one element.
items: detachable LIST [GCSE_PAGE_ITEM]
-- Contains the actual search results. The search results include the URL, title and text snippets that describe the result.
feature -- Change Element
set_current_page (a_page: GCSE_PAGE)
-- Set `current_page' with `a_page'.
do
current_page := a_page
ensure
current_page_set: current_page = a_page
end
set_next_page (a_page: GCSE_PAGE)
-- Set `next_page' with `a_page'.
do
next_page := a_page
ensure
next_page_set: next_page = a_page
end
set_previous_page (a_page: GCSE_PAGE)
-- Set `previous_page' with `a_page'.
do
previous_page := a_page
ensure
previous_page_set: previous_page = a_page
end
add_item (a_item: GCSE_PAGE_ITEM)
-- Add item `a_item' to the list of items.
local
l_items: like items
do
l_items := items
if l_items = Void then
create {ARRAYED_LIST[GCSE_PAGE_ITEM]}l_items.make (10)
items := l_items
end
l_items.force (a_item)
end
feature -- Acess: HTTP Response
status: INTEGER
-- HTTP status code.
status_message: detachable READABLE_STRING_8
-- associated textual phrase for the response status.
feature -- Change Element: HTTP Response
set_status (a_status: like status)
-- Set `status' with `a_status'.
do
status := a_status
ensure
status_set: status = a_status
end
set_status_nessage (a_message: like status_message)
-- Set `status_message' with `a_message'.
do
status_message := a_message
ensure
status_message_set: status_message = a_message
end
;note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,80 @@
note
description : "test application root class"
date : "$Date: 2015-12-02 10:27:38 -0300 (mi. 02 de dic. de 2015) $"
revision : "$Revision: 98180 $"
class
APPLICATION
inherit
ARGUMENTS
create
make
feature {NONE} -- Initialization
make
-- Run application.
local
gcse: GCSE_API
l_parameters: GCSE_QUERY_PARAMETERS
do
create l_parameters.make (key, cx, "scoop")
create gcse.make (l_parameters)
gcse.search
if attached {GCSE_RESPONSE} gcse.last_result as l_result then
if attached l_result.current_page as l_page then
print ("Current Page%N")
print (l_page.debug_output)
end
if attached l_result.next_page as l_page then
print ("Next Page%N")
print (l_page.debug_output)
end
if attached l_result.previous_page as l_page then
print ("Previous Page%N")
print (l_page.debug_output)
end
if attached l_result.items as l_items then
print ("Number of items:" + l_items.count.out)
across l_items as ic loop print (ic.item.debug_output) end
end
if attached l_result.next_page as l_page then
l_parameters.set_start (l_page.start_index.out)
gcse.search
end
end
if attached {GCSE_RESPONSE} gcse.last_result as l_result then
if attached l_result.current_page as l_page then
print ("Current Page%N")
print (l_page.debug_output)
end
if attached l_result.next_page as l_page then
print ("Next Page%N")
print (l_page.debug_output)
end
if attached l_result.previous_page as l_page then
print ("Previous Page%N")
print (l_page.debug_output)
end
if attached l_result.items as l_items then
print ("Number of items:" + l_items.count.out)
across l_items as ic loop print (ic.item.debug_output) end
end
end
end
feature {NONE} -- Implementation
Key: STRING = "AIzaSyBKAXNofo-RqZb6kUmpbiCwPEy7n7-E51k"
cx : STRING = "015017565055626880074:9gdgp1fvt-g"
end

View File

@@ -0,0 +1,31 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
testing: "type/manual"
class
GCSE_API_TEST_SET
inherit
EQA_TEST_SET
feature -- Test routines
feature {NONE} -- Implementation
has_error (l_captcha: GCSE_API; a_error: READABLE_STRING_32): BOOLEAN
do
if attached l_captcha.errors as l_errors then
l_errors.compare_objects
Result := l_errors.has (a_error)
end
end
end

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="test">
<target name="test">
<root class="APPLICATION" feature="make"/>
<option warning="true" void_safety="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="gcse" location="..\gcse-safe.ecf" readonly="false"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<cluster name="test" location=".\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client_extension" uuid="EA6A381D-2E78-448C-8A6D-B71759F1082E" library_target="http_client_extension">
<target name="http_client_extension">
<root all_classes="true"/>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<cluster name="http_client_extension" location=".\src\" recursive="true">
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client_extension" uuid="DD90A4FA-1B7F-4C8C-A739-AE67D6F40384" library_target="http_client_extension">
<target name="http_client_extension">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="http_client_extension" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,199 @@
note
description: "Represent an HTTP request."
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
class
REQUEST
inherit
HTTP_CONSTANTS
create
make
feature {NONE} -- Initialization
make (a_method: READABLE_STRING_8; a_uri: READABLE_STRING_8)
require
valid_http_method: is_http_method (a_method)
valid_uri: is_valid_uri (a_uri)
do
verb := a_method
uri := a_uri
create headers.make (5)
ensure
ver_set: verb = a_method
uri_set: uri = a_uri
end
feature -- Status Report
is_valid_uri (a_uri: READABLE_STRING_8): BOOLEAN
local
l_uri: URI
do
create l_uri.make_from_string (a_uri)
Result := l_uri.is_valid
end
query_string: detachable READABLE_STRING_8
local
l_uri: URI
do
create l_uri.make_from_string (uri)
Result := l_uri.query
end
sanitized_url: READABLE_STRING_8
-- Returns the URL without the query string part
local
l_uri: URI
do
create l_uri.make_from_string (uri)
l_uri.remove_query
Result := l_uri.string
ensure
sanitized: not as_uri (Result).has_query
end
is_http_method (a_method: READABLE_STRING_GENERAL): BOOLEAN
do
if a_method.same_string (method_connect) then
Result := True
elseif a_method.same_string (method_delete) then
Result := True
elseif a_method.same_string (method_get) then
Result := True
elseif a_method.same_string (method_head) then
Result := True
elseif a_method.same_string (method_options) then
Result := True
elseif a_method.same_string (method_patch) then
Result := True
elseif a_method.same_string (method_post) then
Result := True
elseif a_method.same_string (method_put) then
Result := True
elseif a_method.same_string (method_trace) then
Result := True
end
end
feature -- Constants
content_type_header_name: STRING_8 = "Content-Type";
default_content_type: STRING
once
Result := application_json
end
feature -- Access
uri: READABLE_STRING_8
verb: READABLE_STRING_8
headers: STRING_TABLE [READABLE_STRING_8]
payload: detachable READABLE_STRING_8
executor: detachable REQUEST_EXECUTOR
feature -- Change Element
add_payload (a_payload: like payload)
do
payload := a_payload
ensure
payload_set: attached payload as l_payload implies l_payload = a_payload
end
add_header (key: READABLE_STRING_8; value: READABLE_STRING_8)
do
headers.force (value, key)
end
feature -- Execute
execute: detachable RESPONSE
do
initialize_executor
Result := execute_request
end
initialize_executor
do
create executor.make (uri, verb)
end
feature {NONE} -- Implementation
execute_request: detachable RESPONSE
do
if attached executor as l_executor then
-- add headers
add_headers (l_executor)
if verb.same_string (method_put) or else verb.same_string (method_post) or else verb.same_string (method_patch) then
l_executor.set_body (body_contents)
end
if not l_executor.context_executor.headers.has (content_type_header_name) then
l_executor.context_executor.add_header (content_type_header_name, default_content_type)
end
if attached l_executor.execute as l_response then
create Result.make (l_response)
end
end
end
feature {NONE} -- Implementation
add_headers (a_executor: REQUEST_EXECUTOR)
local
l_context_executor: HTTP_CLIENT_REQUEST_CONTEXT
s: READABLE_STRING_GENERAL
utf: UTF_CONVERTER
do
l_context_executor := a_executor.context_executor
across
headers as ic
loop
s := ic.key
if s.is_valid_as_string_8 then
l_context_executor.add_header (s.as_string_8, ic.item)
else
l_context_executor.add_header (utf.utf_32_string_to_utf_8_string_8 (s), ic.item)
end
end
end
body_contents: READABLE_STRING_8
do
if attached payload as l_payload then
Result := l_payload
else
Result := ""
end
end
as_uri (a_string: READABLE_STRING_8): URI
require
is_valid_uri: is_valid_uri (a_string)
do
create Result.make_from_string (a_string)
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,97 @@
note
description: "Executes an HTTP request"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
class
REQUEST_EXECUTOR
inherit
HTTP_CLIENT_HELPER
HTTP_CONSTANTS
create
make
feature {NONE} -- Initialization
make (a_url: READABLE_STRING_8; a_method: READABLE_STRING_8)
do
set_base_url (a_url)
verb := a_method
ensure
base_url_set: base_url.same_string (a_url)
method_set: verb.same_string (a_method)
end
set_base_url (a_url: READABLE_STRING_8)
-- Set base_url with `a_url'
local
s: STRING
do
create s.make_from_string (a_url)
s.left_adjust
s.right_adjust
base_url := s
ensure
base_url_set: a_url.has_substring (base_url)
end
feature -- Access
verb: READABLE_STRING_8
-- HTTP METHOD (Get, Post, ...)
body: detachable READABLE_STRING_8
-- body content
feature -- Element Change
set_body (a_body: like body)
-- Set body with `a_body'.
do
body := a_body
ensure
body_set: body = a_body
end
feature -- Execute
execute: detachable HTTP_CLIENT_RESPONSE
-- Http executor
do
if verb.same_string (method_connect) then
Result := Void -- not supported for now
elseif verb.same_string (method_delete) then
Result := execute_delete ("")
elseif verb.same_string (method_get) then
Result := execute_get ("")
elseif verb.same_string (method_head) then
Result := Void
elseif verb.same_string (method_options) then
Result := Void
elseif verb.same_string (method_patch) then
Result := execute_patch ("", body)
elseif verb.same_string (method_post) then
Result := execute_post ("", body)
elseif verb.same_string (method_put) then
Result := execute_put ("", body)
elseif verb.same_string (method_trace) then
Result := Void
end
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,57 @@
note
description: "Represent and HTTP Response"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
class
RESPONSE
create
make
feature {NONE} --Initialization
make (a_response: HTTP_CLIENT_RESPONSE)
do
http_response := a_response
body := a_response.body
status := a_response.status
headers := a_response.headers
status_message := a_response.status_line
error_message := a_response.error_message
ensure
http_reponse_set: http_response = a_response
headers_set: headers = a_response.headers
status_set: status = a_response.status
status_message_set: status_message = a_response.status_line
error_message_set: error_message = a_response.error_message
end
feature -- Access
status: INTEGER
status_message: detachable READABLE_STRING_8
error_message: detachable READABLE_STRING_8
body: detachable READABLE_STRING_8
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
feature {NONE} -- Implementation
http_response: HTTP_CLIENT_RESPONSE;
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,99 @@
note
description: "Wrapper class for HTTP_CLIENT_SESSION"
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision: "$Revision: 97966 $"
deferred class
HTTP_CLIENT_HELPER
feature -- Access
http_session: detachable HTTP_CLIENT_SESSION
get_http_session
local
h: LIBCURL_HTTP_CLIENT
b: like base_url
do
create h.make
b := base_url
if b = Void then
b := ""
end
if attached {HTTP_CLIENT_SESSION} h.new_session (base_url) as sess then
http_session := sess
sess.set_timeout (-1)
sess.set_connect_timeout (-1)
sess.set_is_insecure (True)
sess.set_any_auth_type
debug ("curl")
sess.set_is_debug (True)
end
debug ("proxy8888")
sess.set_proxy ("127.0.0.1", 8888) --| inspect traffic with http://www.fiddler2.com/
end
end
end
feature -- HTTP client helpers
execute_get (command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.get (command_name, context_executor)
end
end
execute_post (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.post (command_name, context_executor, data)
end
end
execute_delete (command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.delete (command_name, context_executor)
end
end
execute_put (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.put (command_name, context_executor, data)
end
end
execute_patch (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.patch (command_name, context_executor, data)
end
end
context_executor: HTTP_CLIENT_REQUEST_CONTEXT
-- request context for each request
once
create Result.make
end
base_url: STRING;
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -86,8 +86,23 @@ feature -- Query
sql_post_execution
end
sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute an sql query change `a_sql_statement' with the params `a_params'.
sql_finalize
-- <Precursor>
do
-- N/A
end
sql_insert (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- <Precursor>
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
db_handler.execute_change
sql_post_execution
end
sql_modify (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- <Precursor>
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
@@ -133,4 +148,44 @@ feature -- Query
end
end
sql_read_integer_32 (a_index: INTEGER): INTEGER_32
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
i64: INTEGER_64
do
l_item := sql_item (a_index)
if attached {INTEGER_32} l_item as i then
Result := i
elseif attached {INTEGER_32_REF} l_item as l_value then
Result := l_value.item
else
if attached {INTEGER_64} l_item as i then
i64 := i
elseif attached {INTEGER_64_REF} l_item as l_value then
i64 := l_value.item
else
check is_integer_32: False end
end
if i64 <= {INTEGER_32}.max_value then
Result := i64.to_integer_32
else
check is_integer_32: False end
end
end
end
sql_read_date_time (a_index: INTEGER): detachable DATE_TIME
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {DATE_TIME} l_item as dt then
Result := dt
else
check is_date_time_or_null: l_item = Void end
end
end
end

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="persistence_sqlite3" uuid="4E536C92-A09F-4305-8230-2EC5ABC51416" library_target="persistence_sqlite3">
<target name="persistence_sqlite3">
<root all_classes="true"/>
<option warning="true" void_safety="all">
<assertions precondition="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="app_env" location="..\..\app_env\app_env-safe.ecf"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\..\cms-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging-safe.ecf"/>
<library name="model" location="..\..\model\cms_model-safe.ecf"/>
<library name="sqlite3" location="$ISE_LIBRARY\unstable\library\persistency\database\sqlite3\sqlite-safe.ecf" readonly="false">
<option msil_application_optimize="false">
<assertions precondition="true" supplier_precondition="true"/>
</option>
</library>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="persistence_sqlite" location=".\src\" recursive="true">
<file_rule>
<exclude>/old$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,450 @@
note
description: "Summary description for {CMS_STORAGE_MYSQL}."
date: "$Date: 2015-02-09 22:29:56 +0100 (lun., 09 févr. 2015) $"
revision: "$Revision: 96596 $"
class
CMS_STORAGE_SQLITE3
inherit
CMS_STORAGE_SQL
redefine
sql_read_date_time, sql_read_integer_32,
sql_read_string_32
end
CMS_CORE_STORAGE_SQL_I
redefine
sql_read_date_time, sql_read_integer_32,
sql_read_string_32
end
CMS_USER_STORAGE_SQL_I
redefine
sql_read_date_time, sql_read_integer_32,
sql_read_string_32
end
SQLITE_BIND_ARG_MARSHALLER
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
make (db: SQLITE_DATABASE)
do
sqlite := db
create error_handler.make
end
sqlite: SQLITE_DATABASE
-- Associated SQLite database.
feature -- Status report
is_initialized: BOOLEAN
-- Is storage initialized?
do
Result := has_user
end
feature -- Status report
is_available: BOOLEAN
-- Is storage available?
do
Result := sqlite.is_interface_usable
end
feature -- Basic operation
close
-- Close/disconnect current storage.
do
sqlite.close
end
feature -- Execution
transaction_depth: INTEGER
sql_begin_transaction
-- Start a database transtaction.
do
if transaction_depth = 0 then
sqlite.begin_transaction (False)
end
transaction_depth := transaction_depth + 1
debug ("roc_storage")
print ("# sql_begin_transaction (depth="+ transaction_depth.out +").%N")
end
end
sql_rollback_transaction
-- Rollback updates in the database.
do
if sqlite.is_in_transaction then
sqlite.rollback
end
transaction_depth := transaction_depth - 1
debug ("roc_storage")
print ("# sql_rollback_transaction (depth="+ transaction_depth.out +").%N")
end
end
sql_commit_transaction
-- Commit updates in the database.
do
if sqlite.is_in_transaction then
sqlite.commit
end
transaction_depth := transaction_depth - 1
debug ("roc_storage")
print ("# sql_commit_transaction (depth="+ transaction_depth.out +").%N")
end
end
sql_post_execution
-- Post database execution.
-- note: execute after each `sql_query' and `sql_change'.
do
debug ("roc_storage")
print ("# sql_post_execution.%N")
end
-- FIXME
if sqlite.has_error then
write_critical_log (generator + ".post_execution Error occurred!")
end
end
feature -- Operation
last_statement: detachable SQLITE_STATEMENT
last_sqlite_result_cursor: detachable SQLITE_STATEMENT_ITERATION_CURSOR
sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- <Precursor>
local
st: SQLITE_QUERY_STATEMENT
do
debug ("roc_storage")
print ("> sql_query (" +a_sql_statement + ").%N")
end
last_sqlite_result_cursor := Void
create st.make (a_sql_statement, sqlite)
last_statement := st
if st.is_compiled then
if a_params /= Void then
check st.has_arguments end
last_sqlite_result_cursor := st.execute_new_with_arguments (sqlite_arguments (a_params))
else
last_sqlite_result_cursor := st.execute_new
end
else
error_handler.add_custom_error (1, "invalid query", "query compilation failed!")
end
debug ("roc_storage")
print ("< sql_query (" +a_sql_statement + ").%N")
end
end
sql_finalize
-- Finalize sql query (i.e destroy previous query statement.
do
debug ("roc_storage")
print ("> sql_finalize.%N")
end
if attached last_statement as st then
st.cleanup
end
if attached last_sqlite_result_cursor as cur then
if cur.statement /= last_statement then
check should_not_occurs: False end
cur.statement.cleanup
end
last_sqlite_result_cursor := Void
end
last_statement := Void
debug ("roc_storage")
print ("< sql_finalize.%N")
end
end
sql_insert (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- <Precursor>
local
st: SQLITE_INSERT_STATEMENT
do
debug ("roc_storage")
print ("> sql_insert (" +a_sql_statement + ").%N")
end
last_sqlite_result_cursor := Void
create st.make (a_sql_statement, sqlite)
last_statement := st
if st.is_compiled then
if a_params /= Void then
check st.has_arguments end
last_sqlite_result_cursor := st.execute_new_with_arguments (sqlite_arguments (a_params))
else
last_sqlite_result_cursor := st.execute_new
end
else
error_handler.add_custom_error (1, "invalid query", "query compilation failed!")
end
debug ("roc_storage")
print ("< sql_insert (" +a_sql_statement + ").%N")
end
end
sql_modify (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- <Precursor>
local
st: SQLITE_MODIFY_STATEMENT
do
debug ("roc_storage")
print ("> sql_modify (" +a_sql_statement + ").%N")
end
last_sqlite_result_cursor := Void
create st.make (a_sql_statement, sqlite)
last_statement := st
if st.is_compiled then
if a_params /= Void then
check st.has_arguments end
last_sqlite_result_cursor := st.execute_new_with_arguments (sqlite_arguments (a_params))
else
last_sqlite_result_cursor := st.execute_new
end
else
error_handler.add_custom_error (1, "invalid query", "query compilation failed!")
end
debug ("roc_storage")
print ("< sql_modify (" +a_sql_statement + ").%N")
end
end
sqlite_arguments (a_params: STRING_TABLE [detachable ANY]): ARRAYED_LIST [SQLITE_BIND_ARG [ANY]]
local
k: READABLE_STRING_GENERAL
k8: STRING
utf: UTF_CONVERTER
do
create Result.make (a_params.count)
across
a_params as ic
loop
k := ic.key
if k.is_valid_as_string_8 then
k8 := k.as_string_8
else
k8 := utf.utf_32_string_to_utf_8_string_8 (k)
end
if attached {DATE_TIME} ic.item as dt then
Result.force (new_binding_argument (date_time_to_string (dt), ":" + k8))
elseif attached {READABLE_STRING_32} ic.item as s32 then
Result.force (new_binding_argument (utf.utf_32_string_to_utf_8_string_8 (s32), ":" + k8))
else
Result.force (new_binding_argument (ic.item, ":" + k8))
end
end
end
date_time_to_string (dt: DATE_TIME): STRING
do
create Result.make (16)
Result.append_integer (dt.year)
Result.append_character ('-')
if dt.month <= 9 then
Result.append_character ('0')
end
Result.append_integer (dt.month)
Result.append_character ('-')
if dt.day <= 9 then
Result.append_character ('0')
end
Result.append_integer (dt.day)
Result.append_character (' ')
if dt.hour <= 9 then
Result.append_character ('0')
end
Result.append_integer (dt.hour)
Result.append_character (':')
if dt.minute <= 9 then
Result.append_character ('0')
end
Result.append_integer (dt.minute)
Result.append_character (':')
if dt.second <= 9 then
Result.append_character ('0')
end
Result.append_integer (dt.second)
end
string_to_date_time (a_string: READABLE_STRING_GENERAL): DATE_TIME
local
y,m,d: INTEGER
h,min,sec: INTEGER
s: detachable READABLE_STRING_GENERAL
i,j: INTEGER
do
i := 1
-- YYYY
j := a_string.index_of ('-', i)
s := a_string.substring (i, j - 1)
y := s.to_integer
i := j + 1
-- /MM
j := a_string.index_of ('-', i)
s := a_string.substring (i, j - 1)
m := s.to_integer
i := j + 1
-- /DD
j := a_string.index_of (' ', i)
s := a_string.substring (i, j - 1)
d := s.to_integer
i := j + 1
-- %THour
j := a_string.index_of (':', i)
s := a_string.substring (i, j - 1)
h := s.to_integer
i := j + 1
-- :Min
j := a_string.index_of (':', i)
s := a_string.substring (i, j - 1)
min := s.to_integer
i := j + 1
-- :Sec
j := a_string.count + 1
s := a_string.substring (i, j - 1)
sec := s.to_integer
create Result.make (y,m,d,h,min,sec)
end
feature -- Access
sql_start
-- <Precursor>.
do
-- sqlite cursor `last_sqlite_result_cursor', already at first position if any.
end
sql_after: BOOLEAN
-- <Precursor>.
do
if attached last_sqlite_result_cursor as l_cursor then
Result := l_cursor.after
end
end
sql_forth
-- <Precursor>.
do
if attached last_sqlite_result_cursor as l_cursor then
l_cursor.forth
end
end
sql_valid_item_index (a_index: INTEGER): BOOLEAN
local
l_row: SQLITE_RESULT_ROW
do
if attached last_sqlite_result_cursor as l_cursor then
l_row := l_cursor.item
Result := a_index > 0 and a_index.to_natural_32 <= l_row.count
end
end
sql_item (a_index: INTEGER): detachable ANY
local
l_row: SQLITE_RESULT_ROW
do
if attached last_sqlite_result_cursor as l_cursor then
l_row := l_cursor.item
Result := l_row.value (a_index.to_natural_32)
end
end
sql_read_string_32 (a_index: INTEGER): detachable STRING_32
-- <Precursor>
local
utf: UTF_CONVERTER
do
Result := Precursor (a_index)
if Result = Void then
if attached sql_read_string (a_index) as s8 then
Result := utf.utf_8_string_8_to_string_32 (s8)
end
end
end
sql_read_integer_32 (a_index: INTEGER): INTEGER_32
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
i64: INTEGER_64
do
l_item := sql_item (a_index)
if attached {INTEGER_32} l_item as i then
Result := i
elseif attached {INTEGER_32_REF} l_item as l_value then
Result := l_value.item
else
if attached {INTEGER_64} l_item as i then
i64 := i
elseif attached {INTEGER_64_REF} l_item as l_value then
i64 := l_value.item
else
check is_integer_32: False end
end
if i64 <= {INTEGER_32}.max_value then
Result := i64.to_integer_32
else
check is_integer_32: False end
end
end
end
sql_read_date_time (a_index: INTEGER): detachable DATE_TIME
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {DATE_TIME} l_item as dt then
Result := dt
elseif attached {READABLE_STRING_GENERAL} l_item as s then
Result := string_to_date_time (s)
else
check is_date_time_nor_null: l_item = Void end
end
end
feature -- Conversion
sql_statement (a_statement: STRING): STRING
-- <Precursor>.
local
i: INTEGER
do
Result := a_statement
from
i := 1
until
i = 0
loop
i := a_statement.substring_index ("AUTO_INCREMENT", i)
if i > 0 then
if Result = a_statement then
create Result.make_from_string (a_statement)
end
Result.remove (i + 4)
i := i + 14
end
end
end
end

View File

@@ -0,0 +1,90 @@
note
description: "[
Objects that ...
]"
author: "$Author: jfiat $"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
CMS_STORAGE_SQLITE3_BUILDER
inherit
CMS_STORAGE_SQL_BUILDER
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
end
feature -- Factory
storage (a_setup: CMS_SETUP; a_error_handler: ERROR_HANDLER): detachable CMS_STORAGE_SQLITE3
local
s: detachable READABLE_STRING_32
p: PATH
db: detachable SQLITE_DATABASE
l_source: SQLITE_FILE_SOURCE
i,j: INTEGER
do
if
attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (a_setup.environment.application_config_path) as l_database_config
then
if l_database_config.driver.is_case_insensitive_equal ("sqlite3") then
s := l_database_config.database_string
i := s.substring_index ("Database=", 1)
if i > 0 then
i := s.index_of ('=', i) + 1
j := s.index_of (';', i)
if j = 0 then
j := s.count + 1
end
create p.make_from_string (s.substring (i, j - 1))
else
create p.make_from_string (s)
end
if attached reuseable_connection.item as d then
if p.same_as (d.path) then
db := d.database
end
end
if db = Void or else db.is_closed then
create l_source.make (p.name)
create db.make (l_source)
if l_source.exists then
db.open_read_write
else
db.open_create_read_write
end
end
if not db.is_closed then
db.set_busy_timeout (1_000) -- FIXME
create Result.make (db)
-- 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
else
a_error_handler.add_custom_error (0, "Could not connect to the ODBC storage", Void)
end
else
-- Wrong mapping between storage name and storage builder!
end
end
end
reuseable_connection: CELL [detachable TUPLE [path: PATH; database: SQLITE_DATABASE]]
once
create Result.put (Void)
end
end

View File

@@ -35,6 +35,7 @@ feature -- Factory
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.
set_use_extended_types (True) --| Use extended types: STRING_32 etc.
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)

View File

@@ -48,6 +48,7 @@ feature -- Factory
if conn.is_connected then
create Result.make_with_driver (conn, l_database_config.item ("Driver"))
set_map_zero_null_value (False) --| This way we map 0 to 0, instead of Null as default.
set_use_extended_types (True) --| Use extended types: STRING_32 etc.
if Result.is_available then
if not Result.is_initialized then
initialize (a_setup, Result)

View File

@@ -9,7 +9,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks,
setup_hooks,
permissions
end
@@ -47,18 +47,27 @@ feature -- Access: router
configure_web (a_api: CMS_API; a_router: WSF_ROUTER)
local
l_admin_handler: CMS_ADMIN_HANDLER
l_modules_handler: CMS_ADMIN_MODULES_HANDLER
l_users_handler: CMS_ADMIN_USERS_HANDLER
l_roles_handler: CMS_ADMIN_ROLES_HANDLER
l_user_handler: CMS_USER_HANDLER
l_role_handler: CMS_ROLE_HANDLER
l_admin_cache_handler: CMS_ADMIN_CACHE_HANDLER
l_admin_export_handler: CMS_ADMIN_EXPORT_HANDLER
l_uri_mapping: WSF_URI_MAPPING
do
create l_admin_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin", l_admin_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_modules_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/modules", l_modules_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_users_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/users", l_users_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
@@ -67,6 +76,14 @@ feature -- Access: router
create l_uri_mapping.make_trailing_slash_ignored ("/admin/roles", l_roles_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_admin_cache_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/cache", l_admin_cache_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_admin_export_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/export", l_admin_export_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_user_handler.make (a_api)
a_router.handle ("/admin/add/user", l_user_handler, a_router.methods_get_post)
a_router.handle ("/admin/user/{id}", l_user_handler, a_router.methods_get)
@@ -90,15 +107,20 @@ feature -- Security
Result.force ("admin users")
Result.force ("admin roles")
Result.force ("admin modules")
Result.force ("install modules")
Result.force ("admin core caches")
Result.force ("clear blocks cache")
Result.force ("admin export")
Result.force ("export core")
end
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- <Precursor>
do
a_response.subscribe_to_menu_system_alter_hook (Current)
a_response.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
end
response_alter (a_response: CMS_RESPONSE)
@@ -117,8 +139,21 @@ feature -- Hooks
-- TODO: we should probably use more side menu and less primary_menu.
create lnk.make ("Admin", "admin")
lnk.set_permission_arguments (<<"manage " + {CMS_ADMIN_MODULE}.name>>)
a_menu_system.primary_menu.extend (lnk)
a_menu_system.management_menu.extend (lnk)
end
create lnk.make ("Module", "admin/modules")
lnk.set_permission_arguments (<<"manage module">>)
a_menu_system.management_menu.extend (lnk)
-- Per module cache permission!
create lnk.make ("Cache", "admin/cache")
a_menu_system.management_menu.extend (lnk)
-- Per module export permission!
create lnk.make ("Export", "admin/export")
a_menu_system.management_menu.extend (lnk)
end
note

View File

@@ -0,0 +1,93 @@
note
description: "[
Administrate cache functionality.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_ADMIN_CACHE_HANDLER
inherit
CMS_HANDLER
WSF_URI_HANDLER
rename
new_mapping as new_uri_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get,
do_post
end
REFACTORING_HELPER
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_response: CMS_RESPONSE
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_response: CMS_RESPONSE
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := clear_cache_web_form (l_response)
f.process (l_response)
if
attached f.last_data as fd and then
fd.is_valid
then
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_clear_all_caches) then
api.hooks.invoke_clear_cache (Void, l_response)
l_response.add_notice_message ("Caches cleared (if allowed)!")
else
fd.report_error ("Invalid form data!")
end
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end
feature -- Widget
clear_cache_web_form (a_response: CMS_RESPONSE): CMS_FORM
local
but: WSF_FORM_SUBMIT_INPUT
do
create Result.make (a_response.url (a_response.location, Void), "form_clear_cache")
create but.make_with_text ("op", text_clear_all_caches)
Result.extend (but)
end
feature -- Interface text.
text_clear_all_caches: STRING_32 = "Clear all caches"
end

View File

@@ -0,0 +1,115 @@
note
description: "[
Administrate export functionality.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_ADMIN_EXPORT_HANDLER
inherit
CMS_HANDLER
WSF_URI_HANDLER
rename
new_mapping as new_uri_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get,
do_post
end
REFACTORING_HELPER
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_response: CMS_RESPONSE
s: STRING
f: CMS_FORM
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_web_form (l_response)
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_response: CMS_RESPONSE
s: STRING
f: CMS_FORM
l_exportation_parameters: CMS_EXPORT_PARAMETERS
do
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
f := exportation_web_form (l_response)
f.process (l_response)
if
attached f.last_data as fd and then
fd.is_valid
then
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_export_all_data) then
if attached fd.string_item ("folder") as l_folder then
create l_exportation_parameters.make (api.site_location.extended ("export").extended (l_folder))
else
create l_exportation_parameters.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss")))
end
api.hooks.invoke_export_to (Void, l_exportation_parameters, l_response)
l_response.add_notice_message ("All data exported (if allowed)!")
create s.make_empty
across
l_exportation_parameters.logs as ic
loop
s.append (ic.item)
s.append ("<br/>")
s.append_character ('%N')
end
l_response.add_notice_message (s)
else
fd.report_error ("Invalid form data!")
end
end
create s.make_empty
f.append_to_html (l_response.wsf_theme, s)
l_response.set_main_content (s)
l_response.execute
end
feature -- Widget
exportation_web_form (a_response: CMS_RESPONSE): CMS_FORM
local
f_name: WSF_FORM_TEXT_INPUT
but: WSF_FORM_SUBMIT_INPUT
do
create Result.make (a_response.url (a_response.location, Void), "export_all_data")
Result.extend_raw_text ("Export CMS data to ")
create f_name.make_with_text ("folder", (create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss"))
f_name.set_label ("Export folder name")
f_name.set_description ("Folder name under 'exports' folder.")
f_name.set_is_required (True)
Result.extend (f_name)
create but.make_with_text ("op", text_export_all_data)
Result.extend (but)
end
feature -- Interface text.
text_export_all_data: STRING_32 = "Export all data"
end

View File

@@ -0,0 +1,320 @@
note
description: "[
Administrate modules.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_ADMIN_MODULES_HANDLER
inherit
CMS_HANDLER
WSF_URI_HANDLER
rename
new_mapping as new_uri_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get, do_post
end
REFACTORING_HELPER
CMS_SETUP_ACCESS
CMS_ACCESS
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
s: STRING
f: CMS_FORM
l_denied: BOOLEAN
do
if
attached {WSF_STRING} req.query_parameter ("op") as l_op and then l_op.same_string ("uninstall") and then
attached {WSF_TABLE} req.query_parameter ("module_uninstallation") as tb
then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached api.setup.string_8_item ("admin.installation_access") as l_access then
if l_access.is_case_insensitive_equal ("none") then
l_denied := True
elseif l_access.is_case_insensitive_equal ("permission") then
l_denied := not r.has_permission ("install modules")
end
else
l_denied := True
end
if l_denied then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("You do not have permission to access CMS module uninstallation procedure!")
else
create s.make_empty
across
tb as ic
loop
if attached api.setup.modules.item_by_name (ic.item.string_representation) as l_module then
if api.is_module_installed (l_module) then
api.uninstall_module (l_module)
if api.is_module_installed (l_module) then
s.append ("<p>ERROR: Module " + l_module.name + " failed to be uninstalled!</p>")
else
s.append ("<p>Module " + l_module.name + " was successfully uninstalled.</p>")
end
else
s.append ("<p>Module " + l_module.name + " is not installed.</p>")
end
end
end
s.append (r.link ("Back to modules management", r.location, Void))
r.set_main_content (s)
end
r.execute
else
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
f := modules_collection_web_form (r)
create s.make_empty
f.append_to_html (r.wsf_theme, s)
r.set_page_title ("Modules")
r.set_main_content (s)
r.execute
end
end
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
s: STRING
f: CMS_FORM
l_denied: BOOLEAN
do
if attached {WSF_STRING} req.item ("op") as l_op then
if l_op.same_string ("Install modules") then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached api.setup.string_8_item ("admin.installation_access") as l_access then
if l_access.is_case_insensitive_equal ("none") then
l_denied := True
elseif l_access.is_case_insensitive_equal ("permission") then
l_denied := not r.has_permission ("install modules")
end
else
l_denied := True
end
if l_denied then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("You do not have permission to access CMS module installation procedure!")
else
f := modules_collection_web_form (r)
if l_op.same_string ("Install modules") then
f.submit_actions.extend (agent on_installation_submit)
f.process (r)
elseif l_op.same_string ("uninstall") then
f.submit_actions.extend (agent on_uninstallation_submit)
f.process (r)
end
if
not attached f.last_data as l_data or else
not l_data.is_valid
then
r.add_error_message ("Error occurred.")
create s.make_empty
f.append_to_html (r.wsf_theme, s)
r.set_page_title ("Modules")
r.set_main_content (s)
else
r.add_notice_message ("Operation on module(s) succeeded.")
r.set_redirection (r.location)
end
end
r.execute
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.execute
end
else
do_get (req, res)
end
end
modules_collection_web_form (a_response: CMS_RESPONSE): CMS_FORM
local
mod: CMS_MODULE
f_cb: WSF_FORM_CHECKBOX_INPUT
w_tb: WSF_WIDGET_TABLE
w_row: WSF_WIDGET_TABLE_ROW
w_item: WSF_WIDGET_TABLE_ITEM
w_submit: WSF_FORM_SUBMIT_INPUT
w_set: WSF_FORM_FIELD_SET
l_mods_to_install: ARRAYED_LIST [CMS_MODULE]
do
create Result.make (a_response.url (a_response.location, Void), "modules_collection")
create w_tb.make
w_tb.add_css_class ("modules_table")
create w_row.make (5)
create w_item.make_with_text ("Enabled ")
w_row.add_item (w_item)
create w_item.make_with_text ("Module")
w_row.add_item (w_item)
create w_item.make_with_text ("Version")
w_row.add_item (w_item)
create w_item.make_with_text ("Description")
w_row.add_item (w_item)
w_tb.add_head_row (w_row)
create l_mods_to_install.make (0)
across
a_response.api.setup.modules as ic
loop
mod := ic.item
if not a_response.api.is_module_installed (mod) then
l_mods_to_install.extend (mod)
else
create w_row.make (5)
create f_cb.make ("module_" + mod.name)
f_cb.set_text_value (mod.name)
f_cb.set_checked (mod.is_enabled)
f_cb.set_is_readonly (True)
create w_item.make_with_content (f_cb)
w_row.add_item (w_item)
create w_item.make_with_text (mod.name)
w_row.add_item (w_item)
create w_item.make_with_text (mod.version)
w_row.add_item (w_item)
if attached mod.description as l_desc then
create w_item.make_with_text (l_desc)
w_row.add_item (w_item)
else
create w_item.make_with_text ("")
w_row.add_item (w_item)
end
create w_item.make_with_text (a_response.link ("Uninstall", a_response.location + "?op=uninstall&module_uninstallation[]=" + mod.name, Void))
w_row.add_item (w_item)
w_tb.add_row (w_row)
end
end
create w_set.make
w_set.set_legend ("Installed modules")
w_set.extend (w_tb)
-- create w_submit.make ("op")
-- w_submit.set_text_value ("Save")
-- w_set.extend (w_submit)
Result.extend (w_set)
Result.extend_html_text ("<br/>")
if not l_mods_to_install.is_empty then
create w_tb.make
w_tb.add_css_class ("modules_table")
create w_row.make (3)
create w_item.make_with_text ("Install ")
w_row.add_item (w_item)
create w_item.make_with_text ("Module")
w_row.add_item (w_item)
create w_item.make_with_text ("Description")
w_row.add_item (w_item)
w_tb.add_head_row (w_row)
across
l_mods_to_install as ic
loop
mod := ic.item
create w_row.make (3)
create f_cb.make ("module_installation[" + mod.name + "]")
f_cb.set_text_value (mod.name)
create w_item.make_with_content (f_cb)
w_row.add_item (w_item)
create w_item.make_with_text (mod.name)
w_row.add_item (w_item)
if attached mod.description as l_desc then
create w_item.make_with_text (l_desc)
w_row.add_item (w_item)
else
create w_item.make_with_text ("")
w_row.add_item (w_item)
end
w_tb.add_row (w_row)
end
create w_set.make
w_set.set_legend ("Available modules for installation")
w_set.extend (w_tb)
create w_submit.make ("op")
w_submit.set_text_value ("Install modules")
w_set.extend (w_submit)
Result.extend (w_set)
end
end
on_installation_submit (fd: WSF_FORM_DATA)
local
l_mods: CMS_MODULE_COLLECTION
do
if attached {WSF_TABLE} fd.table_item ("module_installation") as tb and then not tb.is_empty then
l_mods := api.setup.modules
across
tb as ic
loop
if
attached {WSF_STRING} ic.item as l_mod_name and then
attached l_mods.item_by_name (l_mod_name.value) as m
then
api.install_module (m)
if not api.is_module_installed (m) then
fd.report_error ("Installation failed for module " + m.name)
end
else
fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value)
end
end
else
fd.report_error ("No module to install!")
end
end
on_uninstallation_submit (fd: WSF_FORM_DATA)
local
l_mods: CMS_MODULE_COLLECTION
do
if attached {WSF_TABLE} fd.table_item ("module_uninstallation") as tb and then not tb.is_empty then
l_mods := api.setup.modules
across
tb as ic
loop
if
attached {WSF_STRING} ic.item as l_mod_name and then
attached l_mods.item_by_name (l_mod_name.value) as m
then
api.uninstall_module (m)
if api.is_module_installed (m) then
fd.report_error ("Un-Installation failed for module " + m.name)
end
else
fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value)
end
end
else
fd.report_error ("No module to uninstall!")
end
end
end

View File

@@ -8,30 +8,10 @@ class
inherit
CMS_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Process
process

View File

@@ -93,7 +93,7 @@ feature -- HTTP Methods
s.append ("<a href=%"")
s.append (req.absolute_script_url ("/admin/role/" + u.id.out))
s.append ("%">")
s.append (u.name)
s.append (html_encoded (u.name))
s.append ("</a>")
s.append ("</li>%N")
end

View File

@@ -8,32 +8,12 @@ class
inherit
CMS_RESPONSE
redefine
make,
initialize
end
CMS_SHARED_SORTING_UTILITIES
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Query
role_id_path_parameter (req: WSF_REQUEST): INTEGER_64
@@ -84,7 +64,7 @@ feature -- Process Edit
do
create b.make_empty
f := new_edit_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate(?,a_role, b))
f.submit_actions.extend (agent edit_form_submit(?, a_role, b))
@@ -117,7 +97,7 @@ feature -- Process Delete
do
create b.make_empty
f := new_delete_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
@@ -149,7 +129,7 @@ feature -- Process New
do
create b.make_empty
f := new_edit_form (l_role, url (request.percent_encoded_path_info, Void), "create-role")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent new_form_validate(?, b))
f.submit_actions.extend (agent edit_form_submit(?, l_role, b))

View File

@@ -8,31 +8,10 @@ class
inherit
CMS_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Query
role_id_path_parameter (req: WSF_REQUEST): INTEGER_64

View File

@@ -106,7 +106,7 @@ feature -- HTTP Methods
s.append ("<a href=%"")
s.append (req.absolute_script_url ("/admin/user/"+u.id.out))
s.append ("%">")
s.append (u.name)
s.append (html_encoded (u.name))
s.append ("</a>")
s.append ("</li>%N")
end

View File

@@ -7,32 +7,11 @@ class
CMS_USER_FORM_RESPONSE
inherit
CMS_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Query
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
@@ -86,7 +65,7 @@ feature -- Process Edit
do
create b.make_empty
f := new_edit_form (a_user, url (location, Void), "edit-user")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.submit_actions.extend (agent edit_form_submit (?, a_user, b))
f.process (Current)
@@ -118,7 +97,7 @@ feature -- Process Delete
do
create b.make_empty
f := new_delete_form (a_user, url (location, Void), "edit-user")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
@@ -151,7 +130,7 @@ feature -- Process New
do
create b.make_empty
f := new_edit_form (l_user, url (location, Void), "create-user")
invoke_form_alter (f, fd)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent new_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_user, b))

View File

@@ -8,31 +8,10 @@ class
inherit
CMS_RESPONSE
redefine
make,
initialize
end
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;)
do
create {WSF_NULL_THEME} wsf_theme.make
Precursor (req, res, a_api)
end
initialize
do
Precursor
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
end
wsf_theme: WSF_THEME
feature -- Query
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64

View File

@@ -9,10 +9,10 @@ class
inherit
CMS_MODULE
redefine
register_hooks
setup_hooks,
permissions
end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_VALUE_TABLE_ALTER
@@ -52,6 +52,13 @@ feature -- Access
name: STRING = "auth"
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("account register")
end
feature -- Access: docs
root_dir: PATH
@@ -91,12 +98,12 @@ feature -- Router
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_response)
a_response.subscribe_to_block_hook (Current)
a_response.subscribe_to_value_table_alter_hook (Current)
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
@@ -123,6 +130,7 @@ feature -- Hooks configuration
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
end
end
feature -- Handler
@@ -227,6 +235,7 @@ feature -- Handler
end
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("You can also contact the webmaster to ask for an account.")
end
r.execute
@@ -521,26 +530,26 @@ feature {NONE} -- Helpers
feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local
-- vals: CMS_VALUE_TABLE
do
if attached template_block (a_block_id, a_response) as l_tpl_block then
-- create vals.make (1)
-- -- add the variable to the block
-- value_table_alter (vals, a_response)
-- across
-- vals as ic
-- loop
-- l_tpl_block.set_value (ic.item, ic.key)
-- get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
-- local
---- vals: CMS_VALUE_TABLE
-- do
-- if attached template_block (a_block_id, a_response) as l_tpl_block then
---- create vals.make (1)
---- -- add the variable to the block
---- value_table_alter (vals, a_response)
---- across
---- vals as ic
---- loop
---- l_tpl_block.set_value (ic.item, ic.key)
---- end
-- a_response.put_required_block (l_tpl_block, "content")
-- else
-- debug ("cms")
-- a_response.add_warning_message ("Error with block [" + a_block_id + "]")
-- end
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
-- end
-- end
get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
@@ -579,7 +588,6 @@ feature {NONE} -- Block views
end
end
get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.request.is_get_request_method then

View File

@@ -13,7 +13,7 @@ inherit
CMS_MODULE
redefine
filters,
register_hooks
setup_hooks
end
CMS_HOOK_AUTO_REGISTER
@@ -101,12 +101,12 @@ feature {NONE} -- Implementation: routes
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_response)
a_response.subscribe_to_block_hook (Current)
a_response.subscribe_to_value_table_alter_hook (Current)
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end
feature -- Hooks

View File

@@ -47,6 +47,7 @@ feature -- HTTP Methods
l_page: CMS_RESPONSE
l_url: STRING
i: INTEGER
l_message: STRING
do
api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void)
if attached req.query_parameter ("prompt") as l_prompt then
@@ -72,6 +73,10 @@ feature -- HTTP Methods
l_page.set_status_code ({HTTP_STATUS_CODE}.found)
l_page.set_redirection (l_url)
end
create l_message.make_from_string (logout_message)
l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login"))
l_message.replace_substring_all ("$site_home", req.absolute_script_url (""))
l_page.set_main_content (l_message)
l_page.execute
end
end
@@ -113,4 +118,15 @@ feature -- HTTP Methods
end
end
feature {NONE}-- Lougout Message
logout_message: STRING = "[
<div class="cms-logout-message">
<h2>You are now signed out</h2>
<p>You can <a href="$site_login">log</a> in again, or go to the <a href="$site_home">front page</a>.</p>
</div>
]"
end

View File

@@ -11,7 +11,7 @@ ROC_AUTH.login = function() {
var username = form.username.value;
var password = form.password.value;
//var host = form.host.value;
var origin = window.location.origin.concat(window.location.pathname);
var origin = window.location.origin + window.location.pathname;
var _login = function(){
@@ -25,7 +25,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
}
}else{
@@ -49,7 +49,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
}
}
@@ -93,7 +93,7 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}else{
@@ -122,8 +122,8 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$("body").append(newdiv);
$("#imgProgressRedirect").hide();
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
@@ -322,4 +322,4 @@ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword;
}
}

View File

@@ -5,12 +5,12 @@
<div>
<form name="cms_basic_auth" action method="POST">
<div>
<input type="text" name="username" required>
<input type="text" name="username" id="username" required>
<label>Username</label>
</div>
<div>
<input type="password" name="password" required>
<input type="password" name="password" id="password" required>
<label>Password</label>
</div>

View File

@@ -50,10 +50,10 @@ feature -- Access
feature -- Access: node
summary: detachable READABLE_STRING_8
summary: detachable READABLE_STRING_32
-- A short summary of the node.
content: detachable READABLE_STRING_8
content: detachable READABLE_STRING_32
-- Content of the node.
format: detachable READABLE_STRING_8

View File

@@ -36,7 +36,7 @@ feature {NONE} -- Initialization
Precursor
-- Create the node storage for type blog
if attached {CMS_STORAGE_SQL_I} storage as l_storage_sql then
if attached storage.as_sql_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
@@ -97,6 +97,21 @@ feature -- Access node
Result := nodes_to_blogs (blog_storage.blogs_from_user_limited (a_user, a_limit, a_offset))
end
feature -- Conversion
full_blog_node (a_blog: CMS_BLOG): CMS_BLOG
-- If `a_blog' is partial, return the full blog node from `a_blog',
-- otherwise return directly `a_blog'.
require
a_blog_set: a_blog /= Void
do
if attached {CMS_BLOG} node_api.full_node (a_blog) as l_full_blog then
Result := l_full_blog
else
Result := a_blog
end
end
feature {NONE} -- Helpers
nodes_to_blogs (a_nodes: LIST [CMS_NODE]): ARRAYED_LIST [CMS_BLOG]

View File

@@ -14,6 +14,8 @@
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="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="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>

View File

@@ -12,7 +12,7 @@ inherit
rename
module_api as blog_api
redefine
register_hooks,
setup_hooks,
initialize,
install,
blog_api
@@ -22,6 +22,10 @@ inherit
CMS_HOOK_RESPONSE_ALTER
CMS_HOOK_EXPORT
CMS_EXPORT_NODE_UTILITIES
create
make
@@ -61,8 +65,8 @@ feature {CMS_API} -- Module Initialization
loop
ct.extend_format (ic.item)
end
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_node_type (ct)
l_node_api.add_node_type_webform_manager (create {CMS_BLOG_NODE_TYPE_WEBFORM_MANAGER}.make (ct, l_node_api))
-- Add support for CMS_BLOG, which requires a storage extension to store the optional "tags" value
-- For now, we only have extension based on SQL statement.
@@ -79,7 +83,7 @@ feature {CMS_API} -- Module management
sql: STRING
do
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if attached api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then
sql := "[
CREATE TABLE blog_post_nodes(
@@ -149,10 +153,11 @@ feature -- Access: router
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_response.subscribe_to_menu_system_alter_hook (Current)
a_response.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_export_hook (Current)
end
response_alter (a_response: CMS_RESPONSE)
@@ -168,4 +173,74 @@ feature -- Hooks
create lnk.make ("Blogs", "blogs/")
a_menu_system.primary_menu.extend (lnk)
end
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
-- Export data identified by `a_export_id_list',
-- or export all data if `a_export_id_list' is Void.
local
n: CMS_BLOG
p: PATH
d: DIRECTORY
f: PLAIN_TEXT_FILE
lst: LIST [CMS_BLOG]
do
if
a_export_id_list = Void
or else across a_export_id_list as ic some ic.item.same_string ("blog") end
then
if
a_response.has_permissions (<<"export any node", "export blog">>) and then
attached blog_api as l_blog_api
then
lst := l_blog_api.blogs_order_created_desc
a_export_parameters.log ("Exporting " + lst.count.out + " blogs")
across
lst as ic
loop
n := l_blog_api.full_blog_node (ic.item)
a_export_parameters.log (n.content_type + " #" + n.id.out)
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
create d.make_with_path (p.parent)
if not d.exists then
d.recursive_create_dir
end
create f.make_with_path (p)
if not f.exists or else f.is_access_writable then
f.open_write
f.put_string (json_to_string (blog_node_to_json (n)))
f.close
end
-- Revisions.
if
attached node_api as l_node_api and then
attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1
then
a_export_parameters.log (n.content_type + " " + l_revisions.count.out + " revisions.")
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
across
l_revisions as revs_ic
loop
if attached {CMS_BLOG} revs_ic.item as l_blog then
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
if not f.exists or else f.is_access_writable then
f.open_write
f.put_string (json_to_string (blog_node_to_json (l_blog)))
end
f.close
end
end
end
end
end
end
end
blog_node_to_json (a_blog: CMS_BLOG): JSON_OBJECT
do
Result := node_to_json (a_blog)
end
end

View File

@@ -13,7 +13,7 @@ inherit
populate_form,
update_node,
new_node,
append_html_output_to
append_content_as_html_to
end
create
@@ -76,34 +76,24 @@ feature -- form
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
append_content_as_html_to (a_node: CMS_BLOG; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
-- <Precursor>
local
s: STRING
do
Precursor (a_node, a_response)
if attached a_response.main_content as l_main_content then
s := l_main_content
else
create s.make_empty
end
Precursor (a_node, is_teaser, a_output, a_response)
if attached {CMS_BLOG} a_node as l_blog_post then
if attached l_blog_post.tags as l_tags then
s.append ("<div><strong>Tags:</strong> ")
a_output.append ("<div><strong>Tags:</strong> ")
across
l_tags as ic
loop
s.append ("<span class=%"tag%">")
s.append (a_response.html_encoded (ic.item))
s.append ("</span> ")
a_output.append ("<span class=%"tag%">")
a_output.append (cms_api.html_encoded (ic.item))
a_output.append ("</span> ")
end
s.append ("</div>")
a_output.append ("</div>")
end
end
a_response.set_main_content (s)
end
end

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