Compare commits
46 Commits
es_rev9806
...
es_rev9828
| Author | SHA1 | Date | |
|---|---|---|---|
| 0813abe0bb | |||
| 1094acb3ec | |||
| e7c9a54f3f | |||
| bbbdac12c8 | |||
|
|
22528315cb | ||
|
|
090a48eb85 | ||
|
|
e05c4dca3a | ||
|
|
2255fcc0f6 | ||
| e50fb6959e | |||
|
|
3b88c746a1 | ||
|
|
fa8ef44a4a | ||
|
|
068943734f | ||
|
|
089179e60e | ||
|
|
c25590c9cd | ||
| 23d266497b | |||
| ce8de442e9 | |||
| e3ae564746 | |||
| b0626d5250 | |||
| 276dcc6fcd | |||
| 6313007fbf | |||
| ecbcb6a5cb | |||
| a5c117e46e | |||
| 20dfce1396 | |||
|
|
1bfc4a6741 | ||
|
|
3fdbcb2eef | ||
|
|
a11a93c285 | ||
|
|
fade19bbee | ||
|
|
d10612f94b | ||
|
|
9da8b8a025 | ||
| f1f3c126dd | |||
| 1d4ce37ebf | |||
|
|
10102e80fa | ||
| 3791ffacdc | |||
| b8920ee8b3 | |||
| 2cf2b1da8c | |||
| 17ae27df40 | |||
|
|
a976b1e21a | ||
| 04df6b85f0 | |||
| 79d30ee3a7 | |||
| a5973c9c8a | |||
| 420051cd14 | |||
| 6b3ff6f980 | |||
| 360855a558 | |||
| 6aaec0be9f | |||
| cb6d13b5f7 | |||
| 951c977892 |
@@ -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"/>
|
||||

|
||||
|
||||
```
|
||||
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.
|
||||
BIN
doc/img_diagram.png
Normal file
BIN
doc/img_diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
396
doc/readme.md
Normal file
396
doc/readme.md
Normal 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.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 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.)*
|
||||
@@ -29,16 +29,17 @@
|
||||
<library name="cms_google_search_module" location="..\..\modules\google_search\google_search-safe.ecf" readonly="false" use_application_options="true"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
|
||||
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
|
||||
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
|
||||
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/>
|
||||
<library name="cms_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" />
|
||||
-->
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
CREATE TABLE session_auth (
|
||||
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
|
||||
`access_token` TEXT NOT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
CONSTRAINT `uid`
|
||||
UNIQUE(`uid`),
|
||||
CONSTRAINT `access_token`
|
||||
UNIQUE(`access_token`)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="primary-tabs">
|
||||
{unless isset="$user"}
|
||||
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
|
||||
<div>
|
||||
<div>
|
||||
<form name="cms_session_auth" action="{$site_url/}account/login-with-session" method="POST">
|
||||
<div>
|
||||
<input type="text" name="username" id="username" required value="{$username/}">
|
||||
<label>Username</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="password" name="password" id="password" required >
|
||||
<label>Password</label>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<a href="{$site_url/}account/new-password">Forgot password?</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/unless}
|
||||
{if isset=$error}
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<strong>{$error/}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
26
examples/demo/site/modules/taxonomy/files/css/taxonomy.css
Normal file
26
examples/demo/site/modules/taxonomy/files/css/taxonomy.css
Normal file
@@ -0,0 +1,26 @@
|
||||
ul.taxonomy {
|
||||
font-size: 80%;
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
ul.taxonomy li {
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
ul.taxonomy li a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.taxonomy li:hover {
|
||||
padding: 1px;
|
||||
border-top: solid 1px #66f;
|
||||
border-bottom: solid 1px #66f;
|
||||
background-color: #ddf;
|
||||
}
|
||||
|
||||
table.taxonomy td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
27
examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss
Normal file
27
examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
ul.taxonomy {
|
||||
font-size: 80%;
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
li {
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
&:hover {
|
||||
padding: 1px;
|
||||
border-top: solid 1px #66f;
|
||||
border-bottom: solid 1px #66f;
|
||||
background-color: #ddf;
|
||||
}
|
||||
}
|
||||
}
|
||||
table.taxonomy {
|
||||
td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
24
examples/demo/site/modules/taxonomy/scripts/install.sql
Normal file
24
examples/demo/site/modules/taxonomy/scripts/install.sql
Normal 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)
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS taxonomy_term;
|
||||
DROP TABLE IF EXISTS taxonomy_hierarchy;
|
||||
DROP TABLE IF EXISTS taxonomy_index;
|
||||
@@ -90,3 +90,11 @@ ul.horizontal li {
|
||||
border: solid 1px red;
|
||||
padding: 5px 2px 5px 2px;
|
||||
}
|
||||
|
||||
table.with_border thead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
table.with_border td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
|
||||
@@ -95,3 +95,13 @@ ul.horizontal {
|
||||
border: solid 1px red;
|
||||
padding: 5px 2px 5px 2px;
|
||||
}
|
||||
|
||||
table.with_border {
|
||||
thead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -85,6 +89,9 @@ feature -- CMS modules
|
||||
|
||||
create {GOOGLE_CUSTOM_SEARCH_MODULE} m.make
|
||||
a_setup.register_module (m)
|
||||
|
||||
create {CMS_SESSION_AUTH_MODULE} m.make
|
||||
a_setup.register_module (m)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -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)
|
||||
@@ -456,7 +466,17 @@ feature {NONE} -- Implementation
|
||||
sk.left_adjust
|
||||
sk.right_adjust
|
||||
k.keep_head (i - 1)
|
||||
if attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
|
||||
if
|
||||
a_section_prefix /= Void and then
|
||||
attached {STRING_TABLE [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_table
|
||||
then
|
||||
tb := l_table
|
||||
elseif
|
||||
attached last_section_name as l_section_prefix and then
|
||||
attached {STRING_TABLE [STRING_8]} items.item (l_section_prefix + {STRING_32} "." + k) as l_table
|
||||
then
|
||||
tb := l_table
|
||||
elseif attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
|
||||
tb := l_table
|
||||
else
|
||||
create tb.make (1)
|
||||
|
||||
@@ -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="gcse" uuid="30466429-C24A-40E8-957F-F245992A7A9F" library_target="gcse">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
|
||||
<target name="gcse">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -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="gcse" uuid="0B73AB16-3D8A-45EA-84B7-B11BBE4BA80A" library_target="gcse">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
|
||||
<target name="gcse">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -152,6 +152,7 @@ feature -- Query
|
||||
-- Retrieved value at `a_index' position in `item'.
|
||||
local
|
||||
l_item: like sql_item
|
||||
i64: INTEGER_64
|
||||
do
|
||||
l_item := sql_item (a_index)
|
||||
if attached {INTEGER_32} l_item as i then
|
||||
@@ -159,7 +160,18 @@ feature -- Query
|
||||
elseif attached {INTEGER_32_REF} l_item as l_value then
|
||||
Result := l_value.item
|
||||
else
|
||||
check is_integer_32: False end
|
||||
if attached {INTEGER_64} l_item as i then
|
||||
i64 := i
|
||||
elseif attached {INTEGER_64_REF} l_item as l_value then
|
||||
i64 := l_value.item
|
||||
else
|
||||
check is_integer_32: False end
|
||||
end
|
||||
if i64 <= {INTEGER_32}.max_value then
|
||||
Result := i64.to_integer_32
|
||||
else
|
||||
check is_integer_32: False end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ feature -- Execution
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := clear_cache_web_form (l_response)
|
||||
create s.make_empty
|
||||
f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s)
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
@@ -63,14 +63,14 @@ feature -- Execution
|
||||
fd.is_valid
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_clear_all_caches) then
|
||||
l_response.hooks.invoke_clear_cache (Void, l_response)
|
||||
api.hooks.invoke_clear_cache (Void, l_response)
|
||||
l_response.add_notice_message ("Caches cleared (if allowed)!")
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
end
|
||||
create s.make_empty
|
||||
f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s)
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
115
modules/admin/handler/cms_admin_export_handler.e
Normal file
115
modules/admin/handler/cms_admin_export_handler.e
Normal file
@@ -0,0 +1,115 @@
|
||||
note
|
||||
description: "[
|
||||
Administrate export functionality.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ADMIN_EXPORT_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
rename
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get,
|
||||
do_post
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_response: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := exportation_web_form (l_response)
|
||||
create s.make_empty
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_response: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
l_exportation_parameters: CMS_EXPORT_PARAMETERS
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
f := exportation_web_form (l_response)
|
||||
f.process (l_response)
|
||||
if
|
||||
attached f.last_data as fd and then
|
||||
fd.is_valid
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_export_all_data) then
|
||||
if attached fd.string_item ("folder") as l_folder then
|
||||
create l_exportation_parameters.make (api.site_location.extended ("export").extended (l_folder))
|
||||
else
|
||||
create l_exportation_parameters.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss")))
|
||||
end
|
||||
api.hooks.invoke_export_to (Void, l_exportation_parameters, l_response)
|
||||
l_response.add_notice_message ("All data exported (if allowed)!")
|
||||
create s.make_empty
|
||||
across
|
||||
l_exportation_parameters.logs as ic
|
||||
loop
|
||||
s.append (ic.item)
|
||||
s.append ("<br/>")
|
||||
s.append_character ('%N')
|
||||
end
|
||||
l_response.add_notice_message (s)
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
end
|
||||
create s.make_empty
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
feature -- Widget
|
||||
|
||||
exportation_web_form (a_response: CMS_RESPONSE): CMS_FORM
|
||||
local
|
||||
f_name: WSF_FORM_TEXT_INPUT
|
||||
but: WSF_FORM_SUBMIT_INPUT
|
||||
do
|
||||
create Result.make (a_response.url (a_response.location, Void), "export_all_data")
|
||||
Result.extend_raw_text ("Export CMS data to ")
|
||||
create f_name.make_with_text ("folder", (create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss"))
|
||||
f_name.set_label ("Export folder name")
|
||||
f_name.set_description ("Folder name under 'exports' folder.")
|
||||
f_name.set_is_required (True)
|
||||
Result.extend (f_name)
|
||||
create but.make_with_text ("op", text_export_all_data)
|
||||
Result.extend (but)
|
||||
end
|
||||
|
||||
feature -- Interface text.
|
||||
|
||||
text_export_all_data: STRING_32 = "Export all data"
|
||||
|
||||
end
|
||||
320
modules/admin/handler/cms_admin_modules_handler.e
Normal file
320
modules/admin/handler/cms_admin_modules_handler.e
Normal file
@@ -0,0 +1,320 @@
|
||||
note
|
||||
description: "[
|
||||
Administrate modules.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ADMIN_MODULES_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
rename
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get, do_post
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
CMS_SETUP_ACCESS
|
||||
|
||||
CMS_ACCESS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
l_denied: BOOLEAN
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.query_parameter ("op") as l_op and then l_op.same_string ("uninstall") and then
|
||||
attached {WSF_TABLE} req.query_parameter ("module_uninstallation") as tb
|
||||
then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
if attached api.setup.string_8_item ("admin.installation_access") as l_access then
|
||||
if l_access.is_case_insensitive_equal ("none") then
|
||||
l_denied := True
|
||||
elseif l_access.is_case_insensitive_equal ("permission") then
|
||||
l_denied := not r.has_permission ("install modules")
|
||||
end
|
||||
else
|
||||
l_denied := True
|
||||
end
|
||||
if l_denied then
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("You do not have permission to access CMS module uninstallation procedure!")
|
||||
else
|
||||
create s.make_empty
|
||||
across
|
||||
tb as ic
|
||||
loop
|
||||
if attached api.setup.modules.item_by_name (ic.item.string_representation) as l_module then
|
||||
if api.is_module_installed (l_module) then
|
||||
api.uninstall_module (l_module)
|
||||
if api.is_module_installed (l_module) then
|
||||
s.append ("<p>ERROR: Module " + l_module.name + " failed to be uninstalled!</p>")
|
||||
else
|
||||
s.append ("<p>Module " + l_module.name + " was successfully uninstalled.</p>")
|
||||
end
|
||||
else
|
||||
s.append ("<p>Module " + l_module.name + " is not installed.</p>")
|
||||
end
|
||||
end
|
||||
end
|
||||
s.append (r.link ("Back to modules management", r.location, Void))
|
||||
r.set_main_content (s)
|
||||
end
|
||||
r.execute
|
||||
else
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
f := modules_collection_web_form (r)
|
||||
create s.make_empty
|
||||
f.append_to_html (r.wsf_theme, s)
|
||||
r.set_page_title ("Modules")
|
||||
r.set_main_content (s)
|
||||
r.execute
|
||||
end
|
||||
end
|
||||
|
||||
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
l_denied: BOOLEAN
|
||||
do
|
||||
if attached {WSF_STRING} req.item ("op") as l_op then
|
||||
if l_op.same_string ("Install modules") then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
|
||||
if attached api.setup.string_8_item ("admin.installation_access") as l_access then
|
||||
if l_access.is_case_insensitive_equal ("none") then
|
||||
l_denied := True
|
||||
elseif l_access.is_case_insensitive_equal ("permission") then
|
||||
l_denied := not r.has_permission ("install modules")
|
||||
end
|
||||
else
|
||||
l_denied := True
|
||||
end
|
||||
if l_denied then
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("You do not have permission to access CMS module installation procedure!")
|
||||
else
|
||||
f := modules_collection_web_form (r)
|
||||
if l_op.same_string ("Install modules") then
|
||||
f.submit_actions.extend (agent on_installation_submit)
|
||||
f.process (r)
|
||||
elseif l_op.same_string ("uninstall") then
|
||||
f.submit_actions.extend (agent on_uninstallation_submit)
|
||||
f.process (r)
|
||||
end
|
||||
if
|
||||
not attached f.last_data as l_data or else
|
||||
not l_data.is_valid
|
||||
then
|
||||
r.add_error_message ("Error occurred.")
|
||||
create s.make_empty
|
||||
f.append_to_html (r.wsf_theme, s)
|
||||
r.set_page_title ("Modules")
|
||||
r.set_main_content (s)
|
||||
else
|
||||
r.add_notice_message ("Operation on module(s) succeeded.")
|
||||
r.set_redirection (r.location)
|
||||
end
|
||||
end
|
||||
r.execute
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.execute
|
||||
end
|
||||
else
|
||||
do_get (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
modules_collection_web_form (a_response: CMS_RESPONSE): CMS_FORM
|
||||
local
|
||||
mod: CMS_MODULE
|
||||
f_cb: WSF_FORM_CHECKBOX_INPUT
|
||||
w_tb: WSF_WIDGET_TABLE
|
||||
w_row: WSF_WIDGET_TABLE_ROW
|
||||
w_item: WSF_WIDGET_TABLE_ITEM
|
||||
w_submit: WSF_FORM_SUBMIT_INPUT
|
||||
w_set: WSF_FORM_FIELD_SET
|
||||
|
||||
l_mods_to_install: ARRAYED_LIST [CMS_MODULE]
|
||||
do
|
||||
create Result.make (a_response.url (a_response.location, Void), "modules_collection")
|
||||
create w_tb.make
|
||||
w_tb.add_css_class ("modules_table")
|
||||
create w_row.make (5)
|
||||
create w_item.make_with_text ("Enabled ")
|
||||
w_row.add_item (w_item)
|
||||
create w_item.make_with_text ("Module")
|
||||
w_row.add_item (w_item)
|
||||
create w_item.make_with_text ("Version")
|
||||
w_row.add_item (w_item)
|
||||
create w_item.make_with_text ("Description")
|
||||
w_row.add_item (w_item)
|
||||
w_tb.add_head_row (w_row)
|
||||
|
||||
create l_mods_to_install.make (0)
|
||||
across
|
||||
a_response.api.setup.modules as ic
|
||||
loop
|
||||
mod := ic.item
|
||||
if not a_response.api.is_module_installed (mod) then
|
||||
l_mods_to_install.extend (mod)
|
||||
else
|
||||
create w_row.make (5)
|
||||
create f_cb.make ("module_" + mod.name)
|
||||
f_cb.set_text_value (mod.name)
|
||||
f_cb.set_checked (mod.is_enabled)
|
||||
f_cb.set_is_readonly (True)
|
||||
create w_item.make_with_content (f_cb)
|
||||
w_row.add_item (w_item)
|
||||
|
||||
create w_item.make_with_text (mod.name)
|
||||
w_row.add_item (w_item)
|
||||
|
||||
create w_item.make_with_text (mod.version)
|
||||
w_row.add_item (w_item)
|
||||
|
||||
if attached mod.description as l_desc then
|
||||
create w_item.make_with_text (l_desc)
|
||||
w_row.add_item (w_item)
|
||||
else
|
||||
create w_item.make_with_text ("")
|
||||
w_row.add_item (w_item)
|
||||
end
|
||||
create w_item.make_with_text (a_response.link ("Uninstall", a_response.location + "?op=uninstall&module_uninstallation[]=" + mod.name, Void))
|
||||
w_row.add_item (w_item)
|
||||
|
||||
w_tb.add_row (w_row)
|
||||
end
|
||||
end
|
||||
create w_set.make
|
||||
w_set.set_legend ("Installed modules")
|
||||
w_set.extend (w_tb)
|
||||
-- create w_submit.make ("op")
|
||||
-- w_submit.set_text_value ("Save")
|
||||
-- w_set.extend (w_submit)
|
||||
Result.extend (w_set)
|
||||
|
||||
Result.extend_html_text ("<br/>")
|
||||
|
||||
if not l_mods_to_install.is_empty then
|
||||
create w_tb.make
|
||||
w_tb.add_css_class ("modules_table")
|
||||
create w_row.make (3)
|
||||
create w_item.make_with_text ("Install ")
|
||||
w_row.add_item (w_item)
|
||||
create w_item.make_with_text ("Module")
|
||||
w_row.add_item (w_item)
|
||||
create w_item.make_with_text ("Description")
|
||||
w_row.add_item (w_item)
|
||||
w_tb.add_head_row (w_row)
|
||||
across
|
||||
l_mods_to_install as ic
|
||||
loop
|
||||
mod := ic.item
|
||||
create w_row.make (3)
|
||||
create f_cb.make ("module_installation[" + mod.name + "]")
|
||||
f_cb.set_text_value (mod.name)
|
||||
create w_item.make_with_content (f_cb)
|
||||
w_row.add_item (w_item)
|
||||
|
||||
create w_item.make_with_text (mod.name)
|
||||
w_row.add_item (w_item)
|
||||
|
||||
if attached mod.description as l_desc then
|
||||
create w_item.make_with_text (l_desc)
|
||||
w_row.add_item (w_item)
|
||||
else
|
||||
create w_item.make_with_text ("")
|
||||
w_row.add_item (w_item)
|
||||
end
|
||||
w_tb.add_row (w_row)
|
||||
end
|
||||
create w_set.make
|
||||
w_set.set_legend ("Available modules for installation")
|
||||
w_set.extend (w_tb)
|
||||
create w_submit.make ("op")
|
||||
w_submit.set_text_value ("Install modules")
|
||||
w_set.extend (w_submit)
|
||||
Result.extend (w_set)
|
||||
end
|
||||
end
|
||||
|
||||
on_installation_submit (fd: WSF_FORM_DATA)
|
||||
local
|
||||
l_mods: CMS_MODULE_COLLECTION
|
||||
do
|
||||
if attached {WSF_TABLE} fd.table_item ("module_installation") as tb and then not tb.is_empty then
|
||||
l_mods := api.setup.modules
|
||||
across
|
||||
tb as ic
|
||||
loop
|
||||
if
|
||||
attached {WSF_STRING} ic.item as l_mod_name and then
|
||||
attached l_mods.item_by_name (l_mod_name.value) as m
|
||||
then
|
||||
api.install_module (m)
|
||||
if not api.is_module_installed (m) then
|
||||
fd.report_error ("Installation failed for module " + m.name)
|
||||
end
|
||||
else
|
||||
fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value)
|
||||
end
|
||||
end
|
||||
else
|
||||
fd.report_error ("No module to install!")
|
||||
end
|
||||
end
|
||||
|
||||
on_uninstallation_submit (fd: WSF_FORM_DATA)
|
||||
local
|
||||
l_mods: CMS_MODULE_COLLECTION
|
||||
do
|
||||
if attached {WSF_TABLE} fd.table_item ("module_uninstallation") as tb and then not tb.is_empty then
|
||||
l_mods := api.setup.modules
|
||||
across
|
||||
tb as ic
|
||||
loop
|
||||
if
|
||||
attached {WSF_STRING} ic.item as l_mod_name and then
|
||||
attached l_mods.item_by_name (l_mod_name.value) as m
|
||||
then
|
||||
api.uninstall_module (m)
|
||||
if api.is_module_installed (m) then
|
||||
fd.report_error ("Un-Installation failed for module " + m.name)
|
||||
end
|
||||
else
|
||||
fd.report_error ("Can not find associated module" + ic.item.as_string.url_encoded_value)
|
||||
end
|
||||
end
|
||||
else
|
||||
fd.report_error ("No module to uninstall!")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -8,30 +8,10 @@ class
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Process
|
||||
|
||||
process
|
||||
|
||||
@@ -8,32 +8,12 @@ class
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
CMS_SHARED_SORTING_UTILITIES
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Query
|
||||
|
||||
role_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
@@ -84,7 +64,7 @@ feature -- Process Edit
|
||||
do
|
||||
create b.make_empty
|
||||
f := new_edit_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
|
||||
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 +97,7 @@ feature -- Process Delete
|
||||
do
|
||||
create b.make_empty
|
||||
f := new_delete_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
|
||||
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 +129,7 @@ feature -- Process New
|
||||
do
|
||||
create b.make_empty
|
||||
f := new_edit_form (l_role, url (request.percent_encoded_path_info, Void), "create-role")
|
||||
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))
|
||||
|
||||
@@ -8,31 +8,10 @@ class
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Query
|
||||
|
||||
role_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
|
||||
@@ -7,32 +7,11 @@ class
|
||||
CMS_USER_FORM_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
@@ -86,7 +65,7 @@ feature -- Process Edit
|
||||
do
|
||||
create b.make_empty
|
||||
f := new_edit_form (a_user, url (location, Void), "edit-user")
|
||||
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 +97,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 +130,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))
|
||||
|
||||
@@ -8,31 +8,10 @@ class
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
|
||||
@@ -9,10 +9,10 @@ class
|
||||
inherit
|
||||
CMS_MODULE
|
||||
redefine
|
||||
register_hooks
|
||||
setup_hooks,
|
||||
permissions
|
||||
end
|
||||
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
|
||||
CMS_HOOK_VALUE_TABLE_ALTER
|
||||
@@ -52,6 +52,13 @@ feature -- Access
|
||||
|
||||
name: STRING = "auth"
|
||||
|
||||
permissions: LIST [READABLE_STRING_8]
|
||||
-- List of permission ids, used by this module, and declared.
|
||||
do
|
||||
Result := Precursor
|
||||
Result.force ("account register")
|
||||
end
|
||||
|
||||
feature -- Access: docs
|
||||
|
||||
root_dir: PATH
|
||||
@@ -91,12 +98,12 @@ feature -- Router
|
||||
|
||||
feature -- Hooks configuration
|
||||
|
||||
register_hooks (a_response: CMS_RESPONSE)
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
-- Module hooks configuration.
|
||||
do
|
||||
auto_subscribe_to_hooks (a_response)
|
||||
a_response.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)
|
||||
@@ -228,6 +235,7 @@ feature -- Handler
|
||||
end
|
||||
else
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("You can also contact the webmaster to ask for an account.")
|
||||
end
|
||||
|
||||
r.execute
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<target name="google_search">
|
||||
<root all_classes="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="cms" location="$ISE_LIBRARY\unstable\library\web\cms\cms-safe.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="$ISE_LIBRARY\unstable\library\web\cms\library\app_env\app_env-safe.ecf" readonly="false"/>
|
||||
<library name="cms_config" location="$ISE_LIBRARY\unstable\library\web\cms\library\configuration\config-safe.ecf"/>
|
||||
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
|
||||
<library name="cms_config" location="..\..\library\configuration\config-safe.ecf"/>
|
||||
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
|
||||
<library name="google_cse" location="..\..\library\gcse\gcse-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<option is_attached_by_default="false" void_safety="none">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="cms" location="$ISE_LIBRARY\unstable\library\web\cms\cms.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="$ISE_LIBRARY\unstable\library\web\cms\library\app_env\app_env.ecf" readonly="false"/>
|
||||
<library name="cms_config" location="$ISE_LIBRARY\unstable\library\web\cms\library\configuration\config.ecf"/>
|
||||
<library name="cms" location="..\..\cms.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="..\..\library\app_env\app_env.ecf" readonly="false"/>
|
||||
<library name="cms_config" location="..\..\library\configuration\config.ecf"/>
|
||||
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
|
||||
<library name="google_cse" location="..\..\library\gcse\gcse.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,7 +10,13 @@ deferred class
|
||||
CMS_NODE
|
||||
|
||||
inherit
|
||||
DEBUG_OUTPUT
|
||||
CMS_CONTENT
|
||||
rename
|
||||
has_identifier as has_id
|
||||
redefine
|
||||
debug_output, has_id
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature{NONE} -- Initialization
|
||||
@@ -59,6 +65,12 @@ feature -- Conversion
|
||||
|
||||
feature -- Access
|
||||
|
||||
identifier: detachable IMMUTABLE_STRING_32
|
||||
-- Optional identifier.
|
||||
do
|
||||
create Result.make_from_string_general (id.out)
|
||||
end
|
||||
|
||||
id: INTEGER_64 assign set_id
|
||||
-- Unique id.
|
||||
--| Should we use NATURAL_64 instead?
|
||||
@@ -67,12 +79,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 +119,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 +155,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 +168,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
|
||||
|
||||
55
modules/node/export/cms_export_node_utilities.e
Normal file
55
modules/node/export/cms_export_node_utilities.e
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -92,10 +92,18 @@ feature -- Forms ...
|
||||
|
||||
f.extend (fset)
|
||||
|
||||
-- Path alias
|
||||
-- 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: CMS_RESPONSE; f: CMS_FORM; a_content: detachable CMS_CONTENT)
|
||||
do
|
||||
if attached {CMS_TAXONOMY_API} response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then
|
||||
l_taxonomy_api.populate_edit_form (response, f, content_type.name, a_content)
|
||||
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 +187,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 +251,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 +263,104 @@ 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
|
||||
then
|
||||
l_taxonomy_api.append_taxonomy_to_xhtml (a_node, a_response, a_output)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,30 +7,10 @@ class
|
||||
|
||||
inherit
|
||||
NODE_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api, a_node_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Execution
|
||||
|
||||
process
|
||||
@@ -114,10 +94,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 +124,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 +153,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 +186,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 +339,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 +361,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 +407,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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,30 +8,10 @@ class
|
||||
|
||||
inherit
|
||||
NODE_RESPONSE
|
||||
redefine
|
||||
make,
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api)
|
||||
do
|
||||
create {WSF_NULL_THEME} wsf_theme.make
|
||||
Precursor (req, res, a_api, a_node_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme)
|
||||
end
|
||||
|
||||
wsf_theme: WSF_THEME
|
||||
|
||||
feature -- Access
|
||||
|
||||
node: detachable CMS_NODE
|
||||
@@ -71,7 +51,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!")
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,13 +93,13 @@ 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)
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for blog module", generating_type)
|
||||
api.logger.put_error ("Could not initialize database for oauth_20 module", generating_type)
|
||||
end
|
||||
-- TODO workaround.
|
||||
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers_initialize.sql")), Void)
|
||||
@@ -108,11 +108,11 @@ feature {CMS_API} -- Module management
|
||||
-- TODO workaround, until we have an admin module
|
||||
l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void)
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type)
|
||||
api.logger.put_error ("Could not initialize database for differnent consumers", generating_type)
|
||||
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
|
||||
@@ -227,7 +227,10 @@ feature -- Hooks
|
||||
until
|
||||
lnk2 /= Void
|
||||
loop
|
||||
if ic.item.location.same_string ("account/roc-logout") then
|
||||
if
|
||||
ic.item.location.same_string ("account/roc-logout") or else
|
||||
ic.item.location.same_string ("basic_auth_logoff")
|
||||
then
|
||||
lnk2 := ic.item
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -203,7 +203,10 @@ feature -- Hooks
|
||||
until
|
||||
lnk2 /= Void
|
||||
loop
|
||||
if ic.item.location.same_string ("account/roc-logout") then
|
||||
if
|
||||
ic.item.location.same_string ("account/roc-logout") or else
|
||||
ic.item.location.same_string ("basic_auth_logoff")
|
||||
then
|
||||
lnk2 := ic.item
|
||||
end
|
||||
end
|
||||
|
||||
@@ -197,7 +197,7 @@ feature {NONE} -- User OpenID
|
||||
|
||||
Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);"
|
||||
|
||||
Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers";
|
||||
Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers;"
|
||||
|
||||
|
||||
feature {NONE} -- Consumer
|
||||
|
||||
@@ -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
|
||||
@@ -284,7 +284,7 @@ feature -- Handler
|
||||
create l_submit.make_with_text ("op", "Filter")
|
||||
l_form.extend (l_submit)
|
||||
l_form.extend_html_text ("<br/>")
|
||||
l_form.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), l_content)
|
||||
l_form.append_to_html (r.wsf_theme, l_content)
|
||||
end
|
||||
|
||||
l_changes.reverse_sort
|
||||
@@ -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
|
||||
|
||||
63
modules/session_auth/cms_session_api.e
Normal file
63
modules/session_auth/cms_session_api.e
Normal file
@@ -0,0 +1,63 @@
|
||||
note
|
||||
description: "API to manage CMS User session authentication"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_SESSION_API
|
||||
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create {CMS_SESSION_AUTH_MODULE}
|
||||
make_with_storage
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_storage (a_api: CMS_API; a_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I)
|
||||
-- Create an object with api `a_api' and storage `a_session_auth_storage'.
|
||||
do
|
||||
session_auth_storage := a_session_auth_storage
|
||||
make (a_api)
|
||||
ensure
|
||||
session_auth_storage_set: session_auth_storage = a_session_auth_storage
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access: User session storage.
|
||||
|
||||
session_auth_storage: CMS_SESSION_AUTH_STORAGE_I
|
||||
-- storage interface.
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token', if any.
|
||||
do
|
||||
Result := session_auth_storage.user_by_session_token (a_token)
|
||||
end
|
||||
|
||||
has_user_token (a_user: CMS_USER): BOOLEAN
|
||||
-- Has the user `a_user' and associated session token?
|
||||
do
|
||||
Result := session_auth_storage.has_user_token (a_user)
|
||||
end
|
||||
|
||||
feature -- Change User session
|
||||
|
||||
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;)
|
||||
-- New user session for user `a_user' with token `a_token'.
|
||||
do
|
||||
session_auth_storage.new_user_session_auth (a_token, a_user)
|
||||
end
|
||||
|
||||
|
||||
update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER )
|
||||
-- Update user session for user `a_user' with token `a_token'.
|
||||
do
|
||||
session_auth_storage.update_user_session_auth (a_token, a_user)
|
||||
end
|
||||
|
||||
end
|
||||
28
modules/session_auth/cms_session_auth-safe.ecf
Normal file
28
modules/session_auth/cms_session_auth-safe.ecf
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<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="session_auth" uuid="8A43B6DD-6B39-472C-9A96-978414CBF1E3" library_target="session_auth">
|
||||
<target name="session_auth">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="false" is_attached_by_default="true" 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_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
|
||||
<library name="cms_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<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="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_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
349
modules/session_auth/cms_session_auth_module.e
Normal file
349
modules/session_auth/cms_session_auth_module.e
Normal file
@@ -0,0 +1,349 @@
|
||||
note
|
||||
description: "[
|
||||
This module allows the use Session Based Authentication using Cookies to restrict access
|
||||
by looking up users in the given providers.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_SESSION_AUTH_MODULE
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as user_session_api
|
||||
redefine
|
||||
filters,
|
||||
setup_hooks,
|
||||
initialize,
|
||||
install,
|
||||
user_session_api
|
||||
end
|
||||
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
|
||||
CMS_HOOK_BLOCK
|
||||
|
||||
CMS_HOOK_MENU_SYSTEM_ALTER
|
||||
|
||||
CMS_HOOK_VALUE_TABLE_ALTER
|
||||
|
||||
SHARED_LOGGER
|
||||
|
||||
CMS_REQUEST_UTIL
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Service to manage cookie based authentication"
|
||||
package := "authentication"
|
||||
add_dependency ({CMS_AUTHENTICATION_MODULE})
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "session_auth"
|
||||
|
||||
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_session_auth_api: like user_session_api
|
||||
l_user_auth_storage: CMS_SESSION_AUTH_STORAGE_I
|
||||
do
|
||||
Precursor (a_api)
|
||||
|
||||
-- Storage initialization
|
||||
if attached a_api.storage.as_sql_storage as l_storage_sql then
|
||||
create {CMS_SESSION_AUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
|
||||
else
|
||||
-- FIXME: in case of NULL storage, should Current be disabled?
|
||||
create {CMS_SESSION_AUTH_STORAGE_NULL} l_user_auth_storage
|
||||
end
|
||||
|
||||
-- API initialization
|
||||
create l_session_auth_api.make_with_storage (a_api, l_user_auth_storage)
|
||||
user_session_api := l_session_auth_api
|
||||
ensure then
|
||||
session_auth_api_set: user_session_api /= Void
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
install (api: CMS_API)
|
||||
do
|
||||
-- Schema
|
||||
if attached api.storage.as_sql_storage as l_sql_storage then
|
||||
if not l_sql_storage.sql_table_exists ("session_auth") then
|
||||
--| Schema
|
||||
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void)
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for session auth module", generating_type)
|
||||
end
|
||||
end
|
||||
l_sql_storage.sql_finalize
|
||||
Precursor {CMS_MODULE}(api)
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
|
||||
user_session_api: detachable CMS_SESSION_API
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Access: router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
a_router.handle ("/account/roc-session-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get)
|
||||
a_router.handle ("/account/roc-session-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/login-with-session", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,user_session_api, ?, ?)), a_router.methods_get_post)
|
||||
end
|
||||
|
||||
feature -- Access: filter
|
||||
|
||||
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
|
||||
-- Possibly list of Filter's module.
|
||||
do
|
||||
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
|
||||
if attached user_session_api as l_session_api then
|
||||
Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api))
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: routes
|
||||
|
||||
handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.execute
|
||||
end
|
||||
|
||||
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_cookie: WSF_COOKIE
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_cookie_token and then
|
||||
attached {CMS_USER} current_user (req) as l_user
|
||||
then
|
||||
-- Logout Session
|
||||
create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_cookie_token.value)
|
||||
l_cookie.set_path ("/")
|
||||
l_cookie.set_max_age (-1)
|
||||
res.add_cookie (l_cookie)
|
||||
unset_current_user (req)
|
||||
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_status_code ({HTTP_CONSTANTS}.found)
|
||||
r.set_redirection (req.absolute_script_url (""))
|
||||
r.execute
|
||||
else
|
||||
fixme (generator + ": missing else implementation in handle_logout!")
|
||||
end
|
||||
end
|
||||
|
||||
handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_token: STRING
|
||||
l_cookie: WSF_COOKIE
|
||||
do
|
||||
if
|
||||
attached a_session_api as l_session_api and then
|
||||
attached {WSF_STRING} req.form_parameter ("username") as l_username and then
|
||||
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
|
||||
api.user_api.is_valid_credential (l_username.value, l_password.value) and then
|
||||
attached api.user_api.user_by_name (l_username.value) as l_user
|
||||
then
|
||||
l_token := generate_token
|
||||
if
|
||||
a_session_api.has_user_token (l_user)
|
||||
then
|
||||
l_session_api.update_user_session_auth (l_token, l_user)
|
||||
else
|
||||
l_session_api.new_user_session_auth (l_token, l_user)
|
||||
end
|
||||
create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_token)
|
||||
l_cookie.set_max_age ({CMS_SESSION_CONSTANTS}.session_max_age)
|
||||
l_cookie.set_path ("/")
|
||||
res.add_cookie (l_cookie)
|
||||
set_current_user (req, l_user)
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_redirection (req.absolute_script_url (""))
|
||||
r.execute
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
if attached template_block ("login", r) as l_tpl_block then
|
||||
if attached {WSF_STRING} req.form_parameter ("username") as l_username then
|
||||
l_tpl_block.set_value (l_username.value, "username")
|
||||
end
|
||||
l_tpl_block.set_value ("Wrong: Username or password ", "error")
|
||||
r.add_block (l_tpl_block, "content")
|
||||
end
|
||||
r.execute
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hooks configuration
|
||||
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
-- Module hooks configuration.
|
||||
do
|
||||
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
|
||||
|
||||
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
if
|
||||
attached a_response.user as u and then
|
||||
attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token)
|
||||
then
|
||||
a_value.force ("account/roc-session-logout", "auth_login_strategy")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
|
||||
-- Hook execution on collection of menu contained by `a_menu_system'
|
||||
-- for related response `a_response'.
|
||||
local
|
||||
lnk: CMS_LOCAL_LINK
|
||||
lnk2: detachable CMS_LINK
|
||||
do
|
||||
if
|
||||
attached a_response.user as u and then
|
||||
attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token)
|
||||
then
|
||||
across
|
||||
a_menu_system.primary_menu.items as ic
|
||||
until
|
||||
lnk2 /= Void
|
||||
loop
|
||||
if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then
|
||||
lnk2 := ic.item
|
||||
end
|
||||
end
|
||||
if lnk2 /= Void then
|
||||
a_menu_system.primary_menu.remove (lnk2)
|
||||
end
|
||||
create lnk.make ("Logout", "account/roc-session-logout" )
|
||||
a_menu_system.primary_menu.extend (lnk)
|
||||
else
|
||||
if a_response.location.starts_with ("account/") then
|
||||
create lnk.make ("Session", "account/roc-session-login")
|
||||
a_response.add_to_primary_tabs (lnk)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
block_list: ITERABLE [like {CMS_BLOCK}.name]
|
||||
local
|
||||
l_string: STRING
|
||||
do
|
||||
Result := <<"login">>
|
||||
debug ("roc")
|
||||
create l_string.make_empty
|
||||
across
|
||||
Result as ic
|
||||
loop
|
||||
l_string.append (ic.item)
|
||||
l_string.append_character (' ')
|
||||
end
|
||||
write_debug_log (generator + ".block_list:" + l_string )
|
||||
end
|
||||
end
|
||||
|
||||
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
||||
do
|
||||
if
|
||||
a_block_id.is_case_insensitive_equal_general ("login") and then
|
||||
a_response.location.starts_with ("account/roc-session-login")
|
||||
then
|
||||
get_block_view_login (a_block_id, a_response)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK
|
||||
-- Smarty content block for `a_block_id'
|
||||
local
|
||||
p: detachable PATH
|
||||
do
|
||||
create p.make_from_string ("templates")
|
||||
p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
|
||||
|
||||
p := a_response.api.module_theme_resource_location (Current, p)
|
||||
if p /= Void then
|
||||
if attached p.entry as e then
|
||||
create Result.make (a_block_id, Void, p.parent, e)
|
||||
else
|
||||
create Result.make (a_block_id, Void, p.parent, p)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Block views
|
||||
|
||||
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
||||
local
|
||||
vals: CMS_VALUE_TABLE
|
||||
do
|
||||
if attached template_block (a_block_id, a_response) as l_tpl_block then
|
||||
create vals.make (1)
|
||||
-- add the variable to the block
|
||||
value_table_alter (vals, a_response)
|
||||
across
|
||||
vals as ic
|
||||
loop
|
||||
l_tpl_block.set_value (ic.item, ic.key)
|
||||
end
|
||||
a_response.add_block (l_tpl_block, "content")
|
||||
else
|
||||
debug ("cms")
|
||||
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
generate_token: STRING
|
||||
-- Generate token to use in a Session.
|
||||
local
|
||||
l_token: STRING
|
||||
l_security: CMS_TOKEN_GENERATOR
|
||||
l_encode: URL_ENCODER
|
||||
do
|
||||
create l_security
|
||||
l_token := l_security.token
|
||||
create l_encode
|
||||
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
|
||||
-- Loop ensure that we have a security token that does not contain characters that need encoding.
|
||||
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
|
||||
-- but the user will need to use an unencoded token if activation has to be done manually.
|
||||
l_token := l_security.token
|
||||
end
|
||||
Result := l_token
|
||||
end
|
||||
end
|
||||
19
modules/session_auth/cms_session_constants.e
Normal file
19
modules/session_auth/cms_session_constants.e
Normal file
@@ -0,0 +1,19 @@
|
||||
note
|
||||
description: "Summary description for {CMS_SESSION_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_SESSION_CONSTANTS
|
||||
|
||||
|
||||
feature
|
||||
session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_"
|
||||
-- Name of Cookie used to keep the session info.
|
||||
-- TODO add a config file to be able to customize this value via coniguration file.
|
||||
|
||||
session_max_age: INTEGER = 86400
|
||||
-- Value of the Max-Age, before the cookie expires.
|
||||
-- TODO add a config file to be able to customize this value via coniguration file.
|
||||
|
||||
end
|
||||
55
modules/session_auth/filter/cms_session_auth_filter.e
Normal file
55
modules/session_auth/filter/cms_session_auth_filter.e
Normal file
@@ -0,0 +1,55 @@
|
||||
note
|
||||
description: "[
|
||||
Processes a HTTP request's checking Session cookies, putting the result into the execution variable user.
|
||||
]"
|
||||
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
|
||||
revision: "$Revision: 96616 $"
|
||||
|
||||
class
|
||||
CMS_SESSION_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
CMS_HANDLER
|
||||
rename
|
||||
make as make_handler
|
||||
end
|
||||
|
||||
WSF_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API; a_session_oauth_api: CMS_SESSION_API)
|
||||
do
|
||||
make_handler (a_api)
|
||||
session_oauth_api := a_session_oauth_api
|
||||
end
|
||||
|
||||
session_oauth_api: CMS_SESSION_API
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
do
|
||||
api.logger.put_debug (generator + ".execute ", Void)
|
||||
-- A valid user
|
||||
if
|
||||
attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_roc_auth_session_token
|
||||
then
|
||||
if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then
|
||||
set_current_user (req, l_user)
|
||||
else
|
||||
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
|
||||
end
|
||||
else
|
||||
api.logger.put_debug (generator + ".execute without authentication", Void)
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
note
|
||||
description: "[
|
||||
API to handle OAUTH storage
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_SESSION_AUTH_STORAGE_I
|
||||
|
||||
inherit
|
||||
SHARED_LOGGER
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: Users
|
||||
|
||||
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token', if any.
|
||||
deferred
|
||||
end
|
||||
|
||||
has_user_token (a_user: CMS_USER): BOOLEAN
|
||||
-- Has the user `a_user' and associated session token?
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
feature -- Change User session
|
||||
|
||||
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;)
|
||||
-- New user session for user `a_user' with token `a_token'.
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER )
|
||||
-- Update user session for user `a_user' with token `a_token'.
|
||||
deferred
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
note
|
||||
description: "Summary description for {CMS_SESSION_AUTH_STORAGE_NULL}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_SESSION_AUTH_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
|
||||
CMS_SESSION_AUTH_STORAGE_I
|
||||
|
||||
|
||||
feature -- Error handler
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token', if any.
|
||||
do
|
||||
end
|
||||
|
||||
has_user_token (a_user: CMS_USER): BOOLEAN
|
||||
-- Has the user `a_user' and associated session token?
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change User session
|
||||
|
||||
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;)
|
||||
-- New user session for user `a_user' with token `a_token'.
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER )
|
||||
-- Update user session for user `a_user' with token `a_token'.
|
||||
do
|
||||
end
|
||||
|
||||
end
|
||||
155
modules/session_auth/persistence/cms_session_auth_storage_sql.e
Normal file
155
modules/session_auth/persistence/cms_session_auth_storage_sql.e
Normal file
@@ -0,0 +1,155 @@
|
||||
note
|
||||
description: "Summary description for {CMS_SESSION_AUTH_STORAGE_SQL}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_SESSION_AUTH_STORAGE_SQL
|
||||
|
||||
inherit
|
||||
|
||||
CMS_SESSION_AUTH_STORAGE_I
|
||||
|
||||
CMS_PROXY_STORAGE_SQL
|
||||
|
||||
CMS_SESSION_AUTH_STORAGE_I
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access User
|
||||
|
||||
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token', if any.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user_by_session_token")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_query (Select_user_by_token, l_parameters)
|
||||
if not has_error and not sql_after then
|
||||
Result := fetch_user
|
||||
sql_forth
|
||||
if not sql_after then
|
||||
check
|
||||
no_more_than_one: False
|
||||
end
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
has_user_token (a_user: CMS_USER): BOOLEAN
|
||||
-- Has the user `a_user' and associated session token?
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".has_user_token")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
sql_query (Select_user_token, l_parameters)
|
||||
if not has_error and not sql_after then
|
||||
if sql_read_integer_64 (1) = 1 then
|
||||
Result := True
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature -- Change User token
|
||||
|
||||
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;)
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".new_user_session")
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_token, "token")
|
||||
l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date")
|
||||
sql_begin_transaction
|
||||
sql_insert (sql_insert_session_auth, l_parameters)
|
||||
sql_commit_transaction
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".update_user_session_auth")
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_token, "token")
|
||||
l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date")
|
||||
sql_begin_transaction
|
||||
sql_modify (sql_update_session_auth, l_parameters)
|
||||
sql_commit_transaction
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fetch_user: detachable CMS_USER
|
||||
local
|
||||
l_id: INTEGER_64
|
||||
l_name: detachable READABLE_STRING_32
|
||||
do
|
||||
if attached sql_read_integer_64 (1) as i then
|
||||
l_id := i
|
||||
end
|
||||
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
|
||||
l_name := s
|
||||
end
|
||||
if l_name /= Void then
|
||||
create Result.make (l_name)
|
||||
if l_id > 0 then
|
||||
Result.set_id (l_id)
|
||||
end
|
||||
elseif l_id > 0 then
|
||||
create Result.make_with_id (l_id)
|
||||
end
|
||||
if Result /= Void then
|
||||
if attached sql_read_string (3) as l_password then
|
||||
-- FIXME: should we return the password here ???
|
||||
Result.set_hashed_password (l_password)
|
||||
end
|
||||
if attached sql_read_string (5) as l_email then
|
||||
Result.set_email (l_email)
|
||||
end
|
||||
if attached sql_read_integer_32 (6) as l_status then
|
||||
Result.set_status (l_status)
|
||||
end
|
||||
else
|
||||
check
|
||||
expected_valid_user: False
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- SQL statements
|
||||
|
||||
Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;"
|
||||
--| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped.
|
||||
|
||||
Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);"
|
||||
|
||||
Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;"
|
||||
|
||||
Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;"
|
||||
|
||||
end
|
||||
153
modules/session_auth/site/cms_token_generator.e
Normal file
153
modules/session_auth/site/cms_token_generator.e
Normal file
@@ -0,0 +1,153 @@
|
||||
note
|
||||
description: "Provides security routine helpers"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_TOKEN_GENERATOR
|
||||
|
||||
inherit
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Access
|
||||
|
||||
token: STRING
|
||||
-- Cryptographic random base 64 string.
|
||||
do
|
||||
Result := salt_with_size (16)
|
||||
-- Remove trailing equal sign
|
||||
Result.keep_head (Result.count - 2)
|
||||
end
|
||||
|
||||
salt: STRING
|
||||
-- Cryptographic random number of 16 bytes.
|
||||
do
|
||||
Result := salt_with_size (16)
|
||||
end
|
||||
|
||||
password: STRING
|
||||
-- Cryptographic random password of 10 bytes.
|
||||
do
|
||||
Result := salt_with_size (10)
|
||||
-- Remove trailing equal signs
|
||||
Result.keep_head (Result.count - 2)
|
||||
end
|
||||
|
||||
password_hash (a_password, a_salt: STRING): STRING
|
||||
-- Password hash based on password `a_password' and salt value `a_salt'.
|
||||
do
|
||||
Result := sha1_string (a_password + a_salt )
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
salt_with_size (a_val: INTEGER): STRING
|
||||
-- Return a salt with size `a_val'.
|
||||
local
|
||||
l_salt: SALT_XOR_SHIFT_64_GENERATOR
|
||||
l_array: ARRAY [INTEGER_8]
|
||||
i: INTEGER
|
||||
do
|
||||
create l_salt.make (a_val)
|
||||
create l_array.make_empty
|
||||
i := 1
|
||||
across
|
||||
l_salt.new_sequence as c
|
||||
loop
|
||||
l_array.force (c.item.as_integer_8, i)
|
||||
i := i + 1
|
||||
end
|
||||
Result := base_64 (l_array)
|
||||
end
|
||||
|
||||
sha1_string (a_str: STRING): STRING
|
||||
-- SHA1 diggest of `a_str'.
|
||||
do
|
||||
sha1.update_from_string (a_str)
|
||||
Result := sha1.digest_as_string
|
||||
sha1.reset
|
||||
end
|
||||
|
||||
sha1: SHA1
|
||||
-- Create a SHA1 object.
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
feature -- Encoding
|
||||
|
||||
|
||||
base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8
|
||||
-- Encodes a byte array into a STRING doing base64 encoding.
|
||||
local
|
||||
l_output: SPECIAL [INTEGER_8]
|
||||
l_remaining: INTEGER
|
||||
i, ptr: INTEGER
|
||||
char: CHARACTER
|
||||
do
|
||||
to_implement ("Check existing code to do that!!!.")
|
||||
create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4)
|
||||
l_remaining := bytes.count
|
||||
from
|
||||
i := 0
|
||||
ptr := 0
|
||||
until
|
||||
l_remaining <= 3
|
||||
loop
|
||||
l_output [ptr] := encode_value (bytes [i] |>> 2)
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF))
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3))
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value (bytes [i + 2] & 0x3F)
|
||||
ptr := ptr + 1
|
||||
l_remaining := l_remaining - 3
|
||||
i := i + 3
|
||||
end
|
||||
-- encode when exactly 1 element (left) to encode
|
||||
char := '='
|
||||
if l_remaining = 1 then
|
||||
l_output [ptr] := encode_value (bytes [i] |>> 2)
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4)
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := char.code.as_integer_8
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := char.code.as_integer_8
|
||||
ptr := ptr + 1
|
||||
end
|
||||
|
||||
-- encode when exactly 2 elements (left) to encode
|
||||
if l_remaining = 2 then
|
||||
l_output [ptr] := encode_value (bytes [i] |>> 2)
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF));
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2);
|
||||
ptr := ptr + 1
|
||||
l_output [ptr] := char.code.as_integer_8
|
||||
ptr := ptr + 1
|
||||
end
|
||||
create Result.make_empty
|
||||
across
|
||||
l_output as elem
|
||||
loop
|
||||
Result.append_character (elem.item.to_character_8)
|
||||
end
|
||||
end
|
||||
|
||||
base64_map: SPECIAL [CHARACTER_8]
|
||||
-- Table for Base64 encoding.
|
||||
once
|
||||
Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area
|
||||
end
|
||||
|
||||
encode_value (i: INTEGER_8): INTEGER_8
|
||||
-- Encode `i'.
|
||||
do
|
||||
Result := base64_map [i & 0x3F].code.as_integer_8
|
||||
end
|
||||
|
||||
end
|
||||
11
modules/session_auth/site/scripts/session_auth_table.sql.tpl
Normal file
11
modules/session_auth/site/scripts/session_auth_table.sql.tpl
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
CREATE TABLE session_auth (
|
||||
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
|
||||
`access_token` TEXT NOT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
CONSTRAINT `uid`
|
||||
UNIQUE(`uid`),
|
||||
CONSTRAINT `access_token`
|
||||
UNIQUE(`access_token`)
|
||||
);
|
||||
|
||||
37
modules/session_auth/site/templates/block_login.tpl
Normal file
37
modules/session_auth/site/templates/block_login.tpl
Normal file
@@ -0,0 +1,37 @@
|
||||
<div class="primary-tabs">
|
||||
{unless isset="$user"}
|
||||
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
|
||||
<div>
|
||||
<div>
|
||||
<form name="cms_session_auth" action="{$site_url/}account/login-with-session" method="POST">
|
||||
<div>
|
||||
<input type="text" name="username" id="username" required value="{$username/}">
|
||||
<label>Username</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="password" name="password" id="password" required >
|
||||
<label>Password</label>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<a href="{$site_url/}account/new-password">Forgot password?</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/unless}
|
||||
{if isset=$error}
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<strong>{$error/}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
571
modules/taxonomy/cms_taxonomy_api.e
Normal file
571
modules/taxonomy/cms_taxonomy_api.e
Normal file
@@ -0,0 +1,571 @@
|
||||
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_64): 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
|
||||
|
||||
types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32]
|
||||
-- Type names associated with `a_vocab'.
|
||||
do
|
||||
Result := taxonomy_storage.types_associated_with_vocabulary (a_vocab)
|
||||
end
|
||||
|
||||
vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION
|
||||
-- Vocabularies including `a_term'.
|
||||
do
|
||||
Result := taxonomy_storage.vocabularies_for_term (a_term)
|
||||
end
|
||||
|
||||
is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN
|
||||
-- Is `a_term' inside `a_vocab' ?
|
||||
require
|
||||
valid_term: a_term.has_id
|
||||
valid_vocabulary: a_vocab.has_id
|
||||
do
|
||||
Result := taxonomy_storage.is_term_inside_vocabulary (a_term, a_vocab)
|
||||
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_content (a_content: CMS_CONTENT; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION
|
||||
-- Terms related to `a_content', and if `a_vocabulary' is set
|
||||
-- constrain to be part of `a_vocabulary'.
|
||||
require
|
||||
content_with_identifier: a_content.has_identifier
|
||||
do
|
||||
if attached a_content.identifier as l_id then
|
||||
Result := terms_of_entity (a_content.content_type, l_id, a_vocabulary)
|
||||
end
|
||||
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)
|
||||
-- Insert or update vocabulary `a_voc'
|
||||
-- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True.
|
||||
do
|
||||
reset_error
|
||||
taxonomy_storage.save_vocabulary (a_voc, False)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
|
||||
save_vocabulary_and_terms (a_voc: CMS_VOCABULARY)
|
||||
-- Insert or update vocabulary `a_voc'
|
||||
-- and also save {CMS_VOCABULARY}.terms.
|
||||
do
|
||||
reset_error
|
||||
taxonomy_storage.save_vocabulary (a_voc, True)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
|
||||
save_term (a_term: CMS_TERM; voc: detachable CMS_VOCABULARY)
|
||||
-- Save `a_term' inside `voc' if set.
|
||||
do
|
||||
reset_error
|
||||
taxonomy_storage.save_term (a_term, voc)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
|
||||
remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY)
|
||||
-- Remove term `t' from vocabulary `voc'.
|
||||
do
|
||||
reset_error
|
||||
taxonomy_storage.remove_term_from_vocabulary (t, voc)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
|
||||
associate_term_with_content (a_term: CMS_TERM; a_content: CMS_CONTENT)
|
||||
-- Associate term `a_term' with `a_content'.
|
||||
require
|
||||
content_with_identifier: a_content.has_identifier
|
||||
do
|
||||
reset_error
|
||||
if attached a_content.identifier as l_id then
|
||||
taxonomy_storage.associate_term_with_entity (a_term, a_content.content_type, l_id)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
end
|
||||
|
||||
unassociate_term_from_content (a_term: CMS_TERM; a_content: CMS_CONTENT)
|
||||
-- Unassociate term `a_term' from `a_content'.
|
||||
require
|
||||
content_with_identifier: a_content.has_identifier
|
||||
do
|
||||
reset_error
|
||||
if attached a_content.identifier as l_id then
|
||||
taxonomy_storage.unassociate_term_from_entity (a_term, a_content.content_type, l_id)
|
||||
error_handler.append (taxonomy_storage.error_handler)
|
||||
end
|
||||
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 -- Web forms
|
||||
|
||||
populate_edit_form (a_response: CMS_RESPONSE; a_form: CMS_FORM; a_content_type_name: READABLE_STRING_8; a_content: detachable CMS_CONTENT)
|
||||
local
|
||||
ti: detachable WSF_FORM_TEXT_INPUT
|
||||
th: WSF_FORM_HIDDEN_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 vocabularies_for_type (a_content_type_name) as l_vocs and then not l_vocs.is_empty
|
||||
then
|
||||
l_has_edit_permission := a_response.has_permissions (<<"update any taxonomy", "update " + a_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
|
||||
create th.make_with_text ({STRING_32} "taxonomy_vocabularies[" + voc.id.out + "]", voc.name)
|
||||
w_set.extend (th)
|
||||
|
||||
l_terms := Void
|
||||
if a_content /= Void then
|
||||
l_terms := terms_of_content (a_content, 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 (cms_api.translation (voc.name, Void))
|
||||
|
||||
create ti.make ({STRING_32} "taxonomy_" + voc.id.out)
|
||||
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 (cms_api.html_encoded (cms_api.translation (l_desc, Void)))
|
||||
else
|
||||
ti.set_description (a_response.html_encoded (cms_api.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
|
||||
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 (cms_api.html_encoded (l_desc))
|
||||
else
|
||||
w_voc_set.set_legend (cms_api.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_" + voc.id.out + "[]", t.text)
|
||||
w_cb.set_title (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_" + voc.id.out)
|
||||
w_voc_set.extend (w_select)
|
||||
|
||||
if attached voc.description as l_desc then
|
||||
w_select.set_description (cms_api.html_encoded (l_desc))
|
||||
else
|
||||
w_select.set_description (cms_api.html_encoded (voc.name))
|
||||
end
|
||||
w_voc_set.set_legend (cms_api.html_encoded (voc.name))
|
||||
|
||||
across
|
||||
voc as voc_terms_ic
|
||||
loop
|
||||
t := voc_terms_ic.item
|
||||
create w_opt.make (cms_api.html_encoded (t.text), cms_api.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
|
||||
|
||||
a_form.submit_actions.extend (agent taxonomy_submit_action (a_response, Current, l_vocs, a_content, ?))
|
||||
|
||||
if
|
||||
attached a_form.fields_by_name ("title") as l_title_fields and then
|
||||
attached l_title_fields.first as l_title_field
|
||||
then
|
||||
a_form.insert_after (w_set, l_title_field)
|
||||
else
|
||||
a_form.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_content: detachable CMS_CONTENT 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
|
||||
vid: INTEGER_64
|
||||
do
|
||||
if
|
||||
a_content /= Void and then a_content.has_identifier and then
|
||||
attached fd.table_item ("taxonomy_vocabularies") as fd_vocs
|
||||
then
|
||||
if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + a_content.content_type + " taxonomy">>) then
|
||||
across
|
||||
fd_vocs.values as ic
|
||||
loop
|
||||
vid := ic.key.to_integer_64
|
||||
l_voc_name := ic.item.string_representation
|
||||
|
||||
if attached a_vocs.item_by_id (vid) as voc then
|
||||
if attached fd.string_item ("taxonomy_" + vid.out) as l_string then
|
||||
l_new_terms := a_taxonomy_api.splitted_string (l_string, ',')
|
||||
elseif attached fd.table_item ("taxonomy_" + vid.out) as fd_terms then
|
||||
create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (fd_terms.count)
|
||||
across
|
||||
fd_terms as t_ic
|
||||
loop
|
||||
l_new_terms.force (t_ic.item.string_representation)
|
||||
end
|
||||
else
|
||||
create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (0)
|
||||
end
|
||||
|
||||
create l_terms_to_remove.make (0)
|
||||
if attached a_taxonomy_api.terms_of_content (a_content, 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_content (t_ic.item, a_content)
|
||||
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_content (t, a_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_taxonomy_to_xhtml (a_content: CMS_CONTENT; a_response: CMS_RESPONSE; a_output: STRING)
|
||||
-- Append taxonomy related to `a_content' to xhtml string `a_output',
|
||||
-- using `a_response' helper routines.
|
||||
do
|
||||
if
|
||||
attached vocabularies_for_type (a_content.content_type) as vocs and then not vocs.is_empty
|
||||
then
|
||||
vocs.sort
|
||||
across
|
||||
vocs as ic
|
||||
loop
|
||||
if
|
||||
attached terms_of_content (a_content, 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 (cms_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
|
||||
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
|
||||
19
modules/taxonomy/cms_taxonomy_hook.e
Normal file
19
modules/taxonomy/cms_taxonomy_hook.e
Normal 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
|
||||
192
modules/taxonomy/cms_taxonomy_module.e
Normal file
192
modules/taxonomy/cms_taxonomy_module.e
Normal file
@@ -0,0 +1,192 @@
|
||||
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)
|
||||
configure_web_amin (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
|
||||
l_voc_handler: TAXONOMY_VOCABULARY_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)
|
||||
|
||||
create l_voc_handler.make (a_api, a_taxonomy_api)
|
||||
a_router.handle ("/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get)
|
||||
a_router.handle ("/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get)
|
||||
end
|
||||
|
||||
configure_web_amin (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_TERM_ADMIN_HANDLER
|
||||
l_voc_handler: TAXONOMY_VOCABULARY_ADMIN_HANDLER
|
||||
do
|
||||
a_router.handle ("/admin/taxonomy/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_admin_taxonomy (?, ?, a_api)), a_router.methods_get)
|
||||
|
||||
create l_taxonomy_handler.make (a_api, a_taxonomy_api)
|
||||
a_router.handle ("/admin/taxonomy/term/", l_taxonomy_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/admin/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get_post)
|
||||
|
||||
create l_voc_handler.make (a_api, a_taxonomy_api)
|
||||
a_router.handle ("/admin/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/admin/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get_post)
|
||||
end
|
||||
|
||||
feature -- Handler
|
||||
|
||||
handle_admin_taxonomy (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
create lnk.make ("Admin Vocabularies", "admin/taxonomy/vocabulary/")
|
||||
l_page.add_to_primary_tabs (lnk)
|
||||
|
||||
create lnk.make ("Create terms", "admin/taxonomy/term/")
|
||||
l_page.add_to_primary_tabs (lnk)
|
||||
|
||||
l_page.execute
|
||||
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)
|
||||
local
|
||||
lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
-- Add the link to the taxonomy to the main menu
|
||||
if a_response.has_permission ("admin taxonomy") then
|
||||
create lnk.make ("Taxonomy", "admin/taxonomy/")
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
110
modules/taxonomy/cms_term.e
Normal file
110
modules/taxonomy/cms_term.e
Normal 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
|
||||
|
||||
108
modules/taxonomy/cms_term_collection.e
Normal file
108
modules/taxonomy/cms_term_collection.e
Normal file
@@ -0,0 +1,108 @@
|
||||
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)
|
||||
items.compare_objects
|
||||
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
|
||||
|
||||
term_by_id (tid: INTEGER_64): detachable CMS_TERM
|
||||
-- Term of id `tid' contained in Current, if any.
|
||||
do
|
||||
across
|
||||
items as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
Result := ic.item
|
||||
if Result.id /= tid then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
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
|
||||
128
modules/taxonomy/cms_vocabulary.e
Normal file
128
modules/taxonomy/cms_vocabulary.e
Normal 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
|
||||
|
||||
114
modules/taxonomy/cms_vocabulary_collection.e
Normal file
114
modules/taxonomy/cms_vocabulary_collection.e
Normal file
@@ -0,0 +1,114 @@
|
||||
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)
|
||||
items.compare_objects
|
||||
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
|
||||
|
||||
item_by_id (a_id: INTEGER_64): detachable CMS_VOCABULARY
|
||||
-- Vocabulary of id `a_id' contained in Current, if any.
|
||||
do
|
||||
across
|
||||
items as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
Result := ic.item
|
||||
if Result.id /= a_id then
|
||||
Result := Void
|
||||
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
|
||||
44
modules/taxonomy/handler/cms_taxonomy_entity.e
Normal file
44
modules/taxonomy/handler/cms_taxonomy_entity.e
Normal 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
|
||||
89
modules/taxonomy/handler/cms_taxonomy_entity_container.e
Normal file
89
modules/taxonomy/handler/cms_taxonomy_entity_container.e
Normal 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
|
||||
155
modules/taxonomy/handler/taxonomy_handler.e
Normal file
155
modules/taxonomy/handler/taxonomy_handler.e
Normal 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
|
||||
269
modules/taxonomy/handler/taxonomy_term_admin_handler.e
Normal file
269
modules/taxonomy/handler/taxonomy_term_admin_handler.e
Normal file
@@ -0,0 +1,269 @@
|
||||
note
|
||||
description: "[
|
||||
Request handler related to
|
||||
/admin/taxonomy/term/{termid}
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$revision$"
|
||||
|
||||
class
|
||||
TAXONOMY_TERM_ADMIN_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,
|
||||
do_post
|
||||
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_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
tid: INTEGER_64
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
t: detachable CMS_TERM
|
||||
l_parents: detachable CMS_VOCABULARY_COLLECTION
|
||||
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
|
||||
if tid > 0 then
|
||||
t := taxonomy_api.term_by_id (tid)
|
||||
end
|
||||
end
|
||||
|
||||
-- Responding with `main_content_html (l_page)'.
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
if l_page.has_permission ("admin taxonomy") then
|
||||
|
||||
if t = Void then
|
||||
l_page.set_title ("New term ...")
|
||||
create t.make ("")
|
||||
else
|
||||
l_page.set_title (t.text)
|
||||
end
|
||||
create s.make_empty
|
||||
f := edit_form (t, l_page, req)
|
||||
f.process (l_page)
|
||||
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 ("Save changes") then
|
||||
if attached fd.string_item ("text") as l_text then
|
||||
t.set_text (l_text)
|
||||
l_page.set_title (t.text)
|
||||
end
|
||||
if attached fd.string_item ("description") as l_description then
|
||||
t.set_description (l_description)
|
||||
end
|
||||
if attached fd.string_item ("weight") as l_weight and then l_weight.is_integer then
|
||||
t.set_weight (l_weight.to_integer)
|
||||
end
|
||||
taxonomy_api.save_term (t, Void)
|
||||
if taxonomy_api.has_error then
|
||||
fd.report_error ("Term creation failed!")
|
||||
else
|
||||
l_page.add_success_message ("Term creation succeed.")
|
||||
s.append ("<div>View term: ")
|
||||
s.append (l_page.link (t.text, "admin/taxonomy/term/" + t.id.out, Void))
|
||||
s.append ("</div>")
|
||||
|
||||
if
|
||||
attached fd.table_item ("vocabularies") as voc_tb and then
|
||||
attached taxonomy_api.vocabularies (0, 0) as l_vocabularies
|
||||
then
|
||||
l_parents := taxonomy_api.vocabularies_for_term (t)
|
||||
across
|
||||
voc_tb as vid_ic
|
||||
until
|
||||
taxonomy_api.has_error
|
||||
loop
|
||||
if attached l_vocabularies.item_by_id (vid_ic.item.string_representation.to_integer_64) as v then
|
||||
if l_parents /= Void and then attached l_parents.item_by_id (v.id) as l_v then
|
||||
-- Already as parent!
|
||||
l_parents.remove (l_v)
|
||||
else
|
||||
taxonomy_api.save_term (t, v)
|
||||
l_vocabularies.remove (v)
|
||||
end
|
||||
end
|
||||
end
|
||||
if l_parents /= Void then
|
||||
across
|
||||
l_parents as v_ic
|
||||
until
|
||||
taxonomy_api.has_error
|
||||
loop
|
||||
taxonomy_api.remove_term_from_vocabulary (t, v_ic.item)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- l_page.set_redirection (l_page.location)
|
||||
end
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
end
|
||||
f.append_to_html (l_page.wsf_theme, s)
|
||||
l_page.set_main_content (s)
|
||||
l_page.execute
|
||||
else
|
||||
send_access_denied (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
tid: INTEGER_64
|
||||
s: STRING
|
||||
f: CMS_FORM
|
||||
t: detachable CMS_TERM
|
||||
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
|
||||
if tid > 0 then
|
||||
t := taxonomy_api.term_by_id (tid)
|
||||
end
|
||||
end
|
||||
-- Responding with `main_content_html (l_page)'.
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
if l_page.has_permission ("admin taxonomy") then
|
||||
if t = Void then
|
||||
l_page.set_title ("Create term ...")
|
||||
create t.make ("")
|
||||
else
|
||||
l_page.set_title (t.text)
|
||||
end
|
||||
create s.make_empty
|
||||
f := edit_form (t, l_page, req)
|
||||
f.append_to_html (l_page.wsf_theme, s)
|
||||
l_page.set_main_content (s)
|
||||
l_page.execute
|
||||
else
|
||||
send_access_denied (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
edit_form (t: CMS_TERM; a_page: CMS_RESPONSE; req: WSF_REQUEST): CMS_FORM
|
||||
local
|
||||
f: CMS_FORM
|
||||
voc: detachable CMS_VOCABULARY
|
||||
w_tf: WSF_FORM_TEXT_INPUT
|
||||
w_txt: WSF_FORM_TEXTAREA
|
||||
w_set: WSF_FORM_FIELD_SET
|
||||
w_cb: WSF_FORM_CHECKBOX_INPUT
|
||||
l_parents: detachable CMS_VOCABULARY_COLLECTION
|
||||
do
|
||||
create f.make (req.percent_encoded_path_info, "taxonomy")
|
||||
if t.has_id then
|
||||
f.extend_html_text (a_page.link ("View associated entities", "taxonomy/term/" + t.id.out, Void))
|
||||
end
|
||||
create w_tf.make_with_text ("text", t.text)
|
||||
w_tf.set_is_required (True)
|
||||
w_tf.set_label ("Text")
|
||||
f.extend (w_tf)
|
||||
|
||||
create w_txt.make ("description")
|
||||
w_txt.set_label ("Description")
|
||||
w_txt.set_rows (3)
|
||||
w_txt.set_cols (60)
|
||||
if attached t.description as l_desc then
|
||||
w_txt.set_text_value (api.html_encoded (l_desc))
|
||||
end
|
||||
w_txt.set_description ("Description of the terms; can be used by modules or administration.")
|
||||
f.extend (w_txt)
|
||||
|
||||
create w_tf.make_with_text ("weight", t.weight.out)
|
||||
w_tf.set_label ("Weight")
|
||||
w_tf.set_description ("Terms are sorted in ascending order by weight.")
|
||||
f.extend (w_tf)
|
||||
|
||||
if attached taxonomy_api.vocabularies (0, 0) as vocs then
|
||||
if t.has_id then
|
||||
l_parents := taxonomy_api.vocabularies_for_term (t)
|
||||
end
|
||||
create w_set.make
|
||||
w_set.set_legend ("Associated vocabularies")
|
||||
across
|
||||
vocs as ic
|
||||
loop
|
||||
voc := ic.item
|
||||
create w_cb.make_with_value ("vocabularies[]", ic.item.id.out)
|
||||
w_cb.set_title (voc.name)
|
||||
if
|
||||
l_parents /= Void and then
|
||||
across l_parents as p_ic some p_ic.item.id = ic.item.id end
|
||||
then
|
||||
w_cb.set_checked (True)
|
||||
end
|
||||
w_set.extend (w_cb)
|
||||
end
|
||||
if w_set.count > 0 then
|
||||
f.extend (w_set)
|
||||
end
|
||||
end
|
||||
|
||||
f.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("op", "Save changes"))
|
||||
Result := f
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
414
modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e
Normal file
414
modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e
Normal file
@@ -0,0 +1,414 @@
|
||||
note
|
||||
description: "[
|
||||
Request handler related to
|
||||
/admin/taxonomy/vocabulary/
|
||||
/admin/taxonomy/vocabulary/{vocid}
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$revision$"
|
||||
|
||||
class
|
||||
TAXONOMY_VOCABULARY_ADMIN_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,
|
||||
do_post
|
||||
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_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
voc: CMS_VOCABULARY
|
||||
l_typename: READABLE_STRING_GENERAL
|
||||
s: STRING
|
||||
do
|
||||
if not api.user_has_permission (current_user (req), "admin taxonomy") then
|
||||
send_access_denied (req, res)
|
||||
else
|
||||
if attached {WSF_STRING} req.form_parameter ("op") as p_op then
|
||||
if p_op.same_string ("New Vocabulary") then
|
||||
if
|
||||
attached {WSF_STRING} req.form_parameter ("vocabulary_name") as p_name and then
|
||||
not p_name.is_empty
|
||||
then
|
||||
create voc.make (p_name.value)
|
||||
taxonomy_api.save_vocabulary (voc)
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
if taxonomy_api.has_error then
|
||||
l_page.add_error_message ("Vocabulary creation failed!")
|
||||
else
|
||||
l_page.add_success_message ("Vocabulary creation succeed!")
|
||||
l_page.set_redirection ("admin/taxonomy/vocabulary/" + voc.id.out)
|
||||
end
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
end
|
||||
elseif
|
||||
p_op.same_string ("Save changes") and then
|
||||
attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid and then p_vocid.is_integer and then
|
||||
attached taxonomy_api.vocabulary (p_vocid.value.to_integer_64) as l_vocabulary
|
||||
then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
create s.make_empty
|
||||
l_page.add_notice_message ("Vocabulary " + l_vocabulary.id.out)
|
||||
if attached {WSF_STRING} req.form_parameter ("name") as p_name then
|
||||
l_vocabulary.set_name (p_name.value)
|
||||
end
|
||||
if attached {WSF_STRING} req.form_parameter ("description") as p_desc then
|
||||
l_vocabulary.set_description (p_desc.value)
|
||||
end
|
||||
if attached {WSF_STRING} req.form_parameter ("weight") as p_weight and then p_weight.is_integer then
|
||||
l_vocabulary.set_weight (p_weight.integer_value)
|
||||
end
|
||||
taxonomy_api.save_vocabulary (l_vocabulary)
|
||||
if taxonomy_api.has_error then
|
||||
l_page.add_error_message ("Could not save vocabulary")
|
||||
elseif
|
||||
attached {WSF_TABLE} req.form_parameter ("typenames") as typenames_table
|
||||
then
|
||||
across
|
||||
typenames_table as ic
|
||||
loop
|
||||
l_typename := ic.item.string_representation
|
||||
create voc.make_from_term (l_vocabulary)
|
||||
voc.set_associated_content_type (l_typename, False, False, False)
|
||||
l_page.add_notice_message ("Content type :" + api.html_encoded (l_typename))
|
||||
if attached {WSF_TABLE} req.form_parameter ({STRING_32} "vocabulary_" + l_typename.as_string_32) as opts then
|
||||
across
|
||||
opts as o_ic
|
||||
loop
|
||||
if o_ic.item.same_string ("tags") then
|
||||
voc.set_is_tags (True)
|
||||
elseif o_ic.item.same_string ("multiple") then
|
||||
voc.allow_multiple_term (True)
|
||||
elseif o_ic.item.same_string ("required") then
|
||||
voc.set_is_term_required (True)
|
||||
end
|
||||
end
|
||||
end
|
||||
taxonomy_api.associate_vocabulary_with_type (voc, l_typename)
|
||||
if taxonomy_api.has_error then
|
||||
l_page.add_error_message ("Could not save vocabulary content type associations.")
|
||||
end
|
||||
end
|
||||
end
|
||||
if not taxonomy_api.has_error then
|
||||
l_page.add_notice_message (l_page.link ({STRING_32} "Back to vocabulary %"" + l_vocabulary.name + "%"", "admin/taxonomy/vocabulary/" + l_vocabulary.id.out, Void))
|
||||
end
|
||||
l_page.set_main_content (s)
|
||||
else
|
||||
create {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
end
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
end
|
||||
l_page.execute
|
||||
end
|
||||
end
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
tid: INTEGER_64
|
||||
do
|
||||
if not api.user_has_permission (current_user (req), "admin taxonomy") then
|
||||
send_access_denied (req, res)
|
||||
else
|
||||
if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then
|
||||
if p_vocid.is_integer then
|
||||
tid := p_vocid.value.to_integer_64
|
||||
end
|
||||
end
|
||||
if tid > 0 then
|
||||
do_get_vocabulary (tid, req, res)
|
||||
else
|
||||
do_get_vocabularies (req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do_get_vocabulary (tid: INTEGER_64; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
require
|
||||
valid_tid: tid > 0
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
s: STRING
|
||||
l_typename: detachable READABLE_STRING_8
|
||||
v: detachable CMS_VOCABULARY
|
||||
l_typenames: detachable LIST [READABLE_STRING_32]
|
||||
f: CMS_FORM
|
||||
wtb: WSF_WIDGET_TABLE
|
||||
wtb_row: WSF_WIDGET_TABLE_ROW
|
||||
wtb_item: WSF_WIDGET_TABLE_ITEM
|
||||
voc: detachable CMS_VOCABULARY
|
||||
l_term: detachable CMS_TERM
|
||||
tf_input: WSF_FORM_TEXT_INPUT
|
||||
tf_text: WSF_FORM_TEXTAREA
|
||||
tf_num: WSF_FORM_NUMBER_INPUT
|
||||
w_set: WSF_FORM_FIELD_SET
|
||||
w_cb: WSF_FORM_CHECKBOX_INPUT
|
||||
sub: WSF_FORM_SUBMIT_INPUT
|
||||
do
|
||||
voc := taxonomy_api.vocabulary (tid)
|
||||
if voc /= Void then
|
||||
-- Responding with `main_content_html (l_page)'.
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
l_page.set_title (voc.name)
|
||||
taxonomy_api.fill_vocabularies_with_terms (voc)
|
||||
|
||||
create f.make (req.percent_encoded_path_info, "taxonomy")
|
||||
|
||||
create tf_input.make_with_text ("name", voc.name)
|
||||
f.extend (tf_input)
|
||||
|
||||
create tf_text.make ("description")
|
||||
tf_text.set_text_value (voc.description)
|
||||
tf_text.set_description ("Description of the vocabulary; also used as intructions to present to the user when selecting terms.")
|
||||
tf_text.set_rows (3)
|
||||
f.extend (tf_text)
|
||||
|
||||
create tf_num.make_with_text ("weight", voc.weight.out)
|
||||
tf_num.set_label ("weight")
|
||||
tf_num.set_description ("Items are displayed in ascending order by weight.")
|
||||
f.extend (tf_num)
|
||||
|
||||
create wtb.make
|
||||
wtb.add_css_class ("with_border")
|
||||
create wtb_row.make (2)
|
||||
create wtb_item.make_with_text ("Text")
|
||||
wtb_row.set_item (wtb_item, 1)
|
||||
create wtb_item.make_with_text ("Description")
|
||||
wtb_row.set_item (wtb_item, 2)
|
||||
wtb.add_head_row (wtb_row)
|
||||
across
|
||||
voc as ic
|
||||
loop
|
||||
l_term := ic.item
|
||||
|
||||
create wtb_row.make (3)
|
||||
wtb.add_row (wtb_row)
|
||||
|
||||
create wtb_item.make_with_text (l_page.link (ic.item.text, "admin/taxonomy/term/" + l_term.id.out, Void))
|
||||
wtb_row.set_item (wtb_item, 1)
|
||||
if attached ic.item.description as l_desc then
|
||||
create wtb_item.make_with_text (api.html_encoded (l_desc))
|
||||
else
|
||||
create wtb_item.make_with_text ("")
|
||||
end
|
||||
wtb_row.set_item (wtb_item, 2)
|
||||
end
|
||||
if wtb.body_row_count > 0 then
|
||||
f.extend (wtb)
|
||||
else
|
||||
f.extend_raw_text ("No terms.")
|
||||
end
|
||||
|
||||
create w_set.make
|
||||
w_set.set_legend ("Content types")
|
||||
f.extend (w_set)
|
||||
|
||||
|
||||
l_typenames := taxonomy_api.types_associated_with_vocabulary (voc)
|
||||
create wtb.make
|
||||
wtb.add_css_class ("with_border")
|
||||
create wtb_row.make (5)
|
||||
wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_text ("Type"), 1)
|
||||
create wtb_item.make_with_text ("Settings ...")
|
||||
wtb_item.add_html_attribute ("colspan", "3")
|
||||
wtb_row.set_item (wtb_item, 2)
|
||||
wtb.add_head_row (wtb_row)
|
||||
|
||||
across
|
||||
api.content_types as ic
|
||||
loop
|
||||
create wtb_row.make (4)
|
||||
wtb.add_row (wtb_row)
|
||||
|
||||
l_typename := ic.item.name
|
||||
create w_cb.make_with_value ("typenames[]", api.html_encoded (l_typename))
|
||||
w_cb.set_title (ic.item.name)
|
||||
wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 1)
|
||||
|
||||
v := Void
|
||||
if
|
||||
l_typenames /= Void and then
|
||||
across l_typenames as tn_ic some l_typename.is_case_insensitive_equal (tn_ic.item) end
|
||||
then
|
||||
w_cb.set_checked (True)
|
||||
if attached taxonomy_api.vocabularies_for_type (l_typename) as v_list then
|
||||
across v_list as v_ic until v /= Void loop
|
||||
if v_ic.item.id = voc.id then
|
||||
v := v_ic.item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "tags")
|
||||
w_cb.set_title ("Tags")
|
||||
w_cb.set_checked (v /= Void and then v.is_tags)
|
||||
wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 2)
|
||||
|
||||
create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "multiple")
|
||||
w_cb.set_title ("Multiple Select")
|
||||
w_cb.set_checked (v /= Void and then v.multiple_terms_allowed)
|
||||
wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 3)
|
||||
|
||||
create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "required")
|
||||
w_cb.set_title ("Required")
|
||||
w_cb.set_checked (v /= Void and then v.is_term_required)
|
||||
wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 4)
|
||||
end
|
||||
if wtb.body_row_count > 0 then
|
||||
w_set.extend (wtb)
|
||||
end
|
||||
|
||||
create sub.make_with_text ("op", "Save changes")
|
||||
f.extend (sub)
|
||||
|
||||
create s.make_empty
|
||||
f.append_to_html (l_page.wsf_theme, s)
|
||||
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
|
||||
end
|
||||
|
||||
do_get_vocabularies (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_page: CMS_RESPONSE
|
||||
s: STRING
|
||||
l_typenames: detachable LIST [READABLE_STRING_32]
|
||||
f: CMS_FORM
|
||||
wtb: WSF_WIDGET_TABLE
|
||||
wtb_row: WSF_WIDGET_TABLE_ROW
|
||||
wtb_item: WSF_WIDGET_TABLE_ITEM
|
||||
voc: detachable CMS_VOCABULARY
|
||||
tf_input: WSF_FORM_TEXT_INPUT
|
||||
w_set: WSF_FORM_FIELD_SET
|
||||
sub: WSF_FORM_SUBMIT_INPUT
|
||||
do
|
||||
-- Responding with `main_content_html (l_page)'.
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
create wtb.make
|
||||
wtb.add_css_class ("with_border")
|
||||
create wtb_row.make (3)
|
||||
create wtb_item.make_with_text ("Name")
|
||||
wtb_row.set_item (wtb_item, 1)
|
||||
create wtb_item.make_with_text ("Type")
|
||||
wtb_row.set_item (wtb_item, 2)
|
||||
create wtb_item.make_with_text ("Operations")
|
||||
wtb_row.set_item (wtb_item, 3)
|
||||
wtb.add_head_row (wtb_row)
|
||||
|
||||
if attached taxonomy_api.vocabularies (0, 0) as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
voc := ic.item
|
||||
create wtb_row.make (3)
|
||||
wtb.add_row (wtb_row)
|
||||
|
||||
create wtb_item.make_with_text (l_page.link (ic.item.name, "admin/taxonomy/vocabulary/" + ic.item.id.out, Void))
|
||||
-- if attached ic.item.description as l_desc then
|
||||
-- s.append (" : <em>")
|
||||
-- s.append (api.html_encoded (l_desc))
|
||||
-- s.append ("</em>")
|
||||
-- end
|
||||
wtb_row.set_item (wtb_item, 1)
|
||||
l_typenames := taxonomy_api.types_associated_with_vocabulary (voc)
|
||||
if l_typenames /= Void then
|
||||
create s.make_empty
|
||||
across
|
||||
l_typenames as types_ic
|
||||
loop
|
||||
if not s.is_empty then
|
||||
s.append_character (',')
|
||||
s.append_character (' ')
|
||||
end
|
||||
s.append (api.html_encoded (types_ic.item))
|
||||
end
|
||||
create wtb_item.make_with_text (s)
|
||||
wtb_row.set_item (wtb_item, 2)
|
||||
end
|
||||
|
||||
s := l_page.link ("edit", "admin/taxonomy/vocabulary/" + voc.id.out, Void)
|
||||
create wtb_item.make_with_text (s)
|
||||
wtb_row.set_item (wtb_item, 3)
|
||||
end
|
||||
end
|
||||
create f.make (req.percent_encoded_path_info, "taxonomy")
|
||||
f.set_method_post
|
||||
|
||||
f.extend (wtb)
|
||||
|
||||
create w_set.make
|
||||
w_set.set_legend ("Create a new vocabulary")
|
||||
create tf_input.make_with_text ("vocabulary_name", "")
|
||||
tf_input.set_label ("Vocabulary name")
|
||||
w_set.extend (tf_input)
|
||||
create sub.make_with_text ("op", "New Vocabulary")
|
||||
w_set.extend (sub)
|
||||
f.extend (w_set)
|
||||
|
||||
create s.make_empty
|
||||
f.append_to_html (l_page.wsf_theme, s)
|
||||
l_page.set_title ("Vocabularies")
|
||||
l_page.set_main_content (s)
|
||||
l_page.execute
|
||||
end
|
||||
|
||||
end
|
||||
129
modules/taxonomy/handler/taxonomy_vocabulary_handler.e
Normal file
129
modules/taxonomy/handler/taxonomy_vocabulary_handler.e
Normal file
@@ -0,0 +1,129 @@
|
||||
note
|
||||
description: "[
|
||||
Request handler related to
|
||||
/taxonomy/vocabulary/
|
||||
/taxonomy/vocabulary/{vocid}
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$revision$"
|
||||
|
||||
class
|
||||
TAXONOMY_VOCABULARY_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
|
||||
s: STRING
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then
|
||||
if p_vocid.is_integer then
|
||||
tid := p_vocid.value.to_integer_64
|
||||
end
|
||||
end
|
||||
if tid > 0 then
|
||||
if attached taxonomy_api.vocabulary (tid) as voc then
|
||||
-- Responding with `main_content_html (l_page)'.
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
l_page.set_title (voc.name)
|
||||
taxonomy_api.fill_vocabularies_with_terms (voc)
|
||||
|
||||
create s.make_empty
|
||||
s.append ("<ul class=%"taxonomy-terms%">")
|
||||
across
|
||||
voc as ic
|
||||
loop
|
||||
s.append ("<li>")
|
||||
s.append (l_page.link (ic.item.text, "taxonomy/term/" + ic.item.id.out, Void))
|
||||
if attached ic.item.description as l_desc then
|
||||
s.append (" : <em>")
|
||||
s.append (api.html_encoded (l_desc))
|
||||
s.append ("</em>")
|
||||
end
|
||||
s.append ("</li>")
|
||||
end
|
||||
s.append ("</ul>")
|
||||
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 {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
|
||||
create s.make_empty
|
||||
if attached taxonomy_api.vocabularies (0, 0) as lst then
|
||||
s.append ("<ul class=%"taxonomy-vocabularies%">")
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
s.append ("<li>")
|
||||
s.append (l_page.link (ic.item.name, "taxonomy/vocabulary/" + ic.item.id.out, Void))
|
||||
s.append ("</li>")
|
||||
end
|
||||
s.append ("</ul>")
|
||||
else
|
||||
s.append ("No vocabulary!")
|
||||
end
|
||||
l_page.set_main_content (s)
|
||||
l_page.execute
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
160
modules/taxonomy/persistence/cms_taxonomy_storage_i.e
Normal file
160
modules/taxonomy/persistence/cms_taxonomy_storage_i.e
Normal file
@@ -0,0 +1,160 @@
|
||||
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
|
||||
|
||||
vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION
|
||||
-- Vocabularies including `a_term'.
|
||||
deferred
|
||||
end
|
||||
|
||||
is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN
|
||||
-- Is `a_term' inside `a_vocab' ?
|
||||
require
|
||||
valid_term: a_term.has_id
|
||||
valid_vocabulary: a_vocab.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32]
|
||||
-- Type names associated with `a_vocab'.
|
||||
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; a_with_terms: BOOLEAN)
|
||||
-- Insert or update vocabulary `a_voc'
|
||||
-- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True.
|
||||
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: detachable CMS_VOCABULARY)
|
||||
-- Insert or update term `t' as part of vocabulary `voc' if set.
|
||||
deferred
|
||||
ensure
|
||||
not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void
|
||||
end
|
||||
|
||||
remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY)
|
||||
-- Remove term `t' from vocabulary `voc'.
|
||||
require
|
||||
t_has_id: t.has_id
|
||||
deferred
|
||||
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
|
||||
144
modules/taxonomy/persistence/cms_taxonomy_storage_null.e
Normal file
144
modules/taxonomy/persistence/cms_taxonomy_storage_null.e
Normal file
@@ -0,0 +1,144 @@
|
||||
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
|
||||
|
||||
vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32]
|
||||
-- <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; a_with_terms: BOOLEAN)
|
||||
-- Insert or update vocabulary `a_voc'
|
||||
-- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True.
|
||||
do
|
||||
error_handler.add_custom_error (-1, "not implemented", "save_vocabulary")
|
||||
end
|
||||
|
||||
save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY)
|
||||
-- <Precursor>
|
||||
do
|
||||
error_handler.add_custom_error (-1, "not implemented", "save_term")
|
||||
end
|
||||
|
||||
remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY)
|
||||
-- Remove term `t' from vocabulary `voc'.
|
||||
do
|
||||
error_handler.add_custom_error (-1, "not implemented", "remove_term_from_vocabulary")
|
||||
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
|
||||
712
modules/taxonomy/persistence/cms_taxonomy_storage_sql.e
Normal file
712
modules/taxonomy/persistence/cms_taxonomy_storage_sql.e
Normal file
@@ -0,0 +1,712 @@
|
||||
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; a_with_terms: BOOLEAN)
|
||||
local
|
||||
l_terms: CMS_TERM_COLLECTION
|
||||
do
|
||||
save_term (voc, create {CMS_VOCABULARY}.make_none)
|
||||
|
||||
if a_with_terms then
|
||||
l_terms := terms (voc, 0, 0)
|
||||
across
|
||||
voc.terms as ic
|
||||
until
|
||||
has_error
|
||||
loop
|
||||
if attached l_terms.term_by_id (ic.item.id) as t then
|
||||
-- Already contained.
|
||||
-- Remove from `l_terms' to leave only terms to remove.
|
||||
l_terms.remove (t)
|
||||
else
|
||||
save_term (ic.item, voc)
|
||||
end
|
||||
end
|
||||
across
|
||||
l_terms as ic
|
||||
until
|
||||
has_error
|
||||
loop
|
||||
remove_term_from_vocabulary (ic.item, voc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY)
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_insert_voc: BOOLEAN
|
||||
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")
|
||||
|
||||
l_insert_voc := voc /= Void and then is_term_inside_vocabulary (t, voc)
|
||||
|
||||
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 and
|
||||
voc /= Void and then
|
||||
not l_insert_voc
|
||||
then
|
||||
l_parameters.wipe_out
|
||||
l_parameters.put (t.id, "tid")
|
||||
if voc.has_id then
|
||||
l_parameters.put (voc.id, "parent_tid")
|
||||
else
|
||||
l_parameters.put (0, "parent_tid")
|
||||
end
|
||||
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
|
||||
|
||||
remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY)
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (t.id, "tid")
|
||||
l_parameters.put (voc.id, "parent_tid")
|
||||
sql_modify (sql_remove_term_from_vocabulary, l_parameters)
|
||||
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
|
||||
|
||||
is_term_inside_vocabulary (a_term: CMS_TERM; a_voc: CMS_VOCABULARY): BOOLEAN
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_term.id, "tid")
|
||||
l_parameters.put (a_voc.id, "parent_tid")
|
||||
sql_query (sql_select_term_in_vocabulary, l_parameters)
|
||||
sql_start
|
||||
if not has_error or sql_after then
|
||||
Result := sql_read_integer_64 (1) > 0
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION
|
||||
-- <Precursor>
|
||||
local
|
||||
voc: detachable CMS_VOCABULARY
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_parent_id: INTEGER_64
|
||||
l_ids: ARRAYED_LIST [INTEGER_64]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_term.id, "tid")
|
||||
sql_query (sql_select_vocabularies_for_term, l_parameters)
|
||||
|
||||
create l_ids.make (1)
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
l_parent_id := sql_read_integer_64 (1)
|
||||
l_ids.force (l_parent_id)
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
|
||||
if l_ids.count > 0 then
|
||||
create Result.make (1)
|
||||
across
|
||||
l_ids as ic
|
||||
loop
|
||||
voc := vocabulary (ic.item)
|
||||
if voc /= Void then
|
||||
Result.force (voc)
|
||||
end
|
||||
end
|
||||
if Result.count = 0 then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32]
|
||||
-- Type names associated with `a_vocab'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_vocab.id, "tid")
|
||||
sql_query (sql_select_type_associated_with_vocabulary, l_parameters)
|
||||
|
||||
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (3)
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if attached sql_read_string_32 (1) as l_typename then
|
||||
Result.force (l_typename)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
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")
|
||||
|
||||
if
|
||||
attached vocabularies_for_type (a_type_name) as lst and then
|
||||
across lst as ic some a_voc.id = ic.item.id end
|
||||
then
|
||||
sql_modify (sql_update_term_index, l_parameters)
|
||||
else
|
||||
sql_insert (sql_insert_term_index, l_parameters)
|
||||
end
|
||||
|
||||
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_vocabularies_for_term: STRING = "[
|
||||
SELECT parent
|
||||
FROM taxonomy_hierarchy
|
||||
WHERE tid = :tid
|
||||
;
|
||||
]"
|
||||
|
||||
sql_select_term_in_vocabulary: STRING = "[
|
||||
SELECT count(*)
|
||||
FROM taxonomy_hierarchy
|
||||
WHERE tid = :tid and parent = :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_remove_term_from_vocabulary: STRING = "[
|
||||
DELETE FROM taxonomy_hierarchy WHERE tid=:tid AND parent=: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_select_type_associated_with_vocabulary: STRING = "[
|
||||
SELECT type
|
||||
FROM taxonomy_index
|
||||
WHERE tid=:tid AND entity <= 0;
|
||||
]"
|
||||
|
||||
sql_update_term_index: STRING = "[
|
||||
UPDATE taxonomy_index
|
||||
SET entity=:entity
|
||||
WHERE tid=:tid and type=:type
|
||||
;
|
||||
]"
|
||||
|
||||
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
|
||||
26
modules/taxonomy/site/files/css/taxonomy.css
Normal file
26
modules/taxonomy/site/files/css/taxonomy.css
Normal file
@@ -0,0 +1,26 @@
|
||||
ul.taxonomy {
|
||||
font-size: 80%;
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
ul.taxonomy li {
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
ul.taxonomy li a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.taxonomy li:hover {
|
||||
padding: 1px;
|
||||
border-top: solid 1px #66f;
|
||||
border-bottom: solid 1px #66f;
|
||||
background-color: #ddf;
|
||||
}
|
||||
|
||||
table.taxonomy td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
27
modules/taxonomy/site/files/scss/taxonomy.scss
Normal file
27
modules/taxonomy/site/files/scss/taxonomy.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
ul.taxonomy {
|
||||
font-size: 80%;
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
li {
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
padding: 2px;
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
&:hover {
|
||||
padding: 1px;
|
||||
border-top: solid 1px #66f;
|
||||
border-bottom: solid 1px #66f;
|
||||
background-color: #ddf;
|
||||
}
|
||||
}
|
||||
}
|
||||
table.taxonomy {
|
||||
td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
24
modules/taxonomy/site/scripts/install.sql
Normal file
24
modules/taxonomy/site/scripts/install.sql
Normal 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)
|
||||
);
|
||||
3
modules/taxonomy/site/scripts/uninstall.sql
Normal file
3
modules/taxonomy/site/scripts/uninstall.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS taxonomy_term;
|
||||
DROP TABLE IF EXISTS taxonomy_hierarchy;
|
||||
DROP TABLE IF EXISTS taxonomy_index;
|
||||
29
modules/taxonomy/taxonomy-safe.ecf
Normal file
29
modules/taxonomy/taxonomy-safe.ecf
Normal 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>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user