Compare commits

...

26 Commits

Author SHA1 Message Date
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
101 changed files with 4802 additions and 640 deletions

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

@@ -29,16 +29,16 @@
<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_openid_module" location="..\..\modules\openid\openid-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_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/>
<!--
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" />
-->

View File

@@ -12,3 +12,4 @@ set ROC_CMS_DIR=%~dp0
%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,10 +85,10 @@ feature -- Access: router
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.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]

View File

@@ -9,30 +9,17 @@ management.conditions[]=is_front
feed.news.weight=3
feed.news.region=feed_news
feed.news.region=content
feed.news.condition=<none>
feed.news.condition=is_front
feed.forum.weight=2
feed.forum.region=feed_forum
feed.forum.region=<none>
feed.forum.condition=<none>
feed.forum.options[size]=15
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]=3
#Aliases
&aliases[foo]=management
&aliases[bar]=feed.forum
foo.region=content
foo.condition=is_front
foo.weight=-10
bar.region=content
bar.condition=is_front
bar.title=Bar
recent_changes.options[size]=4

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(){
@@ -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

@@ -0,0 +1,21 @@
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;
}

View File

@@ -0,0 +1,21 @@
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;
}
}
}

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

@@ -90,3 +90,14 @@ ul.horizontal li {
border: solid 1px red;
padding: 5px 2px 5px 2px;
}
ul.taxonomy-entities {
list-style-type: none;
padding: 0;
}
ul.taxonomy-entities li {
padding: 0;
margin-top: 5px;
margin-bottom: 10px;
border-top: dotted 1px #ccc;
}

View File

@@ -95,3 +95,14 @@ ul.horizontal {
border: solid 1px red;
padding: 5px 2px 5px 2px;
}
ul.taxonomy-entities {
list-style-type: none;
padding: 0;
li {
padding: 0;
margin-top: 5px;
margin-bottom: 10px;
border-top: dotted 1px #ccc;
}
}

View File

@@ -66,6 +66,10 @@ feature -- CMS modules
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

View File

@@ -442,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)

View File

@@ -7,7 +7,6 @@ class
GCSE_PAGE_ITEM
inherit
DEBUG_OUTPUT
feature -- Access
@@ -139,62 +138,62 @@ feature -- Element change
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
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"

View File

@@ -1,7 +1,7 @@
note
description : "test application root class"
date : "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
revision : "$Revision: 97966 $"
date : "$Date: 2015-12-02 10:27:38 -0300 (mi. 02 de dic. de 2015) $"
revision : "$Revision: 98180 $"
class
APPLICATION
@@ -27,20 +27,20 @@ feature {NONE} -- Initialization
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.to_string)
print (l_page.debug_output)
end
if attached l_result.next_page as l_page then
print ("Next Page%N")
print (l_page.to_string)
print (l_page.debug_output)
end
if attached l_result.previous_page as l_page then
print ("Previous Page%N")
print (l_page.to_string)
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.to_string) end
across l_items as ic loop print (ic.item.debug_output) end
end
if attached l_result.next_page as l_page then
@@ -52,20 +52,20 @@ feature {NONE} -- Initialization
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.to_string)
print (l_page.debug_output)
end
if attached l_result.next_page as l_page then
print ("Next Page%N")
print (l_page.to_string)
print (l_page.debug_output)
end
if attached l_result.previous_page as l_page then
print ("Previous Page%N")
print (l_page.to_string)
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.to_string) end
across l_items as ic loop print (ic.item.debug_output) end
end
end

View File

@@ -6,7 +6,6 @@
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<precompile name="base_pre" location="$ISE_PRECOMP\base-safe.ecf"/>
<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"/>

View File

@@ -9,7 +9,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks,
setup_hooks,
permissions
end
@@ -47,6 +47,8 @@ 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
@@ -54,6 +56,7 @@ feature -- Access: router
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
@@ -61,6 +64,10 @@ feature -- Access: router
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)
@@ -73,6 +80,10 @@ feature -- Access: router
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)
@@ -84,8 +95,6 @@ feature -- Access: router
a_router.handle ("/admin/role/{id}", l_role_handler, a_router.methods_get)
a_router.handle ("/admin/role/{id}/edit", l_role_handler, a_router.methods_get_post)
a_router.handle ("/admin/role/{id}/delete", l_role_handler, a_router.methods_get_post)
end
feature -- Security
@@ -101,15 +110,17 @@ feature -- Security
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.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.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)
@@ -129,9 +140,19 @@ feature -- Hooks
create lnk.make ("Admin", "admin")
lnk.set_permission_arguments (<<"manage " + {CMS_ADMIN_MODULE}.name>>)
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

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 (create {CMS_TO_WSF_THEME}.make (l_response, l_response.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 (create {CMS_TO_WSF_THEME}.make (l_response, l_response.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 (create {CMS_TO_WSF_THEME}.make (r, r.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 (create {CMS_TO_WSF_THEME}.make (r, r.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

@@ -84,7 +84,7 @@ feature -- Process Edit
do
create b.make_empty
f := new_edit_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
hooks.invoke_form_alter (f, fd, Current)
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 +117,7 @@ feature -- Process Delete
do
create b.make_empty
f := new_delete_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
hooks.invoke_form_alter (f, fd, Current)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
@@ -149,7 +149,7 @@ feature -- Process New
do
create b.make_empty
f := new_edit_form (l_role, url (request.percent_encoded_path_info, Void), "create-role")
hooks.invoke_form_alter (f, fd, Current)
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

@@ -86,7 +86,7 @@ feature -- Process Edit
do
create b.make_empty
f := new_edit_form (a_user, url (location, Void), "edit-user")
hooks.invoke_form_alter (f, fd, Current)
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 +118,7 @@ feature -- Process Delete
do
create b.make_empty
f := new_delete_form (a_user, url (location, Void), "edit-user")
hooks.invoke_form_alter (f, fd, Current)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
@@ -151,7 +151,7 @@ feature -- Process New
do
create b.make_empty
f := new_edit_form (l_user, url (location, Void), "create-user")
hooks.invoke_form_alter (f, fd, Current)
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

@@ -9,7 +9,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks
setup_hooks
end
@@ -91,12 +91,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.hooks.subscribe_to_block_hook (Current)
a_response.hooks.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)

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.hooks.subscribe_to_block_hook (Current)
a_response.hooks.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

@@ -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(){
@@ -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

@@ -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.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.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

View File

@@ -232,7 +232,7 @@ feature -- HTML Output
if attached api.format (n.format) as f then
f.append_formatted_to (l_summary, a_output)
else
page.formats.default_format.append_formatted_to (l_summary, a_output)
api.formats.default_format.append_formatted_to (l_summary, a_output)
end
a_output.append ("<br />")
a_output.append (page.link ("See more...", lnk.location, Void))

View File

@@ -6,8 +6,12 @@ note
deferred class
CMS_BLOG_STORAGE_I
inherit
CMS_NODE_STORAGE_I
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access

View File

@@ -12,7 +12,7 @@ inherit
module_api as feed_aggregator_api
redefine
initialize,
register_hooks,
setup_hooks,
permissions,
feed_aggregator_api
end
@@ -181,13 +181,13 @@ feature -- Handle
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
a_response.hooks.subscribe_to_block_hook (Current)
a_response.hooks.subscribe_to_response_alter_hook (Current)
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.subscribe_to_cache_hook (Current)
a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_cache_hook (Current)
end
feature -- Hook

View File

@@ -40,9 +40,7 @@ feature {NONE} -- Initialization
local
ct: CMS_PAGE_NODE_TYPE
do
-- Initialize content types.
create content_types.make (1)
create content_type_webform_managers.make (1)
-- Initialize node content types.
create ct
--| For now, add all available formats to content type `ct'.
across
@@ -50,8 +48,8 @@ feature {NONE} -- Initialization
loop
ct.extend_format (ic.item)
end
add_content_type (ct)
add_content_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct))
add_node_type (ct)
add_node_type_webform_manager (create {CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER}.make (ct, Current))
end
feature {CMS_MODULE} -- Access nodes storage.
@@ -60,15 +58,18 @@ feature {CMS_MODULE} -- Access nodes storage.
feature -- Content type
content_types: ARRAYED_LIST [CMS_CONTENT_TYPE]
-- Available content types
add_node_type (a_type: CMS_NODE_TYPE [CMS_NODE])
-- Register node content type `a_type'.
do
cms_api.add_content_type (a_type)
end
node_types: ARRAYED_LIST [attached like node_type]
-- Node content types.
do
create Result.make (content_types.count)
create Result.make (cms_api.content_types.count)
across
content_types as ic
cms_api.content_types as ic
loop
if attached {like node_type} ic.item as l_node_type then
Result.extend (l_node_type)
@@ -76,32 +77,11 @@ feature -- Content type
end
end
add_content_type (a_type: CMS_CONTENT_TYPE)
-- Register content type `a_type'.
do
content_types.force (a_type)
end
content_type (a_name: READABLE_STRING_GENERAL): detachable CMS_CONTENT_TYPE
-- Content type named `a_named' if any.
do
across
content_types as ic
until
Result /= Void
loop
Result := ic.item
if not a_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
end
node_type (a_name: READABLE_STRING_GENERAL): detachable CMS_NODE_TYPE [CMS_NODE]
-- Content type named `a_named' if any.
do
across
content_types as ic
cms_api.content_types as ic
until
Result /= Void
loop
@@ -125,31 +105,16 @@ feature -- Content type
feature -- Content type webform
content_type_webform_managers: ARRAYED_LIST [CMS_CONTENT_TYPE_WEBFORM_MANAGER]
content_type_webform_managers: ARRAYED_LIST [CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_CONTENT]]
-- Available content types
add_content_type_webform_manager (a_manager: CMS_CONTENT_TYPE_WEBFORM_MANAGER)
-- Register webform manager `a_manager'.
do
content_type_webform_managers.force (a_manager)
Result := cms_api.content_type_webform_managers
end
content_type_webform_manager (a_content_type: CMS_CONTENT_TYPE): detachable CMS_CONTENT_TYPE_WEBFORM_MANAGER
-- Web form manager for content type `a_content_type' if any.
local
l_type_name: READABLE_STRING_GENERAL
add_node_type_webform_manager (a_manager: CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_NODE])
-- Register webform manager `a_manager'.
do
l_type_name := a_content_type.name
across
content_type_webform_managers as ic
until
Result /= Void
loop
Result := ic.item
if not l_type_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
cms_api.add_content_type_webform_manager (a_manager)
end
node_type_webform_manager (a_node_type: CMS_CONTENT_TYPE): detachable CMS_NODE_TYPE_WEBFORM_MANAGER_I [CMS_NODE]
@@ -330,6 +295,14 @@ feature -- Access: Node
end
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- List of nodes of type `a_node_type'.
do
Result := node_storage.nodes_of_type (a_node_type)
ensure
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
end
feature -- Access: page/book outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]

View File

@@ -9,7 +9,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks,
setup_hooks,
initialize,
is_installed,
install,
@@ -25,6 +25,12 @@ inherit
CMS_RECENT_CHANGES_HOOK
CMS_TAXONOMY_HOOK
CMS_HOOK_EXPORT
CMS_EXPORT_NODE_UTILITIES
create
make
@@ -37,6 +43,9 @@ feature {NONE} -- Initialization
description := "Service to manage content based on 'node'"
package := "core"
config := a_setup
-- Optional dependencies, mainly for information.
put_dependency ({CMS_RECENT_CHANGES_MODULE}, False)
put_dependency ({CMS_TAXONOMY_MODULE}, False)
end
config: CMS_SETUP
@@ -59,7 +68,7 @@ feature {CMS_API} -- Module Initialization
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_NODE_STORAGE_SQL} l_node_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
@@ -107,7 +116,7 @@ feature {CMS_API} -- Module management
-- Is Current module installed?
do
Result := Precursor (a_api)
if Result and attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
if Result and attached a_api.storage.as_sql_storage as l_sql_storage then
Result := l_sql_storage.sql_table_exists ("nodes") and
l_sql_storage.sql_table_exists ("page_nodes")
end
@@ -116,7 +125,7 @@ feature {CMS_API} -- Module management
install (a_api: CMS_API)
do
-- Schema
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
if attached a_api.storage.as_sql_storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
end
Precursor {CMS_MODULE}(a_api)
@@ -147,10 +156,11 @@ feature -- Access
do
Result := Precursor
Result.force ("create any node")
Result.force ("export any node")
if attached node_api as l_node_api then
across
l_node_api.content_types as ic
l_node_api.node_types as ic
loop
l_type_name := ic.item.name
if not l_type_name.is_whitespace then
@@ -173,6 +183,8 @@ feature -- Access
Result.force ("view unpublished " + l_type_name)
Result.force ("view revisions own " + l_type_name)
Result.force ("export " + l_type_name)
end
end
Result.force ("view trash")
@@ -224,15 +236,17 @@ feature -- Access: router
feature -- Hooks
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- <Precursor>
do
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.subscribe_to_block_hook (Current)
a_response.hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_export_hook (Current)
-- Module specific hook, if available.
a_response.hooks.subscribe_to_hook (Current, {CMS_RECENT_CHANGES_HOOK})
a_hooks.subscribe_to_hook (Current, {CMS_RECENT_CHANGES_HOOK})
a_hooks.subscribe_to_hook (Current, {CMS_TAXONOMY_HOOK})
end
response_alter (a_response: CMS_RESPONSE)
@@ -274,7 +288,7 @@ feature -- Hooks
create perms.make (2)
perms.force ("create any node")
across
l_node_api.content_types as ic
l_node_api.node_types as ic
loop
perms.force ("create " + ic.item.name)
end
@@ -289,7 +303,7 @@ feature -- Hooks
do
if
attached node_api as l_node_api and then
attached l_node_api.content_types as l_types and then
attached l_node_api.node_types as l_types and then
not l_types.is_empty
then
create lst.make (l_types.count)
@@ -353,4 +367,139 @@ feature -- Hooks
end
end
populate_content_associated_with_term (t: CMS_TERM; a_contents: CMS_TAXONOMY_ENTITY_CONTAINER)
local
l_node_typenames: ARRAYED_LIST [READABLE_STRING_8]
nid: INTEGER_64
l_info_to_remove: ARRAYED_LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
do
if
attached node_api as l_node_api and then
attached l_node_api.node_types as l_node_types and then
not l_node_types.is_empty
then
create l_node_typenames.make (l_node_types.count)
across
l_node_types as ic
loop
l_node_typenames.force (ic.item.name)
end
create l_info_to_remove.make (0)
across
a_contents.taxonomy_info as ic
loop
if
attached ic.item.typename as l_typename and then
across l_node_typenames as t_ic some t_ic.item.same_string (l_typename) end
then
if ic.item.entity.is_integer then
nid := ic.item.entity.to_integer_64
if nid > 0 and then attached l_node_api.node (nid) as l_node then
if l_node.link = Void then
l_node.set_link (l_node_api.node_link (l_node))
end
a_contents.force (create {CMS_TAXONOMY_ENTITY}.make (l_node, l_node.modification_date))
l_info_to_remove.force (ic.item)
end
end
end
end
across
l_info_to_remove as ic
loop
a_contents.taxonomy_info.prune_all (ic.item)
end
end
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
l_node_type: CMS_CONTENT_TYPE
n: CMS_NODE
p: PATH
d: DIRECTORY
f: PLAIN_TEXT_FILE
lst: LIST [CMS_NODE]
do
if attached node_api as l_node_api then
across
l_node_api.node_types as types_ic
loop
l_node_type := types_ic.item
if
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
l_node_type.name.same_string_general ("page") and then
( a_export_id_list = Void
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
)
then
-- For now, handle only page from this node module.
lst := l_node_api.nodes_of_type (l_node_type)
a_export_parameters.log ("Exporting " + lst.count.out + " nodes of type " + l_node_type.name)
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name)
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
across
lst as ic
loop
n := l_node_api.full_node (ic.item)
a_export_parameters.log (l_node_type.name + " #" + n.id.out + " rev=" + n.revision.out)
create f.make_with_path (p.extended (n.id.out).appended_with_extension ("json"))
if not f.exists or else f.is_access_writable then
f.open_write
if attached {CMS_PAGE} n as l_page then
f.put_string (json_to_string (page_node_to_json (l_page)))
else
f.put_string (json_to_string (node_to_json (n)))
end
f.close
end
-- Revisions.
if attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1 then
a_export_parameters.log (l_node_type.name + " " + l_revisions.count.out + " revisions.")
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name).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
n := revs_ic.item
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
if attached {CMS_PAGE} n as l_page then
f.put_string (json_to_string (page_node_to_json (l_page)))
else
f.put_string (json_to_string (node_to_json (n)))
end
f.close
end
end
end
end
end
end
end
end
page_node_to_json (a_page: CMS_PAGE): JSON_OBJECT
local
j: JSON_OBJECT
do
Result := node_to_json (a_page)
if attached a_page.parent as l_parent_page then
create j.make_empty
j.put_string (l_parent_page.content_type, "type")
j.put_integer (l_parent_page.id, "nid")
Result.put (j, "parent")
end
end
end

View File

@@ -10,7 +10,11 @@ deferred class
CMS_NODE
inherit
DEBUG_OUTPUT
CMS_CONTENT
redefine
debug_output
end
REFACTORING_HELPER
feature{NONE} -- Initialization
@@ -67,12 +71,6 @@ feature -- Access
-- Revision value.
--| Note: for now version is not supported.
content_type: READABLE_STRING_8
-- Associated content type name.
-- Page, Article, Blog, News, etc.
deferred
end
feature -- Status reports
status: INTEGER
@@ -113,12 +111,6 @@ feature -- Access
deferred
end
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
deferred
end
feature -- Access: date
modification_date: DATE_TIME
@@ -155,12 +147,6 @@ feature -- status report
valid_result: Result implies a_node.id = id
end
is_typed_as (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
-- Is current node of type `a_content_type' ?
do
Result := a_content_type.is_case_insensitive_equal (content_type)
end
feature -- Access: menu
link: detachable CMS_LOCAL_LINK
@@ -174,13 +160,7 @@ feature -- Status report
create Result.make_from_string_general ("#")
Result.append_integer_64 (id)
Result.append_character (' ')
Result.append_character ('<')
Result.append_string_general (content_type)
Result.append_character ('>')
Result.append_character (' ')
Result.append_character ('%"')
Result.append (title)
Result.append_character ('%"')
Result.append (Precursor)
end
feature -- Element change

View File

@@ -0,0 +1,55 @@
note
description: "[
Routines usefull during node exportation (see {CMS_HOOK_EXPORT}).
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_EXPORT_NODE_UTILITIES
inherit
CMS_EXPORT_JSON_UTILITIES
feature -- Access
node_to_json (n: CMS_NODE): JSON_OBJECT
local
jo,j_author: JSON_OBJECT
do
create Result.make_empty
Result.put_string (n.content_type, "type")
Result.put_integer (n.id, "nid")
Result.put_integer (n.revision, "revision")
Result.put_string (n.title, "title")
put_date_into_json (n.creation_date, "creation_date", Result)
put_date_into_json (n.modification_date, "modification_date", Result)
put_date_into_json (n.publication_date, "publication_date", Result)
Result.put_integer (n.status, "status")
if attached n.author as u then
create j_author.make
j_author.put_integer (u.id, "uid")
j_author.put_string (u.name, "name")
Result.put (j_author, "author")
end
create jo.make_empty
if attached n.format as l_format then
jo.put_string (l_format, "format")
end
if attached n.summary as s then
jo.put_string (s, "summary")
end
if attached n.content as s then
jo.put_string (s, "content")
end
Result.put (jo, "data")
if attached n.link as lnk then
create jo.make_empty
jo.put_string (lnk.title, "title")
jo.put_string (lnk.location, "location")
jo.put_integer (lnk.weight, "weight")
Result.put (jo, "link")
end
end
end

View File

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

View File

@@ -48,8 +48,8 @@ feature -- Forms ...
if a_node /= Void then
ta.set_text_value (a_node.content)
end
ta.set_label ("Content")
ta.set_description ("This is the main content")
ta.set_label (response.translation ("Content", Void))
ta.set_description (response.translation ("This is the main content", Void))
ta.set_is_required (False)
-- Summary
@@ -61,8 +61,8 @@ feature -- Forms ...
if a_node /= Void then
sum.set_text_value (a_node.summary)
end
sum.set_label ("Summary")
sum.set_description ("Text displayed in short view.")
sum.set_label (response.translation ("Summary", Void))
sum.set_description (response.translation ("Text displayed in short view.", Void))
sum.set_is_required (False)
create fset.make
@@ -93,9 +93,230 @@ feature -- Forms ...
f.extend (fset)
-- Path alias
populate_form_with_taxonomy (response, f, a_node)
populate_form_with_path_alias (response, f, a_node)
end
populate_form_with_taxonomy (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: detachable WSF_FORM_TEXT_INPUT
w_set: WSF_FORM_FIELD_SET
w_select: WSF_FORM_SELECT
w_opt: WSF_FORM_SELECT_OPTION
w_cb: WSF_FORM_CHECKBOX_INPUT
w_voc_set: WSF_FORM_FIELD_SET
s: STRING_32
voc: CMS_VOCABULARY
t: detachable CMS_TERM
l_terms: detachable CMS_TERM_COLLECTION
l_has_edit_permission: BOOLEAN
do
if
attached {CMS_TAXONOMY_API} response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then
attached l_taxonomy_api.vocabularies_for_type (content_type.name) as l_vocs and then not l_vocs.is_empty
then
l_has_edit_permission := response.has_permissions (<<"update any taxonomy", "update " + content_type.name + " taxonomy">>)
-- Handle Taxonomy fields, if any associated with `content_type'.
create w_set.make
w_set.add_css_class ("taxonomy")
l_vocs.sort
across
l_vocs as vocs_ic
loop
voc := vocs_ic.item
l_terms := Void
if a_node /= Void and then a_node.has_id then
l_terms := l_taxonomy_api.terms_of_entity (a_node.content_type, a_node.id.out, voc)
if l_terms /= Void then
l_terms.sort
end
end
create w_voc_set.make
w_set.extend (w_voc_set)
if voc.is_tags then
w_voc_set.set_legend (response.translation (voc.name, Void))
create ti.make ({STRING_32} "taxonomy_terms[" + voc.name + "]")
w_voc_set.extend (ti)
if voc.is_term_required then
ti.enable_required
end
if attached voc.description as l_desc then
ti.set_description (response.html_encoded (response.translation (l_desc, Void)))
else
ti.set_description (response.html_encoded (response.translation (voc.name, Void)))
end
ti.set_size (70)
if l_terms /= Void then
create s.make_empty
across
l_terms as ic
loop
t := ic.item
if not s.is_empty then
s.append_character (',')
s.append_character (' ')
end
if ic.item.text.has (' ') then
s.append_character ('"')
s.append (t.text)
s.append_character ('"')
else
s.append (t.text)
end
end
ti.set_text_value (s)
end
if not l_has_edit_permission then
ti.set_is_readonly (True)
end
else
l_taxonomy_api.fill_vocabularies_with_terms (voc)
if not voc.terms.is_empty then
if voc.multiple_terms_allowed then
if attached voc.description as l_desc then
w_voc_set.set_legend (response.html_encoded (l_desc))
else
w_voc_set.set_legend (response.html_encoded (voc.name))
end
across
voc as voc_terms_ic
loop
t := voc_terms_ic.item
create w_cb.make_with_value ({STRING_32} "taxonomy_terms[" + voc.name + "]", t.text)
w_voc_set.extend (w_cb)
if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then
w_cb.set_checked (True)
end
if not l_has_edit_permission then
w_cb.set_is_readonly (True)
end
end
else
create w_select.make ({STRING_32} "taxonomy_terms[" + voc.name + "]")
w_voc_set.extend (w_select)
if attached voc.description as l_desc then
w_select.set_description (response.html_encoded (l_desc))
else
w_select.set_description (response.html_encoded (voc.name))
end
w_voc_set.set_legend (response.html_encoded (voc.name))
across
voc as voc_terms_ic
loop
t := voc_terms_ic.item
create w_opt.make (response.html_encoded (t.text), response.html_encoded (t.text))
w_select.add_option (w_opt)
if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then
w_opt.set_is_selected (True)
end
end
if not l_has_edit_permission then
w_select.set_is_readonly (True)
end
end
end
end
end
f.submit_actions.extend (agent taxonomy_submit_action (response, l_taxonomy_api, l_vocs, a_node, ?))
if
attached f.fields_by_name ("title") as l_title_fields and then
attached l_title_fields.first as l_title_field
then
f.insert_after (w_set, l_title_field)
else
f.extend (w_set)
end
end
end
taxonomy_submit_action (a_response: CMS_RESPONSE; a_taxonomy_api: CMS_TAXONOMY_API; a_vocs: CMS_VOCABULARY_COLLECTION; a_node: detachable CMS_NODE fd: WSF_FORM_DATA)
require
vocs_not_empty: not a_vocs.is_empty
local
l_voc_name: READABLE_STRING_32
l_terms_to_remove: ARRAYED_LIST [CMS_TERM]
l_new_terms: LIST [READABLE_STRING_32]
l_text: READABLE_STRING_GENERAL
l_found: BOOLEAN
t: detachable CMS_TERM
do
if
a_node /= Void and then a_node.has_id and then
attached fd.table_item ("taxonomy_terms") as fd_terms
then
across
fd_terms.values as ic
loop
if attached {WSF_STRING} ic.item as l_string then
l_voc_name := ic.key
l_new_terms := a_taxonomy_api.splitted_string (l_string.value, ',')
if attached a_vocs.item_by_name (l_voc_name) as voc then
if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then
create l_terms_to_remove.make (0)
if attached a_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, voc) as l_existing_terms then
across
l_existing_terms as t_ic
loop
l_text := t_ic.item.text
from
l_found := False
l_new_terms.start
until
l_new_terms.after
loop
if l_new_terms.item.same_string_general (l_text) then
-- Already associated with term `t_ic.text'.
l_found := True
l_new_terms.remove
else
l_new_terms.forth
end
end
if not l_found then
-- Remove term
l_terms_to_remove.force (t_ic.item)
end
end
across
l_terms_to_remove as t_ic
loop
a_taxonomy_api.unassociate_term_from_entity (t_ic.item, content_type.name, a_node.id.out)
end
end
across
l_new_terms as t_ic
loop
t := a_taxonomy_api.term_by_text (t_ic.item, voc)
if
t = Void and voc.is_tags
then
-- Create new term!
create t.make (t_ic.item)
a_taxonomy_api.save_term (t, voc)
if a_taxonomy_api.has_error then
t := Void
end
end
if t /= Void then
a_taxonomy_api.associate_term_with_entity (t, content_type.name, a_node.id.out)
end
end
end
end
end
end
end
end
populate_form_with_path_alias (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
local
ti: WSF_FORM_TEXT_INPUT
@@ -179,7 +400,7 @@ feature -- Forms ...
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
else
f := response.formats.default_format
f := cms_api.formats.default_format
end
-- Update node with summary and body content
@@ -243,7 +464,7 @@ feature -- Forms ...
elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
f := f_format
else
f := response.formats.default_format
f := cms_api.formats.default_format
end
-- Update node with summary and content
@@ -255,106 +476,125 @@ feature -- Forms ...
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
append_content_as_html_to (a_node: G; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
-- <Precursor>
local
lnk: CMS_LOCAL_LINK
lnk: detachable CMS_LOCAL_LINK
hdate: HTTP_DATE
s: STRING
node_api: CMS_NODE_API
l_node_api: CMS_NODE_API
do
node_api := a_response.node_api
a_response.set_value (a_node, "node")
l_node_api := node_api
-- Show tabs only if a user is authenticated.
if attached a_response.user as l_user then
lnk := a_response.node_local_link (a_node, a_response.translation ("View", Void))
if
not is_teaser and then
a_response /= Void and then
attached a_response.user as l_user
then
lnk := a_node.link
if lnk /= Void then
lnk := a_response.local_link (a_response.translation ("View", Void), lnk.location)
else
lnk := a_response.local_link (a_response.translation ("View", Void), l_node_api.node_path (a_node))
end
lnk.set_weight (1)
a_response.add_to_primary_tabs (lnk)
if a_node.status = {CMS_NODE_API}.trashed then
create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")
create lnk.make ("Delete", l_node_api.node_path (a_node) + "/delete")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
elseif a_node.has_id then
-- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status.
create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
create lnk.make ("Edit", l_node_api.node_path (a_node) + "/edit")
lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk)
if
node_api.has_permission_for_action_on_node ("view revisions", a_node, l_user)
l_node_api.has_permission_for_action_on_node ("view revisions", a_node, l_user)
then
create lnk.make ("Revisions", node_api.node_path (a_node) + "/revision")
create lnk.make ("Revisions", l_node_api.node_path (a_node) + "/revision")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
if
node_api.has_permission_for_action_on_node ("trash", a_node, l_user)
l_node_api.has_permission_for_action_on_node ("trash", a_node, l_user)
then
create lnk.make ("Move to trash", node_api.node_path (a_node) + "/trash")
create lnk.make ("Move to trash", l_node_api.node_path (a_node) + "/trash")
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
end
create s.make_empty
s.append ("<div class=%"cms-node node-" + a_node.content_type + "%">")
s.append ("<div class=%"info%"> ")
a_output.append ("<div class=%"")
if is_teaser then
a_output.append (" cms-teaser")
end
a_output.append ("cms-node node-" + a_node.content_type + "%">")
a_output.append ("<div class=%"info%"> ")
if attached a_node.author as l_author then
s.append (" by ")
s.append (a_response.html_encoded (l_author.name))
a_output.append (" by ")
a_output.append (l_node_api.html_encoded (l_author.name))
end
if attached a_node.modification_date as l_modified then
s.append (" (modified: ")
a_output.append (" (modified: ")
create hdate.make_from_date_time (l_modified)
s.append (hdate.yyyy_mmm_dd_string)
s.append (")")
a_output.append (hdate.yyyy_mmm_dd_string)
a_output.append (")")
end
s.append ("</div>")
a_output.append ("</div>")
if
a_response /= Void and then
attached {CMS_TAXONOMY_API} cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then
attached l_taxonomy_api.vocabularies_for_type (content_type.name) as vocs and then not vocs.is_empty
then
vocs.sort
across
vocs as ic
loop
if
attached l_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, ic.item) as l_terms and then
not l_terms.is_empty
then
a_output.append ("<ul class=%"taxonomy term-" + ic.item.id.out + "%">")
a_output.append (l_node_api.html_encoded (ic.item.name))
a_output.append (": ")
across
l_terms as t_ic
loop
a_output.append ("<li>")
a_response.append_link_to_html (t_ic.item.text, "taxonomy/term/" + t_ic.item.id.out, Void, a_output)
a_output.append ("</li>")
end
a_output.append ("</ul>%N")
end
end
end
-- We don't show the summary on the detail page, since its just a short view of the full content. Otherwise we would write the same thing twice.
-- The usage of the summary is to give a short overview in the list of nodes or for the meta tag "description"
-- if attached a_node.summary as l_summary then
-- s.append ("<p class=%"summary%">")
-- if attached node_api.cms_api.format (a_node.format) as f then
-- append_formatted_output (l_content, f, s)
-- else
-- append_formatted_output (l_content, a_response.formats.default_format, s)
-- end
-- s.append ("</p>")
-- end
if attached a_node.content as l_content then
s.append ("<p class=%"content%">")
if attached node_api.cms_api.format (a_node.format) as f then
append_formatted_output (l_content, f, s)
else
append_formatted_output (l_content, a_response.formats.default_format, s)
if is_teaser then
if attached a_node.summary as l_summary then
a_output.append ("<p class=%"summary%">")
if attached cms_api.format (a_node.format) as f then
append_formatted_content_to (l_summary, f, a_output)
else
append_formatted_content_to (l_summary, cms_api.formats.default_format, a_output)
end
a_output.append ("</p>")
end
s.append ("</p>")
end
s.append ("</div>")
a_response.set_title (a_node.title)
a_response.set_main_content (s)
end
append_formatted_output (a_content: READABLE_STRING_GENERAL; a_format: CONTENT_FORMAT; a_output: STRING_8)
-- Format `a_content' with format `a_format'.
do
if a_content.is_valid_as_string_8 then
a_output.append (a_format.formatted_output (a_content.to_string_8))
else
a_format.append_formatted_to (a_content, a_output)
elseif attached a_node.content as l_content then
a_output.append ("<p class=%"content%">")
if attached cms_api.format (a_node.format) as f then
append_formatted_content_to (l_content, f, a_output)
else
append_formatted_content_to (l_content, cms_api.formats.default_format, a_output)
end
a_output.append ("</p>")
end
a_output.append ("</div>")
end
end

View File

@@ -10,16 +10,35 @@ deferred class
CMS_NODE_TYPE_WEBFORM_MANAGER_I [G -> CMS_NODE]
inherit
CMS_CONTENT_TYPE_WEBFORM_MANAGER
CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_NODE]
rename
make as old_make
redefine
content_type
end
feature {NONE} -- Initialization
make (a_type: like content_type; a_node_api: CMS_NODE_API)
do
node_api := a_node_api
old_make (a_type)
end
feature -- Access
content_type: CMS_NODE_TYPE [G]
-- Associated content type.
cms_api: CMS_API
-- API for current instance of CMS.
do
Result := node_api.cms_api
end
node_api: CMS_NODE_API
-- Associated node API.
feature -- Query
has_valid_node_type (a_node: CMS_NODE): BOOLEAN
@@ -57,11 +76,18 @@ feature -- Node ...
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
append_content_as_html_to_page (a_node: G; a_response: NODE_RESPONSE)
-- Append an html representation of `a_node' to response `a_response'.
require
has_valid_node_type (a_node)
deferred
local
s: STRING
do
create s.make_empty
a_response.set_value (a_node, "node")
a_response.set_title (a_node.title)
append_content_as_html_to (a_node, False, s, a_response)
a_response.set_main_content (s)
end
end

View File

@@ -10,7 +10,7 @@ inherit
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE]
redefine
content_type,
append_html_output_to,
append_content_as_html_to,
populate_form,
new_node,
update_node
@@ -102,27 +102,27 @@ feature -- Forms ...
parent_validation (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA)
local
node_api: CMS_NODE_API
l_node_api: CMS_NODE_API
l_parent_id: INTEGER_64
nid: INTEGER_64
l_parent_node: detachable CMS_NODE
do
node_api := a_response.node_api
l_node_api := node_api
if attached fd.integer_item ("select_parent_node") as s_parent_node then
l_parent_id := s_parent_node.to_integer_64
else
l_parent_id := 0
end
if l_parent_id > 0 then
l_parent_node := node_api.node (l_parent_id)
l_parent_node := l_node_api.node (l_parent_id)
if l_parent_node = Void then
fd.report_invalid_field ("select_parent_node", "Invalid parent, not found id #" + l_parent_id.out)
else
nid := a_response.node_id_path_parameter
if
nid > 0 and then
attached node_api.node (nid) as l_node and then
node_api.is_node_a_parent_of (l_node, l_parent_node)
attached l_node_api.node (nid) as l_node and then
l_node_api.is_node_a_parent_of (l_node, l_parent_node)
then
fd.report_invalid_field ("select_parent_node", "Invalid parent due to cycle (node #" + nid.out + " is already a parent of node #" + l_parent_id.out)
end
@@ -137,50 +137,51 @@ feature -- Forms ...
feature -- Output
append_html_output_to (a_node: CMS_NODE; a_response: NODE_RESPONSE)
append_content_as_html_to (a_node: CMS_PAGE; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
-- <Precursor>
local
s: STRING
node_api: CMS_NODE_API
l_node_api: CMS_NODE_API
lnk: CMS_LOCAL_LINK
do
node_api := a_response.node_api
Precursor (a_node, a_response)
if a_node.has_id and then not a_node.is_trashed then
if node_api.has_permission_for_action_on_node ("create", a_node, a_response.user) then
create lnk.make ("Add Child", "node/add/page?parent=" + a_node.id.out)
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
if attached a_response.main_content as l_main_content then
s := l_main_content
else
create s.make_empty
end
if attached {CMS_PAGE} a_node as l_node_page then
s.append ("<ul class=%"page-navigation%">")
if attached l_node_page.parent as l_parent_node then
s.append ("<li class=%"page-parent%">Go to parent page ")
s.append (a_response.link (l_parent_node.title, a_response.node_api.node_path (l_parent_node), Void))
s.append ("</li>")
end
if attached node_api.children (a_node) as l_children then
across
l_children as ic
loop
s.append ("<li>")
s.append (a_response.link (ic.item.title, a_response.node_api.node_path (ic.item), Void))
s.append ("</li>")
Precursor (a_node, is_teaser, a_output, a_response)
if not is_teaser then
l_node_api := node_api
if
a_response /= Void and then
a_node.has_id and then not a_node.is_trashed
then
if
l_node_api.has_permission_for_action_on_node ("create", a_node, a_response.user)
then
create lnk.make ("Add Child", "node/add/page?parent=" + a_node.id.out)
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
s.append ("</ul>")
end
a_response.set_main_content (s)
if
a_response /= Void and then
attached {CMS_PAGE} a_node as l_node_page
then
a_output.append ("<ul class=%"page-navigation%">")
if attached l_node_page.parent as l_parent_node then
a_output.append ("<li class=%"page-parent%">Go to parent page ")
a_output.append (a_response.link (l_parent_node.title, l_node_api.node_path (l_parent_node), Void))
a_output.append ("</li>")
end
if attached l_node_api.children (a_node) as l_children then
across
l_children as ic
loop
a_output.append ("<li>")
a_output.append (a_response.link (ic.item.title, l_node_api.node_path (ic.item), Void))
a_output.append ("</li>")
end
end
a_output.append ("</ul>")
end
end
end
end

View File

@@ -114,10 +114,10 @@ feature {NONE} -- Create a new node
if attached a_type.new_node (Void) as l_node then
-- create new node
f := new_edit_form (l_node, url (location, Void), "edit-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, a_type, b))
f.submit_actions.put_front (agent edit_form_submit (?, l_node, a_type, b))
f.process (Current)
fd := f.last_data
end
@@ -144,10 +144,10 @@ feature {NONE} -- Create a new node
fd: detachable WSF_FORM_DATA
do
f := new_edit_form (A_node, url (location, Void), "edit-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, a_node, a_type, b))
f.submit_actions.put_front (agent edit_form_submit (?, a_node, a_type, b))
f.process (Current)
fd := f.last_data
end
@@ -173,25 +173,29 @@ feature {NONE} -- Create a new node
f: like new_edit_form
fd: detachable WSF_FORM_DATA
do
f := new_delete_form (a_node, url (location, Void), "delete-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if a_node.has_id then
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
end
if a_node.is_trashed then
f := new_delete_form (a_node, url (location, Void), "delete-" + a_type.name, a_type)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if a_node.has_id then
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (a_node.title)
b.append (html_encoded (a_type.title) + " deleted")
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (a_node.title)
b.append (html_encoded (a_type.title) + " deleted")
else
set_title (formatted_string (translation ("Delete $1 #$2", Void), [a_type.title, a_node.id]))
f.append_to_html (wsf_theme, b)
end
else
set_title (formatted_string (translation ("Delete $1 #$2", Void), [a_type.title, a_node.id]))
f.append_to_html (wsf_theme, b)
--
end
end
@@ -202,7 +206,7 @@ feature {NONE} -- Create a new node
fd: detachable WSF_FORM_DATA
do
f := new_trash_form (a_node, url (location, Void), "trash-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
api.hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
@@ -355,6 +359,8 @@ feature -- Form
new_delete_form (a_node: detachable CMS_NODE; a_url: READABLE_STRING_8; a_name: STRING; a_node_type: CMS_NODE_TYPE [CMS_NODE]): CMS_FORM
-- Create a web form named `a_name' for node `a_node' (if set), using form action url `a_url', and for type of node `a_node_type'.
require
is_trashed: attached a_node as l_node and then a_node.is_trashed
local
f: CMS_FORM
ts: WSF_FORM_SUBMIT_INPUT
@@ -375,10 +381,27 @@ feature -- Form
ts.set_default_value (translation ("Delete"))
]")
f.extend (ts)
to_implement ("Refactor code to use the new wsf_html HTML5 support")
f.extend_html_text("<input type='submit' value='Cancel' formmethod='GET', formaction='/node/"+a_node.id.out+"'>" )
create ts.make ("op")
ts.set_default_value ("Cancel")
ts.set_formaction ("/node/"+a_node.id.out)
ts.set_formmethod ("GET")
f.extend (ts)
end
f.extend_html_text ("<br/>")
f.extend_html_text ("<legend>Do you want to restore the current node?</legend>")
if
a_node /= Void and then
a_node.id > 0
then
create ts.make ("op")
ts.set_default_value ("Restore")
ts.set_formaction ("/node/"+a_node.id.out+"/delete")
ts.set_formmethod ("POST")
fixme ("[
ts.set_default_value (translation ("Restore"))
]")
f.extend (ts)
end
Result := f
end
@@ -404,19 +427,6 @@ feature -- Form
]")
f.extend (ts)
end
f.extend_html_text ("<br/>")
f.extend_html_text ("<legend>Do you want to restore the current node?</legend>")
if
a_node /= Void and then
a_node.id > 0
then
create ts.make ("op")
ts.set_default_value ("Restore")
fixme ("[
ts.set_default_value (translation ("Restore"))
]")
f.extend (ts)
end
Result := f
end

View File

@@ -173,6 +173,11 @@ feature -- HTTP Methods
l_op.value.same_string ("Delete")
then
do_delete (req, res)
elseif
attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Restore")
then
do_restore (req, res)
end
elseif req.percent_encoded_path_info.ends_with ("/trash") then
if
@@ -180,11 +185,6 @@ feature -- HTTP Methods
l_op.value.same_string ("Trash")
then
do_trash (req, res)
elseif
attached {WSF_STRING} req.form_parameter ("op") as l_op and then
l_op.value.same_string ("Restore")
then
do_restore (req, res)
end
elseif req.percent_encoded_path_info.starts_with ("/node/add/") then
create edit_response.make (req, res, api, node_api)
@@ -242,15 +242,19 @@ feature {NONE} -- Trash:Restore
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Delete a node from the database.
local
l_source: STRING
do
if attached current_user (req) as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if
l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node
attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node
then
if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then
node_api.delete_node (l_node)
l_source := node_api.node_path (l_node)
api.unset_path_alias (l_source, api.location_alias (l_source))
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else
send_access_denied (req, res)

View File

@@ -42,15 +42,6 @@ feature -- Helpers
feature -- Helpers: cms link
user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do
if a_opt_title /= Void then
create Result.make (a_opt_title, user_url (u))
else
create Result.make (u.name, user_url (u))
end
end
node_local_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
do
if attached n.link as lnk then
@@ -59,17 +50,12 @@ feature -- Helpers: cms link
Result := node_api.node_link (n)
end
if a_opt_title /= Void and then not Result.title.same_string_general (a_opt_title) then
create Result.make (a_opt_title, Result.location)
Result := local_link (a_opt_title, Result.location)
end
end
feature -- Helpers: html link
user_html_link (u: CMS_USER): like link
do
Result := link (u.name, "user/" + u.id.out, Void)
end
node_html_link (n: CMS_NODE; a_opt_title: detachable READABLE_STRING_GENERAL): like link
local
l_title: detachable READABLE_STRING_GENERAL
@@ -80,18 +66,10 @@ feature -- Helpers: html link
l_title := n.title
end
Result := link (l_title, node_api.node_path (n), Void)
end
feature -- Helpers: URL
user_url (u: CMS_USER): like url
require
u_with_id: u.has_id
do
Result := url ("user/" + u.id.out, Void)
end
node_url (n: CMS_NODE): like url
require
n_with_id: n.has_id

View File

@@ -71,7 +71,7 @@ feature -- Execution
attached node_api.node_type_for (l_node) as l_content_type and then
attached node_api.node_type_webform_manager (l_content_type) as l_manager
then
l_manager.append_html_output_to (l_node, Current)
l_manager.append_content_as_html_to_page (l_node, Current)
end
elseif revision > 0 then
set_main_content ("Missing revision node!")

View File

@@ -96,7 +96,7 @@ feature -- HTTP Methods
s.append (" <em>(trashed)</em>")
end
debug
if attached node_api.content_type (n.content_type) as ct then
if attached node_api.node_type (n.content_type) as ct then
s.append ("<span class=%"description%">")
s.append (html_encoded (ct.title))
s.append ("</span>")

View File

@@ -1,5 +1,5 @@
<?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="node" uuid="C7114DD4-FA92-4AE5-A209-0FFC45E44257" library_target="node">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="node" uuid="C7114DD4-FA92-4AE5-A209-0FFC45E44257" library_target="node">
<target name="node">
<root all_classes="true"/>
<file_rule>
@@ -7,16 +7,18 @@
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<option warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/>
<library name="cms_taxonomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\web\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>

View File

@@ -14,9 +14,11 @@
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes.ecf" readonly="false"/>
<library name="cms_taxonomy_module" location="..\..\modules\taxonomy\taxonomy.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>

View File

@@ -127,6 +127,26 @@ feature -- Access
deferred
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- List of nodes of type `a_node_type'.
--| Redefine to optimize!
do
Result := nodes
from
Result.start
until
Result.after
loop
if Result.item.content_type.same_string (a_node_type.name) then
Result.forth
else
Result.remove
end
end
ensure
expected_type: across Result as ic all ic.item.content_type.same_string (a_node_type.name) end
end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]

View File

@@ -12,6 +12,9 @@ inherit
CMS_PROXY_STORAGE_SQL
CMS_NODE_STORAGE_I
redefine
nodes_of_type
end
CMS_STORAGE_SQL_I
@@ -264,6 +267,33 @@ feature -- Access
-- end
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".nodes_of_type")
create l_parameters.make (1)
l_parameters.put (a_node_type.name, "node_type")
from
sql_query (sql_select_nodes_of_type, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
check expected_node_type: l_node.content_type.same_string (a_node_type.name) end
Result.force (l_node)
end
sql_forth
end
sql_finalize
end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
@@ -354,6 +384,7 @@ feature -- Change: Node
l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME
do
sql_begin_transaction
create l_time.make_now_utc
write_information_log (generator + ".delete_node_base {" + a_node.id.out + "}")
@@ -370,6 +401,9 @@ feature -- Change: Node
if not error_handler.has_error then
extended_delete (a_node)
sql_commit_transaction
else
sql_rollback_transaction
end
end
@@ -385,9 +419,9 @@ feature -- Change: Node
error_handler.reset
create l_parameters.make (1)
l_parameters.put (l_time, "changed")
l_parameters.put ({CMS_NODE_API}.not_published, "status")
l_parameters.put ({CMS_NODE_API}.published, "status")
l_parameters.put (a_id, "nid")
sql_modify (sql_restore_node, l_parameters)
sql_modify (sql_update_node_status, l_parameters)
sql_finalize
end
@@ -495,6 +529,10 @@ feature {NONE} -- Queries
-- SQL Query to retrieve all nodes.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_nodes_of_type: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE status != -1 AND type=:node_type ;"
-- SQL Query to retrieve all nodes of type :node_type.
--| note: {CMS_NODE_API}.trashed = -1
sql_select_node_revisions: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision < :revision ORDER BY node_revisions.revision DESC;"
-- SQL query to get node revisions (missing the latest one).
@@ -526,8 +564,8 @@ feature {NONE} -- Queries
sql_delete_node: STRING = "DELETE FROM nodes WHERE nid=:nid"
-- Physical deletion with free metadata.
sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Restore node to {CMS_NODE_API}.not_publised.
sql_update_node_status: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Restore node to {CMS_NODE_API}.published
sql_last_insert_node_id: STRING = "SELECT MAX(nid) FROM nodes;"
@@ -550,6 +588,7 @@ feature {NONE} -- Queries
sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;"
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid AND nodes.revision = :revision;"

View File

@@ -101,7 +101,7 @@ feature -- Persistence
l_parent_id /= a_node.id and then
attached node_storage.node_by_id (l_parent_id) as l_parent
then
if attached {CMS_PAGE_NODE_TYPE} node_api.content_type (l_parent.content_type) as l_parent_ct then
if attached {CMS_PAGE_NODE_TYPE} node_api.node_type (l_parent.content_type) as l_parent_ct then
ct := l_parent_ct
else
create ct

View File

@@ -12,7 +12,7 @@ inherit
module_api as user_oauth_api
redefine
filters,
register_hooks,
setup_hooks,
initialize,
install,
user_oauth_api
@@ -72,7 +72,7 @@ feature {CMS_API} -- Module Initialization
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
@@ -93,7 +93,7 @@ feature {CMS_API} -- Module management
l_consumers: LIST [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 ("oauth2_consumers") then
--| Schema
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void)
@@ -112,7 +112,7 @@ feature {CMS_API} -- Module management
else
from
l_sql_storage.sql_start
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
create {ARRAYED_LIST [STRING]} l_consumers.make (2)
until
l_sql_storage.sql_after
loop
@@ -190,12 +190,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.hooks.subscribe_to_block_hook (Current)
a_response.hooks.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

@@ -14,7 +14,7 @@ inherit
module_api as user_openid_api
redefine
filters,
register_hooks,
setup_hooks,
initialize,
install,
user_openid_api
@@ -74,7 +74,7 @@ feature {CMS_API} -- Module Initialization
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_OPENID_STORAGE_SQL} l_openid_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
@@ -93,7 +93,7 @@ feature {CMS_API} -- Module management
install (api: CMS_API)
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 ("openid_consumers") then
--| Schema
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers.sql")), Void)
@@ -166,12 +166,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.hooks.subscribe_to_block_hook (Current)
a_response.hooks.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

@@ -11,7 +11,7 @@ inherit
rename
module_api as recent_changes_api
redefine
register_hooks,
setup_hooks,
permissions
end
@@ -94,7 +94,7 @@ feature -- Hook
recent_changes_feed (a_response, nb, Void).accept (gen)
create b.make (a_block_id, Void, l_content, Void)
a_response.put_block (b, Void, False)
a_response.add_block (b, Void)
end
end
@@ -110,7 +110,7 @@ feature -- Hook
do
l_user := Void -- Public access for the feed!
create l_changes.make (a_size, create {DATE_TIME}.make_now_utc, a_source)
if attached a_response.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
if attached a_response.api.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
across
lst as ic
loop
@@ -236,7 +236,7 @@ feature -- Handler
create l_changes.make (l_size, l_until_date, l_filter_source)
create l_content.make (1024)
if attached r.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
if attached api.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
create l_sources.make (lst.count)
across
@@ -397,12 +397,12 @@ feature -- Handler
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.subscribe_to_response_alter_hook (Current)
a_response.hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
a_hooks.subscribe_to_block_hook (Current)
end
feature -- Hook

View File

@@ -0,0 +1,226 @@
note
description: "[
API to handle taxonomy vocabularies and terms.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_TAXONOMY_API
inherit
CMS_MODULE_API
redefine
initialize
end
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
initialize
-- <Precursor>
do
Precursor
-- Create the node storage for type blog
if attached storage.as_sql_storage as l_storage_sql then
create {CMS_TAXONOMY_STORAGE_SQL} taxonomy_storage.make (l_storage_sql)
else
create {CMS_TAXONOMY_STORAGE_NULL} taxonomy_storage.make
end
end
feature {CMS_MODULE} -- Access nodes storage.
taxonomy_storage: CMS_TAXONOMY_STORAGE_I
feature -- Access node
vocabulary_count: INTEGER_64
-- Number of vocabulary.
do
Result := taxonomy_storage.vocabulary_count
end
vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION
-- List of vocabularies ordered by weight and limited by limit and offset.
do
Result := taxonomy_storage.vocabularies (a_limit, a_offset)
end
vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY
-- Vocabulary associated with id `a_id'.
require
valid_id: a_id > 0
do
Result := taxonomy_storage.vocabulary (a_id)
end
vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION
-- Vocabularies associated with content type `a_type_name'.
do
Result := taxonomy_storage.vocabularies_for_type (a_type_name)
end
fill_vocabularies_with_terms (a_vocab: CMS_VOCABULARY)
-- Fill `a_vocab' with associated terms.
do
reset_error
a_vocab.terms.wipe_out
if attached terms (a_vocab, 0, 0) as lst then
across
lst as ic
loop
a_vocab.extend (ic.item)
end
end
end
term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64
-- Number of terms from vocabulary `a_vocab'.
require
has_id: a_vocab.has_id
do
Result := taxonomy_storage.term_count_from_vocabulary (a_vocab)
end
terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION
-- Terms related to `(a_type_name,a_entity)', and if `a_vocabulary' is set
-- constrain to be part of `a_vocabulary'.
do
Result := taxonomy_storage.terms_of_entity (a_type_name, a_entity, a_vocabulary)
end
terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION
-- List of terms ordered by weight and limited by limit and offset.
require
has_id: a_vocab.has_id
do
Result := taxonomy_storage.terms (a_vocab, a_limit, a_offset)
end
term_by_id (a_tid: INTEGER_64): detachable CMS_TERM
do
Result := taxonomy_storage.term_by_id (a_tid)
end
term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM
-- Term with text `a_term_text', included in vocabulary `a_vocabulary' if provided.
do
Result := taxonomy_storage.term_by_text (a_term_text, a_vocabulary)
end
entities_associated_with_term (a_term: CMS_TERM): detachable LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
-- Entities and related typename associated with `a_term'.
require
a_term_exists: a_term.has_id
do
Result := taxonomy_storage.entities_associated_with_term (a_term)
end
feature -- Write
save_vocabulary (a_voc: CMS_VOCABULARY)
do
reset_error
taxonomy_storage.save_vocabulary (a_voc)
error_handler.append (taxonomy_storage.error_handler)
end
save_term (a_term: CMS_TERM; voc: CMS_VOCABULARY)
do
reset_error
taxonomy_storage.save_term (a_term, voc)
error_handler.append (taxonomy_storage.error_handler)
end
associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Associate term `a_term' with `(a_type_name, a_entity)'.
do
reset_error
taxonomy_storage.associate_term_with_entity (a_term, a_type_name, a_entity)
error_handler.append (taxonomy_storage.error_handler)
end
unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Unassociate term `a_term' from `(a_type_name, a_entity)'.
do
reset_error
taxonomy_storage.unassociate_term_from_entity (a_term, a_type_name, a_entity)
error_handler.append (taxonomy_storage.error_handler)
end
associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Associate vocabulary `a_voc' with type `a_type_name'.
require
existing_term: a_voc.has_id
do
reset_error
taxonomy_storage.associate_vocabulary_with_type (a_voc, a_type_name)
error_handler.append (taxonomy_storage.error_handler)
end
unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Un-associate vocabulary `a_voc' from type `a_type_name'.
require
existing_term: a_voc.has_id
do
reset_error
taxonomy_storage.unassociate_vocabulary_with_type (a_voc, a_type_name)
error_handler.append (taxonomy_storage.error_handler)
end
feature -- Helpers
splitted_string (s: READABLE_STRING_32; sep: CHARACTER): LIST [READABLE_STRING_32]
-- Splitted string from `s' with separator `sep', and support '"..."' wrapping.
local
i,j,n,b: INTEGER
t: STRING_32
do
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (1)
Result.compare_objects
from
i := 1
b := 1
n := s.count
create t.make_empty
until
i > n
loop
if s[i].is_space then
if not t.is_empty then
t.append_character (s[i])
end
elseif s[i] = sep then
t.left_adjust
t.right_adjust
if t.count > 2 and t.starts_with_general ("%"") and t.ends_with_general ("%"") then
t.remove_head (1)
t.remove_tail (1)
end
Result.force (t)
create t.make_empty
elseif s[i] = '"' then
j := s.index_of ('"', i + 1)
if j > 0 then
t.append (s.substring (i, j))
end
i := j
else
t.append_character (s[i])
end
i := i + 1
end
if not t.is_empty then
t.left_adjust
t.right_adjust
Result.force (t)
end
end
end

View File

@@ -0,0 +1,19 @@
note
description: "Hook provided by module {CMS_TAXONOMY_MODULE}."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_TAXONOMY_HOOK
inherit
CMS_HOOK
feature -- Hook
populate_content_associated_with_term (t: CMS_TERM; a_contents: CMS_TAXONOMY_ENTITY_CONTAINER)
-- Populate `a_contents' with taxonomy entity associated with term `t'.
deferred
end
end

View File

@@ -0,0 +1,148 @@
note
description: "[
Taxonomy module managing vocabularies and terms.
]"
date: "$Date: 2015-05-22 15:13:00 +0100 (lun., 18 mai 2015) $"
revision: "$Revision 96616$"
class
CMS_TAXONOMY_MODULE
inherit
CMS_MODULE
rename
module_api as taxonomy_api
redefine
setup_hooks,
initialize,
install,
uninstall,
taxonomy_api,
permissions
end
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_RESPONSE_ALTER
create
make
feature {NONE} -- Initialization
make
do
version := "1.0"
description := "Taxonomy solution"
package := "core"
-- put_dependency ({CMS_NODE_MODULE}, False)
end
feature -- Access
name: STRING = "taxonomy"
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin taxonomy")
Result.force ("update any taxonomy")
Result.force ("update page taxonomy") -- related to node module
Result.force ("update blog taxonomy") -- related to blog module
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
create taxonomy_api.make (api)
end
feature {CMS_API} -- Module management
install (api: CMS_API)
local
voc: CMS_VOCABULARY
l_taxonomy_api: like taxonomy_api
do
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install").appended_with_extension ("sql")), Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not install database for taxonomy module", generating_type)
end
Precursor (api)
create l_taxonomy_api.make (api)
create voc.make ("Tags")
voc.set_description ("Enter comma separated tags.")
l_taxonomy_api.save_vocabulary (voc)
voc.set_is_tags (True)
l_taxonomy_api.associate_vocabulary_with_type (voc, "page")
end
end
uninstall (api: CMS_API)
-- (export status {CMS_API})
do
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("uninstall").appended_with_extension ("sql")), Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not remove database for taxonomy module", generating_type)
end
end
Precursor (api)
end
feature {CMS_API} -- Access: API
taxonomy_api: detachable CMS_TAXONOMY_API
-- <Precursor>
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
if attached taxonomy_api as l_taxonomy_api then
configure_web (a_api, l_taxonomy_api, a_router)
else
-- Issue with api/dependencies,
-- thus Current module should not be used!
-- thus no url mapping
end
end
configure_web (a_api: CMS_API; a_taxonomy_api: CMS_TAXONOMY_API; a_router: WSF_ROUTER)
-- Configure router mapping for web interface.
local
l_taxonomy_handler: TAXONOMY_HANDLER
do
create l_taxonomy_handler.make (a_api, a_taxonomy_api)
a_router.handle ("/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get)
end
feature -- Hooks
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_hooks.subscribe_to_menu_system_alter_hook (Current)
a_hooks.subscribe_to_response_alter_hook (Current)
end
response_alter (a_response: CMS_RESPONSE)
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/taxonomy.css", Void), Void)
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
do
-- Add the link to the taxonomy to the main menu
-- create lnk.make ("Taxonomy", "taxonomy/")
-- a_menu_system.primary_menu.extend (lnk)
end
end

110
modules/taxonomy/cms_term.e Normal file
View File

@@ -0,0 +1,110 @@
note
description: "[
Taxonomy vocabulary term.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_TERM
inherit
COMPARABLE
DEBUG_OUTPUT
undefine
is_equal
end
create
make,
make_with_id
feature {NONE} -- Initialization
make_with_id (a_id: INTEGER_64; a_text: READABLE_STRING_GENERAL)
do
id := a_id
make (a_text)
end
make (a_text: READABLE_STRING_GENERAL)
do
set_text (a_text)
end
feature -- Access
id: INTEGER_64
-- Associated term id.
text: IMMUTABLE_STRING_32
-- Text for the term.
description: detachable IMMUTABLE_STRING_32
-- Optional description.
weight: INTEGER
-- Associated weight for ordering.
feature -- Status report
has_id: BOOLEAN
-- Has valid id?
do
Result := id > 0
end
debug_output: STRING_32
-- String that should be displayed in debugger to represent `Current'.
do
create Result.make_empty
Result.append_character ('#')
Result.append (id.out)
Result.append_character (' ')
Result.append (text)
Result.append_character (' ')
Result.append ("weight=")
Result.append_integer (weight)
end
feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN
-- Is current object less than `other'?
do
if weight = other.weight then
if text.same_string (other.text) then
Result := id < other.id
else
Result := text < other.text
end
else
Result := weight < other.weight
end
end
feature -- Element change
set_id (a_id: INTEGER_64)
do
id := a_id
end
set_text (a_text: READABLE_STRING_GENERAL)
do
create text.make_from_string_general (a_text)
end
set_weight (w: like weight)
do
weight := w
end
set_description (a_description: READABLE_STRING_GENERAL)
do
create description.make_from_string_general (a_description)
end
end

View File

@@ -0,0 +1,92 @@
note
description: "[
Collection of CMS terms (see Taxonomy).
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_TERM_COLLECTION
inherit
ITERABLE [CMS_TERM]
create
make
feature {NONE} -- Initialization
make (nb: INTEGER)
do
create items.make (nb)
end
feature -- Access
new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_TERM]
-- <Precursor>
do
Result := items.new_cursor
end
count: INTEGER
-- Number of terms.
do
Result := items.count
end
feature -- Status report
is_empty: BOOLEAN
do
Result := count = 0
end
has (a_term: CMS_TERM): BOOLEAN
-- Has `a_term'?
do
Result := items.has (a_term)
end
feature -- Element change
wipe_out
-- Remove all items.
do
items.wipe_out
ensure
empty: count = 0
end
force, extend (a_term: CMS_TERM)
-- Add term `a_term';
do
if not has (a_term) then
items.force (a_term)
end
end
remove (a_term: CMS_TERM)
-- Remove term `a_term'.
do
items.prune_all (a_term)
end
sort
-- Sort `items'
local
l_sorter: QUICK_SORTER [CMS_TERM]
do
create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_TERM]})
l_sorter.sort (items)
end
feature {NONE} -- Implementation
items: ARRAYED_LIST [CMS_TERM]
-- List of terms.
;note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,128 @@
note
description: "[
Taxonomy vocabulary.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_VOCABULARY
inherit
CMS_TERM
rename
text as name,
set_text as set_name
redefine
make
end
ITERABLE [CMS_TERM]
undefine
is_equal
end
create
make,
make_with_id,
make_from_term,
make_none
feature {NONE} -- Initialization
make_none
do
make ("")
end
make (a_name: READABLE_STRING_GENERAL)
do
Precursor (a_name)
create terms.make (0)
end
make_from_term (a_term: CMS_TERM)
do
make_with_id (a_term.id, a_term.text)
description := a_term.description
set_weight (a_term.weight)
end
feature -- Access
terms: CMS_TERM_COLLECTION
-- Collection of terms.
new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_TERM]
-- <Precursor>
do
Result := terms.new_cursor
end
feature -- Status report
associated_content_type: detachable READABLE_STRING_GENERAL
-- Associated content type, if any.
is_tags: BOOLEAN
-- New terms accepted (as tags), in the context of `associated_content_type'?
multiple_terms_allowed: BOOLEAN
-- Accepts multiple terms, in the context of `associated_content_type'?
is_term_required: BOOLEAN
-- At least one term is required, in the context of `associated_content_type'?
feature -- Element change
set_is_tags (b: BOOLEAN)
-- Set `is_tags' to `b'.
do
is_tags := b
end
allow_multiple_term (b: BOOLEAN)
-- Set `multiple_terms_allowed' to `b'.
do
multiple_terms_allowed := b
end
set_is_term_required (b: BOOLEAN)
-- Set `is_term_required' to `b'.
do
is_term_required := b
end
set_associated_content_type (a_type: detachable READABLE_STRING_GENERAL; a_is_tags, a_multiple, a_is_required: BOOLEAN)
-- If `a_type' is set, define `associated_content_type' and related options,
-- otherwise reset `associated_content_type'.
do
if a_type = Void then
associated_content_type := Void
set_is_tags (False)
allow_multiple_term (False)
set_is_term_required (False)
else
associated_content_type := a_type
set_is_tags (a_is_tags)
allow_multiple_term (a_multiple)
set_is_term_required (a_is_required)
end
end
feature -- Element change
force, extend (a_term: CMS_TERM)
-- Add `a_term' to the vocabulary terms `terms'.
do
terms.force (a_term)
end
sort
-- Sort `items'
do
terms.sort
end
end

View File

@@ -0,0 +1,98 @@
note
description: "[
Collection of CMS vocabularies (see Taxonomy).
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_VOCABULARY_COLLECTION
inherit
ITERABLE [CMS_VOCABULARY]
create
make
feature {NONE} -- Initialization
make (nb: INTEGER)
do
create items.make (nb)
end
feature -- Access
item_by_name (a_voc_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY
-- Vocabulary from current collection associated with name `a_voc_name', if any.
do
across
items as ic
until
Result /= Void
loop
if ic.item.name.is_case_insensitive_equal_general (a_voc_name) then
Result := ic.item
end
end
end
new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_VOCABULARY]
-- <Precursor>
do
Result := items.new_cursor
end
count: INTEGER
-- Number of vocabularies.
do
Result := items.count
end
feature -- Status report
is_empty: BOOLEAN
do
Result := count = 0
end
has (a_vocabulary: CMS_VOCABULARY): BOOLEAN
-- Has `a_vocabulary'?
do
Result := items.has (a_vocabulary)
end
feature -- Element change
force, extend (a_vocabulary: CMS_VOCABULARY)
-- Add vocabulary `a_vocabulary';
do
if not has (a_vocabulary) then
items.force (a_vocabulary)
end
end
remove (a_vocabulary: CMS_VOCABULARY)
-- Remove vocabulary `a_vocabulary'.
do
items.prune_all (a_vocabulary)
end
sort
-- Sort `items'
local
l_sorter: QUICK_SORTER [CMS_VOCABULARY]
do
create l_sorter.make (create {COMPARABLE_COMPARATOR [CMS_VOCABULARY]})
l_sorter.sort (items)
end
feature {NONE} -- Implementation
items: ARRAYED_LIST [CMS_VOCABULARY]
-- List of vocabularies.
;note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,44 @@
note
description: "[
Information related to content or taxonomy entity in a taxonomy container.
Mainly used to build list of contents/entities associated with a term.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_TAXONOMY_ENTITY
inherit
COMPARABLE
create
make
feature {NONE} -- Initialization
make (a_content: CMS_CONTENT; a_date: DATE_TIME)
-- Build Current information from `a_content' dated by `a_date'.
do
content := a_content
date := a_date
end
feature -- Access
content: CMS_CONTENT
-- Content of the entity.
date: DATE_TIME
-- Date, usually related to last modification.
feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN
-- Is current object less than `other'?
do
Result := date < other.date
end
end

View File

@@ -0,0 +1,89 @@
note
description: "Container of entity associated with taxonomy term."
date: "$Date$"
revision: "$Revision$"
class
CMS_TAXONOMY_ENTITY_CONTAINER
inherit
ITERABLE [CMS_TAXONOMY_ENTITY]
create
make
feature -- Initialization
make (a_taxo_info: like taxonomy_info; a_limit: NATURAL_32; a_date: detachable DATE_TIME; a_type: detachable READABLE_STRING_8)
do
taxonomy_info := a_taxo_info
limit := a_limit
date := a_date
content_type := a_type
create items.make (a_limit.to_integer_32)
end
feature -- Settings
limit: NATURAL_32
-- Maximum container size.
date: detachable DATE_TIME
-- Contents related to entities listed on `taxonomy_info'.
content_type: detachable READABLE_STRING_8
-- Filter by content type if not Void.
feature -- Access
taxonomy_info: LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
-- Associated information.
items: ARRAYED_LIST [CMS_TAXONOMY_ENTITY]
-- List of recent events.
count: INTEGER
-- Number of change items.
do
Result := items.count
end
feature -- Access
new_cursor: ITERATION_CURSOR [CMS_TAXONOMY_ENTITY]
-- <Precursor>
do
Result := items.new_cursor
end
feature -- Change
force (a_item: CMS_TAXONOMY_ENTITY)
-- Add `a_item'.
do
items.force (a_item)
end
feature -- Sorting
sort
-- Sort `items' from older, to newer.
do
item_sorter.sort (items)
end
reverse_sort
-- Sort `items' from newer to older.
do
item_sorter.reverse_sort (items)
end
feature {NONE} -- Implementation
item_sorter: QUICK_SORTER [CMS_TAXONOMY_ENTITY]
-- New change item sorter.
once
create Result.make (create {COMPARABLE_COMPARATOR [CMS_TAXONOMY_ENTITY]})
end
end

View File

@@ -0,0 +1,155 @@
note
description: "[
Request handler related to
/taxonomy/term/{termid}
]"
date: "$Date$"
revision: "$revision$"
class
TAXONOMY_HANDLER
inherit
CMS_MODULE_HANDLER [CMS_TAXONOMY_API]
rename
module_api as taxonomy_api
end
WSF_URI_HANDLER
rename
execute as uri_execute,
new_mapping as new_uri_mapping
end
WSF_URI_TEMPLATE_HANDLER
rename
execute as uri_template_execute,
new_mapping as new_uri_template_mapping
select
new_uri_template_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get
end
REFACTORING_HELPER
CMS_API_ACCESS
create
make
feature -- execute
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for any kind of mapping.
do
execute_methods (req, res)
end
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for URI mapping.
do
execute (req, res)
end
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler for URI-template mapping.
do
execute (req, res)
end
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_page: CMS_RESPONSE
tid: INTEGER_64
l_entity: detachable READABLE_STRING_32
s: STRING
l_contents: CMS_TAXONOMY_ENTITY_CONTAINER
ct: CMS_CONTENT
do
if
attached {WSF_STRING} req.path_parameter ("termid") as p_termid and then
p_termid.is_integer
then
tid := p_termid.value.to_integer_64
end
if tid > 0 then
if attached taxonomy_api.term_by_id (tid) as t then
-- Responding with `main_content_html (l_page)'.
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.set_title (t.text)
create s.make_empty
if
attached taxonomy_api.entities_associated_with_term (t) as l_entity_type_lst and then
not l_entity_type_lst.is_empty
then
create l_contents.make (l_entity_type_lst, 25, create {DATE_TIME}.make_now_utc, Void)
if attached api.hooks.subscribers ({CMS_TAXONOMY_HOOK}) as lst then
across
lst as ic
loop
if attached {CMS_TAXONOMY_HOOK} ic.item as h then
h.populate_content_associated_with_term (t, l_contents)
end
end
l_contents.sort
s.append ("<ul class=%"taxonomy-entities%">")
across
l_contents as ic
loop
ct := ic.item.content
s.append ("<li class=%""+ ct.content_type +"%">")
if attached ct.link as lnk then
l_page.append_link_to_html (lnk.title, lnk.location, Void, s)
end
if attached api.content_type_webform_manager_by_name (ct.content_type) as l_wfm then
l_wfm.append_content_as_html_to (ct, True, s, l_page)
end
s.append ("</li>%N")
end
s.append ("</ul>%N")
end
if not l_contents.taxonomy_info.is_empty then
check all_taxo_handled: False end
s.append ("<ul class=%"error%">Item(s) with unknown content type!")
across
l_contents.taxonomy_info as ic
loop
-- FIXME: for now basic implementation .. to be replaced by specific hook !
if attached ic.item.entity as e and then e.is_valid_as_string_8 then
l_entity := e
s.append ("<li>Entity %"")
s.append (api.html_encoded (e))
s.append ("%"")
if attached ic.item.typename as l_type and then l_type.is_valid_as_string_8 then
s.append (" {" + api.html_encoded (l_type) + "}")
end
s.append ("</li>")
end
end
s.append ("</ul>%N")
end
else
s.append ("No entity found.")
end
l_page.set_main_content (s)
else
-- Responding with `main_content_html (l_page)'.
create {NOT_FOUND_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
end
l_page.execute
else
-- Responding with `main_content_html (l_page)'.
create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
l_page.execute
end
end
end

View File

@@ -0,0 +1,134 @@
note
description: "[
Interface for accessing taxonomy data from storage.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_TAXONOMY_STORAGE_I
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access
vocabulary_count: INTEGER_64
-- Count of vocabularies.
deferred
end
vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION
-- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'.
deferred
end
vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY
-- Vocabulary by id `a_id'.
require
valid_id: a_id > 0
deferred
end
vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION
-- Vocabularies associated with content type `a_type_name'.
require
valid_type_name: not a_type_name.is_whitespace
deferred
end
terms_count: INTEGER_64
-- Number of terms.
deferred
end
term_by_id (tid: INTEGER_64): detachable CMS_TERM
-- Term associated with id `tid'.
deferred
ensure
Result /= Void implies Result.id = tid
end
term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM
-- Term with text `a_term_text', included in vocabulary `a_vocabulary' if provided.
deferred
ensure
Result /= Void implies a_term_text.same_string (Result.text)
end
term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64
-- Number of terms from vocabulary `a_vocab'.
require
has_id: a_vocab.has_id
deferred
end
terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION
-- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'.
require
has_id: a_vocab.has_id
deferred
end
terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION
-- Terms related to `(a_type_name,a_entity)', and if `a_vocabulary' is set
-- constrain to be part of `a_vocabulary'.
deferred
end
entities_associated_with_term (a_term: CMS_TERM): detachable LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
-- Entities and related typename associated with `a_term'.
require
a_term_exists: a_term.has_id
deferred
end
feature -- Store
save_vocabulary (a_voc: CMS_VOCABULARY)
-- Insert or update vocabulary `a_voc'.
deferred
ensure
not error_handler.has_error implies a_voc.has_id and then vocabulary (a_voc.id) /= Void
end
save_term (t: CMS_TERM; voc: CMS_VOCABULARY)
-- Insert or update term `t' as part of vocabulary `voc'.
deferred
ensure
not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void
end
associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Associate term `a_term' with `(a_type_name, a_entity)'.
require
existing_term: a_term.has_id
deferred
end
unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Unassociate term `a_term' from `(a_type_name, a_entity)'.
require
existing_term: a_term.has_id
deferred
end
associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Associate vocabulary `a_voc' with type `a_type_name'.
require
existing_term: a_voc.has_id
deferred
end
unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Un-associate vocabulary `a_voc' from type `a_type_name'.
require
existing_term: a_voc.has_id
deferred
end
end

View File

@@ -0,0 +1,122 @@
note
description: "Summary description for {CMS_TAXONOMY_STORAGE_NULL}."
date: "$Date$"
revision: "$Revision$"
class
CMS_TAXONOMY_STORAGE_NULL
inherit
CMS_TAXONOMY_STORAGE_I
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
create error_handler.make
end
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
feature -- Access
vocabulary_count: INTEGER_64
-- Count of vocabularies.
do
end
term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64
-- Number of terms from vocabulary `a_vocab'.
do
end
vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION
-- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'.
do
create Result.make (0)
end
vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY
-- Vocabulary by id `a_id'.
do
end
vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION
-- <Precursor>
do
end
terms_count: INTEGER_64
-- Number of terms.
do
end
term_by_id (tid: INTEGER_64): detachable CMS_TERM
-- Term associated with id `tid'.
do
end
term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM
do
end
terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION
-- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'.
do
create Result.make (0)
end
terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION
-- Terms related to `(a_type_name,a_entity)'.
do
end
entities_associated_with_term (a_term: CMS_TERM): detachable LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
-- Entities and related typename associated with `a_term'.
do
end
feature -- Store
save_vocabulary (a_voc: CMS_VOCABULARY)
-- Insert or update vocabulary `a_voc'.
do
error_handler.add_custom_error (-1, "not implemented", "save_vocabulary")
end
save_term (t: CMS_TERM; voc: CMS_VOCABULARY)
-- <Precursor>
do
error_handler.add_custom_error (-1, "not implemented", "save_term")
end
associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
do
error_handler.add_custom_error (-1, "not implemented", "associate_term_with_entity")
end
unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
do
error_handler.add_custom_error (-1, "not implemented", "unassociate_term_from_entity")
end
associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Associate vocabulary `a_voc' with type `a_type_name'.
do
error_handler.add_custom_error (-1, "not implemented", "associate_vocabulary_with_type")
end
unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- Un-associate vocabulary `a_voc' from type `a_type_name'.
do
error_handler.add_custom_error (-1, "not implemented", "unassociate_vocabulary_with_type")
end
end

View File

@@ -0,0 +1,546 @@
note
description: "[
Implementation of taxonomy storage using a SQL database.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_TAXONOMY_STORAGE_SQL
inherit
CMS_TAXONOMY_STORAGE_I
CMS_PROXY_STORAGE_SQL
create
make
feature -- Access
vocabulary_count: INTEGER_64
-- Count of vocabularies.
do
error_handler.reset
sql_query (sql_select_vocabularies_count, Void)
if not has_error and not sql_after then
Result := sql_read_integer_64 (1)
end
sql_finalize
end
vocabularies (a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_VOCABULARY_COLLECTION
-- List of vocabularies ordered by weight from `a_offset' to `a_offset + a_limit'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create Result.make (0)
error_handler.reset
create l_parameters.make (3)
l_parameters.put (0, "parent_tid")
from
sql_query (sql_select_terms, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_term as l_term then
Result.force (create {CMS_VOCABULARY}.make_from_term (l_term))
end
sql_forth
end
sql_finalize
end
vocabulary (a_tid: INTEGER_64): detachable CMS_VOCABULARY
-- Vocabulary by id `a_tid'.
do
if attached term_by_id (a_tid) as t then
create Result.make_from_term (t)
end
end
term_count_from_vocabulary (a_vocab: CMS_VOCABULARY): INTEGER_64
-- Number of terms from vocabulary `a_vocab'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_vocab.id, "parent_tid")
sql_query (sql_select_vocabulary_terms_count, Void)
if not has_error and not sql_after then
Result := sql_read_integer_64 (1)
end
sql_finalize
end
terms (a_vocab: CMS_VOCABULARY; a_limit: NATURAL_32; a_offset: NATURAL_32): CMS_TERM_COLLECTION
-- List of terms from vocabulary `a_vocab' ordered by weight from `a_offset' to `a_offset + a_limit'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create Result.make (0)
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_vocab.id, "parent_tid")
-- l_parameters.put (a_limit, "limit")
-- l_parameters.put (a_offset, "offset")
from
sql_query (sql_select_terms, l_parameters)
-- sql_query (sql_select_terms_with_range, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_term as l_term then
Result.force (l_term)
end
sql_forth
end
sql_finalize
end
terms_count: INTEGER_64
-- Number of terms.
do
error_handler.reset
sql_query (sql_select_terms_count, Void)
if not has_error and not sql_after then
Result := sql_read_integer_64 (1)
end
sql_finalize
end
term_by_id (a_tid: INTEGER_64): detachable CMS_TERM
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_tid, "tid")
sql_query (sql_select_term, l_parameters)
sql_start
if not has_error and not sql_after then
Result := fetch_term
end
sql_finalize
end
term_by_text (a_term_text: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_term_text, "text")
if a_vocabulary /= Void then
l_parameters.put (a_vocabulary.id, "parent_tid")
sql_query (sql_select_vocabulary_term_by_text, l_parameters)
else
sql_query (sql_select_term_by_text, l_parameters)
end
sql_start
if not has_error and not sql_after then
Result := fetch_term
end
sql_finalize
end
terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_tids: ARRAYED_LIST [INTEGER_64]
tid: INTEGER_64
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_type_name, "type")
l_parameters.put (a_entity , "entity")
if a_vocabulary /= Void then
l_parameters.put (a_vocabulary.id , "parent_tid")
sql_query (sql_select_vocabulary_terms_of_entity, l_parameters)
else
sql_query (sql_select_terms_of_entity, l_parameters)
end
create l_tids.make (0)
from
sql_start
until
sql_after or has_error
loop
tid := sql_read_integer_64 (1)
if tid > 0 then
l_tids.force (tid)
end
sql_forth
end
sql_finalize
if not l_tids.is_empty then
create Result.make (l_tids.count)
across
l_tids as ic
loop
if
ic.item > 0 and then
attached term_by_id (ic.item) as t
then
Result.force (t)
end
end
end
end
entities_associated_with_term (a_term: CMS_TERM): detachable LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]
-- Entities and related typename associated with `a_term'.
local
l_parameters: STRING_TABLE [detachable ANY]
l_typename: detachable READABLE_STRING_32
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_term.id, "tid")
sql_query (sql_select_entity_and_type_by_term, l_parameters)
if not has_error then
create {ARRAYED_LIST [TUPLE [entity: READABLE_STRING_32; typename: detachable READABLE_STRING_32]]} Result.make (0)
from
sql_start
until
sql_after or has_error
loop
if attached sql_read_string_32 (1) as l_entity then
l_typename := sql_read_string_32 (2)
if l_typename /= Void and then l_typename.is_whitespace then
l_typename := Void
end
Result.force ([l_entity, l_typename])
end
sql_forth
end
end
sql_finalize
end
feature -- Store
save_vocabulary (voc: CMS_VOCABULARY)
do
save_term (voc, create {CMS_VOCABULARY}.make_none)
across
voc.terms as ic
until
has_error
loop
save_term (ic.item, voc)
end
end
save_term (t: CMS_TERM; voc: CMS_VOCABULARY)
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (5)
l_parameters.put (t.text, "text")
l_parameters.put (t.description, "description")
l_parameters.put (t.weight, "weight")
sql_begin_transaction
if t.has_id then
l_parameters.put (t.id, "tid")
sql_modify (sql_update_term, l_parameters)
else
sql_insert (sql_insert_term, l_parameters)
t.set_id (last_inserted_term_id)
end
if not has_error then
l_parameters.put (t.id, "tid")
l_parameters.put (voc.id, "parent_tid")
sql_insert (sql_insert_term_in_vocabulary, l_parameters)
end
if has_error then
sql_rollback_transaction
else
sql_commit_transaction
end
sql_finalize
end
associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Associate term `a_term' with `(a_type_name, a_entity)'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_term.id, "tid")
l_parameters.put (a_entity, "entity")
l_parameters.put (a_type_name, "type")
sql_insert (sql_insert_term_index, l_parameters)
sql_finalize
end
unassociate_term_from_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL)
-- Unassociate term `a_term' from `(a_type_name, a_entity)'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_term.id, "tid")
l_parameters.put (a_entity, "entity")
l_parameters.put (a_type_name, "type")
sql_modify (sql_delete_term_index, l_parameters)
sql_finalize
end
feature -- Vocabulary and types
mask_is_tags: INTEGER = 0b0001 -- 1
mask_multiple_terms: INTEGER = 0b0010 -- 2
mask_is_required: INTEGER = 0b0100 -- 4
vocabularies_for_type (a_type_name: READABLE_STRING_GENERAL): detachable CMS_VOCABULARY_COLLECTION
-- <Precursor>
-- note: vocabularies are not filled with associated terms.
local
voc: detachable CMS_VOCABULARY
l_parameters: STRING_TABLE [detachable ANY]
l_data: ARRAYED_LIST [TUPLE [tid: INTEGER_64; entity: INTEGER_64]]
tid, ent: INTEGER_64
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_type_name, "type")
sql_query (sql_select_vocabularies_for_type, l_parameters)
create l_data.make (0)
from
sql_start
until
sql_after or has_error
loop
tid := sql_read_integer_64 (1)
if attached sql_read_string_32 (2) as s and then s.is_integer_64 then
ent := s.to_integer_64
else
ent := 0
end
if ent > 0 then
-- Vocabulary index should have 0 or negative value for `entity'!
check zero_or_negative_entity_value: False end
else
ent := - ent
if tid > 0 then
l_data.force ([tid, ent])
end
end
sql_forth
end
sql_finalize
if not l_data.is_empty then
create Result.make (l_data.count)
across
l_data as ic
loop
tid := ic.item.tid
ent := ic.item.entity
check ic.item.tid > 0 end
if
attached term_by_id (tid) as t
then
create voc.make_from_term (t)
--| 1: mask 0001: New terms allowed (i.e tags)
--| 2: mask 0010: Allow multiple tags
--| 4: mask 0100: At least one tag is required
voc.set_associated_content_type (a_type_name, ent & mask_is_tags = mask_is_tags, ent & mask_multiple_terms = mask_multiple_terms, ent & mask_is_required = mask_is_required)
Result.force (voc)
end
end
end
end
associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
i: INTEGER
do
error_handler.reset
create l_parameters.make (3)
l_parameters.put (a_voc.id, "tid")
if a_voc.is_tags then
i := i | mask_is_tags
end
if a_voc.is_term_required then
i := i | mask_multiple_terms
end
if a_voc.multiple_terms_allowed then
i := i | mask_is_required
end
l_parameters.put ((- i).out, "entity")
l_parameters.put (a_type_name, "type")
sql_insert (sql_insert_term_index, l_parameters)
sql_finalize
end
unassociate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
create l_parameters.make (2)
l_parameters.put (a_voc.id, "tid")
l_parameters.put (a_type_name, "type")
sql_insert (sql_delete_vocabulary_index, l_parameters)
sql_finalize
end
feature {NONE} -- Queries
last_inserted_term_id: INTEGER_64
-- Last insert term id.
do
error_handler.reset
sql_query (Sql_last_inserted_term_id, Void)
if not has_error and not sql_after then
Result := sql_read_integer_64 (1)
end
sql_finalize
end
fetch_term: detachable CMS_TERM
local
tid: INTEGER_64
l_text: detachable READABLE_STRING_32
do
tid := sql_read_integer_64 (1)
l_text := sql_read_string_32 (2)
if tid > 0 and l_text /= Void then
create Result.make_with_id (tid, l_text)
Result.set_weight (sql_read_integer_32 (3))
if attached sql_read_string_32 (4) as l_desc then
Result.set_description (l_desc)
end
end
end
sql_select_terms_count: STRING = "SELECT count(*) FROM taxonomy_term ;"
-- Number of terms.
sql_select_vocabularies_count: STRING = "SELECT count(*) FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = 0;"
-- Number of terms without parent.
sql_select_vocabulary_terms_count: STRING = "SELECT count(*) FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid WHERE taxonomy_hierarchy.parent = :parent_tid;"
-- Number of terms under :parent_tid.
sql_select_terms: STRING = "[
SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description
FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid
WHERE taxonomy_hierarchy.parent = :parent_tid
ORDER BY taxonomy_term.weight ASC ;
]"
-- Terms under :parent_tid.
sql_select_terms_with_range: STRING = "[
SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description
FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid
WHERE taxonomy_hierarchy.parent = :parent_tid
ORDER BY taxonomy_term.weight ASC LIMIT :limit OFFSET :offset
;
]"
-- Terms under :parent_tid, and :limit, :offset
sql_select_term: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE tid=:tid;"
-- Term with tid :tid .
sql_select_term_by_text: STRING = "SELECT tid, text, weight, description FROM taxonomy_term WHERE text=:text;"
-- Term with text :text .
sql_select_vocabulary_term_by_text: STRING = "[
SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description
FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid
WHERE taxonomy_hierarchy.parent=:parent_tid AND taxonomy_term.text=:text
;
]"
-- Term with text :text and with parent :parent_tid
Sql_last_inserted_term_id: STRING = "SELECT MAX(tid) FROM taxonomy_term;"
sql_insert_term: STRING = "[
INSERT INTO taxonomy_term (text, weight, description, langcode)
VALUES (:text, :weight, :description, null);
]"
sql_update_term: STRING = "[
UPDATE taxonomy_term
SET tid=:tid, text=:text, weight=:weight, description=:description, langcode=null
WHERE tid=:tid;
]"
sql_insert_term_in_vocabulary: STRING = "[
INSERT INTO taxonomy_hierarchy (tid, parent)
VALUES (:tid, :parent_tid);
]"
sql_select_terms_of_entity: STRING = "[
SELECT tid FROM taxonomy_index WHERE type=:type AND entity=:entity;
]"
sql_select_entity_and_type_by_term: STRING = "[
SELECT entity, type FROM taxonomy_index WHERE tid=:tid AND entity > 0
ORDER BY type ASC, entity ASC
;
]"
sql_select_vocabulary_terms_of_entity: STRING = "[
SELECT taxonomy_index.tid
FROM taxonomy_index INNER JOIN taxonomy_hierarchy ON taxonomy_index.tid=taxonomy_hierarchy.tid
WHERE taxonomy_hierarchy.parent=:parent_tid AND taxonomy_index.type=:type AND taxonomy_index.entity=:entity;
]"
sql_select_vocabularies_for_type: STRING = "[
SELECT tid, entity
FROM taxonomy_index
WHERE type=:type AND entity <= 0;
]"
sql_insert_term_index: STRING = "[
INSERT INTO taxonomy_index (tid, entity, type)
VALUES (:tid, :entity, :type);
]"
sql_delete_term_index: STRING = "[
DELETE FROM taxonomy_index WHERE tid=:tid AND entity=:entity AND type=:type
;
]"
sql_delete_vocabulary_index: STRING = "[
DELETE FROM taxonomy_index WHERE tid=:tid AND type=:type
;
]"
end

View File

@@ -0,0 +1,21 @@
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;
}

View File

@@ -0,0 +1,21 @@
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;
}
}
}

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

@@ -0,0 +1,29 @@
<?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_taxonomy_module" uuid="6FD848D1-5B07-46EE-B7F5-CFE2BB01479D" library_target="cms_taxonomy_module">
<target name="cms_taxonomy_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="cms_taxonomy_module" uuid="6FD848D1-5B07-46EE-B7F5-CFE2BB01479D" library_target="cms_taxonomy_module">
<target name="cms_taxonomy_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<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" location="..\..\cms.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model.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="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension.ecf" readonly="false"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -132,14 +132,16 @@ feature {NONE} -- Implementation: update
until
not a_module.is_enabled
loop
if
attached a_collection.item (ic.item) as mod and then
mod.is_enabled
then
update_module_status_within (mod, a_collection)
else
--| dependency not found or disabled
a_module.disable
if ic.item.is_required then
if
attached a_collection.item (ic.item.module_type) as mod and then
mod.is_enabled
then
update_module_status_within (mod, a_collection)
else
--| dependency not found or disabled
a_module.disable
end
end
end
end

View File

@@ -16,28 +16,25 @@ inherit
feature -- Hook
auto_subscribe_to_hooks (a_response: CMS_RESPONSE)
local
l_manager: CMS_HOOK_CORE_MANAGER
auto_subscribe_to_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
l_manager := a_response.hooks
if attached {CMS_HOOK_MENU_SYSTEM_ALTER} Current as h_menu_system_alter then
l_manager.subscribe_to_menu_system_alter_hook (h_menu_system_alter)
a_hooks.subscribe_to_menu_system_alter_hook (h_menu_system_alter)
end
if attached {CMS_HOOK_MENU_ALTER} Current as h_menu_alter then
l_manager.subscribe_to_menu_alter_hook (h_menu_alter)
a_hooks.subscribe_to_menu_alter_hook (h_menu_alter)
end
if attached {CMS_HOOK_BLOCK} Current as h_block then
l_manager.subscribe_to_block_hook (h_block)
a_hooks.subscribe_to_block_hook (h_block)
end
if attached {CMS_HOOK_FORM_ALTER} Current as h_form then
l_manager.subscribe_to_form_alter_hook (h_form)
a_hooks.subscribe_to_form_alter_hook (h_form)
end
if attached {CMS_HOOK_VALUE_TABLE_ALTER} Current as h_value then
l_manager.subscribe_to_value_table_alter_hook (h_value)
a_hooks.subscribe_to_value_table_alter_hook (h_value)
end
if attached {CMS_HOOK_RESPONSE_ALTER} Current as h_resp then
l_manager.subscribe_to_response_alter_hook (h_resp)
a_hooks.subscribe_to_response_alter_hook (h_resp)
end
end

View File

@@ -223,6 +223,34 @@ feature -- Hook: cache
end
end
feature -- Hook: export
subscribe_to_export_hook (h: CMS_HOOK_EXPORT)
-- Add `h' as subscriber of export hooks CMS_HOOK_EXPORT.
do
subscribe_to_hook (h, {CMS_HOOK_EXPORT})
end
invoke_export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
-- Invoke response alter hook for response `a_response'.
local
d: DIRECTORY
do
if attached subscribers ({CMS_HOOK_EXPORT}) as lst then
create d.make_with_path (a_export_parameters.location)
if not d.exists then
d.recursive_create_dir
end
across
lst as ic
loop
if attached {CMS_HOOK_EXPORT} ic.item as h then
h.export_to (a_export_id_list, a_export_parameters, a_response)
end
end
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,42 @@
note
description: "[
Usefull routines to export to JSON.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_EXPORT_JSON_UTILITIES
feature -- Access
put_string_into_json (st: detachable READABLE_STRING_GENERAL; a_key: JSON_STRING; j: JSON_OBJECT)
do
if st /= Void then
j.put_string (st, a_key)
end
end
put_date_into_json (dt: detachable DATE_TIME; a_key: JSON_STRING; j: JSON_OBJECT)
local
hd: HTTP_DATE
do
if dt /= Void then
create hd.make_from_date_time (dt)
j.put_integer (hd.timestamp, a_key)
end
end
json_to_string (j: JSON_VALUE): STRING
local
pp: JSON_PRETTY_STRING_VISITOR
do
create Result.make_empty
create pp.make (Result)
j.accept (pp)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,43 @@
note
description: "[
Parameters used by CMS_HOOK_EXPORT subscribers.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_EXPORT_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_location: PATH)
do
location := a_location
create logs.make (10)
end
feature -- Access
location: PATH
-- Location of export folder.
feature -- Logs
logs: ARRAYED_LIST [READABLE_STRING_8]
-- Associated exportation logs.
log (m: READABLE_STRING_8)
-- Add message `m' into `logs'.
do
logs.force (m)
end
invariant
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,31 @@
note
description: "[
CMS HOOK providing a way to export module data according to specific export parameters.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_HOOK_EXPORT
inherit
CMS_HOOK
feature -- Hook
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.
deferred
end
-- export_identifiers: detachable ITERABLE [READABLE_STRING_32]
-- -- Optional list of exportation ids, if any.
-- do
-- -- To redefine if needed.
-- end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -83,6 +83,7 @@ feature -- Conversion
Result := content
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -19,7 +19,7 @@ feature -- Basic operation
prepare (a_response: CMS_RESPONSE)
do
a_response.hooks.invoke_form_alter (Current, Void, a_response)
a_response.api.hooks.invoke_form_alter (Current, Void, a_response)
end
process (a_response: CMS_RESPONSE)
@@ -29,7 +29,7 @@ feature -- Basic operation
on_prepared (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA)
do
a_response.hooks.invoke_form_alter (Current, fd, a_response)
a_response.api.hooks.invoke_form_alter (Current, fd, a_response)
end
on_processed (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA)

View File

@@ -9,7 +9,7 @@ class
inherit
CMS_MODULE
redefine
register_hooks
setup_hooks
end
CMS_HOOK_BLOCK
@@ -47,11 +47,11 @@ 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.hooks.subscribe_to_block_hook (Current)
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
end
feature -- Hooks
@@ -71,7 +71,7 @@ feature -- Hooks
create s.make_empty
dbg.append_information_to (a_response.request, a_response.response, s)
append_info_to ("Storage", a_response.api.storage.generator, a_response, s)
create b.make ("debug-info", "Debug", s, a_response.formats.plain_text)
create b.make ("debug-info", "Debug", s, a_response.api.formats.plain_text)
b.add_condition (create {CMS_BLOCK_EXPRESSION_CONDITION}.make_none)
a_response.add_block (b, "footer")
end

View File

@@ -27,6 +27,16 @@ feature -- Access
api: detachable CMS_API assign set_api
-- Associated CMS API.
feature -- Conversion
as_sql_storage: detachable CMS_STORAGE_SQL_I
-- SQL based variant of `Current' if possible.
do
if attached {CMS_STORAGE_SQL_I} Current as st then
Result := st
end
end
feature -- Status report
is_available: BOOLEAN
@@ -59,4 +69,7 @@ feature -- Element change
api := a_api
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -80,6 +80,12 @@ feature -- URL aliases
do
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- <Precursor>.
do
create Result.make (0)
end
feature -- Logs
save_log (a_log: CMS_LOG)
@@ -116,5 +122,12 @@ feature -- Custom
end
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
do
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -47,6 +47,11 @@ feature -- URL aliases
deferred
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- All path aliases as a table containing sources indexed by alias.
deferred
end
feature -- Logs
save_log (a_log: CMS_LOG)
@@ -71,5 +76,12 @@ feature -- Misc
deferred
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -127,6 +127,35 @@ feature -- URL aliases
sql_finalize
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- All path aliases as a table containing sources indexed by alias.
local
l_source: READABLE_STRING_8
do
error_handler.reset
create Result.make (5)
sql_query (sql_select_all_path_alias, Void)
if not has_error then
from
sql_start
until
sql_after or has_error
loop
if attached sql_read_string (1) as s_src then
l_source := s_src
if attached sql_read_string (2) as s_alias then
Result.force (l_source, s_alias)
end
end
sql_forth
end
end
sql_finalize
end
sql_select_all_path_alias: STRING = "SELECT source, alias, lang FROM path_aliases;"
-- SQL select all path aliases.
sql_select_path_alias: STRING = "SELECT source FROM path_aliases WHERE alias=:alias ;"
-- SQL select path aliases.
@@ -251,6 +280,38 @@ feature -- Misc
sql_finalize
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
local
l_type, l_name: READABLE_STRING_8
do
error_handler.reset
create {ARRAYED_LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]} Result.make (5)
sql_query (sql_select_all_custom_values, Void)
if not has_error then
from
sql_start
until
sql_after or has_error
loop
if attached sql_read_string (1) as s_type then
l_type := s_type
if attached sql_read_string (2) as s_name then
l_name := s_name
if attached sql_read_string_32 (3) as s_value then
Result.force ([l_name, l_type, s_value])
end
end
end
sql_forth
end
end
sql_finalize
end
sql_select_all_custom_values: STRING = "SELECT type, name, value FROM custom_values;"
-- SQL Insert to add a new custom value.
sql_select_custom_value: STRING = "SELECT value FROM custom_values WHERE type=:type AND name=:name;"
-- SQL Insert to add a new custom value.

View File

@@ -236,16 +236,22 @@ feature -- Access
sql_start
-- Set the cursor on first element.
require
no_error: not has_error
deferred
end
sql_after: BOOLEAN
-- Are there no more items to iterate over?
require
no_error: not has_error
deferred
end
sql_forth
-- Fetch next row from last sql execution, if any.
require
no_error: not has_error
deferred
end
@@ -255,6 +261,7 @@ feature -- Access
sql_item (a_index: INTEGER): detachable ANY
require
no_error: not has_error
valid_index: sql_valid_item_index (a_index)
deferred
end
@@ -446,7 +453,7 @@ feature {NONE} -- Implementation
across
l_removals as ic
loop
Result.remove_substring (ic.item.start_index - j, ic.item.end_index - j)
Result.remove_substring (ic.item.start_index - j - a_start_index + 1, ic.item.end_index - j - a_start_index + 1)
j := j + ic.item.end_index - ic.item.start_index + 1
end
-- a_offset.replace (a_offset.item j)

View File

@@ -829,6 +829,7 @@ feature {NONE} -- Implementation: User
end
fetch_user: detachable CMS_USER
-- Fetch user from fields: 1:uid, 2:name, 3:password, 4:salt, 5:email, 6:status, 7:created, 8:signed.
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
@@ -919,10 +920,10 @@ feature {NONE} -- Sql Queries: USER
Select_user_by_name: STRING = "SELECT * FROM users WHERE name =:name;"
-- Retrieve user by name if exists.
Sql_select_recent_users: STRING = "SELECT * FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;"
Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;"
-- Retrieve recent users
Select_user_by_email: STRING = "SELECT * FROM users WHERE email =:email;"
Select_user_by_email: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users WHERE email =:email;"
-- Retrieve user by email if exists.
Select_salt_by_username: STRING = "SELECT salt FROM users WHERE name =:name;"

View File

@@ -9,6 +9,10 @@ class
inherit
ANY
CMS_HOOK_EXPORT
CMS_EXPORT_JSON_UTILITIES
REFACTORING_HELPER
CMS_ENCODERS
@@ -24,6 +28,7 @@ feature {NONE} -- Initialize
setup := a_setup
create error_handler.make
create {CMS_ENV_LOGGER} logger.make
create hooks.make
initialize
ensure
setup_set: setup = a_setup
@@ -39,6 +44,8 @@ feature {NONE} -- Initialize
do
-- Initialize formats.
initialize_formats
-- Initialize contents.
initialize_content_types
-- Initialize storage.
if attached setup.storage (error_handler) as l_storage then
@@ -82,6 +89,15 @@ feature {NONE} -- Initialize
l_enabled_modules.remove (ic.item)
end
end
-- Initialize hooks system
setup_hooks
end
initialize_content_types
-- Initialize content types.
do
create content_types.make (1)
create content_type_webform_managers.make (1)
end
initialize_formats
@@ -103,7 +119,7 @@ feature {NONE} -- Initialize
feature {CMS_ACCESS} -- Installation
install
install_all_modules
-- Install CMS or uninstalled module which are enabled.
local
l_module: CMS_MODULE
@@ -117,14 +133,30 @@ feature {CMS_ACCESS} -- Installation
-- and leave the responsability to the module to know
-- if this is installed or not...
if not l_module.is_installed (Current) then
l_module.install (Current)
if l_module.is_enabled then
l_module.initialize (Current)
end
install_module (l_module)
end
end
end
install_module (m: CMS_MODULE)
-- Install module `m'.
require
module_not_installed: not is_module_installed (m)
do
m.install (Current)
if m.is_enabled then
m.initialize (Current)
end
end
uninstall_module (m: CMS_MODULE)
-- Uninstall module `m'.
require
module_installed: is_module_installed (m)
do
m.uninstall (Current)
end
feature -- Access
setup: CMS_SETUP
@@ -136,6 +168,64 @@ feature -- Access
storage: CMS_STORAGE
-- Default persistence storage.
feature -- Content
content_types: ARRAYED_LIST [CMS_CONTENT_TYPE]
-- Available content types
add_content_type (a_type: CMS_CONTENT_TYPE)
-- Register content type `a_type'.
do
content_types.force (a_type)
end
content_type (a_name: READABLE_STRING_GENERAL): detachable CMS_CONTENT_TYPE
-- Content type named `a_named' if any.
do
across
content_types as ic
until
Result /= Void
loop
Result := ic.item
if not a_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
end
feature -- Content type webform
content_type_webform_managers: ARRAYED_LIST [CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_CONTENT]]
-- Available content types
add_content_type_webform_manager (a_manager: CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_CONTENT])
-- Register webform manager `a_manager'.
do
content_type_webform_managers.force (a_manager)
end
content_type_webform_manager (a_content_type: CMS_CONTENT_TYPE): detachable CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_CONTENT]
-- Web form manager for content type `a_content_type' if any.
do
Result := content_type_webform_manager_by_name (a_content_type.name)
end
content_type_webform_manager_by_name (a_content_type_name: READABLE_STRING_GENERAL): detachable CMS_CONTENT_TYPE_WEBFORM_MANAGER [CMS_CONTENT]
-- Web form manager for content type named `a_content_type_name' if any.
do
across
content_type_webform_managers as ic
until
Result /= Void
loop
Result := ic.item
if not a_content_type_name.is_case_insensitive_equal (Result.name) then
Result := Void
end
end
end
feature -- Formats
formats: CMS_FORMATS
@@ -302,9 +392,37 @@ feature -- Query: module
end
end
feature -- Hooks
hooks: CMS_HOOK_CORE_MANAGER
-- Manager handling hook subscriptions.
feature {NONE} -- Hooks
setup_hooks
-- Set up CMS hooks.
--| Each module has to opportunity to subscribe to various hooks.
local
l_module: CMS_MODULE
l_hooks: like hooks
do
l_hooks := hooks
register_hooks (l_hooks)
across
enabled_modules as ic
loop
l_module := ic.item
if attached {CMS_HOOK_AUTO_REGISTER} l_module as l_auto then
l_auto.auto_subscribe_to_hooks (l_hooks)
end
l_module.setup_hooks (l_hooks)
end
end
feature -- Query: API
user_api: CMS_USER_API
-- API to access user related data.
local
l_api: like internal_user_api
do
@@ -316,6 +434,14 @@ feature -- Query: API
Result := l_api
end
feature -- Hooks
register_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Register hooks associated with the cms core.
do
a_hooks.subscribe_to_export_hook (Current)
end
feature -- Path aliases
is_valid_path_alias (a_alias: READABLE_STRING_8): BOOLEAN
@@ -581,6 +707,112 @@ feature -- Environment/ modules and theme
Result := module_configuration_by_name (a_module.name, a_name)
end
feature -- Hook
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
-- <Precursor>.
local
p: PATH
d: DIRECTORY
ja: JSON_ARRAY
jobj,jo,j: JSON_OBJECT
f: PLAIN_TEXT_FILE
u: CMS_USER
do
if attached a_response.has_permissions (<<"admin export", "export core">>) then
if a_export_id_list = Void then -- Include everything
p := a_export_parameters.location.extended ("core")
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
-- path_aliases export.
a_export_parameters.log ("Exporting path_aliases")
create jo.make_empty
across storage.path_aliases as ic loop
jo.put_string (ic.item, ic.key)
end
create f.make_with_path (p.extended ("path_aliases.json"))
f.create_read_write
f.put_string (json_to_string (jo))
f.close
-- custom_values export.
if attached storage.custom_values as lst then
a_export_parameters.log ("Exporting custom_values")
create ja.make_empty
across
lst as ic
loop
create j.make_empty
if attached ic.item.type as l_type then
j.put_string (l_type, "type")
end
j.put_string (ic.item.name, "name")
if attached ic.item.type as l_value then
j.put_string (l_value, "value")
end
ja.extend (j)
end
create f.make_with_path (p.extended ("custom_values.json"))
f.create_read_write
f.put_string (json_to_string (ja))
f.close
end
-- users export.
a_export_parameters.log ("Exporting users")
create jo.make_empty
create jobj.make_empty
across user_api.recent_users (create {CMS_DATA_QUERY_PARAMETERS}.make (0, user_api.users_count.as_natural_32)) as ic loop
u := ic.item
create j.make_empty
j.put_string (u.name, "name")
j.put_integer (u.status, "status")
put_string_into_json (u.email, "email", j)
put_string_into_json (u.password, "password", j)
put_string_into_json (u.hashed_password, "hashed_password", j)
put_date_into_json (u.creation_date, "creation_date", j)
put_date_into_json (u.last_login_date, "last_login_date", j)
if attached u.roles as l_roles then
create ja.make (l_roles.count)
across
l_roles as roles_ic
loop
ja.extend (create {JSON_STRING}.make_from_string_32 ({STRING_32} " %"" + roles_ic.item.name + {STRING_32} "%" #" + roles_ic.item.id.out))
end
j.put (ja, "roles")
end
jobj.put (j, u.id.out)
end
jo.put (jobj, "users")
create jobj.make_empty
across user_api.roles as ic loop
create j.make_empty
j.put_string (ic.item.name, "name")
if attached ic.item.permissions as l_perms then
create ja.make (l_perms.count)
across
l_perms as perms_ic
loop
ja.extend (create {JSON_STRING}.make_from_string (perms_ic.item))
end
j.put (ja, "permissions")
end
jobj.put (j, ic.item.id.out)
end
jo.put (jobj, "roles")
create f.make_with_path (p.extended ("users.json"))
f.create_read_write
f.put_string (json_to_string (jo))
f.close
end
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -74,6 +74,7 @@ feature {NONE} -- Initialization
feature -- Factory
initial_cms_setup: CMS_SETUP
-- Default setup object that Current interface can customize.
deferred
end

View File

@@ -27,10 +27,10 @@ feature -- Access
-- Mostly to group modules by package/category.
version: STRING
-- Version od the module?
-- Version of the module?
dependencies: detachable LIST [TYPE [CMS_MODULE]]
-- Optional dependencies.
dependencies: detachable LIST [TUPLE [module_type: TYPE [CMS_MODULE]; is_required: BOOLEAN]]
-- Optional declaration for dependencies.
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
@@ -55,16 +55,22 @@ feature {CMS_API} -- Module Initialization
end
add_dependency (a_type: TYPE [CMS_MODULE])
-- Add dependency using type of module `a_type'.
local
deps: like dependencies
-- Add required dependency using type of module `a_type'.
do
deps := dependencies
if deps = Void then
create {ARRAYED_LIST [TYPE [CMS_MODULE]]} deps.make (1)
dependencies := deps
put_dependency (a_type, True)
end
put_dependency (a_type: TYPE [CMS_MODULE]; is_required: BOOLEAN)
-- Add required or optional dependency using type of module `a_type', based on `is_required' value.
local
lst: like dependencies
do
lst := dependencies
if lst = Void then
create {ARRAYED_LIST [TUPLE [module_type: TYPE [CMS_MODULE]; is_required: BOOLEAN]]} lst.make (1)
dependencies := lst
end
deps.force (a_type)
lst.force ([a_type, is_required])
end
feature -- Status
@@ -130,6 +136,15 @@ feature -- Router
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
obsolete
"!UNSAFE!: it is highly recommended to update this module and use setup_hooks [Dec/2015]."
require
is_enabled: is_enabled
do
setup_hooks (a_response.api.hooks)
end
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
require
is_enabled: is_enabled

View File

@@ -46,6 +46,26 @@ feature {CMS_API_ACCESS, CMS_MODULE, CMS_API} -- Restricted access
Result := cms_api.storage
end
feature -- Bridge to CMS API
html_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded for html output.
do
Result := cms_api.html_encoded (a_string)
end
url_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded with percent encoding, mainly used for url.
do
Result := cms_api.url_encoded (a_string)
end
percent_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded with percent encoding, mainly used for url.
do
Result := cms_api.percent_encoded (a_string)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,66 @@
note
description: "[
Abstract of entity managed by CMS.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_CONTENT
inherit
DEBUG_OUTPUT
feature -- Access
title: detachable READABLE_STRING_32
-- Title associated with Current content.
deferred
end
content_type: READABLE_STRING_8
-- Associated content type name.
-- Page, Article, Blog, News, etc.
deferred
end
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
deferred
end
link: detachable CMS_LOCAL_LINK
-- Associated menu link.
deferred
end
feature -- Status report
is_typed_as (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
-- Is current node of type `a_content_type' ?
do
Result := a_content_type.is_case_insensitive_equal (content_type)
end
feature -- Status report
debug_output: STRING_32
-- <Precursor>
do
create Result.make_empty
Result.append_character ('<')
Result.append_string_general (content_type)
Result.append_character ('>')
if attached title as l_title then
Result.append_character (' ')
Result.append_character ('%"')
Result.append (l_title)
Result.append_character ('%"')
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,54 @@
note
description: "[
Html builder for content type `content_type'.
This is used to build webform and html output for a specific node, or node content type.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_CONTENT_TYPE_WEBFORM_MANAGER [G -> CMS_CONTENT]
inherit
CMS_API_ACCESS
feature {NONE} -- Initialization
make (a_type: like content_type)
do
content_type := a_type
end
feature -- Access
content_type: CMS_CONTENT_TYPE
-- Associated content type.
name: READABLE_STRING_8
-- Associated content type name.
do
Result := content_type.name
end
feature -- Conversion
append_content_as_html_to (a_content: G; is_teaser: BOOLEAN; a_output: STRING; a_response: detachable CMS_RESPONSE)
-- Append `a_content' as html to `a_output', and adapt output according to `is_teaser' (full output, or teaser).
-- In the context of optional `a_response'.
deferred
end
append_formatted_content_to (a_content: READABLE_STRING_GENERAL; a_format: CONTENT_FORMAT; a_output: STRING)
-- Format string `a_content' with format `a_format', and append to `a_output'.
do
if a_content.is_valid_as_string_8 then
a_output.append (a_format.formatted_output (a_content.to_string_8))
else
a_format.append_formatted_to (a_content, a_output)
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,73 @@
note
description: "[
Instance of CMS_CONTENT representing the minimal information
for a CMS_CONTENT.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_PARTIAL_CONTENT
inherit
CMS_CONTENT
create
make_empty
feature {NONE} -- Initialization
make_empty (a_content_type: READABLE_STRING_8)
require
type_not_blank: not a_content_type.is_whitespace
do
content_type := a_content_type
end
feature -- Access
title: detachable READABLE_STRING_32
-- Title associated with Current content.
content_type: READABLE_STRING_8
-- Associated content type name.
-- Page, Article, Blog, News, etc.
format: detachable READABLE_STRING_8
-- Format associated with `content' and `summary'.
-- For example: text, mediawiki, html, etc
link: detachable CMS_LOCAL_LINK
-- Associated menu link.
feature -- Element change
set_title (a_title: detachable READABLE_STRING_GENERAL)
do
if a_title = Void then
title := Void
else
create {STRING_32} title.make_from_string_general (a_title)
end
end
set_format (a_format: like format)
-- Assign `format' with `a_format'.
do
format := a_format
ensure
format_assigned: format = a_format
end
set_link (a_link: like link)
-- Assign `link' with `a_link'.
do
link := a_link
ensure
link_assigned: link = a_link
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -45,6 +45,7 @@ feature -- HTTP Methods
lst: ARRAYED_LIST [CMS_MODULE]
l_denied: BOOLEAN
do
--| FIXME: improve the installer.
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
@@ -77,7 +78,7 @@ feature -- HTTP Methods
lst.force (l_module)
end
end
api.install
api.install_all_modules
across
lst as ic
loop

View File

@@ -188,4 +188,7 @@ feature -- Url
Result := html_encoded (a_text)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

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