Merge branch 'master' into es17.01

# Conflicts:
#	examples/demo/demo.ecf
This commit is contained in:
Jocelyn Fiat
2017-04-06 15:40:14 +02:00
23 changed files with 748 additions and 69 deletions

View File

@@ -0,0 +1,13 @@
Google Custom Search Module 2.0.
# Based on Google Custom Search Engine (CSE)
- https://developers.google.com/custom-search/docs/element
- Search form provides a parameter `q` for the search query.
- Results are displayed in associated page /gcse2 (using <gcse:searchresults-only> )
# Settings
- edit the `site/modules/google_search_20/config/google_search_20.json` file, and set the value `gcse.search_engine_id`.
# How to get a Custom Search engine ID?
- See https://support.google.com/customsearch/answer/2649143?hl=en
- https://cse.google.com/all

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="google_search_20" uuid="3FAAD593-3428-43B9-8CE1-BC6E5FE81F19" library_target="google_search_20">
<target name="google_search_20">
<root all_classes="true"/>
<capability>
<concurrency support="scoop" use="scoop"/>
</capability>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_config" location="..\..\library\configuration\config-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="google_search" uuid="3FAAD593-3428-43B9-8CE1-BC6E5FE81F19" library_target="google_search_20">
<target name="google_search_20">
<root all_classes="true"/>
<option is_attached_by_default="false" void_safety="none">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env.ecf" readonly="false"/>
<library name="cms_config" location="..\..\library\configuration\config.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,5 @@
{
"gcse": {
"search_engine_id":""
}
}

View File

@@ -0,0 +1,6 @@
<form action="{$site_url/}gcse20" class="search-form" id="search-form">
<div class="form-group has-feedback">
<input type="search" class="form-control" name="q" id="search-query" placeholder="search" value="{htmlentities}{$cms_search_query/}{/htmlentities}" >
<span class="glyphicon glyphicon-search form-control-feedback"></span>
</div>
</form>

View File

@@ -0,0 +1,7 @@
<section>
<header>
<h2>Results for <kbd>{$cms_search_query/}</kbd></h2>
</header>
<gcse:searchresults-only></gcse:searchresults-only>
</section>

View File

@@ -0,0 +1,156 @@
note
description: "[
Module providing Google Custom Search functionality.
]"
date: "$Date$"
revision: "$Revision$"
class
GOOGLE_CUSTOM_SEARCH_MODULE_20
inherit
CMS_MODULE
redefine
setup_hooks
end
CMS_HOOK_AUTO_REGISTER
REFACTORING_HELPER
CMS_HOOK_BLOCK
CMS_HOOK_BLOCK_HELPER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "2.0"
description := "Google custome search module 2.0"
package := "search"
end
feature -- Access
name: STRING = "google_search_20"
-- <Precursor>
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration.
local
m: WSF_URI_MAPPING
do
create m.make_trailing_slash_ignored ("/gcse20", create {WSF_URI_AGENT_HANDLER}.make (agent handle_search (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get)
end
feature -- GCSE Keys
gcse_cx_key (api: CMS_API): detachable READABLE_STRING_8
-- Get google custom search engine id.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if
attached cfg.text_item ("gcse.search_engine_id") as l_gcse_cx_key and then
not l_gcse_cx_key.is_empty
then
Result := utf.utf_32_string_to_utf_8_string_8 (l_gcse_cx_key)
end
end
end
feature -- Handler
handle_search (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_script: STRING
b: CMS_CONTENT_BLOCK
do
write_debug_log (generator + ".handle_search")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if
attached {WSF_STRING} req.query_parameter ("q") as l_query and then
not l_query.value.is_empty
then
if
attached gcse_cx_key (api) as l_cx
then
r.set_value (l_query.value, "cms_search_query")
if
attached smarty_template_block (Current, "gcse_search_results", api) as l_tpl_block
then
r.add_block (l_tpl_block, "content")
if attached gcse_cx_key (api) as l_ctx then
create l_script.make_from_string (gcse_20_script)
l_script.replace_substring_all ("#CX_VALUE", l_ctx)
create b.make_raw ("gcse2_js", Void, "<script>%N" + l_script + "%N</script>", Void)
r.add_block (b, "footer")
end
end
end
else
r.add_message ("No query submitted", Void)
end
r.execute
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
do
Result := <<"gcse_search_form">>
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_block_id.is_case_insensitive_equal_general ("gcse_search_form") then
if a_response.request.is_get_request_method then
if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then
a_response.add_block (l_tpl_block, "search")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
feature {NONE} -- Implementation
gcse_20_script: STRING = "[
(function() {
var cx = '#CX_VALUE';
var gcse = document.createElement('script'); gcse.type = 'text/javascript'; gcse.async = true;
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//www.google.com/cse/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
]"
end

View File

@@ -57,6 +57,18 @@ feature -- Settings
session_max_age: INTEGER
-- Value of the Max-Age, before the cookie expires.
feature -- Status report
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
do
if
a_response.is_authenticated and then
attached a_response.request.cookie (session_token)
then
Result := True
end
end
feature -- Access
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER
@@ -71,6 +83,47 @@ feature -- Access
Result := session_auth_storage.has_user_token (a_user)
end
feature -- Basic operation
process_user_login (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_token: STRING
l_cookie: WSF_COOKIE
do
l_token := new_session_token
if has_user_token (a_user) then
update_user_session_auth (l_token, a_user)
else
new_user_session_auth (l_token, a_user)
end
create l_cookie.make (session_token, l_token)
l_cookie.set_max_age (session_max_age)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
cms_api.set_user (a_user)
cms_api.record_user_login (a_user)
end
process_user_logout (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_cookie: WSF_COOKIE
do
if
attached session_token as tok and then
attached {WSF_STRING} req.cookie (tok) as l_cookie_token
then
-- Logout Session
create l_cookie.make (tok, "") -- l_cookie_token.value) -- FIXME: unicode issue?
l_cookie.set_path ("/")
l_cookie.unset_max_age
l_cookie.set_expiration_date (create {DATE_TIME}.make_from_epoch (0))
res.add_cookie (l_cookie)
else
-- it seems the user was not login, as there is no associated cookie!
end
cms_api.unset_user
end
feature -- Change User session
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;)
@@ -86,4 +139,26 @@ feature -- Change User session
session_auth_storage.update_user_session_auth (a_token, a_user)
end
feature -- Token
new_session_token: STRING
-- Generate token to use in a Session.
local
l_token: STRING
l_security: CMS_TOKEN_GENERATOR
l_encode: URL_ENCODER
do
create l_security
l_token := l_security.token
create l_encode
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
l_token := l_security.token
end
Result := l_token
end
end

View File

@@ -100,11 +100,9 @@ feature -- Access: auth strategy
-- <Precursor>
do
if
a_response.is_authenticated and then
attached session_api as l_session_api and then
attached a_response.request.cookie (l_session_api.session_token)
attached session_api as l_session_api
then
Result := True
Result := l_session_api.is_authenticating (a_response)
end
end
@@ -116,7 +114,7 @@ feature -- Access: router
if attached session_api as l_session_api then
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/" + logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_session_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,session_api, ?, ?)), a_router.methods_post)
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_session (a_api, l_session_api, ?, ?)), a_router.methods_post)
end
end
@@ -164,39 +162,33 @@ feature {NONE} -- Implementation: routes
handle_logout (api: CMS_API; a_session_api: CMS_SESSION_API ; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_cookie: WSF_COOKIE
tok: STRING
do
tok := a_session_api.session_token
if
attached {WSF_STRING} req.cookie (tok) as l_cookie_token and then
attached api.user as l_user
then
-- Logout Session
create l_cookie.make (tok, "") -- l_cookie_token.value) -- FIXME: unicode issue?
l_cookie.set_path ("/")
l_cookie.unset_max_age
l_cookie.set_expiration_date (create {DATE_TIME}.make_from_epoch (0))
res.add_cookie (l_cookie)
api.unset_user
if attached api.user as l_user then
a_session_api.process_user_logout (l_user, req, res)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
else
-- Not loggued in ... redirect to home
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found)
r.set_redirection (req.absolute_script_url (""))
r.execute
else
fixme (generator + ": missing else implementation in handle_logout!")
end
if
attached {WSF_STRING} req.item ("destination") as p_destination and then
attached p_destination.value as v and then
v.is_valid_as_string_8
then
r.set_redirection (v.to_string_8)
else
r.set_redirection (req.absolute_script_url (""))
end
r.execute
end
handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE)
handle_login_with_session (api: CMS_API; a_session_api: CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_token: STRING
l_cookie: WSF_COOKIE
do
if
attached a_session_api as l_session_api and then
attached {WSF_STRING} req.form_parameter ("username") as l_username and then
attached {WSF_STRING} req.form_parameter ("password") as l_password
then
@@ -204,18 +196,7 @@ feature {NONE} -- Implementation: routes
api.user_api.is_valid_credential (l_username.value, l_password.value) and then
attached api.user_api.user_by_name (l_username.value) as l_user
then
l_token := generate_token
if a_session_api.has_user_token (l_user) then
l_session_api.update_user_session_auth (l_token, l_user)
else
l_session_api.new_user_session_auth (l_token, l_user)
end
create l_cookie.make (a_session_api.session_token, l_token)
l_cookie.set_max_age (a_session_api.session_max_age)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
api.set_user (l_user)
api.record_user_login (l_user)
a_session_api.process_user_login (l_user, req, res)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if
@@ -295,23 +276,4 @@ feature {NONE} -- Block views
end
end
generate_token: STRING
-- Generate token to use in a Session.
local
l_token: STRING
l_security: CMS_TOKEN_GENERATOR
l_encode: URL_ENCODER
do
create l_security
l_token := l_security.token
create l_encode
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
l_token := l_security.token
end
Result := l_token
end
end