Compare commits

..

9 Commits

Author SHA1 Message Date
a6642e9f3e Fixed location of library "http_client_extension" in recaptcha library. 2016-01-15 14:11:51 +01:00
affe3beb27 Merge branch 'roc_register' of https://github.com/jvelilla/ROC into roc_register 2016-01-15 13:30:58 +01:00
jvelilla
2f95c66295 Removed unneeded template
Updated Sql sentence.
2016-01-14 08:39:35 -03:00
de443a2163 Do not use ODBC by default.
(bad for default, since it depends on odbc drivers to be installed)
2016-01-13 17:30:38 +01:00
jvelilla
f244e86f13 Updated user.sql added tabled auth_temp_users.
Added CMS_TEMP_USER as part of the core.
Moved the code from CMS_TEMP_USER_API and CMS_TEMP_USER_STORAGE_* to
CMS_USER_API and CMS_USER_STORAGE_*.
2016-01-12 09:34:39 -03:00
jvelilla
0cf6e59a76 Updated templates and fixed typos.
Renamed classes
2016-01-08 20:26:34 -03:00
jvelilla
0ca336d467 Updated templates
Replaced hardcoded name "ROC CMS" for placeholder $sitename, and
missing href's with $host.
Renamed database script and database table for temporal users.
Renamed CMS_AUTH_API AS CMS_USER_TEMP_API.
Revert design to use CMS_TEMPORAL_USER and clean CMS_USER.
Refactor rename cms_auth_storage_* classes to CMS_TEMPORAL_USER_STORAGE_*
Added Pending Registrations to the admin menu to show the list of pending registrarions
added CSS to display temporal users
2016-01-06 21:29:21 -03:00
jvelilla
5d8ea2065e Rename script name and table name for
temporary users.
Updated message, after account reactivation.
Updated message, post account application.
Updated Form name, Registration instead of Registration Form.
Updated CMS_USER to have two optional features used for temporary users.
Updated CMS_AUTH_API to user CMS_USER instead of CMS_TEMPORAL_USER
Removed CMS_TEMPORAL_USER
Updateed CMS_AUTHENTICATION_MODULE, with new permission to enable
activate, reject or reactivate a pending user registration.
Updated User Storage and API to create a new user from a temporal user.
2016-01-04 21:14:13 -03:00
jvelilla
682193d116 Updated Register Module.
Worlflow

1- Register
1.1 Create a new temporal user
1.2 Email to the new User
1.3 Email to Web Master
2 Web Master Review th Account Application
2.1 Accept and Send an confirmation email to the user and remove the temporal user
2.2 Reject the application send a rejection email to the user and remove the temporal

Added a new table to save temporal users to review their Application to the site.
Updated Register Form with an new input Application and Recaptcha validation.
Updated Emails templates and messages. (TODO improve messages)
Updated mails templates. Simple messages (Todo improve messages).
Added a new handler to reject a user
Updated existing hanlders to handler the new workflow.
2015-12-30 12:32:00 -03:00
70 changed files with 1929 additions and 1414 deletions

1
ROC

Submodule ROC deleted from ec53a2682b

View File

@@ -1,14 +1,14 @@
<?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="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<description>Example/demo for Eiffel ROC CMS library</description>
<target name="common" abstract="true">
<root class="DEMO_CMS_SERVER" feature="make_and_launch"/>
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="concurrency" value="thread"/>
@@ -26,21 +26,23 @@
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
<library name="cms_file_uploader" location="..\..\modules\file_upload\file_uploader.ecf" readonly="false"/>
<library name="cms_google_search_module" location="..\..\modules\google_search\google_search-safe.ecf" readonly="false" use_application_options="true"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
<library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/>
<library name="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="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false">
<option>
<assertions/>
</option>
</library>
<!--
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/>
-->
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" />
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target>

View File

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

Binary file not shown.

View File

@@ -1,79 +0,0 @@
<!-- Updated: 12/04/2015 12:30:04.000 AM --><div class="feed">
<ul>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/-jtyp4iengE">Re: [eiffel-users] Precompile on Linux</a>
<div class="description">Hi, I just check if the .ecf files are in /usr/share/eiffelstudio-MM.mm/precomp/spec/unix and they are. When I look in the "Locations of configuration files" dialog in EiffelStudio, I see this: # Search paths for precompiled libraries are specified in the configuration file: # /usr/share/ei</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/-jtyp4iengE">RE: [eiffel-users] Precompile on Linux</a>
<div class="description">This is a bug in the Unix layout then. Do you mind submitting a problem report? Thanks, Manu *From:* eiffel...@googlegroups.com [mailto:eiffel...@googlegroups.com] *On Behalf Of *Louis *Sent:* Tuesday, December 08, 2015 23:02 *To:* eiffel...@googlegroups.com *Subject:* Re: [eiffel-users]</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">Re: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">Not really. What I need to know is when I run tests on the performance and suitability for use of EiffelBase2, do I have to repeat them with all of ES versions 15.01, 15.08 and 15.11? On 8 December 2015 at 13:52, Jocelyn Fiat <jf...@eiffelsolution.com> wrote: > As the author of EiffelBase2 does</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">RE: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">In this particular case, it is sufficient to take any version of EiffelStudio and the EiffelBase2 version that comes with it to evaluate EiffelBase2 as the library has not really changed much in the past few years. In the general case, you should stick to one version of EiffelStudio and</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">Re: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">But this doesn't answer my question. Which version of EiffelBase2 ships with which version of EiffelStudio? On 8 December 2015 at 13:05, Jocelyn Fiat <jf...@eiffel.com> wrote: > Hi all, > > Usually the version uploaded to iron server comes from the associated > branch > for instance > > - 15.01</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">Re: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">Hi all, Usually the version uploaded to iron server comes from the associated branch for instance - 15.01 has almost same content as https://svn.eiffel.com/eiffelstudio/branches/Eiffel_15.01/ - ... I said "almost" because, the ecf are modified to use iron references rather than $ISE_LIBRARY/...</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">Re: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">As the author of EiffelBase2 does not define any specific versions, I would say - EiffelStudio 14.05 ships EiffelBase2 version 14.05, and associated source code can be found at https://svn.eiffel.com/eiffelstudio/branches/Eiffel_14.05/Src/unstable/library/base2/ (last changed revision: 94979)</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/-jtyp4iengE">RE: [eiffel-users] Precompile on Linux</a>
<div class="description">We currently only ship precompiled libraries for EiffelBase, WEL on Windows and EiffelVision2. The configuration files for those precompiled library are in a read-only folder of the EiffelStudio installation. The first time EiffelStudio tries to access $ISE_PRECOMP (which if not set resolves to</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/ddSxnBrvhUg">Re: [eiffel-users] Re: Version of EiffelBase2 shipped with different EiffelStudio versions</a>
<div class="description">Come on - someone must know - the person who loaded it to iron, e.g. On 7 December 2015 at 12:03, Colin Adams <colinpa...@gmail.com> wrote: > I know what EiffelBase2 is. I was asking about the version in the iron > repository for each version of EiffelStudio. > > On 7 December 2015 at 11:59,</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/Vwsw67vq6ss">Re: Explaining high-level programming</a>
<div class="description">A very interesting article Ian. I learned more by reading it. However, the title leads me to ask: Who (precisely) is the target audience to whom the plea is being made? From my point of view, I cannot see this audience, so I am asking for your view to help me identify the audience. Thanks,</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/Vwsw67vq6ss">Explaining high-level programming</a>
<div class="description">On 4 Dec 2015, at 00:24, Bertrand Meyer <Bertran...@inf.ethz.ch> wrote: 3. The community should help spread Eiffel. The last point is particularly important. The user community needs to grow; if it moves up to the next level then we at Eiffel Software will be able to perform many more</div>
</li>
<li>
<div class="date"> Dec 08</div>
<a href="https://groups.google.com/d/topic/eiffel-users/-jtyp4iengE">Precompile on Linux</a>
<div class="description">Recently I installed EiffelStudio 15.08 on my Xubuntu laptop, with an assist from Louis M who showed me how to install it using apt-get. In the past, after extracting everything in the .tar.bz archive, I would run the supplied precompile script to produce the precompiled libraries I needed. The</div>
</li>
<li>
<div class="date"> Dec 07</div>
<a href="https://groups.google.com/d/topic/eiffel-users/4BS09rbY6UY">RE: [eiffel-users] Maximum capacity of a TUPLE</a>
<div class="description">If you are looking at TUPLE with more than 20 entries, I think I would consider using LIST or ARRAY for storing data. The issue is not so much on the TUPLE capacity (which has the same limit as a SPECIAL object) but on the number of class types any type declaration. This is a limit for all</div>
</li>
<li>
<div class="date"> Dec 07</div>
<a href="https://groups.google.com/d/topic/eiffel-users/YgyWglEEN3A">Re: [eiffel-users] Does {MEMORY}.collection_off reset the progress of the garbage collector?</a>
<div class="description">Thanks, that was what I was looking for.</div>
</li>
<li>
<div class="date"> Dec 07</div>
<a href="https://groups.google.com/d/topic/eiffel-users/4BS09rbY6UY">Maximum capacity of a TUPLE</a>
<div class="description">The header comment to {TUPLE}.plus says the result can be Void if the result exceeds the capacity of a TUPLE. What is this maximum capacity? (without knowing it is is impossible to prove the result of such a concatenation will be attached).</div>
</li>
<liv class="nav"><a href="/feed_aggregation/forum">See more ...</a></li>
</ul>

View File

@@ -1,29 +0,0 @@
<!-- Updated: 01/10/2016 12:50:45.000 PM --><div class="feed">
<ul>
<li>
<div class="date"> Jan 11</div>
<a href="https://groups.google.com/d/topic/eiffel-users/mCVwJQ21QBA">SCOOP &amp; Contracts</a>
<div class="description">Recently, I was reading a research paper from a Canadian university talking about applying something like SCOOP to Java. Because Java does not have Design-by-Contract, they wanted to build in a new keyword (like "require") called "await". From the writers point of view, the require contract was</div>
</li>
<li>
<div class="date"> Jan 11</div>
<a href="https://groups.google.com/d/topic/eiffel-users/7HR-z0DVklU">General Question: Object Persistence Mechanism</a>
<div class="description">NEED: An innate, compiler-known, object persistence mechanism, whereby objects are tracked for version (at design-time), version-updating (at run-time comparative between memory object and persisted object), and attribute change auto-persist. What would be nice is something akin to the Design-by</div>
</li>
<li>
<div class="date"> Jan 11</div>
<a href="https://groups.google.com/d/topic/eiffel-users/BouQ6JU-fQ4">RE: [eiffel-users] Solving OOSC 2/E E7.3</a>
<div class="description">In addition to Colin's reply, "is" is no longer used, as it is unnecessary. The functions sqrt and atan are in classes SINGLE_MATH or DOUBLE_MATH which are interfaces to the C library functions defined in math.h. Peter Horan -----Original Message----- From: eiffel...@googlegroups.com</div>
</li>
<li>
<div class="date"> Jan 11</div>
<a href="https://groups.google.com/d/topic/eiffel-users/BouQ6JU-fQ4">Re: [eiffel-users] Solving OOSC 2/E E7.3</a>
<div class="description">The indexing clause has been replaced by the note clause (just a change of keyword). You might try changing the configuration to use transitional syntax instead of standard syntax. On 11 January 2016 at 09:46, <sagyo1...@gmail.com> wrote: > Hi, everyone. > > I'm reading the japanese version of</div>
</li>
<li>
<div class="date"> Jan 11</div>
<a href="https://groups.google.com/d/topic/eiffel-users/BouQ6JU-fQ4">Solving OOSC 2/E E7.3</a>
<div class="description">Hi, everyone. I'm reading the japanese version of Object-Oriented Software Construction 2/E. I tried to build the code of "7.5.4 class" in EiffelStudio 15.11 and it raises some build errors. My question is: 1) Is the "indexing description:" description disposed? 2) Is the "function: syntax</div>
</li>
<liv class="nav"><a href="/feed_aggregation/forum">See more ...</a></li>
</ul>

View File

@@ -1,138 +0,0 @@
<!-- Updated: 01/10/2016 12:50:45.000 PM --><div class="feed">
<ul>
<li>
<div class="date">2015, Nov 17</div>
<a href="https://room.eiffel.com/blog/colinadams/some_lazy_data_structures_implemented_in_eiffel_part_i_iterating_the_calkinwilf_tree">Some lazy data structures implemented in Eiffel - Part I - Iterating the Calkin-Wilf tree</a>
<div class="description"><p>This is the first part of a series in which I intend to make some explorations of lazy, infinite data structures in Eiffel. If you want to compile the code in these articles, you will need EiffelStudio 15.11 or later.
</p><p>In this first article, I am going to iterate an infinite data structure - the strictly-positive rational numbers, represented by an infinite tree - <a href="https://en.wikipedia.org/wiki/Calkin%E2%80%93Wilf_tree" class="external text" title="https://en.wikipedia.org/wiki/Calkin%E2%80%93Wilf_tree">The Calkin-Wilf tree</a>. The easiest way to follow the code is to view it directly on <a href="https://github.com/colin-adams/lazy_eiffel" class="external text" title="https://github.com/colin-adams/lazy_eiffel">GitHub</a>. An alternative is to checkout the repository and compile it in EiffelStudio. To do the latter (instructions are for Linux from a bash terminal, but should be similar for other O/S I think):
</p>
<ol><li> git clone git@github.com:colin-adams/lazy_eiffel.git
</li><li> git checkout V1
</li><li> cd lazy_eiffel/examples/calkin_wilf/src
</li><li> estudio calkin_wilf.ecf &amp;
</li><li> Press OK
</li></ol>
<p>The first class worth looking at briefly is <span class="geshifilter"><code class="eiffel geshifilter-eiffel">LAZY_BINARY_TREE</code></span>. This represents a single node in an infinite binary tree, together with a link to it's parent, and two <span class="geshifilter"><code class="eiffel geshifilter-eiffel"><a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+function&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">FUNCTION</span></a></code></span>s to find the left and right children. Incidentally, you may be surprised at the syntax used for declaring these <span class="geshifilter"><code class="eiffel geshifilter-eiffel"><a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+function&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">FUNCTION</span></a></code></span>s unless you have already read <a href="https://groups.google.com/forum/#!topic/eiffel-users/poTM7aUIa4I" class="external text" title="https://groups.google.com/forum/#!topic/eiffel-users/poTM7aUIa4I">this thread</a>. This is why 15.11 or later is needed to compile the code. I think it's worth showing one of those agents here:
</p><p><div class="geshifilter"><div class="eiffel geshifilter-eiffel" style="font-family:monospace;">&nbsp; &nbsp; &nbsp; &nbsp; left_child_function<span style="color: #600000;">:</span> <a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+function&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">FUNCTION</span></a> <span style="color: #FF0000;">&#91;</span>LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>G<span style="color: #FF0000;">&#93;</span>, LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>G<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#93;</span></div></div>
</p><p>This syntax is starting to look lightweight. Looking quite comparable to Haskell, for example (<span class="geshifilter"><code class="text geshifilter-text">leftChildFunction :: LazyBinaryTree a -&gt; LazyBinaryTree a</code></span>), and none of that horrible camelCase.
</p><p>Then let's look at the <span class="geshifilter"><code class="eiffel geshifilter-eiffel">CALKIN_WILF</code></span> tree itself. The core of the class is a root node, two functions to navigate from any node in the tree to the left and right children (or to lazily build the tree structure, depending on how you want to look at it), and a creation procedure to initialize root to 1/1.
</p><p><div class="geshifilter"><div class="eiffel geshifilter-eiffel" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">feature</span> <span style="color: #FF0000;">&#123;</span><a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+none&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">NONE</span></a><span style="color: #FF0000;">&#125;</span> <span style="color: #008000; font-style: italic;">-- Initialization</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; make<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- Create `root'.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; left_child_agent<6E><span style="color: #600000;">:=</span> <span style="color: #0600FF; font-weight: bold;">agent</span> left_child<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; right_child_agent<6E><span style="color: #600000;">:=</span> <span style="color: #0600FF; font-weight: bold;">agent</span> right_child<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #603000;">root</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><span style="color: #FF0000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #FF0000;">&#123;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#125;</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><span style="color: #FF0000;">1</span>, <span style="color: #FF0000;">1</span><span style="color: #FF0000;">&#41;</span><span style="color: #FF0000;">&#41;</span>, <span style="color: #800080;">Void</span>, left_child_agent, right_child_agent<span style="color: #FF0000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; start<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">end</span><br />
<br />
<span style="color: #0600FF; font-weight: bold;">feature</span> <span style="color: #008000; font-style: italic;">-- Access</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #603000;">root</span><span style="color: #600000;">:</span> LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- 1/1</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; left_child_agent<span style="color: #600000;">:</span> <a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+function&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">FUNCTION</span></a> <span style="color: #FF0000;">&#91;</span>LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span>, LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- Function from a node to its left child</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; right_child_agent<span style="color: #600000;">:</span> <a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+function&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">FUNCTION</span></a> <span style="color: #FF0000;">&#91;</span>LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span>, LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- Function from a node to its left child</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; left_child <span style="color: #FF0000;">&#40;</span>a_node<span style="color: #600000;">:</span> LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#41;</span><span style="color: #600000;">:</span> LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- Left child of `a_node'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #800080;">Result</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #FF0000;">&#123;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#125;</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">numerator</span>, a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">numerator</span> <span style="color: #600000;">+</span> a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">denominator</span><span style="color: #FF0000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a_node, left_child_agent, right_child_agent<span style="color: #FF0000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">end</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; right_child <span style="color: #FF0000;">&#40;</span>a_node<span style="color: #600000;">:</span> LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#41;</span><span style="color: #600000;">:</span> LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000; font-style: italic;">-- Right child of `a_node'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #800080;">Result</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><span style="color: #0600FF; font-weight: bold;">create</span> <span style="color: #FF0000;">&#123;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#125;</span>.<span style="color: #000060;">make</span> <span style="color: #FF0000;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">numerator</span> <span style="color: #600000;">+</span> a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">denominator</span>, a_node.<span style="color: #000060;">item</span>.<span style="color: #000060;">denominator</span><span style="color: #FF0000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a_node, left_child_agent, right_child_agent<span style="color: #FF0000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF; font-weight: bold;">end</span></div></div>
</p><p>However, I muddled this nice little picture by inheriting from <span class="geshifilter"><code class="eiffel geshifilter-eiffel"><a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+linear&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">LINEAR</span></a> <span style="color: #FF0000;">&#91;</span>LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#93;</span></code></span>. So the class <span class="geshifilter"><code class="eiffel geshifilter-eiffel">CALKIN_WILF</code></span> <em>has</em> a lazy tree of rationals, and <em>is</em> a linear iteration of them. In the root class <span class="geshifilter"><code class="eiffel geshifilter-eiffel">CALKIN_WILF_DEMO_ROOT</code></span> we simply print the first 100 rational numbers (I could have made the program take an argument) using a <span class="geshifilter"><code class="eiffel geshifilter-eiffel"><a href="http://www.google.com/search?q=site%3Ahttp%3A%2F%2Fdocs.eiffel.com%2Feiffelstudio%2Flibraries+linear_iterator&amp;btnI=I%27m+Feeling+Lucky"><span style="color: #800000">LINEAR_ITERATOR</span></a> <span style="color: #FF0000;">&#91;</span>LAZY_BINARY_TREE <span style="color: #FF0000;">&#91;</span>RATIONAL_NUMBER<span style="color: #FF0000;">&#93;</span><span style="color: #FF0000;">&#93;</span></code></span>. However, the iteration is <em>not</em> in numerical order. In a future post we'll see other ways of iterating the rationals.
</p><p>The really interesting thing (to me) about the Calkin-Wilf tree is the way I did a breadth-first traversal of this infinite tree. It turns out that the index in the linear structure, when translated into binary, can be considered as a set of instructions to move through the tree. You ignore all leading zeros. At the first one, move to the root. Then every time you see a zero, you take the left child, and every time you see a one, you take the right child. Lovely!
</p></div>
</li>
<li>
<div class="date">2015, Sep 15</div>
<a href="http://feedproxy.google.com/~r/BertrandMeyer/~3/vAyEwWESHTY/">Design by Contract: ACM Webinar this Thursday</a>
<div class="description"><p>A third ACM webinar this year (after two on agile methods): I will be providing a general introduction to Design by Contract. The date is this coming Thursday, September 17, and the time is noon New York (18 Paris/Zurich, 17 London, 9 Los Angeles, see here for hours elsewhere). Please tune in! The event is [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://bertrandmeyer.com/2015/09/15/design-by-contract-acm-webinar-this-thursday/">Design by Contract: ACM Webinar this Thursday</a> appeared first on <a rel="nofollow" href="https://bertrandmeyer.com">Bertrand Meyer&#039;s technology+ blog</a>.</p></div>
</li>
<li>
<div class="date">2015, Jan 21</div>
<a href="http://feedproxy.google.com/~r/BertrandMeyer/~3/zNoU82qSoBU/">Framing the frame problem (new paper)</a>
<div class="description"><p>Among the open problems of verification, particularly the verification of object-oriented programs, one of the most vexing is framing: how to specify and verify what programs element do not change. Continuing previous work, this article presents a &#8220;double frame inference&#8221; method, automatic on both sides the specification and verification sides. There is no need to [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://bertrandmeyer.com/2015/01/21/framing-the-frame-problem-new-paper/">Framing the frame problem (new paper)</a> appeared first on <a rel="nofollow" href="https://bertrandmeyer.com">Bertrand Meyer&#039;s technology+ blog</a>.</p></div>
</li>
<li>
<div class="date">2015, Jan 21</div>
<a href="http://feedproxy.google.com/~r/BertrandMeyer/~3/gYfn3TjKVzA/">Detecting deadlock automatically? (New paper)</a>
</li>
<li>
<div class="date">2015, Jan 11</div>
<a href="https://room.eiffel.com/blog/conaclos/a_colored_year_on_the_web_for_eiffel">A colored year on the web for Eiffel</a>
<div class="description"><p>Happy new year!
</p><p>ACE, Prism, and Rouge now support the syntax highlighting of the Eiffel language.
</p>
<table id="toc" class="toc" summary="Contents"><tr><td><div id="toctitle"><strong>Contents</strong></div>
<div id="tocbody">
<ul>
<li class="toclevel-1"><a href="#Prism"><span class="tocnumber">1</span> <span class="toctext">Prism</span></a></li>
<li class="toclevel-1"><a href="#ACE_editor"><span class="tocnumber">2</span> <span class="toctext">ACE editor</span></a></li>
<li class="toclevel-1"><a href="#Rouge"><span class="tocnumber">3</span> <span class="toctext">Rouge</span></a></li>
<li class="toclevel-1"><a href="#Other_syntax_highlighters"><span class="tocnumber">4</span> <span class="toctext">Other syntax highlighters</span></a></li>
</ul></div>
</td></tr></table><a name="Prism"></a><h2> <span class="mw-headline"> Prism </span></h2>
<p><a href="http://prismjs.com/" class="external text" title="http://prismjs.com/">Prism</a> is by-design a lightweight syntax highlighter for the web.
It is very simple to use, and the <a href="http://prismjs.com/download.html" class="external text" title="http://prismjs.com/download.html">download page</a> enables to get only what you need. It proposes interesting add-ons (Line numbers, File Highlight, ...).
</p><p>The library is usable with a bench of tools, including <a href="https://wordpress.org/" class="external text" title="https://wordpress.org/">Wordpress</a> and <a href="http://jekyllrb.com/" class="external text" title="http://jekyllrb.com/">Jekyll</a>.
</p><p>You can test the highlighting <a href="http://prismjs.com/test.html" class="external text" title="http://prismjs.com/test.html">here</a>. The Eiffel support should be full, including the verbatim options.
</p><p>If you note any issues, you can report it on my <a href="https://github.com/Conaclos/prism" class="external text" title="https://github.com/Conaclos/prism">github fork</a>.
</p><p>I have commited a <a href="https://github.com/PrismJS/prism/pull/471" class="external text" title="https://github.com/PrismJS/prism/pull/471">Pull Request</a> to enable class name highlighting.
Please feel free to express your support in the discussion thread of the <a href="https://github.com/PrismJS/prism/pull/471" class="external text" title="https://github.com/PrismJS/prism/pull/471">Pull Request</a>.
The PR is waiitng for author agreement since January...
</p>
<a name="ACE_editor"></a><h2> <span class="mw-headline"> ACE editor </span></h2>
<p><a href="http://ace.c9.io/" class="external text" title="http://ace.c9.io/">ACE Editor</a> is certainly the most use web-based code editor.
A bench of web applications use ACE including:
</p>
<ul><li> the <a href="https://help.github.com/articles/editing-files-in-your-repository/" class="external text" title="https://help.github.com/articles/editing-files-in-your-repository/">Github web editor</a>
</li><li> the <a href="https://c9.io/" class="external text" title="https://c9.io/">Cloud9 web IDE</a>
</li><li> <a href="http://codecombat.com/" class="external text" title="http://codecombat.com/">Code Combat</a>, a game to learn programming basis
</li><li> and <a href="https://ace.c9.io/#nav=production" class="external text" title="https://ace.c9.io/#nav=production">more</a>
</li></ul>
<p>The support of Eiffel is not full. In particular, the verbatim options and multiple-line strings are not supported.
</p><p>If you note any issues, you can report it on my <a href="https://github.com/Conaclos/ace" class="external text" title="https://github.com/Conaclos/ace">github fork</a>.
</p>
<a name="Rouge"></a><h2> <span class="mw-headline"> Rouge </span></h2>
<p>Rouge is a recent syntax highlighter increasing in popularity.
It is compatible with the stylesheets of Pygments.
</p><p>It is used by:
</p>
<ul><li> <a href="https://about.gitlab.com/" class="external text" title="https://about.gitlab.com/">Gitlab</a>
</li><li> <a href="http://kramdown.gettalong.org/" class="external text" title="http://kramdown.gettalong.org/">krandown</a>, a markdown parser (anoption must be enabled)
</li><li> <a href="https://github.com/vmg/redcarpet" class="external text" title="https://github.com/vmg/redcarpet">RedCarpet</a>, another markdown parser
</li><li> the static site builder <a href="https://middlemanapp.com/" class="external text" title="https://middlemanapp.com/">Middleman</a>
</li></ul>
<p>The support of Eiffel is not full. In particular, the verbatim options are not supported.
</p><p>If you note any issues, you can report it on my <a href="https://github.com/Conaclos/rouge" class="external text" title="https://github.com/Conaclos/rouge">github fork</a>.
</p>
<a name="Other_syntax_highlighters"></a><h2> <span class="mw-headline"> Other syntax highlighters </span></h2>
<p>Some tools need updates in order to fully support the highlighting of Eiffel.
For instance:
</p>
<ul><li> <a href="https://codemirror.net/" class="external text" title="https://codemirror.net/">Code Mirror</a>, a web-based code editor
</li><li> <a href="http://pygments.org/" class="external text" title="http://pygments.org/">Pygments</a>
</li><li> <a href="https://github.com/textmate/eiffel.tmbundle" class="external text" title="https://github.com/textmate/eiffel.tmbundle">TexMate</a>, (used also by <a href="https://github.com/github/linguist" class="external text" title="https://github.com/github/linguist">Github Linguist</a>
</li><li> <a href="http://qbnz.com/highlighter/" class="external text" title="http://qbnz.com/highlighter/">GeSHi</a>
</li></ul>
<p>Enjoy&nbsp;;)
</p></div>
</li>
<liv class="nav"><a href="/feed_aggregation/news">See more ...</a></li>
</ul>

View File

@@ -0,0 +1,8 @@
{
"email": "webmaster@eiffel.org",
"subject": "Thank you for contacting us",
"recaptcha": {
"site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD",
"secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx"
}
}

View File

@@ -0,0 +1,22 @@
ul.cms-temp-users {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_information::before {
content: "[personal information] "; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_email::before {
content: "[email] "; }
/*# sourceMappingURL=auth.css.map */

View File

@@ -1,18 +1,14 @@
<!doctype html>
<html lang="en">
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Thank you for registering at <a href="$host">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
<p>Thank you for applying to <a href="$host">$sitename</a> $user</p>
<p>We will review your application and send you a resolution<p>
</body>
</html>
</html>

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation Confirmation</title>
<meta name="description" content="Activation Confirmation">
<meta name="author" content="$sitename">
</head>
<body>
<p>Your account has been confirmed <a href="$host">$sitename</a> $email</p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -4,13 +4,13 @@
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have required a new password at <a href="$host">ROC CMS</a></p>
<p>You have required a new password at <a href="$host">$sitename</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p>To complete your request, please click on this link to generate a new password:<p>
<p><a href="$link">$link</a></p>
</body>

View File

@@ -4,11 +4,11 @@
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have request a new activation token at <a href="$host">ROC CMS</a></p>
<p>You have request a new activation token at <a href="$host">$sitename</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Application Rejected</title>
<meta name="description" content="Application Rejected">
<meta name="author" content="$sitename">
</head>
<body>
<p>You requested has been rejected, your application does not conform our rules <a href="$host">$sitename</a></p>
</body>
</html>

View File

@@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Welcome to<a href="$host">ROC CMS</a></p>
<p>Welcome to <a href="$host">$sitename</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Account Evaluation</title>
<meta name="description" content="Account Evaluation">
<meta name="author" content="$sitename">
</head>
<body>
<h2> Account Evaluation </h2>
<p>The user $user ($email) wants to register to the site <a href="$host">$sitename</a></p>
<blockquote><p>User application:</p>
<p>$application</p>
</blockquote>
<p>To complete the registration, please click on the following link to activate the user account:<p>
<p><a href="$activation_url">$activation_url</a></p>
<p>To reject the registration, please click on the following link <p>
<p><a href="$rejection_url<">$rejection_url</a></p>
</body>
</html>

View File

@@ -0,0 +1,13 @@
CREATE TABLE `auth_temp_users` (
`uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`application` TEXT NOT NULL,
CONSTRAINT `name`
UNIQUE(`name`)
);

View File

@@ -1,3 +1,3 @@
<div>
<p>We have send you a new activation code, check your email to activate your account.</p>
<p>Thanks for your application, we will review it to activate your account.</p>
</div>

View File

@@ -1,3 +1,3 @@
<div>
<p>Thanks for register, check your email to activate your account.</p>
<p>Thanks for your application, we will review it to activate your account.</p>
</div>

View File

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

View File

@@ -46,3 +46,18 @@ CREATE TABLE `users_password_recovery` (
CONSTRAINT `token` UNIQUE (`token`)
);
CREATE TABLE `auth_temp_users` (
`uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`application` TEXT NOT NULL,
CONSTRAINT `name`
UNIQUE(`name`)
);

View File

@@ -34,8 +34,8 @@ feature -- CMS storage
setup_storage (a_setup: CMS_SETUP)
do
a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE3_BUILDER}.make, "sqlite3")
-- a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql")
--a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc")
end
feature -- CMS modules
@@ -91,10 +91,6 @@ feature -- CMS modules
a_setup.register_module (m)
create {CMS_SESSION_AUTH_MODULE} m.make
a_setup.register_module (m)
-- uploader
create {CMS_FILE_UPLOADER_MODULE} m.make
a_setup.register_module (m)
end

View File

@@ -35,12 +35,14 @@ feature {NONE} -- Initialization
parameters: EMAIL_SERVICE_PARAMETERS
-- Associated parameters.
admin_email: IMMUTABLE_STRING_8
-- Site admin's email.
mailer: NOTIFICATION_MAILER
-- SMTP protocol.
feature -- Access
admin_email: IMMUTABLE_STRING_8
-- Site admin's email.
feature -- Basic Operations
send_internal_email (a_content: READABLE_STRING_GENERAL)

View File

@@ -0,0 +1,44 @@
note
description: "Summary description for {CMS_TEMP_USER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_TEMP_USER
inherit
CMS_USER
create
make,
make_with_id
feature -- Access
personal_information: detachable STRING_32
-- User personal information.
salt: detachable STRING_32
-- User's password salt.
feature -- Element change
set_personal_information (an_personal_information: like personal_information)
-- Assign `personal_information' with `an_personal_information'.
do
personal_information := an_personal_information
ensure
personal_information_assigned: personal_information = an_personal_information
end
set_salt (a_salt: like salt)
-- Assign `salt' with `a_salt'.
do
salt := a_salt
ensure
salt_assigned: salt = a_salt
end
end

View File

@@ -80,7 +80,6 @@ feature -- Access
-- active
-- trashed
feature -- Access: helper
utf_8_name: STRING_8
@@ -302,6 +301,6 @@ invariant
id_or_name_set: id > 0 or else not name.is_whitespace
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)"
end

View File

@@ -61,6 +61,16 @@ CREATE TABLE `users_password_recovery` (
UNIQUE KEY `token` (`token`)
);
CREATE TABLE `auth_temp_users` (
`uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`application` TEXT NOT NULL,
CONSTRAINT `name`
UNIQUE(`name`)
);
COMMIT;

View File

@@ -0,0 +1,4 @@
Recaptcha Eiffel Lbrary
Based on https://developers.google.com/recaptcha/

View File

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

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="recaptcha" uuid="2A966489-284A-48A0-91BC-31E84EA9C3B1" library_target="recaptcha">
<target name="recaptcha">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" is_obsolete_routine_type="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_client_extension" location="..\http_client_extension\http_client_extension-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<cluster name="recaptcha" location=".\src\" 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-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="recaptcha" uuid="2A966489-284A-48A0-91BC-31E84EA9C3B1" library_target="recaptcha">
<target name="recaptcha">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="none">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="http_client_extension" location="..\http_client_extension\http_client_extension.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<cluster name="recaptcha" location=".\src\" recursive="true">
</cluster>
</target>
</system>

View File

@@ -0,0 +1,147 @@
note
description: "[
Simple API to call {RECAPTCHA} Google API.
Example call:
https://www.google.com/recaptcha/api/siteverify?secret=your_secret&response=response_string&remoteip=user_ip_address
]"
date: "$Date: 2015-01-28 11:44:15 -0300 (mi. 28 de ene. de 2015) $"
revision: "$Revision: 96551 $"
EIS: "name=RECAPTCHA", "src=https://developers.google.com/recaptcha/", "protocol=uri"
EIS: "name=RECAPTCHA API verify", "src=https://developers.google.com/recaptcha/docs/verify", "protocol=uri"
class
RECAPTCHA_API
create
make
feature {NONE} -- Initialization
make (a_secret_key, a_response: READABLE_STRING_8)
-- Create an object Recaptcha with secret key `a_secret_key' and response token `a_response'.
do
secret := a_secret_key
response := a_response
ensure
secret_set: secret.same_string (a_secret_key)
response_set: response.same_string (a_response)
end
feature -- Access
base_uri: STRING_8 = "https://www.google.com/recaptcha/api/siteverify"
-- Recaptcha base URI
secret: READABLE_STRING_8
-- Required. The shared key between your site and ReCAPTCHA.
response: READABLE_STRING_8
-- Required. The user response token provided by the reCAPTCHA to the user and provided to your site on.
remoteip: detachable READABLE_STRING_8
-- Optional. The user's IP address.
feature -- Status Reports
errors: detachable LIST [READABLE_STRING_8]
-- optional table of error codes
-- missing-input-secret The secret parameter is missing.
-- invalid-input-secret The secret parameter is invalid or malformed.
-- missing-input-response The response parameter is missing.
-- invalid-input-response The response parameter is invalid or malformed.
feature -- Change Element
set_remoteip (a_remoteip: READABLE_STRING_8)
-- Set `remoteip' with `a_remoteip'.
do
remoteip := a_remoteip
ensure
remoteip_set: remoteip = a_remoteip
end
feature -- API
verify: BOOLEAN
-- Verify the user's response
local
l_parser: JSON_PARSER
do
if attached get as l_response then
if attached l_response.body as l_body then
create l_parser.make_with_string (l_body)
l_parser.parse_content
if
l_parser.is_parsed and then attached {JSON_OBJECT} l_parser.parsed_json_object as jv and then
attached {JSON_BOOLEAN} jv.item ("success") as l_success
then
Result := l_success.item
if not Result and then attached {JSON_ARRAY} jv.item ("error-codes") as l_error_codes then
across
l_error_codes as c
loop
if attached {JSON_STRING} c.item as ji then
put_error (ji.unescaped_string_32)
end
end
end
end
else
put_error (l_response.status.out)
end
else
put_error ("unknown")
end
end
feature {NONE} -- REST API
get: detachable RESPONSE
-- Reading Data
local
l_request: REQUEST
do
create l_request.make ("GET", new_uri)
Result := l_request.execute
end
feature {NONE} -- Implementation
new_uri: STRING_8
-- new uri (BaseUri?secret=secret_value&response=response_value[&remoteip=remoteip_value]
do
create Result.make_from_string (base_uri)
Result.append ("?secret=")
Result.append (secret)
Result.append ("&response=")
Result.append (response)
if attached remoteip as l_remoteip then
Result.append ("&remoteip=" + l_remoteip)
end
end
put_error (a_code: READABLE_STRING_GENERAL)
local
l_errors: like errors
utf: UTF_CONVERTER
do
l_errors := errors
if l_errors = Void then
create {ARRAYED_LIST [STRING]} l_errors.make (1)
errors := l_errors
end
l_errors.force (utf.utf_32_string_to_utf_8_string_8 (a_code))
end
note
copyright: "2011-2015 Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,61 @@
note
description : "test application root class"
date : "$Date: 2015-01-14 15:37:57 -0300 (mi. 14 de ene. de 2015) $"
revision : "$Revision: 96458 $"
class
APPLICATION
inherit
ARGUMENTS
create
make
feature {NONE} -- Initialization
make
-- Run application.
do
test_invalid_input
test_missing_input
test_missing_key_input
end
test_invalid_input
-- invalid-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("","234")
check
not_true:not l_captcha.verify
end
end
test_missing_input
-- missing-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("key","")
check
not_true:not l_captcha.verify
end
end
test_missing_key_input
-- missing-input-response
-- invalid-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("","")
l_captcha.set_remoteip("localhost")
check
not_true:not l_captcha.verify
end
end
end

View File

@@ -0,0 +1,69 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date: 2015-01-14 15:37:57 -0300 (mi. 14 de ene. de 2015) $"
revision: "$Revision: 96458 $"
testing: "type/manual"
class
RECAPTCHA_API_TEST_SET
inherit
EQA_TEST_SET
feature -- Test routines
test_invalid_input
-- invalid-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("","234")
check
not_true:not l_captcha.verify
end
assert ("Not true", not l_captcha.verify)
assert ("Has error invalid-input-response",has_error (l_captcha,"invalid-input-response"))
end
test_missing_input
-- missing-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("key","")
check
not_true:not l_captcha.verify
end
assert ("Not true", not l_captcha.verify)
assert ("Has error missing-input-response",has_error (l_captcha,"missing-input-response"))
end
test_missing_key_input
-- missing-input-response
-- invalid-input-response
local
l_captcha: RECAPTCHA_API
do
create l_captcha.make ("","")
l_captcha.set_remoteip("localhost")
assert ("Not true", not l_captcha.verify)
assert ("Has error missing-input-response",has_error (l_captcha,"missing-input-response"))
assert ("Has error invalid-input-response",has_error (l_captcha,"invalid-input-response"))
end
feature {NONE} -- Implementation
has_error (l_captcha: RECAPTCHA_API; a_error: READABLE_STRING_32): BOOLEAN
do
if attached l_captcha.errors as l_errors then
l_errors.compare_objects
Result := l_errors.has (a_error)
end
end
end

View File

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

View File

@@ -1,34 +1,35 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="auth_module" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="auth_module">
<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="auth_module" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="auth_module">
<target name="auth_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
<option warning="true" full_class_checking="true" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="standard">
</option>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location="..\..\library\configuration\config-safe.ecf"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
<library name="email_service" location="..\..\library\email\email-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="recaptcha" location="..\..\library\recaptcha\recaptcha-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_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="email_service" location="..\..\library\email\email-safe.ecf"/>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
<cluster name="src" location=".\" recursive="true">
<file_rule>
<exclude>^persistence$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -17,18 +17,17 @@ feature {NONE} -- Initialization
make (a_cms_api: CMS_API)
local
utf: UTF_CONVERTER
l_site_name: READABLE_STRING_8
s: detachable READABLE_STRING_32
l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8
do
cms_api := a_cms_api
-- Use global smtp setting if any, otherwise "localhost"
smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost"))
l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
admin_email := a_cms_api.setup.site_email
if not admin_email.has ('<') then
admin_email := l_site_name + " <" + admin_email +">"
admin_email := site_name + " <" + admin_email +">"
end
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
@@ -60,7 +59,7 @@ feature {NONE} -- Initialization
end
if l_contact_email /= Void then
if not l_contact_email.has ('<') then
l_contact_email := l_site_name + " <" + l_contact_email + ">"
l_contact_email := site_name + " <" + l_contact_email + ">"
end
contact_email := l_contact_email
else
@@ -88,8 +87,15 @@ feature {NONE} -- Initialization
contact_subject_oauth := "Welcome."
end
contact_subject_account_evaluation := "New register, account evalution"
contact_subject_rejected := "Your account was rejected"
contact_subject_activated := "Your account was activated"
end
feature -- Access
cms_api: CMS_API
@@ -101,10 +107,23 @@ feature -- Access
contact_email: IMMUTABLE_STRING_8
-- Contact email.
site_name: IMMUTABLE_STRING_8
-- UTF-8 encoded Site name.
contact_subject_account_evaluation: IMMUTABLE_STRING_8
contact_subject_register: IMMUTABLE_STRING_8
contact_subject_activate: IMMUTABLE_STRING_8
contact_subject_password: IMMUTABLE_STRING_8
contact_subject_oauth: IMMUTABLE_STRING_8
contact_subject_rejected: IMMUTABLE_STRING_8
contact_subject_activated: IMMUTABLE_STRING_8
account_evaluation: STRING
-- Account evaluation template email message.
do
Result := template_string ("admin_account_evaluation.html", default_template_account_evaluation)
end
account_activation: STRING
-- Account activation template email message.
@@ -112,12 +131,24 @@ feature -- Access
Result := template_string ("account_activation.html", default_template_account_activation)
end
account_activation_confirmation: STRING
-- Account activation confirmation template email message.
do
Result := template_string ("account_activation_confirmation.html", default_template_account_activation_confirmation)
end
account_re_activation: STRING
-- Account re_activation template email message.
do
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation)
end
account_rejected: STRING
-- Account rejected template email message.
do
Result := template_string ("accunt_rejected.html", default_template_account_rejected)
end
account_password: STRING
-- Account password template email message.
do
@@ -146,7 +177,7 @@ feature {NONE} -- Implementation: Template
local
p: PATH
do
p := template_path ("account_activation.html")
p := template_path (a_name)
if attached read_template_file (p) as l_content then
Result := l_content
else
@@ -177,6 +208,36 @@ feature {NONE} -- Implementation
feature {NONE} -- Message email
default_template_account_evaluation: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Account Evaluation</title>
<meta name="description" content="Account Evaluation">
<meta name="author" content="$sitename">
</head>
<body>
<h2> Account Evaluation </h2>
<p>The user $user ($email) wants to register to the site <a href="$host">$sitename</a></p>
<blockquote><p>This is his/her application.</p>
<p>$application</p>
</blockquote>
<p>To complete the registration, please click on the following link to activate the user account:<p>
<p><a href="$activation_url">$activation_url</a></p>
<p>To reject the registration, please click on the following link <p>
<p><a href="$rejection_url">$rejection_url</a></p>
</body>
</html>
]"
default_template_account_activation: STRING = "[
<!doctype html>
<html lang="en">
@@ -184,21 +245,53 @@ feature {NONE} -- Message email
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Thank you for registering at <a href="...">ROC CMS</a></p>
<p>Thank you for applying to <a href="$host">$sitename</a> $user</p>
<p>To complete your registration, please click on the following link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>We will review your application and send you an email<p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
default_template_account_activation_confirmation: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation Confirmation">
<meta name="author" content="$sitename">
</head>
<body>
<p>Your account has been confirmed <a href="$host">$sitename</a> $email</p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
default_template_account_rejected: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Application Rejected</title>
<meta name="description" content="Application Rejected">
<meta name="author" content="$sitename">
</head>
<body>
<p>You requested has been rejected, your application does not conform our rules <a href="$host">$sitename</a></p>
</body>
</html>
]"
default_template_account_re_activation: STRING = "[
<!doctype html>
<html lang="en">
@@ -206,11 +299,11 @@ feature {NONE} -- Message email
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have requested a new activation token at <a href="...">ROC CMS</a></p>
<p>You have requested a new activation token at <a href="$host">$sitename</a></p>
<p>To complete your registration, please click on the following link to activate your account:<p>
@@ -229,11 +322,11 @@ feature {NONE} -- Message email
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>You have required a new password at <a href="$host">$sitename</a></p>
<p>To complete your request, please click on this link to generate a new password:<p>
@@ -250,11 +343,11 @@ feature {NONE} -- Message email
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Welcome to<a href="...">ROC CMS</a></p>
<p>Welcome to<a href="...">$sitename</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -7,6 +7,7 @@ class
CMS_AUTHENTICATION_MODULE
inherit
CMS_MODULE
redefine
setup_hooks,
@@ -15,6 +16,8 @@ inherit
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_RESPONSE_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
CMS_HOOK_BLOCK
@@ -43,7 +46,6 @@ feature {NONE} -- Initialization
version := "1.0"
description := "Authentication module"
package := "authentication"
create root_dir.make_current
cache_duration := 0
end
@@ -57,6 +59,10 @@ feature -- Access
do
Result := Precursor
Result.force ("account register")
Result.force ("account activate")
Result.force ("account reject")
Result.force ("account reactivate")
Result.force ("admin registration")
end
feature -- Access: docs
@@ -80,20 +86,29 @@ feature -- Router
-- <Precursor>
do
configure_web (a_api, a_router)
configure_web_admin (a_api, a_router)
end
configure_web (a_api: CMS_API; a_router: WSF_ROUTER)
do
a_router.handle ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account (a_api, ?, ?)), 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)
a_router.handle ("/account/roc-logout", 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/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (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/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/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password (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)
a_router.handle ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account(a_api, ?, ?)), 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)
a_router.handle ("/account/roc-logout", 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/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/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/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/post-change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_change_password(a_api, ?, ?)), a_router.methods_get)
end
configure_web_admin (a_api: CMS_API; a_router: WSF_ROUTER)
-- Configure router mapping for admin web interface.
do
a_router.handle ("/admin/pending-registrations/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_admin_pending_registrations (?, ?, a_api)), a_router.methods_get)
end
feature -- Hooks configuration
@@ -104,6 +119,7 @@ feature -- Hooks configuration
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
end
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
@@ -119,7 +135,7 @@ feature -- Hooks configuration
lnk: CMS_LOCAL_LINK
do
if attached a_response.user as u then
create lnk.make (u.name, "account" )
create lnk.make (u.name, "account")
lnk.set_weight (97)
a_menu_system.primary_menu.extend (lnk)
create lnk.make ("Logout", "account/roc-logout")
@@ -130,7 +146,11 @@ feature -- Hooks configuration
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
end
-- Add the link to the taxonomy to the main menu
if a_response.has_permission ("admin registration") then
create lnk.make ("Registration", "admin/pending-registrations/")
a_menu_system.management_menu.extend (lnk)
end
end
feature -- Handler
@@ -140,7 +160,6 @@ feature -- Handler
r: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached template_block ("account_info", r) as l_tpl_block then
if attached r.user as l_user then
r.set_value (api.user_api.user_roles (l_user), "roles")
@@ -182,53 +201,68 @@ feature -- Handler
local
r: CMS_RESPONSE
l_user_api: CMS_USER_API
u: CMS_USER
u: CMS_TEMP_USER
l_exist: BOOLEAN
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_url: STRING
l_url_activate: STRING
l_url_reject: STRING
l_token: STRING
l_captcha_passed: BOOLEAN
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if r.has_permission ("account register") then
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("name") as l_name and then
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("email") as l_email
then
if attached {WSF_STRING} req.form_parameter ("name") as l_name and then attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("email") as l_email and then attached {WSF_STRING} req.form_parameter ("personal_information") as l_personal_information then
l_user_api := api.user_api
if attached l_user_api.user_by_name (l_name.value) then
if attached l_user_api.user_by_name (l_name.value) or else attached l_user_api.temp_user_by_name (l_name.value) then
-- Username already exist.
r.set_value ("User name already exists!", "error_name")
l_exist := True
end
if attached l_user_api.user_by_email (l_email.value) then
if attached l_user_api.user_by_email (l_email.value) or else attached l_user_api.temp_user_by_email (l_email.value) then
-- Emails already exist.
r.set_value ("An account is already associated with that email address!", "error_email")
l_exist := True
end
if attached recaptcha_secret_key (api) as l_recaptcha_key then
if attached {WSF_STRING} req.form_parameter ("g-recaptcha-response") as l_recaptcha_response and then is_captcha_verified (l_recaptcha_key, l_recaptcha_response.value) then
l_captcha_passed := True
else
--| Bad or missing captcha
l_captcha_passed := False
end
else
--| reCaptcha is not setup, so no verification
l_captcha_passed := True
end
if not l_exist then
-- New user
-- New temp user
create u.make (l_name.value)
u.set_email (l_email.value)
u.set_password (l_password.value)
l_user_api.new_user (u)
u.set_personal_information (l_personal_information.value)
l_user_api.new_temp_user (u)
-- Create activation token
l_token := new_token
l_user_api.new_activation (l_token, u.id)
l_url := req.absolute_script_url ("/account/activate/" + l_token)
l_url_activate := req.absolute_script_url ("/account/activate/" + l_token)
l_url_reject := req.absolute_script_url ("/account/reject/" + l_token)
-- Send Email
-- Send Email to webmaster
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_register_email")
es.send_account_evaluation (u, l_personal_information.value, l_url_activate, l_url_reject, req.absolute_script_url (""))
-- Send Email to user
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_email")
es.send_contact_email (l_email.value, l_url)
es.send_contact_email (l_email.value, l_name.value, req.absolute_script_url (""))
else
r.set_value (l_name.value, "name")
r.set_value (l_email.value, "email")
r.set_value (l_personal_information.value, "personal_information")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
@@ -237,7 +271,6 @@ feature -- Handler
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("You can also contact the webmaster to ask for an account.")
end
r.execute
end
@@ -246,29 +279,84 @@ feature -- Handler
r: CMS_RESPONSE
l_user_api: CMS_USER_API
l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
do
l_user_api := api.user_api
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.path_parameter ("token") as l_token then
if r.has_permission ("account activate") then
if attached {WSF_STRING} req.path_parameter ("token") as l_token then
if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_user then
if attached {CMS_USER} l_user_api.user_by_activation_token (l_token.value) as l_user then
-- Valid user_id
l_user.mark_active
l_user_api.update_user (l_user)
l_user_api.remove_activation (l_token.value)
r.set_main_content ("<p> Your account <i>"+ l_user.name +"</i> has been activated</p>")
-- TODO copy the personal information
--! to CMS_USER_PROFILE and persist data
--! check also CMS_USER.data_items
-- Delete temporal User
l_user_api.delete_temp_user (l_user)
-- Valid user_id
l_user.set_id (0)
l_user.mark_active
l_user_api.new_user_from_temp_user (l_user)
l_user_api.remove_activation (l_token.value)
r.set_main_content ("<p> The account <i>" + l_user.name + "</i> has been activated</p>")
-- Send Email
if attached l_user.email as l_email then
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_activation_confirmation_email")
es.send_contact_activation_confirmation_email (l_email, "", req.absolute_script_url (""))
end
else
-- the token does not exist, or it was already used.
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.set_main_content ("<p>The token <i>" + l_token.value + "</i> is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "</p>")
end
r.execute
else
-- the token does not exist, or it was already used.
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.set_main_content ("<p>The token <i>" + l_token.value +"</i> is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "</p>")
create l_ir.make (req, res, api)
l_ir.execute
end
r.execute
else
create l_ir.make (req, res, api)
l_ir.execute
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.execute
end
end
handle_reject (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if r.has_permission ("account reject") then
if attached {WSF_STRING} req.path_parameter ("token") as l_token then
l_user_api := api.user_api
if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_user then
l_user_api.delete_temp_user (l_user)
r.set_main_content ("<p> The temporal account for <i>" + l_user.name + "</i> has been removed</p>")
-- Send Email
if attached l_user.email as l_email then
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_activation_reject_email")
es.send_contact_activation_reject_email (l_email, "", req.absolute_script_url (""))
end
else
-- the token does not exist, or it was already used.
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.set_main_content ("<p>The token <i>" + l_token.value + "</i> is not valid ")
end
r.execute
else
create l_ir.make (req, res, api)
l_ir.execute
end
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.execute
end
end
handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
@@ -276,37 +364,42 @@ feature -- Handler
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
l_token: STRING
l_url: STRING
l_url_activate: STRING
l_url_reject: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("email") as l_email
then
l_user_api := api.user_api
if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
if l_user.is_active then
r.set_value ("The asociated user to the given email " + l_email.value + " , is already active", "is_active")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
if r.has_permission ("account reactivate") then
if req.is_post_request_method then
if attached {WSF_STRING} req.form_parameter ("email") as l_email then
l_user_api := api.user_api
if attached {CMS_TEMP_USER} l_user_api.temp_user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
if l_user.is_active then
r.set_value ("The asociated user to the given email " + l_email.value + " , is already active", "is_active")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
else
l_token := new_token
l_user_api.new_activation (l_token, l_user.id)
l_url_activate := req.absolute_script_url ("/account/activate/" + l_token)
l_url_reject := req.absolute_script_url ("/account/reject/" + l_token)
-- Send Email to webmaster
if attached l_user.personal_information as l_personal_information then
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_register_email")
es.send_account_evaluation (l_user, l_personal_information, l_url_activate, l_url_reject, req.absolute_script_url (""))
end
end
else
l_token := new_token
l_user_api.new_activation (l_token, l_user.id)
l_url := req.absolute_script_url ("/account/activate/" + l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_activation_email")
es.send_contact_activation_email (l_email.value, l_url)
r.set_value ("The email does not exist or !", "error_email")
r.set_value (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
else
r.set_value ("The email does not exist or !", "error_email")
r.set_value (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.execute
end
r.execute
end
@@ -322,34 +415,32 @@ feature -- Handler
if req.is_post_request_method then
l_user_api := api.user_api
if attached {WSF_STRING} req.form_parameter ("email") as l_email then
if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
l_token := new_token
l_user_api.new_password (l_token, l_user.id)
l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token)
-- Send Email
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_password_email")
es.send_contact_password_email (l_email.value, l_url)
es.send_contact_password_email (l_email.value, l_url, req.absolute_script_url (""))
else
r.set_value ("The email does not exist !", "error_email")
r.set_value (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
elseif attached {WSF_STRING} req.form_parameter ("username") as l_username then
if attached {CMS_USER} l_user_api.user_by_name (l_username) as l_user and then
attached l_user.email as l_email
then
-- User exist create a new token and send a new email.
if attached {CMS_USER} l_user_api.user_by_name (l_username) as l_user and then attached l_user.email as l_email then
-- User exist create a new token and send a new email.
l_token := new_token
l_user_api.new_password (l_token, l_user.id)
l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token)
-- Send Email
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_password_email")
es.send_contact_password_email (l_email, l_url)
es.send_contact_password_email (l_email, l_url, req.absolute_script_url (""))
else
r.set_value ("The username does not exist !", "error_username")
r.set_value (l_username.value, "username")
@@ -360,7 +451,6 @@ feature -- Handler
r.execute
end
handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
@@ -368,24 +458,18 @@ feature -- Handler
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_user_api := api.user_api
if attached {WSF_STRING} req.query_parameter ("token") as l_token then
if attached {WSF_STRING} req.query_parameter ("token") as l_token then
r.set_value (l_token.value, "token")
if l_user_api.user_by_password_token (l_token.value) = Void then
r.set_value ("The token " + l_token.value + " is not valid, " + r.link ("click here" , "account/new-password", Void) + " to generate a new token.", "error_token")
if l_user_api.user_by_password_token (l_token.value) = Void then
r.set_value ("The token " + l_token.value + " is not valid, " + r.link ("click here", "account/new-password", Void) + " to generate a new token.", "error_token")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("token") as l_token and then
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password
then
-- Does the passwords match?
if attached {WSF_STRING} req.form_parameter ("token") as l_token and then attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password then
-- Does the passwords match?
if l_password.value.same_string (l_confirm_password.value) then
-- is the token valid?
-- is the token valid?
if attached {CMS_USER} l_user_api.user_by_password_token (l_token.value) as l_user then
l_user.set_password (l_password.value)
l_user_api.update_user (l_user)
@@ -408,22 +492,17 @@ feature -- Handler
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_user_api := api.user_api
if req.is_post_request_method then
if attached r.user as l_user then
if attached r.user as l_user then
r.set_value (api.user_api.user_roles (l_user), "roles")
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
-- Does the passwords match?
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
-- Does the passwords match?
l_user.set_password (l_password.value)
l_user_api.update_user (l_user)
r.set_redirection (req.absolute_script_url ("/account/post-change-password"))
else
if attached template_block ("account_info", r) as l_tpl_block then
-- r.set_value (l_user, "user")
-- r.set_value (l_user, "user")
r.set_value ("Passwords Don't Match", "error_password")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.add_block (l_tpl_block, "content")
@@ -445,11 +524,104 @@ feature -- Handler
r.execute
end
handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
l_response: CMS_RESPONSE
s: STRING
u: CMS_TEMP_USER
l_page_helper: CMS_PAGINATION_GENERATOR
s_pager: STRING
l_count: INTEGER
l_user_api: CMS_USER_API
do
-- At the moment the template are hardcoded, but we can
-- get them from the configuration file and load them into
-- the setup class.
create {FORBIDDEN_ERROR_CMS_RESPONSE} l_response.make (req, res, api)
if
l_response.has_permission ("admin registration")
then
l_user_api := api.user_api
l_count := l_user_api.temp_users_count
create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api)
create s.make_empty
if l_count > 1 then
l_response.set_title ("Listing " + l_count.out + " Pending Registrations")
else
l_response.set_title ("Listing " + l_count.out + " Pending Registration")
end
create s_pager.make_empty
create l_page_helper.make ("admin/pending-registrations/?page={page}&size={size}", l_user_api.temp_users_count.as_natural_64, 25) -- FIXME: Make this default page size a global CMS settings
l_page_helper.get_setting_from_request (req)
if l_page_helper.has_upper_limit and then l_page_helper.pages_count > 1 then
l_page_helper.append_to_html (l_response, s_pager)
if l_page_helper.page_size > 25 then
s.append (s_pager)
end
end
if attached l_user_api.temp_recent_users (create {CMS_DATA_QUERY_PARAMETERS}.make (l_page_helper.current_page_offset, l_page_helper.page_size)) as lst then
s.append ("<ul class=%"cms-temp-users%">%N")
across
lst as ic
loop
u := ic.item
s.append ("<li class=%"cms_temp_user%">")
s.append ("User:" + u.name)
s.append ("<ul class=%"cms_temp_user_details%">")
if attached u.personal_information as l_information then
s.append ("<li class=%"cms_temp_user_detail_information%">")
s.append (l_information)
s.append ("</li>%N")
end
if attached u.email as l_email then
s.append ("<li class=%"cms_temp_user_detail_email%">")
s.append (l_email)
s.append ("</li>%N")
end
if attached l_user_api.token_by_temp_user_id (u.id) as l_token then
s.append ("<li>")
s.append ("<a href=%"")
s.append (req.absolute_script_url ("/account/activate/" + l_token))
s.append ("%">")
s.append (html_encoded ("Activate"))
s.append ("</a>")
s.append ("</li>%N")
s.append ("<li>")
s.append ("<a href=%"")
s.append (req.absolute_script_url ("/account/reject/" + l_token))
s.append ("%">")
s.append (html_encoded ("Reject"))
s.append ("</a>")
s.append ("</li>%N")
end
s.append ("</ul>%N")
s.append ("</li>%N")
end
s.append ("</ul>%N")
end
-- Again the pager at the bottom, if needed
s.append (s_pager)
l_response.set_main_content (s)
l_response.execute
else
l_response.execute
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do
Result := <<"register", "reactivate", "new_password", "reset_password">>
Result := <<"register", "reactivate", "new_password", "reset_password", "registration">>
debug ("roc")
create l_string.make_empty
across
@@ -458,32 +630,22 @@ feature -- Handler
l_string.append (ic.item)
l_string.append_character (' ')
end
write_debug_log (generator + ".block_list:" + l_string )
write_debug_log (generator + ".block_list:" + l_string)
end
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if
a_block_id.is_case_insensitive_equal_general ("register") and then
a_response.location.starts_with ("account/roc-register")
then
if a_block_id.is_case_insensitive_equal_general ("register") and then a_response.location.starts_with ("account/roc-register") then
get_block_view_register (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reactivate") and then
a_response.location.starts_with ("account/reactivate")
then
elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then a_response.location.starts_with ("account/reactivate") then
get_block_view_reactivate (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("new_password") and then
a_response.location.starts_with ("account/new-password")
then
elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then a_response.location.starts_with ("account/new-password") then
get_block_view_new_password (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reset_password") and then
a_response.location.starts_with ("account/reset-password")
then
elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then a_response.location.starts_with ("account/reset-password") then
get_block_view_reset_password (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("registration") and then a_response.location.starts_with ("admin/pending-registrations") then
get_block_view_registration (a_block_id, a_response)
end
end
@@ -499,10 +661,13 @@ feature {NONE} -- Token Generation
create l_security
l_token := l_security.token
create l_encode
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
from
until
l_token.same_string (l_encode.encoded_string (l_token))
loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
l_token := l_security.token
end
Result := l_token
@@ -517,7 +682,6 @@ feature {NONE} -- Helpers
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
@@ -530,32 +694,14 @@ feature {NONE} -- Helpers
feature {NONE} -- Block views
-- get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
-- local
---- vals: CMS_VALUE_TABLE
-- do
-- if attached template_block (a_block_id, a_response) as l_tpl_block then
---- create vals.make (1)
---- -- add the variable to the block
---- value_table_alter (vals, a_response)
---- across
---- vals as ic
---- loop
---- l_tpl_block.set_value (ic.item, ic.key)
---- end
-- a_response.put_required_block (l_tpl_block, "content")
-- else
-- debug ("cms")
-- a_response.add_warning_message ("Error with block [" + a_block_id + "]")
-- end
-- end
-- end
get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.has_permission ("account register") 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 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")
@@ -565,10 +711,13 @@ feature {NONE} -- Block views
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 template_block (a_block_id, a_response) 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 (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")
@@ -601,9 +750,9 @@ feature {NONE} -- Block views
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 attached template_block (a_block_id, a_response) 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 ("email"), "email")
-- l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active")
-- 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 ("is_active"), "is_active")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
@@ -633,12 +782,12 @@ feature {NONE} -- Block views
end
end
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
-- 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 ("error_username"), "error_username")
-- l_tpl_block.set_value (a_response.values.item ("username"), "username")
-- 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 ("error_username"), "error_username")
-- l_tpl_block.set_value (a_response.values.item ("username"), "username")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
@@ -661,8 +810,8 @@ feature {NONE} -- Block views
do
if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) 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 ("error_token"), "error_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")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
@@ -670,11 +819,11 @@ feature {NONE} -- Block views
end
end
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
-- 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 ("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_password"), "error_password")
-- l_tpl_block.set_value (a_response.values.item ("token"), "token")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
@@ -693,14 +842,75 @@ feature {NONE} -- Block views
end
end
get_block_view_registration (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
end
feature -- Recaptcha
recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if attached cfg.text_item ("recaptcha.secret_key") as l_recaptcha_key and then not l_recaptcha_key.is_empty then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
recaptcha_site_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if attached cfg.text_item ("recaptcha.site_key") as l_recaptcha_key and then not l_recaptcha_key.is_empty then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
feature -- Response Alter
response_alter (a_response: CMS_RESPONSE)
do
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)
end
feature {NONE} -- Implementation
is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN
local
api: RECAPTCHA_API
l_errors: STRING
do
write_debug_log (generator + ".is_captcha_verified with response: [" + a_response + "]")
create api.make (a_secret, a_response)
Result := api.verify
if not Result and then attached api.errors as l_api_errors then
create l_errors.make_empty
l_errors.append_character ('%N')
across
l_api_errors as ic
loop
l_errors.append (ic.item)
l_errors.append_character ('%N')
end
write_error_log (generator + ".is_captcha_verified api_errors [" + l_errors + "]")
end
end
note
copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -34,52 +34,112 @@ feature -- Access
feature -- Basic Operations
send_contact_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
send_account_evaluation (a_user: CMS_USER; a_application, a_url_activate, a_url_reject, a_host: READABLE_STRING_8)
-- Send new user register to webmaster to confirm or reject itt.
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_evaluation)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$user", a_user.name)
if attached a_user.email as l_email then
l_message.replace_substring_all ("$email", l_email)
else
l_message.replace_substring_all ("$email", "unknown email")
end
l_message.replace_substring_all ("$application", a_application)
l_message.replace_substring_all ("$activation_url", a_url_activate)
l_message.replace_substring_all ("$rejection_url", a_url_reject)
send_message (contact_email, contact_email, parameters.contact_subject_account_evaluation, l_message)
end
send_contact_email (a_to, a_user, a_host: READABLE_STRING_8)
-- Send successful contact message to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_activation)
l_message.replace_substring_all ("$link", a_content)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$user", a_user)
send_message (contact_email, a_to, parameters.contact_subject_register, l_message)
end
send_contact_activation_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
send_contact_activation_email (a_to, a_content, a_host: READABLE_STRING_8)
-- Send successful contact activation message to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_re_activation)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_activate, l_message)
end
send_contact_password_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
send_contact_activation_confirmation_email (a_to, a_content, a_host: READABLE_STRING_8)
-- Send successful message activation to a_to.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_activation_confirmation)
l_message.replace_substring_all ("$hot", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$email", a_content)
send_message (contact_email, a_to, parameters.contact_subject_activated, l_message)
end
send_contact_activation_reject_email (a_to, a_content, a_host: READABLE_STRING_8)
-- Send successful contact activation reject message to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_rejected)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_rejected, l_message)
end
send_contact_password_email (a_to, a_content, a_host: READABLE_STRING_8)
-- Send successful new account password message to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_password)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitename", parameters.site_name)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_password, l_message)
end
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
send_contact_welcome_email (a_to, a_content, a_host: READABLE_STRING_8)
-- Send successful welcome message to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_welcome)
l_message.replace_substring_all ("$host", a_host)
l_message.replace_substring_all ("$sitenme", parameters.site_name)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message)
end

View File

@@ -0,0 +1,8 @@
{
"email": "webmaster@eiffel.org",
"subject": "Thank you for contacting us",
"recaptcha": {
"site_key":"6Lex9RMTAAAAAKleC4x6TaRlFcpLbEWgH_U7MSiD",
"secret_key":"6Lex9RMTAAAAAAkBczvX5DUiyg_xoM_EthVVgRRx"
}
}

View File

@@ -0,0 +1,22 @@
ul.cms-temp-users {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_information::before {
content: "[personal information] "; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_email::before {
content: "[email] "; }
/*# sourceMappingURL=auth.css.map */

View File

@@ -0,0 +1,22 @@
ul.cms-temp-users {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li {
border-top: dotted 1px #ccc; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li:first-child {
border-top: none; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_information::before {
content: "[personal information] "; }
ul.cms-temp-users li.cms_temp_user ul.cms_temp_user_details li.cms_temp_user_detail_email::before {
content: "[email] "; }
/*# sourceMappingURL=auth.css.map */

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,iBAAkB;EAEjB,eAAe,EAAE,IAAI;EACrB,OAAO,EAAE,eAAe;EACxB,MAAM,EAAE,cAAc;EAEtB,oBAAE;IACD,UAAU,EAAE,eAAe;IAC3B,gCAAc;MACb,UAAU,EAAE,IAAI;EAMjB,2DAAyB;IACxB,eAAe,EAAE,IAAI;IACrB,OAAO,EAAE,eAAe;IACxB,MAAM,EAAE,cAAc;IAEtB,8DAAE;MACD,UAAU,EAAE,eAAe;MAC3B,0EAAc;QACb,UAAU,EAAE,IAAI;IAGlB,uGAA2C;MAC1C,OAAO,EAAE,yBAAyB;IAEnC,iGAAqC;MACpC,OAAO,EAAE,UAAU",
"sources": ["auth.scss"],
"names": [],
"file": "auth.css"
}

View File

@@ -0,0 +1,37 @@
ul.cms-temp-users {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc;
li{
border-top: dotted 1px #ccc;
&:first-child {
border-top: none;
}
}
li.cms_temp_user {
ul.cms_temp_user_details {
list-style-type: none;
padding: 3px 3px 3px 3px;
border: solid 1px #ccc;
li{
border-top: dotted 1px #ccc;
&:first-child {
border-top: none;
}
}
li.cms_temp_user_detail_information::before{
content: "[personal information] "
}
li.cms_temp_user_detail_email::before{
content: "[email] "
}
}
}
}

View File

@@ -1,18 +1,14 @@
<!doctype html>
<html lang="en">
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Thank you for registering at <a href="$host">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
<p>Thank you for applying to <a href="$host">$sitename</a> $user</p>
<p>We will review your application and send you a resolution<p>
</body>
</html>
</html>

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation Confirmation</title>
<meta name="description" content="Activation Confirmation">
<meta name="author" content="$sitename">
</head>
<body>
<p>Your account has been confirmed <a href="$host">$sitename</a> $email</p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -4,13 +4,13 @@
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have required a new password at <a href="$host">ROC CMS</a></p>
<p>You have required a new password at <a href="$host">$sitename</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p>To complete your request, please click on this link to generate a new password:<p>
<p><a href="$link">$link</a></p>
</body>

View File

@@ -4,11 +4,11 @@
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>You have request a new activation token at <a href="$host">ROC CMS</a></p>
<p>You have request a new activation token at <a href="$host">$sitename</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Application Rejected</title>
<meta name="description" content="Application Rejected">
<meta name="author" content="$sitename">
</head>
<body>
<p>You requested has been rejected, your application does not conform our rules <a href="$host">$sitename</a></p>
</body>
</html>

View File

@@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
<meta name="author" content="$sitename">
</head>
<body>
<p>Welcome to<a href="$host">ROC CMS</a></p>
<p>Welcome to <a href="$host">$sitename</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Account Evaluation</title>
<meta name="description" content="Account Evaluation">
<meta name="author" content="$sitename">
</head>
<body>
<h2> Account Evaluation </h2>
<p>The user $user ($email) wants to register to the site <a href="$host">$sitename</a></p>
<blockquote><p>User application:</p>
<p>$application</p>
</blockquote>
<p>To complete the registration, please click on the following link to activate the user account:<p>
<p><a href="$activation_url">$activation_url</a></p>
<p>To reject the registration, please click on the following link <p>
<p><a href="$rejection_url<">$rejection_url</a></p>
</body>
</html>

View File

@@ -0,0 +1,13 @@
CREATE TABLE `auth_temp_users` (
`uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`salt` VARCHAR(100) NOT NULL,
`email` VARCHAR(250) NOT NULL,
`application` TEXT NOT NULL,
CONSTRAINT `name`
UNIQUE(`name`)
);

View File

@@ -1,3 +1,3 @@
<div>
<p>We have send you a new activation code, check your email to activate your account.</p>
<p>Thanks for your application, we will review it to activate your account.</p>
</div>

View File

@@ -1,3 +1,3 @@
<div>
<p>Thanks for register, check your email to activate your account.</p>
<p>Thanks for your application, we will review it to activate your account.</p>
</div>

View File

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

View File

@@ -1,69 +0,0 @@
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
location := a_relative_path
end
cms_api: CMS_API
feature -- Access
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
-- 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 (location))
Result := d.exists
end
is_file: BOOLEAN
local
f: RAW_FILE
do
create f.make_with_path (cms_api.files_location.extended_path (location))
Result := f.exists
end
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
end

View File

@@ -1,70 +0,0 @@
note
description: "Summary description for {CMS_FILE_UPLOAD_FILE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_UPLOAD_FILE
inherit
WSF_UPLOADED_FILE
rename
make as make_uploaded_file,
name as uploaded_file_name,
change_name as change_uploaded_file_name,
is_empty as is_empty_uploaded_file,
exists as uploaded_file_exists
end
RAW_FILE
rename
make as make_file
end
-- undefine
-- make, change_name, is_empty, exists
-- end
create
make
feature -- Initialization
make (a_name: READABLE_STRING_GENERAL; a_filename: READABLE_STRING_GENERAL; a_content_type: like content_type; a_size: like size; a_user: CMS_USER)
local
time: DATE_TIME
do
make_uploaded_file (a_name, a_filename, a_content_type, a_size)
make_with_name (a_filename)
uploaded_file_name := a_name.as_string_32
url_encoded_name := url_encoded_string (a_name)
filename := a_filename.as_string_32
content_type := a_content_type
size := a_size
create time.make_now_utc
set_uploaded_time (time)
set_uploaded_by (a_user)
end
feature -- Access
uploaded_by: CMS_USER
-- user who has uploaded the file
uploaded_time: DATE_TIME
-- time and date when file was uploaded
feature -- Setter functions
set_uploaded_by (a_user: CMS_USER)
do
uploaded_by := a_user
end
set_uploaded_time (a_time: DATE_TIME)
do
uploaded_time := a_time
end
end

View File

@@ -1,145 +0,0 @@
note
description: "Summary description for {CMS_FILE_UPLOAD_FILE_SYSTEM_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_UPLOAD_FILE_SYSTEM_HANDLER
inherit
WSF_FILE_SYSTEM_HANDLER
redefine
process_index
end
create
make_with_p
feature -- Initialization
make_with_p (d: like document_root)
do
if d.is_empty then
document_root := execution_environment.current_working_path
else
document_root := d
end
ensure
not document_root.is_empty
end
feature -- process function
process_index (a_uri: READABLE_STRING_8; dn: PATH; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
uri, s: STRING_8
d: DIRECTORY
l_files: LIST [PATH]
p: PATH
n: READABLE_STRING_32
httpdate: HTTP_DATE
pf: RAW_FILE
l_is_dir: BOOLEAN
do
create d.make_with_path (dn)
d.open_read
if attached directory_index_file (d) as f then
process_file (f, req, res)
else
uri := a_uri
if not uri.is_empty and then uri [uri.count] /= '/' then
uri.append_character ('/')
end
s := "[
<html>
<head>
<title>Index of $URI</title>
<style>
td { padding-left: 10px;}
</style>
</head>
<body>
<h1>Index of $URI</h1>
<table>
<tr><th/><th>Name</th><th>Last modified</th><th>Size</th></tr>
<tr><th colspan="4"><hr></th></tr>
]"
s.replace_substring_all ("$URI", uri)
from
l_files := d.entries
l_files.start
until
l_files.after
loop
p := l_files.item
if ignoring_index_entry (p) then
else
n := p.name
create pf.make_with_path (dn.extended_path (p))
if pf.exists and then pf.is_directory then
l_is_dir := True
else
l_is_dir := False
end
s.append ("<tr><td>")
if l_is_dir then
s.append ("[dir]")
else
s.append ("&nbsp;")
end
s.append ("</td>")
s.append ("<td><a href=%"" + uri)
url_encoder.append_percent_encoded_string_to (n, s)
s.append ("%">")
if p.is_parent_symbol then
s.append ("[Parent Directory] ..")
else
s.append (html_encoder.encoded_string (n))
end
if l_is_dir then
s.append ("/")
end
s.append ("</td>")
s.append ("<td>")
if pf.exists then
create httpdate.make_from_date_time (file_date (pf))
httpdate.append_to_rfc1123_string (s)
end
s.append ("</td>")
s.append ("<td>")
if not l_is_dir and pf.exists then
s.append_integer (file_size (pf))
end
s.append ("</td>")
s.append ("</tr>")
end
l_files.forth
end
s.append ("[
<tr><th colspan="4"><hr></th></tr>
</table>
</body>
</html>
]"
)
create h.make
h.put_content_type_text_html
res.set_status_code ({HTTP_STATUS_CODE}.ok)
h.put_content_length (s.count)
res.put_header_lines (h)
if not req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head) then
res.put_string (s)
end
res.flush
end
d.close
end
end

View File

@@ -1,79 +0,0 @@
note
description: "API to manage files."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_UPLOADER_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create
make
feature -- Access
uploads_directory_name: STRING = "uploaded_files"
uploads_location: PATH
do
Result := cms_api.files_location.extended (uploads_directory_name)
end
file_link (f: CMS_FILE): CMS_LOCAL_LINK
local
s: STRING
do
s := "files"
across
f.location.components as ic
loop
s.append_character ('/')
s.append (percent_encoded (ic.item.name))
end
create Result.make (f.filename, s)
end
feature -- Factory
new_file (p: PATH): CMS_FILE
do
create Result.make (p, cms_api)
end
new_uploads_file (p: PATH): CMS_FILE
-- New uploaded path from `p' related to `uploads_location'.
do
create Result.make ((create {PATH}.make_from_string (uploads_directory_name)).extended_path (p), cms_api)
end
feature -- Storage
save_uploaded_file (f: CMS_UPLOADED_FILE)
local
p: PATH
ut: FILE_UTILITIES
stored: BOOLEAN
do
reset_error
p := f.location
if p.is_absolute then
else
p := uploads_location.extended_path (p)
end
if ut.file_path_exists (p) then
-- FIXME: find an alternative name for it, by appending "-" + i.out , with i: INTEGER;
error_handler.add_custom_error (-1, "uploaded file storage failed", "A file with same name already exists!")
else
-- move file to path
stored := f.move_to (p)
if not stored then
error_handler.add_custom_error (-1, "uploaded file storage failed", "Issue occurred when saving uploaded file!")
end
end
end
end

View File

@@ -1,345 +0,0 @@
note
description: "file_upload application root class"
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_UPLOADER_MODULE
inherit
CMS_MODULE
rename
module_api as file_upload_api
redefine
install,
initialize,
setup_hooks,
permissions,
file_upload_api
end
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Initialization
make
do
name := "file_uploader"
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 uploaded files")
Result.force ("upload files")
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
if file_upload_api = Void then
create file_upload_api.make (api)
end
end
feature {CMS_API}-- Module management
install (api: CMS_API)
-- install the module
local
sql: STRING
l_file_upload_api: like file_upload_api
d: DIRECTORY
do
-- create a database table
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
-- FIXME: This is not used, is it planned in the future?
if not l_sql_storage.sql_table_exists ("file_upload_table") then
sql := "[
CREATE TABLE file_upload_table(
`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK("id">=0),
`name` VARCHAR(100) NOT NULL,
`uploaded_date` DATE,
`size` INTEGER
);
]"
l_sql_storage.sql_execute_script (sql, Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for file uploader module", generating_type)
end
end
end
create l_file_upload_api.make (api)
create d.make_with_path (l_file_upload_api.uploads_location)
if not d.exists then
d.recursive_create_dir
end
file_upload_api := l_file_upload_api
Precursor (api)
end
feature {CMS_API} -- Access: API
file_upload_api: detachable CMS_FILE_UPLOADER_API
-- <Precursor>
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
local
-- www: WSF_FILE_SYSTEM_HANDLER
do
map_uri_template_agent (a_router, "/upload/", agent execute_upload (?, ?, a_api), Void) -- Accepts any method GET, HEAD, POST, PUT, DELETE, ...
map_uri_template_agent (a_router, "/upload/{filename}", agent display_uploaded_file_info (?, ?, a_api), a_router.methods_get)
-- create www.make_with_path (document_root)
-- www.set_directory_index (<<"index.html">>)
-- www.set_not_found_handler (agent execute_not_found_handler)
-- a_router.handle("", www, a_router.methods_get)
end
feature -- Hooks
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_hooks.subscribe_to_menu_system_alter_hook (Current)
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") then
create link.make ("Upload", "upload/")
a_menu_system.primary_menu.extend (link)
-- end
end
--feature -- Configuration
-- document_root: PATH
-- -- Document root to look for files or directories
-- once
-- Result := execution_environment.current_working_path.extended ("site")
-- end
-- files_root: PATH
-- -- Uploaded files will be stored in `files_root' folder
-- local
-- tmp: PATH
-- once
-- tmp := document_root.extended ("files")
-- Result := tmp.extended ("uploaded_files")
-- 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 Redirectioin 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
fn: READABLE_STRING_32
do
check req.is_get_request_method end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create body.make_empty
if attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
fn := p_filename.value
body.append ("<h1>File %"" + api.html_encoded (fn) + "%"</h1>%N")
body.append ("<div class=%"uploaded-file%">%N") -- To ease css customization.
if attached file_upload_api as l_file_upload_api then
f := l_file_upload_api.new_uploads_file (create {PATH}.make_from_string (fn))
-- FIXME: get CMS information related to this file ... owner, ...
body.append ("<p>Open the media <a href=%"" + req.script_url ("/" + l_file_upload_api.file_link (f).location) + "%">")
body.append (api.html_encoded (f.filename))
body.append ("</a>.</p>%N")
if attached f.location.extension as ext then
if
ext.is_case_insensitive_equal_general ("png")
or ext.is_case_insensitive_equal_general ("jpg")
then
body.append ("<div><img src=%"" + req.script_url ("/" + l_file_upload_api.file_link (f).location) + "%" /></div>")
end
end
end
body.append ("%N</div>%N")
end
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", "upload/"))
r.set_main_content (body)
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)
if r.has_permission ("upload files") then
-- create body
body.append ("<p>Please choose some file(s) to upload.</p>")
-- create form to choose files and upload them
body.append ("<form action=%"" + req.script_url ("/upload/") + "%" enctype=%"multipart/form-data%" method=%"POST%"> %N")
body.append ("<input name=%"file-name[]%" type=%"file%" multiple> %N")
body.append ("<button type=submit>Upload</button>%N")
body.append ("</form>%N")
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.
append_uploaded_file_album_to (req, api, body)
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)
-- show all uploaded files
local
-- stored: BOOLEAN
-- file_system_handler: WSF_FILE_SYSTEM_HANDLER
-- file_system_upload_handler: CMS_FILE_UPLOAD_FILE_SYSTEM_HANDLER
l_uploaded_file: CMS_UPLOADED_FILE
uf: WSF_UPLOADED_FILE
-- ut: FILE_UTILITIES
-- files_root: PATH
do
if attached file_upload_api as l_file_upload_api then
-- if has uploaded files, then store them
if req.has_uploaded_file then
a_output.append ("<ul class=%"uploaded-files%"><strong>Uploaded file(s):</strong>%N")
across
req.uploaded_files as ic
loop
uf := ic.item
create l_uploaded_file.make_with_uploaded_file (l_file_upload_api.uploads_location, uf)
a_output.append ("<li>")
a_output.append (api.html_encoded (l_uploaded_file.filename))
-- Record current user, ..
-- for now, only user, but it should also take care of uploaded time, ...
l_uploaded_file.set_owner (api.current_user (req))
l_file_upload_api.save_uploaded_file (l_uploaded_file)
-- FIXME: display for information, about the new disk filename.
if l_file_upload_api.error_handler.has_error then
a_output.append (" <span class=%"error%">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
do
if attached file_upload_api as l_file_upload_api then
create rel.make_from_string (l_file_upload_api.uploads_directory_name)
p := api.files_location.extended_path (rel)
a_output.append ("<ul class=%"directory-index%"><strong>Index of uploads:</strong>%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_file_upload_api.new_file (rel.extended_path (ic.item))
if f.is_directory then
a_output.append ("<li class=%"directory%">")
else
a_output.append ("<li class=%"file%">")
end
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 ("( <a href=%"" + req.script_url ("/" + l_file_upload_api.file_link (f).location) + "%">")
a_output.append ("media</a>)")
a_output.append ("</li>%N")
end
end
end
a_output.append ("</ul>%N")
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

@@ -1,62 +0,0 @@
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_location: PATH; uf: WSF_UPLOADED_FILE)
do
uploads_location := a_uploads_location
uploaded_file := uf
location := a_uploads_location.extended (uf.safe_filename)
end
feature -- Access
uploaded_file: WSF_UPLOADED_FILE
uploads_location: 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.
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
feature -- Basic operation
move_to (p: PATH): BOOLEAN
do
Result := uploaded_file.move_to (p.name)
end
end

View File

@@ -1,21 +0,0 @@
<?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="file_uploader" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="file_uploader">
<target name="file_uploader">
<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="file_uploader" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -1,27 +0,0 @@
<?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="file_uploader" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="file_uploader">
<target name="file_uploader">
<!-- <root class="CMS_FILE_UPLOAD" feature="make"/> -->
<root all_classes="true" />
<option warning="true">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<!-- <precompile name="base_pre" location="$ISE_PRECOMP\base-safe.ecf"/> -->
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="\home\fmurer\Documents\EWF_ROC\ROC\cms-safe.ecf"/>
<library name="cms_model" location="\home\fmurer\Documents\EWF_ROC\ROC\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="\home\fmurer\Documents\EWF_ROC\eiffelstudio-src\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="file_uploader" location=".\" recursive="true">
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -493,7 +493,7 @@ feature -- OAuth2 Login with Provider
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, "")
es.send_contact_welcome_email (l_email, "", req.absolute_script_url (""))
end
end
r.set_redirection (r.front_page_url)

View File

@@ -443,7 +443,7 @@ feature -- Openid Login
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle_callback_openid: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, "")
es.send_contact_welcome_email (l_email, "", req.absolute_script_url (""))
end
end
r.set_redirection (r.front_page_url)

View File

@@ -1,28 +0,0 @@
note
description: "Summary description for {CMS_FILE_UPLOADER}."
author: "Fabian Murer"
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_UPLOADER
inherit
ANY
create
make
feature {NONE} -- initialize
make (a_setup: CMS_SETUP)
-- creates the CMS_FILE_UPLOADER with a setup `a_setup'
do
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -216,11 +216,6 @@ feature -- Change: User activation
deferred
end
remove_activation (a_token: READABLE_STRING_32)
-- <Precursor>.
deferred
end
feature -- Change: User password recovery
save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
@@ -233,7 +228,87 @@ feature -- Change: User password recovery
deferred
end
feature -- Access: Temp Users
temp_users_count: INTEGER
-- Number of pending users
--! to be accepted or rejected
deferred
end
temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve a temporal user by id `a_uid' for the consumer `a_consumer', if aby.
deferred
end
temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
-- User with name `a_name', if any.
require
a_name /= Void and then not a_name.is_empty
deferred
ensure
same_name: Result /= Void implies a_name ~ Result.name
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
end
temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
-- User with name `a_email', if any.
deferred
ensure
same_email: Result /= Void implies a_email ~ Result.email
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
end
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
-- User with activation token `a_token', if any.
deferred
ensure
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
end
temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER]
-- List of recent `a_count' temporal users with an offset of `lower'.
deferred
end
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
-- Retrieve activation token for user identified with id `a_id', if any.
deferred
end
feature -- New Temp User
new_user_from_temporal_user (a_user: CMS_TEMP_USER)
-- new user from temporal user `a_user'
require
no_id: not a_user.has_id
deferred
end
remove_activation (a_token: READABLE_STRING_32)
-- Remove activation by token `a_token'.
deferred
end
new_temp_user (a_user: CMS_TEMP_USER)
-- New temp user `a_user'.
require
no_id: not a_user.has_id
deferred
end
delete_temp_user (a_user: CMS_TEMP_USER)
-- Delete user `a_user'.
require
has_id: a_user.has_id
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -131,10 +131,6 @@ feature -- Change: User activation
do
end
remove_activation (a_token: READABLE_STRING_32)
-- <Precursor>.
do
end
feature -- Change: User password recovery
@@ -148,7 +144,67 @@ feature -- Change: User password recovery
do
end
feature -- Access: Users
temp_users_count: INTEGER
-- <Precursor>
do
end
temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- <Precursor>
do
end
temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
-- <Precursor>
do
end
temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
-- <Precursor>
do
end
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
-- <Precursor>
do
end
temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER]
-- List of recent `a_count' temporal users with an offset of `lower'.
do
create {ARRAYED_LIST[CMS_TEMP_USER]} Result.make (0)
end
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
-- <Precursor>
do
end
feature -- Temp Users
new_user_from_temporal_user (a_user: CMS_TEMP_USER)
-- <Precursor>
do
end
remove_activation (a_token: READABLE_STRING_32)
-- <Precursor>.
do
end
new_temp_user (a_user: CMS_TEMP_USER)
-- <Precursor>
do
end
delete_temp_user (a_user: CMS_TEMP_USER)
-- <Precursor>
do
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -196,6 +196,7 @@ feature -- Access: user
end
sql_finalize
end
feature -- Change: user
new_user (a_user: CMS_USER)
@@ -755,21 +756,6 @@ feature -- Change: User activation
sql_finalize
end
remove_activation (a_token: READABLE_STRING_32)
-- <Precursor>.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
sql_begin_transaction
write_information_log (generator + ".remove_activation")
create l_parameters.make (1)
l_parameters.put (a_token, "token")
sql_modify (sql_remove_activation, l_parameters)
sql_commit_transaction
sql_finalize
end
feature -- Change: User password recovery
save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
@@ -866,19 +852,6 @@ feature {NONE} -- Implementation: User
end
end
last_inserted_user_id: INTEGER_64
-- Last insert user id.
do
error_handler.reset
write_information_log (generator + ".last_inserted_user_id")
sql_query (Sql_last_insert_user_id, Void)
if not sql_after then
Result := sql_read_integer_64 (1)
sql_forth
check one_row: sql_after end
end
sql_finalize
end
feature {NONE} -- Implementation: User role
@@ -909,8 +882,6 @@ feature {NONE} -- Sql Queries: USER
Select_users_count: STRING = "SELECT count(*) FROM users;"
-- Number of users.
Sql_last_insert_user_id: STRING = "SELECT MAX(uid) FROM users;"
Select_users: STRING = "SELECT * FROM users;"
-- List of users.
@@ -920,7 +891,7 @@ feature {NONE} -- Sql Queries: USER
Select_user_by_name: STRING = "SELECT * FROM users WHERE name =:name;"
-- Retrieve user by name if exists.
Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;"
Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset;"
-- Retrieve recent users
Select_user_by_email: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users WHERE email =:email;"
@@ -941,6 +912,8 @@ feature {NONE} -- Sql Queries: USER ROLE
sql_last_insert_user_role_id: STRING = "SELECT MAX(rid) FROM roles;"
sql_last_insert_user_id: STRING = "SELECT MAX(uid) FROM users;"
select_user_roles: STRING = "SELECT rid, name FROM roles;"
-- List of user roles.
@@ -1009,7 +982,367 @@ feature {NONE} -- User Password Recovery
Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;"
-- Retrieve user by password token if exist.
feature -- Acess: Temp users
temp_users_count: INTEGER
-- Number of items users.
do
error_handler.reset
write_information_log (generator + ".temp_users_count")
sql_query (select_temp_users_count, Void)
if not has_error and then not sql_after then
Result := sql_read_integer_64 (1).to_integer_32
sql_forth
check one_row: sql_after end
end
sql_finalize
end
temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING
do
error_handler.reset
write_information_log (generator + ".temp_user_by_id")
create l_parameters.make (1)
l_parameters.put (a_uid, "uid")
create l_string.make_from_string (select_user_auth_temp_by_id)
sql_query (l_string, l_parameters)
if not has_error and not sql_after then
Result := fetch_temp_user
sql_forth
if not sql_after then
check no_more_than_one: False end
Result := Void
end
end
sql_finalize
end
temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
-- User for the given name `a_name', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".temp_user_by_name")
create l_parameters.make (1)
l_parameters.put (a_name, "name")
sql_query (select_temp_user_by_name, l_parameters)
if not sql_after then
Result := fetch_temp_user
sql_forth
check one_row: sql_after end
end
sql_finalize
end
temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
-- User for the given email `a_email', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".temp_user_by_name")
create l_parameters.make (1)
l_parameters.put (a_email, "email")
sql_query (select_temp_user_by_email, l_parameters)
if not sql_after then
Result := fetch_temp_user
sql_forth
check one_row: sql_after end
end
sql_finalize
end
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
-- User for the given activation token `a_token', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".temp_user_by_activation_token")
create l_parameters.make (1)
l_parameters.put (a_token, "token")
sql_query (select_temp_user_by_activation_token, l_parameters)
if not sql_after then
Result := fetch_temp_user
sql_forth
check one_row: sql_after end
end
sql_finalize
end
temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER]
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_TEMP_USER]} Result.make (0)
error_handler.reset
write_information_log (generator + ".temp_recent_users")
from
create l_parameters.make (2)
l_parameters.put (a_count, "rows")
l_parameters.put (a_lower, "offset")
sql_query (sql_select_temp_recent_users, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_temp_user as l_user then
Result.force (l_user)
end
sql_forth
end
sql_finalize
end
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
-- Number of items users.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".token_by_temp_user_id")
create l_parameters.make (1)
l_parameters.put (a_id, "uid")
sql_query (select_token_activation_by_user_id, l_parameters)
if not has_error and then not sql_after then
Result := sql_read_string (1)
sql_forth
check one_row: sql_after end
end
sql_finalize
end
feature {NONE} -- Implementation: User
fetch_temp_user: detachable CMS_TEMP_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_64 (1) as i then
l_id := i
end
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
l_name := s
end
if l_name /= Void then
create Result.make (l_name)
if l_id > 0 then
Result.set_id (l_id)
end
elseif l_id > 0 then
create Result.make_with_id (l_id)
end
if Result /= Void then
if attached sql_read_string (3) as l_password then
Result.set_hashed_password (l_password)
end
if attached sql_read_string (4) as l_salt then
Result.set_salt (l_salt)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
if attached sql_read_string (6) as l_application then
Result.set_personal_information (l_application)
end
else
check expected_valid_user: False end
end
end
feature -- New Temp User
new_user_from_temporal_user (a_user: CMS_TEMP_USER)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
if
attached a_user.hashed_password as l_password_hash and then
attached a_user.email as l_email and then
attached a_user.salt as l_password_salt
then
sql_begin_transaction
write_information_log (generator + ".new_user_from_temporal_user")
create l_parameters.make (4)
l_parameters.put (a_user.name, "name")
l_parameters.put (l_password_hash, "password")
l_parameters.put (l_password_salt, "salt")
l_parameters.put (l_email, "email")
l_parameters.put (create {DATE_TIME}.make_now_utc, "created")
l_parameters.put (a_user.status, "status")
sql_insert (sql_insert_user, l_parameters)
if not error_handler.has_error then
a_user.set_id (last_inserted_user_id)
end
if not error_handler.has_error then
sql_commit_transaction
else
sql_rollback_transaction
end
sql_finalize
else
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
new_temp_user (a_user: CMS_TEMP_USER)
-- Add a new temp_user `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
l_password_salt, l_password_hash: STRING
l_security: SECURITY_PROVIDER
do
error_handler.reset
if
attached a_user.password as l_password and then
attached a_user.email as l_email and then
attached a_user.personal_information as l_personal_information
then
create l_security
l_password_salt := l_security.salt
l_password_hash := l_security.password_hash (l_password, l_password_salt)
write_information_log (generator + ".new_temp_user")
create l_parameters.make (4)
l_parameters.put (a_user.name, "name")
l_parameters.put (l_password_hash, "password")
l_parameters.put (l_password_salt, "salt")
l_parameters.put (l_email, "email")
l_parameters.put (l_personal_information, "application")
sql_begin_transaction
sql_insert (sql_insert_temp_user, l_parameters)
if not error_handler.has_error then
a_user.set_id (last_inserted_temp_user_id)
sql_commit_transaction
else
sql_rollback_transaction
end
sql_finalize
else
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
feature -- Remove Activation
remove_activation (a_token: READABLE_STRING_32)
-- <Precursor>.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
sql_begin_transaction
write_information_log (generator + ".remove_activation")
create l_parameters.make (1)
l_parameters.put (a_token, "token")
sql_modify (sql_remove_activation, l_parameters)
sql_commit_transaction
sql_finalize
end
delete_temp_user (a_user: CMS_TEMP_USER)
-- Delete user `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
sql_begin_transaction
write_information_log (generator + ".delete_temp_user")
create l_parameters.make (1)
l_parameters.put (a_user.id, "uid")
sql_modify (sql_delete_temp_user, l_parameters)
sql_commit_transaction
sql_finalize
end
feature {NONE} -- Implementation
last_inserted_temp_user_id: INTEGER_64
-- Last insert user id.
do
error_handler.reset
write_information_log (generator + ".last_inserted_temp_user_id")
sql_query (sql_last_insert_temp_user_id, Void)
if not sql_after then
Result := sql_read_integer_64 (1)
sql_forth
check one_row: sql_after end
end
sql_finalize
end
last_inserted_user_id: INTEGER_64
-- Last insert user id.
do
error_handler.reset
write_information_log (generator + ".last_inserted_user_id")
sql_query (sql_last_insert_user_id, Void)
if not sql_after then
Result := sql_read_integer_64 (1)
sql_forth
check one_row: sql_after end
end
sql_finalize
end
feature {NONE} -- SQL select
sql_last_insert_temp_user_id: STRING = "SELECT MAX(uid) FROM auth_temp_users;"
Select_user_auth_temp_by_id: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users as u where uid=:uid;"
sql_insert_temp_user: STRING = "INSERT INTO auth_temp_users (name, password, salt, email, application) VALUES (:name, :password, :salt, :email, :application);"
-- SQL Insert to add a new user.
select_temp_user_by_name: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users WHERE name =:name;"
-- Retrieve user by name if exists.
select_temp_user_by_email: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users WHERE email =:email;"
-- Retrieve user by email if exists.
select_temp_user_by_activation_token: STRING = "SELECT u.uid, u.name, u.password, u.salt, u.email, u.application FROM auth_temp_users as u JOIN users_activations as ua ON ua.uid = u.uid and ua.token = :token;"
-- Retrieve user by activation token if exist.
sql_delete_temp_user: STRING = "DELETE FROM auth_temp_users WHERE uid=:uid;"
select_temp_users_count: STRING = "SELECT count(*) FROM auth_temp_users;"
-- Number of temporal users.
sql_select_temp_recent_users: STRING = "SELECT uid, name, password, salt, email, application FROM auth_temp_users ORDER BY uid DESC LIMIT :rows OFFSET :offset ;"
-- Retrieve recent users
select_token_activation_by_user_id: STRING = "SELECT token FROM users_activations WHERE uid = :uid;"
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -251,7 +251,6 @@ feature -- Change User
do
reset_error
if
attached a_user.password as l_password and then
attached a_user.email as l_email
then
storage.new_user (a_user)
@@ -289,12 +288,6 @@ feature -- User Activation
storage.save_activation (a_token, a_id)
end
remove_activation (a_token: READABLE_STRING_32)
-- Remove activation token `a_token', from the storage.
do
storage.remove_activation (a_token)
end
feature -- User Password Recovery
new_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
@@ -320,7 +313,101 @@ feature -- User status
Trashed: INTEGER = -1
-- The user is trashed (soft delete), ready to be deleted/destroyed from storage.
feature -- Access - Temp User
temp_users_count: INTEGER
-- Number of pending users.
--! to be accepted or rehected
do
Result := storage.temp_users_count
end
temp_user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
-- User by name `a_user_name', if any.
do
Result := storage.temp_user_by_name (a_username.as_string_32)
end
temp_user_by_email (a_email: READABLE_STRING_8): detachable CMS_USER
-- User by email `a_email', if any.
do
Result := storage.temp_user_by_email (a_email)
end
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
-- User by activation token `a_token'.
do
Result := storage.temp_user_by_activation_token (a_token)
end
temp_recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_TEMP_USER]
-- List of the `a_rows' most recent users starting from `a_offset'.
do
Result := storage.temp_recent_users (params.offset.to_integer_32, params.size.to_integer_32)
end
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
do
Result := storage.token_by_temp_user_id (a_id)
end
feature -- Change Temp User
new_user_from_temp_user (a_user: CMS_TEMP_USER)
-- Add a new user `a_user'.
require
no_id: not a_user.has_id
has_hashed_password: a_user.hashed_password /= Void
has_sal: a_user.salt /= Void
do
reset_error
if
attached a_user.hashed_password as l_password and then
attached a_user.salt as l_salt and then
attached a_user.email as l_email
then
storage.new_user_from_temporal_user (a_user)
error_handler.append (storage.error_handler)
else
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
end
end
new_temp_user (a_user: CMS_TEMP_USER)
-- Add a new user `a_user'.
require
no_id: not a_user.has_id
no_hashed_password: a_user.hashed_password = Void
do
reset_error
if
attached a_user.password as l_password and then
attached a_user.email as l_email
then
storage.new_temp_user (a_user)
error_handler.append (storage.error_handler)
else
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
end
end
remove_activation (a_token: READABLE_STRING_32)
-- Remove activation token `a_token', from the storage.
do
storage.remove_activation (a_token)
end
delete_temp_user (a_user: CMS_TEMP_USER)
-- Delete user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.delete_temp_user (a_user)
error_handler.append (storage.error_handler)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end