Files
ROC/modules/contact/src/cms_contact_module.e
Jocelyn Fiat 3496536751 Added CMS_API.request: WSF_REQUEST to ease dev of ROC CMS code.
- Removed CMS_REQUEST_UTIL
  - centralize a few request related code into CMS_API
Added CMS_API.user, CMS_API.set_user (CMS_USER), ... and user related routines.

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

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

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

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

Added CMS_RESPONSE.redirection_delay, if ever one code want to redirect after a few seconds.
Added the request uri info to the not found cms response.
2016-01-29 21:58:49 +01:00

534 lines
16 KiB
Plaintext

note
description: "[
Module that provide contact us web form functionality.
]"
author: "$Author: jfiat $"
date: "$Date: 2016-01-08 22:43:12 +0100 (ven., 08 janv. 2016) $"
revision: "$Revision: 98369 $"
class
CMS_CONTACT_MODULE
inherit
CMS_MODULE
rename
module_api as contact_api
redefine
setup_hooks,
install,
initialize,
contact_api
end
CMS_HOOK_BLOCK
CMS_HOOK_BLOCK_HELPER
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "Contact form module"
package := "messaging"
end
feature -- Access
name: STRING = "contact"
-- <Precursor>
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
local
l_contact_api: like contact_api
ut: FILE_UTILITIES
p: PATH
contact_storage: CONTACT_STORAGE_I
do
Precursor (api)
-- if attached api.storage.as_sql_storage as l_storage_sql then
-- create {CONTACT_STORAGE_SQL} contact_storage.make (l_storage_sql)
-- else
p := file_system_storage_path (api)
if ut.directory_path_exists (p) then
create {CONTACT_STORAGE_FS} contact_storage.make (p, api)
else
create {CONTACT_STORAGE_NULL} contact_storage.make
end
create l_contact_api.make (api, contact_storage)
contact_api := l_contact_api
end
feature {CMS_API} -- Module management
install (api: CMS_API)
local
retried: BOOLEAN
d: DIRECTORY
do
if not retried then
create d.make_with_path (file_system_storage_path (api))
d.recursive_create_dir
Precursor {CMS_MODULE}(api) -- Marked installed
end
rescue
retried := True
retry
end
file_system_storage_path (api: CMS_API): PATH
-- Location of eventual file system based storage for contact messages.
do
Result := api.site_location.extended ("db").extended (name).extended ("messages")
end
feature {CMS_API} -- Access: API
contact_api: detachable CONTACT_API
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 ("/contact", create {WSF_URI_AGENT_HANDLER}.make (agent handle_contact (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get)
a_router.handle ("/contact", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_contact (a_api, ?, ?)), a_router.methods_put_post)
end
feature -- Recaptcha
recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if
attached cfg.text_item ("recaptcha.secret_key") as l_recaptcha_key and then
not l_recaptcha_key.is_empty
then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
recaptcha_site_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if
attached cfg.text_item ("recaptcha.site_key") as l_recaptcha_key and then
not l_recaptcha_key.is_empty
then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
end
feature -- Hooks
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'.
do
debug ("refactor_fixme")
fixme ("add contact to menu")
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
do
Result := <<"?contact">>
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_block_id.is_case_insensitive_equal_general ("contact") then
-- "contact", "post_contact"
if a_response.request.is_get_request_method then
if attached template_block (Current, a_block_id, a_response) as l_tpl_block then
if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end
a_response.add_block (l_tpl_block, "content")
a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void)
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
new_html_contact_form (a_response: CMS_RESPONSE; api: CMS_API): STRING
local
f: CMS_FORM
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void)
if attached template_block (Current, "contact", a_response) as l_tpl_block then
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end
across
a_response.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
Result := l_tpl_block.to_html (a_response.theme)
else
f := new_contact_form (a_response, api)
api.hooks.invoke_form_alter (f, f.last_data, a_response)
Result := "<div class=%"contact-box%"><h1>Contact us!</h1>" + f.to_html (a_response.wsf_theme) + "<br/></div>"
end
end
new_contact_form (a_response: CMS_RESPONSE; api: CMS_API): CMS_FORM
local
f: CMS_FORM
f_name: WSF_FORM_TEXT_INPUT
f_email: WSF_FORM_EMAIL_INPUT
f_msg: WSF_FORM_TEXTAREA
f_submit: WSF_FORM_SUBMIT_INPUT
do
create f.make (a_response.url ("contact", Void), "contact-form")
create f_name.make ("name")
f_name.set_label ("Name")
f_name.set_is_required (True)
f.extend (f_name)
create f_email.make ("email")
f_email.set_label ("Email Address")
f_email.set_is_required (True)
f.extend (f_email)
create f_msg.make ("message")
f_msg.set_label ("Message")
f_msg.set_rows (5)
f_msg.set_is_required (True)
f.extend (f_msg)
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
f.extend_html_text ("<div class=%"g-recaptcha%" data-sitekey=%"" + l_recaptcha_site_key + "%"></div><br/>")
end
create f_submit.make_with_text ("submit-op", "Send")
f.extend (f_submit)
-- f.extend_html_text ("[
-- <p class="req-field-desc"><span class="required">*</span> indicates a required field</p>
-- ]")
Result := f
end
handle_contact (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
-- FIXME: we should use WSF_FORM, and integrate the recaptcha using the form alter hook.
write_debug_log (generator + ".handle_contact")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.values.force ("contact", "contact")
r.set_main_content (new_html_contact_form (r, api))
r.execute
end
handle_post_contact (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
msg: CONTACT_MESSAGE
l_params: CONTACT_EMAIL_SERVICE_PARAMETERS
e: CMS_EMAIL
vars: STRING_TABLE [READABLE_STRING_8]
l_contact_email_address: READABLE_STRING_8
do
write_information_log (generator + ".handle_post_contact")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.add_style (r.url ("/module/" + name + "/files/css/contact.css", Void), Void)
r.values.force (False, "has_error")
create vars.make_caseless (5)
vars.put (safe_html_encoded (api.setup.site_url), "siteurl")
vars.put (safe_html_encoded (api.setup.site_name), "sitename")
write_debug_log (generator + ".handle_post_contact {Form Parameters:" + form_parameters_as_string (req) + "}")
if
attached {WSF_STRING} req.form_parameter ("name") as l_name and then
attached {WSF_STRING} req.form_parameter ("email") as l_email and then
attached {WSF_STRING} req.form_parameter ("message") as l_message
then
if
is_form_captcha_verified (req, "g-recaptcha-response", api) and then
l_email.value.is_valid_as_string_8
then
l_contact_email_address := l_email.value.to_string_8
if attached contact_api as l_contact_api then
create msg.make (l_name.value, l_message.value)
msg.set_email (l_contact_email_address)
l_contact_api.save_contact_message (msg)
end
create l_params.make (api, Current)
-- Send internal email to admin.
vars.put (html_encoded (l_name.value), "name")
vars.put (html_encoded (l_contact_email_address), "email")
vars.put (html_encoded (l_message.value), "message")
write_debug_log (generator + ".handle_post_contact: send notification email")
e := api.new_email (l_params.admin_email, "Notification Contact", email_html_message ("notification", r, vars))
e.set_from_address (l_params.admin_email)
e.add_header_line ("MIME-Version:1.0")
e.add_header_line ("Content-Type: text/html; charset=utf-8")
api.process_email (e)
if not api.has_error then
-- Send Contact email to the user
write_information_log (generator + ".handle_post_contact: preparing the message.")
e := api.new_email (l_contact_email_address, l_params.contact_subject_text, email_html_message ("message", r, vars))
e.set_from_address (l_params.admin_email)
e.add_header_line ("MIME-Version:1.0")
e.add_header_line ("Content-Type: text/html; charset=utf-8")
write_debug_log (generator + ".handle_post_contact: send_contact_email")
api.process_email (e)
end
if api.has_error then
write_error_log (generator + ".handle_post_contact: error message:["+ api.string_representation_of_errors +"]")
r.set_status_code ({HTTP_CONSTANTS}.internal_server_error)
r.values.force (True, "has_error")
vars.put ("True", "has_error")
end
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
r.set_main_content (l_tpl_block.to_html (r.theme))
else
r.set_main_content ("Thank you for your message.")
end
r.execute
else
-- send a bad request status code and redisplay the form with the previous data loaded.
r.set_value (False, "error")
r.set_status_code ({HTTP_STATUS_CODE}.bad_request)
if attached template_block_with_values (Current, "contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
l_tpl_block.set_value (<<"Missing Captcha", "Internal Server Error">>, "error_response")
end
r.set_main_content (l_tpl_block.to_html (r.theme))
else
debug ("cms")
r.add_warning_message ("Error with block [contact]")
end
end
r.execute
end
else
-- Internal server error
write_error_log (generator + ".handle_post_contact: Internal Server error")
r.values.force (True, "has_error")
r.set_status_code ({HTTP_CONSTANTS}.internal_server_error)
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
r.set_main_content (l_tpl_block.to_html (r.theme))
end
r.execute
end
end
is_form_captcha_verified (req: WSF_REQUEST; a_form_field_id: READABLE_STRING_GENERAL; api: CMS_API): BOOLEAN
do
if attached recaptcha_secret_key (api) as l_recaptcha_key then
if
attached {WSF_STRING} req.form_parameter (a_form_field_id) as l_recaptcha_response and then
is_captcha_verified (l_recaptcha_key, l_recaptcha_response.value)
then
Result := True
else
--| Bad or missing captcha
Result := False
end
else
--| reCaptcha is not setup, so no verification
Result := True
end
end
feature {NONE} -- Helpers
form_parameters_as_string (req: WSF_REQUEST): STRING
do
create Result.make_empty
across req.form_parameters as ic loop
Result.append (ic.item.key)
Result.append_character ('=')
Result.append_string (ic.item.string_representation)
Result.append_character ('%N')
end
end
feature {NONE} -- Contact Message
template_block_with_values (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_values: STRING_TABLE [ANY]): like template_block
do
Result := template_block (a_module, a_block_id, a_response)
if Result /= Void then
across
a_values as ic
loop
Result.set_value (ic.item, ic.key)
end
end
end
email_html_message (a_message_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_html_encoded_values: STRING_TABLE [READABLE_STRING_8]): STRING
-- html message related to `a_message_id'.
local
res: PATH
p: detachable PATH
tpl: CMS_SMARTY_TEMPLATE_BLOCK
exp: CMS_STRING_EXPANDER [STRING_8]
do
write_debug_log (generator + ".email_html_message for [" + a_message_id + " ]")
create res.make_from_string ("templates")
res := res.extended ("email_").appended (a_message_id).appended_with_extension ("tpl")
p := a_response.api.module_theme_resource_location (Current, res)
if p /= Void then
if attached p.entry as e then
create tpl.make (a_message_id, Void, p.parent, e)
write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
else
create tpl.make (a_message_id, Void, p.parent, p)
write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
end
across
a_html_encoded_values as ic
loop
tpl.set_value (ic.item, ic.key)
end
Result := tpl.to_html (a_response.theme)
else
if a_message_id.is_case_insensitive_equal_general ("message") then
create Result.make_from_string (contact_message_template)
elseif a_message_id.is_case_insensitive_equal_general ("notification") then
create Result.make_from_string (contact_notification_message_template)
else
create Result.make_from_string (a_message_id)
across
a_html_encoded_values as ic
loop
Result.append ("<li>")
Result.append (html_encoded (ic.key))
Result.append (": ")
Result.append (ic.item) -- Already html encoded.
Result.append ("</li>%N")
end
end
create exp.make
across
a_html_encoded_values as ic
loop
exp.put (ic.item, ic.key)
end
exp.expand_string (Result)
write_debug_log (generator + ".email_html_message using built-in message:" + Result)
end
end
contact_message_template: STRING
do
Result := "[
<p>Thank you for contacting $sitename.<br/>
We will get back to you promptly on your contact request.
</p>
]"
+ contact_notification_message_template
end
contact_notification_message_template: STRING = "[
<h2>Contact information:</h2>
<div>
<strong>Name<strong>: $name <br/>
<strong>Email<strong>: $email <br/>
<strong>Message<strong>: $message <br/>
</div>
]"
feature {NONE} -- Google recaptcha uri template
is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN
local
api: RECAPTCHA_API
l_errors: STRING
do
write_debug_log (generator + ".is_captcha_verified with response: [" + a_response + "]")
create api.make (a_secret, a_response)
Result := api.verify
if not Result and then attached api.errors as l_api_errors then
create l_errors.make_empty
l_errors.append_character ('%N')
across l_api_errors as ic loop
l_errors.append ( ic.item )
l_errors.append_character ('%N')
end
write_error_log (generator + ".is_captcha_verified api_errors [" + l_errors + "]")
end
end
end