Revisited the persistence layer.

Simplified schema to focus on user and node.
Now possible to have sqlite via ODBC and/or mysql support, and select using configuration file.
Updated demo example.
This commit is contained in:
2015-01-27 19:48:37 +01:00
parent db9e40cec4
commit 7d5869f3b9
71 changed files with 2665 additions and 3313 deletions
+16 -2
View File
@@ -2,8 +2,8 @@ note
description: "[
Default CMS_SETUP that can be reused easily, and/or redefined to match specific setup.
]"
date: "$Date: 2015-01-14 16:13:47 +0100 (mer., 14 janv. 2015) $"
revision: "$Revision: 96454 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_DEFAULT_SETUP
@@ -31,7 +31,10 @@ feature {NONE} -- Initialization
do
configure
create modules.make (3)
create storage_drivers.make (2)
build_mailer
initialize_storages
initialize_modules
end
@@ -72,6 +75,12 @@ feature {NONE} -- Initialization
compute_theme_assets_location
end
initialize_storages
-- Initialize storages
do
storage_drivers.force (create {CMS_STORAGE_NULL_BUILDER}, "null")
end
initialize_modules
-- Intialize core modules.
local
@@ -144,6 +153,11 @@ feature -- Access
to_implement ("Not implemented mailer")
end
feature -- Access: storage
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
-- Precursor
feature -- Element change
register_module (m: CMS_MODULE)
+53 -3
View File
@@ -1,11 +1,14 @@
note
description: "Class that enable to set basic configuration, application layout, core modules and themes."
date: "$Date: 2015-01-14 18:12:03 +0100 (mer., 14 janv. 2015) $"
revision: "$Revision: 96456 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_SETUP
inherit
REFACTORING_HELPER
feature -- Access
layout: CMS_LAYOUT
@@ -88,7 +91,6 @@ feature -- Query
deferred
end
feature -- Access: Theme
themes_location: PATH
@@ -109,6 +111,54 @@ feature -- Access: Theme
theme_name: READABLE_STRING_32
-- theme name.
feature -- Access: storage
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
deferred
end
storage (a_error_handler: ERROR_HANDLER): detachable CMS_STORAGE
local
retried: BOOLEAN
l_message: STRING
do
if not retried then
to_implement ("Refactor database setup")
if
attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (layout.application_config_path) as l_database_config and then
attached storage_drivers.item (l_database_config.driver) as l_builder
then
Result := l_builder.storage (Current)
else
create {CMS_STORAGE_NULL} Result
end
else
to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.")
-- error hanling.
create {CMS_STORAGE_NULL} Result
create l_message.make (1024)
if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then
if attached l_exception.description as l_description then
l_message.append (l_description.as_string_32)
l_message.append ("%N%N")
elseif attached l_exception.trace as l_trace then
l_message.append (l_trace)
l_message.append ("%N%N")
else
l_message.append (l_exception.out)
l_message.append ("%N%N")
end
else
l_message.append ("The application crash without available information")
l_message.append ("%N%N")
end
a_error_handler.add_custom_error (0, " Database Connection ", l_message)
end
rescue
retried := True
retry
end
feature -- Element change
register_module (m: CMS_MODULE)
@@ -1,7 +1,7 @@
note
description: "Summary description for {NEW_CONTENT_HANDLER}."
date: "$Date: 2014-12-19 14:17:32 +0100 (ven., 19 déc. 2014) $"
revision: "$Revision: 96402 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NODE_CONTENT_HANDLER
@@ -120,7 +120,7 @@ feature -- HTTP Methods
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
u_node := extract_data_form (req)
u_node.set_id (l_id.integer_value)
u_node.set_id (l_id.value.to_integer_64)
api.update_node_content (l_user.id, u_node.id, u_node.content)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
else
+28 -19
View File
@@ -1,7 +1,7 @@
note
description: "Summary description for {NODE_HANDLER}."
date: "$Date: 2014-12-19 14:17:32 +0100 (ven., 19 déc. 2014) $"
revision: "$Revision: 96402 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NODE_HANDLER
@@ -68,7 +68,10 @@ feature -- HTTP Methods
do
-- Existing node
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
if
l_id.is_integer and then
attached api.node (l_id.value.to_integer_64) as l_node
then
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
l_page.add_variable (l_node, "node")
l_page.execute
@@ -89,7 +92,10 @@ feature -- HTTP Methods
to_implement ("Check user permissions!!!")
if attached current_user (req) as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
if
l_id.is_integer and then
attached {CMS_NODE} api.node (l_id.value.to_integer_64) as l_node
then
if attached {WSF_STRING} req.form_parameter ("method") as l_method then
if l_method.is_case_insensitive_equal ("DELETE") then
do_delete (req, res)
@@ -104,7 +110,8 @@ feature -- HTTP Methods
end
else
-- New node
u_node := extract_data_form (req)
create u_node.make ("", "", "")
update_node_from_data_form (req, u_node)
u_node.set_author (l_user)
api.new_node (u_node)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
@@ -116,16 +123,17 @@ feature -- HTTP Methods
do_put (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
local
u_node: CMS_NODE
do
if attached current_user (req) as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
u_node := extract_data_form (req)
u_node.set_id (l_id.integer_value)
api.update_node (l_user.id,u_node)
if
l_id.is_integer and then
attached api.node (l_id.value.to_integer_64) as l_node
then
update_node_from_data_form (req, l_node)
l_node.set_author (l_user)
api.update_node (l_node)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
else
do_error (req, res, l_id)
@@ -136,7 +144,6 @@ feature -- HTTP Methods
else
(create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res)
end
end
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
@@ -144,8 +151,11 @@ feature -- HTTP Methods
do
if attached current_user_name (req) then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
api.delete_node (l_id.integer_value)
if
l_id.is_integer and then
attached api.node (l_id.integer_value) as l_node
then
api.delete_node (l_node)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
else
do_error (req, res, l_id)
@@ -198,19 +208,18 @@ feature {NONE} -- Node
feature -- {NONE} Form data
extract_data_form (req: WSF_REQUEST): CMS_NODE
update_node_from_data_form (req: WSF_REQUEST; a_node: CMS_NODE)
-- Extract request form data and build a object
-- Node
do
create Result.make ("", "", "")
if attached {WSF_STRING} req.form_parameter ("title") as l_title then
Result.set_title (l_title.value)
a_node.set_title (l_title.value)
end
if attached {WSF_STRING} req.form_parameter ("summary") as l_summary then
Result.set_summary (l_summary.value)
a_node.set_summary (l_summary.value)
end
if attached {WSF_STRING} req.form_parameter ("content") as l_content then
Result.set_content (l_content.value)
a_node.set_content (l_content.value)
end
end
@@ -1,7 +1,7 @@
note
description: "Summary description for {NODE_SUMMARY_HANDLER}."
date: "$Date: 2014-12-19 14:17:32 +0100 (ven., 19 déc. 2014) $"
revision: "$Revision: 96402 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NODE_SUMMARY_HANDLER
@@ -118,7 +118,7 @@ feature -- HTTP Methods
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
u_node := extract_data_form (req)
u_node.set_id (l_id.integer_value)
u_node.set_id (l_id.value.to_integer_64)
api.update_node_summary (l_user.id,u_node.id, u_node.summary)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
else
@@ -1,7 +1,7 @@
note
description: "Summary description for {NODE_TITLE_HANDLER}."
date: "$Date: 2014-12-19 14:17:32 +0100 (ven., 19 déc. 2014) $"
revision: "$Revision: 96402 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NODE_TITLE_HANDLER
@@ -118,7 +118,7 @@ feature -- HTTP Methods
if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if l_id.is_integer and then attached {CMS_NODE} api.node (l_id.integer_value) as l_node then
u_node := extract_data_form (req)
u_node.set_id (l_id.integer_value)
u_node.set_id (l_id.value.to_integer_64)
api.update_node_title (l_user.id,u_node.id, u_node.title)
(create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url (""))
else
+37 -6
View File
@@ -1,7 +1,7 @@
note
description: "CMS module that bring support for NODE management."
date: "$Date$"
revision: "$Revision$"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NODE_MODULE
@@ -33,7 +33,6 @@ feature {NONE} -- Initialization
config := a_setup
end
config: CMS_SETUP
-- Node configuration.
@@ -159,10 +158,42 @@ feature -- Handler
do_get_nodes (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: CMS_API)
local
r: CMS_RESPONSE
s: STRING
l_user: CMS_USER
l_node: CMS_NODE
do
create {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE} r.make (req, res, a_api)
r.set_main_content ("Sorry: listing the CMS nodes is not yet implemented.")
r.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet implemented", Void), "highlighted")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_api)
if attached a_api.user_by_name ("foo") as u then
l_user := u
else
create l_user.make ("foo")
l_user.set_password ("foobar#")
l_user.set_email ("test@example.com")
a_api.new_user (l_user)
end
if a_api.nodes_count = 0 then
create l_node.make ({STRING_32} "This is a content", {STRING_32} "And a summary", {STRING_32} "Nice title")
l_node.set_author (l_user)
a_api.new_node (l_node)
end
create s.make_from_string ("<p>Nodes:</p>")
if attached a_api.nodes as lst then
across
lst as ic
loop
s.append ("<li>")
s.append (a_api.html_encoded (ic.item.title))
s.append (" (")
s.append (ic.item.id.out)
s.append (")")
s.append ("</li>%N")
end
end
r.set_main_content (s)
r.add_block (create {CMS_CONTENT_BLOCK}.make ("nodes_warning", Void, "/nodes/ is not yet fully implemented<br/>", Void), "highlighted")
r.execute
end
+47
View File
@@ -0,0 +1,47 @@
note
description : "[
CMS interface to storage
]"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE
inherit
CMS_USER_STORAGE
CMS_NODE_STORAGE
SHARED_LOGGER
feature {NONE} -- Initialization
initialize
do
end
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
feature -- Misc
-- set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: READABLE_STRING_8)
-- -- Save data `a_name:a_value' for type `a_type'
-- deferred
-- end
-- custom_value (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable TABLE_ITERABLE [READABLE_STRING_8, STRING_8]
-- -- Data for name `a_name' and type `a_type'.
-- deferred
-- end
-- custom_value_names_where (a_where_key, a_where_value: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable LIST [READABLE_STRING_8]
-- -- Names where custom value has item `a_where_key' same as `a_where_value' for type `a_type'.
-- deferred
-- end
end
@@ -0,0 +1,18 @@
note
description: "[
Objects that ...
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE_BUILDER
feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE
deferred
end
end
+168
View File
@@ -0,0 +1,168 @@
note
description: "Summary description for {CMS_STORAGE_NULL}."
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_STORAGE_NULL
inherit
CMS_STORAGE
redefine
default_create
select
default_create
end
REFACTORING_HELPER
rename
default_create as default_create_rh
end
feature -- Initialization
default_create
do
create error_handler.make
end
feature -- Access: user
has_user: BOOLEAN
-- Has any user?
do
end
users: LIST [CMS_USER]
do
create {ARRAYED_LIST[CMS_USER]} Result.make (0)
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
do
end
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
do
end
user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
do
end
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
do
end
feature -- User Nodes
user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE]
-- Possible list of nodes where the user identified by `a_id', is a collaborator.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE]
-- Possible list of nodes where the user identified by `a_id', is the author.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
feature -- Change: user
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
do
end
update_user (a_user: CMS_USER)
-- Update user `a_user'.
do
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
do
end
user_roles: LIST [CMS_USER_ROLE]
do
create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0)
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
do
end
feature -- Access: node
nodes_count: INTEGER_64
-- Count of nodes.
do
end
nodes: LIST[CMS_NODE]
-- List of nodes.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of the `a_count' most recent nodes, starting from `a_lower'.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- <Precursor>
do
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
-- Node's author. if any.
do
end
node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER]
-- Possible list of node's collaborator.
do
create {ARRAYED_LIST[CMS_USER]} Result.make (0)
end
feature -- Node
new_node (a_node: CMS_NODE)
-- Add a new node
do
end
delete_node_by_id (a_id: INTEGER_64)
-- <Precursor>
do
end
update_node (a_node: CMS_NODE)
-- <Precursor>
do
end
update_node_title (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- <Precursor>
do
end
update_node_summary (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- <Precursor>
do
end
update_node_content (a_user_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- <Precursor>
do
end
end
@@ -0,0 +1,22 @@
note
description: "[
Objects that ...
]"
author: "$Author: jfiat $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_STORAGE_NULL_BUILDER
inherit
CMS_STORAGE_BUILDER
feature -- Factory
storage (a_setup: CMS_SETUP): detachable CMS_STORAGE_NULL
do
create Result
end
end
+214
View File
@@ -0,0 +1,214 @@
note
description: "Summary description for {CMS_STORAGE_SQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE_SQL
feature -- Error handler
error_handler: ERROR_HANDLER
deferred
end
feature -- Execution
sql_begin_transaction
deferred
end
sql_rollback_transaction
deferred
end
sql_commit_transaction
deferred
end
sql_post_execution
-- Post database execution.
deferred
end
feature -- Operation
check_sql_query_validity (a_sql_statement: READABLE_STRING_8; a_params: detachable STRING_TABLE [detachable ANY])
local
l_sql_params: STRING_TABLE [READABLE_STRING_8]
i,j,n: INTEGER
s: STRING
do
create l_sql_params.make_caseless (0)
from
i := 1
n := a_sql_statement.count
until
i > n
loop
i := a_sql_statement.index_of (':', i)
if i = 0 then
i := n -- exit
else
from
j := i + 1
until
j > n or not (a_sql_statement[j].is_alpha_numeric or a_sql_statement[j] = '_')
loop
j := j + 1
end
s := a_sql_statement.substring (i + 1, j - 1)
l_sql_params.force (s, s)
end
i := i + 1
end
if a_params = Void then
if not l_sql_params.is_empty then
check False end
error_handler.add_custom_error (-1, "invalid query", "missing value for sql parameters")
end
else
across
a_params as ic
loop
if l_sql_params.has (ic.key) then
l_sql_params.remove (ic.key)
else
error_handler.add_custom_error (-1, "useless value", "value for unexpected parameter [" + ic.key + "]")
end
end
across
l_sql_params as ic
loop
error_handler.add_custom_error (-1, "invalid query", "missing value for sql parameter [" + ic.item + "]")
end
end
end
sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
deferred
end
sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
deferred
end
feature -- Access
sql_rows_count: INTEGER
-- Number of rows for last sql execution.
deferred
end
sql_start
-- Set the cursor on first element.
deferred
end
sql_after: BOOLEAN
-- Are there no more items to iterate over?
deferred
end
sql_forth
-- Fetch next row from last sql execution, if any.
deferred
end
sql_item (a_index: INTEGER): detachable ANY
deferred
end
sql_read_integer_64 (a_index: INTEGER): INTEGER_64
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {INTEGER_64} l_item as i then
Result := i
elseif attached {INTEGER_64_REF} l_item as l_value then
Result := l_value.item
else
Result := sql_read_integer_32 (a_index).to_integer_64
end
end
sql_read_integer_32 (a_index: INTEGER): INTEGER_32
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {INTEGER_32} l_item as i then
Result := i
elseif attached {INTEGER_32_REF} l_item as l_value then
Result := l_value.item
else
-- check is_integer_32: False end
end
end
sql_read_string (a_index: INTEGER): detachable STRING
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {READABLE_STRING_8} l_item as l_string then
Result := l_string
elseif attached {BOOLEAN} l_item as l_boolean then
Result := l_boolean.out
elseif attached {BOOLEAN_REF} l_item as l_boolean_ref then
Result := l_boolean_ref.item.out
else
-- check is_string: False end
end
end
sql_read_string_32 (a_index: INTEGER): detachable STRING_32
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
-- FIXME: handle string_32 !
l_item := sql_item (a_index)
if attached {READABLE_STRING_32} l_item as l_string then
Result := l_string
else
if attached sql_read_string (a_index) as s8 then
Result := s8.to_string_32 -- FIXME
end
end
end
sql_read_date_time (a_index: INTEGER): detachable DATE_TIME
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {DATE_TIME} l_item as dt then
Result := dt
else
-- check is_date_time: False end
end
end
sql_read_boolean (a_index: INTEGER): detachable BOOLEAN
-- Retrieved value at `a_index' position in `item'.
local
l_item: like sql_item
do
l_item := sql_item (a_index)
if attached {BOOLEAN} l_item as l_boolean then
Result := l_boolean
elseif attached {BOOLEAN_REF} l_item as l_boolean_ref then
Result := l_boolean_ref.item
else
check is_boolean: False end
end
end
end
@@ -0,0 +1,122 @@
note
description: "Summary description for {CMS_NODE_STORAGE}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_NODE_STORAGE
inherit
SHARED_LOGGER
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access
nodes_count: INTEGER_64
-- Count of nodes.
deferred
end
nodes: LIST [CMS_NODE]
-- List of nodes.
deferred
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
deferred
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- Retrieve node by id `a_id', if any.
require
a_id > 0
deferred
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
-- Node's author. if any.
require
valid_node: a_id >0
deferred
end
feature -- Change: Node
save_node (a_node: CMS_NODE)
-- Create or update `a_node'.
do
if a_node.has_id then
update_node (a_node)
else
new_node (a_node)
end
end
new_node (a_node: CMS_NODE)
-- Save node `a_node'.
require
no_id: not a_node.has_id
valid_user: attached a_node.author as l_author and then l_author.id > 0
deferred
end
delete_node (a_node: CMS_NODE)
-- Delete `a_node'.
do
if a_node.has_id then
delete_node_by_id (a_node.id)
end
end
delete_node_by_id (a_id: INTEGER_64)
-- Remove node by id `a_id'.
require
valid_node_id: a_id > 0
deferred
end
update_node (a_node: CMS_NODE)
-- Update node content `a_node'.
-- The user `a_id' is an existing or new collaborator.
require
has_id: a_node.has_id
has_author: attached a_node.author as l_author and then l_author.has_id
deferred
end
update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- Update node title to `a_title', node identified by id `a_node_id'.
-- The user `a_user_id' is an existing or new collaborator.
require
valid_node_id: a_node_id > 0
valid_user_id: a_user_id > 0
deferred
end
update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- Update node summary to `a_summary', node identified by id `a_node_id'.
-- The user `a_user_id' is an existing or new collaborator.
require
valid_id: a_node_id > 0
valid_user_id: a_user_id > 0
deferred
end
update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- Update node content to `a_content', node identified by id `a_node_id'.
-- The user `a_user_id' is an existing or new collaborator.
require
valid_id: a_node_id > 0
valid_user_id: a_user_id > 0
deferred
end
end
@@ -0,0 +1,339 @@
note
description: "Summary description for {CMS_NODE_STORAGE_SQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_NODE_STORAGE_SQL
inherit
CMS_NODE_STORAGE
CMS_STORAGE_SQL
REFACTORING_HELPER
SHARED_LOGGER
feature -- Access
nodes_count: INTEGER_64
-- Number of items nodes.
do
error_handler.reset
log.write_information (generator + ".nodes_count")
sql_query (select_nodes_count, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
sql_post_execution
end
nodes: LIST [CMS_NODE]
-- List of nodes.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
log.write_information (generator + ".nodes")
from
sql_query (select_nodes, Void)
sql_post_execution
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
sql_post_execution
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of recent `a_count' nodes with an offset of `lower'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
log.write_information (generator + ".nodes")
from
create l_parameters.make (2)
l_parameters.put (a_count, "rows")
l_parameters.put (a_lower, "offset")
sql_query (select_recent_nodes, l_parameters)
sql_post_execution
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
sql_post_execution
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- Retrieve node by id `a_id', if any.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
log.write_information (generator + ".node")
create l_parameters.make (1)
l_parameters.put (a_id,"id")
sql_query (select_node_by_id, l_parameters)
if sql_rows_count = 1 then
Result := fetch_node
end
sql_post_execution
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
-- Node's author for the given node id.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
log.write_information (generator + ".node_author")
create l_parameters.make (1)
l_parameters.put (a_id, "node_id")
sql_query (select_node_author, l_parameters)
if sql_rows_count >= 1 then
Result := fetch_author
end
sql_post_execution
end
last_inserted_node_id: INTEGER_64
-- Last insert node id.
do
error_handler.reset
log.write_information (generator + ".last_inserted_node_id")
sql_query (Sql_last_insert_node_id, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
sql_post_execution
end
feature -- Change: Node
new_node (a_node: CMS_NODE)
-- Save node `a_node'.
local
l_parameters: STRING_TABLE [detachable ANY]
do
-- New node
error_handler.reset
log.write_information (generator + ".new_node")
create l_parameters.make (7)
l_parameters.put (a_node.title, "title")
l_parameters.put (a_node.summary, "summary")
l_parameters.put (a_node.content, "content")
l_parameters.put (a_node.publication_date, "publish")
l_parameters.put (a_node.creation_date, "created")
l_parameters.put (a_node.modification_date, "changed")
if
attached a_node.author as l_author and then
l_author.id > 0
then
l_parameters.put (l_author.id, "author")
else
l_parameters.put (0, "author")
end
sql_change (sql_insert_node, l_parameters)
sql_post_execution
if not error_handler.has_error then
a_node.set_id (last_inserted_node_id)
sql_post_execution
end
end
delete_node_by_id (a_id: INTEGER_64)
-- Remove node by id `a_id'.
local
l_parameters: STRING_TABLE [ANY]
do
log.write_information (generator + ".delete_node")
error_handler.reset
create l_parameters.make (1)
l_parameters.put (a_id, "id")
sql_change (sql_delete_node, l_parameters)
sql_post_execution
end
update_node (a_node: CMS_NODE)
-- Update node content `a_node'.
local
l_parameters: STRING_TABLE [detachable ANY]
now: DATE_TIME
do
create now.make_now_utc
error_handler.reset
log.write_information (generator + ".update_node")
create l_parameters.make (7)
l_parameters.put (a_node.title, "title")
l_parameters.put (a_node.summary, "summary")
l_parameters.put (a_node.content, "content")
l_parameters.put (a_node.publication_date, "publish")
l_parameters.put (now, "changed")
l_parameters.put (a_node.id, "id")
if attached a_node.author as l_author then
l_parameters.put (l_author.id, "author")
else
l_parameters.put (0, "author")
end
sql_change (sql_update_node, l_parameters)
sql_post_execution
if not error_handler.has_error then
a_node.set_modification_date (now)
end
end
update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
-- FIXME: unused a_user_id !
error_handler.reset
log.write_information (generator + ".update_node_title")
create l_parameters.make (3)
l_parameters.put (a_title, "title")
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
l_parameters.put (a_node_id, "nid")
sql_change (sql_update_node_title, l_parameters)
sql_post_execution
end
update_node_summary (a_user_id: Like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
-- FIXME: unused a_user_id !
error_handler.reset
log.write_information (generator + ".update_node_summary")
create l_parameters.make (3)
l_parameters.put (a_summary, "summary")
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
l_parameters.put (a_node_id, "nid")
sql_change (sql_update_node_summary, l_parameters)
sql_post_execution
end
update_node_content (a_user_id: Like {CMS_USER}.id;a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
-- FIXME: unused a_user_id !
error_handler.reset
log.write_information (generator + ".update_node_content")
create l_parameters.make (3)
l_parameters.put (a_content, "content")
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
l_parameters.put (a_node_id, "nid")
sql_change (sql_update_node_content, l_parameters)
sql_post_execution
end
feature {NONE} -- Queries
Select_nodes_count: STRING = "select count(*) from Nodes;"
Select_nodes: STRING = "select * from Nodes;"
-- SQL Query to retrieve all nodes.
Select_node_by_id: STRING = "select * from Nodes where nid =:nid order by nid desc, publish desc;"
Select_recent_nodes: STRING = "select * from Nodes order by nid desc, publish desc LIMIT :rows OFFSET :offset ;"
SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publish, created, changed, author) values (:title, :summary, :content, :publish, :created, :changed, :author);"
-- SQL Insert to add a new node.
SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publish=:publish, changed=:changed, version = version + 1, author=:author where nid=:nid;"
-- SQL node.
SQL_Delete_node: STRING = "delete from nodes where nid=:nid;"
Sql_update_node_author: STRING = "update nodes SET author=:author where nid=:nid;"
SQL_Update_node_title: STRING ="update nodes SET title=:title, changed=:changed, version = version + 1 where nid=:nid;"
-- SQL update node title.
SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, changed=:changed, version = version + 1 where nid=:nid;"
-- SQL update node summary.
SQL_Update_node_content: STRING ="update nodes SET content=:content, changed=:changed, version = version + 1 where nid=:nid;"
-- SQL node content.
Sql_last_insert_node_id: STRING = "SELECT MAX(nid) from nodes;"
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT * FROM Nodes INNER JOIN users ON nodes.author=users.uid and users.uid = :uid;"
Select_node_author: STRING = "SELECT * FROM Users INNER JOIN nodes ON nodes.author=users.uid and nodes.nid =:nid;"
feature {NONE} -- Implementation
fetch_node: CMS_NODE
do
create Result.make ("", "", "")
if attached sql_read_integer_64 (1) as l_id then
Result.set_id (l_id)
end
if attached sql_read_string_32 (4) as l_title then
Result.set_title (l_title)
end
if attached sql_read_string_32 (5) as l_summary then
Result.set_summary (l_summary)
end
if attached sql_read_string (6) as l_content then
Result.set_content (l_content)
end
if attached sql_read_date_time (8) as l_publication_date then
Result.set_publication_date (l_publication_date)
end
if attached sql_read_date_time (9) as l_creation_date then
Result.set_creation_date (l_creation_date)
end
if attached sql_read_date_time (10) as l_modif_date then
Result.set_modification_date (l_modif_date)
end
if attached sql_read_integer_64 (7) as l_author_id then
-- access to API ...
end
end
fetch_author: detachable CMS_USER
do
if attached sql_read_string_32 (2) as l_name and then not l_name.is_whitespace then
create Result.make (l_name)
if attached sql_read_integer_32 (1) as l_id then
Result.set_id (l_id)
end
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
else
check expected_valid_user: False end
end
end
end
@@ -0,0 +1,139 @@
note
description: "Summary description for {CMS_USER_STORAGE}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_USER_STORAGE
inherit
SHARED_LOGGER
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access
has_user: BOOLEAN
-- Has any user?
deferred
end
users: LIST [CMS_USER]
-- List of users.
deferred
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
-- User with id `a_id', if any.
require
a_id > 0
deferred
ensure
same_id: Result /= Void implies Result.id = a_id
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
end
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
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
is_valid_credential (a_u, a_p: READABLE_STRING_32): BOOLEAN
-- Does account with username `a_username' and password `a_password' exist?
deferred
end
feature -- Change: user
save_user (a_user: CMS_USER)
-- Create or update `a_user'.
do
if a_user.has_id then
update_user (a_user)
else
new_user (a_user)
end
end
new_user (a_user: CMS_USER)
-- New user `a_user'.
require
no_id: not a_user.has_id
deferred
end
update_user (a_user: CMS_USER)
-- Save user `a_user'.
require
has_id: a_user.has_id
deferred
end
feature -- Access: roles and permissions
user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN
-- Anonymous or user `u' has permission for `s' ?
--| `s' could be "create page",
do
-- if s = Void then
-- Result := True
-- elseif u = Void then
---- Result := user_role_has_permission (anonymous_user_role, s)
-- else
-- Result := user_role_has_permission (authenticated_user_role, s)
-- if not Result and attached u.roles as l_roles then
-- across
-- l_roles as r
-- until
-- Result
-- loop
-- if attached user_role_by_id (r.item) as ur then
-- Result := user_role_has_permission (ur, s)
-- end
-- end
-- end
-- end
end
user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN
do
Result := a_role.has_permission (s)
end
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
-- User role by id `a_id', if any.
deferred
end
user_roles: LIST [CMS_USER_ROLE]
-- Possible list of user roles.
deferred
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
-- Save user role `a_user_role'
deferred
end
end
@@ -0,0 +1,331 @@
note
description: "Summary description for {CMS_USER_STORAGE_SQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_USER_STORAGE_SQL
inherit
CMS_USER_STORAGE
CMS_STORAGE_SQL
REFACTORING_HELPER
SHARED_LOGGER
feature -- Access: user
has_user: BOOLEAN
-- Has any user?
do
Result := user_count > 0
end
user_count: INTEGER
-- Number of items users.
do
error_handler.reset
log.write_information (generator + ".user_count")
sql_query (select_users_count, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_32 (1)
end
sql_post_execution
end
users: LIST [CMS_USER]
do
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
error_handler.reset
log.write_information (generator + ".all_users")
from
sql_query (select_users, Void)
sql_post_execution
sql_start
until
sql_after
loop
if attached fetch_user as l_user then
Result.force (l_user)
end
sql_forth
end
sql_post_execution
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
-- User for the given id `a_id', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
log.write_information (generator + ".user")
create l_parameters.make (1)
l_parameters.put (a_id, "uid")
sql_query (select_user_by_id, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
sql_post_execution
end
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
log.write_information (generator + ".user_by_name")
create l_parameters.make (1)
l_parameters.put (a_name, "name")
sql_query (select_user_by_name, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
sql_post_execution
end
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
log.write_information (generator + ".user_by_email")
create l_parameters.make (1)
l_parameters.put (a_email, "email")
sql_query (select_user_by_email, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
sql_post_execution
end
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
local
l_security: SECURITY_PROVIDER
do
if attached user_salt (l_auth_login) as l_hash then
if attached user_by_name (l_auth_login) as l_user then
create l_security
if
attached l_user.hashed_password as l_hashed_password and then
l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_hashed_password)
then
Result := True
else
log.write_information (generator + ".is_valid_credential User: wrong username or password" )
end
else
log.write_information (generator + ".is_valid_credential User:" + l_auth_login + "does not exist" )
end
end
end
feature -- Change: user
new_user (a_user: CMS_USER)
-- Add a new 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
then
sql_begin_transaction
create l_security
l_password_salt := l_security.salt
l_password_hash := l_security.password_hash (l_password, l_password_salt)
log.write_information (generator + ".new_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")
sql_change (sql_insert_user, l_parameters)
sql_post_execution
if not error_handler.has_error then
a_user.set_id (last_inserted_user_id)
sql_post_execution
end
sql_commit_transaction
else
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
update_user (a_user: CMS_USER)
-- Save user `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
l_password_salt, l_password_hash: detachable READABLE_STRING_8
l_security: SECURITY_PROVIDER
do
error_handler.reset
if attached a_user.password as l_password then
-- New password!
create l_security
l_password_salt := l_security.salt
l_password_hash := l_security.password_hash (l_password, l_password_salt)
else
-- Existing hashed password
l_password_hash := a_user.hashed_password
l_password_salt := user_salt (a_user.name)
end
if
l_password_hash /= Void and l_password_salt /= Void and
attached a_user.email as l_email
then
log.write_information (generator + ".update_user")
create l_parameters.make (6)
l_parameters.put (a_user.id, "uid")
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, "changed")
sql_change (sql_update_user, l_parameters)
sql_post_execution
else
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
do
to_implement (generator + ".user_role_by_id")
end
user_roles: LIST [CMS_USER_ROLE]
do
to_implement (generator + ".user_roles")
create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0)
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
do
to_implement (generator + ".save_user_role")
end
feature {NONE} -- Implementation
user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8
-- User salt for the given user `a_username', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
log.write_information (generator + ".user_salt")
create l_parameters.make (1)
l_parameters.put (a_username, "name")
sql_query (select_salt_by_username, l_parameters)
if sql_rows_count = 1 then
if attached sql_read_string (1) as l_salt then
Result := l_salt
end
end
sql_post_execution
end
fetch_user: detachable CMS_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_32 (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
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
else
check expected_valid_user: False end
end
end
last_inserted_user_id: INTEGER_64
-- Last insert user id.
do
error_handler.reset
log.write_information (generator + ".last_inserted_user_id")
sql_query (Sql_last_insert_user_id, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
sql_post_execution
end
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.
Select_user_by_id: STRING = "select * from Users where uid =:uid;"
-- Retrieve user by id if exists.
Select_user_by_name: STRING = "select * from Users where name =:name;"
-- Retrieve user by name if exists.
Select_user_by_email: STRING = "select * from Users where email =:email;"
-- Retrieve user by email if exists.
Select_salt_by_username: STRING = "select salt from Users where name =:name;"
-- Retrieve salt by username if exists.
Sql_Insert_user: STRING = "insert into users (name, password, salt, email, created) values (:name, :password, :salt, :email, :created);"
-- SQL Insert to add a new node.
sql_update_user: STRING = "update users SET name=:name, password=:password, salt=:salt, email=:email WHERE uid=:uid;"
end
+159
View File
@@ -0,0 +1,159 @@
note
description: "Provides security routine helpers"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
SECURITY_PROVIDER
inherit
REFACTORING_HELPER
feature -- Access
token: STRING
-- Cryptographic random base 64 string.
do
Result := salt_with_size (5)
-- Remove trailing equal sign
Result.keep_head (Result.count - 1)
end
salt: STRING
-- Cryptographic random number of 16 bytes.
do
Result := salt_with_size (16)
end
password: STRING
-- Cryptographic random password of 10 bytes.
do
Result := salt_with_size (10)
-- Remove trailing equal signs
Result.keep_head (Result.count - 2)
end
password_hash (a_password: READABLE_STRING_GENERAL; a_salt: STRING): STRING
-- Password hash based on password `a_password' and salt value `a_salt'.
local
utf: UTF_CONVERTER
s: STRING
do
create s.make (a_password.count + a_salt.count)
utf.utf_32_string_into_utf_8_string_8 (a_password, s)
s.append (a_salt)
Result := sha1_string (s)
end
feature {NONE} -- Implementation
salt_with_size (a_val: INTEGER): STRING
-- Return a salt with size `a_val'.
local
l_salt: SALT_XOR_SHIFT_64_GENERATOR
l_array: ARRAY [INTEGER_8]
i: INTEGER
do
create l_salt.make (a_val)
create l_array.make_empty
i := 1
across
l_salt.new_sequence as c
loop
l_array.force (c.item.as_integer_8, i)
i := i + 1
end
Result := encode_base_64 (l_array)
end
sha1_string (a_str: STRING): STRING
-- SHA1 diggest of `a_str'.
do
sha1.update_from_string (a_str)
Result := sha1.digest_as_string
sha1.reset
end
sha1: SHA1
-- Create a SHA1 object.
once
create Result.make
end
feature -- Encoding
encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8
-- Encodes a byte array into a STRING doing base64 encoding.
local
l_output: SPECIAL [INTEGER_8]
l_remaining: INTEGER
i, ptr: INTEGER
char: CHARACTER
do
to_implement ("Check existing code to do that!!!.")
create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4)
l_remaining := bytes.count
from
i := 0
ptr := 0
until
l_remaining <= 3
loop
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF))
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3))
ptr := ptr + 1
l_output [ptr] := encode_value (bytes [i + 2] & 0x3F)
ptr := ptr + 1
l_remaining := l_remaining - 3
i := i + 3
end
-- encode when exactly 1 element (left) to encode
char := '='
if l_remaining = 1 then
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4)
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
end
-- encode when exactly 2 elements (left) to encode
if l_remaining = 2 then
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF));
ptr := ptr + 1
l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2);
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
end
Result := ""
across
l_output as elem
loop
Result.append_character (elem.item.to_character_8)
end
end
base64_map: SPECIAL [CHARACTER_8]
-- Table for Base64 encoding.
once
Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area
end
encode_value (i: INTEGER_8): INTEGER_8
-- Encode `i'.
do
Result := base64_map [i & 0x3F].code.as_integer_8
end
end
+71 -62
View File
@@ -1,15 +1,26 @@
note
description: "API for a CMS"
date: "$Date: 2015-01-14 16:13:47 +0100 (mer., 14 janv. 2015) $"
revision: "$Revision: 96454 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
CMS_API
inherit
ANY
REFACTORING_HELPER
SHARED_HTML_ENCODER
export
{NONE} all
end
SHARED_WSF_PERCENT_ENCODER
export
{NONE} all
end
create
make
@@ -31,46 +42,13 @@ feature -- Initialize
initialize
-- Initialize the persitent layer.
local
l_database: DATABASE_CONNECTION
retried: BOOLEAN
l_message: STRING
do
if not retried then
to_implement ("Refactor database setup")
if attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (setup.layout.application_config_path) as l_database_config then
create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string)
create {CMS_STORAGE_MYSQL} storage.make (l_database)
else
create {DATABASE_CONNECTION_NULL} l_database.make_common
create {CMS_STORAGE_NULL} storage
end
to_implement ("Refactor database setup")
if attached setup.storage (error_handler) as l_storage then
storage := l_storage
else
to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.")
-- error hanling.
create {DATABASE_CONNECTION_NULL} l_database.make_common
create {CMS_STORAGE_NULL} storage
create l_message.make (1024)
if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then
if attached l_exception.description as l_description then
l_message.append (l_description.as_string_32)
l_message.append ("%N%N")
elseif attached l_exception.trace as l_trace then
l_message.append (l_trace)
l_message.append ("%N%N")
else
l_message.append (l_exception.out)
l_message.append ("%N%N")
end
else
l_message.append ("The application crash without available information")
l_message.append ("%N%N")
end
error_handler.add_custom_error (0, " Database Connection ", l_message)
end
rescue
retried := True
retry
end
feature -- Access: Error
@@ -110,16 +88,18 @@ feature -- Status Report
feature -- Access: Node
nodes: LIST[CMS_NODE]
-- List of nodes.
nodes_count: INTEGER_64
do
debug ("refactor_fixme")
fixme ("Implementation")
end
Result := storage.recent_nodes (0, 10)
Result := storage.nodes_count
end
recent_nodes (a_offset, a_rows: INTEGER): LIST[CMS_NODE]
nodes: LIST [CMS_NODE]
-- List of nodes.
do
Result := storage.nodes
end
recent_nodes (a_offset, a_rows: INTEGER): LIST [CMS_NODE]
-- List of the `a_rows' most recent nodes starting from `a_offset'.
do
Result := storage.recent_nodes (a_offset, a_rows)
@@ -131,54 +111,58 @@ feature -- Access: Node
debug ("refactor_fixme")
fixme ("Check preconditions")
end
Result := storage.node (a_id)
Result := storage.node_by_id (a_id)
end
feature -- Change: Node
new_node (a_node: CMS_NODE)
-- Add a new node `a_node'
require
no_id: not a_node.has_id
do
storage.save_node (a_node)
storage.new_node (a_node)
end
delete_node (a_id: INTEGER_64)
-- Delete a node identified by `a_id', if any.
delete_node (a_node: CMS_NODE)
-- Delete `a_node'.
do
storage.delete_node (a_id)
if a_node.has_id then
storage.delete_node (a_node)
end
end
update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE)
-- Update node by id `a_id' with `a_node' data.
update_node (a_node: CMS_NODE)
-- Update node `a_node' data.
do
storage.update_node (a_id,a_node)
storage.update_node (a_node)
end
update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
update_node_title (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- Update node title, with user identified by `a_id', with node id `a_node_id' and a new title `a_title'.
do
debug ("refactor_fixme")
fixme ("Check preconditions")
end
storage.update_node_title (a_id,a_node_id,a_title)
storage.update_node_title (a_user_id, a_node_id, a_title)
end
update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- Update node summary, with user identified by `a_id', with node id `a_node_id' and a new summary `a_summary'.
update_node_summary (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- Update node summary, with user identified by `a_user_id', with node id `a_node_id' and a new summary `a_summary'.
do
debug ("refactor_fixme")
fixme ("Check preconditions")
end
storage.update_node_summary (a_id,a_node_id, a_summary)
storage.update_node_summary (a_user_id, a_node_id, a_summary)
end
update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- Update node content, with user identified by `a_id', with node id `a_node_id' and a new content `a_content'.
update_node_content (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- Update node content, with user identified by `a_user_id', with node id `a_node_id' and a new content `a_content'.
do
debug ("refactor_fixme")
fixme ("Check preconditions")
end
storage.update_node_content (a_id,a_node_id, a_content)
storage.update_node_content (a_user_id, a_node_id, a_content)
end
@@ -194,12 +178,15 @@ feature -- Change User
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
require
no_id: not a_user.has_id
no_hashed_password: a_user.hashed_password = Void
do
if
attached a_user.password as l_password and then
attached a_user.email as l_email
then
storage.save_user (a_user)
storage.new_user (a_user)
else
debug ("refactor_fixme")
fixme ("Add error")
@@ -207,6 +194,28 @@ feature -- Change User
end
end
update_user (a_user: CMS_USER)
-- Update user `a_user'.
require
has_id: a_user.has_id
do
storage.update_user (a_user)
end
feature -- Helpers
html_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded for html output.
do
Result := html_encoder.general_encoded_string (a_string)
end
percent_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded with percent encoding, mainly used for url.
do
Result := percent_encoder.percent_encoded_string (a_string)
end
feature -- Layout
module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
@@ -1,7 +1,7 @@
note
description: "Summary description for {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE}."
date: "$Date$"
revision: "$Revision$"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
@@ -32,7 +32,9 @@ feature -- Execution
do
set_title ("Not Implemented")
set_page_title (Void)
set_main_content (request.percent_encoded_path_info + " is not implemented!")
if main_content = Void then
set_main_content (request.percent_encoded_path_info + " is not implemented!")
end
end
end