Compare commits

...

27 Commits

Author SHA1 Message Date
167ac563aa List user roles for each users in the admin list of users. 2016-05-18 11:02:16 +02:00
f4ac4be684 Fixed role changes for an user. 2016-05-18 11:01:54 +02:00
12a3898487 Decode url for redirection purpose during authentication. 2016-04-27 16:07:24 +02:00
0e3419fea0 Do not try to redirect to previous page or destination on logout! 2016-04-13 23:12:11 +02:00
816f0eb820 Improved session auth module routing. 2016-04-13 12:53:10 +02:00
bd3fe63976 Fixed "destination" support when login.
(i.e when visitor click on signin from page A, one he is signed, he will be redirected to the initial page A.)
2016-04-13 10:56:28 +02:00
0c7d8af9d7 Added support for CMS_BLOCK.is_raw in custom block module. 2016-02-19 00:53:47 +01:00
f80268c1ac Updated CMS_HOOK_BLOCK, to provide a better block_identifiers (CMS_RESPONSE): detachable ITERABLE [READABLE_STRING_8] query, to be implemented by CMS_HOOK_RESPONSE_BLOCK descendants.
Added CONFIG_READER.table_keys: detachable LIST [READABLE_STRING_32].
2016-02-19 00:10:09 +01:00
77e2c28d18 Added logs admin viewer.
Added CMS_SETUP.is_debug: BOOLEAN  (see cms.ini   site.debug setting)
2016-02-19 00:03:15 +01:00
af137629e0 Restored previous template_block* function signatures and made them obsolete.
Added smarty_template_block* functions as new recommended functions.
2016-02-18 16:26:00 +01:00
a7f1f14b8a Added missing change to installed file for demo example. 2016-02-18 16:14:58 +01:00
fbda2c9eb2 Reuse improved CMS_HOOK_BLOCK_HELPER to help creation of block templates.
Fixed typo in basic auth login form.
2016-02-18 16:13:58 +01:00
ff58593bff Added functions to get link from menu or link composite.
Improved management menu, but using sub menu.
2016-02-18 12:47:21 +01:00
c65f5765d6 Removed unwanted auth.json file (wrong directory). 2016-02-17 16:11:50 +01:00
19565b9c98 Updated registration form. 2016-02-17 16:07:20 +01:00
6716cb5575 Protected cache, export and feeds menu link. 2016-02-17 15:20:38 +01:00
75332c148d Added protection/permissions. 2016-02-17 12:24:58 +01:00
b54fd85172 Added files module, with for now, a focus on upload files facility.
Contribution from Fabian Murer, as part of an ETH student project.
Supervised, refactorized and merged by Jocelyn Fiat.

Signed-off-by: Fabian Murer <fmurer@student.ethz.ch>
Signed-off-by: Jocelyn Fiat <git@djoce.net>
2016-02-17 12:03:24 +01:00
bc07aad01b Restored iron package name to "roc" 2016-02-12 09:03:56 +01:00
ce4bb551d2 Updated package.iron . 2016-02-04 00:01:32 +01:00
5ceb9d3dd3 Fixed new account admin message.
- the rejection url was badly formatted in the template.
2016-02-03 23:59:55 +01:00
c1a5838320 Fixed auth email messages code and template. 2016-02-03 23:51:20 +01:00
db697cec3e Fixed auth mail template text and code. 2016-02-03 23:33:52 +01:00
892f2331de Do not set destination query parameter to any account/auth url.
Set "site_sign_in_url" and "site_sign_out_url" as variables (so it could be used by template).
2016-02-03 23:16:05 +01:00
3496536751 Added CMS_API.request: WSF_REQUEST to ease dev of ROC CMS code.
- Removed CMS_REQUEST_UTIL
  - centralize a few request related code into CMS_API
Added CMS_API.user, CMS_API.set_user (CMS_USER), ... and user related routines.

Refactored Auth related code
  - added various abstractions to factorize implementation and harmonize solutions.
  - revisited the logout strategy.
  - updated the account info page, and remove info user should not care about.
  - simplified the process, and encourage auth module to follow same design.

Added CMS_LINK helper routines to modify the related query string.
Removed CMS_USER.profile (and related routines)
   - It was not used so far.
   - it will probably a specific module later, if needed.

Update various module to avoid fetching user from sql directly, and let this task to CMS_USER_API.

Removed CMS_NODE_API.node_author (a_node: CMS_NODE): detachable CMS_USER,
   - as the info is already in CMS_NODE.author

Added CMS_RESPONSE.redirection_delay, if ever one code want to redirect after a few seconds.
Added the request uri info to the not found cms response.
2016-01-29 21:58:49 +01:00
41ac45d07b Fixed various CMS_MODULE.install, by not marked module installed if an error occurred!
Improved Auth related module implementation by having a way to change settings like token, max age.
  - use CMS_SETUP.site_id and related "auth.$module.token" ... configuration values.
  - removed related CMS_..._CONSTANTS classes.

For auth session module, use auth_session as table name, and use VARCHAR(64).
Extracted sql from blog module, and store it under site/scripts/install.sql .
Renamed a few $modulename.sql as install.sql
2016-01-27 18:22:20 +01:00
d3b485f4d3 Removed unused local variable. 2016-01-22 22:19:24 +01:00
157 changed files with 12288 additions and 2543 deletions

View File

@@ -8,7 +8,7 @@
<exclude>/CVS$</exclude> <exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude> <exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional"> <option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/> <debug name="dbglog" enabled="true"/>
</option> </option>
<setting name="executable_name" value="demo"/> <setting name="executable_name" value="demo"/>
@@ -25,9 +25,11 @@
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/> <library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
<library name="cms_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/> <library name="cms_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_contact_module" location="..\..\modules\contact\contact-safe.ecf" readonly="false"/> <library name="cms_contact_module" location="..\..\modules\contact\contact-safe.ecf" readonly="false"/>
<library name="cms_custom_block_module" location="..\..\modules\custom_block\custom_block-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/> <library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-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_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
<library name="cms_files_module" location="..\..\modules\files\files-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_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_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/> <library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
@@ -37,12 +39,9 @@
<library name="cms_seo_module" location="..\..\modules\seo\seo-safe.ecf" readonly="false"/> <library name="cms_seo_module" location="..\..\modules\seo\seo-safe.ecf" readonly="false"/>
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/> <library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/> <library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false"> <library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false"/>
<option>
<assertions/>
</option>
</library>
<!-- <!--
By default, commented, since it depends on specific environment settings.
<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" /> <library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" />
--> -->

View File

@@ -1,2 +1,3 @@
port=9090 port=9090
#port=12345
#verbose=true #verbose=true

View File

@@ -1,3 +1,4 @@
@echo off
setlocal setlocal
set ROC_CMD=call %~dp0..\..\tools\roc.bat set ROC_CMD=call %~dp0..\..\tools\roc.bat
set ROC_CMS_DIR=%~dp0 set ROC_CMS_DIR=%~dp0
@@ -7,11 +8,14 @@ set ROC_CMS_DIR=%~dp0
%ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\contact --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\contact --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\node --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\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%
%ROC_CMD% install --module ..\..\modules\seo --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\seo --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\session_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\files --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\custom_block --dir %ROC_CMS_DIR%

View File

@@ -70,8 +70,9 @@ CREATE TABLE tb_demo(
api.logger.put_error ("Could not initialize database for demo module", generating_type) api.logger.put_error ("Could not initialize database for demo module", generating_type)
end end
end end
Precursor {CMS_MODULE}(api)
end end
-- For this demo, be flexible, and do not required sql.
Precursor {CMS_MODULE}(api)
end end
feature -- Access: router feature -- Access: router

View File

@@ -2,8 +2,8 @@
#navigation.region=sidebar_first #navigation.region=sidebar_first
#navigation.condition=is_front #navigation.condition=is_front
management.conditions[]=path:admin* #management.conditions[]=path:admin*
management.conditions[]=is_front #management.conditions[]=is_front
#Feeds #Feeds
feed.news.weight=3 feed.news.weight=3

View File

@@ -4,9 +4,14 @@ root-dir=site/www
#modules-dir=site/modules #modules-dir=site/modules
[site] [site]
# General token that could be use for cookies, and related.
id=_EIFFEL_CMS_
#debug=true
# Name of the site, for the title, and eventual message. # Name of the site, for the title, and eventual message.
name=Eiffel CMS name=Eiffel CMS
# Properties used for SEO.
property[headline]=Eiffel CMS -- the demo property[headline]=Eiffel CMS -- the demo
property[description]=Demo for Eiffel ROC CMS. property[description]=Demo for Eiffel ROC CMS.
property[keywords]=eiffel,cms,demo property[keywords]=eiffel,cms,demo
@@ -43,6 +48,14 @@ output=site\db\mailer.log
[blocks] [blocks]
@include=blocks.ini @include=blocks.ini
[auth]
# token, default is $site.id or built-in.
#token=_ROC_AUTH_TOKEN_
#session.token=
#session.max_age=86400
#openid.token=
#oauth.token=
[admin] [admin]
# CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none) # CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none)
installation_access=all installation_access=all

View File

@@ -1,5 +1,10 @@
{ {
"subject": "Thank you for contacting us", "subject": "Thank you for contacting us",
"forms": {
"registration": {
"application_description": "Present yourself in a few lines, otherwise your application is likely to be rejected."
}
},
"recaptcha": { "recaptcha": {
"site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD", "site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD",
"secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx" "secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx"

View File

@@ -21,6 +21,6 @@
<p>To reject the registration, please click on the following link <p> <p>To reject the registration, please click on the following link <p>
<p><a href="$rejection_url<">$rejection_url</a></p> <p><a href="$rejection_url">$rejection_url</a></p>
</body> </body>
</html> </html>

View File

@@ -0,0 +1 @@
{include file="block_account_info.tpl" /}

View File

@@ -1,62 +1,34 @@
<div class="primary-tabs"> <div class="primary-tabs">
{if isset="$user"} {if isset="$user"}
<h3>Account Information</h3> <h3>Account Information</h3>
<div> <ul class="user-information">
<div> <div>
<div> <label>Username:</label> {$user.name/}
<label>Username:</label> {$user.name/}
</div>
<div>
<label>Email:</label> {$user.email/}
</div>
<div>
<label>Creation Date:</label> {$user.creation_date/}
</div>
<div>
<label>Last login:</label> {$user.last_login_date/}
</div>
<div>
<form method="get" action="{$site_url/}{$auth_login_strategy/}">
<button type="submit">Logout</button>
</form>
</div>
</div> </div>
</div> <div>
<hr> <label>Email:</label> {$user.email/}
{include file="block_change_password.tpl" /} </div>
<hr> <div>
<h4>Roles</h4> <label>Creation Date:</label> {$user.creation_date/} (UTC)
<div> </div>
{foreach item="ic" from="$roles"} <div>
<div> <label>Last login:</label> {$user.last_login_date/} (UTC)
<ul> </div>
<li> <div>
<strong>{$ic.name/}</strong> <form method="get" action="{$site_url/}account/roc-logout">
<ul> <button type="submit">Logout</button>
<li> <i>permissions</i> </form>
<ul> </div>
{foreach item="ip" from="$ic.permissions"} </ul>
<li>{$ip/}</li>
{/foreach}
</ul>
</li>
</ul>
</li>
</ul>
</div>
{/foreach}
</div>
<hr> <hr>
<h4>Profile</h4> <h4>Profile</h4>
<div> <ul class="user-profile">
{foreach item="the_value" key="the_name" from="$user.profile"} {foreach item="the_value" key="the_name" from="$user.profile"}
<div> <li>
<label>{$the_name/}:</label> {$the_value/} <label>{$the_name/}:</label><div>{$the_value/}</div>
</div> </li>
{/foreach} {/foreach}
</div> </ul>
{/if} {/if}
{unless isset="$user"} {unless isset="$user"}
<div> <div>

View File

@@ -1,7 +1,7 @@
<div> <div>
<form action="{$site_url/}account/change-password" method="post"> <form action="{$site_url/}account/change-password" method="post">
<fieldset> <fieldset>
<legend>Change Password Form</legend> <legend>Change Password</legend>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>

View File

@@ -1,29 +0,0 @@
<div class="primary-tabs">
{unless isset="$user"}
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div>
<div>
<form action method="POST">
<div>
<input type="text" name="username" required>
<label>Username</label>
</div>
<div>
<input type="password" name="password" required>
<label>Password</label>
</div>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div>
<div>
<div>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div>
{/unless}
</div>

View File

@@ -1,39 +1,38 @@
<div> <div>
<form action="{$site_url/}account/roc-register" method="post"> <form action="{$site_url/}account/roc-register" method="post">
<fieldset> <fieldset>
<legend>Registration</legend> <legend>Registration</legend>
<div> <div>
<input type="text" id="name" name="name" value="{$name/}" required autofocus /> <input type="text" id="name" name="name" value="{$name/}" required autofocus />
<label for="name">Name</label> <label for="name">Name</label>
{if isset="$error_name"} {if isset="$error_name"}
<span><i>{$error_name/}</i></span> <br> <span><i>{$error_name/}</i></span> <br>
{/if} {/if}
</div> </div>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>
</div> </div>
<div> <div>
<input type="email" id="email" name="email" value="{$email/}" required/> <input type="email" id="email" name="email" value="{$email/}" required/>
<label for="email">Email</label> <label for="email">Email</label>
{if isset="$error_email"} {if isset="$error_email"}
<span><i>{$error_email/}</i></span> <br> <span><i>{$error_email/}</i></span> <br/>
{/if} {/if}
</div> </div>
<div> <div>
<textarea rows="4" cols="50" name="personal_information" id="personal_information" required> <textarea rows="4" cols="50" name="personal_information" id="personal_information" required>{$personal_information/}</textarea>
{$personal_information/} <label for="personal_information">Tell us why you want to register an account</label>
</textarea> {if isset="$error_application"}
<label for="personal_information">Tell us why you want to register an account</label> <span><i>{$error_application/}</i></span><br/>
{if isset="$error_application"} {/if}
<span><i>{$error_application/}</i></span> <br> {if isset="$application_description"}
{/if} <br/>
</div> <p class="description">{$application_description/}</p>
{unless isempty="$recaptcha_site_key"} {/if}
<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div> </div>
<br/> {unless isempty="$recaptcha_site_key"}<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div><br/>{/unless}
{/unless} <button type="submit">Register</button>
<button type="submit">Register</button> </fieldset>
</fieldset> </form>
</form>
</div> </div>

View File

@@ -1,307 +1,291 @@
var ROC_AUTH = ROC_AUTH || { }; var ROC_AUTH = ROC_AUTH || { };
var loginURL = "/basic_auth_login"; var loginURL = "/roc-basic-login";
var logoutURL = "/basic_auth_logoff"; var logoutURL = "/roc-basic-logoff";
var userAgent = navigator.userAgent.toLowerCase(); var userAgent = navigator.userAgent.toLowerCase();
var firstLogIn = true; var firstLogIn = true;
ROC_AUTH.login = function() { ROC_AUTH.login = function() {
var form = document.forms['cms_basic_auth']; var form = document.forms['cms_basic_auth'];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
//var host = form.host.value; //var host = form.host.value;
var origin = window.location.origin + window.location.pathname; var origin = window.location.origin + window.location.pathname;
var _login = function(){ var _login = function(){
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') !== null ) { if (document.getElementById('myModalFormId') === null ) {
ROC_AUTH.remove ('myModalFormId'); var newdiv = document.createElement('div');
} newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
} else {
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
// .. ?
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}
}
}
}
}
if (username === "" || password === "") { var userAgent = navigator.userAgent.toLowerCase();
if (document.getElementById('myModalFormId') === null ) { if (userAgent.indexOf("firefox") != -1) { //TODO: check version number
var newdiv = document.createElement('div'); if (firstLogIn) {
newdiv.innerHTML = "<br>Invalid Credentials</br>"; _login();
newdiv.id = 'myModalFormId'; } else {
$(".primary-tabs").append(newdiv); ROC_AUTH.logoff(_login);
} }
}else{ } else {
_login();
//Instantiate HTTP Request }
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
}
} if (firstLogIn) {
} firstLogIn = false;
} }
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.login_with_redirect = function() { ROC_AUTH.login_with_redirect = function() {
var form = document.forms[2]; var form = document.forms[2];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
var host = form.host.value; var host = form.host.value;
var _login = function(){ var _login = function(){
var redirectURL = form.redirect && form.redirect.value || "";
$("#imgProgressRedirect").show();
var redirectURL = form.redirect && form.redirect.value || ""; if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
} else {
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host + loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host + "/";
} else {
window.location=host + redirectURL;
}
} else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
$("#imgProgressRedirect").show(); var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (document.getElementById('myModalFormId') !== null ) { if (firstLogIn) {
ROC_AUTH.remove ('myModalFormId'); _login();
} } else {
ROC_AUTH.logoff(_login);
}
if (username === "" || password === "") { } else{
if (document.getElementById('myModalFormId') === null ) { _login();
var newdiv = document.createElement('div'); }
newdiv.innerHTML = "<br>Invalid Credentials</br>"; if (firstLogIn) {
newdiv.id = 'myModalFormId'; firstLogIn = false;
$(".primary-tabs").append(newdiv); }
$("#imgProgressRedirect").hide();
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(loginURL), true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host.concat("/");
} else {
window.location=host.concat(redirectURL);
}
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.getQueryParameterByName = function (name) { ROC_AUTH.getQueryParameterByName = function (name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
results = regex.exec(location.search); return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
} }
ROC_AUTH.logoff = function(callback){ ROC_AUTH.logoff = function(callback){
var form = document.forms[0]; var form = document.forms[0];
var host = form.host.value; var host = form.host.value;
if (userAgent.indexOf("msie") != -1) { if (userAgent.indexOf("msie") != -1) {
document.execCommand("ClearAuthenticationCache"); document.execCommand("ClearAuthenticationCache");
} } else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest();
var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest(); //Logout. Tell the server not to return the "WWW-Authenticate" header
request1.open("GET", host + logoutURL + "?prompt=false", true);
//Logout. Tell the server not to return the "WWW-Authenticate" header request1.send("");
request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); request1.onreadystatechange = function(){
request1.send(""); if (request1.readyState == 4) {
request1.onreadystatechange = function(){ //Sign in with dummy credentials to clear the auth cache
if (request1.readyState == 4) { request2.open("GET", host + logoutURL, true, "logout", "logout");
request2.send("");
//Sign in with dummy credentials to clear the auth cache request2.onreadystatechange = function(){
request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); if (request2.readyState == 4) {
request2.send(""); if (callback!=null) {
callback.call();
request2.onreadystatechange = function(){ } else {
if (request2.readyState == 4) { window.location=host + logoutURL;
if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} }
} }
} }
}
} }
} } else {
} var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
else { request.open("GET", host + logoutURL, true, "logout", "logout");
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); request.send("");
request.open("GET", host.concat(logoutURL), true, "logout", "logout"); request.onreadystatechange = function(){
request.send(""); if (request.status==401 || request.status==403 ) {
request.onreadystatechange = function(){ window.location=host + logoutURL;
if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); }
} }
} }
}
}; };
ROC_AUTH.remove = function (id) ROC_AUTH.remove = function (id) {
{ var element = document.getElementById(id);
var element = document.getElementById(id); element.outerHTML = "";
element.outerHTML = ""; delete element;
delete element; return;
return;
}; };
$(document).ready(function() { $(document).ready(function() {
if (typeof String.prototype.contains != 'function') {
if (typeof String.prototype.contains != 'function') { String.prototype.contains = function (str){
String.prototype.contains = function (str){ return this.indexOf(str) != -1;
return this.indexOf(str) != -1; };
}; }
} ROC_AUTH.progressive_loging();
ROC_AUTH.progressive_loging();
}); });
ROC_AUTH.progressive_loging = function () { ROC_AUTH.progressive_loging = function () {
ROC_AUTH.login_href();
ROC_AUTH.login_href();
}; };
$(document).keypress(function(e) { $(document).keypress(function(e) {
if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) {
ROC_AUTH.login(); ROC_AUTH.login();
} }
}); });
ROC_AUTH.OnOneClick = function(event) { ROC_AUTH.OnOneClick = function(event) {
event.preventDefault(); event.preventDefault();
if ( document.forms[0] === undefined ) { if ( document.forms[0] === undefined ) {
ROC_AUTH.create_form(); ROC_AUTH.create_form();
} }
return false; return false;
}; };
ROC_AUTH.login_href = function() { ROC_AUTH.login_href = function() {
var els = document.getElementsByTagName("a"); var els = document.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) { for (var i = 0, l = els.length; i < l; i++) {
var el = els[i]; var el = els[i];
if (el.href.contains("/basic_auth_login?destination")) { if (el.href.contains(loginURL + "?destination")) {
loginURL = el.href; // loginURL = el.href;
var OneClick = el; var OneClick = el;
OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false);
} }
} }
}; };
ROC_AUTH.create_form = function() { ROC_AUTH.create_form = function() {
// Fetching HTML Elements in Variables by ID. // Fetching HTML Elements in Variables by ID.
var createform = document.createElement('form'); // Create New Element Form var createform = document.createElement('form'); // Create New Element Form
createform.setAttribute("action", ""); // Setting Action Attribute on Form createform.setAttribute("action", ""); // Setting Action Attribute on Form
createform.setAttribute("method", "post"); // Setting Method Attribute on Form createform.setAttribute("method", "post"); // Setting Method Attribute on Form
$("body").append(createform); $("body").append(createform);
var heading = document.createElement('h2'); // Heading of Form var heading = document.createElement('h2'); // Heading of Form
heading.innerHTML = "Login Form "; heading.innerHTML = "Login Form ";
createform.appendChild(heading); createform.appendChild(heading);
var line = document.createElement('hr'); // Giving Horizontal Row After Heading var line = document.createElement('hr'); // Giving Horizontal Row After Heading
createform.appendChild(line); createform.appendChild(line);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var namelabel = document.createElement('label'); // Create Label for Name Field var namelabel = document.createElement('label'); // Create Label for Name Field
namelabel.innerHTML = "Username : "; // Set Field Labels namelabel.innerHTML = "Username : "; // Set Field Labels
createform.appendChild(namelabel); createform.appendChild(namelabel);
var inputelement = document.createElement('input'); // Create Input Field for UserName var inputelement = document.createElement('input'); // Create Input Field for UserName
inputelement.setAttribute("type", "text"); inputelement.setAttribute("type", "text");
inputelement.setAttribute("name", "username"); inputelement.setAttribute("name", "username");
inputelement.setAttribute("required","required"); inputelement.setAttribute("required","required");
createform.appendChild(inputelement); createform.appendChild(inputelement);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var passwordlabel = document.createElement('label'); // Create Label for Password Field var passwordlabel = document.createElement('label'); // Create Label for Password Field
passwordlabel.innerHTML = "Password : "; passwordlabel.innerHTML = "Password : ";
createform.appendChild(passwordlabel); createform.appendChild(passwordlabel);
var passwordelement = document.createElement('input'); // Create Input Field for Password. var passwordelement = document.createElement('input'); // Create Input Field for Password.
passwordelement.setAttribute("type", "password"); passwordelement.setAttribute("type", "password");
passwordelement.setAttribute("name", "password"); passwordelement.setAttribute("name", "password");
passwordelement.setAttribute("id", "password"); passwordelement.setAttribute("id", "password");
passwordelement.setAttribute("required","required"); passwordelement.setAttribute("required","required");
createform.appendChild(passwordelement); createform.appendChild(passwordelement);
var passwordbreak = document.createElement('br'); var passwordbreak = document.createElement('br');
createform.appendChild(passwordbreak); createform.appendChild(passwordbreak);
var submitelement = document.createElement('button'); // Append Submit Button var submitelement = document.createElement('button'); // Append Submit Button
submitelement.setAttribute("type", "button"); submitelement.setAttribute("type", "button");
submitelement.setAttribute("onclick", "ROC_AUTH.login();"); submitelement.setAttribute("onclick", "ROC_AUTH.login();");
submitelement.innerHTML = "Sign In "; submitelement.innerHTML = "Sign In ";
createform.appendChild(submitelement); createform.appendChild(submitelement);
}; };
@@ -310,16 +294,16 @@ var password = document.getElementById("password");
var confirm_password = document.getElementById("confirm_password"); var confirm_password = document.getElementById("confirm_password");
ROC_AUTH.validatePassword =function(){ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
if(password.value != confirm_password.value) { if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match"); confirm_password.setCustomValidity("Passwords Don't Match");
} else { } else {
confirm_password.setCustomValidity(''); confirm_password.setCustomValidity('');
} }
} }
} }
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword(); password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword; confirm_password.onkeyup = ROC_AUTH.validatePassword;
} }

View File

@@ -1,29 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Basic Auth" relies on the HTTP basic access authentication.<br/>(see also: <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">https://en.wikipedia.org/wiki/Basic_access_authentication</a> )</div>
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<div> <form name="cms_basic_auth" action="{$site_url/}roc-basic-login" method="POST">
<form name="cms_basic_auth" action method="POST"> <input type="hidden" name="host" id="host" value="{$site_url/}">
<div> <div>
<input type="text" name="username" id="username" required> <input type="text" name="username" id="username" required>
<label>Username</label> <label>Username</label>
</div> </div>
<div>
<div> <input type="password" name="password" id="password" required>
<input type="password" name="password" id="password" required> <label>Password</label>
<label>Password</label> </div>
</div> <button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div> </div>
<div> <div>
<div> <a href="{$site_url/}account/new-password">Forgot password?</a>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div> </div>
{/unless}
</div> </div>
{/unless}

View File

@@ -0,0 +1,6 @@
CREATE TABLE blog_post_nodes(
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);

View File

@@ -0,0 +1,10 @@
{
"blocks": {
"test": {
"title": "Custom block test",
"region": "footer",
"weight": 100,
"conditions": ["path:demo/*"]
}
}
}

View File

@@ -0,0 +1,3 @@
<div>
This is a nice custom block test for site {$sitename/}.
</div>

View File

@@ -0,0 +1,52 @@
.uploaded-files table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files table th {
padding: 3px 0 3px 5px;
}
.uploaded-files table td {
padding: 3px 0 3px 5px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.uploaded-files a.button:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
.upload-files .center {
text-align: center;
padding: 10px;
}
.upload-files a.button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.upload-files a.button:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
position: relative;
.dz-preview {
position: relative;
display: inline-block;
width: 120px;
margin: 0.5em;
.dz-progress {
display: block;
height: 15px;
border: 1px solid #aaa;
.dz-upload {
display: block;
height: 100%;
width: 0;
background: green;
}
}
.dz-error-message {
color: red;
display: none;
}
&.dz-error {
.dz-error-message, .dz-error-mark {
display: block;
}
}
&.dz-success {
.dz-success-mark {
display: block;
}
}
.dz-error-mark, .dz-success-mark {
position: absolute;
display: none;
left: 30px;
top: 30px;
width: 54px;
height: 58px;
left: 50%;
margin-left: -(54px/2);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
@include keyframes(passing-through) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30%, 70% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
100% {
opacity: 0;
@include prefix((transform: translateY(-40px)));
}
}
@include keyframes(slide-in) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
}
@include keyframes(pulse) {
0% { @include prefix((transform: scale(1))); }
10% { @include prefix((transform: scale(1.1))); }
20% { @include prefix((transform: scale(1))); }
}
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
$image-size: 120px;
$image-border-radius: 20px;
&.dz-clickable {
cursor: pointer;
* {
cursor: default;
}
.dz-message {
&, * {
cursor: pointer;
}
}
}
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px;
&.dz-started {
.dz-message {
display: none;
}
}
&.dz-drag-hover {
border-style: solid;
.dz-message {
opacity: 0.5;
}
}
.dz-message {
text-align: center;
margin: 2em 0;
}
.dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px;
&:hover {
// Making sure that always the hovered preview element is on top
z-index: 1000;
.dz-details {
opacity: 1;
}
}
&.dz-file-preview {
.dz-image {
border-radius: $image-border-radius;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd);
}
.dz-details {
opacity: 1;
}
}
&.dz-image-preview {
background: white;
.dz-details {
@include prefix((transition: opacity 0.2s linear));
}
}
.dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none;
&:hover {
text-decoration: underline;
}
}
&:hover .dz-details {
opacity: 1;
}
.dz-details {
$background-color: #444;
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
$width: 120px;
line-height: 150%;
.dz-size {
margin-bottom: 1em;
font-size: 16px;
}
.dz-filename {
white-space: nowrap;
&:hover {
span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8);
}
}
&:not(:hover) {
span {
border: 1px solid transparent;
}
overflow: hidden;
text-overflow: ellipsis;
}
}
.dz-filename, .dz-size {
span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px;
}
}
}
&:hover {
.dz-image {
// opacity: 0.8;
img {
@include prefix((transform: scale(1.05, 1.05))); // Getting rid of that white bleed-in
@include prefix((filter: blur(8px)), webkit); // Getting rid of that white bleed-in
}
}
}
.dz-image {
border-radius: $image-border-radius;
overflow: hidden;
width: $image-size;
height: $image-size;
position: relative;
display: block;
z-index: 10;
img {
display: block;
}
}
&.dz-success {
.dz-success-mark {
@include prefix((animation: passing-through 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
&.dz-error {
.dz-error-mark {
opacity: 1;
@include prefix((animation: slide-in 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
.dz-success-mark, .dz-error-mark {
$image-height: 54px;
$image-width: 54px;
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -($image-width/2);
margin-top: -($image-height/2);
svg {
display: block;
width: $image-width;
height: $image-height;
}
}
&.dz-processing .dz-progress {
opacity: 1;
@include prefix((transition: all 0.2s linear));
}
&.dz-complete .dz-progress {
opacity: 0;
@include prefix((transition: opacity 0.4s ease-in));
}
&:not(.dz-processing) {
.dz-progress {
@include prefix((animation: pulse 6s ease infinite));
}
}
.dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
// border: 2px solid #333;
background: rgba(255, 255, 255, 0.9);
// Fix for chrome bug: https://code.google.com/p/chromium/issues/detail?id=157218
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden;
.dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
@include prefix((transition: width 300ms ease-in-out));
}
}
&.dz-error {
.dz-error-message {
display: block;
}
&:hover .dz-error-message {
opacity: 1;
pointer-events: auto;
}
}
.dz-error-message {
$width: $image-size + 20px;
$color: rgb(190, 38, 38);
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 0;
@include prefix((transition: opacity 0.3s ease));
border-radius: 8px;
font-size: 13px;
top: $image-size + 10px;
left: -10px;
width: $width;
background: $color;
background: linear-gradient(to bottom, $color, darken($color, 5%));
padding: 0.5em 1.2em;
color: white;
// The triangle pointing up
&:after {
content: '';
position: absolute;
top: -6px;
left: $width / 2 - 6px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $color;
}
}
}
}

View File

@@ -0,0 +1,63 @@
.uploaded-files {
table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding: 3px 0 3px 5px;
}
td {
padding: 3px 0 3px 5px;
}
}
a.button{
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
}
}
.upload-files {
.center {
text-align: center;
padding: 10px;
}
a.button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
}
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}

View File

@@ -0,0 +1,9 @@
CREATE TABLE auth_session (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` VARCHAR(64) NOT NULL,
`created` DATETIME NOT NULL,
CONSTRAINT `uid` UNIQUE(`uid`),
CONSTRAINT `access_token` UNIQUE(`access_token`)
);

View File

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

View File

@@ -1,37 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Session" is the standard authentication system. (based on cookie)</div>
<div> <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"> <form name="cms_session_auth" action="{$site_url/}account/auth/roc-session-login" 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> <div>
<p> <input type="text" name="username" id="username" required value="{$username/}">
<a href="{$site_url/}account/new-password">Forgot password?</a> <label>Username</label>
</p>
</div> </div>
</div>
{/unless}
{if isset=$error}
<div> <div>
<div> <input type="password" name="password" id="password" required >
<p> <label>Password</label>
<strong>{$error/}
</p>
</div> </div>
</div> <button type="submit">Login</button>
{/if} </form>
</div> </div>
<div>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</div>
{if isset="$error"}<div class="error">{$error/}</div>{/if}
</div>
{/unless}

View File

@@ -57,6 +57,9 @@ feature -- CMS modules
a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup)) a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup))
a_setup.register_module (create {CMS_BLOG_MODULE}.make) a_setup.register_module (create {CMS_BLOG_MODULE}.make)
-- Files
a_setup.register_module (create {CMS_FILES_MODULE}.make)
-- Contact -- Contact
a_setup.register_module (create {CMS_CONTACT_MODULE}.make) a_setup.register_module (create {CMS_CONTACT_MODULE}.make)
@@ -74,8 +77,10 @@ feature -- CMS modules
-- Miscellanious -- Miscellanious
a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make) a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make)
a_setup.register_module (create {CMS_CUSTOM_BLOCK_MODULE}.make)
a_setup.register_module (create {CMS_DEBUG_MODULE}.make) a_setup.register_module (create {CMS_DEBUG_MODULE}.make)
a_setup.register_module (create {CMS_DEMO_MODULE}.make) a_setup.register_module (create {CMS_DEMO_MODULE}.make)
end end
end end

View File

@@ -34,7 +34,7 @@ feature -- Query
end end
resolved_text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32] resolved_text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k', -- List of String items associated with key `k',
-- and expanded values to resolved variables ${varname}. -- and expanded values to resolved variables ${varname}.
do do
if attached text_list_item (k) as lst then if attached text_list_item (k) as lst then
@@ -50,7 +50,7 @@ feature -- Query
end end
resolved_text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32] resolved_text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k', -- Table of String items associated with key `k',
-- and expanded values to resolved variables ${varname}. -- and expanded values to resolved variables ${varname}.
do do
if attached text_table_item (k) as tb then if attached text_table_item (k) as tb then
@@ -71,12 +71,17 @@ feature -- Query
end end
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32] text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'. -- List of String items associated with key `k'.
deferred deferred
end end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32] text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'. -- Table of String items associated with key `k'.
deferred
end
table_keys (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- Keys of table associated with key `k'.
deferred deferred
end end

View File

@@ -163,6 +163,20 @@ feature -- Access: Config Reader
end end
end end
table_keys (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- <Precursor>
do
if attached {STRING_TABLE [like item]} item (k) as l_list then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_list.count)
Result.compare_objects
across
l_list as ic
loop
Result.force (ic.key.as_string_32)
end
end
end
integer_item (k: READABLE_STRING_GENERAL): INTEGER integer_item (k: READABLE_STRING_GENERAL): INTEGER
-- Integer item associated with key `k'. -- Integer item associated with key `k'.
do do
@@ -442,12 +456,12 @@ feature {NONE} -- Implementation
j := k.index_of (']', i + 1) j := k.index_of (']', i + 1)
if j = i + 1 then -- ends_with "[]" if j = i + 1 then -- ends_with "[]"
k.keep_head (i - 1) k.keep_head (i - 1)
if if
a_section_prefix /= Void and then a_section_prefix /= Void and then
attached {LIST [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_list attached {LIST [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_list
then then
lst := l_list lst := l_list
elseif elseif
attached last_section_name as l_section_prefix and then 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 attached {LIST [STRING_8]} items.item (l_section_prefix + {STRING_32} "." + k) as l_list
then then
@@ -466,12 +480,12 @@ feature {NONE} -- Implementation
sk.left_adjust sk.left_adjust
sk.right_adjust sk.right_adjust
k.keep_head (i - 1) k.keep_head (i - 1)
if if
a_section_prefix /= Void and then a_section_prefix /= Void and then
attached {STRING_TABLE [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_table attached {STRING_TABLE [STRING_8]} items.item (a_section_prefix + {STRING_32} "." + k) as l_table
then then
tb := l_table tb := l_table
elseif elseif
attached last_section_name as l_section_prefix and then 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 attached {STRING_TABLE [STRING_8]} items.item (l_section_prefix + {STRING_32} "." + k) as l_table
then then
@@ -522,7 +536,7 @@ feature {NONE} -- Implementation
invariant invariant
note note
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -105,6 +105,20 @@ feature -- Access: Config Reader
end end
end end
end end
table_keys (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- <Precursor>
do
if attached {JSON_OBJECT} item (k) as obj then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (obj.count)
Result.compare_objects
across
obj as ic
loop
Result.force (ic.key.item)
end
end
end
integer_item (k: READABLE_STRING_GENERAL): INTEGER integer_item (k: READABLE_STRING_GENERAL): INTEGER
-- Integer item associated with key `k'. -- Integer item associated with key `k'.

View File

@@ -75,13 +75,21 @@ feature -- Test
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3") lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
) )
) )
assert ("has_item (table)", cfg.has_item ("table")) assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then ( assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test" tb.item ("d") ~ {STRING_32} "test"
)
)
assert ("keys of (table)", attached cfg.table_keys ("table") as tb and then (
tb.i_th (1) ~ {STRING_32} "a" and
tb.i_th (2) ~ {STRING_32} "b" and
tb.i_th (3) ~ {STRING_32} "c" and
tb.i_th (4) ~ {STRING_32} "d"
) )
) )
@@ -198,13 +206,21 @@ feature -- Test
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3") lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
) )
) )
assert ("has_item (table)", cfg.has_item ("table")) assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then ( assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test" tb.item ("d") ~ {STRING_32} "test"
)
)
assert ("keys of (table)", attached cfg.table_keys ("table") as tb and then (
tb.i_th (1) ~ {STRING_32} "a" and
tb.i_th (2) ~ {STRING_32} "b" and
tb.i_th (3) ~ {STRING_32} "c" and
tb.i_th (4) ~ {STRING_32} "d"
) )
) )

View File

@@ -37,6 +37,80 @@ feature -- Access
weight: INTEGER weight: INTEGER
-- Optional weight used for order. -- Optional weight used for order.
query_string: detachable STRING
-- Query string from `location'.
local
i: INTEGER
loc: like location
do
loc := location
i := loc.index_of ('?', 1)
if i > 0 then
Result := loc.substring (i + 1, loc.count)
i := loc.last_index_of ('#', loc.count)
if i > 0 then
Result.keep_head (i - 1)
end
end
end
fragment_string: detachable STRING
-- Query string from `location'.
local
i: INTEGER
loc: like location
do
loc := location
i := loc.last_index_of ('#', loc.count)
if i > 0 then
Result := loc.substring (i + 1, loc.count)
end
end
feature -- Element change
add_query_parameter (a_encoded_name: READABLE_STRING_8; a_encoded_value: detachable READABLE_STRING_8)
-- Add query parameter "$a_encoded_name=$a_encoded_value" to `location'.
-- note: the argument must already be url encoded!
local
q: STRING_8
f: detachable READABLE_STRING_8
i,j: INTEGER
loc: STRING_8
do
create loc.make_from_string (location)
j := loc.last_index_of ('#', loc.count)
if j > 0 then
f := loc.substring (j, loc.count)
loc.keep_head (j - 1)
end
i := loc.index_of ('?', 1)
if i > 0 then
q := loc.substring (i + 1, loc.count)
loc.keep_head (i)
else
create q.make_empty
end
if not q.is_empty then
q.append_character ('&')
end
q.append (a_encoded_name)
if a_encoded_value /= Void then
q.append_character ('=')
q.append (a_encoded_value)
end
loc.append_character ('?')
loc.append (q)
if f /= Void then
loc.append (f)
end
location := loc
end
feature -- Comparison feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN is_less alias "<" (other: like Current): BOOLEAN
@@ -89,6 +163,22 @@ feature -- Security
feature -- Element change feature -- Element change
set_title (a_title: detachable READABLE_STRING_GENERAL)
-- Set `title' to `a_title' or `location'.
do
if a_title /= Void then
title := a_title.as_string_32
else
title := location.as_string_32
end
end
set_location (a_loc: READABLE_STRING_8)
-- Set `location' to `a_loc'.
do
location := a_loc
end
set_weight (a_weight: INTEGER) set_weight (a_weight: INTEGER)
-- Set `weight' to `a_weight'. -- Set `weight' to `a_weight'.
do do
@@ -134,6 +224,6 @@ feature -- Status report
end end
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -18,13 +18,82 @@ feature -- Access
deferred deferred
end end
feature -- Element change item_by_title (a_title: READABLE_STRING_GENERAL): detachable CMS_LINK
-- First link with title `a_title' if any.
do
if attached items as l_items then
across
l_items as ic
until
Result /= Void
loop
Result := ic.item
if not a_title.is_case_insensitive_equal (Result.title) then
Result := Void
end
end
end
ensure
coherent_result: Result /= Void implies Result.title.is_case_insensitive_equal_general (a_title)
end
item_by_location (a_loc: READABLE_STRING_8): detachable CMS_LINK
-- First link with location `a_loc' if any.
do
if attached items as l_items then
across
l_items as ic
until
Result /= Void
loop
Result := ic.item
if not a_loc.same_string (Result.location) then
Result := Void
end
end
end
ensure
coherent_result: Result /= Void implies Result.location.same_string (a_loc)
end
new_composite_item (a_title: detachable READABLE_STRING_GENERAL; a_location: READABLE_STRING_8): CMS_LINK_COMPOSITE
-- If exists, item with location `a_location' or title `a_title',
-- otherwise create new local link and extend to Current.
local
lnk: CMS_LOCAL_LINK
do
if attached {CMS_LINK_COMPOSITE} item_by_location (a_location) as l_parent then
Result := l_parent
elseif a_title /= Void and then attached {CMS_LINK_COMPOSITE} item_by_title (a_title) as l_parent then
Result := l_parent
else
create lnk.make (a_title, a_location)
extend (lnk)
Result := lnk
end
if attached {CMS_LOCAL_LINK} Result as l_local_lnk and then not l_local_lnk.is_expanded then
l_local_lnk.set_expandable (True)
l_local_lnk.set_collapsed (True)
end
end
feature -- Element change
extend (lnk: CMS_LINK) extend (lnk: CMS_LINK)
-- Add `lnk' as a sub link. -- Add `lnk' as a sub link.
deferred deferred
end end
extend_into (lnk: CMS_LINK; a_parent_title: detachable READABLE_STRING_GENERAL; a_parent_location: READABLE_STRING_8)
-- Extend `lnk' into local link with location `a_parent_location'.
-- If the parent is not found, create it with title `a_parent_title'.
local
l_parent: CMS_LINK_COMPOSITE
do
l_parent := new_composite_item (a_parent_title, a_parent_location)
l_parent.extend (lnk)
end
remove (lnk: CMS_LINK) remove (lnk: CMS_LINK)
-- Remove link `lnk' from Current container. -- Remove link `lnk' from Current container.
deferred deferred
@@ -68,6 +137,6 @@ feature -- status report
end end
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -79,16 +79,6 @@ feature -- Security
feature -- Element change feature -- Element change
set_title (a_title: detachable READABLE_STRING_GENERAL)
-- Set `title' to `a_title' or `location'.
do
if a_title /= Void then
title := a_title.as_string_32
else
title := location.as_string_32
end
end
add_link (lnk: CMS_LINK) add_link (lnk: CMS_LINK)
-- <Precursor> -- <Precursor>
local local
@@ -194,6 +184,6 @@ feature {NONE} -- Implementation
invariant invariant
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -73,7 +73,7 @@ feature -- Status report
end end
end end
feature -- Element change feature -- Element change
extend (lnk: CMS_LINK) extend (lnk: CMS_LINK)
-- <Precursor> -- <Precursor>
@@ -104,6 +104,6 @@ feature -- Access
invariant invariant
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -31,7 +31,7 @@ feature {NONE} -- Initialization
feature -- Access feature -- Access
id: INTEGER id: INTEGER_64
-- Unique identifier of Current. -- Unique identifier of Current.
category: READABLE_STRING_8 category: READABLE_STRING_8
@@ -124,4 +124,7 @@ feature -- Constants
level_info: INTEGER = 7 level_info: INTEGER = 7
level_debug: INTEGER = 8 level_debug: INTEGER = 8
note
copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -65,9 +65,6 @@ feature -- Access
email: detachable READABLE_STRING_8 email: detachable READABLE_STRING_8
-- User email. -- User email.
profile: detachable CMS_USER_PROFILE
-- User profile.
creation_date: DATE_TIME creation_date: DATE_TIME
-- Creation date. -- Creation date.
@@ -189,26 +186,6 @@ feature -- Change element
email_set: email = a_email email_set: email = a_email
end end
set_profile (prof: like profile)
-- Set `profile' with `prof'.
do
profile := prof
ensure
profile_set: profile = prof
end
set_profile_item (k: READABLE_STRING_8; v: READABLE_STRING_8)
local
prof: like profile
do
prof := profile
if prof = Void then
create prof.make
profile := prof
end
prof.force (v, k)
end
set_last_login_date (dt: like last_login_date) set_last_login_date (dt: like last_login_date)
do do
last_login_date := dt last_login_date := dt
@@ -284,7 +261,6 @@ feature -- Status change
status_set: status = a_status status_set: status = a_status
end end
feature -- User status feature -- User status
not_active: INTEGER = 0 not_active: INTEGER = 0

View File

@@ -1,56 +0,0 @@
note
description: "[
User profile used to extend information associated with a {CMS_USER}.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_USER_PROFILE
inherit
TABLE_ITERABLE [READABLE_STRING_8, READABLE_STRING_GENERAL]
create
make
feature {NONE} -- Initialization
make
-- Create Current profile.
do
create items.make (0)
end
feature -- Access
item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
-- Profile item associated with key `k'.
do
Result := items.item (k)
end
feature -- Change
force (v: READABLE_STRING_8; k: READABLE_STRING_GENERAL)
-- Associated value `v' with key `k'.
do
items.force (v, k)
end
feature -- Access
new_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_GENERAL]
-- Fresh cursor associated with current structure
do
Result := items.new_cursor
end
feature {NONE} -- Implementation
items: STRING_TABLE [READABLE_STRING_8]
;note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -54,6 +54,7 @@ feature -- Access: router
l_user_handler: CMS_USER_HANDLER l_user_handler: CMS_USER_HANDLER
l_role_handler: CMS_ROLE_HANDLER l_role_handler: CMS_ROLE_HANDLER
l_admin_logs_handler: CMS_LOGS_HANDLER
l_admin_cache_handler: CMS_ADMIN_CACHE_HANDLER l_admin_cache_handler: CMS_ADMIN_CACHE_HANDLER
l_admin_export_handler: CMS_ADMIN_EXPORT_HANDLER l_admin_export_handler: CMS_ADMIN_EXPORT_HANDLER
@@ -76,6 +77,11 @@ feature -- Access: router
create l_uri_mapping.make_trailing_slash_ignored ("/admin/roles", l_roles_handler) create l_uri_mapping.make_trailing_slash_ignored ("/admin/roles", l_roles_handler)
a_router.map (l_uri_mapping, a_router.methods_get_post) a_router.map (l_uri_mapping, a_router.methods_get_post)
create l_admin_logs_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/logs", l_admin_logs_handler)
a_router.map (l_uri_mapping, a_router.methods_get)
create l_admin_cache_handler.make (a_api) create l_admin_cache_handler.make (a_api)
create l_uri_mapping.make_trailing_slash_ignored ("/admin/cache", l_admin_cache_handler) 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) a_router.map (l_uri_mapping, a_router.methods_get_post)
@@ -103,11 +109,12 @@ feature -- Security
-- List of permission ids, used by this module, and declared. -- List of permission ids, used by this module, and declared.
do do
Result := Precursor Result := Precursor
Result.force ("manage admin") Result.force ("access admin")
Result.force ("admin users") Result.force ("admin users")
Result.force ("admin roles") Result.force ("admin roles")
Result.force ("admin modules") Result.force ("admin modules")
Result.force ("install modules") Result.force ("install modules")
Result.force ("view logs")
Result.force ("admin core caches") Result.force ("admin core caches")
Result.force ("clear blocks cache") Result.force ("clear blocks cache")
Result.force ("admin export") Result.force ("admin export")
@@ -132,28 +139,23 @@ feature -- Hooks
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
local local
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
admin_lnk: CMS_LINK_COMPOSITE
do do
if if a_response.api.user_is_authenticated then
a_response.has_permission ("manage " + {CMS_ADMIN_MODULE}.name) -- Note: admin user has all permissions enabled by default. admin_lnk := a_menu_system.management_menu.new_composite_item ("Admin", "admin")
then
-- TODO: we should probably use more side menu and less primary_menu.
create lnk.make ("Admin", "admin")
lnk.set_permission_arguments (<<"manage " + {CMS_ADMIN_MODULE}.name>>)
a_menu_system.management_menu.extend (lnk)
create lnk.make ("Module", "admin/modules")
lnk.set_permission_arguments (<<"manage module">>)
admin_lnk.extend (lnk)
-- Per module cache permission!
create lnk.make ("Cache", "admin/cache")
admin_lnk.extend (lnk)
-- Per module export permission!
create lnk.make ("Export", "admin/export")
admin_lnk.extend (lnk)
end 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 end
note note

View File

@@ -17,22 +17,59 @@ feature -- Process
process process
local local
b: STRING b: STRING
l_admin_links: ARRAYED_LIST [TUPLE [package: READABLE_STRING_8; permissions: ARRAY [READABLE_STRING_GENERAL]; link: CMS_LINK; help: READABLE_STRING_GENERAL]]
lst: detachable ARRAYED_LIST [TUPLE [permissions: ARRAY [READABLE_STRING_GENERAL]; link: CMS_LINK; help: READABLE_STRING_GENERAL]]
categories: STRING_TABLE [ARRAYED_LIST [TUPLE [permissions: ARRAY [READABLE_STRING_GENERAL]; link: CMS_LINK; help: READABLE_STRING_GENERAL]]]
l_package: READABLE_STRING_8
do do
create l_admin_links.make (5)
l_admin_links.force (["core", <<"admin users">>, local_link ("Users", "admin/users"), "View/Edit/Add Users"])
l_admin_links.force (["core", <<"admin roles">>, local_link ("Roles", "admin/roles"), "View/Edit/Add Roles"])
l_admin_links.force (["core", <<"admin modules">>, local_link ("Modules", "admin/modules"), "(un)Install modules"])
l_admin_links.force (["core", <<"view logs">>, local_link ("Logs", "admin/logs"), "View logs"])
l_admin_links.force (["support", <<"admin cache">>, local_link ("Cache", "admin/cache"), "Clear caches"])
l_admin_links.force (["support", <<"admin export">>, local_link ("Export", "admin/export"), "Export CMS contents, and modules contents."])
create categories.make_caseless (3)
across
l_admin_links as ic
loop
l_package := ic.item.package
lst := categories.item (l_package)
if lst = Void then
create lst.make (1)
categories.force (lst, l_package)
end
lst.force ([ic.item.permissions, ic.item.link, ic.item.help])
end
create b.make_empty create b.make_empty
set_title (translation ("Admin Page", Void)) set_title (translation ("Admin Page", Void))
b.append ("<ul id=%"content-types%">")
fixme ("Check how to make it configurable") fixme ("Check how to make it configurable")
if has_permissions (<< "admin users">>) then across
b.append ("<li>" + link ("Users", "admin/users", Void)) categories as cats_ic
b.append ("<div class=%"description%">View/Edit/Add Users</div>") loop
b.append ("</li>") lst := cats_ic.item
b.append ("<h3>")
b.append (html_encoded (cats_ic.key))
b.append ("</h3>")
b.append ("<ul>")
across
lst as ic
loop
if has_permissions (ic.item.permissions) then
b.append ("<li>")
if attached ic.item.link as lnk then
b.append (link (lnk.title, lnk.location, Void))
end
b.append ("<div class=%"description%">")
b.append (html_encoded (ic.item.help))
b.append ("</div>")
b.append ("</li>")
end
end
b.append ("</ul>")
end end
if has_permissions (<< "admin roles">>) then
b.append ("<li>" + link ("Roles", "admin/roles", Void))
b.append ("<div class=%"description%">View/Edit/Add Roles</div>")
b.append ("</li>")
end
b.append ("</ul>")
set_main_content (b) set_main_content (b)
end end

View File

@@ -0,0 +1,114 @@
note
description: "[
Handler for a CMS logs in the CMS interface.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_LOGS_HANDLER
inherit
CMS_HANDLER
WSF_URI_HANDLER
rename
execute as uri_execute,
new_mapping as new_uri_mapping
end
WSF_URI_TEMPLATE_HANDLER
rename
execute as uri_template_execute,
new_mapping as new_uri_template_mapping
select
new_uri_template_mapping
end
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get
end
REFACTORING_HELPER
create
make
feature -- execute
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
end
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute (req, res)
end
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute (req, res)
end
feature -- HTTP Methods
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
l_logs: LIST [CMS_LOG]
l_log: CMS_LOG
r: CMS_RESPONSE
l_cat: detachable READABLE_STRING_8
l_lower: INTEGER
l_count: INTEGER
b: STRING
do
if api.has_permission ("view logs") then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.query_parameter ("category") as p_cat then
l_cat := p_cat.value
end
if attached {WSF_STRING} req.query_parameter ("lower") as p_lower and then p_lower.is_integer then
l_lower := p_lower.integer_value
end
if attached {WSF_STRING} req.query_parameter ("count") as p_count and then p_count.is_integer then
l_count := p_count.integer_value
end
l_logs := api.logs (l_cat, l_lower, l_count)
create b.make (100)
b.append ("<ul class=%"logs%">%N")
across
l_logs as ic
loop
l_log := ic.item
b.append ("<li class=%"log-level-"+ l_log.level.out +"%">")
b.append ("[" + l_log.category + "] ")
b.append (l_log.message)
b.append ("%N<p>(date: " + l_log.date.out + ")")
if attached l_log.link as lnk then
b.append (" <a href=%"" + req.script_url (lnk.location) + "%">" + html_encoded (lnk.title) + "</a>")
end
b.append ("</p>%N")
if attached l_log.info as l_info then
b.append ("<pre>" + l_info + "</pre>%N")
end
b.append ("</li>%N")
end
b.append ("</ul>%N")
r.set_main_content (b)
r.set_page_title ("Logs ...")
r.set_title ("Logs")
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
r.execute
end
end

View File

@@ -166,7 +166,7 @@ feature -- Error
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- <Precursor>
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then

View File

@@ -108,6 +108,16 @@ feature -- HTTP Methods
s.append ("%">") s.append ("%">")
s.append (html_encoded (u.name)) s.append (html_encoded (u.name))
s.append ("</a>") s.append ("</a>")
if attached user_api.user_roles (u) as l_roles and then not l_roles.is_empty then
s.append (" <span class=%"cms_roles%">(")
across
l_roles as ic_roles
loop
s.append (html_encoded (ic_roles.item.name))
s.append (" ")
end
s.append (")</span>")
end
s.append ("</li>%N") s.append ("</li>%N")
end end
s.append ("</ul>%N") s.append ("</ul>%N")

View File

@@ -386,6 +386,9 @@ feature -- Form
-- Update node `a_node' with form_data `a_form_data' for the given content type `a_content_type'. -- Update node `a_node' with form_data `a_form_data' for the given content type `a_content_type'.
local local
l_uroles: LIST [CMS_USER_ROLE] l_uroles: LIST [CMS_USER_ROLE]
l_new_roles: ARRAYED_LIST [CMS_USER_ROLE]
r: detachable CMS_USER_ROLE
rid: INTEGER
do do
if attached a_form_data.string_item ("op") as f_op then if attached a_form_data.string_item ("op") as f_op then
if f_op.is_case_insensitive_equal_general ("Update user role") then if f_op.is_case_insensitive_equal_general ("Update user role") then
@@ -394,23 +397,53 @@ feature -- Form
then then
l_uroles := api.user_api.user_roles (l_user) l_uroles := api.user_api.user_roles (l_user)
l_uroles.compare_objects l_uroles.compare_objects
if attached {WSF_STRING} a_form_data.item ("cms_roles") as l_role then
if attached api.user_api.user_role_by_id (l_role.integer_value) as role then if attached {WSF_STRING} a_form_data.item ("cms_roles") as p_role_id then
if not l_uroles.has (role) then rid := p_role_id.integer_value
api.user_api.assign_role_to_user (role, a_user) r := api.user_api.user_role_by_id (rid)
if r /= Void then
create l_new_roles.make (0)
l_new_roles.force (r)
end
elseif attached {WSF_MULTIPLE_STRING} a_form_data.item ("cms_roles") as p_roles_ids then
create l_new_roles.make (p_roles_ids.values.count)
across
p_roles_ids as ic
loop
rid := ic.item.integer_value
r := api.user_api.user_role_by_id (rid)
if r /= Void then
l_new_roles.force (r)
end end
end end
elseif attached {WSF_MULTIPLE_STRING} a_form_data.item ("cms_roles") as l_roles then end
across l_roles as ic loop if l_new_roles = Void or else l_new_roles.is_empty then
if attached api.user_api.user_role_by_id (ic.item.integer_value) as role then across
if not l_uroles.has (role) then l_uroles as ic
api.user_api.assign_role_to_user (role, a_user) loop
end r := ic.item
end api.user_api.unassign_role_from_user (r, a_user)
end end
else else
across api.user_api.roles as ic loop across
api.user_api.unassign_role_from_user (ic.item, a_user) l_new_roles as ic
loop
r := ic.item
if l_uroles.has (r) then
-- Already assigned to that role.
else
api.user_api.assign_role_to_user (ic.item, a_user)
end
end
-- Remove other roles for `a_user'.
l_new_roles.compare_objects
across
l_uroles as ic
loop
r := ic.item
if not l_new_roles.has (r) then
api.user_api.unassign_role_from_user (r, a_user)
end
end end
end end
add_success_message ("Roles updated") add_success_message ("Roles updated")

View File

@@ -166,7 +166,7 @@ feature -- Error
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- <Precursor>
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then

View File

@@ -86,12 +86,13 @@ feature -- Execution
not l_roles.is_empty not l_roles.is_empty
then then
s.append ("<h4>Role(s):</h4>") s.append ("<h4>Role(s):</h4>")
s.append ("<ul class=%"user-roles%">")
across l_roles as ic loop across l_roles as ic loop
l_role := ic.item l_role := ic.item
s.append ("<i>") s.append ("<li>")
s.append (link (l_role.name, "admin/role/" + l_role.id.out, Void)) s.append (link (l_role.name, "admin/role/" + l_role.id.out, Void))
s.append ("</i>") s.append ("</li>")
debug if request.query_parameter ("debug") /= Void then
s.append ("<h5>Permissions:</h5>") s.append ("<h5>Permissions:</h5>")
s.append ("<ul class=%"cms-permissions%">%N") s.append ("<ul class=%"cms-permissions%">%N")
across l_role.permissions as perms_ic loop across l_role.permissions as perms_ic loop
@@ -100,6 +101,7 @@ feature -- Execution
s.append ("</ul>%N") s.append ("</ul>%N")
end end
end end
s.append ("</ul>%N")
end end
s.append ("</div>") s.append ("</div>")

View File

@@ -0,0 +1,17 @@
note
description: "[
Common interface for Auth module API.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTH_API_I
inherit
CMS_MODULE_API
create
make
end

View File

@@ -0,0 +1,44 @@
note
description: "[
Processes a HTTP request, and depending on header, authenticate a current user or not.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_AUTH_FILTER_I
inherit
WSF_FILTER
feature {NONE} -- Initialization
make (a_api: CMS_API)
-- Initialize Current handler with `a_api'.
do
api := a_api
end
feature -- API Service
api: CMS_API
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
deferred
end
auth_strategy: STRING
deferred
end
set_current_user (u: CMS_USER)
do
api.set_user (u)
-- Record auth strategy:
api.set_execution_variable ("auth_strategy", auth_strategy)
end
end

View File

@@ -0,0 +1,110 @@
note
description: "Common ancestor for Authentication modules."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_AUTH_MODULE_I
inherit
CMS_MODULE
redefine
setup_hooks
end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_BLOCK_HELPER
SHARED_LOGGER
feature {NONE} -- Initialization
make
do
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
end
feature -- Access: auth strategy
login_title: READABLE_STRING_GENERAL
-- Module specific login title.
deferred
end
login_location: STRING
-- Login cms location for Current module.
deferred
end
logout_location: STRING
-- Logout cms location for Current module.
deferred
end
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- Is Current module strategy currently authenticating active user?
deferred
ensure
Result implies a_response.is_authenticated
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_menu_system_alter_hook (Current)
end
feature -- Hooks
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- <Precursor>
local
lnk: CMS_LOCAL_LINK
l_destination: READABLE_STRING_8
do
if attached {WSF_STRING} a_response.request.item ("destination") as p_destination then
l_destination := p_destination.url_encoded_value
else
l_destination := a_response.location
end
if is_authenticating (a_response) then
else
if a_response.location.starts_with ("account/auth/") then
create lnk.make (login_title, login_location)
if not l_destination.starts_with ("account/auth/") then
lnk.add_query_parameter ("destination", l_destination)
end
lnk.set_expandable (True)
a_response.add_to_primary_tabs (lnk)
end
end
end
feature {NONE} -- Template
smarty_template_login_block (a_request: WSF_REQUEST; a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_cms_api: CMS_API): like smarty_template_block
local
l_destination: detachable READABLE_STRING_32
do
Result := smarty_template_block (a_module, a_block_id, a_cms_api)
if Result /= Void then
if attached {WSF_STRING} a_request.query_parameter ("destination") as p_destination then
l_destination := p_destination.value
elseif attached {WSF_STRING} a_request.form_parameter ("destination") as p_destination then
l_destination := p_destination.value
end
if l_destination /= Void then
Result.set_value (l_destination, "site_destination")
end
end
end
end

View File

@@ -112,6 +112,7 @@ feature -- Basic Operations / Contact
l_message.replace_substring_all ("$host", a_host) l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name)
l_message.replace_substring_all ("$user", a_user.utf_8_name) l_message.replace_substring_all ("$user", a_user.utf_8_name)
l_message.replace_substring_all ("$email", a_to)
send_message (contact_email_address, a_to, parameters.contact_subject_register, l_message) send_message (contact_email_address, a_to, parameters.contact_subject_register, l_message)
end end
@@ -126,6 +127,7 @@ feature -- Basic Operations / Contact
l_message.replace_substring_all ("$host", a_host) l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name)
l_message.replace_substring_all ("$link", a_link) l_message.replace_substring_all ("$link", a_link)
l_message.replace_substring_all ("$email", a_to)
send_message (contact_email_address, a_to, parameters.contact_subject_activate, l_message) send_message (contact_email_address, a_to, parameters.contact_subject_activate, l_message)
end end
@@ -137,7 +139,7 @@ feature -- Basic Operations / Contact
l_message: STRING l_message: STRING
do do
create l_message.make_from_string (parameters.account_activation_confirmation) create l_message.make_from_string (parameters.account_activation_confirmation)
l_message.replace_substring_all ("$hot", a_host) l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name) l_message.replace_substring_all ("$sitename", parameters.utf_8_site_name)
l_message.replace_substring_all ("$user", a_user.utf_8_name) l_message.replace_substring_all ("$user", a_user.utf_8_name)
l_message.replace_substring_all ("$email", a_to) l_message.replace_substring_all ("$email", a_to)

View File

@@ -130,13 +130,13 @@ feature -- Access
account_re_activation: STRING account_re_activation: STRING
-- Account re_activation template email message. -- Account re_activation template email message.
do do
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation) Result := template_string ("account_re_activation.html", default_template_account_re_activation)
end end
account_rejected: STRING account_rejected: STRING
-- Account rejected template email message. -- Account rejected template email message.
do do
Result := template_string ("accunt_rejected.html", default_template_account_rejected) Result := template_string ("account_rejected.html", default_template_account_rejected)
end end
account_password: STRING account_password: STRING
@@ -277,7 +277,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>You requested has been rejected, your application does not conform our rules <a href="$host">$sitename</a></p> <p>Your account application is rejected, it does not conform our rules <a href="$host">$sitename</a></p>
</body> </body>
</html> </html>
]" ]"
@@ -316,7 +316,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>You have required a new password at <a href="$host">$sitename</a></p> <p>You have requested a new password at <a href="$host">$sitename</a></p>
<p>To complete your request, please click on this link to generate a new password:<p> <p>To complete your request, please click on this link to generate a new password:<p>
@@ -337,7 +337,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>Welcome to<a href="...">$sitename</a></p> <p>Welcome to <a href="$host">$sitename</a>.</p>
<p>Thank you for joining us.</p> <p>Thank you for joining us.</p>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,12 @@
note note
description: "Module Auth" description: "Module Auth"
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $" date: "$Date: 2016-04-13 10:59:18 +0200 (mer., 13 avr. 2016) $"
revision: "$Revision: 97328 $" revision: "$Revision: 98616 $"
class class
CMS_AUTHENTICATION_MODULE CMS_AUTHENTICATION_MODULE
inherit inherit
CMS_MODULE CMS_MODULE
redefine redefine
setup_hooks, setup_hooks,
@@ -22,6 +21,8 @@ inherit
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_BLOCK_HELPER
CMS_HOOK_MENU_SYSTEM_ALTER CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT SHARED_EXECUTION_ENVIRONMENT
@@ -33,8 +34,6 @@ inherit
SHARED_LOGGER SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -82,6 +81,10 @@ feature -- Access: docs
feature -- Router feature -- Router
roc_login_location: STRING = "account/roc-login"
roc_logout_location: STRING = "account/roc-logout"
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
@@ -93,19 +96,22 @@ feature -- Router
local local
m: WSF_URI_MAPPING m: WSF_URI_MAPPING
do do
create m.make_trailing_slash_ignored ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account(a_api, ?, ?))) create m.make_trailing_slash_ignored ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get) a_router.map (m, a_router.methods_head_get)
a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) create m.make_trailing_slash_ignored ("/account/edit", create {WSF_URI_AGENT_HANDLER}.make (agent handle_edit_account (a_api, ?, ?)))
a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout(a_api, ?, ?)), a_router.methods_head_get) a_router.map (m, a_router.methods_head_get)
a_router.handle ("/" + roc_login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/" + roc_logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register(a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation(a_api, ?, ?)), a_router.methods_head_get) a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/reject/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_reject(a_api, ?, ?)), a_router.methods_head_get) a_router.handle ("/account/reject/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_reject(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation(a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password(a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/change/{field}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_change_field (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/post-change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_change_password(a_api, ?, ?)), a_router.methods_get)
end end
@@ -128,8 +134,35 @@ feature -- Hooks configuration
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor> -- <Precursor>
local
l_destination: detachable READABLE_STRING_GENERAL
l_url: STRING
l_url_name: READABLE_STRING_GENERAL
do do
a_value.force (a_response.user, "user") if attached {WSF_STRING} a_response.request.item ("destination") as p_destination then
l_destination := p_destination.value
else
l_destination := a_response.location
end
if l_destination.starts_with ("account/auth/") then
l_destination := Void
end
if attached a_response.user as u then
a_value.force (u, "user")
l_url_name := "site_sign_out_url"
l_url := a_response.url (roc_logout_location, Void)
else
a_value.force (Void, "user")
l_url_name := "site_sign_in_url"
l_url := a_response.url (roc_login_location, Void)
end
if l_destination /= Void and then not l_url.has_substring ("?destination") then
l_url.append ("?destination=" + percent_encoded (l_destination))
end
a_value.force (l_url, l_url_name)
end end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
@@ -142,18 +175,26 @@ feature -- Hooks configuration
create lnk.make (u.name, "account") create lnk.make (u.name, "account")
lnk.set_weight (97) lnk.set_weight (97)
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
create lnk.make ("Logout", "account/roc-logout")
lnk.set_weight (98) create lnk.make ("Logout", roc_logout_location)
a_menu_system.primary_menu.extend (lnk)
else else
create lnk.make ("Login", "account/roc-login") create lnk.make ("Login", roc_login_location)
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
end end
lnk.set_weight (98)
if
a_response.location.starts_with_general ("account/auth/")
or a_response.location.starts_with_general ("account/roc-log") -- in ou out
then
-- ignore destination
else
lnk.add_query_parameter ("destination", percent_encoded (a_response.location))
end
a_menu_system.primary_menu.extend (lnk)
-- Add the link to the taxonomy to the main menu -- Add the link to the taxonomy to the main menu
if a_response.has_permission ("admin registration") then if a_response.has_permission ("admin registration") then
create lnk.make ("Registration", "admin/pending-registrations/") create lnk.make ("Registration", "admin/pending-registrations/")
a_menu_system.management_menu.extend (lnk) a_menu_system.management_menu.extend_into (lnk, "Admin", "admin")
end end
end end
@@ -162,18 +203,78 @@ feature -- Handler
handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_user: detachable CMS_USER
b: STRING
lnk: CMS_LOCAL_LINK
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached template_block ("account_info", r) as l_tpl_block then create b.make_empty
if attached r.user as l_user then l_user := r.user
r.set_value (api.user_api.user_roles (l_user), "roles") if attached smarty_template_block (Current, "account_info", api) as l_tpl_block then
end l_tpl_block.set_weight (-10)
r.add_block (l_tpl_block, "content") r.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
r.add_warning_message ("Error with block [resources_page]") r.add_warning_message ("Error with block [resources_page]")
end end
end end
if r.is_authenticated then
create lnk.make ("View", "account/")
lnk.set_weight (1)
r.add_to_primary_tabs (lnk)
create lnk.make ("Edit", "account/edit")
lnk.set_weight (2)
r.add_to_primary_tabs (lnk)
end
r.set_main_content (b)
if l_user = Void then
r.set_redirection (roc_login_location)
end
r.execute
end
handle_edit_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_user: detachable CMS_USER
b: STRING
f: CMS_FORM
lnk: CMS_LOCAL_LINK
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create b.make_empty
l_user := r.user
if attached smarty_template_block (Current, "account_edit", api) as l_tpl_block then
l_tpl_block.set_weight (-10)
r.add_block (l_tpl_block, "content")
else
debug ("cms")
r.add_warning_message ("Error with block [resources_page]")
end
end
create lnk.make ("View", "account/")
lnk.set_weight (1)
r.add_to_primary_tabs (lnk)
create lnk.make ("Edit", "account/edit")
lnk.set_weight (2)
r.add_to_primary_tabs (lnk)
f := new_change_password_form (r)
f.append_to_html (r.wsf_theme, b)
f := new_change_email_form (r)
f.append_to_html (r.wsf_theme, b)
r.set_main_content (b)
if l_user = Void then
r.set_redirection ("account")
end
r.execute r.execute
end end
@@ -181,10 +282,30 @@ feature -- Handler
local local
r: CMS_RESPONSE r: CMS_RESPONSE
do do
if attached api.module_by_name ("basic_auth") then if api.user_is_authenticated then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection ("account")
r.execute
elseif attached api.module_by_name ("session_auth") then
-- FIXME: find better solution to support a default login system. -- FIXME: find better solution to support a default login system.
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (r.absolute_url ("/account/roc-basic-auth", Void)) if attached {WSF_STRING} req.item ("destination") as l_destination then
r.set_redirection ("account/auth/roc-session-login?destination=" + l_destination.url_encoded_value)
else
r.set_redirection ("account/auth/roc-session-login")
end
r.execute
elseif attached api.module_by_name ("basic_auth") then
-- FIXME: find better solution to support a default login system.
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.item ("destination") as l_destination then
r.set_redirection ("account/auth/roc-basic-login?destination=" + l_destination.url_encoded_value)
else
r.set_redirection ("account/auth/roc-basic-login")
end
r.execute r.execute
else else
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
@@ -195,9 +316,19 @@ feature -- Handler
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
loc: STRING
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (r.absolute_url ("", Void)) if attached {READABLE_STRING_8} api.execution_variable ("auth_strategy") as l_auth_strategy then
loc := l_auth_strategy
else
loc := ""
end
-- Do not try to redirect to previous page or destination!
-- if attached {WSF_STRING} req.query_parameter ("destination") as l_destination then
-- loc.append ("?destination=" + l_destination.url_encoded_value)
-- end
r.set_redirection (loc)
r.execute r.execute
end end
@@ -232,7 +363,7 @@ feature -- Handler
l_exist := True l_exist := True
end end
if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then
-- Emails already exist. -- Email already exists.
r.set_value ("An account is already associated with that email address!", "error_email") r.set_value ("An account is already associated with that email address!", "error_email")
l_exist := True l_exist := True
end end
@@ -524,45 +655,91 @@ feature -- Handler
r.execute r.execute
end end
handle_change_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_change_field (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_user_api: CMS_USER_API l_user_api: CMS_USER_API
f: CMS_FORM
l_fieldname: detachable READABLE_STRING_8
b: STRING
lnk: CMS_LOCAL_LINK
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if attached {WSF_STRING} req.path_parameter ("field") as p_field then
l_user_api := api.user_api l_fieldname := p_field.url_encoded_value
if req.is_post_request_method then end
if attached r.user as l_user then if l_fieldname = Void then
r.set_value (api.user_api.user_roles (l_user), "roles") create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then l_password.value.same_string (l_confirm_password.value) then else
-- Does the passwords match? create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_user.set_password (l_password.value)
l_user_api.update_user (l_user) if r.is_authenticated then
r.set_redirection (req.absolute_script_url ("/account/post-change-password")) create lnk.make ("View", "account/")
else lnk.set_weight (1)
if attached template_block ("account_info", r) as l_tpl_block then r.add_to_primary_tabs (lnk)
-- r.set_value (l_user, "user")
r.set_value ("Passwords Don't Match", "error_password") create lnk.make ("Edit", "account/edit")
r.set_status_code ({HTTP_CONSTANTS}.bad_request) lnk.set_weight (2)
r.add_block (l_tpl_block, "content") r.add_to_primary_tabs (lnk)
end
l_user_api := api.user_api
if req.is_post_request_method then
if attached r.user as l_user then
if l_fieldname.is_case_insensitive_equal ("password") then
if
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then
l_password.value.same_string (l_confirm_password.value)
then
-- passwords matched?
l_user.set_password (l_password.value)
l_user_api.update_user (l_user)
r.add_success_message ("Password updated.")
r.set_redirection ("account/")
r.set_redirection_delay (3)
else
r.add_error_message ("Passwords do not match!")
f := new_change_password_form (r)
r.set_main_content (f.to_html (r.wsf_theme))
end
elseif l_fieldname.is_case_insensitive_equal ("email") then
-- FIXME: find a safer workflow .. allow multiple emails, and have a primary email?
if
attached {WSF_STRING} req.form_parameter ("email") as l_email and then
attached {WSF_STRING} req.form_parameter ("confirm_email") as l_confirm_email and then
l_email.value.same_string (l_confirm_email.value) and then
l_email.value.is_valid_as_string_8
then
-- emails matched?
l_user.set_email (l_email.value.to_string_8)
l_user_api.update_user (l_user)
r.add_success_message ("Email updated.")
r.set_redirection ("account/")
r.set_redirection_delay (3)
else
r.add_error_message ("Emails do not match!")
f := new_change_email_form (r)
r.set_main_content (f.to_html (r.wsf_theme))
end
else
r.add_error_message ("You can not change %"" + l_fieldname + "%" information!")
end end
end end
else
create b.make_empty
if l_fieldname.is_case_insensitive_equal_general ("password") then
f := new_change_password_form (r)
f.append_to_html (r.wsf_theme, b)
elseif l_fieldname.is_case_insensitive_equal_general ("email") then
f := new_change_email_form (r)
f.append_to_html (r.wsf_theme, b)
end
r.set_main_content (b)
end end
end end
r.execute r.execute
end end
handle_post_change_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached template_block ("post_change", r) as l_tpl_block then
r.add_block (l_tpl_block, "content")
end
r.execute
end
handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local local
l_response: CMS_RESPONSE l_response: CMS_RESPONSE
@@ -655,20 +832,8 @@ feature -- Handler
end end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"register", "reactivate", "new_password", "reset_password", "registration">> Result := <<"register", "reactivate", "new_password", "reset_password">>
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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -684,11 +849,56 @@ feature -- Handler
get_block_view_new_password (a_block_id, a_response) get_block_view_new_password (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then loc.starts_with ("account/reset-password") then elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then loc.starts_with ("account/reset-password") then
get_block_view_reset_password (a_block_id, a_response) get_block_view_reset_password (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("registration") and then loc.starts_with ("admin/pending-registrations") then
get_block_view_registration (a_block_id, a_response)
end end
end end
new_change_password_form (a_response: CMS_RESPONSE): CMS_FORM
local
fs: WSF_FORM_FIELD_SET
pwd: WSF_FORM_PASSWORD_INPUT
do
create Result.make (a_response.url ("account/change/password", Void), "change-password-form")
create fs.make
fs.set_legend ("Change password")
Result.extend (fs)
create pwd.make ("password")
pwd.set_label ("Password")
pwd.enable_required
fs.extend (pwd)
create pwd.make ("confirm_password")
pwd.set_label ("Confirm password")
pwd.enable_required
fs.extend (pwd)
-- create but.make_with_text ("op", "Confirm")
-- fs.extend (but)
fs.extend_html_text ("<button type=%"submit%">Confirm</button>")
end
new_change_email_form (a_response: CMS_RESPONSE): CMS_FORM
local
fs: WSF_FORM_FIELD_SET
tf: WSF_FORM_EMAIL_INPUT
do
create Result.make (a_response.url ("account/change/email", Void), "change-email-form")
create fs.make
fs.set_legend ("Change email")
Result.extend (fs)
create tf.make ("email")
tf.set_label ("Email")
tf.enable_required
fs.extend (tf)
create tf.make ("confirm_email")
tf.set_label ("Confirm email")
tf.enable_required
fs.extend (tf)
fs.extend_html_text ("<button type=%"submit%">Confirm</button>")
end
feature {NONE} -- Token Generation feature {NONE} -- Token Generation
new_token: STRING new_token: STRING
@@ -713,32 +923,24 @@ feature {NONE} -- Token Generation
Result := l_token Result := l_token
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 feature {NONE} -- Block views
get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if a_response.has_permission ("account register") then if a_response.has_permission ("account register") then
if a_response.request.is_get_request_method then if
if attached template_block (a_block_id, a_response) as l_tpl_block then a_response.request.is_get_request_method
or else (
a_response.values.has ("error_name")
or else a_response.values.has ("error_email")
)
then
if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
-- l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name")
-- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
-- l_tpl_block.set_value (a_response.values.item ("email"), "email")
-- l_tpl_block.set_value (a_response.values.item ("name"), "name")
l_tpl_block.set_value (form_registration_application_description (a_response.api), "application_description")
if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key") l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end end
@@ -749,28 +951,11 @@ feature {NONE} -- Block views
end end
end end
elseif a_response.request.is_post_request_method then elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then if attached smarty_template_block (Current, "post_register", a_response.api) as l_tpl_block then
if attached template_block (a_block_id, a_response) as l_tpl_block then a_response.add_block (l_tpl_block, "content")
-- l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name")
-- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
-- l_tpl_block.set_value (a_response.values.item ("email"), "email")
-- l_tpl_block.set_value (a_response.values.item ("name"), "name")
if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_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
else else
if attached template_block ("post_register", a_response) as l_tpl_block then debug ("cms")
a_response.add_block (l_tpl_block, "content") a_response.add_warning_message ("Error with block [" + a_block_id + "]")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end end
end end
end end
@@ -780,7 +965,7 @@ feature {NONE} -- Block views
get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if a_response.request.is_get_request_method then if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -789,7 +974,7 @@ feature {NONE} -- Block views
end end
elseif a_response.request.is_post_request_method then elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
-- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
-- l_tpl_block.set_value (a_response.values.item ("email"), "email") -- l_tpl_block.set_value (a_response.values.item ("email"), "email")
-- l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") -- l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active")
@@ -800,7 +985,7 @@ feature {NONE} -- Block views
end end
end end
else else
if attached template_block ("post_reactivate", a_response) as l_tpl_block then if attached smarty_template_block (Current, "post_reactivate", a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -814,7 +999,7 @@ feature {NONE} -- Block views
get_block_view_new_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_new_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if a_response.request.is_get_request_method then if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -823,7 +1008,7 @@ feature {NONE} -- Block views
end end
elseif a_response.request.is_post_request_method then elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_email") or else a_response.values.has ("error_username") then if a_response.values.has ("error_email") or else a_response.values.has ("error_username") then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
-- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") -- l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
-- l_tpl_block.set_value (a_response.values.item ("email"), "email") -- l_tpl_block.set_value (a_response.values.item ("email"), "email")
-- l_tpl_block.set_value (a_response.values.item ("error_username"), "error_username") -- l_tpl_block.set_value (a_response.values.item ("error_username"), "error_username")
@@ -835,7 +1020,7 @@ feature {NONE} -- Block views
end end
end end
else else
if attached template_block ("post_password", a_response) as l_tpl_block then if attached smarty_template_block (Current, "post_password", a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -849,7 +1034,7 @@ feature {NONE} -- Block views
get_block_view_reset_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_reset_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if a_response.request.is_get_request_method then if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
-- l_tpl_block.set_value (a_response.values.item ("token"), "token") -- l_tpl_block.set_value (a_response.values.item ("token"), "token")
-- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") -- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token")
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
@@ -860,7 +1045,7 @@ feature {NONE} -- Block views
end end
elseif a_response.request.is_post_request_method then elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
-- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") -- l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token")
-- l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") -- l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password")
-- l_tpl_block.set_value (a_response.values.item ("token"), "token") -- l_tpl_block.set_value (a_response.values.item ("token"), "token")
@@ -871,7 +1056,7 @@ feature {NONE} -- Block views
end end
end end
else else
if attached template_block ("post_reset", a_response) as l_tpl_block then if attached smarty_template_block (Current, "post_reset", a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -882,11 +1067,19 @@ feature {NONE} -- Block views
end end
end end
get_block_view_registration (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) feature -- Access: configuration
do
end
feature -- Recaptcha form_registration_application_description (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 ("forms.registration.application_description") as l_desc and then not l_desc.is_whitespace then
Result := utf.utf_32_string_to_utf_8_string_8 (l_desc)
end
end
end
recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8 recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key. -- Get recaptcha security key.
@@ -919,6 +1112,7 @@ feature -- Response Alter
a_response.add_javascript_url ("https://www.google.com/recaptcha/api.js") a_response.add_javascript_url ("https://www.google.com/recaptcha/api.js")
a_response.add_style (a_response.url ("/module/" + name + "/files/css/auth.css", Void), Void) a_response.add_style (a_response.url ("/module/" + name + "/files/css/auth.css", Void), Void)
end end
feature {NONE} -- Implementation feature {NONE} -- Implementation
is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN

View File

@@ -1,8 +0,0 @@
{
"email": "webmaster@example.com",
"subjet_register": "Thank you for regitering with us, activate account",
"subjet_activate": "New account ativation token",
"subjet_password": "Password Recovery!!!",
"subjet_oauth": "Welcome",
"smtp": "127.0.0.1"
}

View File

@@ -1,5 +1,10 @@
{ {
"subject": "Thank you for contacting us", "subject": "Thank you for contacting us",
"forms": {
"registration": {
"application_description": "Present yourself in a few lines, otherwise your application is likely to be rejected."
}
},
"recaptcha": { "recaptcha": {
"site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD", "site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD",
"secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx" "secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx"

View File

@@ -21,6 +21,6 @@
<p>To reject the registration, please click on the following link <p> <p>To reject the registration, please click on the following link <p>
<p><a href="$rejection_url<">$rejection_url</a></p> <p><a href="$rejection_url">$rejection_url</a></p>
</body> </body>
</html> </html>

View File

@@ -0,0 +1 @@
{include file="block_account_info.tpl" /}

View File

@@ -1,62 +1,34 @@
<div class="primary-tabs"> <div class="primary-tabs">
{if isset="$user"} {if isset="$user"}
<h3>Account Information</h3> <h3>Account Information</h3>
<div> <ul class="user-information">
<div> <div>
<div> <label>Username:</label> {$user.name/}
<label>Username:</label> {$user.name/}
</div>
<div>
<label>Email:</label> {$user.email/}
</div>
<div>
<label>Creation Date:</label> {$user.creation_date/}
</div>
<div>
<label>Last login:</label> {$user.last_login_date/}
</div>
<div>
<form method="get" action="{$site_url/}{$auth_login_strategy/}">
<button type="submit">Logout</button>
</form>
</div>
</div> </div>
</div> <div>
<hr> <label>Email:</label> {$user.email/}
{include file="block_change_password.tpl" /} </div>
<hr> <div>
<h4>Roles</h4> <label>Creation Date:</label> {$user.creation_date/} (UTC)
<div> </div>
{foreach item="ic" from="$roles"} <div>
<div> <label>Last login:</label> {$user.last_login_date/} (UTC)
<ul> </div>
<li> <div>
<strong>{$ic.name/}</strong> <form method="get" action="{$site_url/}account/roc-logout">
<ul> <button type="submit">Logout</button>
<li> <i>permissions</i> </form>
<ul> </div>
{foreach item="ip" from="$ic.permissions"} </ul>
<li>{$ip/}</li>
{/foreach}
</ul>
</li>
</ul>
</li>
</ul>
</div>
{/foreach}
</div>
<hr> <hr>
<h4>Profile</h4> <h4>Profile</h4>
<div> <ul class="user-profile">
{foreach item="the_value" key="the_name" from="$user.profile"} {foreach item="the_value" key="the_name" from="$user.profile"}
<div> <li>
<label>{$the_name/}:</label> {$the_value/} <label>{$the_name/}:</label><div>{$the_value/}</div>
</div> </li>
{/foreach} {/foreach}
</div> </ul>
{/if} {/if}
{unless isset="$user"} {unless isset="$user"}
<div> <div>

View File

@@ -1,7 +1,7 @@
<div> <div>
<form action="{$site_url/}account/change-password" method="post"> <form action="{$site_url/}account/change-password" method="post">
<fieldset> <fieldset>
<legend>Change Password Form</legend> <legend>Change Password</legend>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>

View File

@@ -1,29 +0,0 @@
<div class="primary-tabs">
{unless isset="$user"}
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div>
<div>
<form action method="POST">
<div>
<input type="text" name="username" required>
<label>Username</label>
</div>
<div>
<input type="password" name="password" required>
<label>Password</label>
</div>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div>
<div>
<div>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div>
{/unless}
</div>

View File

@@ -1,39 +1,38 @@
<div> <div>
<form action="{$site_url/}account/roc-register" method="post"> <form action="{$site_url/}account/roc-register" method="post">
<fieldset> <fieldset>
<legend>Registration</legend> <legend>Registration</legend>
<div> <div>
<input type="text" id="name" name="name" value="{$name/}" required autofocus /> <input type="text" id="name" name="name" value="{$name/}" required autofocus />
<label for="name">Name</label> <label for="name">Name</label>
{if isset="$error_name"} {if isset="$error_name"}
<span><i>{$error_name/}</i></span> <br> <span><i>{$error_name/}</i></span> <br>
{/if} {/if}
</div> </div>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>
</div> </div>
<div> <div>
<input type="email" id="email" name="email" value="{$email/}" required/> <input type="email" id="email" name="email" value="{$email/}" required/>
<label for="email">Email</label> <label for="email">Email</label>
{if isset="$error_email"} {if isset="$error_email"}
<span><i>{$error_email/}</i></span> <br> <span><i>{$error_email/}</i></span> <br/>
{/if} {/if}
</div> </div>
<div> <div>
<textarea rows="4" cols="50" name="personal_information" id="personal_information" required> <textarea rows="4" cols="50" name="personal_information" id="personal_information" required>{$personal_information/}</textarea>
{$personal_information/} <label for="personal_information">Tell us why you want to register an account</label>
</textarea> {if isset="$error_application"}
<label for="personal_information">Tell us why you want to register an account</label> <span><i>{$error_application/}</i></span><br/>
{if isset="$error_application"} {/if}
<span><i>{$error_application/}</i></span> <br> {if isset="$application_description"}
{/if} <br/>
</div> <p class="description">{$application_description/}</p>
{unless isempty="$recaptcha_site_key"} {/if}
<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div> </div>
<br/> {unless isempty="$recaptcha_site_key"}<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div><br/>{/unless}
{/unless} <button type="submit">Register</button>
<button type="submit">Register</button> </fieldset>
</fieldset> </form>
</form>
</div> </div>

View File

@@ -10,24 +10,17 @@ class
CMS_BASIC_AUTH_MODULE CMS_BASIC_AUTH_MODULE
inherit inherit
CMS_MODULE CMS_AUTH_MODULE_I
rename
module_api as basic_auth_api
redefine redefine
make,
filters, filters,
setup_hooks setup_hooks
end end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -35,26 +28,42 @@ feature {NONE} -- Initialization
make make
do do
Precursor
version := "1.0" version := "1.0"
description := "Service to manage basic authentication" description := "Service to manage basic authentication"
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
end end
feature -- Access feature -- Access
name: STRING = "basic_auth" name: STRING = "basic_auth"
feature -- Access: router feature -- Access: auth strategy
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) login_title: STRING = "Basic Auth"
-- Module specific login title.
login_location: STRING = "account/auth/roc-basic-login"
do_login_location: STRING = "roc-basic-login" -- IMPORTANT: it has to be at the root !
logout_location: STRING = "roc-basic-logoff" -- IMPORTANT: it has to be at the root !
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor> -- <Precursor>
do do
configure_api_login (a_api, a_router) if
configure_api_logoff (a_api, a_router) a_response.is_authenticated and then
a_router.handle ("/account/roc-basic-auth", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_basic_auth (a_api, ?, ?)), a_router.methods_head_get) a_response.request.http_authorization /= Void
then
Result := True
end
end end
feature {CMS_API} -- Access: API
oauth20_api: detachable CMS_AUTH_API_I
-- <Precursor>
feature -- Access: filter feature -- Access: filter
filters (a_api: CMS_API): detachable LIST [WSF_FILTER] filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
@@ -65,6 +74,16 @@ feature -- Access: filter
Result.extend (create {CMS_BASIC_AUTH_FILTER}.make (a_api)) Result.extend (create {CMS_BASIC_AUTH_FILTER}.make (a_api))
end end
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
configure_api_login (a_api, a_router)
configure_api_logoff (a_api, a_router)
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_basic_auth (a_api, ?, ?)), a_router.methods_head_get)
end
feature {NONE} -- Implementation: routes feature {NONE} -- Implementation: routes
configure_api_login (api: CMS_API; a_router: WSF_ROUTER) configure_api_login (api: CMS_API; a_router: WSF_ROUTER)
@@ -75,7 +94,7 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api) create l_bal_handler.make (api)
create l_methods create l_methods
l_methods.enable_get l_methods.enable_get
a_router.handle ("/basic_auth_login", l_bal_handler, l_methods) a_router.handle ("/" + do_login_location, l_bal_handler, l_methods)
end end
configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER) configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER)
@@ -86,16 +105,38 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api) create l_bal_handler.make (api)
create l_methods create l_methods
l_methods.enable_get l_methods.enable_get
a_router.handle ("/basic_auth_logoff", l_bal_handler, l_methods) a_router.handle ("/" + logout_location, l_bal_handler, l_methods)
end end
handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
vals: CMS_VALUE_TABLE
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_value ("Basic Auth", "optional_content_type") if api.user_is_authenticated then
r.add_error_message ("You are already signed in!")
r.set_main_content (r.link ("Logout", "account/roc-logout", Void))
else
if attached smarty_template_login_block (req, Current, "login", api) as l_tpl_block then
r.add_javascript_url (r.url ("module/" + name + "/files/js/roc_basic_auth.js", Void))
create vals.make (1)
-- add the variable to the block
api.hooks.invoke_value_table_alter (vals, r)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
end
r.add_block (l_tpl_block, "content")
else
debug ("cms")
r.add_warning_message ("Error with block [login]")
end
end
r.set_value ("Basic Auth", "optional_content_type")
end
r.execute r.execute
end end
@@ -104,113 +145,35 @@ feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_hooks) Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end end
feature -- Hooks feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if a_response.is_authenticated then
a_value.force ("basic_auth_logoff", "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 then
across
a_menu_system.primary_menu.items as ic
until
lnk2 /= Void
loop
if ic.item.location.same_string ("account/roc-logout") then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make ("Logout", "basic_auth_logoff")
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
else
if a_response.location.starts_with ("account/") then
create lnk.make ("Basic Auth", "account/roc-basic-auth")
lnk.set_expandable (True)
a_response.add_to_primary_tabs (lnk)
end
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"login">> 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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if if a_block_id.is_case_insensitive_equal_general ("login") then
a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.location.starts_with ("account/roc-basic-auth")
then
a_response.add_javascript_url (a_response.url ("module/" + name + "/files/js/roc_basic_auth.js", Void)) a_response.add_javascript_url (a_response.url ("module/" + name + "/files/js/roc_basic_auth.js", Void))
get_block_view_login (a_block_id, a_response) get_block_view_login (a_block_id, a_response)
end end
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 feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local local
vals: CMS_VALUE_TABLE vals: CMS_VALUE_TABLE
do do
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached smarty_template_login_block (a_response.request, Current, a_block_id, a_response.api) as l_tpl_block then
create vals.make (1) create vals.make (1)
-- add the variable to the block -- add the variable to the block
value_table_alter (vals, a_response) a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across across
vals as ic vals as ic
loop loop

View File

@@ -9,54 +9,46 @@ class
CMS_BASIC_AUTH_FILTER CMS_BASIC_AUTH_FILTER
inherit inherit
WSF_URI_TEMPLATE_HANDLER CMS_AUTH_FILTER_I
CMS_HANDLER
WSF_FILTER REFACTORING_HELPER
create create
make make
feature -- Basic operations feature -- Basic operations
auth_strategy: STRING
do
Result := {CMS_BASIC_AUTH_MODULE}.logout_location
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter. -- Execute the filter.
local local
l_auth: HTTP_AUTHORIZATION l_auth: HTTP_AUTHORIZATION
do do
api.logger.put_debug (generator + ".execute ", Void)
create l_auth.make (req.http_authorization) create l_auth.make (req.http_authorization)
debug
if attached req.raw_header_data as l_raw_data then
api.logger.put_debug (generator + ".execute " + (create {UTF_CONVERTER}).escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
end
end
-- A valid user
if if
(attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal_general ("basic")) and then l_auth.is_basic and then
attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password attached l_auth.login as l_auth_login and then
attached l_auth.password as l_auth_password
then then
if api.user_api.is_valid_credential (l_auth_login, l_auth_password) then if
if attached api.user_api.user_by_name (l_auth_login) as l_user then api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
debug ("refactor_fixme") attached api.user_api.user_by_name (l_auth_login) as l_user
fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT") then
-- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user)) debug ("refactor_fixme")
-- other authentication filters (OpenID, etc) should implement the same approach. fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT")
end -- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user))
set_current_user (req, l_user) -- other authentication filters (OpenID, etc) should implement the same approach.
execute_next (req, res)
else
debug ("refactor_fixme")
to_implement ("Internal server error")
end
end end
set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void)
execute_next (req, res)
end end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
execute_next (req, res)
end end
execute_next (req, res)
end end
end end

View File

@@ -49,9 +49,9 @@ feature -- HTTP Methods
-- <Precursor> -- <Precursor>
do do
api.logger.put_information (generator + ".do_get Processing basic auth login", Void) api.logger.put_information (generator + ".do_get Processing basic auth login", Void)
if attached {STRING_32} current_user_name (req) as l_user then if api.user_is_authenticated then
if attached {WSF_STRING} req.query_parameter ("destination") as l_uri then if attached {WSF_STRING} req.query_parameter ("destination") as l_uri then
redirect_to (req.absolute_script_url (l_uri.url_encoded_value), res) redirect_to (req.absolute_script_url (l_uri.url_encoded_value), res)
else else
redirect_to (req.absolute_script_url ("/"), res) redirect_to (req.absolute_script_url ("/"), res)
end end

View File

@@ -51,11 +51,11 @@ feature -- HTTP Methods
do do
api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void)
if attached req.query_parameter ("prompt") as l_prompt then if attached req.query_parameter ("prompt") as l_prompt then
unset_current_user (req) api.unset_current_user (req)
send_access_denied_message (res) send_access_denied_message (res)
else else
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
unset_current_user (req) api.unset_current_user (req)
l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection
l_url := req.absolute_script_url ("") l_url := req.absolute_script_url ("")
i := l_url.substring_index ("://", 1) i := l_url.substring_index ("://", 1)

View File

@@ -1,307 +1,291 @@
var ROC_AUTH = ROC_AUTH || { }; var ROC_AUTH = ROC_AUTH || { };
var loginURL = "/basic_auth_login"; var loginURL = "/roc-basic-login";
var logoutURL = "/basic_auth_logoff"; var logoutURL = "/roc-basic-logoff";
var userAgent = navigator.userAgent.toLowerCase(); var userAgent = navigator.userAgent.toLowerCase();
var firstLogIn = true; var firstLogIn = true;
ROC_AUTH.login = function() { ROC_AUTH.login = function() {
var form = document.forms['cms_basic_auth']; var form = document.forms['cms_basic_auth'];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
//var host = form.host.value; //var host = form.host.value;
var origin = window.location.origin + window.location.pathname; var origin = window.location.origin + window.location.pathname;
var _login = function(){ var _login = function(){
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') !== null ) { if (document.getElementById('myModalFormId') === null ) {
ROC_AUTH.remove ('myModalFormId'); var newdiv = document.createElement('div');
} newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
} else {
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
// .. ?
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}
}
}
}
}
if (username === "" || password === "") { var userAgent = navigator.userAgent.toLowerCase();
if (document.getElementById('myModalFormId') === null ) { if (userAgent.indexOf("firefox") != -1) { //TODO: check version number
var newdiv = document.createElement('div'); if (firstLogIn) {
newdiv.innerHTML = "<br>Invalid Credentials</br>"; _login();
newdiv.id = 'myModalFormId'; } else {
$(".primary-tabs").append(newdiv); ROC_AUTH.logoff(_login);
} }
}else{ } else {
_login();
//Instantiate HTTP Request }
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
}
} if (firstLogIn) {
} firstLogIn = false;
} }
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.login_with_redirect = function() { ROC_AUTH.login_with_redirect = function() {
var form = document.forms[2]; var form = document.forms[2];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
var host = form.host.value; var host = form.host.value;
var _login = function(){ var _login = function(){
var redirectURL = form.redirect && form.redirect.value || "";
$("#imgProgressRedirect").show();
var redirectURL = form.redirect && form.redirect.value || ""; if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
} else {
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host + loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host + "/";
} else {
window.location=host + redirectURL;
}
} else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
$("#imgProgressRedirect").show(); var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (document.getElementById('myModalFormId') !== null ) { if (firstLogIn) {
ROC_AUTH.remove ('myModalFormId'); _login();
} } else {
ROC_AUTH.logoff(_login);
}
if (username === "" || password === "") { } else{
if (document.getElementById('myModalFormId') === null ) { _login();
var newdiv = document.createElement('div'); }
newdiv.innerHTML = "<br>Invalid Credentials</br>"; if (firstLogIn) {
newdiv.id = 'myModalFormId'; firstLogIn = false;
$(".primary-tabs").append(newdiv); }
$("#imgProgressRedirect").hide();
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(loginURL), true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host.concat("/");
} else {
window.location=host.concat(redirectURL);
}
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.getQueryParameterByName = function (name) { ROC_AUTH.getQueryParameterByName = function (name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
results = regex.exec(location.search); return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
} }
ROC_AUTH.logoff = function(callback){ ROC_AUTH.logoff = function(callback){
var form = document.forms[0]; var form = document.forms[0];
var host = form.host.value; var host = form.host.value;
if (userAgent.indexOf("msie") != -1) { if (userAgent.indexOf("msie") != -1) {
document.execCommand("ClearAuthenticationCache"); document.execCommand("ClearAuthenticationCache");
} } else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest();
var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest(); //Logout. Tell the server not to return the "WWW-Authenticate" header
request1.open("GET", host + logoutURL + "?prompt=false", true);
//Logout. Tell the server not to return the "WWW-Authenticate" header request1.send("");
request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); request1.onreadystatechange = function(){
request1.send(""); if (request1.readyState == 4) {
request1.onreadystatechange = function(){ //Sign in with dummy credentials to clear the auth cache
if (request1.readyState == 4) { request2.open("GET", host + logoutURL, true, "logout", "logout");
request2.send("");
//Sign in with dummy credentials to clear the auth cache request2.onreadystatechange = function(){
request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); if (request2.readyState == 4) {
request2.send(""); if (callback!=null) {
callback.call();
request2.onreadystatechange = function(){ } else {
if (request2.readyState == 4) { window.location=host + logoutURL;
if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} }
} }
} }
}
} }
} } else {
} var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
else { request.open("GET", host + logoutURL, true, "logout", "logout");
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); request.send("");
request.open("GET", host.concat(logoutURL), true, "logout", "logout"); request.onreadystatechange = function(){
request.send(""); if (request.status==401 || request.status==403 ) {
request.onreadystatechange = function(){ window.location=host + logoutURL;
if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); }
} }
} }
}
}; };
ROC_AUTH.remove = function (id) ROC_AUTH.remove = function (id) {
{ var element = document.getElementById(id);
var element = document.getElementById(id); element.outerHTML = "";
element.outerHTML = ""; delete element;
delete element; return;
return;
}; };
$(document).ready(function() { $(document).ready(function() {
if (typeof String.prototype.contains != 'function') {
if (typeof String.prototype.contains != 'function') { String.prototype.contains = function (str){
String.prototype.contains = function (str){ return this.indexOf(str) != -1;
return this.indexOf(str) != -1; };
}; }
} ROC_AUTH.progressive_loging();
ROC_AUTH.progressive_loging();
}); });
ROC_AUTH.progressive_loging = function () { ROC_AUTH.progressive_loging = function () {
ROC_AUTH.login_href();
ROC_AUTH.login_href();
}; };
$(document).keypress(function(e) { $(document).keypress(function(e) {
if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) {
ROC_AUTH.login(); ROC_AUTH.login();
} }
}); });
ROC_AUTH.OnOneClick = function(event) { ROC_AUTH.OnOneClick = function(event) {
event.preventDefault(); event.preventDefault();
if ( document.forms[0] === undefined ) { if ( document.forms[0] === undefined ) {
ROC_AUTH.create_form(); ROC_AUTH.create_form();
} }
return false; return false;
}; };
ROC_AUTH.login_href = function() { ROC_AUTH.login_href = function() {
var els = document.getElementsByTagName("a"); var els = document.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) { for (var i = 0, l = els.length; i < l; i++) {
var el = els[i]; var el = els[i];
if (el.href.contains("/basic_auth_login?destination")) { if (el.href.contains(loginURL + "?destination")) {
loginURL = el.href; // loginURL = el.href;
var OneClick = el; var OneClick = el;
OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false);
} }
} }
}; };
ROC_AUTH.create_form = function() { ROC_AUTH.create_form = function() {
// Fetching HTML Elements in Variables by ID. // Fetching HTML Elements in Variables by ID.
var createform = document.createElement('form'); // Create New Element Form var createform = document.createElement('form'); // Create New Element Form
createform.setAttribute("action", ""); // Setting Action Attribute on Form createform.setAttribute("action", ""); // Setting Action Attribute on Form
createform.setAttribute("method", "post"); // Setting Method Attribute on Form createform.setAttribute("method", "post"); // Setting Method Attribute on Form
$("body").append(createform); $("body").append(createform);
var heading = document.createElement('h2'); // Heading of Form var heading = document.createElement('h2'); // Heading of Form
heading.innerHTML = "Login Form "; heading.innerHTML = "Login Form ";
createform.appendChild(heading); createform.appendChild(heading);
var line = document.createElement('hr'); // Giving Horizontal Row After Heading var line = document.createElement('hr'); // Giving Horizontal Row After Heading
createform.appendChild(line); createform.appendChild(line);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var namelabel = document.createElement('label'); // Create Label for Name Field var namelabel = document.createElement('label'); // Create Label for Name Field
namelabel.innerHTML = "Username : "; // Set Field Labels namelabel.innerHTML = "Username : "; // Set Field Labels
createform.appendChild(namelabel); createform.appendChild(namelabel);
var inputelement = document.createElement('input'); // Create Input Field for UserName var inputelement = document.createElement('input'); // Create Input Field for UserName
inputelement.setAttribute("type", "text"); inputelement.setAttribute("type", "text");
inputelement.setAttribute("name", "username"); inputelement.setAttribute("name", "username");
inputelement.setAttribute("required","required"); inputelement.setAttribute("required","required");
createform.appendChild(inputelement); createform.appendChild(inputelement);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var passwordlabel = document.createElement('label'); // Create Label for Password Field var passwordlabel = document.createElement('label'); // Create Label for Password Field
passwordlabel.innerHTML = "Password : "; passwordlabel.innerHTML = "Password : ";
createform.appendChild(passwordlabel); createform.appendChild(passwordlabel);
var passwordelement = document.createElement('input'); // Create Input Field for Password. var passwordelement = document.createElement('input'); // Create Input Field for Password.
passwordelement.setAttribute("type", "password"); passwordelement.setAttribute("type", "password");
passwordelement.setAttribute("name", "password"); passwordelement.setAttribute("name", "password");
passwordelement.setAttribute("id", "password"); passwordelement.setAttribute("id", "password");
passwordelement.setAttribute("required","required"); passwordelement.setAttribute("required","required");
createform.appendChild(passwordelement); createform.appendChild(passwordelement);
var passwordbreak = document.createElement('br'); var passwordbreak = document.createElement('br');
createform.appendChild(passwordbreak); createform.appendChild(passwordbreak);
var submitelement = document.createElement('button'); // Append Submit Button var submitelement = document.createElement('button'); // Append Submit Button
submitelement.setAttribute("type", "button"); submitelement.setAttribute("type", "button");
submitelement.setAttribute("onclick", "ROC_AUTH.login();"); submitelement.setAttribute("onclick", "ROC_AUTH.login();");
submitelement.innerHTML = "Sign In "; submitelement.innerHTML = "Sign In ";
createform.appendChild(submitelement); createform.appendChild(submitelement);
}; };
@@ -310,16 +294,16 @@ var password = document.getElementById("password");
var confirm_password = document.getElementById("confirm_password"); var confirm_password = document.getElementById("confirm_password");
ROC_AUTH.validatePassword =function(){ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
if(password.value != confirm_password.value) { if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match"); confirm_password.setCustomValidity("Passwords Don't Match");
} else { } else {
confirm_password.setCustomValidity(''); confirm_password.setCustomValidity('');
} }
} }
} }
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword(); password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword; confirm_password.onkeyup = ROC_AUTH.validatePassword;
} }

View File

@@ -1,29 +1,24 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Basic Auth" relies on the HTTP basic access authentication.<br/>(see also: <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">https://en.wikipedia.org/wiki/Basic_access_authentication</a> )</div>
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<div> <form name="cms_basic_auth" action="{$site_url/}roc-basic-login" method="POST">
<form name="cms_basic_auth" action method="POST"> {unless isempty="$site_destination"}<input type="hidden" name="destination" value="{$site_destination/}">{/unless}
<div> <input type="hidden" name="host" id="host" value="{$site_url/}">
<input type="text" name="username" id="username" required> <div>
<label>Username</label> <input type="text" name="username" id="username" required>
</div> <label>Username</label>
</div>
<div> <div>
<input type="password" name="password" id="password" required> <input type="password" name="password" id="password" required>
<label>Password</label> <label>Password</label>
</div> </div>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
<button type="button" onclick="ROC_AUTH.login();">Login</button> </form>
</form>
</div>
</div> </div>
<div> <div>
<div> <a href="{$site_url/}account/new-password">Forgot password?</a>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div> </div>
{/unless}
</div> </div>
{/unless}

View File

@@ -78,27 +78,17 @@ feature {CMS_API} -- Module Initialization
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
install (api: CMS_API) install (a_api: CMS_API)
local
sql: STRING
do do
-- Schema -- Schema
if attached api.storage.as_sql_storage as l_sql_storage then if attached a_api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
sql := "[
CREATE TABLE blog_post_nodes( if l_sql_storage.has_error then
`nid` INTEGER NOT NULL CHECK("nid">=0), a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
`revision` INTEGER NOT NULL, else
`tags` VARCHAR(255), Precursor {CMS_MODULE} (a_api)
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);
]"
l_sql_storage.sql_execute_script (sql, Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for blog module", generating_type)
end
end end
Precursor (api)
end end
end end

View File

@@ -0,0 +1,6 @@
CREATE TABLE blog_post_nodes(
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);

View File

@@ -15,14 +15,11 @@ inherit
module_api as contact_api module_api as contact_api
redefine redefine
setup_hooks, setup_hooks,
is_installed,
install, install,
initialize, initialize,
contact_api contact_api
end end
SHARED_HTML_ENCODER
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_BLOCK_HELPER CMS_HOOK_BLOCK_HELPER
@@ -53,7 +50,6 @@ feature {NONE} -- Initialization
package := "messaging" package := "messaging"
end end
feature -- Access feature -- Access
name: STRING = "contact" name: STRING = "contact"
@@ -86,14 +82,6 @@ feature {CMS_API} -- Module Initialization
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed?
local
ut: FILE_UTILITIES
do
Result := ut.directory_path_exists (file_system_storage_path (api))
end
install (api: CMS_API) install (api: CMS_API)
local local
retried: BOOLEAN retried: BOOLEAN
@@ -102,6 +90,7 @@ feature {CMS_API} -- Module management
if not retried then if not retried then
create d.make_with_path (file_system_storage_path (api)) create d.make_with_path (file_system_storage_path (api))
d.recursive_create_dir d.recursive_create_dir
Precursor {CMS_MODULE}(api) -- Marked installed
end end
rescue rescue
retried := True retried := True
@@ -192,7 +181,7 @@ feature -- Hooks
if a_block_id.is_case_insensitive_equal_general ("contact") then if a_block_id.is_case_insensitive_equal_general ("contact") then
-- "contact", "post_contact" -- "contact", "post_contact"
if a_response.request.is_get_request_method then if a_response.request.is_get_request_method then
if attached template_block (Current, a_block_id, a_response) as l_tpl_block then if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key") l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end end
@@ -212,7 +201,7 @@ feature -- Hooks
f: CMS_FORM f: CMS_FORM
do do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void) a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void)
if attached template_block (Current, "contact", a_response) as l_tpl_block then if attached smarty_template_block (Current, "contact", api) as l_tpl_block then
if attached recaptcha_site_key (api) as l_recaptcha_site_key then if attached recaptcha_site_key (api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key") l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end end
@@ -296,8 +285,8 @@ feature -- Hooks
r.values.force (False, "has_error") r.values.force (False, "has_error")
create vars.make_caseless (5) create vars.make_caseless (5)
vars.put (html_encoded (api.setup.site_url), "siteurl") vars.put (safe_html_encoded (api.setup.site_url), "siteurl")
vars.put (html_encoded (api.setup.site_name), "sitename") vars.put (safe_html_encoded (api.setup.site_name), "sitename")
write_debug_log (generator + ".handle_post_contact {Form Parameters:" + form_parameters_as_string (req) + "}") write_debug_log (generator + ".handle_post_contact {Form Parameters:" + form_parameters_as_string (req) + "}")
if if
@@ -350,7 +339,7 @@ feature -- Hooks
r.values.force (True, "has_error") r.values.force (True, "has_error")
vars.put ("True", "has_error") vars.put ("True", "has_error")
end end
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then if attached smarty_template_block_with_values (Current, "post_contact", api, vars) as l_tpl_block then
across across
r.values as tb r.values as tb
loop loop
@@ -365,7 +354,7 @@ feature -- Hooks
-- send a bad request status code and redisplay the form with the previous data loaded. -- send a bad request status code and redisplay the form with the previous data loaded.
r.set_value (False, "error") r.set_value (False, "error")
r.set_status_code ({HTTP_STATUS_CODE}.bad_request) r.set_status_code ({HTTP_STATUS_CODE}.bad_request)
if attached template_block_with_values (Current, "contact", r, vars) as l_tpl_block then if attached smarty_template_block_with_values (Current, "contact", api, vars) as l_tpl_block then
across across
r.values as tb r.values as tb
loop loop
@@ -388,7 +377,7 @@ feature -- Hooks
write_error_log (generator + ".handle_post_contact: Internal Server error") write_error_log (generator + ".handle_post_contact: Internal Server error")
r.values.force (True, "has_error") r.values.force (True, "has_error")
r.set_status_code ({HTTP_CONSTANTS}.internal_server_error) r.set_status_code ({HTTP_CONSTANTS}.internal_server_error)
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then if attached smarty_template_block_with_values (Current, "post_contact", api, vars) as l_tpl_block then
across across
r.values as tb r.values as tb
loop loop
@@ -431,31 +420,8 @@ feature {NONE} -- Helpers
end end
end end
feature {NONE} -- HTML ENCODING.
html_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8
do
if s /= Void then
Result := html_encoder.general_encoded_string (s)
else
create Result.make_empty
end
end
feature {NONE} -- Contact Message feature {NONE} -- Contact Message
template_block_with_values (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_values: STRING_TABLE [ANY]): like template_block
do
Result := template_block (a_module, a_block_id, a_response)
if Result /= Void then
across
a_values as ic
loop
Result.set_value (ic.item, ic.key)
end
end
end
email_html_message (a_message_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_html_encoded_values: STRING_TABLE [READABLE_STRING_8]): STRING email_html_message (a_message_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_html_encoded_values: STRING_TABLE [READABLE_STRING_8]): STRING
-- html message related to `a_message_id'. -- html message related to `a_message_id'.
local local
@@ -463,7 +429,6 @@ feature {NONE} -- Contact Message
p: detachable PATH p: detachable PATH
tpl: CMS_SMARTY_TEMPLATE_BLOCK tpl: CMS_SMARTY_TEMPLATE_BLOCK
exp: CMS_STRING_EXPANDER [STRING_8] exp: CMS_STRING_EXPANDER [STRING_8]
utf: UTF_CONVERTER
do do
write_debug_log (generator + ".email_html_message for [" + a_message_id + " ]") write_debug_log (generator + ".email_html_message for [" + a_message_id + " ]")

View File

@@ -0,0 +1,170 @@
note
description: "[
Module that provide custom block factory.
]"
author: "$Author: jfiat $"
date: "$Date: 2016-01-08 22:43:12 +0100 (ven., 08 janv. 2016) $"
revision: "$Revision: 98369 $"
class
CMS_CUSTOM_BLOCK_MODULE
inherit
CMS_MODULE
rename
module_api as custom_block_api
redefine
initialize,
setup_hooks,
custom_block_api
end
CMS_HOOK_RESPONSE_BLOCK
CMS_HOOK_BLOCK_HELPER
CMS_HOOK_AUTO_REGISTER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "Custom Block"
package := "layout"
end
feature -- Access
name: STRING = "custom_block"
-- <Precursor>
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- Initialize Current module with `api'.
do
create custom_block_api.make (api)
Precursor (api)
end
custom_block_api: detachable CMS_MODULE_API
-- <Precursor>.
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration.
do
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)
end
feature -- Hooks
block_identifiers (a_response: detachable CMS_RESPONSE): detachable ARRAYED_LIST [READABLE_STRING_8]
-- <Precursor>
local
api: CMS_API
l_name: READABLE_STRING_32
l_block_id: READABLE_STRING_8
l_conds: detachable ARRAYED_LIST [CMS_BLOCK_CONDITION]
do
if attached custom_block_api as l_mod_api then
api := l_mod_api.cms_api
if
attached api.module_configuration (Current, name) as cfg and then
attached cfg.table_keys ("blocks") as lst
then
create Result.make (0)
across
lst as ic
loop
l_name := ic.item
if l_name.is_valid_as_string_8 then
l_block_id := l_name.to_string_8
if a_response /= Void then
if attached cfg.text_list_item ("blocks." + l_block_id + ".conditions") as l_cond_expressions then
if l_conds = Void then
create l_conds.make (l_cond_expressions.count)
end
across
l_cond_expressions as exp_ic
loop
l_conds.force (create {CMS_BLOCK_EXPRESSION_CONDITION}.make (exp_ic.item))
end
end
if l_conds = Void or else l_conds.is_empty then
Result.force ("?" + l_block_id)
elseif are_conditions_satisfied (l_conds, a_response) then
Result.force (l_block_id)
end
else
Result.force ("?" + l_block_id)
end
end
end
end
end
end
are_conditions_satisfied (a_conditions: LIST [CMS_BLOCK_CONDITION]; a_response: CMS_RESPONSE): BOOLEAN
-- Are `a_conditions' satisfied for `a_response'?
do
Result := across a_conditions as ic some ic.item.satisfied_for_response (a_response) end
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local
l_region: detachable READABLE_STRING_8
l_cond: CMS_BLOCK_EXPRESSION_CONDITION
l_block_pref: STRING
do
if attached smarty_template_block (Current, a_block_id, a_response.api) as bk then
if attached a_response.api.module_configuration (Current, name) as cfg then
l_block_pref := "blocks." + a_block_id
if
attached cfg.text_item (l_block_pref + ".region") as s and then
s.is_valid_as_string_8
then
l_region := s.to_string_8
end
bk.set_weight (cfg.integer_item (l_block_pref + ".weight"))
bk.set_title (cfg.text_item (l_block_pref + ".title"))
if attached cfg.text_item (l_block_pref + ".is_raw") as l_is_raw then
bk.set_is_raw (l_is_raw.is_case_insensitive_equal ("yes"))
end
if attached cfg.text_list_item (l_block_pref + ".conditions") as l_cond_exp_list then
across
l_cond_exp_list as ic
loop
create l_cond.make (ic.item)
bk.add_condition (l_cond)
end
end
end
a_response.add_block (bk, l_region)
else
a_response.add_debug_message ("Missing template for custom block %"" + a_block_id + "%"!")
end
end
end

View File

@@ -0,0 +1,21 @@
<?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="custom_block_module" uuid="F9B7C390-04F2-4E59-9040-00DD68A5BDBC" library_target="custom_block_module">
<target name="custom_block_module">
<root all_classes="true" />
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard"/>
<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"/>
<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"/>
<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=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,22 @@
<?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="custom_block_module" uuid="F9B7C390-04F2-4E59-9040-00DD68A5BDBC" library_target="custom_block_module">
<target name="custom_block_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.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=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,11 @@
{
"blocks": {
"test": {
"title": "Custom block test",
"is_raw": "yes",
"region": "footer",
"weight": 100,
"conditions": ["path:demo/*"]
}
}
}

View File

@@ -0,0 +1,3 @@
<div>
This is a nice custom block test for site {$sitename/}.
</div>

View File

@@ -346,9 +346,11 @@ feature -- Hook
-- Hook execution on collection of menu contained by `a_menu_system' -- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'. -- for related response `a_response'.
do do
a_menu_system.navigation_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds", "feed_aggregation/")) if a_response.is_authenticated then
if a_response.has_permission (permission__manage_feed_aggregator) then a_menu_system.navigation_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds", "feed_aggregation/"))
a_menu_system.management_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds (admin)", "admin/feed_aggregator/")) if a_response.has_permission (permission__manage_feed_aggregator) then
a_menu_system.management_menu.extend_into (create {CMS_LOCAL_LINK}.make ("Feeds (admin)", "admin/feed_aggregator/"), "Admin", "admin")
end
end end
end end

69
modules/files/cms_file.e Normal file
View File

@@ -0,0 +1,69 @@
note
description: "Interface representing any files under `{CMS_API}.files_location' ."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE
create
make
feature {NONE} -- Initializaion
make (a_relative_path: PATH; a_api: CMS_API)
do
cms_api := a_api
relative_path := a_relative_path
end
cms_api: CMS_API
feature -- Access
filename: STRING_32
-- File name of Current file.
local
p: PATH
do
p := relative_path
if attached p.entry as e then
Result := e.name
else
Result := p.name
end
end
relative_path: PATH
-- Path relative the `CMS_API.files_location'.
owner: detachable CMS_USER
-- Optional owner.
feature -- Status report
is_directory: BOOLEAN
local
d: DIRECTORY
do
create d.make_with_path (cms_api.files_location.extended_path (relative_path))
Result := d.exists
end
is_file: BOOLEAN
local
f: RAW_FILE
do
create f.make_with_path (cms_api.files_location.extended_path (relative_path))
Result := f.exists
end
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
end

View File

@@ -0,0 +1,57 @@
note
description: "Metadata associated to a CMS_FILE."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_METADATA
create
make,
make_empty
feature {NONE} -- Initialization
make_empty
do
create date.make_now_utc
end
make (f: CMS_FILE)
do
make_empty
end
feature -- Access
user: detachable CMS_USER
date: detachable DATE_TIME
size: INTEGER
file_type: detachable READABLE_STRING_8
feature -- Element change
set_user (u: detachable CMS_USER)
do
user := u
end
set_date (dt: detachable DATE_TIME)
do
date := dt
end
set_size (a_size: INTEGER)
do
size := a_size
end
set_file_type (a_type: detachable READABLE_STRING_8)
do
file_type := a_type
end
end

View File

@@ -0,0 +1,272 @@
note
description: "API to manage files."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILES_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create
make
feature -- Access : path
uploads_relative_path: PATH
-- Path relative to `{CMS_API}.files_location'.
do
create Result.make_from_string (uploads_directory_name)
end
uploads_directory: PATH
do
Result := cms_api.files_location.extended (uploads_directory_name)
end
uploaded_file_path (f: READABLE_STRING_GENERAL): PATH
do
Result := uploads_directory.extended (f)
end
thumbnail_directory: PATH
do
Result := uploads_directory.extended (thumbnail_directory_name)
end
feature {CMS_FILES_MODULE} -- Access : metadata path
metadata_path (f: READABLE_STRING_GENERAL): PATH
do
Result := metadata_directory.extended (f).appended_with_extension ("cms-metadata")
end
metadata_directory: PATH
do
Result := uploads_directory.extended (metadata_directory_name)
end
feature -- Access : links
file_link (f: CMS_FILE): CMS_LOCAL_LINK
local
s: STRING
do
s := "files"
across
f.relative_path.components as ic
loop
s.append_character ('/')
s.append (percent_encoded (ic.item.name))
end
create Result.make (f.filename, s)
end
feature {NONE} -- Constants
uploads_directory_name: STRING = "uploads"
metadata_directory_name: STRING = ".metadata"
thumbnail_directory_name: STRING = ".thumbnails"
feature -- Factory
new_file (a_relative_path: PATH): CMS_FILE
-- New CMS_FILE for path `a_relative_path' relative to `files' directory.
do
create Result.make (a_relative_path, cms_api)
end
new_uploads_file (p: PATH): CMS_FILE
-- New uploaded path from `p' relative to `uploads_directory'.
do
create Result.make (uploads_relative_path.extended_path (p), cms_api)
end
feature -- Storage
delete_file (fn: READABLE_STRING_GENERAL)
-- Delete file at `fn'.
local
p: PATH
do
error_handler.reset
p := uploaded_file_path (fn)
safe_delete (p)
if not has_error then
p := metadata_path (fn)
safe_delete (p)
end
end
save_uploaded_file (uf: CMS_UPLOADED_FILE)
local
p: PATH
ut: FILE_UTILITIES
stored: BOOLEAN
original_name: STRING_32
n: INTEGER_32
finished: BOOLEAN
do
reset_error
create original_name.make_from_string (uf.filename)
p := uf.location
if not p.is_absolute then
p := uploads_directory.extended_path (p)
end
if ut.file_path_exists (p) then
from
n := 1
until
finished
loop
if ut.file_path_exists (p) then
uf.set_new_location_with_number (n)
p := uf.location
if p.is_absolute then
else
p := uploads_directory.extended_path (p)
end
n := n + 1
else
finished := True
end
end
stored := uf.move_to (p)
else
-- move file to path
stored := uf.move_to (p)
end
if stored then
save_metadata_from_uploaded_file (uf, cms_api.user)
else
error_handler.add_custom_error (-1, "uploaded file storage failed", "Issue occurred when saving uploaded file!")
end
end
save_metadata_from_uploaded_file (a_uploaded_file: CMS_UPLOADED_FILE; u: detachable CMS_USER)
local
f: detachable RAW_FILE
h_date: HTTP_DATE
retried: BOOLEAN
do
if retried then
-- FIXME: Report error?
if f /= Void and then not f.is_closed then
f.close
end
else
-- create a file for metadata
create f.make_with_path (metadata_path (a_uploaded_file.filename))
if f.exists then
f.open_write
else
f.create_read_write
end
-- insert username
if u /= Void then
f.put_string (u.id.out)
f.put_new_line
-- f.put_string (utf.utf_32_string_to_utf_8_string_8 (u.name))
-- f.put_new_line
else
f.put_new_line
f.put_new_line
end
-- insert uploaded_time
create h_date.make_now_utc
f.put_string (h_date.timestamp.out)
f.put_new_line
-- insert size of file
f.put_string (a_uploaded_file.size.out)
f.put_new_line
-- insert file type
if attached a_uploaded_file.type as type then
f.put_string (type.out)
f.put_new_line
end
f.close
end
rescue
retried := True
retry
end
metadata (a_cms_file: CMS_FILE): detachable CMS_FILE_METADATA
local
f: RAW_FILE
s: READABLE_STRING_8
do
if attached metadata_path (a_cms_file.filename) as p then
create f.make_with_path (p)
if f.exists and then f.is_access_readable then
create Result.make_empty
f.open_read
f.read_line
s := f.last_string
if s.is_integer_64 then
Result.set_user (cms_api.user_api.user_by_id (s.to_integer_64))
else
Result.set_user (cms_api.user_api.user_by_name (s))
end
f.read_line
s := f.last_string
if s.is_integer_64 then
Result.set_date ((create {HTTP_DATE}.make_from_timestamp (s.to_integer_64)).date_time)
end
f.read_line
s := f.last_string
if s.is_integer_32 then
Result.set_size (s.to_integer_32)
else
Result.set_size (-1)
end
if not f.end_of_file then
f.read_line
Result.set_file_type (f.last_string)
end
f.close
end
end
end
safe_delete (p: PATH)
-- Safe remove file at path `p'.
local
f: RAW_FILE
retried: BOOLEAN
do
if retried then
error_handler.add_custom_error (-1, "Can not delete file", {STRING_32} "Can not delete file %"" + p.name + "%"")
else
create f.make_with_path (p)
if f.exists then
f.delete
else
-- Not considered as failure.
end
end
rescue
retried := True
retry
end
end

View File

@@ -0,0 +1,504 @@
note
description: "files module."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILES_MODULE
inherit
CMS_MODULE
rename
module_api as files_api
redefine
install,
initialize,
setup_hooks,
permissions,
files_api
end
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Initialization
make
do
name := "files"
version := "1.0"
description := "Service to upload files, and manage them."
package := "file"
end
feature -- Access
name: STRING
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force (admin_files_permission)
Result.force (upload_files_permission)
Result.force (browse_files_permission)
end
admin_files_permission: STRING = "admin files"
upload_files_permission: STRING = "upload files"
browse_files_permission: STRING = "browse files"
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
if files_api = Void then
create files_api.make (api)
end
end
feature {CMS_API}-- Module management
install (api: CMS_API)
-- install the module
local
l_files_api: like files_api
d: DIRECTORY
do
create l_files_api.make (api)
create d.make_with_path (l_files_api.uploads_directory)
if not d.exists then
d.recursive_create_dir
end
create d.make_with_path (l_files_api.metadata_directory)
if not d.exists then
d.recursive_create_dir
end
files_api := l_files_api
Precursor (api)
end
feature {CMS_API} -- Access: API
files_api: detachable CMS_FILES_API
-- <Precursor>
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
map_uri_template_agent (a_router, "/" + uploads_location, agent execute_upload (?, ?, a_api), Void) -- Accepts any method GET, HEAD, POST, PUT, DELETE, ...
map_uri_template_agent (a_router, "/" + uploads_location + "{filename}", agent display_uploaded_file_info (?, ?, a_api), a_router.methods_get)
map_uri_template_agent (a_router, "/" + uploads_location + "remove/{filename}", agent remove_file (?, ?, a_api), a_router.methods_get)
end
uploads_location: STRING = "upload/"
feature -- Hooks
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_hooks.subscribe_to_menu_system_alter_hook (Current)
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
local
link: CMS_LOCAL_LINK
do
-- login in demo did somehow not work
if a_response.has_permission (upload_files_permission) then
create link.make ("Upload files", uploads_location)
a_menu_system.navigation_menu.extend (link)
end
end
feature -- Handler
execute_not_found_handler (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
-- `uri' is not found, redirect to default page
do
res.redirect_now_with_content (req.script_url ("/"), uri + ": not found. %N Redirection to" + req.script_url ("/"), "text/html")
end
display_uploaded_file_info (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
-- Display information related to a cms uploaded file.
local
body: STRING_8
r: CMS_RESPONSE
f: CMS_FILE
md: detachable CMS_FILE_METADATA
fn: READABLE_STRING_32
do
check req.is_get_request_method end
if not api.has_permission (browse_files_permission) then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("You are not allowed to browse CMS files!")
elseif attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- add style
r.add_style (r.url ("/module/" + name + "/files/css/files.css", Void), Void)
create body.make_empty
fn := p_filename.value
r.set_page_title ({STRING_32} "File %"" + fn + {STRING_32} "%"")
body.append ("<div class=%"uploaded-files%">%N") -- To ease css customization.
if attached files_api as l_files_api then
f := l_files_api.new_uploads_file (create {PATH}.make_from_string (fn))
body.append ("<div class=%"metadata%">")
md := l_files_api.metadata (f)
--| Uploader user
body.append ("<strong>User: </strong>")
if md /= Void and then attached md.user as meta_user then
body.append (api.html_encoded (meta_user.name))
else
body.append ("unknown user")
end
body.append ("<br/>%N")
--| Uploaded date
body.append ("<strong>Upload Time: </strong>")
if md /= Void and then attached md.date as meta_time then
body.append (meta_time.out)
else
body.append ("NA")
end
body.append ("<br/>%N")
--| File size
body.append ("<strong> File Size: </strong>")
if md /= Void and then md.size > 0 then
body.append (file_size_human_string (md.size))
else
body.append ("NA")
end
body.append ("<br/>%N")
--| File type
body.append ("<strong>File Type: </strong>")
if md /= Void and then attached md.file_type as meta_type then
body.append (meta_type)
else
body.append ("NA")
end
body.append ("<br/><br/>%N")
body.append ("<a class=%"button%" href=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%">Download</a>%N")
body.append ("<a class=%"button%" href=%"" + req.script_url ("/" + uploads_location + "remove/" + f.filename) + "%">Remove</a>%N")
body.append ("</div>%N") -- metadata
body.append ("<div class=%"overview%">")
if
attached f.relative_path.extension as ext and then
(
ext.is_case_insensitive_equal_general ("png")
or ext.is_case_insensitive_equal_general ("jpg")
or ext.is_case_insensitive_equal_general ("gif")
)
then
body.append ("<img src=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%" />")
else
-- add default thumbnail
body.append ("<img src=%"" + req.script_url ("/module/" + name + "/files/img/file-logo.png") + "%" />")
end
body.append ("</div>%N") -- Overview
end
body.append ("</div>%N")
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", uploads_location))
r.set_main_content (body)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Missing 'filename' field value!")
end
r.execute
end
execute_upload (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
body: STRING_8
r: CMS_RESPONSE
do
if req.is_get_head_request_method or req.is_post_request_method then
create body.make_empty
body.append ("<h1> Upload files </h1>%N")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- set style
r.add_style (r.url ("/module/" + name + "/files/css/files.css", void), void)
-- add JS for dropzone
r.add_javascript_url (r.url ("/module/" + name + "/files/js/dropzone.js", void))
r.add_style (r.url ("/module/" + name + "/files/js/dropzone.css", void), void)
if api.has_permission (upload_files_permission) then
-- create body
body.append ("<p>Please choose some file(s) to upload.</p>")
-- create form to choose files and upload them
body.append ("<div class=%"upload-files%">")
body.append ("<form action=%"" + r.url (uploads_location, Void) + "%" class=%"dropzone%">")
body.append ("</form>%N")
body.append ("<div class=%"center%"><a class=%"button%" href=%""+ r.url (uploads_location, Void) +"%">Upload Files</a></div>")
body.append ("</div>")
if req.is_post_request_method then
process_uploaded_files (req, api, body)
end
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
-- Build the response.
if r.has_permission (browse_files_permission) then
append_uploaded_file_album_to (req, api, body)
else
r.add_warning_message ("You are not allowed to browse files!")
end
r.set_main_content (body)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
r.execute
end
process_uploaded_files (req: WSF_REQUEST; api: CMS_API; a_output: STRING)
-- Process http request uploaded files.
require
has_permission: api.has_permission (upload_files_permission)
local
l_uploaded_file: CMS_UPLOADED_FILE
uf: WSF_UPLOADED_FILE
do
if attached files_api as l_files_api then
-- if has uploaded files, then store them
if req.has_uploaded_file then
a_output.append ("<strong>Newly uploaded file(s): </strong>%N")
a_output.append ("<ul class=%"uploaded-files%">")
across
req.uploaded_files as ic
loop
uf := ic.item
create l_uploaded_file.make_with_uploaded_file (l_files_api.uploads_directory, uf)
a_output.append ("<li>")
a_output.append (api.html_encoded (l_uploaded_file.filename))
-- create medadata file
l_uploaded_file.set_size (uf.size)
l_uploaded_file.set_type (uf.content_type)
-- store the just uploaded file
l_files_api.save_uploaded_file (l_uploaded_file)
if l_files_api.has_error then
a_output.append (" <span class=%"error%">: upload failed!</span>")
end
a_output.append ("</li>")
end
a_output.append ("</ul>%N")
end
end
end
append_uploaded_file_album_to (req: WSF_REQUEST; api: CMS_API; a_output: STRING)
local
d: DIRECTORY
f: CMS_FILE
p: PATH
rel: PATH
md: detachable CMS_FILE_METADATA
do
if attached files_api as l_files_api then
rel := l_files_api.uploads_relative_path
p := l_files_api.uploads_directory
a_output.append ("<div class=%"uploaded-files%">%N")
a_output.append ("<strong>Uploaded files:</strong>%N")
a_output.append ("<table>%N")
a_output.append ("<tr><th>Filename</th><th>Uploading Time</th><th>User</th><th>Size</th><th></th><th></th></tr>%N")
create d.make_with_path (p)
if d.exists then
across
d.entries as ic
loop
if ic.item.is_current_symbol then
-- Ignore
elseif ic.item.is_parent_symbol then
-- Ignore for now.
else
f := l_files_api.new_file (rel.extended_path (ic.item))
-- check if f is directory -> yes, then do not show
if not f.is_directory then
a_output.append ("<tr>")
-- add filename
a_output.append ("<td class=%"filename%">")
a_output.append ("<a href=%"" + api.percent_encoded (f.filename) + "%">")
a_output.append (api.html_encoded (f.filename))
a_output.append ("</a>")
a_output.append ("</td>%N")
md := l_files_api.metadata (f)
if md = Void then
a_output.append ("<td class=%"date%"></td>%N")
a_output.append ("<td class=%"user%"></td>%N")
a_output.append ("<td class=%"size%"></td>%N")
else
-- add uploading time
a_output.append ("<td class=%"date%">")
if attached md.date as meta_time then
a_output.append (meta_time.out)
end
a_output.append ("</td>%N")
-- add user
a_output.append ("<td class=%"user%">")
if attached md.user as u then
a_output.append (api.html_encoded (u.name))
end
a_output.append ("</td>%N")
-- add size
a_output.append ("<td class=%"size%">")
if md.size > 0 then
a_output.append (file_size_human_string (md.size))
else
a_output.append ("NA")
end
a_output.append ("</td>%N")
end
-- add download link
a_output.append ("<td>")
a_output.append ("<a class=%"button%" href=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%" download>Download</a>%N")
a_output.append ("</td>%N")
-- add remove button
a_output.append ("<td>")
a_output.append ("<a class=%"button%" href=%"" + req.script_url ("/" + uploads_location + "remove/" + f.filename) + "%">Remove</a>%N")
a_output.append ("</td>%N")
a_output.append ("</tr>%N")
else
if f.relative_path.is_current_symbol or f.relative_path.is_parent_symbol then
-- Ignore "." and ".."
else
-- folder support not yet supported
-- -- add directory identifier
-- a_output.append ("<td>[dir]</td>%N")
-- a_output.append ("<td>")
-- a_output.append ("<a href=%"" + api.percent_encoded (f.filename) + "%">")
-- a_output.append (api.html_encoded (f.filename))
-- a_output.append ("</a>")
-- a_output.append ("</td>%N")
-- a_output.append ("<td></td><td></td><td></td><td></td><td></td>")
end
end
end
end
end
a_output.append ("</table>%N")
a_output.append ("</div>%N")
end
end
remove_file (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
body: STRING
r: CMS_RESPONSE
err: BOOLEAN
do
if attached files_api as l_files_api then
if not api.has_permission (admin_files_permission) then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("You are not allowed to remove file!")
elseif attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_files_api.delete_file (p_filename.value)
err := l_files_api.has_error
l_files_api.reset_error
create body.make_empty
if err then
body.append ("<h3>The file has been removed successfully!</h3>")
else
body.append ("<h3>The file removal failed!</h3>")
end
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", uploads_location))
r.set_main_content (body)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("Missing 'filename' parameter!")
end
else
create {INTERNAL_SERVER_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Removal of file failed due to internal server error!")
end
r.execute
end
feature -- Helpers
file_size_human_string (a_size: INTEGER): STRING
local
size: INTEGER
do
size := a_size
if size >= 1000000 then
size := size // 1000000
Result := size.out + " MB"
else
if size >= 1000 then
size := size // 1000
Result := size.out + " kB"
else
Result := size.out + " bytes"
end
end
end
feature -- Mapping helper: uri template agent (analogue to the demo-module)
map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `h' as handler for `a_tpl', according to `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
h_attached: h /= Void
do
a_router.map (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
end
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `proc' as handler for `a_tpl', according to `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
proc_attached: proc /= Void
do
map_uri_template (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
end
end

View File

@@ -0,0 +1,105 @@
note
description: "Summary description for {CMS_UPLOADED_FILE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_UPLOADED_FILE
create
make_with_uploaded_file
feature {NONE} -- Initializaion
make_with_uploaded_file (a_uploads_directory: PATH; uf: WSF_UPLOADED_FILE)
do
uploads_directory := a_uploads_directory
uploaded_file := uf
location := a_uploads_directory.extended (uf.safe_filename)
end
feature -- Access
uploaded_file: WSF_UPLOADED_FILE
uploads_directory: PATH
filename: STRING_32
-- File name of Current file.
local
p: PATH
do
p := location
if attached p.entry as e then
Result := e.name
else
Result := p.name
end
end
location: PATH
-- Absolute path, or relative path to the `CMS_API.files_location'.
owner: detachable CMS_USER
-- Optional owner.
upload_time: detachable DATE_TIME
-- time when the file was uploaded
size: detachable INTEGER_32
-- file size
type: detachable STRING
-- file type
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
set_time (a_time: detachable DATE_TIME)
-- Set `upload_time' to `a_time'
do
upload_time := a_time
end
set_size (a_size: detachable INTEGER_32)
-- Set `size' to `a_size'
do
size := a_size
end
set_type (a_type: detachable STRING)
-- Set `type' to `a_type'
do
type := a_type
end
set_new_location_with_number (a_number: INTEGER_32)
-- sets `a_number' after the name. This is done when the file was already uploaded
local
position: INTEGER_32
new_name: STRING_8
do
position := uploaded_file.string_representation.index_of ('.', 1)
create new_name.make_empty
new_name := uploaded_file.string_representation.head (position-1)
new_name.append ("_(" + a_number.out + ")")
new_name.append (uploaded_file.string_representation.substring (position, uploaded_file.string_representation.count))
location := uploads_directory.extended (new_name)
end
feature -- Basic operation
move_to (p: PATH): BOOLEAN
do
Result := uploaded_file.move_to (p.name)
end
end

View File

@@ -0,0 +1,21 @@
<?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="files_module" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="files_module">
<target name="files_module">
<root all_classes="true" />
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard"/>
<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"/>
<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"/>
<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=".\" recursive="true"/>
</target>
</system>

22
modules/files/files.ecf Normal file
View File

@@ -0,0 +1,22 @@
<?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="files_module" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="files_module">
<target name="files_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.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=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,52 @@
.uploaded-files table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files table th {
padding: 3px 0 3px 5px;
}
.uploaded-files table td {
padding: 3px 0 3px 5px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.uploaded-files a.button:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
.upload-files .center {
text-align: center;
padding: 10px;
}
.upload-files a.button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.upload-files a.button:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
position: relative;
.dz-preview {
position: relative;
display: inline-block;
width: 120px;
margin: 0.5em;
.dz-progress {
display: block;
height: 15px;
border: 1px solid #aaa;
.dz-upload {
display: block;
height: 100%;
width: 0;
background: green;
}
}
.dz-error-message {
color: red;
display: none;
}
&.dz-error {
.dz-error-message, .dz-error-mark {
display: block;
}
}
&.dz-success {
.dz-success-mark {
display: block;
}
}
.dz-error-mark, .dz-success-mark {
position: absolute;
display: none;
left: 30px;
top: 30px;
width: 54px;
height: 58px;
left: 50%;
margin-left: -(54px/2);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
@include keyframes(passing-through) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30%, 70% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
100% {
opacity: 0;
@include prefix((transform: translateY(-40px)));
}
}
@include keyframes(slide-in) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
}
@include keyframes(pulse) {
0% { @include prefix((transform: scale(1))); }
10% { @include prefix((transform: scale(1.1))); }
20% { @include prefix((transform: scale(1))); }
}
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
$image-size: 120px;
$image-border-radius: 20px;
&.dz-clickable {
cursor: pointer;
* {
cursor: default;
}
.dz-message {
&, * {
cursor: pointer;
}
}
}
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px;
&.dz-started {
.dz-message {
display: none;
}
}
&.dz-drag-hover {
border-style: solid;
.dz-message {
opacity: 0.5;
}
}
.dz-message {
text-align: center;
margin: 2em 0;
}
.dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px;
&:hover {
// Making sure that always the hovered preview element is on top
z-index: 1000;
.dz-details {
opacity: 1;
}
}
&.dz-file-preview {
.dz-image {
border-radius: $image-border-radius;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd);
}
.dz-details {
opacity: 1;
}
}
&.dz-image-preview {
background: white;
.dz-details {
@include prefix((transition: opacity 0.2s linear));
}
}
.dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none;
&:hover {
text-decoration: underline;
}
}
&:hover .dz-details {
opacity: 1;
}
.dz-details {
$background-color: #444;
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
$width: 120px;
line-height: 150%;
.dz-size {
margin-bottom: 1em;
font-size: 16px;
}
.dz-filename {
white-space: nowrap;
&:hover {
span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8);
}
}
&:not(:hover) {
span {
border: 1px solid transparent;
}
overflow: hidden;
text-overflow: ellipsis;
}
}
.dz-filename, .dz-size {
span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px;
}
}
}
&:hover {
.dz-image {
// opacity: 0.8;
img {
@include prefix((transform: scale(1.05, 1.05))); // Getting rid of that white bleed-in
@include prefix((filter: blur(8px)), webkit); // Getting rid of that white bleed-in
}
}
}
.dz-image {
border-radius: $image-border-radius;
overflow: hidden;
width: $image-size;
height: $image-size;
position: relative;
display: block;
z-index: 10;
img {
display: block;
}
}
&.dz-success {
.dz-success-mark {
@include prefix((animation: passing-through 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
&.dz-error {
.dz-error-mark {
opacity: 1;
@include prefix((animation: slide-in 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
.dz-success-mark, .dz-error-mark {
$image-height: 54px;
$image-width: 54px;
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -($image-width/2);
margin-top: -($image-height/2);
svg {
display: block;
width: $image-width;
height: $image-height;
}
}
&.dz-processing .dz-progress {
opacity: 1;
@include prefix((transition: all 0.2s linear));
}
&.dz-complete .dz-progress {
opacity: 0;
@include prefix((transition: opacity 0.4s ease-in));
}
&:not(.dz-processing) {
.dz-progress {
@include prefix((animation: pulse 6s ease infinite));
}
}
.dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
// border: 2px solid #333;
background: rgba(255, 255, 255, 0.9);
// Fix for chrome bug: https://code.google.com/p/chromium/issues/detail?id=157218
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden;
.dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
@include prefix((transition: width 300ms ease-in-out));
}
}
&.dz-error {
.dz-error-message {
display: block;
}
&:hover .dz-error-message {
opacity: 1;
pointer-events: auto;
}
}
.dz-error-message {
$width: $image-size + 20px;
$color: rgb(190, 38, 38);
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 0;
@include prefix((transition: opacity 0.3s ease));
border-radius: 8px;
font-size: 13px;
top: $image-size + 10px;
left: -10px;
width: $width;
background: $color;
background: linear-gradient(to bottom, $color, darken($color, 5%));
padding: 0.5em 1.2em;
color: white;
// The triangle pointing up
&:after {
content: '';
position: absolute;
top: -6px;
left: $width / 2 - 6px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $color;
}
}
}
}

View File

@@ -0,0 +1,63 @@
.uploaded-files {
table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding: 3px 0 3px 5px;
}
td {
padding: 3px 0 3px 5px;
}
}
a.button{
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
}
}
.upload-files {
.center {
text-align: center;
padding: 10px;
}
a.button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
border: solid 1px #06f;
background-color: #cff;
}
}
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}

View File

@@ -114,7 +114,7 @@ feature -- Handler
attached l_search.last_result as l_result and then attached l_search.last_result as l_result and then
l_result.status = 200 l_result.status = 200
then then
if attached template_block (Current, "search", r) as l_tpl_block then if attached smarty_template_block (Current, "search", api) as l_tpl_block then
l_tpl_block.set_value (l_result, "result") l_tpl_block.set_value (l_result, "result")
r.add_block (l_tpl_block, "content") r.add_block (l_tpl_block, "content")
end end

View File

@@ -290,9 +290,7 @@ feature -- Access: Node
is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN
-- Is the user `u' owner of the node `n'. -- Is the user `u' owner of the node `n'.
do do
if attached node_storage.node_author (a_node) as l_author then Result := u.same_as (a_node.author)
Result := u.same_as (l_author)
end
end end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE] nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]

View File

@@ -127,8 +127,13 @@ feature {CMS_API} -- Module management
-- Schema -- Schema
if attached a_api.storage.as_sql_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) 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)
if l_sql_storage.has_error then
a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
else
Precursor {CMS_MODULE} (a_api)
end
end end
Precursor {CMS_MODULE}(a_api)
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API

View File

@@ -122,7 +122,7 @@ feature -- HTTP Methods
if if
l_node /= Void and then l_node /= Void and then
l_rev > 0 and then l_rev > 0 and then
node_api.has_permission_for_action_on_node ("view revisions", l_node, current_user (req)) node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user)
then then
l_node := node_api.revision_node (l_nid, l_rev) l_node := node_api.revision_node (l_nid, l_rev)
end end
@@ -137,9 +137,9 @@ feature -- HTTP Methods
view_response.set_revision (l_rev) view_response.set_revision (l_rev)
view_response.execute view_response.execute
elseif elseif
attached current_user (req) as l_user and then attached api.user as l_user and then
( node_api.is_author_of_node (l_user, l_node) ( node_api.is_author_of_node (l_user, l_node)
or else api.user_api.user_has_permission (l_user, "view unpublished " + l_node.content_type) or else api.user_has_permission (l_user, "view unpublished " + l_node.content_type)
) )
then then
create view_response.make (req, res, api, node_api) create view_response.make (req, res, api, node_api)
@@ -208,13 +208,13 @@ feature -- HTTP Methods
do_trash (req: WSF_REQUEST; res: WSF_RESPONSE) do_trash (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Trash a node, soft delete. -- Trash a node, soft delete.
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("trash", l_node, l_user) then
node_api.trash_node (l_node) node_api.trash_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
@@ -245,13 +245,13 @@ feature {NONE} -- Trash:Restore
local local
l_source: STRING l_source: STRING
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("delete", l_node, l_user) then
node_api.delete_node (l_node) node_api.delete_node (l_node)
l_source := node_api.node_path (l_node) l_source := node_api.node_path (l_node)
api.unset_path_alias (l_source, api.location_alias (l_source)) api.unset_path_alias (l_source, api.location_alias (l_source))
@@ -274,13 +274,13 @@ feature {NONE} -- Trash:Restore
do_restore (req: WSF_REQUEST; res: WSF_RESPONSE) do_restore (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published. -- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("restore", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("restore", l_node, l_user) then
node_api.restore_node (l_node) node_api.restore_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
@@ -310,7 +310,7 @@ feature {NONE} -- Trash:Restore
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("view revisions", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user) then
create r.make (req, res, api) create r.make (req, res, api)
create b.make_empty create b.make_empty
b.append ("<ul>") b.append ("<ul>")

View File

@@ -53,6 +53,7 @@ feature -- Execution
then then
l_manager.append_content_as_html_to_page (l_node, Current) l_manager.append_content_as_html_to_page (l_node, Current)
end end
set_modification_date (l_node.modification_date)
elseif revision > 0 then elseif revision > 0 then
set_main_content ("Missing revision node!") set_main_content ("Missing revision node!")
else else
@@ -64,8 +65,6 @@ feature -- Execution
if l_node /= Void and revision > 0 then if l_node /= Void and revision > 0 then
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title)) set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
end end
end end
end end

View File

@@ -120,13 +120,6 @@ feature -- Access
deferred deferred
end end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author. if any.
require
valid_node: a_node.has_id
deferred
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE] nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- List of nodes of type `a_node_type'. -- List of nodes of type `a_node_type'.
--| Redefine to optimize! --| Redefine to optimize!

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