Compare commits
58 Commits
es_rev9802
...
es_rev9836
| Author | SHA1 | Date | |
|---|---|---|---|
| 56b9355f3c | |||
| 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 | |||
| 2a4ebfa12e | |||
| 955852747a | |||
| 23fe22cad1 | |||
| da4b36869a | |||
| 5624892ebc | |||
|
|
e40a7969fa | ||
|
|
67fdd357df | ||
|
|
193760b34f | ||
|
|
9263f31521 | ||
|
|
454d92f85b | ||
|
|
0e63c14613 |
@@ -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.)*
|
||||
@@ -26,9 +26,12 @@
|
||||
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
|
||||
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
|
||||
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
|
||||
<library name="cms_google_search_module" location="..\..\modules\google_search\google_search-safe.ecf" readonly="false" use_application_options="true"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
|
||||
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
|
||||
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
|
||||
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/>
|
||||
<library name="cms_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">
|
||||
@@ -36,7 +39,7 @@
|
||||
<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" />
|
||||
-->
|
||||
|
||||
@@ -11,3 +11,5 @@ set ROC_CMS_DIR=%~dp0
|
||||
%ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR%
|
||||
%ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR%
|
||||
%ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR%
|
||||
%ROC_CMD% install --module ..\..\modules\google_search --dir %ROC_CMS_DIR%
|
||||
%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR%
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -4,13 +4,26 @@ root-dir=site/www
|
||||
#modules-dir=site/modules
|
||||
|
||||
[site]
|
||||
# Name of the site, for the title, and eventual message.
|
||||
name=Eiffel CMS
|
||||
email=your@email.com
|
||||
|
||||
# Email used for notification
|
||||
email=noreply@example.com
|
||||
|
||||
# Name of website theme.
|
||||
theme=bootstrap
|
||||
|
||||
[notification]
|
||||
# By default, notification.email = site.email
|
||||
# you can change here the email that will receive internal messages.
|
||||
#email=notif@example.com
|
||||
|
||||
[mailer]
|
||||
#The mailer is used mostly used by the CMS to send email messages.
|
||||
# you can change the "From:" by setting mailer.from value"
|
||||
#from=...
|
||||
#smtp=localhost:25
|
||||
#sendmail=/usr/bin/sendmail
|
||||
#sendmail=site\bin\roc_sendmail.bat
|
||||
output=@stderr
|
||||
|
||||
[modules]
|
||||
|
||||
@@ -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,6 @@
|
||||
{
|
||||
"gcse": {
|
||||
"cx":"",
|
||||
"secret_key":""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<section>
|
||||
<header>
|
||||
<h2>Results for <kbd>{$result.current_page.search_terms/}</kbd></h2>
|
||||
</header>
|
||||
|
||||
<!-- list of results -->
|
||||
<ol start="{$result.current_page.start_index/}">
|
||||
|
||||
<!-- Item result -->
|
||||
{foreach from="$result.items" item="item"}
|
||||
<li>
|
||||
<article>
|
||||
<header>
|
||||
<h3>
|
||||
<cite>
|
||||
<a href="{$item.link/}">{$item.title/}</a>
|
||||
</cite>
|
||||
</h3>
|
||||
</header>
|
||||
<blockquote cite="{$item.link/}">
|
||||
<p>{$item.html_snippet/}</p>
|
||||
<footer>
|
||||
<p><abbr title="Uniform Resource Locator">Source</abbr> <a href="{$item.link/}">{$item.display_link/}</a></p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</article>
|
||||
</li>
|
||||
{/foreach}
|
||||
|
||||
|
||||
</ol>
|
||||
<ul class="cms-page-links">
|
||||
{if isset="$result.previous_page"}
|
||||
<li><a href="{$site_url/}gcse/?q={$result.previous_page.search_terms/}&start={$result.previous_page.start_index/}&num={$result.previous_page.count/}">Previous</a></li>
|
||||
{/if}
|
||||
{if isset="$result.next_page"}
|
||||
<li><a href="{$site_url/}gcse/?q={$result.next_page.search_terms/}&start={$result.next_page.start_index/}&num={$result.next_page.count/}">Next</a></li>
|
||||
{/if}
|
||||
</ul>
|
||||
</section>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
$(document).ready(function() {
|
||||
$('#gcse_search_form').submit(function() {
|
||||
window.open('', 'formpopup', 'width=600,height=600,resizeable,scrollbars');
|
||||
this.target = 'formpopup';
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- EWF CMS -->
|
||||
<link rel="stylesheet" href="{$site_url/}theme/css/style.css">
|
||||
|
||||
|
||||
<!-- jQuery dep -->
|
||||
<script src="{$site_url/}theme/js/jquery-1.10.2.min.js"></script>
|
||||
<script src="{$site_url/}theme/js/popup_search.js"></script>
|
||||
|
||||
{if isset="$head"}{$head/}{/if}
|
||||
{if isset="$styles"}{$styles/}{/if}
|
||||
@@ -36,7 +37,17 @@
|
||||
{$page.primary_nav/}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Page search -->
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-md-offset-9">
|
||||
<form action="{$site_url/}gcse" class="search-form" id="gcse_search_form">
|
||||
<div class="form-group has-feedback">
|
||||
<input type="search" class="form-control" name="q" id="gcse_search" placeholder="search">
|
||||
<span class="glyphicon glyphicon-search form-control-feedback"></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- General Page Content -->
|
||||
<div id='content' class='row-fluid'>
|
||||
<!-- Left Sidebar sidebar_first -->
|
||||
|
||||
@@ -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
|
||||
@@ -82,6 +86,12 @@ feature -- CMS modules
|
||||
|
||||
create {CMS_DEMO_MODULE} m.make
|
||||
a_setup.register_module (m)
|
||||
|
||||
create {GOOGLE_CUSTOM_SEARCH_MODULE} m.make
|
||||
a_setup.register_module (m)
|
||||
|
||||
create {CMS_SESSION_AUTH_MODULE} m.make
|
||||
a_setup.register_module (m)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -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)
|
||||
|
||||
4
library/gcse/Readme.md
Normal file
4
library/gcse/Readme.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Google Custom Search Engine Eiffel Lbrary
|
||||
|
||||
Based on https://developers.google.com/custom-search/json-api/v1/using_rest
|
||||
|
||||
21
library/gcse/gcse-safe.ecf
Normal file
21
library/gcse/gcse-safe.ecf
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
|
||||
<target name="gcse">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="all">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http_client_extension" location="..\http_client_extension\http_client_extension-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<cluster name="gcse" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
21
library/gcse/gcse.ecf
Normal file
21
library/gcse/gcse.ecf
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="gcse" uuid="81645CEF-4651-45CF-A890-B126E4A6D78C" library_target="gcse">
|
||||
<target name="gcse">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="none">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http_client_extension" location="..\http_client_extension\http_client_extension.ecf" readonly="false"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
|
||||
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
|
||||
<cluster name="gcse" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
10
library/gcse/license.lic
Normal file
10
library/gcse/license.lic
Normal file
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR} Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
236
library/gcse/src/gcse_api.e
Normal file
236
library/gcse/src/gcse_api.e
Normal file
@@ -0,0 +1,236 @@
|
||||
note
|
||||
description: "[
|
||||
Simple API to call Google Custome Search Engine
|
||||
Example call:
|
||||
GET https://www.googleapis.com/customsearch/v1?key=INSERT_YOUR_API_KEY&cx=017576662512468239146:omuauf_lfve&q=lectures
|
||||
]"
|
||||
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
|
||||
revision: "$Revision: 97973 $"
|
||||
EIS: "name=Google Custom Search Engine", "src=https://developers.google.com/custom-search/json-api/v1/using_rest", "protocol=uri"
|
||||
|
||||
class
|
||||
GCSE_API
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_query_parameters: GCSE_QUERY_PARAMETERS)
|
||||
-- Create an object GCSE with query_parameters `a_query_parameters'
|
||||
do
|
||||
query_parameter := a_query_parameters
|
||||
ensure
|
||||
query_parameters_set: query_parameter = a_query_parameters
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
base_uri: STRING_8 = "https://www.googleapis.com/customsearch/v1"
|
||||
-- Google custom search base URI.
|
||||
|
||||
query_parameter: GCSE_QUERY_PARAMETERS
|
||||
-- Google custom search parameters.
|
||||
|
||||
last_result: detachable GCSE_RESPONSE
|
||||
-- Search results.
|
||||
|
||||
feature -- Status Reports
|
||||
|
||||
errors: detachable LIST [READABLE_STRING_8]
|
||||
-- optional list of error messages.
|
||||
|
||||
feature -- API
|
||||
|
||||
search
|
||||
-- Search
|
||||
local
|
||||
l_parser: JSON_PARSER
|
||||
l_gcse_response: detachable GCSE_RESPONSE
|
||||
do
|
||||
-- Data format for the response.
|
||||
-- At the moment we are using the default value: json
|
||||
-- but it's possible to define atom response using the alt parameter.
|
||||
last_result := Void
|
||||
if attached get as l_response then
|
||||
create l_gcse_response
|
||||
l_gcse_response.set_status (l_response.status)
|
||||
l_gcse_response.set_status_nessage (l_response.status_message)
|
||||
|
||||
if attached l_response.body as l_body then
|
||||
create l_parser.make_with_string (l_body)
|
||||
l_parser.parse_content
|
||||
if l_response.status = 200 and then l_parser.is_parsed and then attached {JSON_OBJECT} l_parser.parsed_json_object as jv then
|
||||
-- Queries
|
||||
if attached {JSON_OBJECT} jv.item (queries_key) as jqueries then
|
||||
-- Next Page
|
||||
if attached {GCSE_PAGE} query_page (next_page_key, jqueries) as l_page then
|
||||
l_gcse_response.set_next_page (l_page)
|
||||
end
|
||||
-- Current Page
|
||||
if attached {GCSE_PAGE} query_page (request_key, jqueries) as l_page then
|
||||
l_gcse_response.set_current_page (l_page)
|
||||
end
|
||||
-- Previous Page
|
||||
if attached {GCSE_PAGE} query_page (previous_page_key, jqueries) as l_page then
|
||||
l_gcse_response.set_previous_page (l_page)
|
||||
end
|
||||
end
|
||||
if attached {JSON_ARRAY} jv.item (items_key) as jitems then
|
||||
across jitems as ic loop
|
||||
if attached{JSON_OBJECT} ic.item as j_item then
|
||||
l_gcse_response.add_item (item (j_item))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
put_error (l_body)
|
||||
end
|
||||
else
|
||||
put_error (l_response.status.out)
|
||||
end
|
||||
else
|
||||
put_error ("unknown")
|
||||
end
|
||||
last_result := l_gcse_response
|
||||
end
|
||||
|
||||
feature {NONE} -- REST API
|
||||
|
||||
get: detachable RESPONSE
|
||||
-- Reading Data.
|
||||
local
|
||||
l_request: REQUEST
|
||||
do
|
||||
create l_request.make ("GET", new_uri)
|
||||
Result := l_request.execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
new_uri: STRING_8
|
||||
-- new uri (BaseUri?key=secret_value&cx=a_cx_id&q=a_query
|
||||
-- ?key=INSERT_YOUR_API_KEY&cx=017576662512468239146:omuauf_lfve&q=lectures
|
||||
-- full template BaseUri?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&
|
||||
-- safe={safe?}&cx={cx?}&cref={cref?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&
|
||||
-- googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&
|
||||
-- siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&
|
||||
-- orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&
|
||||
-- searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&
|
||||
-- imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
|
||||
|
||||
do
|
||||
create Result.make_from_string (base_uri)
|
||||
Result.append ("?key=")
|
||||
Result.append (query_parameter.secret)
|
||||
Result.append ("&cx=")
|
||||
Result.append (query_parameter.cx)
|
||||
Result.append ("&q=")
|
||||
Result.append (query_parameter.query)
|
||||
-- num
|
||||
if attached query_parameter.num as l_num then
|
||||
Result.append ("&num=")
|
||||
Result.append (l_num)
|
||||
end
|
||||
if attached query_parameter.start as l_start then
|
||||
Result.append ("&start=")
|
||||
Result.append (l_start)
|
||||
end
|
||||
end
|
||||
|
||||
put_error (a_message: READABLE_STRING_GENERAL)
|
||||
-- put error message `a_message'.
|
||||
local
|
||||
l_errors: like errors
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
l_errors := errors
|
||||
if l_errors = Void then
|
||||
create {ARRAYED_LIST [STRING]} l_errors.make (1)
|
||||
errors := l_errors
|
||||
end
|
||||
l_errors.force (utf.utf_32_string_to_utf_8_string_8 (a_message))
|
||||
end
|
||||
|
||||
item (a_item: JSON_OBJECT): GCSE_PAGE_ITEM
|
||||
-- Google Result Metadata Item.
|
||||
do
|
||||
create Result
|
||||
if attached {JSON_STRING} a_item.item ("kind") as l_kind then
|
||||
Result.set_kind (l_kind.item)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("title") as l_title then
|
||||
Result.set_title (l_title.item)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("htmlTitle") as l_htmltitle then
|
||||
Result.set_html_title (l_htmltitle.unescaped_string_32)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("link") as l_link then
|
||||
Result.set_link (l_link.item)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("displayLink") as l_display_link then
|
||||
Result.set_display_link (l_display_link.item)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("snippet") as l_snippet then
|
||||
Result.set_snippet (l_snippet.unescaped_string_8)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("htmlSnippet") as l_html_snippet then
|
||||
Result.set_html_snippet (l_html_snippet.unescaped_string_32)
|
||||
end
|
||||
if attached {JSON_STRING} a_item.item ("formattedUrl") as l_formatted_url then
|
||||
Result.set_formatted_url (l_formatted_url.item)
|
||||
end
|
||||
end
|
||||
|
||||
query_page (a_page_key: JSON_STRING; a_queries: JSON_OBJECT): detachable GCSE_PAGE
|
||||
-- Google result medata query. Return a query page based for a query with page key `a_page_key', if any.
|
||||
do
|
||||
if
|
||||
attached {JSON_ARRAY} a_queries.item (a_page_key) as jquerypage and then
|
||||
jquerypage.count > 0 and then
|
||||
attached {JSON_OBJECT} jquerypage.i_th (1) as jpage
|
||||
then
|
||||
create Result
|
||||
if attached {JSON_STRING} jpage.item ("title") as l_title then
|
||||
Result.set_title (l_title.item)
|
||||
end
|
||||
if attached {JSON_STRING} jpage.item ("totalResults") as l_results then
|
||||
Result.set_total_results (l_results.item.to_integer)
|
||||
end
|
||||
if attached {JSON_STRING} jpage.item ("searchTerms") as l_search_terms then
|
||||
Result.set_search_terms (l_search_terms.item)
|
||||
end
|
||||
-- TODO check if we should use INTEGER_64
|
||||
if attached {JSON_NUMBER} jpage.item ("count") as l_count then
|
||||
Result.set_count (l_count.integer_64_item.as_integer_32)
|
||||
end
|
||||
if attached {JSON_NUMBER} jpage.item ("startIndex") as l_index then
|
||||
Result.set_start_index (l_index.integer_64_item.as_integer_32)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- JSON Keys
|
||||
|
||||
queries_key: STRING = "queries"
|
||||
|
||||
next_page_key: STRING = "nextPage"
|
||||
|
||||
request_key: STRING = "request"
|
||||
|
||||
previous_page_key: STRING = "previousPage"
|
||||
|
||||
items_key: STRING = "items"
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
118
library/gcse/src/gcse_page.e
Normal file
118
library/gcse/src/gcse_page.e
Normal file
@@ -0,0 +1,118 @@
|
||||
note
|
||||
description: "Represent metadata describing the query for the current set of results."
|
||||
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
|
||||
revision: "$Revision: 97973 $"
|
||||
|
||||
class
|
||||
GCSE_PAGE
|
||||
|
||||
inherit
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
feature -- Access
|
||||
|
||||
search_terms: detachable STRING_8
|
||||
-- search term
|
||||
|
||||
title: detachable STRING_8
|
||||
-- Search title.
|
||||
|
||||
total_results: INTEGER
|
||||
-- Search total results.
|
||||
|
||||
count: INTEGER
|
||||
-- Rows per page.
|
||||
|
||||
start_index: INTEGER
|
||||
-- Page index.
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_search_terms (a_search_terms: like search_terms)
|
||||
-- Assign `search_terms' with `a_search_terms'.
|
||||
do
|
||||
search_terms := a_search_terms
|
||||
ensure
|
||||
search_terms_assigned: search_terms = a_search_terms
|
||||
end
|
||||
|
||||
feature -- Change element
|
||||
|
||||
set_title (a_title: like title)
|
||||
-- Set title with `a_title'
|
||||
do
|
||||
title := a_title
|
||||
ensure
|
||||
title_set: title = a_title
|
||||
end
|
||||
|
||||
set_total_results (a_total_results: like total_results)
|
||||
-- Set total_results with `a_total_results'.
|
||||
do
|
||||
total_results := a_total_results
|
||||
ensure
|
||||
total_results_set: total_results = a_total_results
|
||||
end
|
||||
|
||||
set_count (a_count: like count)
|
||||
-- Set count with `a_count'.
|
||||
do
|
||||
count := a_count
|
||||
ensure
|
||||
count_set: count = a_count
|
||||
end
|
||||
|
||||
set_start_index (a_start_index: like start_index)
|
||||
-- Set start_index with `a_start_index'.
|
||||
do
|
||||
start_index := a_start_index
|
||||
ensure
|
||||
start_index_set: start_index = a_start_index
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING_8
|
||||
-- <Precursor>
|
||||
do
|
||||
create Result.make_from_string ("%NPage details%N")
|
||||
if attached title as l_title then
|
||||
Result.append ("Title:")
|
||||
Result.append (l_title)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached search_terms as l_search_tearm then
|
||||
Result.append ("Search Tearm:")
|
||||
Result.append (l_search_tearm)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
|
||||
Result.append ("Count:")
|
||||
Result.append (count.out)
|
||||
Result.append_character ('%N')
|
||||
Result.append ("Total Result:")
|
||||
Result.append (count.out)
|
||||
Result.append_character ('%N')
|
||||
Result.append ("Count:")
|
||||
Result.append (total_results.out)
|
||||
Result.append_character ('%N')
|
||||
Result.append ("Start index:")
|
||||
Result.append (start_index.out)
|
||||
Result.append_character ('%N')
|
||||
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
208
library/gcse/src/gcse_page_item.e
Normal file
208
library/gcse/src/gcse_page_item.e
Normal file
@@ -0,0 +1,208 @@
|
||||
note
|
||||
description: "Represent a search result, include the URL, title and text snippets that describe the result"
|
||||
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
|
||||
revision: "$Revision: 97973 $"
|
||||
|
||||
class
|
||||
GCSE_PAGE_ITEM
|
||||
|
||||
inherit
|
||||
DEBUG_OUTPUT
|
||||
|
||||
feature -- Access
|
||||
|
||||
html_formatted_url: detachable STRING_8
|
||||
-- Html formatted url of this result
|
||||
|
||||
formatted_url: detachable STRING_8
|
||||
-- Formatted url of this result
|
||||
|
||||
cache_id: detachable STRING_8
|
||||
-- Cache id of this result
|
||||
|
||||
html_snippet: detachable STRING_8
|
||||
-- Html snippet of this request
|
||||
|
||||
snippet: detachable STRING_8
|
||||
-- Snippet of this result
|
||||
|
||||
display_link: detachable STRING_8
|
||||
-- Display link of this result
|
||||
|
||||
link: detachable STRING_8
|
||||
-- link of this result
|
||||
|
||||
html_title: detachable STRING_8
|
||||
-- html title of result
|
||||
|
||||
title: detachable STRING_8
|
||||
-- title of result.
|
||||
|
||||
kind: detachable STRING_8
|
||||
-- Kind of actual search result.
|
||||
|
||||
page_map: detachable GCSE_PAGE_MAP
|
||||
-- Page map
|
||||
--! Not supported for now.
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_html_formatted_url (a_html_formatted_url: like html_formatted_url)
|
||||
-- Assign `html_formatted_url' with `a_html_formatted_url'.
|
||||
do
|
||||
html_formatted_url := a_html_formatted_url
|
||||
ensure
|
||||
html_formatted_url_assigned: html_formatted_url = a_html_formatted_url
|
||||
end
|
||||
|
||||
set_formatted_url (a_formatted_url: like formatted_url)
|
||||
-- Assign `formatted_url' with `a_formatted_url'.
|
||||
do
|
||||
formatted_url := a_formatted_url
|
||||
ensure
|
||||
formatted_url_assigned: formatted_url = a_formatted_url
|
||||
end
|
||||
|
||||
set_cache_id (a_cache_id: like cache_id)
|
||||
-- Assign `cache_id' with `a_cache_id'.
|
||||
do
|
||||
cache_id := a_cache_id
|
||||
ensure
|
||||
cache_id_assigned: cache_id = a_cache_id
|
||||
end
|
||||
|
||||
set_html_snippet (a_html_snippet: like html_snippet)
|
||||
-- Assign `html_snippet' with `a_html_snippet'.
|
||||
do
|
||||
html_snippet := a_html_snippet
|
||||
ensure
|
||||
html_snippet_assigned: html_snippet = a_html_snippet
|
||||
end
|
||||
|
||||
set_snippet (a_snippet: like snippet)
|
||||
-- Assign `snippet' with `a_snippet'.
|
||||
do
|
||||
snippet := a_snippet
|
||||
ensure
|
||||
snippet_assigned: snippet = a_snippet
|
||||
end
|
||||
|
||||
set_display_link (a_display_link: like display_link)
|
||||
-- Assign `display_link' with `a_display_link'.
|
||||
do
|
||||
display_link := a_display_link
|
||||
ensure
|
||||
display_link_assigned: display_link = a_display_link
|
||||
end
|
||||
|
||||
set_link (a_link: like link)
|
||||
-- Assign `link' with `a_link'.
|
||||
do
|
||||
link := a_link
|
||||
ensure
|
||||
link_assigned: link = a_link
|
||||
end
|
||||
|
||||
set_html_title (a_html_title: like html_title)
|
||||
-- Assign `html_title' with `a_html_title'.
|
||||
do
|
||||
html_title := a_html_title
|
||||
ensure
|
||||
html_title_assigned: html_title = a_html_title
|
||||
end
|
||||
|
||||
set_title (a_title: like title)
|
||||
-- Assign `title' with `a_title'.
|
||||
do
|
||||
title := a_title
|
||||
ensure
|
||||
title_assigned: title = a_title
|
||||
end
|
||||
|
||||
set_kind (a_kind: like kind)
|
||||
-- Assign `kind' with `a_kind'.
|
||||
do
|
||||
kind := a_kind
|
||||
ensure
|
||||
kind_assigned: kind = a_kind
|
||||
end
|
||||
|
||||
set_page_map (a_map: like page_map)
|
||||
-- Assign `kind' with `a_kind'.
|
||||
do
|
||||
page_map := a_map
|
||||
ensure
|
||||
page_map_assigned: page_map = a_map
|
||||
end
|
||||
|
||||
|
||||
feature -- Output
|
||||
|
||||
debug_output: STRING_8
|
||||
-- <Precursor>
|
||||
do
|
||||
create Result.make_from_string ("%NPage Item details%N")
|
||||
if attached title as l_title then
|
||||
Result.append ("Title:")
|
||||
Result.append (l_title)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached kind as l_kind then
|
||||
Result.append ("Kind:")
|
||||
Result.append (l_kind)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached html_title as l_html_title then
|
||||
Result.append ("Html title:")
|
||||
Result.append (l_html_title)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached link as l_link then
|
||||
Result.append ("Link:")
|
||||
Result.append (l_link)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached display_link as l_display_link then
|
||||
Result.append ("Display link:")
|
||||
Result.append (l_display_link)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached snippet as l_snippet then
|
||||
Result.append ("Snippet:")
|
||||
Result.append (l_snippet)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached html_snippet as l_html_snippet then
|
||||
Result.append ("Html snippet:")
|
||||
Result.append (l_html_snippet)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached cache_id as l_cache_id then
|
||||
Result.append ("Cache_id:")
|
||||
Result.append (l_cache_id)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached formatted_url as l_formatted_url then
|
||||
Result.append ("Formatted url:")
|
||||
Result.append (l_formatted_url)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if attached html_formatted_url as l_html_formatted_url then
|
||||
Result.append ("Html formatted url:")
|
||||
Result.append (l_html_formatted_url)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
41
library/gcse/src/gcse_page_map.e
Normal file
41
library/gcse/src/gcse_page_map.e
Normal file
@@ -0,0 +1,41 @@
|
||||
note
|
||||
description: "[
|
||||
Represent a google page map
|
||||
"pagemap": {
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://www.eiffel.org/portal/files/userpictures/picture-40.jpg"
|
||||
}
|
||||
],
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"width": "81",
|
||||
"height": "61",
|
||||
"src": "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRnC-RKzps6BFItx_MLYBVskFI7U6u0y3VJBInomPYEF5sO6gkip94mLw"
|
||||
}
|
||||
]
|
||||
}
|
||||
]"
|
||||
date: "$Date: 2015-10-09 08:11:07 -0300 (vi., 09 oct. 2015) $"
|
||||
revision: "$Revision: 97973 $"
|
||||
EIS: "name=PageMaps", "src=https://developers.google.com/custom-search/docs/structured_data#pagemaps", "protocol=url"
|
||||
|
||||
class
|
||||
GCSE_PAGE_MAP
|
||||
|
||||
feature -- Access
|
||||
|
||||
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
238
library/gcse/src/gcse_query_parameters.e
Normal file
238
library/gcse/src/gcse_query_parameters.e
Normal file
@@ -0,0 +1,238 @@
|
||||
note
|
||||
description: "[
|
||||
Represent google custom search parameters
|
||||
Example url template
|
||||
"template": "https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&cref={cref?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
|
||||
]"
|
||||
optional_parameters: "[
|
||||
Optional parameters
|
||||
c2coff string Enables or disables Simplified and Traditional Chinese Search.
|
||||
The default value for this parameter is 0 (zero), meaning that the feature is enabled. Supported values are:
|
||||
1: Disabled
|
||||
0: Enabled (default)
|
||||
cr string Restricts search results to documents originating in a particular country.
|
||||
You may use Boolean operators in the cr parameter's value.
|
||||
Google Search determines the country of a document by analyzing:
|
||||
the top-level domain (TLD) of the document's URL
|
||||
the geographic location of the Web server's IP address
|
||||
See the Country Parameter Values page for a list of valid values for this parameter.
|
||||
cref string The URL of a linked custom search engine specification to use for this request.
|
||||
Does not apply for Google Site Search
|
||||
If both cx and cref are specified, the cx value is used
|
||||
cx string The custom search engine ID to use for this request.
|
||||
If both cx and cref are specified, the cx value is used.
|
||||
dateRestrict string Restricts results to URLs based on date. Supported values include:
|
||||
d[number]: requests results from the specified number of past days.
|
||||
w[number]: requests results from the specified number of past weeks.
|
||||
m[number]: requests results from the specified number of past months.
|
||||
y[number]: requests results from the specified number of past years.
|
||||
exactTerms string Identifies a phrase that all documents in the search results must contain.
|
||||
excludeTerms string Identifies a word or phrase that should not appear in any documents in the search results.
|
||||
fileType string Restricts results to files of a specified extension. A list of file types indexable by Google can be found in Webmaster Tools Help Center.
|
||||
filter string Controls turning on or off the duplicate content filter.
|
||||
See Automatic Filtering for more information about Google's search results filters. Note that host crowding filtering applies only to multi-site searches.
|
||||
By default, Google applies filtering to all search results to improve the quality of those results.
|
||||
|
||||
|
||||
Acceptable values are:
|
||||
"0": Turns off duplicate content filter.
|
||||
"1": Turns on duplicate content filter.
|
||||
gl string Geolocation of end user.
|
||||
The gl parameter value is a two-letter country code. The gl parameter boosts search results whose country of origin matches the parameter value. See the Country Codes page for a list of valid values.
|
||||
Specifying a gl parameter value should lead to more relevant results. This is particularly true for international customers and, even more specifically, for customers in English- speaking countries other than the United States.
|
||||
googlehost string The local Google domain (for example, google.com, google.de, or google.fr) to use to perform the search.
|
||||
highRange string
|
||||
Specifies the ending value for a search range.
|
||||
Use lowRange and highRange to append an inclusive search range of lowRange...highRange to the query.
|
||||
hl string Sets the user interface language.
|
||||
Explicitly setting this parameter improves the performance and the quality of your search results.
|
||||
See the Interface Languages section of Internationalizing Queries and Results Presentation for more information, and Supported Interface Languages for a list of supported languages.
|
||||
hq string Appends the specified query terms to the query, as if they were combined with a logical AND operator.
|
||||
imgColorType string Returns black and white, grayscale, or color images: mono, gray, and color.
|
||||
|
||||
Acceptable values are:
|
||||
"color": color
|
||||
"gray": gray
|
||||
"mono": mono
|
||||
imgDominantColor string Returns images of a specific dominant color.
|
||||
|
||||
Acceptable values are:
|
||||
"black": black
|
||||
"blue": blue
|
||||
"brown": brown
|
||||
"gray": gray
|
||||
"green": green
|
||||
"pink": pink
|
||||
"purple": purple
|
||||
"teal": teal
|
||||
"white": white
|
||||
"yellow": yellow
|
||||
imgSize string Returns images of a specified size.
|
||||
|
||||
Acceptable values are:
|
||||
"huge": huge
|
||||
"icon": icon
|
||||
"large": large
|
||||
"medium": medium
|
||||
"small": small
|
||||
"xlarge": xlarge
|
||||
"xxlarge": xxlarge
|
||||
imgType string Returns images of a type.
|
||||
|
||||
Acceptable values are:
|
||||
"clipart": clipart
|
||||
"face": face
|
||||
"lineart": lineart
|
||||
"news": news
|
||||
"photo": photo
|
||||
linkSite string Specifies that all search results should contain a link to a particular URL
|
||||
lowRange string Specifies the starting value for a search range.
|
||||
Use lowRange and highRange to append an inclusive search range of lowRange...highRange to the query.
|
||||
lr string Restricts the search to documents written in a particular language (e.g., lr=lang_ja).
|
||||
|
||||
Acceptable values are:
|
||||
"lang_ar": Arabic
|
||||
"lang_bg": Bulgarian
|
||||
"lang_ca": Catalan
|
||||
"lang_cs": Czech
|
||||
"lang_da": Danish
|
||||
"lang_de": German
|
||||
"lang_el": Greek
|
||||
"lang_en": English
|
||||
"lang_es": Spanish
|
||||
"lang_et": Estonian
|
||||
"lang_fi": Finnish
|
||||
"lang_fr": French
|
||||
"lang_hr": Croatian
|
||||
"lang_hu": Hungarian
|
||||
"lang_id": Indonesian
|
||||
"lang_is": Icelandic
|
||||
"lang_it": Italian
|
||||
"lang_iw": Hebrew
|
||||
"lang_ja": Japanese
|
||||
"lang_ko": Korean
|
||||
"lang_lt": Lithuanian
|
||||
"lang_lv": Latvian
|
||||
"lang_nl": Dutch
|
||||
"lang_no": Norwegian
|
||||
"lang_pl": Polish
|
||||
"lang_pt": Portuguese
|
||||
"lang_ro": Romanian
|
||||
"lang_ru": Russian
|
||||
"lang_sk": Slovak
|
||||
"lang_sl": Slovenian
|
||||
"lang_sr": Serbian
|
||||
"lang_sv": Swedish
|
||||
"lang_tr": Turkish
|
||||
"lang_zh-CN": Chinese (Simplified)
|
||||
"lang_zh-TW": Chinese (Traditional)
|
||||
|
||||
orTerms string Provides additional search terms to check for in a document, where each document in the search results must contain at least one of the additional search terms.
|
||||
relatedSite string Specifies that all search results should be pages that are related to the specified URL.
|
||||
rights string Filters based on licensing. Supported values include: cc_publicdomain, cc_attribute, cc_sharealike, cc_noncommercial, cc_nonderived, and combinations of these.
|
||||
safe string Search safety level.
|
||||
|
||||
Acceptable values are:
|
||||
"high": Enables highest level of SafeSearch filtering.
|
||||
"medium": Enables moderate SafeSearch filtering.
|
||||
"off": Disables SafeSearch filtering. (default)
|
||||
searchType string Specifies the search type: image. If unspecified, results are limited to webpages.
|
||||
|
||||
Acceptable values are:
|
||||
"image": custom image search.
|
||||
siteSearch string Specifies all search results should be pages from a given site.
|
||||
siteSearchFilter string Controls whether to include or exclude results from the site named in the siteSearch parameter.
|
||||
|
||||
Acceptable values are:
|
||||
"e": exclude
|
||||
"i": include
|
||||
sort string The sort expression to apply to the results.
|
||||
]"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
EIS: "GCSE parameters", "src=https://developers.google.com/custom-search/json-api/v1/reference/cse/list", "protocol=URI"
|
||||
|
||||
class
|
||||
GCSE_QUERY_PARAMETERS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_secret_key, a_cx, a_query: READABLE_STRING_8)
|
||||
-- Create an object GCSE_QUERY_PARAMETERS with secret key `a_secret_key' and a custom search engine id `a_cx'.
|
||||
-- and query `a_query'.
|
||||
do
|
||||
-- TODO
|
||||
-- At the moment the API only use cx as Google Custom Search id.
|
||||
-- Custom search engine ID - Use either cx or cref to specify the custom search engine you want to use to perform this search
|
||||
|
||||
secret := a_secret_key
|
||||
cx := a_cx
|
||||
query := a_query
|
||||
ensure
|
||||
secret_set: secret.same_string (a_secret_key)
|
||||
cx_set: cx.same_string (a_cx)
|
||||
query_set: query.same_string (a_query)
|
||||
end
|
||||
|
||||
feature -- Access : Required Parameters
|
||||
|
||||
secret: READABLE_STRING_8
|
||||
-- Required. The shared key between your site and Google Custom Search Engine.
|
||||
|
||||
cx: READABLE_STRING_8
|
||||
-- Custom search engine id to perform this search.
|
||||
|
||||
query: READABLE_STRING_8
|
||||
-- Search query, query parameter to specify your search expression.
|
||||
|
||||
feature -- Optional Parameters
|
||||
|
||||
|
||||
|
||||
num : detachable STRING_8
|
||||
-- Number of search results to return.
|
||||
-- Valid values are integers between 1 and 10, inclusive.
|
||||
|
||||
start: detachable STRING_8
|
||||
-- The index of the first result to return.
|
||||
|
||||
|
||||
feature -- Change Elements
|
||||
|
||||
set_num (a_num: READABLE_STRING_8)
|
||||
require
|
||||
is_number: a_num.is_integer
|
||||
valid_range: a_num.to_integer >= 1 and then a_num.to_integer <= 10
|
||||
do
|
||||
num := a_num
|
||||
ensure
|
||||
num_set: num = a_num
|
||||
valid_rage_set: attached num as l_num and then l_num.to_integer >= 1 and then l_num.to_integer <= 10
|
||||
end
|
||||
|
||||
|
||||
set_start (a_start: READABLE_STRING_8)
|
||||
require
|
||||
is_number: a_start.is_integer
|
||||
valid_start: a_start.to_integer >= 1
|
||||
do
|
||||
start := a_start
|
||||
ensure
|
||||
start_set: start = a_start
|
||||
valid_start_set: attached start as l_start and then l_start.to_integer >= 1
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
115
library/gcse/src/gcse_response.e
Normal file
115
library/gcse/src/gcse_response.e
Normal file
@@ -0,0 +1,115 @@
|
||||
note
|
||||
description: "[
|
||||
Represent search request metadata
|
||||
URL: search template used for the current results.
|
||||
Queries: current, next and previous page.
|
||||
Context
|
||||
Search infromation
|
||||
Items: array of actual search results.
|
||||
]"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
|
||||
class
|
||||
GCSE_RESPONSE
|
||||
|
||||
--! TODO
|
||||
--! All suppport for for url, context and search information.
|
||||
|
||||
feature -- Access
|
||||
|
||||
current_page: detachable GCSE_PAGE
|
||||
-- Metadata describing the query for the current set of results.
|
||||
-- This role is always present in the response.
|
||||
-- It is always an array with just one element.
|
||||
|
||||
next_page: detachable GCSE_PAGE
|
||||
-- Metadata describing the query to use for the next page of results.
|
||||
-- This role is not present if the current results are the last page. Note: This API returns up to the first 100 results only.
|
||||
-- When present, it is always a array with just one element.
|
||||
|
||||
previous_page: detachable GCSE_PAGE
|
||||
-- Metadata describing the query to use for the previous page of results.
|
||||
-- Not present if the current results are the first page.
|
||||
-- When present, it is always a array with just one element.
|
||||
|
||||
items: detachable LIST [GCSE_PAGE_ITEM]
|
||||
-- Contains the actual search results. The search results include the URL, title and text snippets that describe the result.
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_current_page (a_page: GCSE_PAGE)
|
||||
-- Set `current_page' with `a_page'.
|
||||
do
|
||||
current_page := a_page
|
||||
ensure
|
||||
current_page_set: current_page = a_page
|
||||
end
|
||||
|
||||
set_next_page (a_page: GCSE_PAGE)
|
||||
-- Set `next_page' with `a_page'.
|
||||
do
|
||||
next_page := a_page
|
||||
ensure
|
||||
next_page_set: next_page = a_page
|
||||
end
|
||||
|
||||
set_previous_page (a_page: GCSE_PAGE)
|
||||
-- Set `previous_page' with `a_page'.
|
||||
do
|
||||
previous_page := a_page
|
||||
ensure
|
||||
previous_page_set: previous_page = a_page
|
||||
end
|
||||
|
||||
add_item (a_item: GCSE_PAGE_ITEM)
|
||||
-- Add item `a_item' to the list of items.
|
||||
local
|
||||
l_items: like items
|
||||
do
|
||||
l_items := items
|
||||
if l_items = Void then
|
||||
create {ARRAYED_LIST[GCSE_PAGE_ITEM]}l_items.make (10)
|
||||
items := l_items
|
||||
end
|
||||
l_items.force (a_item)
|
||||
end
|
||||
|
||||
|
||||
feature -- Acess: HTTP Response
|
||||
|
||||
status: INTEGER
|
||||
-- HTTP status code.
|
||||
|
||||
status_message: detachable READABLE_STRING_8
|
||||
-- associated textual phrase for the response status.
|
||||
|
||||
feature -- Change Element: HTTP Response
|
||||
|
||||
set_status (a_status: like status)
|
||||
-- Set `status' with `a_status'.
|
||||
do
|
||||
status := a_status
|
||||
ensure
|
||||
status_set: status = a_status
|
||||
end
|
||||
|
||||
set_status_nessage (a_message: like status_message)
|
||||
-- Set `status_message' with `a_message'.
|
||||
do
|
||||
status_message := a_message
|
||||
ensure
|
||||
status_message_set: status_message = a_message
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
80
library/gcse/test/application.e
Normal file
80
library/gcse/test/application.e
Normal file
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description : "test application root class"
|
||||
date : "$Date: 2015-12-02 10:27:38 -0300 (mi. 02 de dic. de 2015) $"
|
||||
revision : "$Revision: 98180 $"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
ARGUMENTS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Run application.
|
||||
local
|
||||
gcse: GCSE_API
|
||||
l_parameters: GCSE_QUERY_PARAMETERS
|
||||
do
|
||||
create l_parameters.make (key, cx, "scoop")
|
||||
create gcse.make (l_parameters)
|
||||
gcse.search
|
||||
|
||||
if attached {GCSE_RESPONSE} gcse.last_result as l_result then
|
||||
if attached l_result.current_page as l_page then
|
||||
print ("Current Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
if attached l_result.next_page as l_page then
|
||||
print ("Next Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
if attached l_result.previous_page as l_page then
|
||||
print ("Previous Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
|
||||
if attached l_result.items as l_items then
|
||||
print ("Number of items:" + l_items.count.out)
|
||||
across l_items as ic loop print (ic.item.debug_output) end
|
||||
end
|
||||
|
||||
if attached l_result.next_page as l_page then
|
||||
l_parameters.set_start (l_page.start_index.out)
|
||||
gcse.search
|
||||
end
|
||||
end
|
||||
|
||||
if attached {GCSE_RESPONSE} gcse.last_result as l_result then
|
||||
if attached l_result.current_page as l_page then
|
||||
print ("Current Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
if attached l_result.next_page as l_page then
|
||||
print ("Next Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
if attached l_result.previous_page as l_page then
|
||||
print ("Previous Page%N")
|
||||
print (l_page.debug_output)
|
||||
end
|
||||
|
||||
if attached l_result.items as l_items then
|
||||
print ("Number of items:" + l_items.count.out)
|
||||
across l_items as ic loop print (ic.item.debug_output) end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
Key: STRING = "AIzaSyBKAXNofo-RqZb6kUmpbiCwPEy7n7-E51k"
|
||||
cx : STRING = "015017565055626880074:9gdgp1fvt-g"
|
||||
end
|
||||
31
library/gcse/test/gcse_api_test_set.e
Normal file
31
library/gcse/test/gcse_api_test_set.e
Normal file
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
GCSE_API_TEST_SET
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
has_error (l_captcha: GCSE_API; a_error: READABLE_STRING_32): BOOLEAN
|
||||
do
|
||||
if attached l_captcha.errors as l_errors then
|
||||
l_errors.compare_objects
|
||||
Result := l_errors.has (a_error)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
20
library/gcse/test/test.ecf
Normal file
20
library/gcse/test/test.ecf
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="test">
|
||||
<target name="test">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<option warning="true" void_safety="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="gcse" location="..\gcse-safe.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<cluster name="test" location=".\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
26
library/http_client_extension/http_client_extension-safe.ecf
Normal file
26
library/http_client_extension/http_client_extension-safe.ecf
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client_extension" uuid="EA6A381D-2E78-448C-8A6D-B71759F1082E" library_target="http_client_extension">
|
||||
<target name="http_client_extension">
|
||||
<root all_classes="true"/>
|
||||
<option warning="true" void_safety="all">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="http_client_extension" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
25
library/http_client_extension/http_client_extension.ecf
Normal file
25
library/http_client_extension/http_client_extension.ecf
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client_extension" uuid="DD90A4FA-1B7F-4C8C-A739-AE67D6F40384" library_target="http_client_extension">
|
||||
<target name="http_client_extension">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="none">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="http_client_extension" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
199
library/http_client_extension/src/request/request.e
Normal file
199
library/http_client_extension/src/request/request.e
Normal file
@@ -0,0 +1,199 @@
|
||||
note
|
||||
description: "Represent an HTTP request."
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
|
||||
class
|
||||
REQUEST
|
||||
|
||||
inherit
|
||||
|
||||
HTTP_CONSTANTS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_method: READABLE_STRING_8; a_uri: READABLE_STRING_8)
|
||||
require
|
||||
valid_http_method: is_http_method (a_method)
|
||||
valid_uri: is_valid_uri (a_uri)
|
||||
do
|
||||
verb := a_method
|
||||
uri := a_uri
|
||||
create headers.make (5)
|
||||
ensure
|
||||
ver_set: verb = a_method
|
||||
uri_set: uri = a_uri
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
is_valid_uri (a_uri: READABLE_STRING_8): BOOLEAN
|
||||
local
|
||||
l_uri: URI
|
||||
do
|
||||
create l_uri.make_from_string (a_uri)
|
||||
Result := l_uri.is_valid
|
||||
end
|
||||
|
||||
query_string: detachable READABLE_STRING_8
|
||||
local
|
||||
l_uri: URI
|
||||
do
|
||||
create l_uri.make_from_string (uri)
|
||||
Result := l_uri.query
|
||||
end
|
||||
|
||||
sanitized_url: READABLE_STRING_8
|
||||
-- Returns the URL without the query string part
|
||||
local
|
||||
l_uri: URI
|
||||
do
|
||||
create l_uri.make_from_string (uri)
|
||||
l_uri.remove_query
|
||||
Result := l_uri.string
|
||||
ensure
|
||||
sanitized: not as_uri (Result).has_query
|
||||
end
|
||||
|
||||
is_http_method (a_method: READABLE_STRING_GENERAL): BOOLEAN
|
||||
do
|
||||
if a_method.same_string (method_connect) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_delete) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_get) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_head) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_options) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_patch) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_post) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_put) then
|
||||
Result := True
|
||||
elseif a_method.same_string (method_trace) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Constants
|
||||
|
||||
content_type_header_name: STRING_8 = "Content-Type";
|
||||
|
||||
default_content_type: STRING
|
||||
once
|
||||
Result := application_json
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
uri: READABLE_STRING_8
|
||||
|
||||
verb: READABLE_STRING_8
|
||||
|
||||
headers: STRING_TABLE [READABLE_STRING_8]
|
||||
|
||||
payload: detachable READABLE_STRING_8
|
||||
|
||||
executor: detachable REQUEST_EXECUTOR
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
add_payload (a_payload: like payload)
|
||||
do
|
||||
payload := a_payload
|
||||
ensure
|
||||
payload_set: attached payload as l_payload implies l_payload = a_payload
|
||||
end
|
||||
|
||||
add_header (key: READABLE_STRING_8; value: READABLE_STRING_8)
|
||||
do
|
||||
headers.force (value, key)
|
||||
end
|
||||
|
||||
feature -- Execute
|
||||
|
||||
execute: detachable RESPONSE
|
||||
do
|
||||
initialize_executor
|
||||
Result := execute_request
|
||||
end
|
||||
|
||||
initialize_executor
|
||||
do
|
||||
create executor.make (uri, verb)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
execute_request: detachable RESPONSE
|
||||
do
|
||||
if attached executor as l_executor then
|
||||
-- add headers
|
||||
add_headers (l_executor)
|
||||
if verb.same_string (method_put) or else verb.same_string (method_post) or else verb.same_string (method_patch) then
|
||||
l_executor.set_body (body_contents)
|
||||
end
|
||||
if not l_executor.context_executor.headers.has (content_type_header_name) then
|
||||
l_executor.context_executor.add_header (content_type_header_name, default_content_type)
|
||||
end
|
||||
if attached l_executor.execute as l_response then
|
||||
create Result.make (l_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
add_headers (a_executor: REQUEST_EXECUTOR)
|
||||
local
|
||||
l_context_executor: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
s: READABLE_STRING_GENERAL
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
l_context_executor := a_executor.context_executor
|
||||
across
|
||||
headers as ic
|
||||
loop
|
||||
s := ic.key
|
||||
if s.is_valid_as_string_8 then
|
||||
l_context_executor.add_header (s.as_string_8, ic.item)
|
||||
else
|
||||
l_context_executor.add_header (utf.utf_32_string_to_utf_8_string_8 (s), ic.item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
body_contents: READABLE_STRING_8
|
||||
do
|
||||
if attached payload as l_payload then
|
||||
Result := l_payload
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
as_uri (a_string: READABLE_STRING_8): URI
|
||||
require
|
||||
is_valid_uri: is_valid_uri (a_string)
|
||||
do
|
||||
create Result.make_from_string (a_string)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
97
library/http_client_extension/src/request/request_executor.e
Normal file
97
library/http_client_extension/src/request/request_executor.e
Normal file
@@ -0,0 +1,97 @@
|
||||
note
|
||||
description: "Executes an HTTP request"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
|
||||
class
|
||||
REQUEST_EXECUTOR
|
||||
|
||||
inherit
|
||||
|
||||
HTTP_CLIENT_HELPER
|
||||
|
||||
HTTP_CONSTANTS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_url: READABLE_STRING_8; a_method: READABLE_STRING_8)
|
||||
do
|
||||
set_base_url (a_url)
|
||||
verb := a_method
|
||||
ensure
|
||||
base_url_set: base_url.same_string (a_url)
|
||||
method_set: verb.same_string (a_method)
|
||||
end
|
||||
|
||||
set_base_url (a_url: READABLE_STRING_8)
|
||||
-- Set base_url with `a_url'
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
create s.make_from_string (a_url)
|
||||
s.left_adjust
|
||||
s.right_adjust
|
||||
base_url := s
|
||||
ensure
|
||||
base_url_set: a_url.has_substring (base_url)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
verb: READABLE_STRING_8
|
||||
-- HTTP METHOD (Get, Post, ...)
|
||||
|
||||
body: detachable READABLE_STRING_8
|
||||
-- body content
|
||||
|
||||
feature -- Element Change
|
||||
|
||||
set_body (a_body: like body)
|
||||
-- Set body with `a_body'.
|
||||
do
|
||||
body := a_body
|
||||
ensure
|
||||
body_set: body = a_body
|
||||
end
|
||||
|
||||
feature -- Execute
|
||||
|
||||
execute: detachable HTTP_CLIENT_RESPONSE
|
||||
-- Http executor
|
||||
do
|
||||
if verb.same_string (method_connect) then
|
||||
Result := Void -- not supported for now
|
||||
elseif verb.same_string (method_delete) then
|
||||
Result := execute_delete ("")
|
||||
elseif verb.same_string (method_get) then
|
||||
Result := execute_get ("")
|
||||
elseif verb.same_string (method_head) then
|
||||
Result := Void
|
||||
elseif verb.same_string (method_options) then
|
||||
Result := Void
|
||||
elseif verb.same_string (method_patch) then
|
||||
Result := execute_patch ("", body)
|
||||
elseif verb.same_string (method_post) then
|
||||
Result := execute_post ("", body)
|
||||
elseif verb.same_string (method_put) then
|
||||
Result := execute_put ("", body)
|
||||
elseif verb.same_string (method_trace) then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
57
library/http_client_extension/src/response/response.e
Normal file
57
library/http_client_extension/src/response/response.e
Normal file
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "Represent and HTTP Response"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
|
||||
class
|
||||
RESPONSE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} --Initialization
|
||||
|
||||
make (a_response: HTTP_CLIENT_RESPONSE)
|
||||
do
|
||||
http_response := a_response
|
||||
body := a_response.body
|
||||
status := a_response.status
|
||||
headers := a_response.headers
|
||||
status_message := a_response.status_line
|
||||
error_message := a_response.error_message
|
||||
ensure
|
||||
http_reponse_set: http_response = a_response
|
||||
headers_set: headers = a_response.headers
|
||||
status_set: status = a_response.status
|
||||
status_message_set: status_message = a_response.status_line
|
||||
error_message_set: error_message = a_response.error_message
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
status: INTEGER
|
||||
|
||||
status_message: detachable READABLE_STRING_8
|
||||
|
||||
error_message: detachable READABLE_STRING_8
|
||||
|
||||
body: detachable READABLE_STRING_8
|
||||
|
||||
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
http_response: HTTP_CLIENT_RESPONSE;
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
99
library/http_client_extension/src/util/http_client_helper.e
Normal file
99
library/http_client_extension/src/util/http_client_helper.e
Normal file
@@ -0,0 +1,99 @@
|
||||
note
|
||||
description: "Wrapper class for HTTP_CLIENT_SESSION"
|
||||
date: "$Date: 2015-10-08 07:51:29 -0300 (ju., 08 oct. 2015) $"
|
||||
revision: "$Revision: 97966 $"
|
||||
|
||||
deferred class
|
||||
HTTP_CLIENT_HELPER
|
||||
|
||||
feature -- Access
|
||||
|
||||
http_session: detachable HTTP_CLIENT_SESSION
|
||||
|
||||
get_http_session
|
||||
local
|
||||
h: LIBCURL_HTTP_CLIENT
|
||||
b: like base_url
|
||||
do
|
||||
create h.make
|
||||
b := base_url
|
||||
if b = Void then
|
||||
b := ""
|
||||
end
|
||||
if attached {HTTP_CLIENT_SESSION} h.new_session (base_url) as sess then
|
||||
http_session := sess
|
||||
sess.set_timeout (-1)
|
||||
sess.set_connect_timeout (-1)
|
||||
sess.set_is_insecure (True)
|
||||
sess.set_any_auth_type
|
||||
debug ("curl")
|
||||
sess.set_is_debug (True)
|
||||
end
|
||||
debug ("proxy8888")
|
||||
sess.set_proxy ("127.0.0.1", 8888) --| inspect traffic with http://www.fiddler2.com/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP client helpers
|
||||
|
||||
execute_get (command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
get_http_session
|
||||
if attached http_session as sess then
|
||||
Result := sess.get (command_name, context_executor)
|
||||
end
|
||||
end
|
||||
|
||||
execute_post (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
get_http_session
|
||||
if attached http_session as sess then
|
||||
Result := sess.post (command_name, context_executor, data)
|
||||
end
|
||||
end
|
||||
|
||||
execute_delete (command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
get_http_session
|
||||
if attached http_session as sess then
|
||||
Result := sess.delete (command_name, context_executor)
|
||||
end
|
||||
end
|
||||
|
||||
execute_put (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
get_http_session
|
||||
if attached http_session as sess then
|
||||
Result := sess.put (command_name, context_executor, data)
|
||||
end
|
||||
end
|
||||
|
||||
execute_patch (command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
get_http_session
|
||||
if attached http_session as sess then
|
||||
Result := sess.patch (command_name, context_executor, data)
|
||||
end
|
||||
end
|
||||
|
||||
context_executor: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
-- request context for each request
|
||||
once
|
||||
create Result.make
|
||||
end
|
||||
|
||||
base_url: STRING;
|
||||
|
||||
note
|
||||
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
@@ -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
|
||||
@@ -99,15 +108,19 @@ feature -- Security
|
||||
Result.force ("admin roles")
|
||||
Result.force ("admin modules")
|
||||
Result.force ("install modules")
|
||||
Result.force ("admin core caches")
|
||||
Result.force ("clear blocks cache")
|
||||
Result.force ("admin export")
|
||||
Result.force ("export core")
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
register_hooks (a_response: CMS_RESPONSE)
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
-- <Precursor>
|
||||
do
|
||||
a_response.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)
|
||||
@@ -127,14 +140,20 @@ feature -- Hooks
|
||||
create lnk.make ("Admin", "admin")
|
||||
lnk.set_permission_arguments (<<"manage " + {CMS_ADMIN_MODULE}.name>>)
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
|
||||
end
|
||||
if
|
||||
a_response.has_permission ("admin cache") -- Note: admin user has all permissions enabled by default.
|
||||
then
|
||||
create lnk.make ("Cache", "admin/cache")
|
||||
lnk.set_permission_arguments (<<"admin cache">>)
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
end
|
||||
|
||||
create lnk.make ("Module", "admin/modules")
|
||||
lnk.set_permission_arguments (<<"manage module">>)
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
|
||||
-- Per module cache permission!
|
||||
create lnk.make ("Cache", "admin/cache")
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
|
||||
-- Per module export permission!
|
||||
create lnk.make ("Export", "admin/export")
|
||||
a_menu_system.management_menu.extend (lnk)
|
||||
end
|
||||
|
||||
note
|
||||
|
||||
@@ -42,14 +42,10 @@ feature -- Execution
|
||||
f: CMS_FORM
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
if l_response.has_permission ("admin cache") then
|
||||
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)
|
||||
l_response.set_main_content (s)
|
||||
else
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
end
|
||||
f := clear_cache_web_form (l_response)
|
||||
create s.make_empty
|
||||
f.append_to_html (l_response.wsf_theme, s)
|
||||
l_response.set_main_content (s)
|
||||
l_response.execute
|
||||
end
|
||||
|
||||
@@ -60,26 +56,22 @@ feature -- Execution
|
||||
f: CMS_FORM
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
if l_response.has_permission ("admin cache") then
|
||||
f := clear_cache_web_form (l_response)
|
||||
f.process (l_response)
|
||||
if
|
||||
attached f.last_data as fd and then
|
||||
fd.is_valid
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_clear_all_caches) then
|
||||
l_response.hooks.invoke_clear_cache (Void, l_response)
|
||||
l_response.add_notice_message ("Cache cleared!")
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
f := clear_cache_web_form (l_response)
|
||||
f.process (l_response)
|
||||
if
|
||||
attached f.last_data as fd and then
|
||||
fd.is_valid
|
||||
then
|
||||
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_clear_all_caches) then
|
||||
api.hooks.invoke_clear_cache (Void, l_response)
|
||||
l_response.add_notice_message ("Caches cleared (if allowed)!")
|
||||
else
|
||||
fd.report_error ("Invalid form data!")
|
||||
end
|
||||
create s.make_empty
|
||||
f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s)
|
||||
l_response.set_main_content (s)
|
||||
else
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
|
||||
end
|
||||
create s.make_empty
|
||||
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
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
|
||||
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
|
||||
|
||||
<library name="email_service" location="..\..\library\email\email-safe.ecf"/>
|
||||
|
||||
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
|
||||
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
|
||||
150
modules/auth/cms_authentication_email_service.e
Normal file
150
modules/auth/cms_authentication_email_service.e
Normal file
@@ -0,0 +1,150 @@
|
||||
note
|
||||
description: "Summary description for {CMS_AUTHENTICATION_EMAIL_SERVICE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_params: like parameters)
|
||||
-- Create instance of email service with `a_params' data.
|
||||
do
|
||||
parameters := a_params
|
||||
initialize
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize service.
|
||||
do
|
||||
create error_handler.make
|
||||
reset_error
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
parameters: CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS
|
||||
-- Associated parameters.
|
||||
|
||||
cms_api: CMS_API
|
||||
do
|
||||
Result := parameters.cms_api
|
||||
end
|
||||
|
||||
contact_email_address: IMMUTABLE_STRING_8
|
||||
-- contact email.
|
||||
do
|
||||
Result := parameters.contact_email_address
|
||||
end
|
||||
|
||||
notif_email_address: IMMUTABLE_STRING_8
|
||||
-- Site admin's email.
|
||||
do
|
||||
Result := parameters.notif_email_address
|
||||
end
|
||||
|
||||
sender_email_address: IMMUTABLE_STRING_8
|
||||
-- Site sender's email.
|
||||
do
|
||||
Result := parameters.sender_email_address
|
||||
end
|
||||
|
||||
feature -- Error
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
|
||||
has_error: BOOLEAN
|
||||
do
|
||||
Result := error_handler.has_error
|
||||
end
|
||||
|
||||
reset_error
|
||||
do
|
||||
error_handler.reset
|
||||
end
|
||||
|
||||
feature -- Basic Operations / Internal
|
||||
|
||||
send_internal_email (a_content: READABLE_STRING_GENERAL)
|
||||
do
|
||||
send_message (sender_email_address, notif_email_address, "Notification Contact", a_content)
|
||||
end
|
||||
|
||||
send_email_internal_server_error (a_content: READABLE_STRING_GENERAL)
|
||||
do
|
||||
send_message (sender_email_address, notif_email_address, "Internal Server Error", a_content)
|
||||
end
|
||||
|
||||
feature -- Basic Operations / Contact
|
||||
|
||||
send_contact_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_activation)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email_address, a_to, parameters.contact_subject_register, l_message)
|
||||
end
|
||||
|
||||
send_contact_activation_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_re_activation)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email_address, a_to, parameters.contact_subject_activate, l_message)
|
||||
end
|
||||
|
||||
send_contact_password_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_password)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email_address, a_to, parameters.contact_subject_password, l_message)
|
||||
end
|
||||
|
||||
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_welcome)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email_address, a_to, parameters.contact_subject_oauth, l_message)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_message (a_from_address, a_to_address: READABLE_STRING_8; a_subjet: READABLE_STRING_GENERAL; a_content: READABLE_STRING_GENERAL)
|
||||
local
|
||||
l_email: CMS_EMAIL
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
reset_error
|
||||
l_email := cms_api.new_email (a_to_address, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet), utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
|
||||
l_email.set_from_address (a_from_address)
|
||||
l_email.add_header_line ("MIME-Version:1.0")
|
||||
l_email.add_header_line ("Content-Type: text/html; charset=utf-8")
|
||||
cms_api.process_email (l_email)
|
||||
if cms_api.has_error then
|
||||
error_handler.add_custom_error (-1, generator + "send_message failed", cms_api.string_representation_of_errors)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -6,9 +6,6 @@ note
|
||||
class
|
||||
CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS
|
||||
|
||||
inherit
|
||||
EMAIL_SERVICE_PARAMETERS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -23,19 +20,15 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
cms_api := a_cms_api
|
||||
-- Use global smtp setting if any, otherwise "localhost"
|
||||
smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost"))
|
||||
l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
|
||||
admin_email := a_cms_api.setup.site_email
|
||||
notif_email_address := a_cms_api.setup.site_notification_email
|
||||
sender_email_address := a_cms_api.setup.site_email
|
||||
|
||||
if not admin_email.has ('<') then
|
||||
admin_email := l_site_name + " <" + admin_email +">"
|
||||
if not notif_email_address.has ('<') then
|
||||
notif_email_address := l_site_name + " <" + notif_email_address + ">"
|
||||
end
|
||||
|
||||
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
|
||||
if attached cfg.text_item ("smtp") as l_smtp then
|
||||
-- Overwrite global smtp setting if any.
|
||||
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
|
||||
end
|
||||
if attached a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
|
||||
s := cfg.text_item ("email")
|
||||
if s /= Void then
|
||||
l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
@@ -56,15 +49,14 @@ feature {NONE} -- Initialization
|
||||
if s /= Void then
|
||||
l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
|
||||
end
|
||||
if l_contact_email /= Void then
|
||||
if not l_contact_email.has ('<') then
|
||||
l_contact_email := l_site_name + " <" + l_contact_email + ">"
|
||||
end
|
||||
contact_email := l_contact_email
|
||||
contact_email_address := l_contact_email
|
||||
else
|
||||
contact_email := admin_email
|
||||
contact_email_address := notif_email_address
|
||||
end
|
||||
if l_subject_register /= Void then
|
||||
contact_subject_register := l_subject_register
|
||||
@@ -87,18 +79,17 @@ feature {NONE} -- Initialization
|
||||
else
|
||||
contact_subject_oauth := "Welcome."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
cms_api: CMS_API
|
||||
|
||||
smtp_server: IMMUTABLE_STRING_8
|
||||
notif_email_address: IMMUTABLE_STRING_8
|
||||
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
sender_email_address: IMMUTABLE_STRING_8
|
||||
|
||||
contact_email: IMMUTABLE_STRING_8
|
||||
contact_email_address: IMMUTABLE_STRING_8
|
||||
-- Contact email.
|
||||
|
||||
contact_subject_register: IMMUTABLE_STRING_8
|
||||
|
||||
@@ -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)
|
||||
@@ -123,6 +130,7 @@ feature -- Hooks configuration
|
||||
lnk.set_weight (98)
|
||||
a_menu_system.primary_menu.extend (lnk)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
feature -- Handler
|
||||
@@ -176,7 +184,7 @@ feature -- Handler
|
||||
l_user_api: CMS_USER_API
|
||||
u: CMS_USER
|
||||
l_exist: BOOLEAN
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
es: CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
l_url: STRING
|
||||
l_token: STRING
|
||||
do
|
||||
@@ -227,6 +235,7 @@ feature -- Handler
|
||||
end
|
||||
else
|
||||
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("You can also contact the webmaster to ask for an account.")
|
||||
end
|
||||
|
||||
r.execute
|
||||
@@ -264,7 +273,7 @@ feature -- Handler
|
||||
handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
es: CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
l_user_api: CMS_USER_API
|
||||
l_token: STRING
|
||||
l_url: STRING
|
||||
@@ -304,7 +313,7 @@ feature -- Handler
|
||||
handle_new_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
es: CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
l_user_api: CMS_USER_API
|
||||
l_token: STRING
|
||||
l_url: STRING
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {CMS_AUTHENTICATON_EMAIL_SERVICE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
|
||||
inherit
|
||||
EMAIL_SERVICE
|
||||
redefine
|
||||
initialize,
|
||||
parameters
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
contact_email := parameters.contact_email
|
||||
end
|
||||
|
||||
parameters: CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS
|
||||
-- Associated parameters.
|
||||
|
||||
feature -- Access
|
||||
|
||||
contact_email: IMMUTABLE_STRING_8
|
||||
-- contact email.
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
send_contact_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_activation)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_register, l_message)
|
||||
end
|
||||
|
||||
|
||||
send_contact_activation_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_re_activation)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_activate, l_message)
|
||||
end
|
||||
|
||||
|
||||
send_contact_password_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_password)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_password, l_message)
|
||||
end
|
||||
|
||||
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_welcome)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -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
|
||||
@@ -46,9 +46,13 @@ feature -- Access
|
||||
-- List of permission ids, used by this module, and declared.
|
||||
do
|
||||
Result := Precursor
|
||||
Result.force ("manage feed aggregator")
|
||||
Result.force (permission__manage_feed_aggregator)
|
||||
Result.force (permission__clear_feed_cache)
|
||||
end
|
||||
|
||||
permission__manage_feed_aggregator: STRING = "manage feed aggregator"
|
||||
permission__clear_feed_cache: STRING = "clear feed cache"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (api: CMS_API)
|
||||
@@ -177,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
|
||||
@@ -194,12 +198,14 @@ feature -- Hook
|
||||
p: PATH
|
||||
dir: DIRECTORY
|
||||
do
|
||||
if a_cache_id_list = Void then
|
||||
-- Clear all cache.
|
||||
p := a_response.api.files_location.extended (".cache").extended (name)
|
||||
create dir.make_with_path (p)
|
||||
if dir.exists then
|
||||
dir.recursive_delete
|
||||
if a_response.has_permissions (<<permission__clear_feed_cache, permission__manage_feed_aggregator>>) then
|
||||
if a_cache_id_list = Void then
|
||||
-- Clear all cache.
|
||||
p := a_response.api.files_location.extended (".cache").extended (name)
|
||||
create dir.make_with_path (p)
|
||||
if dir.exists then
|
||||
dir.recursive_delete
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -341,7 +347,7 @@ feature -- Hook
|
||||
-- for related response `a_response'.
|
||||
do
|
||||
a_menu_system.navigation_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds", "feed_aggregation/"))
|
||||
if a_response.has_permission ("manage feed aggregator") then
|
||||
if a_response.has_permission (permission__manage_feed_aggregator) then
|
||||
a_menu_system.management_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds (admin)", "admin/feed_aggregator/"))
|
||||
end
|
||||
end
|
||||
|
||||
1
modules/google_search/Readme.md
Normal file
1
modules/google_search/Readme.md
Normal file
@@ -0,0 +1 @@
|
||||
Google Custom Search Module.
|
||||
18
modules/google_search/google_search-safe.ecf
Normal file
18
modules/google_search/google_search-safe.ecf
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="google_search" uuid="054E9C5C-ACCB-4A4D-B825-6C574AEC30A9" library_target="google_search">
|
||||
<target name="google_search">
|
||||
<root all_classes="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="..\..\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"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-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_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
20
modules/google_search/google_search.ecf
Normal file
20
modules/google_search/google_search.ecf
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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="google_search" uuid="054E9C5C-ACCB-4A4D-B825-6C574AEC30A9" library_target="google_search">
|
||||
<target name="google_search">
|
||||
<root all_classes="true"/>
|
||||
<option is_attached_by_default="false" void_safety="none">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.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"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
|
||||
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
6
modules/google_search/site/config/google_search.json
Normal file
6
modules/google_search/site/config/google_search.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"gcse": {
|
||||
"cx":"",
|
||||
"secret_key":""
|
||||
}
|
||||
}
|
||||
40
modules/google_search/site/templates/block_search.tpl
Normal file
40
modules/google_search/site/templates/block_search.tpl
Normal file
@@ -0,0 +1,40 @@
|
||||
<section>
|
||||
<header>
|
||||
<h2>Results for <kbd>{$result.current_page.search_terms/}</kbd></h2>
|
||||
</header>
|
||||
|
||||
<!-- list of results -->
|
||||
<ol start="{$result.current_page.start_index/}">
|
||||
|
||||
<!-- Item result -->
|
||||
{foreach from="$result.items" item="item"}
|
||||
<li>
|
||||
<article>
|
||||
<header>
|
||||
<h3>
|
||||
<cite>
|
||||
<a href="{$item.link/}">{$item.title/}</a>
|
||||
</cite>
|
||||
</h3>
|
||||
</header>
|
||||
<blockquote cite="{$item.link/}">
|
||||
<p>{$item.html_snippet/}</p>
|
||||
<footer>
|
||||
<p><abbr title="Uniform Resource Locator">Source</abbr> <a href="{$item.link/}">{$item.display_link/}</a></p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</article>
|
||||
</li>
|
||||
{/foreach}
|
||||
|
||||
|
||||
</ol>
|
||||
<ul class="cms-page-links">
|
||||
{if isset="$result.previous_page"}
|
||||
<li><a href="{$site_url/}gcse/?q={$result.previous_page.search_terms/}&start={$result.previous_page.start_index/}&num={$result.previous_page.count/}">Previous</a></li>
|
||||
{/if}
|
||||
{if isset="$result.next_page"}
|
||||
<li><a href="{$site_url/}gcse/?q={$result.next_page.search_terms/}&start={$result.next_page.start_index/}&num={$result.next_page.count/}">Next</a></li>
|
||||
{/if}
|
||||
</ul>
|
||||
</section>
|
||||
150
modules/google_search/src/google_custom_search_module.e
Normal file
150
modules/google_search/src/google_custom_search_module.e
Normal file
@@ -0,0 +1,150 @@
|
||||
note
|
||||
description: "[
|
||||
Module providing Google Custom Search functionality.
|
||||
]"
|
||||
date: "$Date: 2015-10-09 20:50:01 -0300 (vi. 09 de oct. de 2015) $"
|
||||
revision: "$Revision: 97982 $"
|
||||
|
||||
class
|
||||
GOOGLE_CUSTOM_SEARCH_MODULE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_MODULE
|
||||
|
||||
CMS_HOOK_BLOCK_HELPER
|
||||
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_LOGGER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Create current module
|
||||
do
|
||||
version := "1.0"
|
||||
description := "Google custome search module"
|
||||
package := "search"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "google_search"
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- Router configuration.
|
||||
do
|
||||
a_router.handle ("/gcse", create {WSF_URI_AGENT_HANDLER}.make (agent handle_search (a_api, ?, ?)), a_router.methods_head_get)
|
||||
end
|
||||
|
||||
feature -- Recaptcha
|
||||
|
||||
gcse_secret_key (api: CMS_API): detachable READABLE_STRING_8
|
||||
-- Get recaptcha security key.
|
||||
local
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
if attached api.module_configuration (Current, Void) as cfg then
|
||||
if
|
||||
attached cfg.text_item ("gcse.secret_key") as l_recaptcha_key and then
|
||||
not l_recaptcha_key.is_empty
|
||||
then
|
||||
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gcse_cx_key (api: CMS_API): detachable READABLE_STRING_8
|
||||
-- Get recaptcha security key.
|
||||
local
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
if attached api.module_configuration (Current, Void) as cfg then
|
||||
if
|
||||
attached cfg.text_item ("gcse.cx") as l_recaptcha_key and then
|
||||
not l_recaptcha_key.is_empty
|
||||
then
|
||||
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Handler
|
||||
|
||||
handle_search (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_parameters:GCSE_QUERY_PARAMETERS
|
||||
l_search: GCSE_API
|
||||
do
|
||||
-- TODO handle errors!!!
|
||||
write_debug_log (generator + ".handle_search")
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
if
|
||||
attached {WSF_STRING} req.query_parameter ("q") as l_query and then
|
||||
not l_query.value.is_empty
|
||||
then
|
||||
if
|
||||
attached gcse_cx_key (api) as l_cx and then
|
||||
attached gcse_secret_key (api) as l_key
|
||||
then
|
||||
create l_parameters.make (l_key, l_cx, l_query.url_encoded_value )
|
||||
if
|
||||
attached {WSF_STRING} req.query_parameter ("start") as l_index and then
|
||||
attached {WSF_STRING} req.query_parameter ("num") as l_num
|
||||
then
|
||||
l_parameters.set_start (l_index.value)
|
||||
l_parameters.set_num (l_num.value)
|
||||
end
|
||||
create l_search.make (l_parameters)
|
||||
l_search.search
|
||||
if
|
||||
attached l_search.last_result as l_result and then
|
||||
l_result.status = 200
|
||||
then
|
||||
if attached template_block (Current, "search", r) as l_tpl_block then
|
||||
l_tpl_block.set_value (l_result, "result")
|
||||
r.add_block (l_tpl_block, "content")
|
||||
end
|
||||
else
|
||||
-- Quota limit (403 status code) or not results.
|
||||
google_search_site (req, r, l_query)
|
||||
end
|
||||
else
|
||||
-- If no key are provided, at least output google search result page.
|
||||
google_search_site (req, r, l_query)
|
||||
end
|
||||
else
|
||||
r.add_message ("No query submitted", Void)
|
||||
end
|
||||
r.execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Helper
|
||||
|
||||
google_search_site (req: WSF_REQUEST; res: CMS_RESPONSE; query: WSF_STRING)
|
||||
-- Workaround to output google search result page
|
||||
-- If no key are provided or if GCSE reached the quota limit.
|
||||
local
|
||||
l_url_encoder: URL_ENCODER
|
||||
do
|
||||
create l_url_encoder
|
||||
if req.is_https then
|
||||
res.set_redirection ("https://www.google.com/search?sitesearch=" + l_url_encoder.general_encoded_string (res.absolute_url ("", Void)) + "&q=" + query.url_encoded_value)
|
||||
else
|
||||
res.set_redirection ("http://www.google.com/search?sitesearch=" + l_url_encoder.general_encoded_string (res.absolute_url ("", Void)) + "&q=" + query.url_encoded_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
@@ -427,7 +430,7 @@ feature -- OAuth2 Login with Provider
|
||||
l_user: CMS_USER
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
l_cookie: WSF_COOKIE
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
es: CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as l_callback and then
|
||||
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
|
||||
|
||||
@@ -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
|
||||
@@ -387,7 +390,7 @@ feature -- Openid Login
|
||||
l_user: CMS_USER
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
l_cookie: WSF_COOKIE
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
es: CMS_AUTHENTICATION_EMAIL_SERVICE
|
||||
b: STRING
|
||||
o: OPENID_CONSUMER
|
||||
v: OPENID_CONSUMER_VALIDATION
|
||||
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user