Integrated new registration workflow.

Added optional "mailer.subject_prefix" configuration item.
Added CMS_SETUP.utf_8_site_name for convenience.
Fixed a few potential unicode issues.
Fixed various typos.
This commit is contained in:
2016-01-15 17:46:56 +01:00
55 changed files with 2030 additions and 364 deletions

View File

@@ -16,7 +16,7 @@ feature {NONE} -- Initialization
initialize
local
l_url: like site_url
l_email: detachable READABLE_STRING_8
s, l_email: detachable READABLE_STRING_8
do
site_location := environment.path
@@ -51,27 +51,32 @@ feature {NONE} -- Initialization
site_email := l_email
-- Email address for current web site
--| Also known
site_notification_email := string_8_item_or_default ("notification.email", site_email)
-- Email subject tuning.
s := string_8_item ("mailer.subject_prefix")
if s /= Void and then not s.ends_with_general (" ") then
s := s + " "
end
site_email_subject_prefix := s
-- Location for public files
if attached text_item ("files-dir") as s then
create files_location.make_from_string (s)
if attached text_item ("files-dir") as l_files_dir then
create files_location.make_from_string (l_files_dir)
else
files_location := site_location.extended ("files")
end
-- Location for modules folders.
if attached text_item ("modules-dir") as s then
create modules_location.make_from_string (s)
if attached text_item ("modules-dir") as l_modules_dir then
create modules_location.make_from_string (l_modules_dir)
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)
if attached text_item ("themes-dir") as l_themes_dir then
create themes_location.make_from_string (l_themes_dir)
else
themes_location := environment.themes_path
end
@@ -196,6 +201,14 @@ feature -- Access: Site
site_name: READABLE_STRING_32
-- Name of the site.
utf_8_site_name: READABLE_STRING_8
-- `site_name' encoded with UTF-8.
local
utf: UTF_CONVERTER
do
Result := utf.utf_32_string_to_utf_8_string_8 (site_name)
end
site_email: READABLE_STRING_8
-- Website email address.
-- Used as "From:" address when the site is sending emails
@@ -204,6 +217,9 @@ feature -- Access: Site
site_notification_email: READABLE_STRING_8
-- Email address receiving internal notification.
site_email_subject_prefix: detachable READABLE_STRING_8
-- Optional prefix for any email sent by Current site.
site_url: detachable READABLE_STRING_8
-- Optional url of current CMS site.

View File

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

View File

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

View File

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

View File

@@ -316,8 +316,16 @@ feature -- Emails
new_email (a_to_address: READABLE_STRING_8; a_subject: READABLE_STRING_8; a_content: READABLE_STRING_8): CMS_EMAIL
-- New email object.
local
l_subject: READABLE_STRING_8
do
create Result.make (setup.site_email, a_to_address, a_subject, a_content)
l_subject := a_subject
if attached setup.site_email_subject_prefix as l_prefix then
if not l_subject.starts_with (l_prefix) then
l_subject := l_prefix + l_subject
end
end
create Result.make (setup.site_email, a_to_address, l_subject, a_content)
end
process_email (e: CMS_EMAIL)

View File

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