Compare commits
23 Commits
ewf_v1_aut
...
BEFORE_RES
| Author | SHA1 | Date | |
|---|---|---|---|
| 28ab4786a1 | |||
| 8294a47f17 | |||
| e45dac84c8 | |||
| bb3e3b992f | |||
| ebc5924c01 | |||
| 48b0ad5195 | |||
| ae9eea99dd | |||
|
|
268f53e53f | ||
|
|
e17fc570a1 | ||
| ba7ef17d34 | |||
|
|
c8bbac664b | ||
| 04e98dbb48 | |||
| 2886c90782 | |||
|
|
db6799d55b | ||
|
|
7c0032ada4 | ||
|
|
fa5efede2c | ||
| 0fca03a4d1 | |||
| 642b901856 | |||
| 4f3bcf290f | |||
| 6ca8a9ce82 | |||
| 2c72fe6738 | |||
| 149de898c0 | |||
|
|
a94a8857ae |
@@ -21,6 +21,7 @@
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n-safe.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
|
||||
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty-safe.ecf" readonly="false"/>
|
||||
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
|
||||
1
cms.ecf
1
cms.ecf
@@ -22,6 +22,7 @@
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
|
||||
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email.ecf"/>
|
||||
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty.ecf" readonly="false"/>
|
||||
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
|
||||
@@ -1,81 +1,84 @@
|
||||
<?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="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
|
||||
<description>Example/demo for Eiffel ROC CMS library</description>
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<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" void_safety="all" syntax="transitional">
|
||||
<debug name="dbglog" enabled="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<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_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_blog_module" location="modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
|
||||
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="cms_login_module" location="..\..\modules\login\login-safe.ecf" readonly="false"/>
|
||||
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
|
||||
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
|
||||
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
|
||||
<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>
|
||||
<target name="demo_any" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/>
|
||||
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/>
|
||||
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/>
|
||||
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_standalone" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<option debug="true">
|
||||
<debug name="dbglog" enabled="true"/>
|
||||
</option>
|
||||
<variable name="httpd_ssl_disabled" value="true"/><!-- for now ... due to issue with libcurl+eiffelnet ssl -->
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_standalone_none" extends="demo_standalone">
|
||||
<setting name="concurrency" value="none"/>
|
||||
</target>
|
||||
<target name="demo_standalone_mt" extends="demo_standalone">
|
||||
<setting name="concurrency" value="thread"/>
|
||||
</target>
|
||||
<target name="demo_standalone_scoop" extends="demo_standalone">
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
</target>
|
||||
<target name="demo_nino" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_cgi" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_libfcgi" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo" extends="demo_standalone">
|
||||
</target>
|
||||
<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">
|
||||
<file_rule>
|
||||
<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" void_safety="all" syntax="transitional">
|
||||
<debug name="dbglog" enabled="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<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_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
|
||||
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
|
||||
<library name="cms_blog_module" location="modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
|
||||
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
|
||||
<!--
|
||||
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
|
||||
-->
|
||||
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
|
||||
<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>
|
||||
<target name="demo_any" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/>
|
||||
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/>
|
||||
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/>
|
||||
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_standalone" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<option debug="true">
|
||||
<debug name="dbglog" enabled="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<variable name="httpd_ssl_disabled" value="true"/>
|
||||
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_standalone_none" extends="demo_standalone">
|
||||
<setting name="concurrency" value="none"/>
|
||||
</target>
|
||||
<target name="demo_standalone_mt" extends="demo_standalone">
|
||||
<setting name="concurrency" value="thread"/>
|
||||
</target>
|
||||
<target name="demo_standalone_scoop" extends="demo_standalone">
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
</target>
|
||||
<target name="demo_nino" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_cgi" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo_libfcgi" extends="common">
|
||||
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="demo" extends="demo_standalone">
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -27,12 +27,15 @@ feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
name := "Blog demo module"
|
||||
version := "1.0"
|
||||
description := "Service to demonstrate new node for blog"
|
||||
package := "demo"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "blog"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (api: CMS_API)
|
||||
@@ -74,7 +77,7 @@ CREATE TABLE blog_post_nodes(
|
||||
`tags` VARCHAR(255)
|
||||
);
|
||||
]"
|
||||
l_sql_storage.sql_execute_script (sql)
|
||||
l_sql_storage.sql_execute_script (sql, Void)
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for blog module", generating_type)
|
||||
end
|
||||
@@ -117,18 +120,18 @@ feature -- Access: router
|
||||
|
||||
-- Let the class BLOG_HANDLER handle the requests on "/blogs"
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/blogs", l_blog_handler)
|
||||
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get)
|
||||
|
||||
-- We can add a page number after /blogs/ to get older posts
|
||||
a_router.handle_with_request_methods ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
|
||||
a_router.handle ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
|
||||
|
||||
-- If a user id is given route with blog user handler
|
||||
--| FIXME: maybe /user/{user}/blogs/ would be better.
|
||||
a_router.handle_with_request_methods ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
|
||||
a_router.handle ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
|
||||
|
||||
-- If a user id is given we also want to allow different pages
|
||||
--| FIXME: what about /user/{user}/blogs/?page={page} ?
|
||||
a_router.handle_with_request_methods ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
|
||||
a_router.handle ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -86,8 +86,6 @@ feature -- Query
|
||||
-- User id from path /blogs/{user}.
|
||||
-- Unsigned integer since negative ids are not allowed.
|
||||
-- If no valid id can be read it returns -1
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
Result := -1
|
||||
if attached {WSF_STRING} req.path_parameter ("user") as l_user_id then
|
||||
|
||||
@@ -27,12 +27,15 @@ feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
name := "Demo module"
|
||||
version := "1.0"
|
||||
description := "Service to demonstrate and test cms system"
|
||||
package := "demo"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "demo"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (api: CMS_API)
|
||||
@@ -69,7 +72,7 @@ CREATE TABLE tb_demo(
|
||||
`value` TEXT
|
||||
);
|
||||
]"
|
||||
l_sql_storage.sql_execute_script (sql)
|
||||
l_sql_storage.sql_execute_script (sql, Void)
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for demo module", generating_type)
|
||||
end
|
||||
@@ -83,8 +86,8 @@ feature -- Access: router
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api))
|
||||
map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
|
||||
map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api), Void)
|
||||
map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api), Void)
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
@@ -144,42 +147,24 @@ feature -- Handler
|
||||
|
||||
feature -- Mapping helper: uri template
|
||||
|
||||
map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER)
|
||||
-- Map `h' as handler for `a_tpl'
|
||||
require
|
||||
a_tpl_attached: a_tpl /= Void
|
||||
h_attached: h /= Void
|
||||
do
|
||||
map_uri_template_with_request_methods (a_router, a_tpl, h, Void)
|
||||
end
|
||||
|
||||
map_uri_template_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
map_uri_template (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
-- Map `h' as handler for `a_tpl' for request methods `rqst_methods'.
|
||||
require
|
||||
a_tpl_attached: a_tpl /= Void
|
||||
h_attached: h /= Void
|
||||
do
|
||||
a_router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
|
||||
a_router.map (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
|
||||
end
|
||||
|
||||
feature -- Mapping helper: uri template agent
|
||||
|
||||
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]])
|
||||
-- Map `proc' as handler for `a_tpl'
|
||||
require
|
||||
a_tpl_attached: a_tpl /= Void
|
||||
proc_attached: proc /= Void
|
||||
do
|
||||
map_uri_template_agent_with_request_methods (a_router, a_tpl, proc, Void)
|
||||
end
|
||||
|
||||
map_uri_template_agent_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
-- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'.
|
||||
require
|
||||
a_tpl_attached: a_tpl /= Void
|
||||
proc_attached: proc /= Void
|
||||
do
|
||||
map_uri_template_with_request_methods (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
|
||||
map_uri_template (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
[layout]
|
||||
root-dir=site/www
|
||||
themes-dir=site/themes
|
||||
#themes-dir=site/themes
|
||||
#modules-dir=site/modules
|
||||
|
||||
[site]
|
||||
name=Eiffel CMS
|
||||
email=your@email.com
|
||||
theme=bootstrap
|
||||
|
||||
[misc]
|
||||
smtp=localhost
|
||||
[mailer]
|
||||
smtp=localhost:25
|
||||
#sendmail=/usr/bin/sendmail
|
||||
#output=@stderr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="primary-tabs">
|
||||
{unless isset="$user"}
|
||||
<h3>Login or <a href="/account/roc-register">Register</a></h3>
|
||||
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
|
||||
<div>
|
||||
<div>
|
||||
<form action method="POST">
|
||||
@@ -21,14 +21,14 @@
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<a href="/account/new-password">Forgot password?</a>
|
||||
<a href="{$site_url/}account/new-password">Forgot password?</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{foreach item="item" from="$oauth_consumers"}
|
||||
<a href="/account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
|
||||
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/unless}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
CREATE TABLE `oauth2_consumers`(
|
||||
CREATE TABLE oauth2_consumers(
|
||||
`cid` INTEGER PRIMARY KEY NOT NULL CHECK(`cid`>=0),
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`api_secret` TEXT NOT NULL,
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Change the values TO_COMPLETE based on your API.
|
||||
-- API SECTET KEY AND API PUBLIC KEY
|
||||
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint)
|
||||
VALUES ('google', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', 'callback_google', 'json','https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://accounts.google.com/o/oauth2/token');
|
||||
|
||||
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint )
|
||||
VALUES ('facebook', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', 'callback_facebook','text','https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://graph.facebook.com/oauth/access_token');
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
CREATE TABLE :table_name (
|
||||
CREATE TABLE $table_name (
|
||||
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
|
||||
`access_token` TEXT NOT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="primary-tabs">
|
||||
<div>
|
||||
{foreach item="item" from="$oauth_consumers"}
|
||||
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
-- Change the values `TO_COMPLETE` based on your API.
|
||||
-- API SECTET KEY AND API PUBLIC KEY
|
||||
|
||||
INSERT INTO `oauth2_consumers` ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint")
|
||||
VALUES ("google", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', "callback_google", "json","https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://accounts.google.com/o/oauth2/token");
|
||||
INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint" )
|
||||
VALUES ("facebook", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', "callback_facebook","text","https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://graph.facebook.com/oauth/access_token");
|
||||
@@ -30,19 +30,19 @@ CREATE TABLE `role_permissions`(
|
||||
`module` VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE TABLE "users_activations" (
|
||||
"aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0),
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"uid" INTEGER NOT NULL CHECK ("uid" >= 0),
|
||||
"created" DATETIME NOT NULL,
|
||||
CONSTRAINT "token" UNIQUE ("token")
|
||||
CREATE TABLE `users_activations` (
|
||||
`aid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK (`aid` >= 0),
|
||||
`token` VARCHAR(255) NOT NULL,
|
||||
`uid` INTEGER NOT NULL CHECK (`uid` >= 0),
|
||||
`created` DATETIME NOT NULL,
|
||||
CONSTRAINT `token` UNIQUE (`token`)
|
||||
);
|
||||
|
||||
CREATE TABLE "users_password_recovery" (
|
||||
"aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0),
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"uid" INTEGER NOT NULL CHECK ("uid" >= 0),
|
||||
"created" DATETIME NOT NULL,
|
||||
CONSTRAINT "token" UNIQUE ("token")
|
||||
CREATE TABLE `users_password_recovery` (
|
||||
`aid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK (`aid` >= 0),
|
||||
`token` VARCHAR(255) NOT NULL,
|
||||
`uid` INTEGER NOT NULL CHECK (`uid` >= 0),
|
||||
`created` DATETIME NOT NULL,
|
||||
CONSTRAINT `token` UNIQUE (`token`)
|
||||
);
|
||||
|
||||
|
||||
@@ -69,6 +69,10 @@ feature -- CMS setup
|
||||
a_setup.register_module (m)
|
||||
end
|
||||
|
||||
create {CMS_OAUTH_20_MODULE} m.make
|
||||
m.enable
|
||||
a_setup.register_module (m)
|
||||
|
||||
create {CMS_DEBUG_MODULE} m.make
|
||||
m.enable
|
||||
a_setup.register_module (m)
|
||||
|
||||
@@ -109,7 +109,7 @@ feature -- Access: internal
|
||||
do
|
||||
p := internal_application_config_path
|
||||
if p = Void then
|
||||
p := config_path.extended ("application_configuration.json")
|
||||
p := config_path.extended (name + ".json")
|
||||
internal_application_config_path := p
|
||||
end
|
||||
Result := p
|
||||
|
||||
18
library/email/email-safe.ecf
Normal file
18
library/email/email-safe.ecf
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-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="email_service" uuid="261DC9D5-C3A0-498D-A063-0BB6C80423CC" library_target="email_service">
|
||||
<target name="email_service">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="cms_app_env" location="..\app_env\app_env-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -7,8 +7,8 @@ class
|
||||
EMAIL_SERVICE
|
||||
|
||||
inherit
|
||||
|
||||
SHARED_ERROR
|
||||
|
||||
SHARED_LOGGER
|
||||
|
||||
create
|
||||
@@ -31,9 +31,7 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
admin_email := parameters.admin_email
|
||||
|
||||
-- Get local host name needed in creation of SMTP_PROTOCOL.
|
||||
create l_address_factory
|
||||
create smtp_protocol.make (parameters.smtp_server, l_address_factory.create_localhost.host_name)
|
||||
create {NOTIFICATION_SMTP_MAILER} mailer.make (parameters.smtp_server)
|
||||
set_successful
|
||||
end
|
||||
|
||||
@@ -43,7 +41,7 @@ feature {NONE} -- Initialization
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
-- Site admin's email.
|
||||
|
||||
smtp_protocol: SMTP_PROTOCOL
|
||||
mailer: NOTIFICATION_MAILER
|
||||
-- SMTP protocol.
|
||||
|
||||
feature -- Basic Operations
|
||||
@@ -60,38 +58,34 @@ feature -- Basic Operations
|
||||
|
||||
send_message (a_from_address, a_to_address: READABLE_STRING_8; a_subjet: READABLE_STRING_GENERAL; a_content: READABLE_STRING_GENERAL)
|
||||
local
|
||||
l_email: EMAIL
|
||||
l_email: NOTIFICATION_EMAIL
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
write_debug_log (generator + ".send_message: [from:" + a_from_address + ", to:" + a_to_address + ", subject:" + a_subjet + ", content:" + a_content)
|
||||
create l_email.make_with_entry (a_from_address, a_to_address)
|
||||
l_email.set_message (utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet))
|
||||
l_email.add_header_entry ("MIME-Version:", "1.0")
|
||||
l_email.add_header_entry ("Content-Type", "text/html; charset=utf-8")
|
||||
create l_email.make (a_from_address, a_to_address, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet) , utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
|
||||
l_email.add_header_line ("MIME-Version:1.0")
|
||||
l_email.add_header_line ("Content-Type: text/html; charset=utf-8")
|
||||
send_email (l_email)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_email (a_email: EMAIL)
|
||||
send_email (a_email: NOTIFICATION_EMAIL)
|
||||
-- Send the email represented by `a_email'.
|
||||
local
|
||||
l_retried: BOOLEAN
|
||||
do
|
||||
if not l_retried then
|
||||
write_information_log (generator + ".send_email Process send email.")
|
||||
smtp_protocol.initiate_protocol
|
||||
smtp_protocol.transfer (a_email)
|
||||
smtp_protocol.close_protocol
|
||||
mailer.process_email (a_email)
|
||||
write_information_log (generator + ".send_email Email sent.")
|
||||
if smtp_protocol.error then
|
||||
if mailer.has_error then
|
||||
set_last_error ("smtp_protocol reported an error", generator + ".send_email")
|
||||
else
|
||||
set_successful
|
||||
end
|
||||
else
|
||||
write_error_log (generator + ".send_email Email not send " + last_error_message )
|
||||
write_error_log (generator + ".send_email Email not send " + last_error_message)
|
||||
end
|
||||
rescue
|
||||
set_last_error_from_exception (generator + ".send_email")
|
||||
@@ -23,9 +23,9 @@ feature -- Initialization
|
||||
-- Create a database handler for ODBC with common settings.
|
||||
do
|
||||
create database_error_handler.make
|
||||
create db_application.login (username, password)
|
||||
db_application.set_hostname (hostname)
|
||||
db_application.set_data_source (database_name)
|
||||
create db_application.login (default_username, default_password)
|
||||
db_application.set_hostname (default_username)
|
||||
db_application.set_data_source (default_database_name)
|
||||
db_application.set_base
|
||||
create db_control.make
|
||||
end
|
||||
|
||||
3
license.lic
Normal file
3
license.lic
Normal file
@@ -0,0 +1,3 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
@@ -1,6 +1,6 @@
|
||||
<?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="login" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="login">
|
||||
<target name="login">
|
||||
<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">
|
||||
<target name="auth_module">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
@@ -10,18 +10,22 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="cms" location="$ISE_LIBRARY\unstable\library\web\cms\cms-safe.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="$ISE_LIBRARY\unstable\library\web\cms\library\app_env\app_env-safe.ecf" readonly="false"/>
|
||||
<library name="cms_model" location="$ISE_LIBRARY\unstable\library\web\cms\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="config" location="$ISE_LIBRARY\unstable\library\web\cms\library\configuration\config-safe.ecf"/>
|
||||
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
|
||||
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
|
||||
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
|
||||
<library name="config" location="..\..\library\configuration\config-safe.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="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="apis" location="$ISE_LIBRARY\contrib\library\web\communication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
|
||||
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\communication\oauth\cypress\consumer\cypress_consumer-safe.ecf" readonly="false"/>
|
||||
<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"/>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ feature {NONE} -- Initialization
|
||||
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
|
||||
setup := a_cms_api.setup
|
||||
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)
|
||||
@@ -31,7 +31,7 @@ feature {NONE} -- Initialization
|
||||
admin_email := l_site_name + " <" + admin_email +">"
|
||||
end
|
||||
|
||||
if attached {CONFIG_READER} a_cms_api.module_configuration ("login", Void) as cfg then
|
||||
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
|
||||
if attached cfg.text_item ("smtp") as l_smtp then
|
||||
-- Overwrite global smtp setting if any.
|
||||
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
|
||||
@@ -92,6 +92,8 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- Access
|
||||
|
||||
cms_api: CMS_API
|
||||
|
||||
smtp_server: IMMUTABLE_STRING_8
|
||||
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
@@ -104,74 +106,67 @@ feature -- Access
|
||||
contact_subject_password: IMMUTABLE_STRING_8
|
||||
contact_subject_oauth: IMMUTABLE_STRING_8
|
||||
|
||||
|
||||
|
||||
account_activation: STRING
|
||||
-- Account activation template email message.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_activation.html")
|
||||
if attached read_template_file (p) as l_content then
|
||||
Result := l_content
|
||||
else
|
||||
create Result.make_from_string (template_account_activation)
|
||||
end
|
||||
Result := template_string ("account_activation.html", default_template_account_activation)
|
||||
end
|
||||
|
||||
account_re_activation: STRING
|
||||
-- Account re_activation template email message.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := setup.environment.config_path.extended ("modules").extended ("login").extended("accunt_re_activation.html")
|
||||
if attached read_template_file (p) as l_content then
|
||||
Result := l_content
|
||||
else
|
||||
create Result.make_from_string (template_account_re_activation)
|
||||
end
|
||||
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation)
|
||||
end
|
||||
|
||||
account_password: STRING
|
||||
-- Account password template email message.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_new_password.html")
|
||||
if attached read_template_file (p) as l_content then
|
||||
Result := l_content
|
||||
else
|
||||
create Result.make_from_string (template_account_new_password)
|
||||
end
|
||||
Result := template_string ("account_new_password.html", default_template_account_new_password)
|
||||
end
|
||||
|
||||
account_welcome: STRING
|
||||
-- Account welcome template email message.
|
||||
do
|
||||
Result := template_string ("account_welcome.html", default_template_account_welcome)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: Template
|
||||
|
||||
template_path (a_name: READABLE_STRING_GENERAL): PATH
|
||||
-- Location of template named `a_name'.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_welcome.html")
|
||||
create p.make_from_string (a_name)
|
||||
Result := cms_api.module_location_by_name ({CMS_AUTHENTICATION_MODULE}.name).extended ("mail_templates").extended (a_name)
|
||||
end
|
||||
|
||||
template_string (a_name: READABLE_STRING_GENERAL; a_default: STRING): STRING
|
||||
-- Content of template named `a_name', or `a_default' if template is not found.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := template_path ("account_activation.html")
|
||||
if attached read_template_file (p) as l_content then
|
||||
Result := l_content
|
||||
else
|
||||
create Result.make_from_string (template_account_welcome)
|
||||
create Result.make_from_string (a_default)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
setup: CMS_SETUP
|
||||
|
||||
|
||||
read_template_file (a_path: PATH): detachable STRING
|
||||
-- Read the content of the file at path `a_path'.
|
||||
local
|
||||
l_file: FILE
|
||||
n: INTEGER
|
||||
do
|
||||
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
|
||||
if l_file.exists and then l_file.is_readable then
|
||||
n := l_file.count
|
||||
l_file.open_read
|
||||
l_file.read_stream (l_file.count)
|
||||
l_file.read_stream (n)
|
||||
Result := l_file.last_string
|
||||
l_file.close
|
||||
else
|
||||
@@ -182,7 +177,7 @@ feature {NONE} -- Implementation
|
||||
|
||||
feature {NONE} -- Message email
|
||||
|
||||
template_account_activation: STRING= "[
|
||||
default_template_account_activation: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -195,7 +190,7 @@ feature {NONE} -- Message email
|
||||
<body>
|
||||
<p>Thank you for registering at <a href="...">ROC CMS</a></p>
|
||||
|
||||
<p>To complete your registration, please click on this link to activate your account:<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>Thank you for joining us.</p>
|
||||
@@ -204,7 +199,7 @@ feature {NONE} -- Message email
|
||||
]"
|
||||
|
||||
|
||||
template_account_re_activation: STRING= "[
|
||||
default_template_account_re_activation: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -215,9 +210,9 @@ feature {NONE} -- Message email
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have request a new activation token at<a href="...">ROC CMS</a></p>
|
||||
<p>You have requested a new activation token at <a href="...">ROC CMS</a></p>
|
||||
|
||||
<p>To complete your registration, please click on this link to activate your account:<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>Thank you for joining us.</p>
|
||||
@@ -227,7 +222,7 @@ feature {NONE} -- Message email
|
||||
|
||||
|
||||
|
||||
template_account_new_password: STRING= "[
|
||||
default_template_account_new_password: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -240,7 +235,7 @@ feature {NONE} -- Message email
|
||||
<body>
|
||||
<p>You have required a new password at <a href="...">ROC CMS</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>
|
||||
@@ -248,7 +243,7 @@ feature {NONE} -- Message email
|
||||
]"
|
||||
|
||||
|
||||
template_account_welcome: STRING= "[
|
||||
default_template_account_welcome: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -1,5 +1,5 @@
|
||||
note
|
||||
description: "Module Logging supporting different authentication strategies"
|
||||
description: "Module Auth"
|
||||
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
|
||||
revision: "$Revision: 97328 $"
|
||||
|
||||
@@ -8,18 +8,10 @@ class
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as user_oauth_api
|
||||
redefine
|
||||
filters,
|
||||
register_hooks,
|
||||
initialize,
|
||||
is_installed,
|
||||
install,
|
||||
user_oauth_api
|
||||
register_hooks
|
||||
end
|
||||
|
||||
|
||||
CMS_HOOK_BLOCK
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
@@ -39,7 +31,6 @@ inherit
|
||||
|
||||
CMS_REQUEST_UTIL
|
||||
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -48,112 +39,17 @@ feature {NONE} -- Initialization
|
||||
make
|
||||
-- Create current module
|
||||
do
|
||||
name := "login"
|
||||
version := "1.0"
|
||||
description := "Eiffel login module"
|
||||
package := "login"
|
||||
description := "Authentication module"
|
||||
package := "authentication"
|
||||
|
||||
create root_dir.make_current
|
||||
cache_duration := 0
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
feature -- Access
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_user_auth_api: like user_oauth_api
|
||||
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I
|
||||
do
|
||||
Precursor (a_api)
|
||||
|
||||
-- Storage initialization
|
||||
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
|
||||
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
|
||||
else
|
||||
-- FIXME: in case of NULL storage, should Current be disabled?
|
||||
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage
|
||||
end
|
||||
|
||||
-- Node API initialization
|
||||
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage)
|
||||
user_oauth_api := l_user_auth_api
|
||||
ensure then
|
||||
user_oauth_api_set: user_oauth_api /= Void
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
is_installed (api: CMS_API): BOOLEAN
|
||||
-- Is Current module installed?
|
||||
do
|
||||
Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes")
|
||||
end
|
||||
|
||||
install (api: CMS_API)
|
||||
local
|
||||
l_setup: CMS_SETUP
|
||||
l_params: detachable STRING_TABLE [detachable ANY]
|
||||
l_consumers: LIST [STRING]
|
||||
do
|
||||
l_setup := api.setup
|
||||
|
||||
-- Schema
|
||||
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
|
||||
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
|
||||
--| Schema
|
||||
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers.sql"))
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for blog module", generating_type)
|
||||
end
|
||||
-- TODO workaround.
|
||||
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers_initialize.sql"))
|
||||
end
|
||||
|
||||
-- TODO workaround, until we have an admin module
|
||||
l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void)
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type)
|
||||
else
|
||||
from
|
||||
l_sql_storage.sql_start
|
||||
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
|
||||
until
|
||||
l_sql_storage.sql_after
|
||||
loop
|
||||
if attached l_sql_storage.sql_read_string (1) as l_name then
|
||||
l_consumers.force ("oauth2_"+l_name)
|
||||
end
|
||||
l_sql_storage.sql_forth
|
||||
end
|
||||
across l_consumers as ic loop
|
||||
if not l_sql_storage.sql_table_exists (ic.item) then
|
||||
create l_params.make (1)
|
||||
l_params.force (ic.item, "table_name")
|
||||
l_sql_storage.sql_execute_file_script_with_params (l_setup.environment.path.extended ("scripts").extended ("oauth2_template.sql"), l_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
|
||||
user_oauth_api: detachable CMS_OAUTH_20_API
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Filters
|
||||
|
||||
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
|
||||
-- Possibly list of Filter's module.
|
||||
do
|
||||
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
|
||||
if attached user_oauth_api as l_user_oauth_api then
|
||||
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api))
|
||||
end
|
||||
end
|
||||
name: STRING = "auth"
|
||||
|
||||
feature -- Access: docs
|
||||
|
||||
@@ -172,30 +68,24 @@ feature -- Access: docs
|
||||
|
||||
feature -- Router
|
||||
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
if attached user_oauth_api as l_user_oauth_api then
|
||||
configure_web (a_api, l_user_oauth_api, a_router)
|
||||
end
|
||||
configure_web (a_api, a_router)
|
||||
end
|
||||
|
||||
|
||||
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER)
|
||||
configure_web (a_api: CMS_API; a_router: WSF_ROUTER)
|
||||
do
|
||||
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-basic-auth", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_basic_auth (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/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post)
|
||||
end
|
||||
|
||||
|
||||
feature -- Hooks configuration
|
||||
|
||||
register_hooks (a_response: CMS_RESPONSE)
|
||||
@@ -229,46 +119,55 @@ feature -- Hooks
|
||||
end
|
||||
a_menu_system.primary_menu.extend (lnk)
|
||||
lnk.set_weight (98)
|
||||
if a_response.location.starts_with ("account/roc-login") then
|
||||
create lnk.make ("Basic Auth", "account/roc-basic-auth")
|
||||
lnk.set_expandable (True)
|
||||
a_response.add_to_primary_tabs (lnk)
|
||||
end
|
||||
end
|
||||
|
||||
block_list: ITERABLE [like {CMS_BLOCK}.name]
|
||||
local
|
||||
l_string: STRING
|
||||
do
|
||||
Result := <<"login","register","reactivate","new_password", "reset_password">>
|
||||
create l_string.make_empty
|
||||
across Result as ic loop
|
||||
Result := <<"login", "register", "reactivate", "new_password", "reset_password">>
|
||||
debug ("roc")
|
||||
create l_string.make_empty
|
||||
across
|
||||
Result as ic
|
||||
loop
|
||||
l_string.append (ic.item)
|
||||
l_string.append_character (' ')
|
||||
end
|
||||
write_debug_log (generator + ".block_list:" + l_string )
|
||||
write_debug_log (generator + ".block_list:" + l_string )
|
||||
end
|
||||
end
|
||||
|
||||
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
||||
do
|
||||
if
|
||||
a_block_id.is_case_insensitive_equal_general ("login") and then
|
||||
a_response.request.path_info.starts_with ("/account/roc-login")
|
||||
a_response.location.starts_with ("account/roc-basic-auth")
|
||||
then
|
||||
get_block_view_login (a_block_id, a_response)
|
||||
elseif
|
||||
a_block_id.is_case_insensitive_equal_general ("register") and then
|
||||
a_response.request.path_info.starts_with ("/account/roc-register")
|
||||
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.request.path_info.starts_with ("/account/reactivate")
|
||||
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.request.path_info.starts_with ("/account/new-password")
|
||||
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.request.path_info.starts_with ("/account/reset-password")
|
||||
a_response.location.starts_with ("account/reset-password")
|
||||
then
|
||||
get_block_view_reset_password (a_block_id, a_response)
|
||||
end
|
||||
@@ -283,34 +182,25 @@ feature -- Hooks
|
||||
r.execute
|
||||
end
|
||||
|
||||
handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_value ("Basic Auth", "optional_content_type")
|
||||
r.execute
|
||||
end
|
||||
|
||||
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_url: STRING
|
||||
l_cookie: WSF_COOKIE
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_cookie_token and then
|
||||
attached {CMS_USER} current_user (req) as l_user
|
||||
then
|
||||
-- Logout gmail
|
||||
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_cookie_token.value)
|
||||
l_cookie.set_path ("/")
|
||||
l_cookie.set_max_age (-1)
|
||||
res.add_cookie (l_cookie)
|
||||
unset_current_user (req)
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_status_code ({HTTP_CONSTANTS}.found)
|
||||
r.set_redirection (req.absolute_script_url (""))
|
||||
r.execute
|
||||
else
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_status_code ({HTTP_CONSTANTS}.found)
|
||||
l_url := req.absolute_script_url ("")
|
||||
l_url.append ("/basic_auth_logoff")
|
||||
r.set_redirection (l_url)
|
||||
r.execute
|
||||
end
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_status_code ({HTTP_CONSTANTS}.found)
|
||||
l_url := req.absolute_script_url ("/basic_auth_logoff")
|
||||
r.set_redirection (l_url)
|
||||
r.execute
|
||||
end
|
||||
|
||||
handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
@@ -321,7 +211,7 @@ feature -- Hooks
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
l_exist: BOOLEAN
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
l_link: STRING
|
||||
l_url: STRING
|
||||
l_token: STRING
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
@@ -359,15 +249,12 @@ feature -- Hooks
|
||||
-- Create activation token
|
||||
l_token := new_token
|
||||
l_user_api.new_activation (l_token, u.id)
|
||||
create l_link.make_from_string (req.server_url)
|
||||
l_link.append ("/account/activate/")
|
||||
l_link.append (l_token)
|
||||
|
||||
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_email")
|
||||
es.send_contact_email (l_email.value, l_link)
|
||||
es.send_contact_email (l_email.value, l_url)
|
||||
|
||||
else
|
||||
r.values.force (l_name.value, "name")
|
||||
@@ -401,8 +288,7 @@ feature -- Hooks
|
||||
-- the token does not exist, or it was already used.
|
||||
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
|
||||
r.set_value ("Account not activated", "optional_content_type")
|
||||
r.set_main_content ("<p>The token <i>"+ l_token.value +"</i> is not valid <a href=%"/account/reactivate%">Reactivate Account</a></p>" )
|
||||
|
||||
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
|
||||
@@ -418,7 +304,7 @@ feature -- Hooks
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
l_user_api: CMS_USER_API
|
||||
l_token: STRING
|
||||
l_link: STRING
|
||||
l_url: STRING
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
if req.is_post_request_method then
|
||||
@@ -434,14 +320,12 @@ feature -- Hooks
|
||||
else
|
||||
l_token := new_token
|
||||
l_user_api.new_activation (l_token, l_user.id)
|
||||
create l_link.make_from_string (req.server_url)
|
||||
l_link.append ("/account/activate/")
|
||||
l_link.append (l_token)
|
||||
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_link)
|
||||
es.send_contact_activation_email (l_email.value, l_url)
|
||||
end
|
||||
else
|
||||
r.values.force ("The email does not exist or !", "error_email")
|
||||
@@ -460,7 +344,7 @@ feature -- Hooks
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
l_user_api: CMS_USER_API
|
||||
l_token: STRING
|
||||
l_link: STRING
|
||||
l_url: STRING
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
if req.is_post_request_method then
|
||||
@@ -470,14 +354,12 @@ feature -- Hooks
|
||||
-- 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)
|
||||
create l_link.make_from_string (req.server_url)
|
||||
l_link.append ("/account/reset-password?token=")
|
||||
l_link.append (l_token)
|
||||
l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token)
|
||||
|
||||
-- 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_link)
|
||||
es.send_contact_password_email (l_email.value, l_url)
|
||||
else
|
||||
r.values.force ("The email does not exist !", "error_email")
|
||||
r.values.force (l_email.value, "email")
|
||||
@@ -499,7 +381,7 @@ feature -- Hooks
|
||||
if attached {WSF_STRING} req.query_parameter ("token") as l_token then
|
||||
r.values.force (l_token.value, "token")
|
||||
if l_user_api.user_by_password_token (l_token.value) = Void then
|
||||
r.values.force ("The token " + l_token.value + " is not valid, click <a href=%"/account/new-password%">here</a> to generate a new token.", "error_token")
|
||||
r.values.force ("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
|
||||
@@ -538,7 +420,8 @@ 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.module_resource_path (Current, p)
|
||||
|
||||
p := a_response.api.module_theme_resource_location (Current, p)
|
||||
if p /= Void then
|
||||
if attached p.entry as e then
|
||||
create Result.make (a_block_id, Void, p.parent, e)
|
||||
@@ -563,13 +446,6 @@ feature {NONE} -- Block views
|
||||
loop
|
||||
l_tpl_block.set_value (ic.item, ic.key)
|
||||
end
|
||||
if
|
||||
attached user_oauth_api as l_auth_api and then
|
||||
attached l_auth_api.oauth2_consumers as l_list
|
||||
then
|
||||
l_tpl_block.set_value (l_list, "oauth_consumers")
|
||||
end
|
||||
|
||||
a_response.add_block (l_tpl_block, "content")
|
||||
else
|
||||
debug ("cms")
|
||||
@@ -717,111 +593,6 @@ feature {NONE} -- Block views
|
||||
end
|
||||
end
|
||||
|
||||
feature -- OAuth2 Login with google.
|
||||
|
||||
handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_oauth: CMS_OAUTH_20_WORKFLOW
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then
|
||||
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
|
||||
then
|
||||
create l_oauth.make (req.server_url, l_consumer)
|
||||
if attached l_oauth.authorization_url as l_authorization_url then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_redirection (l_authorization_url)
|
||||
r.execute
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("Bad request")
|
||||
r.execute
|
||||
end
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("Bad request")
|
||||
r.execute
|
||||
end
|
||||
end
|
||||
|
||||
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_auth: CMS_OAUTH_20_WORKFLOW
|
||||
l_user_api: CMS_USER_API
|
||||
l_user: CMS_USER
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
l_cookie: WSF_COOKIE
|
||||
es: CMS_AUTHENTICATON_EMAIL_SERVICE
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then
|
||||
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
|
||||
attached {WSF_STRING} req.query_parameter ("code") as l_code
|
||||
then
|
||||
create l_auth.make (req.server_url, l_consumer)
|
||||
l_auth.sign_request (l_code.value)
|
||||
if
|
||||
attached l_auth.access_token as l_access_token and then
|
||||
attached l_auth.user_profile as l_user_profile
|
||||
then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
-- extract user email
|
||||
-- check if the user exist
|
||||
l_user_api := api.user_api
|
||||
-- 1 if the user exit put it in the context
|
||||
if
|
||||
attached l_auth.user_email as l_email
|
||||
then
|
||||
if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then
|
||||
-- User with email exist
|
||||
if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
|
||||
-- Update oauth entry
|
||||
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
|
||||
else
|
||||
-- create a oauth entry
|
||||
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
|
||||
end
|
||||
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
|
||||
l_cookie.set_max_age (l_access_token.expires_in)
|
||||
l_cookie.set_path ("/")
|
||||
res.add_cookie (l_cookie)
|
||||
else
|
||||
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1)
|
||||
l_roles.force (l_user_api.authenticated_user_role)
|
||||
|
||||
-- Create a new user and oauth entry
|
||||
create l_user.make (l_email)
|
||||
l_user.set_email (l_email)
|
||||
l_user.set_password (new_token) -- generate a random password.
|
||||
l_user.set_roles (l_roles)
|
||||
l_user.mark_active
|
||||
l_user_api.new_user (l_user)
|
||||
|
||||
-- Add oauth entry
|
||||
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name )
|
||||
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
|
||||
l_cookie.set_max_age (l_access_token.expires_in)
|
||||
l_cookie.set_path ("/")
|
||||
res.add_cookie (l_cookie)
|
||||
set_current_user (req, l_user)
|
||||
|
||||
|
||||
-- Send Email
|
||||
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
|
||||
write_debug_log (generator + ".handle register: send_contact_welcome_email")
|
||||
es.send_contact_welcome_email (l_email, "")
|
||||
end
|
||||
else
|
||||
end
|
||||
r.set_redirection (req.absolute_script_url (""))
|
||||
r.execute
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
feature {NONE} -- Token Generation
|
||||
|
||||
@@ -845,7 +616,6 @@ feature {NONE} -- Token Generation
|
||||
end
|
||||
|
||||
|
||||
|
||||
feature {NONE} -- Implementation: date and time
|
||||
|
||||
http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME
|
||||
10
modules/auth/cms_login_form.e
Normal file
10
modules/auth/cms_login_form.e
Normal file
@@ -0,0 +1,10 @@
|
||||
note
|
||||
description: "Summary description for {CMS_LOGIN_FORM}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_LOGIN_FORM
|
||||
|
||||
end
|
||||
8
modules/auth/site/auth.json
Normal file
8
modules/auth/site/auth.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"email": "webmaster@example.com",
|
||||
"subjet_register": "Thank you for regitering with us, activate account",
|
||||
"subjet_activate": "New account ativation token",
|
||||
"subjet_password": "Password Recovery!!!",
|
||||
"subjet_oauth": "Welcome",
|
||||
"smtp": "127.0.0.1"
|
||||
}
|
||||
18
modules/auth/site/mail_templates/account_activation.html
Normal file
18
modules/auth/site/mail_templates/account_activation.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Activation</title>
|
||||
<meta name="description" content="Activation">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</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>
|
||||
</body>
|
||||
</html>
|
||||
17
modules/auth/site/mail_templates/account_new_password.html
Normal file
17
modules/auth/site/mail_templates/account_new_password.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>New Password</title>
|
||||
<meta name="description" content="New Password">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have required a new password at <a href="...">ROC CMS</a></p>
|
||||
|
||||
<p>To complete your request, please click on this link to genereate a new password:<p>
|
||||
|
||||
<p><a href="$link">$link</a></p>
|
||||
</body>
|
||||
</html>
|
||||
18
modules/auth/site/mail_templates/account_re_activation.html
Normal file
18
modules/auth/site/mail_templates/account_re_activation.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>New Activation</title>
|
||||
<meta name="description" content="New Activation token">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have request a new activation token at<a href="...">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>
|
||||
</body>
|
||||
</html>
|
||||
13
modules/auth/site/mail_templates/account_welcome.html
Normal file
13
modules/auth/site/mail_templates/account_welcome.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Welcome</title>
|
||||
<meta name="description" content="Welcome">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
<body>
|
||||
<p>Welcome to<a href="...">ROC CMS</a></p>
|
||||
<p>Thank you for joining us.</p>
|
||||
</body>
|
||||
</html>
|
||||
7
modules/auth/site/oauth2_gmail.json
Normal file
7
modules/auth/site/oauth2_gmail.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"api_secret":"ADD_YOUR_SECRET_KEY",
|
||||
"api_key":"ADD_YOUR_PUBLIC_KEY",
|
||||
"scope": "email",
|
||||
"api_revoke":"https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN",
|
||||
"protected_resource_url":"https://www.googleapis.com/plus/v1/people/me"
|
||||
}
|
||||
34
modules/auth/site/templates/block_login.tpl
Normal file
34
modules/auth/site/templates/block_login.tpl
Normal file
@@ -0,0 +1,34 @@
|
||||
<div class="primary-tabs">
|
||||
{unless isset="$user"}
|
||||
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
|
||||
<div>
|
||||
<div>
|
||||
<form action method="POST">
|
||||
<div>
|
||||
<input type="text" name="username" required>
|
||||
<label>Username</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="password" name="password" required>
|
||||
<label>Password</label>
|
||||
</div>
|
||||
|
||||
<button type="button" onclick="ROC_AUTH.login();">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
<a href="{$site_url/}account/new-password">Forgot password?</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{foreach item="item" from="$oauth_consumers"}
|
||||
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/unless}
|
||||
</div>
|
||||
16
modules/auth/site/templates/block_new_password.tpl
Normal file
16
modules/auth/site/templates/block_new_password.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
<div>
|
||||
<form action="/account/new-password" method="post">
|
||||
<fieldset>
|
||||
<legend>Require new password</legend>
|
||||
<div>
|
||||
<input type="email" id="email" name="email" value="{$email/}" required/>
|
||||
<label for="email">Email</label>
|
||||
{if isset="$error_email"}
|
||||
<span><i>{$error_email/}</i></span> <br>
|
||||
{/if}
|
||||
<br>
|
||||
</div>
|
||||
<button type="submit">Send</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
3
modules/auth/site/templates/block_post_password.tpl
Normal file
3
modules/auth/site/templates/block_post_password.tpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>We have send you a new token code, check your email to generate a new password</p>
|
||||
</div>
|
||||
3
modules/auth/site/templates/block_post_reactivate.tpl
Normal file
3
modules/auth/site/templates/block_post_reactivate.tpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>We have send you a new activation code, check your email to activate your account.</p>
|
||||
</div>
|
||||
3
modules/auth/site/templates/block_post_register.tpl
Normal file
3
modules/auth/site/templates/block_post_register.tpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>Thanks for register, check your email to activate your account.</p>
|
||||
</div>
|
||||
3
modules/auth/site/templates/block_post_reset.tpl
Normal file
3
modules/auth/site/templates/block_post_reset.tpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<p>You new password has been saved!</p>
|
||||
</div>
|
||||
19
modules/auth/site/templates/block_reactivate.tpl
Normal file
19
modules/auth/site/templates/block_reactivate.tpl
Normal file
@@ -0,0 +1,19 @@
|
||||
<div>
|
||||
<form action="/account/reactivate" method="post">
|
||||
<fieldset>
|
||||
<legend>Reactivate Form</legend>
|
||||
<div>
|
||||
<input type="email" id="email" name="email" value="{$email/}" required/>
|
||||
<label for="email">Email</label>
|
||||
{if isset="$error_email"}
|
||||
<span><i>{$error_email/}</i></span> <br>
|
||||
{/if}
|
||||
<br>
|
||||
{if isset="$is_active"}
|
||||
<span><i>{$is_active/}</i></span> <br>
|
||||
{/if}
|
||||
</div>
|
||||
<button type="submit">Reactivate</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
28
modules/auth/site/templates/block_register.tpl
Normal file
28
modules/auth/site/templates/block_register.tpl
Normal file
@@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<form action="/account/roc-register" method="post">
|
||||
<fieldset>
|
||||
<legend>Register Form</legend>
|
||||
<div>
|
||||
<input type="text" id="name" name="name" value="{$name/}" required autofocus />
|
||||
<label for="name">Name</label>
|
||||
{if isset="$error_name"}
|
||||
<span><i>{$error_name/}</i></span> <br>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" id="password" name="password" value="" required/>
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="email" id="email" name="email" value="{$email/}" required/>
|
||||
<label for="email">Email</label>
|
||||
{if isset="$error_email"}
|
||||
<span><i>{$error_email/}</i></span> <br>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit">Register</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
28
modules/auth/site/templates/block_reset_password.tpl
Normal file
28
modules/auth/site/templates/block_reset_password.tpl
Normal file
@@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<form action="/account/reset-password" method="post">
|
||||
<fieldset>
|
||||
<legend>Generate New Password Form</legend>
|
||||
<div>
|
||||
<input type="text" id="token" name="token" value="{$token/}" required />
|
||||
<label for="token">Token</label>
|
||||
{if isset="$error_token"}
|
||||
<span><i>{$error_token/}</i></span> <br>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" id="password" name="password" value="" required/>
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" id="confirm_password" name="confirm_password" value="" required/>
|
||||
<label for="password">Confirm Password</label>
|
||||
</div>
|
||||
|
||||
<button type="submit">Confirm</button>
|
||||
{if isset="$error_password"}
|
||||
<span><i>{$error_password/}</i></span> <br>
|
||||
{/if}
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="basic_auth" uuid="05216FE9-20F3-45FB-AB2E-8FCD75A8AD7E" library_target="basic_auth">
|
||||
<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="basic_auth" uuid="26D7AD29-6E07-444D-A9CE-067D53029340" library_target="basic_auth">
|
||||
<target name="basic_auth">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -29,12 +29,15 @@ feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
name := "basic auth"
|
||||
version := "1.0"
|
||||
description := "Service to manage basic authentication"
|
||||
package := "core"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "basic_auth"
|
||||
|
||||
feature -- Access: router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
@@ -64,7 +67,7 @@ feature {NONE} -- Implementation: routes
|
||||
create l_bal_handler.make (api)
|
||||
create l_methods
|
||||
l_methods.enable_get
|
||||
a_router.handle_with_request_methods ("/basic_auth_login", l_bal_handler, l_methods)
|
||||
a_router.handle ("/basic_auth_login", l_bal_handler, l_methods)
|
||||
end
|
||||
|
||||
configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER)
|
||||
@@ -75,7 +78,7 @@ feature {NONE} -- Implementation: routes
|
||||
create l_bal_handler.make (api)
|
||||
create l_methods
|
||||
l_methods.enable_get
|
||||
a_router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods)
|
||||
a_router.handle ("/basic_auth_logoff", l_bal_handler, l_methods)
|
||||
end
|
||||
|
||||
feature -- Hooks configuration
|
||||
@@ -106,7 +109,7 @@ feature -- Hooks
|
||||
-- Hook execution on collection of menu contained by `a_menu_system'
|
||||
-- for related response `a_response'.
|
||||
local
|
||||
lnk: CMS_LOCAL_LINK
|
||||
-- lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
-- if attached a_response.current_user (a_response.request) as u then
|
||||
-- create lnk.make (u.name + " (Logout)", "basic_auth_logoff?destination=" + a_response.request.request_uri)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {CMS_AUTHENTICATION_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_AUTHENTICATION_CONSTANTS
|
||||
|
||||
feature -- Access
|
||||
|
||||
oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_"
|
||||
|
||||
end
|
||||
@@ -29,7 +29,6 @@ feature {NONE} -- Initialization
|
||||
make (a_setup: CMS_SETUP)
|
||||
-- Create Current module, disabled by default.
|
||||
do
|
||||
name := "node"
|
||||
version := "1.0"
|
||||
description := "Service to manage content based on 'node'"
|
||||
package := "core"
|
||||
@@ -39,6 +38,10 @@ feature {NONE} -- Initialization
|
||||
config: CMS_SETUP
|
||||
-- Node configuration.
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "node"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
@@ -99,7 +102,8 @@ feature {CMS_API} -- Module management
|
||||
is_installed (a_api: CMS_API): BOOLEAN
|
||||
-- Is Current module installed?
|
||||
do
|
||||
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
|
||||
Result := Precursor (a_api)
|
||||
if Result and attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
|
||||
Result := l_sql_storage.sql_table_exists ("nodes") and
|
||||
l_sql_storage.sql_table_exists ("page_nodes")
|
||||
end
|
||||
@@ -109,8 +113,9 @@ feature {CMS_API} -- Module management
|
||||
do
|
||||
-- Schema
|
||||
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
|
||||
l_sql_storage.sql_execute_file_script (a_api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql"))
|
||||
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
|
||||
end
|
||||
Precursor {CMS_MODULE}(a_api)
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
@@ -149,26 +154,26 @@ feature -- Access: router
|
||||
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
|
||||
create l_node_handler.make (a_api, a_node_api)
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/node", l_node_handler)
|
||||
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get_post)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get_post)
|
||||
|
||||
a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle_with_request_methods ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle_with_request_methods ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
|
||||
a_router.handle ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
|
||||
|
||||
a_router.handle_with_request_methods ("/node/{id}", l_node_handler, a_router.methods_get)
|
||||
a_router.handle ("/node/{id}", l_node_handler, a_router.methods_get)
|
||||
-- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post)
|
||||
|
||||
-- Nodes
|
||||
create l_nodes_handler.make (a_api, a_node_api)
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
|
||||
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get)
|
||||
|
||||
--Trash
|
||||
|
||||
create l_trash_handler.make (a_api, a_node_api)
|
||||
create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
|
||||
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
|
||||
a_router.map (l_uri_mapping, a_router.methods_get)
|
||||
|
||||
end
|
||||
|
||||
|
||||
22
modules/node/site/scripts/node.sql
Normal file
22
modules/node/site/scripts/node.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
CREATE TABLE nodes (
|
||||
`nid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
|
||||
`revision` INTEGER,
|
||||
`type` TEXT NOT NULL,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`summary` TEXT,
|
||||
`content` TEXT,
|
||||
`format` VARCHAR(128),
|
||||
`author` INTEGER,
|
||||
`publish` DATETIME,
|
||||
`created` DATETIME NOT NULL,
|
||||
`changed` DATETIME NOT NULL,
|
||||
`status` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE page_nodes(
|
||||
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||
`revision` INTEGER,
|
||||
`parent` INTEGER
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ inherit
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create {CMS_AUTHENTICATION_MODULE}
|
||||
create {CMS_OAUTH_20_MODULE}
|
||||
make_with_storage
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
@@ -34,20 +34,20 @@ feature {CMS_MODULE} -- Access: User oauth storage.
|
||||
|
||||
feature -- Access: User Oauth20
|
||||
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
|
||||
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if any.
|
||||
do
|
||||
Result := oauth_20_storage.user_oauth2_by_id (a_uid, a_consumer)
|
||||
end
|
||||
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
|
||||
do
|
||||
Result := oauth_20_storage.user_oauth2_by_token (a_token, a_consumer)
|
||||
end
|
||||
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
|
||||
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
|
||||
do
|
||||
Result := oauth_20_storage.user_oauth2_without_consumer_by_token (a_token)
|
||||
end
|
||||
@@ -75,7 +75,7 @@ feature -- Access: Consumers OAuth20
|
||||
feature -- Change: User OAuth20
|
||||
|
||||
|
||||
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
|
||||
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL)
|
||||
-- Add a new user with oauth20 using the consumer `a_consumer'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
@@ -84,7 +84,7 @@ feature -- Change: User OAuth20
|
||||
end
|
||||
|
||||
|
||||
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
|
||||
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
|
||||
-- Updaate user `a_user' with oauth2 for the consumer `a_consumer'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
21
modules/oauth20/cms_oauth_20_constants.e
Normal file
21
modules/oauth20/cms_oauth_20_constants.e
Normal file
@@ -0,0 +1,21 @@
|
||||
note
|
||||
description: "Summary description for {CMS_OAUTH_20_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_OAUTH_20_CONSTANTS
|
||||
|
||||
feature -- Access
|
||||
|
||||
oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_"
|
||||
-- Name of Cookie used to keep the session info.
|
||||
-- FIXME: make this configurable.
|
||||
|
||||
oauth_callback: STRING = "callback"
|
||||
-- Callback parameter.
|
||||
|
||||
oauth_code: STRING = "code"
|
||||
-- Code query parameter.
|
||||
|
||||
end
|
||||
@@ -8,17 +8,23 @@ class
|
||||
CMS_OAUTH_20_CONSUMER
|
||||
|
||||
inherit
|
||||
|
||||
ANY
|
||||
redefine
|
||||
default_create
|
||||
end
|
||||
|
||||
create
|
||||
default_create
|
||||
default_create,
|
||||
make_with_id
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_id (a_id: like id)
|
||||
do
|
||||
id := a_id
|
||||
default_create
|
||||
end
|
||||
|
||||
default_create
|
||||
do
|
||||
set_endpoint ("")
|
||||
@@ -34,29 +40,29 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- Access
|
||||
|
||||
endpoint: READABLE_STRING_32
|
||||
endpoint: READABLE_STRING_8
|
||||
-- Url that receives the access token request.
|
||||
|
||||
authorize_url: READABLE_STRING_32
|
||||
authorize_url: READABLE_STRING_8
|
||||
--
|
||||
|
||||
extractor: READABLE_STRING_32
|
||||
extractor: READABLE_STRING_8
|
||||
-- text, json
|
||||
|
||||
|
||||
callback_name: READABLE_STRING_32
|
||||
callback_name: READABLE_STRING_8
|
||||
-- consumer callback name
|
||||
|
||||
protected_resource_url: READABLE_STRING_32
|
||||
protected_resource_url: READABLE_STRING_8
|
||||
-- consumer resource url
|
||||
|
||||
scope: READABLE_STRING_32
|
||||
scope: READABLE_STRING_8
|
||||
-- consumer scope
|
||||
|
||||
api_key: READABLE_STRING_32
|
||||
api_key: READABLE_STRING_8
|
||||
-- consumer public key
|
||||
|
||||
api_secret: READABLE_STRING_32
|
||||
api_secret: READABLE_STRING_8
|
||||
-- consumer secret.
|
||||
|
||||
name: READABLE_STRING_32
|
||||
@@ -65,8 +71,6 @@ feature -- Access
|
||||
id: INTEGER_64
|
||||
-- unique identifier.
|
||||
|
||||
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_extractor (a_extractor: like extractor)
|
||||
63
modules/oauth20/cms_oauth_20_email_service.e
Normal file
63
modules/oauth20/cms_oauth_20_email_service.e
Normal file
@@ -0,0 +1,63 @@
|
||||
note
|
||||
description: "Summary description for {CMS_OAUTH_20_EMAIL_SERVICE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_OAUTH_20_EMAIL_SERVICE
|
||||
|
||||
inherit
|
||||
EMAIL_SERVICE
|
||||
redefine
|
||||
initialize,
|
||||
parameters
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
contact_email := parameters.contact_email
|
||||
end
|
||||
|
||||
parameters: CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS
|
||||
-- Associated parameters.
|
||||
|
||||
feature -- Access
|
||||
|
||||
contact_email: IMMUTABLE_STRING_8
|
||||
-- contact email.
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
send_contact_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_activation)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_register, l_message)
|
||||
end
|
||||
|
||||
|
||||
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
|
||||
-- Send successful contact message `a_token' to `a_to'.
|
||||
require
|
||||
attached_to: a_to /= Void
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (parameters.account_welcome)
|
||||
l_message.replace_substring_all ("$link", a_content)
|
||||
send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
260
modules/oauth20/cms_oauth_20_email_service_parameters.e
Normal file
260
modules/oauth20/cms_oauth_20_email_service_parameters.e
Normal file
@@ -0,0 +1,260 @@
|
||||
note
|
||||
description: "Summary description for {CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS
|
||||
|
||||
inherit
|
||||
EMAIL_SERVICE_PARAMETERS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
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)
|
||||
admin_email := a_cms_api.setup.site_email
|
||||
|
||||
if not admin_email.has ('<') then
|
||||
admin_email := l_site_name + " <" + admin_email +">"
|
||||
end
|
||||
|
||||
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
|
||||
if attached cfg.text_item ("smtp") as l_smtp then
|
||||
-- Overwrite global smtp setting if any.
|
||||
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
|
||||
end
|
||||
s := cfg.text_item ("email")
|
||||
if s /= Void then
|
||||
l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
s := cfg.text_item ("subject_register")
|
||||
if s /= Void then
|
||||
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
s := cfg.text_item ("subject_activate")
|
||||
if s /= Void then
|
||||
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
s := cfg.text_item ("subject_password")
|
||||
if s /= Void then
|
||||
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
s := cfg.text_item ("subject_oauth")
|
||||
if s /= Void then
|
||||
l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
|
||||
end
|
||||
if l_contact_email /= Void then
|
||||
if not l_contact_email.has ('<') then
|
||||
l_contact_email := l_site_name + " <" + l_contact_email + ">"
|
||||
end
|
||||
contact_email := l_contact_email
|
||||
else
|
||||
contact_email := admin_email
|
||||
end
|
||||
if l_subject_register /= Void then
|
||||
contact_subject_register := l_subject_register
|
||||
else
|
||||
contact_subject_register := "Thank you for registering with us."
|
||||
end
|
||||
|
||||
if l_subject_activate /= Void then
|
||||
contact_subject_activate := l_subject_activate
|
||||
else
|
||||
contact_subject_activate := "New account activation token."
|
||||
end
|
||||
if l_subject_password /= Void then
|
||||
contact_subject_password := l_subject_password
|
||||
else
|
||||
contact_subject_password := "Password Recovery."
|
||||
end
|
||||
if l_subject_oauth /= Void then
|
||||
contact_subject_oauth := l_subject_oauth
|
||||
else
|
||||
contact_subject_oauth := "Welcome."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
smtp_server: IMMUTABLE_STRING_8
|
||||
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
|
||||
contact_email: IMMUTABLE_STRING_8
|
||||
-- Contact email.
|
||||
|
||||
contact_subject_register: IMMUTABLE_STRING_8
|
||||
contact_subject_activate: IMMUTABLE_STRING_8
|
||||
contact_subject_password: IMMUTABLE_STRING_8
|
||||
contact_subject_oauth: IMMUTABLE_STRING_8
|
||||
|
||||
account_activation: STRING
|
||||
-- Account activation template email message.
|
||||
do
|
||||
Result := template_string ("account_activation.html", default_template_account_activation)
|
||||
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_password: STRING
|
||||
-- Account password template email message.
|
||||
do
|
||||
Result := template_string ("account_new_password.html", default_template_account_new_password)
|
||||
end
|
||||
|
||||
account_welcome: STRING
|
||||
-- Account welcome template email message.
|
||||
do
|
||||
Result := template_string ("account_welcome.html", default_template_account_welcome)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: Template
|
||||
|
||||
template_path (a_name: READABLE_STRING_GENERAL): PATH
|
||||
-- Location of template named `a_name'.
|
||||
do
|
||||
Result := cms_api.module_location_by_name ({CMS_AUTHENTICATION_MODULE}.name).extended (a_name)
|
||||
end
|
||||
|
||||
template_string (a_name: READABLE_STRING_GENERAL; a_default: STRING): STRING
|
||||
-- Content of template named `a_name', or `a_default' if template is not found.
|
||||
local
|
||||
p: PATH
|
||||
do
|
||||
p := template_path ("account_activation.html")
|
||||
if attached read_template_file (p) as l_content then
|
||||
Result := l_content
|
||||
else
|
||||
create Result.make_from_string (a_default)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
cms_api: CMS_API
|
||||
|
||||
read_template_file (a_path: PATH): detachable STRING
|
||||
-- Read the content of the file at path `a_path'.
|
||||
local
|
||||
l_file: FILE
|
||||
n: INTEGER
|
||||
do
|
||||
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
|
||||
if l_file.exists and then l_file.is_readable then
|
||||
n := l_file.count
|
||||
l_file.open_read
|
||||
l_file.read_stream (n)
|
||||
Result := l_file.last_string
|
||||
l_file.close
|
||||
else
|
||||
-- Error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Message email
|
||||
|
||||
default_template_account_activation: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Activation</title>
|
||||
<meta name="description" content="Activation">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Thank you for registering at <a href="...">ROC CMS</a></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>Thank you for joining us.</p>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
|
||||
default_template_account_re_activation: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>New Activation</title>
|
||||
<meta name="description" content="New Activation token">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have requested a new activation token at <a href="...">ROC CMS</a></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>Thank you for joining us.</p>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
|
||||
|
||||
default_template_account_new_password: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>New Password</title>
|
||||
<meta name="description" content="New Password">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have required a new password at <a href="...">ROC CMS</a></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>
|
||||
</html>
|
||||
]"
|
||||
|
||||
|
||||
default_template_account_welcome: STRING = "[
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Welcome</title>
|
||||
<meta name="description" content="Welcome">
|
||||
<meta name="author" content="ROC CMS">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Welcome to<a href="...">ROC CMS</a></p>
|
||||
<p>Thank you for joining us.</p>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
end
|
||||
512
modules/oauth20/cms_oauth_20_module.e
Normal file
512
modules/oauth20/cms_oauth_20_module.e
Normal file
@@ -0,0 +1,512 @@
|
||||
note
|
||||
description: "Generic OAuth Module supporting authentication using different providers."
|
||||
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
|
||||
revision: "$Revision: 97328 $"
|
||||
|
||||
class
|
||||
CMS_OAUTH_20_MODULE
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
module_api as user_oauth_api
|
||||
redefine
|
||||
filters,
|
||||
register_hooks,
|
||||
initialize,
|
||||
install,
|
||||
user_oauth_api
|
||||
end
|
||||
|
||||
|
||||
CMS_HOOK_BLOCK
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
|
||||
CMS_HOOK_MENU_SYSTEM_ALTER
|
||||
|
||||
CMS_HOOK_VALUE_TABLE_ALTER
|
||||
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_LOGGER
|
||||
|
||||
CMS_REQUEST_UTIL
|
||||
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Create current module
|
||||
do
|
||||
version := "1.0"
|
||||
description := "OAuth20 module"
|
||||
package := "Oauth20"
|
||||
|
||||
create root_dir.make_current
|
||||
cache_duration := 0
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "oauth20"
|
||||
|
||||
feature {CMS_API} -- Module Initialization
|
||||
|
||||
initialize (a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_user_auth_api: like user_oauth_api
|
||||
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I
|
||||
do
|
||||
Precursor (a_api)
|
||||
|
||||
-- Storage initialization
|
||||
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
|
||||
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
|
||||
else
|
||||
-- FIXME: in case of NULL storage, should Current be disabled?
|
||||
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage
|
||||
end
|
||||
|
||||
-- API initialization
|
||||
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage)
|
||||
user_oauth_api := l_user_auth_api
|
||||
ensure then
|
||||
user_oauth_api_set: user_oauth_api /= Void
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
install (api: CMS_API)
|
||||
local
|
||||
l_consumers: LIST [STRING]
|
||||
do
|
||||
-- Schema
|
||||
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
|
||||
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
|
||||
--| Schema
|
||||
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void)
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for blog module", generating_type)
|
||||
end
|
||||
-- TODO workaround.
|
||||
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers_initialize.sql")), Void)
|
||||
end
|
||||
|
||||
-- TODO workaround, until we have an admin module
|
||||
l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void)
|
||||
if l_sql_storage.has_error then
|
||||
api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type)
|
||||
else
|
||||
from
|
||||
l_sql_storage.sql_start
|
||||
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
|
||||
until
|
||||
l_sql_storage.sql_after
|
||||
loop
|
||||
if attached l_sql_storage.sql_read_string (1) as l_name then
|
||||
l_consumers.force ("oauth2_" + l_name)
|
||||
end
|
||||
l_sql_storage.sql_forth
|
||||
end
|
||||
across l_consumers as ic loop
|
||||
if not l_sql_storage.sql_table_exists (ic.item) then
|
||||
if attached l_sql_storage.sql_script_content (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_table.sql.tpl"))) as sql then
|
||||
-- FIXME: shouldn't we use a unique table for all oauth providers? or as it is .. one table per oauth provider?
|
||||
sql.replace_substring_all ("$table_name", ic.item)
|
||||
l_sql_storage.sql_execute_script (sql, Void)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Precursor {CMS_MODULE}(api)
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
|
||||
user_oauth_api: detachable CMS_OAUTH_20_API
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Filters
|
||||
|
||||
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
|
||||
-- Possibly list of Filter's module.
|
||||
do
|
||||
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
|
||||
if attached user_oauth_api as l_user_oauth_api then
|
||||
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api))
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access: docs
|
||||
|
||||
root_dir: PATH
|
||||
|
||||
cache_duration: INTEGER
|
||||
-- Caching duration
|
||||
--| 0: disable
|
||||
--| -1: cache always valie
|
||||
--| nb: cache expires after nb seconds.
|
||||
|
||||
cache_disabled: BOOLEAN
|
||||
do
|
||||
Result := cache_duration = 0
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
if attached user_oauth_api as l_user_oauth_api then
|
||||
configure_web (a_api, l_user_oauth_api, a_router)
|
||||
end
|
||||
end
|
||||
|
||||
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER)
|
||||
do
|
||||
a_router.handle ("/account/roc-oauth-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
|
||||
a_router.handle ("/account/roc-oauth-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post)
|
||||
end
|
||||
|
||||
feature -- Hooks configuration
|
||||
|
||||
register_hooks (a_response: CMS_RESPONSE)
|
||||
-- Module hooks configuration.
|
||||
do
|
||||
auto_subscribe_to_hooks (a_response)
|
||||
a_response.subscribe_to_block_hook (Current)
|
||||
a_response.subscribe_to_value_table_alter_hook (Current)
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
if attached current_user (a_response.request) as l_user then
|
||||
a_value.force (l_user, "user")
|
||||
end
|
||||
end
|
||||
|
||||
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
|
||||
-- Hook execution on collection of menu contained by `a_menu_system'
|
||||
-- for related response `a_response'.
|
||||
local
|
||||
lnk: CMS_LOCAL_LINK
|
||||
lnk2: detachable CMS_LINK
|
||||
do
|
||||
if
|
||||
attached a_response.current_user (a_response.request) as u and then
|
||||
attached {WSF_STRING} a_response.request.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_roc_auth_session_token
|
||||
then
|
||||
across
|
||||
a_menu_system.primary_menu.items as ic
|
||||
until
|
||||
lnk2 /= Void
|
||||
loop
|
||||
if ic.item.title.has_substring ("(Logout)") then
|
||||
lnk2 := ic.item
|
||||
end
|
||||
end
|
||||
if lnk2 /= Void then
|
||||
a_menu_system.primary_menu.remove (lnk2)
|
||||
end
|
||||
create lnk.make (u.name + " (Logout)", "account/roc-oauth-logout" )
|
||||
a_menu_system.primary_menu.extend (lnk)
|
||||
end
|
||||
if a_response.location.starts_with ("account/roc-login") then
|
||||
create lnk.make ("OAuth", "account/roc-oauth-login")
|
||||
a_response.add_to_primary_tabs (lnk)
|
||||
end
|
||||
end
|
||||
|
||||
block_list: ITERABLE [like {CMS_BLOCK}.name]
|
||||
local
|
||||
l_string: STRING
|
||||
do
|
||||
Result := <<"login">>
|
||||
debug ("roc")
|
||||
create l_string.make_empty
|
||||
across
|
||||
Result as ic
|
||||
loop
|
||||
l_string.append (ic.item)
|
||||
l_string.append_character (' ')
|
||||
end
|
||||
write_debug_log (generator + ".block_list:" + l_string )
|
||||
end
|
||||
end
|
||||
|
||||
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
||||
do
|
||||
if
|
||||
a_block_id.is_case_insensitive_equal_general ("login") and then
|
||||
a_response.location.starts_with ("account/roc-oauth-login")
|
||||
then
|
||||
get_block_view_login (a_block_id, a_response)
|
||||
end
|
||||
end
|
||||
|
||||
handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_value ("Login", "optional_content_type")
|
||||
r.execute
|
||||
end
|
||||
|
||||
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_cookie: WSF_COOKIE
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_cookie_token and then
|
||||
attached {CMS_USER} current_user (req) as l_user
|
||||
then
|
||||
-- Logout OAuth
|
||||
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_cookie_token.value)
|
||||
l_cookie.set_path ("/")
|
||||
l_cookie.set_max_age (-1)
|
||||
res.add_cookie (l_cookie)
|
||||
unset_current_user (req)
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_status_code ({HTTP_CONSTANTS}.found)
|
||||
r.set_redirection (req.absolute_script_url (""))
|
||||
r.execute
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK
|
||||
-- Smarty content block for `a_block_id'
|
||||
local
|
||||
p: detachable PATH
|
||||
do
|
||||
create p.make_from_string ("templates")
|
||||
p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
|
||||
p := a_response.api.module_theme_resource_location (Current, p)
|
||||
if p /= Void then
|
||||
if attached p.entry as e then
|
||||
create Result.make (a_block_id, Void, p.parent, e)
|
||||
else
|
||||
create Result.make (a_block_id, Void, p.parent, p)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Block views
|
||||
|
||||
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
|
||||
local
|
||||
vals: CMS_VALUE_TABLE
|
||||
do
|
||||
if attached template_block (a_block_id, a_response) as l_tpl_block then
|
||||
create vals.make (1)
|
||||
-- add the variable to the block
|
||||
value_table_alter (vals, a_response)
|
||||
across
|
||||
vals as ic
|
||||
loop
|
||||
l_tpl_block.set_value (ic.item, ic.key)
|
||||
end
|
||||
if
|
||||
attached user_oauth_api as l_auth_api and then
|
||||
attached l_auth_api.oauth2_consumers as l_list
|
||||
then
|
||||
l_tpl_block.set_value (l_list, "oauth_consumers")
|
||||
end
|
||||
|
||||
a_response.add_block (l_tpl_block, "content")
|
||||
else
|
||||
debug ("cms")
|
||||
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- OAuth2 Login with Provider
|
||||
|
||||
handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_oauth: CMS_OAUTH_20_WORKFLOW
|
||||
do
|
||||
if
|
||||
attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as p_consumer and then
|
||||
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
|
||||
then
|
||||
create l_oauth.make (req.server_url, l_consumer)
|
||||
if attached l_oauth.authorization_url as l_authorization_url then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_redirection (l_authorization_url)
|
||||
r.execute
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("Bad request")
|
||||
r.execute
|
||||
end
|
||||
else
|
||||
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
|
||||
r.set_main_content ("Bad request")
|
||||
r.execute
|
||||
end
|
||||
end
|
||||
|
||||
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
r: CMS_RESPONSE
|
||||
l_auth: CMS_OAUTH_20_WORKFLOW
|
||||
l_user_api: CMS_USER_API
|
||||
l_user: CMS_USER
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
l_cookie: WSF_COOKIE
|
||||
es: CMS_OAUTH_20_EMAIL_SERVICE
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as l_callback and then
|
||||
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
|
||||
attached {WSF_STRING} req.query_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_code) as l_code
|
||||
then
|
||||
create l_auth.make (req.server_url, l_consumer)
|
||||
l_auth.sign_request (l_code.value)
|
||||
if
|
||||
attached l_auth.access_token as l_access_token and then
|
||||
attached l_auth.user_profile as l_user_profile
|
||||
then
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
-- extract user email
|
||||
-- check if the user exist
|
||||
l_user_api := api.user_api
|
||||
-- 1 if the user exit put it in the context
|
||||
if
|
||||
attached l_auth.user_email as l_email
|
||||
then
|
||||
if attached l_user_api.user_by_email (l_email) as p_user then
|
||||
-- User with email exist
|
||||
if attached a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
|
||||
-- Update oauth entry
|
||||
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
|
||||
else
|
||||
-- create a oauth entry
|
||||
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
|
||||
end
|
||||
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token)
|
||||
l_cookie.set_max_age (l_access_token.expires_in)
|
||||
l_cookie.set_path ("/")
|
||||
res.add_cookie (l_cookie)
|
||||
else
|
||||
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1)
|
||||
l_roles.force (l_user_api.authenticated_user_role)
|
||||
|
||||
-- Create a new user and oauth entry
|
||||
create l_user.make (l_email)
|
||||
l_user.set_email (l_email)
|
||||
l_user.set_password (new_token) -- generate a random password.
|
||||
l_user.set_roles (l_roles)
|
||||
l_user.mark_active
|
||||
l_user_api.new_user (l_user)
|
||||
|
||||
-- Add oauth entry
|
||||
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name )
|
||||
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token)
|
||||
l_cookie.set_max_age (l_access_token.expires_in)
|
||||
l_cookie.set_path ("/")
|
||||
res.add_cookie (l_cookie)
|
||||
set_current_user (req, l_user)
|
||||
|
||||
|
||||
-- Send Email
|
||||
create es.make (create {CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS}.make (api))
|
||||
write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email")
|
||||
es.send_contact_welcome_email (l_email, "")
|
||||
end
|
||||
end
|
||||
r.set_redirection (r.front_page_url)
|
||||
r.execute
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
feature {NONE} -- Token Generation
|
||||
|
||||
new_token: STRING
|
||||
-- Generate a new token activation token
|
||||
local
|
||||
l_token: STRING
|
||||
l_security: SECURITY_PROVIDER
|
||||
l_encode: URL_ENCODER
|
||||
do
|
||||
create l_security
|
||||
l_token := l_security.token
|
||||
create l_encode
|
||||
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
|
||||
-- Loop ensure that we have a security token that does not contain characters that need encoding.
|
||||
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
|
||||
-- but the user will need to use an unencoded token if activation has to be done manually.
|
||||
l_token := l_security.token
|
||||
end
|
||||
Result := l_token
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: date and time
|
||||
|
||||
http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME
|
||||
local
|
||||
d: HTTP_DATE
|
||||
do
|
||||
create d.make_from_string (s)
|
||||
if not d.has_error then
|
||||
Result := d.date_time
|
||||
end
|
||||
end
|
||||
|
||||
file_date (p: PATH): DATE_TIME
|
||||
require
|
||||
path_exists: (create {FILE_UTILITIES}).file_path_exists (p)
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make_with_path (p)
|
||||
Result := timestamp_to_date (f.date)
|
||||
end
|
||||
|
||||
timestamp_to_date (n: INTEGER): DATE_TIME
|
||||
local
|
||||
d: HTTP_DATE
|
||||
do
|
||||
create d.make_from_timestamp (n)
|
||||
Result := d.date_time
|
||||
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
|
||||
]"
|
||||
end
|
||||
@@ -1,5 +1,7 @@
|
||||
note
|
||||
description: "Summary description for {CMS_OAUTH_20_FILTER}."
|
||||
description: "[
|
||||
Extracts an OAuth2 token from the incoming request (cookie) and uses it to populate the user (or cms user context)
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -32,27 +34,21 @@ feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
local
|
||||
do
|
||||
api.logger.put_debug (generator + ".execute ", Void)
|
||||
-- if attached req.raw_header_data as l_raw_data then
|
||||
-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
|
||||
-- end
|
||||
-- A valid user
|
||||
if
|
||||
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_roc_auth_session_token
|
||||
attached {WSF_STRING} req.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_roc_auth_session_token
|
||||
then
|
||||
if attached {CMS_USER} user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
|
||||
if attached user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
|
||||
set_current_user (req, l_user)
|
||||
execute_next (req, res)
|
||||
else
|
||||
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
|
||||
execute_next (req, res)
|
||||
end
|
||||
else
|
||||
api.logger.put_debug (generator + ".execute without authentication", Void)
|
||||
execute_next (req, res)
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
end
|
||||
29
modules/oauth20/oauth20-safe.ecf
Normal file
29
modules/oauth20/oauth20-safe.ecf
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-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="oauth_module" uuid="D64B990F-B51F-4E0D-AB2E-4AA5DDB783CE" library_target="oauth_module">
|
||||
<target name="oauth_module">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="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_auth_module" location="..\auth\auth-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="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -20,18 +20,18 @@ feature -- Error Handling
|
||||
|
||||
feature -- Access: Users
|
||||
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
|
||||
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
|
||||
deferred
|
||||
end
|
||||
|
||||
@@ -53,12 +53,12 @@ feature -- Access: Consumers
|
||||
|
||||
feature -- Change: User Oauth2
|
||||
|
||||
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
|
||||
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
|
||||
-- Add a new user with oauth2 authentication.
|
||||
deferred
|
||||
end
|
||||
|
||||
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
|
||||
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL )
|
||||
-- Update user `a_user' with oauth2 authentication.
|
||||
deferred
|
||||
end
|
||||
@@ -22,17 +22,17 @@ feature -- Error handler
|
||||
|
||||
feature -- Access: Users
|
||||
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- CMS User with Oauth credential by id if any.
|
||||
do
|
||||
end
|
||||
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- -- CMS User with Oauth credential by access token `a_token' if any.
|
||||
do
|
||||
end
|
||||
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL ): detachable CMS_USER
|
||||
do
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ feature -- Access: Consumers
|
||||
|
||||
oauth2_consumers: LIST [STRING]
|
||||
do
|
||||
create {ARRAYED_LIST[STRING]} Result.make (0)
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
end
|
||||
|
||||
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
|
||||
@@ -55,12 +55,12 @@ feature -- Access: Consumers
|
||||
|
||||
feature -- Change: User Oauth2
|
||||
|
||||
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
|
||||
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
|
||||
-- Add a new user with oauth2 authentication.
|
||||
do
|
||||
end
|
||||
|
||||
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
|
||||
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL )
|
||||
-- Update user `a_user' with oauth2 authentication.
|
||||
do
|
||||
end
|
||||
@@ -22,10 +22,10 @@ create
|
||||
|
||||
feature -- Access User Outh
|
||||
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
|
||||
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
|
||||
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
|
||||
local
|
||||
l_list: LIST[STRING]
|
||||
l_list: LIST [STRING]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user_oauth2_without_consumer_by_token")
|
||||
@@ -33,16 +33,14 @@ feature -- Access User Outh
|
||||
from
|
||||
l_list.start
|
||||
until
|
||||
l_list.after or attached Result
|
||||
l_list.after or Result /= Void
|
||||
loop
|
||||
if attached {CMS_USER} user_oauth2_by_token (a_token, l_list.item) as l_user then
|
||||
Result := l_user
|
||||
end
|
||||
Result := user_oauth2_by_token (a_token, l_list.item)
|
||||
l_list.forth
|
||||
end
|
||||
end
|
||||
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
@@ -53,7 +51,7 @@ feature -- Access User Outh
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_uid, "uid")
|
||||
create l_string.make_from_string (select_user_oauth2_template_by_id)
|
||||
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
|
||||
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
|
||||
sql_query (l_string, l_parameters)
|
||||
if sql_rows_count = 1 then
|
||||
Result := fetch_user
|
||||
@@ -62,7 +60,7 @@ feature -- Access User Outh
|
||||
end
|
||||
end
|
||||
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
|
||||
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
@@ -73,7 +71,7 @@ feature -- Access User Outh
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
create l_string.make_from_string (select_user_by_oauth2_template_token)
|
||||
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
|
||||
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
|
||||
sql_query (l_string, l_parameters)
|
||||
if sql_rows_count = 1 then
|
||||
Result := fetch_user
|
||||
@@ -85,11 +83,11 @@ feature -- Access User Outh
|
||||
|
||||
feature --Access: Consumers
|
||||
|
||||
oauth2_consumers: LIST[STRING]
|
||||
oauth2_consumers: LIST [STRING]
|
||||
-- Return a list of consumers, or empty
|
||||
do
|
||||
error_handler.reset
|
||||
create {ARRAYED_LIST[STRING]}Result.make (0)
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
write_information_log (generator + ".user_by_oauth2_token")
|
||||
sql_query (Sql_oauth_consumers, Void)
|
||||
if not has_error then
|
||||
@@ -142,7 +140,7 @@ feature --Access: Consumers
|
||||
|
||||
feature -- Change: User OAuth
|
||||
|
||||
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
|
||||
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL)
|
||||
-- Add a new user with oauth2 authentication.
|
||||
-- <Precursor>.
|
||||
local
|
||||
@@ -160,12 +158,12 @@ feature -- Change: User OAuth
|
||||
l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date")
|
||||
|
||||
create l_string.make_from_string (sql_insert_oauth2_template)
|
||||
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
|
||||
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
|
||||
sql_change (l_string, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
|
||||
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32 )
|
||||
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL )
|
||||
-- Update user `a_user' with oauth2 authentication.
|
||||
-- <Precursor>
|
||||
local
|
||||
@@ -182,7 +180,7 @@ feature -- Change: User OAuth
|
||||
l_parameters.put (a_user_profile, "profile")
|
||||
|
||||
create l_string.make_from_string (sql_update_oauth2_template)
|
||||
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
|
||||
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
|
||||
sql_change (l_string, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
@@ -192,39 +190,38 @@ feature {NONE} -- Implementation OAuth Consumer
|
||||
fetch_consumer: detachable CMS_OAUTH_20_CONSUMER
|
||||
do
|
||||
if attached sql_read_integer_64 (1) as l_id then
|
||||
create Result
|
||||
Result.set_id (l_id)
|
||||
end
|
||||
if Result /= Void then
|
||||
if attached sql_read_string_32 (2) as l_name then
|
||||
create Result.make_with_id (l_id)
|
||||
|
||||
if attached sql_read_string (2) as l_name then
|
||||
Result.set_name (l_name)
|
||||
end
|
||||
if attached sql_read_string_32 (3) as l_api_secret then
|
||||
if attached sql_read_string (3) as l_api_secret then
|
||||
Result.set_api_secret (l_api_secret)
|
||||
end
|
||||
if attached sql_read_string_32 (4) as l_api_key then
|
||||
if attached sql_read_string (4) as l_api_key then
|
||||
Result.set_api_key (l_api_key)
|
||||
end
|
||||
if attached sql_read_string_32 (5) as l_scope then
|
||||
if attached sql_read_string (5) as l_scope then
|
||||
Result.set_scope (l_scope)
|
||||
end
|
||||
if attached sql_read_string_32 (6) as l_resource_url then
|
||||
if attached sql_read_string (6) as l_resource_url then
|
||||
Result.set_protected_resource_url (l_resource_url)
|
||||
end
|
||||
if attached sql_read_string_32 (7) as l_callback_name then
|
||||
if attached sql_read_string (7) as l_callback_name then
|
||||
Result.set_callback_name (l_callback_name)
|
||||
end
|
||||
if attached sql_read_string_32 (8) as l_extractor then
|
||||
if attached sql_read_string (8) as l_extractor then
|
||||
Result.set_extractor (l_extractor)
|
||||
end
|
||||
if attached sql_read_string_32 (9) as l_authorize_url then
|
||||
if attached sql_read_string (9) as l_authorize_url then
|
||||
Result.set_authorize_url (l_authorize_url)
|
||||
end
|
||||
if attached sql_read_string_32 (10) as l_endpoint then
|
||||
if attached sql_read_string (10) as l_endpoint then
|
||||
Result.set_endpoint (l_endpoint)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: User
|
||||
|
||||
fetch_user: detachable CMS_USER
|
||||
@@ -232,7 +229,7 @@ feature {NONE} -- Implementation: User
|
||||
l_id: INTEGER_64
|
||||
l_name: detachable READABLE_STRING_32
|
||||
do
|
||||
if attached sql_read_integer_32 (1) as i then
|
||||
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
|
||||
@@ -264,15 +261,36 @@ feature {NONE} -- Implementation: User
|
||||
end
|
||||
end
|
||||
|
||||
feature -- {NONE} User OAuth2
|
||||
feature {NONE} -- User OAuth2
|
||||
|
||||
sql_table_name (a_consumer: READABLE_STRING_8): STRING_8
|
||||
oauth2_sql_table_name (a_consumer: READABLE_STRING_GENERAL): STRING_8
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
Result := Sql_table_prefix.twin
|
||||
Result.append (a_consumer)
|
||||
create Result.make_from_string (Sql_oauth2_table_prefix)
|
||||
if a_consumer.is_valid_as_string_8 then
|
||||
Result.append (a_consumer.to_string_8)
|
||||
else
|
||||
check only_ascii: False end
|
||||
-- Replace non ascii char by '-'
|
||||
from
|
||||
i := 1
|
||||
n := a_consumer.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if a_consumer [i].is_character_8 then
|
||||
Result.append_code (a_consumer.code (i))
|
||||
else
|
||||
Result.append_character ('-')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.access_token = :token;"
|
||||
--| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped.
|
||||
|
||||
Select_user_oauth2_template_by_id: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.uid = :uid;"
|
||||
|
||||
@@ -282,12 +300,12 @@ feature -- {NONE} User OAuth2
|
||||
|
||||
Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers";
|
||||
|
||||
Sql_table_prefix: STRING = "oauth2_"
|
||||
Sql_oauth2_table_prefix: STRING = "oauth2_"
|
||||
|
||||
feature -- {NONE} Consumer
|
||||
feature {NONE} -- Consumer
|
||||
|
||||
Sql_oauth_consumer_callback: STRING ="SELECT * FROM oauth2_consumers where callback_name =:name;"
|
||||
Sql_oauth_consumer_callback: STRING = "SELECT * FROM oauth2_consumers where callback_name =:name;"
|
||||
|
||||
Sql_oauth_consumer_name: STRING ="SELECT * FROM oauth2_consumers where name =:name;"
|
||||
Sql_oauth_consumer_name: STRING = "SELECT * FROM oauth2_consumers where name =:name;"
|
||||
|
||||
end
|
||||
18
modules/oauth20/site/scripts/oauth2_consumers.sql
Normal file
18
modules/oauth20/site/scripts/oauth2_consumers.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
CREATE TABLE oauth2_consumers(
|
||||
`cid` INTEGER PRIMARY KEY NOT NULL CHECK(`cid`>=0),
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`api_secret` TEXT NOT NULL,
|
||||
`api_key` TEXT NOT NULL,
|
||||
`scope` VARCHAR (100) NOT NULL,
|
||||
`protected_resource_url` VARCHAR (255) NOT NULL,
|
||||
`callback_name` VARCHAR(255) NOT NULL,
|
||||
`extractor` VARCHAR(50) NOT NULL,
|
||||
`authorize_url` VARCHAR (255) NOT NULL,
|
||||
`endpoint` VARCHAR (255) NOT NULL,
|
||||
CONSTRAINT `cid`
|
||||
UNIQUE(`cid`),
|
||||
CONSTRAINT `name`
|
||||
UNIQUE(`name`)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Change the values TO_COMPLETE based on your API.
|
||||
-- API SECTET KEY AND API PUBLIC KEY
|
||||
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint)
|
||||
VALUES ('google', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', 'callback_google', 'json','https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://accounts.google.com/o/oauth2/token');
|
||||
|
||||
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint )
|
||||
VALUES ('facebook', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', 'callback_facebook','text','https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://graph.facebook.com/oauth/access_token');
|
||||
10
modules/oauth20/site/scripts/oauth2_table.sql.tpl
Normal file
10
modules/oauth20/site/scripts/oauth2_table.sql.tpl
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
CREATE TABLE $table_name (
|
||||
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
|
||||
`access_token` TEXT NOT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
`details` TEXT NOT NULL,
|
||||
CONSTRAINT `uid`
|
||||
UNIQUE(`uid`)
|
||||
);
|
||||
|
||||
7
modules/oauth20/site/templates/block_login.tpl
Normal file
7
modules/oauth20/site/templates/block_login.tpl
Normal file
@@ -0,0 +1,7 @@
|
||||
<div class="primary-tabs">
|
||||
<div>
|
||||
{foreach item="item" from="$oauth_consumers"}
|
||||
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,6 +42,8 @@ feature {NONE} -- Initialization
|
||||
local
|
||||
l_url: like site_url
|
||||
do
|
||||
site_location := environment.path
|
||||
|
||||
--| Site id, used to identified a site, this could be set to a uuid, or else
|
||||
site_id := text_item_or_default ("site.id", "_EWF_CMS_NO_ID_")
|
||||
|
||||
@@ -61,11 +63,18 @@ feature {NONE} -- Initialization
|
||||
-- Can be also used to precise the "From:" value for email.
|
||||
site_email := text_item_or_default ("site.email", "webmaster")
|
||||
|
||||
-- Location for theme folders.
|
||||
-- Location for modules folders.
|
||||
if attached text_item ("modules-dir") as s then
|
||||
create modules_location.make_from_string (s)
|
||||
else
|
||||
modules_location := environment.modules_path
|
||||
end
|
||||
|
||||
-- Location for themes folders.
|
||||
if attached text_item ("themes-dir") as s then
|
||||
create themes_location.make_from_string (s)
|
||||
else
|
||||
themes_location := environment.www_path.extended ("themes")
|
||||
themes_location := environment.themes_path
|
||||
end
|
||||
|
||||
-- Selected theme's name
|
||||
@@ -76,7 +85,6 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
|
||||
compute_theme_location
|
||||
compute_theme_assets_location
|
||||
end
|
||||
|
||||
initialize_storages
|
||||
@@ -139,10 +147,45 @@ feature -- Access
|
||||
end
|
||||
|
||||
build_mailer
|
||||
local
|
||||
retried: BOOLEAN
|
||||
f: FILE
|
||||
do
|
||||
to_implement ("Not implemented mailer")
|
||||
if not retried then
|
||||
if attached text_item ("mailer.smtp") as l_smtp then
|
||||
create {NOTIFICATION_SMTP_MAILER} mailer.make (l_smtp)
|
||||
elseif attached text_item ("mailer.sendmail") as l_sendmail then
|
||||
create {NOTIFICATION_SENDMAIL_MAILER} mailer.make_with_location (l_sendmail)
|
||||
elseif attached text_item ("mailer.output") as l_output then
|
||||
if l_output.is_case_insensitive_equal ("@stderr") then
|
||||
f := io.error
|
||||
elseif l_output.is_case_insensitive_equal ("@stdout") then
|
||||
f := io.output
|
||||
else
|
||||
create {RAW_FILE} f.make_with_name (l_output)
|
||||
if not f.exists then
|
||||
f.create_read_write
|
||||
f.close
|
||||
end
|
||||
end
|
||||
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (f))
|
||||
else
|
||||
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (io.error))
|
||||
end
|
||||
else
|
||||
check valid_mailer: False end
|
||||
-- FIXME: should we report persistent error message? If yes, see how.
|
||||
create {NOTIFICATION_NULL_MAILER} mailer
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
mailer: NOTIFICATION_MAILER
|
||||
|
||||
feature -- Access: storage
|
||||
|
||||
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
|
||||
@@ -163,15 +206,7 @@ feature -- Theme: Compute location
|
||||
theme_location := themes_location.extended (theme_name)
|
||||
end
|
||||
|
||||
compute_theme_assets_location
|
||||
-- assets (js, css, images, etc)
|
||||
-- Not used at the moment.
|
||||
do
|
||||
debug ("refactor_fixme")
|
||||
fixme ("Check if we really need it")
|
||||
end
|
||||
-- Check how to get this path from the CMS_THEME information.
|
||||
theme_assets_location := theme_location.extended ("assets")
|
||||
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
|
||||
|
||||
@@ -23,15 +23,35 @@ create
|
||||
|
||||
feature -- Access
|
||||
|
||||
theme_path: PATH
|
||||
site_path: PATH
|
||||
-- Directory containing the site.
|
||||
--| For now, an alias for `path'.
|
||||
do
|
||||
Result := path
|
||||
end
|
||||
|
||||
modules_path: PATH
|
||||
-- Directory for templates (HTML, etc).
|
||||
local
|
||||
p: detachable PATH
|
||||
do
|
||||
p := internal_theme_path
|
||||
p := internal_modules_path
|
||||
if p = Void then
|
||||
p := www_path.extended ("theme")
|
||||
internal_theme_path := p
|
||||
p := site_path.extended ("modules")
|
||||
internal_modules_path := p
|
||||
end
|
||||
Result := p
|
||||
end
|
||||
|
||||
themes_path: PATH
|
||||
-- Directory for cms themes.
|
||||
local
|
||||
p: detachable PATH
|
||||
do
|
||||
p := internal_themes_path
|
||||
if p = Void then
|
||||
p := site_path.extended ("themes")
|
||||
internal_themes_path := p
|
||||
end
|
||||
Result := p
|
||||
end
|
||||
@@ -58,7 +78,9 @@ feature -- Access
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
internal_theme_path: detachable like theme_path
|
||||
internal_modules_path: detachable like modules_path
|
||||
|
||||
internal_themes_path: detachable like themes_path
|
||||
|
||||
internal_cms_config_ini_path: detachable like cms_config_ini_path
|
||||
|
||||
|
||||
@@ -90,7 +90,13 @@ feature -- Query
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: Theme
|
||||
feature -- Access: Theme
|
||||
|
||||
site_location: PATH
|
||||
-- Path to CMS site root dir.
|
||||
|
||||
modules_location: PATH
|
||||
-- Path to modules.
|
||||
|
||||
themes_location: PATH
|
||||
-- Path to themes.
|
||||
@@ -98,9 +104,6 @@ feature -- Access: Theme
|
||||
theme_location: PATH
|
||||
-- Path to a active theme.
|
||||
|
||||
theme_assets_location: PATH
|
||||
-- Path to a active theme assets folder.
|
||||
|
||||
theme_information_location: PATH
|
||||
-- Active theme informations.
|
||||
do
|
||||
@@ -110,6 +113,13 @@ feature -- Access: Theme
|
||||
theme_name: READABLE_STRING_32
|
||||
-- theme name.
|
||||
|
||||
feature -- Access
|
||||
|
||||
mailer: NOTIFICATION_MAILER
|
||||
-- Email processor.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: storage
|
||||
|
||||
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
|
||||
@@ -186,4 +196,7 @@ feature -- Element change
|
||||
module_registered: module_registered (m)
|
||||
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
|
||||
|
||||
@@ -28,12 +28,15 @@ feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
name := "debug"
|
||||
version := "1.0"
|
||||
description := "Debug"
|
||||
package := "cms"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "debug"
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
@@ -134,7 +137,7 @@ feature -- Handler
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -22,8 +22,8 @@ feature -- Initialization
|
||||
l_roles: LIST [CMS_USER_ROLE]
|
||||
do
|
||||
--| Schema
|
||||
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql"))
|
||||
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("user.sql"))
|
||||
a_storage.sql_execute_file_script (a_setup.environment.site_path.extended ("scripts").extended ("core.sql"), Void)
|
||||
a_storage.sql_execute_file_script (a_setup.environment.site_path.extended ("scripts").extended ("user.sql"), Void)
|
||||
|
||||
--| Roles
|
||||
create l_anonymous_role.make ("anonymous")
|
||||
|
||||
@@ -76,7 +76,13 @@ feature -- Operation
|
||||
i := a_sql_statement.index_of (':', i)
|
||||
if i = 0 then
|
||||
i := n -- exit
|
||||
elseif a_sql_statement.at (i-1).is_equal ('%'') or else a_sql_statement.at (i-1).is_equal ('%"') or else a_sql_statement.at (i-1).is_equal (' ') or else a_sql_statement.at (i-1).is_equal ('=') then
|
||||
elseif
|
||||
a_sql_statement [i-1] = '%''
|
||||
or else a_sql_statement [i-1] = '%"'
|
||||
or else a_sql_statement [i-1] = ' '
|
||||
or else a_sql_statement [i-1] = '='
|
||||
or else a_sql_statement [i-1] = '('
|
||||
then
|
||||
from
|
||||
j := i + 1
|
||||
until
|
||||
@@ -124,15 +130,14 @@ feature -- Operation
|
||||
|
||||
feature -- Helper
|
||||
|
||||
sql_execute_file_script_with_params (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- Execute SQL script from `a_path' and with params `a_params'.
|
||||
sql_script_content (a_path: PATH): detachable STRING
|
||||
-- Content of sql script located at `a_path'.
|
||||
local
|
||||
f: PLAIN_TEXT_FILE
|
||||
sql: STRING
|
||||
do
|
||||
create f.make_with_path (a_path)
|
||||
if f.exists and then f.is_access_readable then
|
||||
create sql.make (f.count)
|
||||
create Result.make (f.count)
|
||||
f.open_read
|
||||
from
|
||||
f.start
|
||||
@@ -140,43 +145,27 @@ feature -- Helper
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_stream_thread_aware (1_024)
|
||||
sql.append (f.last_string)
|
||||
Result.append (f.last_string)
|
||||
end
|
||||
f.close
|
||||
sql_execute_script_with_params (sql, a_params)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sql_execute_file_script (a_path: PATH)
|
||||
-- Execute SQL script from `a_path'.
|
||||
local
|
||||
f: PLAIN_TEXT_FILE
|
||||
sql: STRING
|
||||
sql_execute_file_script (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- Execute SQL script from `a_path' and with optional parameters `a_params'.
|
||||
do
|
||||
create f.make_with_path (a_path)
|
||||
if f.exists and then f.is_access_readable then
|
||||
create sql.make (f.count)
|
||||
f.open_read
|
||||
from
|
||||
f.start
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_stream_thread_aware (1_024)
|
||||
sql.append (f.last_string)
|
||||
end
|
||||
f.close
|
||||
sql_execute_script (sql)
|
||||
if attached sql_script_content (a_path) as sql then
|
||||
sql_execute_script (sql, a_params)
|
||||
end
|
||||
end
|
||||
|
||||
sql_execute_script (a_sql_script: STRING)
|
||||
sql_execute_script (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- Execute SQL script.
|
||||
-- i.e: multiple SQL statements.
|
||||
local
|
||||
i: INTEGER
|
||||
err: BOOLEAN
|
||||
cl: CELL [INTEGER]
|
||||
do
|
||||
reset_error
|
||||
sql_begin_transaction
|
||||
@@ -184,16 +173,17 @@ feature -- Helper
|
||||
-- sql_change (a_sql_script, Void)
|
||||
from
|
||||
i := 1
|
||||
create cl.put (0)
|
||||
until
|
||||
i > a_sql_script.count or err
|
||||
loop
|
||||
if attached next_sql_statement (a_sql_script, i) as s then
|
||||
if attached next_sql_statement (a_sql_script, i, cl) as s then
|
||||
if not s.is_whitespace then
|
||||
sql_change (sql_statement (s), Void)
|
||||
sql_change (sql_statement (s), a_params)
|
||||
err := err or has_error
|
||||
reset_error
|
||||
end
|
||||
i := i + s.count
|
||||
i := i + cl.item
|
||||
else
|
||||
i := a_sql_script.count + 1
|
||||
end
|
||||
@@ -205,14 +195,6 @@ feature -- Helper
|
||||
end
|
||||
end
|
||||
|
||||
sql_execute_script_with_params (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- Execute SQL script.
|
||||
-- i.e: multiple SQL statements.
|
||||
do
|
||||
reset_error
|
||||
sql_change (a_sql_script, a_params)
|
||||
end
|
||||
|
||||
sql_table_exists (a_table_name: READABLE_STRING_8): BOOLEAN
|
||||
-- Does table `a_table_name' exists?
|
||||
do
|
||||
@@ -382,11 +364,12 @@ feature -- Conversion
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
next_sql_statement (a_script: STRING; a_start_index: INTEGER): detachable STRING
|
||||
next_sql_statement (a_script: STRING; a_start_index: INTEGER; a_offset: CELL [INTEGER]): detachable STRING
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
c: CHARACTER
|
||||
l_end: INTEGER
|
||||
l_removals: detachable ARRAYED_LIST [TUPLE [start_index,end_index: INTEGER]]
|
||||
do
|
||||
from
|
||||
i := a_start_index
|
||||
@@ -400,21 +383,32 @@ feature {NONE} -- Implementation
|
||||
if i < n and then a_script[i + 1] = '-' then
|
||||
-- Commented line "--" until New Line
|
||||
j := a_script.index_of ('%N', i)
|
||||
if j > 0 then
|
||||
i := j
|
||||
if j = 0 then
|
||||
j := n
|
||||
else
|
||||
i := n
|
||||
-- j := j
|
||||
end
|
||||
if l_removals = Void then
|
||||
create l_removals.make (1)
|
||||
end
|
||||
l_removals.force ([i,j])
|
||||
i := j
|
||||
end
|
||||
when '/' then
|
||||
if i < n and then a_script[i + 1] = '*' then
|
||||
-- Commented text "/*" until closing "*/"
|
||||
j := a_script.substring_index ("*/", i)
|
||||
if j > 0 then
|
||||
i := j
|
||||
|
||||
if j = 0 then
|
||||
j := n
|
||||
else
|
||||
i := n
|
||||
j := j + 1 -- Include '/'
|
||||
end
|
||||
if l_removals = Void then
|
||||
create l_removals.make (1)
|
||||
end
|
||||
l_removals.force ([i,j])
|
||||
i := j
|
||||
end
|
||||
when '`', '"', '%'' then
|
||||
from
|
||||
@@ -428,6 +422,8 @@ feature {NONE} -- Implementation
|
||||
if a_script [j - 1] /= '\' then
|
||||
l_end := j
|
||||
end
|
||||
else
|
||||
l_end := i
|
||||
end
|
||||
end
|
||||
if l_end > 0 then
|
||||
@@ -440,10 +436,23 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
i := a_script.index_of (';', a_start_index)
|
||||
if i > a_start_index then
|
||||
if i <= n and i > a_start_index then
|
||||
Result := a_script.substring (a_start_index, i)
|
||||
a_offset.replace (Result.count)
|
||||
if l_removals /= Void then
|
||||
j := 0
|
||||
across
|
||||
l_removals as ic
|
||||
loop
|
||||
Result.remove_substring (ic.item.start_index - j, ic.item.end_index - j)
|
||||
j := j + ic.item.end_index - ic.item.start_index + 1
|
||||
end
|
||||
-- a_offset.replace (a_offset.item j)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -137,6 +137,28 @@ feature -- Logging
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Emails
|
||||
|
||||
process_email (e: CMS_EMAIL)
|
||||
-- Process email `e'.
|
||||
do
|
||||
reset_error
|
||||
setup.mailer.safe_process_email (e)
|
||||
if setup.mailer.has_error then
|
||||
error_handler.add_custom_error (0, "Mailer error", "Error occurred while processing email.")
|
||||
end
|
||||
end
|
||||
|
||||
process_emails (lst: ITERABLE [CMS_EMAIL])
|
||||
-- Process collection of email `lst'.
|
||||
do
|
||||
reset_error
|
||||
setup.mailer.process_emails (lst)
|
||||
if setup.mailer.has_error then
|
||||
error_handler.add_custom_error (0, "Mailer error", "Error occurred while processing emails.")
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Permissions system
|
||||
|
||||
user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
|
||||
@@ -152,11 +174,8 @@ feature -- Query: module
|
||||
-- Enabled module typed `a_type', if any.
|
||||
--| usage: if attached module ({FOO_MODULE}) as mod then ...
|
||||
local
|
||||
-- t: STRING_8
|
||||
l_type: TYPE [detachable CMS_MODULE]
|
||||
do
|
||||
-- t := type_name_without_annotation (a_type)
|
||||
|
||||
across
|
||||
setup.modules as ic
|
||||
until
|
||||
@@ -173,8 +192,6 @@ feature -- Query: module
|
||||
attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type)
|
||||
then
|
||||
-- Found
|
||||
-- elseif t.same_string (type_name_without_annotation (l_type)) then
|
||||
-- -- Found
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
@@ -322,55 +339,48 @@ feature {NONE}-- Implemenation
|
||||
internal_user_api: detachable like user_api
|
||||
-- Cached value for `user_api'.
|
||||
|
||||
type_name_without_annotation (a_type: TYPE [detachable ANY]): STRING
|
||||
-- Type name for `a_type, without any annotation.
|
||||
-- Used by `module' to search by type.
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
c: CHARACTER
|
||||
feature -- Environment/ theme
|
||||
|
||||
site_location: PATH
|
||||
-- CMS site location.
|
||||
do
|
||||
create Result.make_from_string (a_type.name)
|
||||
from
|
||||
i := 1
|
||||
n := Result.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
c := Result[i]
|
||||
if c = '!' or c = '?' then
|
||||
Result.remove (i)
|
||||
n := n - 1
|
||||
elseif c.is_lower then
|
||||
j := Result.index_of (' ', i + 1)
|
||||
if j > 0 then
|
||||
Result.remove_substring (i, j)
|
||||
n := n - (j - i)
|
||||
end
|
||||
else
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
if Result.starts_with ("!") or Result.starts_with ("?") then
|
||||
Result.remove_head (1)
|
||||
elseif Result.starts_with ("detachable ") then
|
||||
Result.remove_head (11)
|
||||
end
|
||||
Result := setup.site_location
|
||||
end
|
||||
|
||||
feature -- Environment
|
||||
theme_location: PATH
|
||||
-- Active theme location.
|
||||
do
|
||||
Result := setup.theme_location
|
||||
end
|
||||
|
||||
module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
theme_assets_location: PATH
|
||||
-- assets (js, css, images, etc).
|
||||
do
|
||||
debug ("refactor_fixme")
|
||||
fixme ("Check if we really need it")
|
||||
end
|
||||
-- Check how to get this path from the CMS_THEME information.
|
||||
Result := theme_location.extended ("assets")
|
||||
end
|
||||
|
||||
feature -- Environment/ module
|
||||
|
||||
module_configuration_by_name (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration reader for `a_module', and if `a_name' is set, using name `a_name'.
|
||||
local
|
||||
p, l_path: PATH
|
||||
p, l_path: detachable PATH
|
||||
l_name: READABLE_STRING_GENERAL
|
||||
ut: FILE_UTILITIES
|
||||
do
|
||||
p := setup.environment.config_path.extended ("modules").extended (a_module_name)
|
||||
if a_name = Void then
|
||||
p := p.extended (a_module_name)
|
||||
l_name := a_module_name
|
||||
else
|
||||
p := p.extended (a_name)
|
||||
l_name := a_name
|
||||
end
|
||||
p := setup.environment.config_path
|
||||
|
||||
p := module_location_by_name (a_module_name).extended ("config").extended (l_name)
|
||||
|
||||
l_path := p.appended_with_extension ("json")
|
||||
if ut.file_path_exists (l_path) then
|
||||
create {JSON_CONFIG} Result.make_from_file (l_path)
|
||||
@@ -382,13 +392,109 @@ feature -- Environment
|
||||
end
|
||||
if Result = Void and a_name /= Void then
|
||||
-- Use sub config from default?
|
||||
if attached {CONFIG_READER} module_configuration (a_module_name, Void) as cfg then
|
||||
if attached {CONFIG_READER} module_configuration_by_name (a_module_name, Void) as cfg then
|
||||
Result := cfg.sub_config (a_name)
|
||||
else
|
||||
-- Maybe try to use the global cms.ini ?
|
||||
-- Maybe try to use the global cms.ini ?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
modules_location: PATH
|
||||
-- Directory containing cms modules.
|
||||
do
|
||||
Result := setup.modules_location
|
||||
end
|
||||
|
||||
module_location (a_module: CMS_MODULE): PATH
|
||||
-- Location associated with `a_module'.
|
||||
do
|
||||
Result := module_location_by_name (a_module.name)
|
||||
end
|
||||
|
||||
module_location_by_name (a_module_name: READABLE_STRING_GENERAL): PATH
|
||||
-- Location associated with `a_module_name'.
|
||||
do
|
||||
Result := modules_location.extended (a_module_name)
|
||||
end
|
||||
|
||||
module_resource_location (a_module: CMS_MODULE; a_resource: PATH): PATH
|
||||
-- Location of resource `a_resource' for `a_module'.
|
||||
do
|
||||
--| site/modules/$modname/$a_name.json
|
||||
Result := module_resource_location_by_name (a_module.name, a_resource)
|
||||
end
|
||||
|
||||
module_resource_location_by_name (a_module_name: READABLE_STRING_GENERAL; a_resource: PATH): PATH
|
||||
-- Location of resource `a_resource' for `a_module'.
|
||||
do
|
||||
--| site/modules/$modname/$a_name.json
|
||||
Result := module_location_by_name (a_module_name).extended_path (a_resource)
|
||||
end
|
||||
|
||||
feature -- Environment/ modules and theme
|
||||
|
||||
module_theme_resource_location (a_module: CMS_MODULE; a_resource: PATH): detachable PATH
|
||||
-- Theme resource location of `a_resource' for module `a_module', if exists.
|
||||
-- By default, located under the module location folder, but could be overriden
|
||||
-- from files located under modules subfolder of active `theme_location'.
|
||||
--| First search in themes/$theme/modules/$a_module.name/$a_resource,
|
||||
--| and if not found then search in
|
||||
--| modules/$a_module_name/$a_resource.
|
||||
local
|
||||
ut: FILE_UTILITIES
|
||||
do
|
||||
-- Check first in selected theme folder.
|
||||
Result := module_theme_location (a_module).extended_path (a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
-- And if not found, look into site/modules/$a_module.name/.... folders.
|
||||
Result := module_resource_location (a_module, a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module_theme_resource_location_by_name (a_module_name: READABLE_STRING_GENERAL; a_resource: PATH): detachable PATH
|
||||
-- Theme resource location of `a_resource' for module named `a_module_name', if exists.
|
||||
-- By default, located under the module location folder, but could be overriden
|
||||
-- from files located under modules subfolder of active `theme_location'.
|
||||
--| First search in themes/$theme/modules/$a_module.name/$a_resource,
|
||||
--| and if not found then search in
|
||||
--| modules/$a_module_name/$a_resource.
|
||||
local
|
||||
ut: FILE_UTILITIES
|
||||
do
|
||||
-- Check first in selected theme folder.
|
||||
Result := module_theme_location_by_name (a_module_name).extended_path (a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
-- And if not found, look into site/modules/$a_module.name/.... folders.
|
||||
Result := module_resource_location_by_name (a_module_name, a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module_theme_location (a_module: CMS_MODULE): PATH
|
||||
-- Location for overriden files associated with `a_module_name'.
|
||||
do
|
||||
Result := module_theme_location_by_name (a_module.name)
|
||||
end
|
||||
|
||||
module_theme_location_by_name (a_module_name: READABLE_STRING_GENERAL): PATH
|
||||
-- Location for overriden files associated with `a_module_name'.
|
||||
do
|
||||
Result := theme_location.extended ("modules").extended (a_module_name)
|
||||
end
|
||||
|
||||
module_configuration (a_module: CMS_MODULE; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
do
|
||||
Result := module_configuration_by_name (a_module.name, a_name)
|
||||
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
|
||||
|
||||
|
||||
18
src/service/cms_email.e
Normal file
18
src/service/cms_email.e
Normal file
@@ -0,0 +1,18 @@
|
||||
note
|
||||
description: "CMS interface representing an email to be used with {CMS_API}.process_email (e: CMS_EMAIL)."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_EMAIL
|
||||
|
||||
inherit
|
||||
NOTIFICATION_EMAIL
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
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
|
||||
@@ -156,7 +156,7 @@ feature -- Settings: router
|
||||
do
|
||||
api.logger.put_information (generator + ".configure_api_file_handler", Void)
|
||||
|
||||
create fhdl.make_hidden_with_path (setup.theme_assets_location)
|
||||
create fhdl.make_hidden_with_path (api.theme_assets_location)
|
||||
fhdl.disable_index
|
||||
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||
do
|
||||
@@ -253,7 +253,7 @@ feature -- Execution
|
||||
r: NOT_FOUND_ERROR_CMS_RESPONSE
|
||||
f: WSF_FILE_RESPONSE
|
||||
do
|
||||
p := api.setup.theme_assets_location.extended ("favicon.ico")
|
||||
p := api.theme_assets_location.extended ("favicon.ico")
|
||||
if ut.file_path_exists (p) then
|
||||
create f.make_with_path (p)
|
||||
res.send (f)
|
||||
|
||||
@@ -16,6 +16,8 @@ feature -- Access
|
||||
|
||||
name: STRING
|
||||
-- Name of the module.
|
||||
deferred
|
||||
end
|
||||
|
||||
description: STRING
|
||||
-- Description of the module.
|
||||
@@ -132,4 +134,7 @@ feature -- Hooks
|
||||
create Result.make_empty
|
||||
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
|
||||
|
||||
@@ -152,39 +152,6 @@ feature -- API
|
||||
Result := api.formats
|
||||
end
|
||||
|
||||
feature -- Module
|
||||
|
||||
module_resource_path (a_module: CMS_MODULE; a_resource: PATH): detachable PATH
|
||||
-- Resource path of `a_resource' for module `a_module', if resource exists.
|
||||
local
|
||||
rp: PATH
|
||||
ut: FILE_UTILITIES
|
||||
do
|
||||
-- Check first in selected theme folder.
|
||||
rp := module_assets_theme_location (a_module)
|
||||
Result := rp.extended_path (a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
-- And if not found, look into site/modules/$a_module.name/.... folders.
|
||||
rp := module_assets_location (a_module)
|
||||
Result := rp.extended_path (a_resource)
|
||||
if not ut.file_path_exists (Result) then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module_assets_location (a_module: CMS_MODULE): PATH
|
||||
-- Location for the assets associated with `a_module'.
|
||||
do
|
||||
Result := setup.environment.path.extended ("modules").extended (a_module.name)
|
||||
end
|
||||
|
||||
module_assets_theme_location (a_module: CMS_MODULE): PATH
|
||||
-- Location for the assets associated with `a_module'.
|
||||
do
|
||||
Result := setup.theme_location.extended ("modules").extended (a_module.name)
|
||||
end
|
||||
|
||||
feature -- URL utilities
|
||||
|
||||
is_front: BOOLEAN
|
||||
@@ -921,13 +888,19 @@ feature -- Generation
|
||||
across
|
||||
reg_ic.item.blocks as ic
|
||||
loop
|
||||
-- if attached {CMS_SMARTY_CONTENT_BLOCK} ic.item as l_tpl_block then
|
||||
-- across
|
||||
-- page.variables as var_ic
|
||||
-- loop
|
||||
-- l_tpl_block.set_value (var_ic.item, var_ic.key)
|
||||
-- end
|
||||
-- end
|
||||
if attached {CMS_SMARTY_TEMPLATE_BLOCK} ic.item as l_tpl_block then
|
||||
-- Apply page variables to smarty block.
|
||||
-- FIXME: maybe add notion of values at the CMS_BLOCK level
|
||||
-- or consider a CMS_BLOCK_WITH_VALUES ...
|
||||
across
|
||||
page.variables as var_ic
|
||||
loop
|
||||
if not l_tpl_block.values.has (var_ic.key) then
|
||||
-- Do not overwrite if has key.
|
||||
l_tpl_block.set_value (var_ic.item, var_ic.key)
|
||||
end
|
||||
end
|
||||
end
|
||||
page.add_to_region (theme.block_html (ic.item), reg_ic.item.name)
|
||||
end
|
||||
end
|
||||
|
||||
122
tools/roc/application.e
Normal file
122
tools/roc/application.e
Normal file
@@ -0,0 +1,122 @@
|
||||
note
|
||||
description: "[
|
||||
roc tool: install modules into an existing CMS Application.
|
||||
|
||||
roc install [--module|-m <MODULE_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>]
|
||||
|
||||
install: Install a given module to the corresponding cms application
|
||||
--module|-m: module path or current directory if is not defined.
|
||||
--dir|-d cms application path or current directory if is not defined
|
||||
|
||||
Running the command will copy to the CMS Application site/modules the following artifacts if the current module provide them.
|
||||
|
||||
config
|
||||
scripts
|
||||
themes
|
||||
running
|
||||
roc install blog
|
||||
will look for a module blog in the modules directory starting at the current directory.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
rename
|
||||
print as ascii_print
|
||||
end
|
||||
|
||||
ARGUMENTS_32
|
||||
rename
|
||||
print as ascii_print
|
||||
end
|
||||
|
||||
LOCALIZED_PRINTER
|
||||
rename
|
||||
print as ascii_print,
|
||||
localized_print as print
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Initialize tool.
|
||||
local
|
||||
cmd_args: like command_arguments
|
||||
do
|
||||
-- TODO add support to other commands.
|
||||
if argument_count = 0 then
|
||||
print_usage
|
||||
elseif attached commands.item (argument (1)) as cmd then
|
||||
cmd_args := command_arguments
|
||||
if cmd.is_valid (cmd_args) then
|
||||
cmd.execute (cmd_args)
|
||||
else
|
||||
print_command_usage (cmd)
|
||||
end
|
||||
else
|
||||
print ("Wrong command %"" + argument (1) + "%".%N")
|
||||
print_usage
|
||||
end
|
||||
end
|
||||
|
||||
commands: STRING_TABLE [ROC_COMMAND]
|
||||
local
|
||||
cmd: ROC_COMMAND
|
||||
once
|
||||
create Result.make (1)
|
||||
create {ROC_INSTALL_COMMAND} cmd.make ("install")
|
||||
Result.force (cmd, cmd.name)
|
||||
end
|
||||
|
||||
command_arguments: ARRAY [READABLE_STRING_32]
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.rebase (0)
|
||||
Result.force (argument (0), 0)
|
||||
from
|
||||
i := 2 -- skip first arg which is command name
|
||||
n := argument_count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
Result.force (argument (i), i - 1)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Usage
|
||||
|
||||
print_usage
|
||||
do
|
||||
print ("Usage:%N")
|
||||
across
|
||||
commands as ic
|
||||
loop
|
||||
print_command_usage (ic.item)
|
||||
end
|
||||
end
|
||||
|
||||
print_command_usage (cmd: ROC_COMMAND)
|
||||
do
|
||||
print ("roc ")
|
||||
print (cmd.name)
|
||||
print (" ")
|
||||
print (cmd.help)
|
||||
print ("%N")
|
||||
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
|
||||
3
tools/roc/license.lic
Normal file
3
tools/roc/license.lic
Normal file
@@ -0,0 +1,3 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
19
tools/roc/roc.ecf
Normal file
19
tools/roc/roc.ecf
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="roc" uuid="10B0F9A7-B711-419B-A1B5-833EB61DF8A6">
|
||||
<target name="roc">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<option warning="true" void_safety="all">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="encoding" location="$ISE_LIBRARY\library\encoding\encoding-safe.ecf"/>
|
||||
<cluster name="roc" location=".\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
53
tools/roc/roc_command.e
Normal file
53
tools/roc/roc_command.e
Normal file
@@ -0,0 +1,53 @@
|
||||
note
|
||||
description: "Abstraction of roc command."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
ROC_COMMAND
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
rename
|
||||
print as ascii_print
|
||||
end
|
||||
|
||||
LOCALIZED_PRINTER
|
||||
rename
|
||||
print as ascii_print,
|
||||
localized_print as print
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_name: READABLE_STRING_8)
|
||||
do
|
||||
create name.make_from_string (a_name)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: IMMUTABLE_STRING_8
|
||||
|
||||
help: STRING_32
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid (args: ARRAY [READABLE_STRING_32]): BOOLEAN
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (args: ARRAY [READABLE_STRING_32])
|
||||
require
|
||||
args.lower = 0 -- Prog name at index 0, args at index 1, ...
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
285
tools/roc/roc_install_command.e
Normal file
285
tools/roc/roc_install_command.e
Normal file
@@ -0,0 +1,285 @@
|
||||
note
|
||||
description: "Installation command."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ROC_INSTALL_COMMAND
|
||||
|
||||
inherit
|
||||
ROC_COMMAND
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
help: STRING_32
|
||||
once
|
||||
Result := "[--module|-m <MODULE_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>] [--site-dir <CMS_SITE_PATH>]"
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid (args: ARRAY [READABLE_STRING_32]): BOOLEAN
|
||||
-- Is the submitted install command valid?
|
||||
local
|
||||
i, n: INTEGER
|
||||
optional_module: BOOLEAN
|
||||
cms_path: BOOLEAN
|
||||
do
|
||||
n := args.upper
|
||||
-- TODO add error reporting.
|
||||
if n >= 1 and then n <= 4 then
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if args [i].same_string ("-m") then
|
||||
optional_module := True
|
||||
elseif args [i].same_string ("--module") then
|
||||
optional_module := True
|
||||
elseif args [i].same_string ("-d") then
|
||||
cms_path := True
|
||||
elseif args [i].same_string ("--dir") then
|
||||
cms_path := True
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if n = 4 then
|
||||
if (cms_path and optional_module) then
|
||||
-- valid command
|
||||
Result := True
|
||||
else
|
||||
print ("Error check the optional argument --module|-m and --dir|-d")
|
||||
end
|
||||
elseif n = 2 then
|
||||
if (cms_path and not optional_module) then
|
||||
Result := True
|
||||
else
|
||||
print ("Error missing value for dir")
|
||||
Result := False
|
||||
end
|
||||
else
|
||||
Result := True
|
||||
end
|
||||
else
|
||||
Result := False
|
||||
if n > 4 then
|
||||
print ("Too many arguments")
|
||||
end
|
||||
if n < 1 then
|
||||
print ("Too few argumetns")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Helpers
|
||||
|
||||
module_name (a_path: PATH): STRING_32
|
||||
do
|
||||
-- FIXME: better implementation needed. Either based on "a" new module.info file, or parsing the .ecf
|
||||
if attached a_path.entry as e then
|
||||
Result := e.name
|
||||
else
|
||||
Result := a_path.name
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (args: ARRAY [READABLE_STRING_32])
|
||||
-- Install a module into a cms application.
|
||||
-- Pattern: module_src/site/* => cms/site/modules/$module_name/*
|
||||
local
|
||||
l_site_path, l_cms_path, l_module_source_path: detachable PATH
|
||||
l_site_dir: DIRECTORY
|
||||
l_modules_dir: DIRECTORY
|
||||
l_dest_dir: DIRECTORY
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := args.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if attached args[i] as arg then
|
||||
if
|
||||
arg.same_string ("-d")
|
||||
or arg.same_string ("--dir")
|
||||
then
|
||||
i := i + 1
|
||||
if i <= n then
|
||||
create l_cms_path.make_from_string (args[i])
|
||||
end
|
||||
elseif
|
||||
arg.same_string ("--site-dir")
|
||||
then
|
||||
i := i + 1
|
||||
if i <= n then
|
||||
create l_site_path.make_from_string (args[i])
|
||||
end
|
||||
elseif
|
||||
arg.same_string ("-m")
|
||||
or arg.same_string ("--module")
|
||||
then
|
||||
i := i + 1
|
||||
if i <= n then
|
||||
create l_module_source_path.make_from_string (args[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if l_module_source_path = Void then
|
||||
l_module_source_path := Execution_environment.current_working_path
|
||||
end
|
||||
if l_cms_path = Void then
|
||||
l_cms_path := Execution_environment.current_working_path.extended ("modules")
|
||||
end
|
||||
if l_cms_path /= Void and l_module_source_path /= Void then
|
||||
-- If l_site_path is not set; initialize it to $cms_path/site.
|
||||
if l_site_path = Void then
|
||||
l_site_path := l_cms_path.extended ("site")
|
||||
end
|
||||
|
||||
-- Install configuration files.
|
||||
if attached module_name (l_module_source_path) as l_mod_name then
|
||||
create l_site_dir.make_with_path (l_site_path)
|
||||
|
||||
if l_site_dir.exists then
|
||||
create l_modules_dir.make_with_path (l_site_path.extended ("modules"))
|
||||
if not l_modules_dir.exists then
|
||||
l_modules_dir.create_dir
|
||||
end
|
||||
|
||||
create l_dest_dir.make_with_path (l_modules_dir.path.extended (l_mod_name))
|
||||
if not l_dest_dir.exists then
|
||||
l_dest_dir.create_dir
|
||||
end
|
||||
install_module_elements (l_module_source_path, l_dest_dir.path, Void)
|
||||
-- install_module_elements (l_module_source_path, l_dest_dir.path, Config_dir)
|
||||
-- install_module_elements (l_module_source_path, l_dest_dir.path, Scripts_dir)
|
||||
-- install_module_elements (l_module_source_path, l_dest_dir.path, Themes_dir)
|
||||
print ("Module ")
|
||||
print (l_mod_name)
|
||||
print (" was successfuly installed to the CMS Application location ")
|
||||
print (l_cms_path.name)
|
||||
print ("%NCheck the module elements at ")
|
||||
print (l_dest_dir.path.name)
|
||||
print (".%N")
|
||||
else
|
||||
print ({STRING_32} "The CMS Application located at " + l_cms_path.name + "does not have the site or modules folders.%N")
|
||||
end
|
||||
else
|
||||
print ("Error: not possible to retrieve module name.%N")
|
||||
end
|
||||
else
|
||||
print ("Error: wrong path to CMS application.%N")
|
||||
end
|
||||
end
|
||||
|
||||
install_module_elements (a_module_source_path: PATH; a_cms_module_target_path: PATH; a_element: detachable READABLE_STRING_GENERAL)
|
||||
-- Install module site files from `a_module_source_path' to cms application `a_cms_module_target_path' under expected modules folder.
|
||||
-- If `a_element' is set, take into account only sub folder `a_element'.
|
||||
local
|
||||
l_path: PATH
|
||||
l_dest_dir: DIRECTORY
|
||||
l_src_dir: DIRECTORY
|
||||
do
|
||||
l_path := a_module_source_path.extended ("site")
|
||||
if a_element /= Void then
|
||||
-- Copy all files under "site/$a_element" into "site/modules/$module_name/$a_element" location.
|
||||
create l_src_dir.make_with_path (l_path.extended (a_element))
|
||||
create l_dest_dir.make_with_path (a_cms_module_target_path.extended (a_element))
|
||||
else
|
||||
-- Copy all files under "site" into "site/modules/$module_name/" location.
|
||||
create l_src_dir.make_with_path (l_path)
|
||||
create l_dest_dir.make_with_path (a_cms_module_target_path)
|
||||
end
|
||||
if not l_dest_dir.exists then
|
||||
l_dest_dir.create_dir
|
||||
end
|
||||
copy_directory (l_src_dir, l_dest_dir, True)
|
||||
end
|
||||
|
||||
feature {NONE} -- System/copy files
|
||||
|
||||
copy_directory (a_src: DIRECTORY; a_dest: DIRECTORY; is_recursive: BOOLEAN)
|
||||
-- Copy all elements from `a_src' to `a_dest'.
|
||||
local
|
||||
l_dir: DIRECTORY
|
||||
l_new_dir: DIRECTORY
|
||||
entry: PATH
|
||||
l_path: PATH
|
||||
l_file: FILE
|
||||
ut: FILE_UTILITIES
|
||||
do
|
||||
across
|
||||
a_src.entries as ic
|
||||
loop
|
||||
entry := ic.item
|
||||
if not (entry.is_current_symbol or else entry.is_parent_symbol) then
|
||||
l_path := a_src.path.extended_path (entry)
|
||||
create {RAW_FILE} l_file.make_with_path (l_path)
|
||||
if not l_file.is_directory then
|
||||
copy_file_in_directory (l_file, a_dest.path)
|
||||
elseif is_recursive then
|
||||
create l_dir.make_with_path (l_path)
|
||||
create l_new_dir.make_with_path (a_dest.path.extended_path (entry))
|
||||
ut.create_directory_path (l_new_dir.path)
|
||||
if l_dir.exists then
|
||||
copy_directory (l_dir, l_new_dir, is_recursive)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
copy_file_in_directory (a_file: FILE; a_dir: PATH)
|
||||
-- Copy file `a_file' to dir `a_dir'.
|
||||
local
|
||||
retried: BOOLEAN
|
||||
l_dest: RAW_FILE
|
||||
do
|
||||
if not retried then
|
||||
if attached a_file.path.entry as e then
|
||||
create l_dest.make_with_path (a_dir.extended_path (e))
|
||||
l_dest.create_read_write
|
||||
a_file.open_read
|
||||
-- Copy file source to destination
|
||||
if
|
||||
l_dest.exists and then
|
||||
l_dest.is_writable and then
|
||||
a_file.exists and then
|
||||
a_file.is_readable
|
||||
then
|
||||
a_file.copy_to (l_dest)
|
||||
a_file.close
|
||||
l_dest.close
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
--feature -- Constants
|
||||
|
||||
-- Config_dir: STRING = "config"
|
||||
-- -- Configuration dir.
|
||||
|
||||
-- Scripts_dir: STRING = "scripts"
|
||||
-- -- Scripts dir.
|
||||
|
||||
-- Themes_dir: STRING = "themes"
|
||||
-- -- Themes dir.
|
||||
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user