Added basic webapi system to ROC CMS system.
Added sql_delete routine to replace sql_modify with "DELETE FROM .." sql statement.
Fixed filter setup when a module has more than one filter.
Fixed filter setup for site,admin and webapi modes.
Added CMS_AUTH_FILTER, and check if user is already authenticated, then skip following auth filters.
Added specific webapi handler classes for root, user, access token, ...
Added user profile system to the core module.
Moved /user/{uid} from auth module to core module.
Added possibility to add html before and after a cms form. (useful to add a form before or after, as nested form are forbidden).
Now theme can be installed using roc install command.
This commit is contained in:
@@ -103,6 +103,24 @@ feature {NONE} -- Initialization
|
||||
site_theme_name := text_item_or_default ("site.theme", "default")
|
||||
set_theme (site_theme_name)
|
||||
|
||||
|
||||
-- Webapi
|
||||
webapi_enabled := string_8_item_or_default ("webapi.mode", "off").is_case_insensitive_equal_general ("on")
|
||||
|
||||
l_url := string_8_item ("webapi.base_path")
|
||||
if l_url /= Void and then not l_url.is_empty then
|
||||
if l_url [l_url.count] = '/' then
|
||||
l_url := l_url.substring (1, l_url.count - 1)
|
||||
end
|
||||
if l_url [1] /= '/' then
|
||||
l_url := "/" + l_url
|
||||
end
|
||||
create webapi_base_path.make_from_string (l_url)
|
||||
else
|
||||
create webapi_base_path.make_from_string (default_webapi_base_path)
|
||||
end
|
||||
|
||||
|
||||
-- Administration
|
||||
l_url := string_8_item ("administration.base_path")
|
||||
if l_url /= Void and then not l_url.is_empty then
|
||||
@@ -117,6 +135,7 @@ feature {NONE} -- Initialization
|
||||
create administration_base_path.make_from_string (default_administration_base_path)
|
||||
end
|
||||
administration_theme_name := text_item_or_default ("administration.theme", theme_name) -- TODO: Default to builtin theme?
|
||||
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
@@ -314,11 +333,19 @@ feature -- Access: Site
|
||||
-- Optional path defining the front page.
|
||||
-- By default "" or "/".
|
||||
|
||||
webapi_enabled: BOOLEAN
|
||||
-- Is WebAPI enabled?
|
||||
|
||||
webapi_base_path: IMMUTABLE_STRING_8
|
||||
-- Web API base url, default=`default_webapi_base_path`.
|
||||
|
||||
administration_base_path: IMMUTABLE_STRING_8
|
||||
-- Administration base url, default=`default_administration_base_path`.
|
||||
|
||||
feature {NONE} -- Constants
|
||||
|
||||
default_webapi_base_path: STRING = "/api"
|
||||
|
||||
default_administration_base_path: STRING = "/admin"
|
||||
|
||||
feature -- Settings
|
||||
@@ -338,6 +365,12 @@ feature -- Settings
|
||||
end
|
||||
end
|
||||
|
||||
set_webapi_mode
|
||||
-- Switch to webapi mode.
|
||||
do
|
||||
set_site_mode
|
||||
end
|
||||
|
||||
set_administration_mode
|
||||
-- Switch to administration mode.
|
||||
--| - Change theme
|
||||
|
||||
@@ -36,9 +36,13 @@ feature -- Hook
|
||||
if attached {CMS_HOOK_RESPONSE_ALTER} Current as h_resp then
|
||||
a_hooks.subscribe_to_response_alter_hook (h_resp)
|
||||
end
|
||||
if attached {CMS_HOOK_WEBAPI_RESPONSE_ALTER} Current as h_resp then
|
||||
a_hooks.subscribe_to_webapi_response_alter_hook (h_resp)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -60,6 +60,28 @@ feature -- Hook: response
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hook: webapi response
|
||||
|
||||
subscribe_to_webapi_response_alter_hook (h: CMS_HOOK_WEBAPI_RESPONSE_ALTER)
|
||||
-- Add `h' as subscriber of response alter hooks CMS_HOOK_WEBAPI_RESPONSE_ALTER.
|
||||
do
|
||||
subscribe_to_hook (h, {CMS_HOOK_WEBAPI_RESPONSE_ALTER})
|
||||
end
|
||||
|
||||
invoke_webapi_response_alter (a_response: WEBAPI_RESPONSE)
|
||||
-- Invoke response alter hook for response `a_response'.
|
||||
do
|
||||
if attached subscribers ({CMS_HOOK_WEBAPI_RESPONSE_ALTER}) as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if attached {CMS_HOOK_WEBAPI_RESPONSE_ALTER} ic.item as h then
|
||||
h.webapi_response_alter (a_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hook: menu_system_alter
|
||||
|
||||
subscribe_to_menu_system_alter_hook (h: CMS_HOOK_MENU_SYSTEM_ALTER)
|
||||
@@ -110,7 +132,7 @@ feature -- Hook: form_alter
|
||||
-- Add `h' as subscriber of form alter hooks CMS_HOOK_FORM_ALTER,
|
||||
-- and response `a_response'.
|
||||
do
|
||||
subscribe_to_hook (h, {CMS_HOOK_MENU_ALTER})
|
||||
subscribe_to_hook (h, {CMS_HOOK_FORM_ALTER})
|
||||
end
|
||||
|
||||
invoke_form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_response: CMS_RESPONSE)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "[
|
||||
Hook providing a way to alter a webapi response.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_HOOK_WEBAPI_RESPONSE_ALTER
|
||||
|
||||
inherit
|
||||
CMS_HOOK
|
||||
|
||||
feature -- Hook
|
||||
|
||||
webapi_response_alter (a_response: WEBAPI_RESPONSE)
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -10,11 +10,68 @@ inherit
|
||||
WSF_FORM
|
||||
rename
|
||||
process as process_form
|
||||
redefine
|
||||
append_to_html
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
before_widgets: detachable ARRAYED_LIST [WSF_WIDGET]
|
||||
-- Optional widget before the Current form.
|
||||
|
||||
after_widgets: detachable ARRAYED_LIST [WSF_WIDGET]
|
||||
-- Optional widget after the Current form.
|
||||
|
||||
feature -- Element change
|
||||
|
||||
put_widget_before_form (w: WSF_WIDGET)
|
||||
local
|
||||
lst: like before_widgets
|
||||
do
|
||||
lst := before_widgets
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
before_widgets := lst
|
||||
end
|
||||
lst.extend (w)
|
||||
end
|
||||
|
||||
put_widget_after_form (w: WSF_WIDGET)
|
||||
local
|
||||
lst: like after_widgets
|
||||
do
|
||||
lst := after_widgets
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
after_widgets := lst
|
||||
end
|
||||
lst.extend (w)
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
append_to_html (a_theme: WSF_THEME; a_html: STRING_8)
|
||||
do
|
||||
if attached before_widgets as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
ic.item.append_to_html (a_theme, a_html)
|
||||
end
|
||||
end
|
||||
Precursor (a_theme, a_html)
|
||||
if attached after_widgets as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
ic.item.append_to_html (a_theme, a_html)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
prepare (a_response: CMS_RESPONSE)
|
||||
@@ -40,6 +97,6 @@ feature -- Basic operation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -10,10 +10,17 @@ inherit
|
||||
CMS_MODULE
|
||||
redefine
|
||||
initialize,
|
||||
setup_hooks,
|
||||
install,
|
||||
permissions
|
||||
end
|
||||
|
||||
CMS_WITH_WEBAPI
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
|
||||
CMS_HOOK_FORM_ALTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -41,10 +48,16 @@ feature {CMS_API} -- Module Initialization
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
install (a_api: CMS_API)
|
||||
local
|
||||
l_parent_loc: PATH
|
||||
do
|
||||
-- Schema
|
||||
if attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name + ".sql")), Void)
|
||||
l_parent_loc := a_api.module_resource_location (Current, create {PATH}.make_from_string ("scripts"))
|
||||
l_sql_storage.sql_execute_file_script (l_parent_loc.extended (name + ".sql"), Void)
|
||||
if not l_sql_storage.has_error then
|
||||
l_sql_storage.sql_execute_file_script (l_parent_loc.extended ("user_profile.sql"), Void)
|
||||
end
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
|
||||
@@ -81,6 +94,7 @@ feature {CMS_API} -- Module management
|
||||
--! external configuration file?
|
||||
--! at the moment we only have 1 admin to the whole site.
|
||||
--! is that ok?
|
||||
|
||||
l_anonymous_role.add_permission ("view any page")
|
||||
a_api.user_api.save_user_role (l_anonymous_role)
|
||||
|
||||
@@ -101,6 +115,7 @@ feature -- Router
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
a_router.handle ("/user/{uid}", create {CMS_USER_HANDLER}.make (a_api), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Security
|
||||
@@ -117,6 +132,54 @@ feature -- Security
|
||||
Result.force ("edit path_alias")
|
||||
end
|
||||
|
||||
feature {CMS_EXECUTION} -- Administration
|
||||
|
||||
webapi: CMS_CORE_MODULE_WEBAPI
|
||||
do
|
||||
create Result.make (Current)
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
do
|
||||
a_hooks.subscribe_to_form_alter_hook (Current)
|
||||
end
|
||||
|
||||
feature -- Hook
|
||||
|
||||
form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_response: CMS_RESPONSE)
|
||||
-- Hook execution on form `a_form' and its associated data `a_form_data',
|
||||
-- for related response `a_response'.
|
||||
local
|
||||
fset: WSF_FORM_FIELD_SET
|
||||
tf: WSF_FORM_TEXT_INPUT
|
||||
do
|
||||
if
|
||||
attached a_form.id as fid and then
|
||||
fid.same_string ("roccms-user-view")
|
||||
then
|
||||
if
|
||||
attached a_response.user as u and then
|
||||
attached a_response.api.user_api as l_user_profile_api and then
|
||||
attached l_user_profile_api.user_profile (u) as l_profile and then
|
||||
not l_profile.is_empty
|
||||
then
|
||||
create fset.make
|
||||
fset.set_legend ("User-Profile")
|
||||
a_form.extend (fset)
|
||||
across
|
||||
l_profile as ic
|
||||
loop
|
||||
create tf.make_with_text (ic.key.to_string_32, ic.item)
|
||||
tf.set_label (ic.key.to_string_32)
|
||||
a_form.extend (tf)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
@@ -8,12 +8,34 @@ class
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
CMS_USER_PROFILE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor {CMS_MODULE_API}
|
||||
Precursor {CMS_USER_PROFILE_API}
|
||||
user_storage := storage
|
||||
end
|
||||
|
||||
feature -- Storage
|
||||
|
||||
user_storage: CMS_USER_STORAGE_I
|
||||
-- User storage.
|
||||
|
||||
feature -- Validation
|
||||
|
||||
is_valid_username (a_name: READABLE_STRING_32): BOOLEAN
|
||||
@@ -87,43 +109,53 @@ feature -- Access: user
|
||||
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
|
||||
-- User by id `a_id', if any.
|
||||
do
|
||||
Result := storage.user_by_id (a_id)
|
||||
Result := user_storage.user_by_id (a_id)
|
||||
end
|
||||
|
||||
user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by name `a_user_name', if any.
|
||||
do
|
||||
Result := storage.user_by_name (a_username)
|
||||
Result := user_storage.user_by_name (a_username)
|
||||
end
|
||||
|
||||
user_by_id_or_name (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by id or name `a_uid`, if any.
|
||||
do
|
||||
if a_uid.is_integer_64 then
|
||||
Result := user_by_id (a_uid.to_integer_64)
|
||||
else
|
||||
Result := user_by_name (a_uid)
|
||||
end
|
||||
end
|
||||
|
||||
user_by_email (a_email: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by email `a_email', if any.
|
||||
do
|
||||
Result := storage.user_by_email (a_email)
|
||||
Result := user_storage.user_by_email (a_email)
|
||||
end
|
||||
|
||||
user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by activation token `a_token'.
|
||||
do
|
||||
Result := storage.user_by_activation_token (a_token)
|
||||
Result := user_storage.user_by_activation_token (a_token)
|
||||
end
|
||||
|
||||
user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by password token `a_token'.
|
||||
do
|
||||
Result := storage.user_by_password_token (a_token)
|
||||
Result := user_storage.user_by_password_token (a_token)
|
||||
end
|
||||
|
||||
users_count: INTEGER
|
||||
-- Number of users.
|
||||
do
|
||||
Result := storage.users_count
|
||||
Result := user_storage.users_count
|
||||
end
|
||||
|
||||
recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_USER]
|
||||
-- List of the `a_rows' most recent users starting from `a_offset'.
|
||||
do
|
||||
Result := storage.recent_users (params.offset.to_integer_32, params.size.to_integer_32)
|
||||
Result := user_storage.recent_users (params.offset.to_integer_32, params.size.to_integer_32)
|
||||
end
|
||||
|
||||
admin_user: detachable CMS_USER
|
||||
@@ -149,8 +181,8 @@ feature -- Change User
|
||||
if
|
||||
attached a_user.email as l_email
|
||||
then
|
||||
storage.new_user (a_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.new_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
else
|
||||
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
|
||||
end
|
||||
@@ -164,8 +196,8 @@ feature -- Change User
|
||||
user_by_name (a_new_username) = Void
|
||||
do
|
||||
reset_error
|
||||
storage.update_username (a_user, a_new_username)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.update_username (a_user, a_new_username)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
update_user (a_user: CMS_USER)
|
||||
@@ -174,8 +206,8 @@ feature -- Change User
|
||||
has_id: a_user.has_id
|
||||
do
|
||||
reset_error
|
||||
storage.update_user (a_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.update_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
delete_user (a_user: CMS_USER)
|
||||
@@ -184,8 +216,8 @@ feature -- Change User
|
||||
has_id: a_user.has_id
|
||||
do
|
||||
reset_error
|
||||
storage.delete_user (a_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.delete_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
@@ -193,7 +225,7 @@ feature -- Status report
|
||||
is_valid_credential (a_auth_login, a_auth_password: READABLE_STRING_32): BOOLEAN
|
||||
-- Is the credentials `a_auth_login' and `a_auth_password' valid?
|
||||
do
|
||||
Result := storage.is_valid_credential (a_auth_login, a_auth_password)
|
||||
Result := user_storage.is_valid_credential (a_auth_login, a_auth_password)
|
||||
end
|
||||
|
||||
user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
|
||||
@@ -229,7 +261,7 @@ feature -- Status report
|
||||
if l_roles = Void then
|
||||
-- Fill user with its roles.
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (0)
|
||||
l_roles := storage.user_roles_for (a_user)
|
||||
l_roles := user_storage.user_roles_for (a_user)
|
||||
end
|
||||
Result := l_roles
|
||||
end
|
||||
@@ -262,13 +294,13 @@ feature -- User roles.
|
||||
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
|
||||
-- Retrieve a `Role' represented by an id `a_id' if any.
|
||||
do
|
||||
Result := storage.user_role_by_id (a_id)
|
||||
Result := user_storage.user_role_by_id (a_id)
|
||||
end
|
||||
|
||||
user_role_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER_ROLE
|
||||
-- Retrieve a `Role' represented by a name `a_name' if any.
|
||||
do
|
||||
Result := storage.user_role_by_name (a_name)
|
||||
Result := user_storage.user_role_by_name (a_name)
|
||||
end
|
||||
|
||||
role_permissions: HASH_TABLE [LIST [READABLE_STRING_8], STRING_8]
|
||||
@@ -278,12 +310,16 @@ feature -- User roles.
|
||||
do
|
||||
create Result.make (cms_api.enabled_modules.count + 1)
|
||||
|
||||
l_used_permissions := storage.role_permissions
|
||||
l_used_permissions := user_storage.role_permissions
|
||||
across
|
||||
cms_api.enabled_modules as ic
|
||||
loop
|
||||
lst := ic.item.permissions
|
||||
if attached {CMS_ADMINISTRABLE} ic.item as adm then
|
||||
if
|
||||
attached {CMS_ADMINISTRABLE} ic.item as adm and then
|
||||
attached adm.module_administration.permissions as adm_permissions and then
|
||||
not adm_permissions.is_empty
|
||||
then
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} l_full_lst.make (lst.count)
|
||||
l_full_lst.compare_objects
|
||||
-- l_full_lst.append (lst)
|
||||
@@ -294,14 +330,38 @@ feature -- User roles.
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
-- l_full_lst.append (adm.module_administration.permissions)
|
||||
lst := adm.module_administration.permissions
|
||||
-- l_full_lst.append (adm_permissions)
|
||||
across
|
||||
adm_permissions as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
lst := l_full_lst
|
||||
end
|
||||
if
|
||||
attached {CMS_WITH_WEBAPI} ic.item as wapi and then
|
||||
attached wapi.module_webapi.permissions as wapi_permissions and then
|
||||
not wapi_permissions.is_empty
|
||||
then
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} l_full_lst.make (lst.count)
|
||||
l_full_lst.compare_objects
|
||||
-- l_full_lst.append (lst)
|
||||
across
|
||||
lst as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
-- l_full_lst.append (wapi_permissions)
|
||||
across
|
||||
wapi_permissions as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
lst := l_full_lst
|
||||
end
|
||||
@@ -331,7 +391,7 @@ feature -- User roles.
|
||||
roles: LIST [CMS_USER_ROLE]
|
||||
-- List of possible roles.
|
||||
do
|
||||
Result := storage.user_roles
|
||||
Result := user_storage.user_roles
|
||||
end
|
||||
|
||||
effective_roles: LIST [CMS_USER_ROLE]
|
||||
@@ -340,7 +400,7 @@ feature -- User roles.
|
||||
l_roles: like roles
|
||||
r: CMS_USER_ROLE
|
||||
do
|
||||
l_roles := storage.user_roles
|
||||
l_roles := user_storage.user_roles
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (l_roles.count)
|
||||
across
|
||||
l_roles as ic
|
||||
@@ -357,7 +417,7 @@ feature -- User roles.
|
||||
roles_count: INTEGER
|
||||
-- Number of roles
|
||||
do
|
||||
Result := storage.user_roles.count
|
||||
Result := user_storage.user_roles.count
|
||||
end
|
||||
|
||||
feature -- Change User role
|
||||
@@ -365,31 +425,31 @@ feature -- Change User role
|
||||
save_user_role (a_user_role: CMS_USER_ROLE)
|
||||
do
|
||||
reset_error
|
||||
storage.save_user_role (a_user_role)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.save_user_role (a_user_role)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
unassign_role_from_user (a_role: CMS_USER_ROLE; a_user: CMS_USER; )
|
||||
-- Unassign user_role `a_role' to user `a_user'.
|
||||
do
|
||||
reset_error
|
||||
storage.unassign_role_from_user (a_role, a_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.unassign_role_from_user (a_role, a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
assign_role_to_user (a_role: CMS_USER_ROLE; a_user: CMS_USER; )
|
||||
-- Assign user_role `a_role' to user `a_user'.
|
||||
do
|
||||
reset_error
|
||||
storage.assign_role_to_user (a_role, a_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.assign_role_to_user (a_role, a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
delete_role (a_role: CMS_USER_ROLE)
|
||||
do
|
||||
reset_error
|
||||
storage.delete_role (a_role)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.delete_role (a_role)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
feature -- User Activation
|
||||
@@ -397,7 +457,7 @@ feature -- User Activation
|
||||
new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- Save activation token `a_token', for the user with the id `a_id'.
|
||||
do
|
||||
storage.save_activation (a_token, a_id)
|
||||
user_storage.save_activation (a_token, a_id)
|
||||
end
|
||||
|
||||
feature -- User Password Recovery
|
||||
@@ -405,13 +465,13 @@ feature -- User Password Recovery
|
||||
new_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- Save password token `a_token', for the user with the id `a_id'.
|
||||
do
|
||||
storage.save_password (a_token, a_id)
|
||||
user_storage.save_password (a_token, a_id)
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- Remove password token `a_token', from the storage.
|
||||
-- Remove password token `a_token', from the user_storage.
|
||||
do
|
||||
storage.remove_password (a_token)
|
||||
user_storage.remove_password (a_token)
|
||||
end
|
||||
|
||||
feature -- User status
|
||||
@@ -423,7 +483,7 @@ feature -- User status
|
||||
-- The user is active
|
||||
|
||||
Trashed: INTEGER = -1
|
||||
-- The user is trashed (soft delete), ready to be deleted/destroyed from storage.
|
||||
-- The user is trashed (soft delete), ready to be deleted/destroyed from user_storage.
|
||||
|
||||
feature -- Access - Temp User
|
||||
|
||||
@@ -431,36 +491,36 @@ feature -- Access - Temp User
|
||||
-- Number of pending users.
|
||||
--! to be accepted or rehected
|
||||
do
|
||||
Result := storage.temp_users_count
|
||||
Result := user_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)
|
||||
Result := user_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)
|
||||
Result := user_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)
|
||||
Result := user_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)
|
||||
Result := user_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)
|
||||
Result := user_storage.token_by_temp_user_id (a_id)
|
||||
end
|
||||
|
||||
feature -- Change Temp User
|
||||
@@ -477,8 +537,8 @@ feature -- Change Temp User
|
||||
attached a_temp_user.salt as l_salt and then
|
||||
attached a_temp_user.email as l_email
|
||||
then
|
||||
storage.new_user_from_temp_user (a_temp_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.new_user_from_temp_user (a_temp_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
else
|
||||
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
|
||||
end
|
||||
@@ -495,17 +555,17 @@ feature -- Change Temp User
|
||||
attached a_temp_user.password as l_password and then
|
||||
attached a_temp_user.email as l_email
|
||||
then
|
||||
storage.new_temp_user (a_temp_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.new_temp_user (a_temp_user)
|
||||
error_handler.append (user_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.
|
||||
-- Remove activation token `a_token', from the user_storage.
|
||||
do
|
||||
storage.remove_activation (a_token)
|
||||
user_storage.remove_activation (a_token)
|
||||
end
|
||||
|
||||
delete_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
@@ -514,10 +574,38 @@ feature -- Change Temp User
|
||||
has_id: a_temp_user.has_id
|
||||
do
|
||||
reset_error
|
||||
storage.delete_temp_user (a_temp_user)
|
||||
error_handler.append (storage.error_handler)
|
||||
user_storage.delete_temp_user (a_temp_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
--feature -- Access: User profile
|
||||
|
||||
-- user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- -- User profile for `a_user'.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- Result := user_profile_storage.user_profile (a_user)
|
||||
-- end
|
||||
|
||||
-- user_profile_item (a_item_name: READABLE_STRING_GENERAL; a_user: CMS_USER): detachable READABLE_STRING_32
|
||||
-- -- User profile item `a_item_name` for `a_user`.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- Result := user_profile_storage.user_profile_item (a_user, a_item_name)
|
||||
-- end
|
||||
|
||||
--feature -- Change: User profile
|
||||
|
||||
-- save_user_profile (a_user: CMS_USER; a_user_profile: CMS_USER_PROFILE)
|
||||
-- -- Save `a_user' profile `a_user_profile'.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- user_profile_storage.save_user_profile (a_user, a_user_profile)
|
||||
-- end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
@@ -0,0 +1,87 @@
|
||||
note
|
||||
description: "API to handle user profiles."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_API
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor
|
||||
-- Storage initialization
|
||||
if attached cms_api.storage.as_sql_storage as l_storage_sql then
|
||||
create {CMS_USER_PROFILE_STORAGE_SQL} user_profile_storage.make (l_storage_sql)
|
||||
else
|
||||
-- FIXME: in case of NULL storage, should Current be disabled?
|
||||
create {CMS_USER_PROFILE_STORAGE_NULL} user_profile_storage
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
user_profile_storage: CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Access: profile
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
Result := user_profile_storage.user_profile (a_user)
|
||||
end
|
||||
|
||||
user_profile_item (a_item_name: READABLE_STRING_GENERAL; a_user: CMS_USER): detachable READABLE_STRING_32
|
||||
-- User profile item `a_item_name` for `a_user`.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
Result := user_profile_storage.user_profile_item (a_user, a_item_name)
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
require
|
||||
not a_item_name.is_whitespace
|
||||
do
|
||||
Result := user_profile_storage.users_with_profile_item (a_item_name, a_value)
|
||||
end
|
||||
|
||||
feature -- Change: profile
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_user_profile: CMS_USER_PROFILE)
|
||||
-- Save `a_user' profile `a_user_profile'.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
user_profile_storage.save_user_profile (a_user, a_user_profile)
|
||||
end
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_user_profile_name, a_user_profile_value: READABLE_STRING_GENERAL)
|
||||
-- Save `a_user' profile item `a_user_profile_name=a_user_profile_value`.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
user_profile_storage.save_user_profile_item (a_user, a_user_profile_name, a_user_profile_value)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
note
|
||||
description: "[
|
||||
Handler for a CMS user in the CMS interface
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
rename
|
||||
execute as uri_execute,
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
rename
|
||||
execute as uri_template_execute,
|
||||
new_mapping as new_uri_template_mapping
|
||||
select
|
||||
new_uri_template_mapping
|
||||
end
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- execute
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute (req, res)
|
||||
end
|
||||
|
||||
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute (req, res)
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_path_parameter (req: WSF_REQUEST): detachable CMS_USER
|
||||
-- User id (uid or username) passed as path parameter for request `req'.
|
||||
local
|
||||
s: STRING
|
||||
l_uid: INTEGER_64
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_nid then
|
||||
s := p_nid.value
|
||||
if s.is_integer_64 then
|
||||
l_uid := s.to_integer_64
|
||||
if l_uid > 0 then
|
||||
Result := api.user_api.user_by_id (l_uid)
|
||||
end
|
||||
else
|
||||
Result := api.user_api.user_by_name (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP Methods
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_user: detachable CMS_USER
|
||||
do
|
||||
if api.has_permission ("view user") then
|
||||
-- Display existing node
|
||||
l_user := user_path_parameter (req)
|
||||
if
|
||||
l_user /= Void
|
||||
then
|
||||
(create {CMS_USER_VIEW_RESPONSE}.make_with_user (l_user, req, res, api)).execute
|
||||
else
|
||||
send_not_found (req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,137 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_VIEW_RESPONSE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_VIEW_RESPONSE
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
|
||||
create
|
||||
make_with_user
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_user (u: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
make (req, res, a_api)
|
||||
associated_user := u
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
associated_user: CMS_USER
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
-- User id passed as path parameter for request `req'.
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_nid then
|
||||
s := p_nid.value
|
||||
if s.is_integer_64 then
|
||||
Result := s.to_integer_64
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Process
|
||||
|
||||
process
|
||||
-- Computed response message.
|
||||
local
|
||||
b: STRING_8
|
||||
f: CMS_FORM
|
||||
do
|
||||
create b.make_empty
|
||||
if
|
||||
attached associated_user as l_user
|
||||
then
|
||||
if
|
||||
api.has_permission ("view user")
|
||||
or l_user.same_as (user) -- Same user
|
||||
then
|
||||
f := new_view_form (l_user, request.request_uri, "view-user")
|
||||
f.append_to_html (wsf_theme, b)
|
||||
else
|
||||
b.append ("You don't have the permission to view this user!")
|
||||
end
|
||||
else
|
||||
b.append ("User not found!")
|
||||
end
|
||||
set_main_content (b)
|
||||
end
|
||||
|
||||
feature -- Process Edit
|
||||
|
||||
new_view_form (a_user: detachable CMS_USER; a_url: READABLE_STRING_8; a_name: STRING): CMS_FORM
|
||||
-- Create a web form named `a_name' for user `a_user' (if set), using form action url `a_url'.
|
||||
local
|
||||
th: WSF_FORM_HIDDEN_INPUT
|
||||
do
|
||||
create Result.make (a_url, a_name)
|
||||
create th.make ("user-id")
|
||||
if a_user /= Void then
|
||||
th.set_text_value (a_user.id.out)
|
||||
else
|
||||
th.set_text_value ("0")
|
||||
end
|
||||
Result.extend (th)
|
||||
|
||||
populate_form (Result, a_user)
|
||||
end
|
||||
|
||||
populate_form (a_form: CMS_FORM; a_user: detachable CMS_USER)
|
||||
-- Fill the web form `a_form' with data from `a_node' if set,
|
||||
-- and apply this to content type `a_content_type'.
|
||||
local
|
||||
ti: WSF_FORM_TEXT_INPUT
|
||||
fs: WSF_FORM_FIELD_SET
|
||||
l_new_access_token_form: WSF_FORM
|
||||
l_access_token: detachable READABLE_STRING_32
|
||||
do
|
||||
if a_user /= Void then
|
||||
create fs.make
|
||||
fs.set_legend ("User Information")
|
||||
create ti.make_with_text ("profile_name", a_user.name)
|
||||
if attached a_user.profile_name as l_profile_name then
|
||||
ti.set_text_value (l_profile_name)
|
||||
end
|
||||
ti.set_label ("Profile name")
|
||||
ti.set_is_readonly (True)
|
||||
fs.extend (ti)
|
||||
a_form.extend (fs)
|
||||
if api.setup.webapi_enabled then
|
||||
create fs.make
|
||||
fs.set_legend ("Web API")
|
||||
l_access_token := api.user_api.user_profile_item ("access_token", a_user)
|
||||
if l_access_token /= Void then
|
||||
create ti.make_with_text ("api_access_token", a_user.name)
|
||||
ti.set_text_value (l_access_token)
|
||||
ti.set_label ("Access Token")
|
||||
ti.set_is_readonly (True)
|
||||
fs.extend (ti)
|
||||
end
|
||||
|
||||
create l_new_access_token_form.make (api.webapi_path ("access_token"), Void)
|
||||
l_new_access_token_form.set_method_post
|
||||
if l_access_token /= Void then
|
||||
l_new_access_token_form.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("access_token_op", "Refresh Access Token"))
|
||||
else
|
||||
l_new_access_token_form.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("access_token_op", "Create Access Token"))
|
||||
end
|
||||
l_new_access_token_form.extend (create {WSF_FORM_HIDDEN_INPUT}.make_with_text ("destination", request.percent_encoded_path_info))
|
||||
a_form.put_widget_after_form (l_new_access_token_form)
|
||||
a_form.extend (fs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
+1
-1
@@ -800,7 +800,7 @@ feature -- Change: User activation
|
||||
sql_begin_transaction
|
||||
write_information_log (generator + ".save_activation")
|
||||
create l_utc_date.make_now_utc
|
||||
create l_parameters.make (2)
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_token, "token")
|
||||
l_parameters.put (a_id, "uid")
|
||||
l_parameters.put (l_utc_date, "utc_date")
|
||||
@@ -0,0 +1,66 @@
|
||||
note
|
||||
description: "Interface for accessing user profile contents from the database."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
if attached user_profile (a_user) as pf then
|
||||
Result := pf.item (a_item_name)
|
||||
end
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- Save user profile `a_profile' for `a_user'.
|
||||
require
|
||||
user_has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_profile_item_name: READABLE_STRING_GENERAL; a_profile_item_value: READABLE_STRING_GENERAL)
|
||||
require
|
||||
user_has_id: a_user.has_id
|
||||
local
|
||||
pf: detachable CMS_USER_PROFILE
|
||||
do
|
||||
pf := user_profile (a_user)
|
||||
if pf = Void then
|
||||
create pf.make
|
||||
end
|
||||
pf.force (a_profile_item_value, a_profile_item_name)
|
||||
save_user_profile (a_user, pf)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_PROFILE_STORAGE_NULL}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Error handler
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,212 @@
|
||||
note
|
||||
description: "Interface for accessing user profile contents from SQL database."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_STORAGE_SQL
|
||||
|
||||
inherit
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
redefine
|
||||
user_profile_item,
|
||||
save_user_profile_item
|
||||
end
|
||||
|
||||
CMS_PROXY_STORAGE_SQL
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- User profile for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_item_name, "key")
|
||||
sql_query (sql_select_user_profile_item, l_parameters)
|
||||
if not has_error then
|
||||
Result := sql_read_string_32 (2)
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
sql_query (sql_select_user_profile_items, l_parameters)
|
||||
if not has_error then
|
||||
create Result.make
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if
|
||||
attached sql_read_string_32 (1) as l_key and
|
||||
attached sql_read_string_32 (2) as l_val
|
||||
then
|
||||
Result.force (l_val, l_key)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_uids: ARRAYED_LIST [INTEGER_64]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_item_name, "key")
|
||||
if a_value = Void then
|
||||
sql_query (sql_select_users_with_profile_item_named, l_parameters)
|
||||
else
|
||||
l_parameters.put (a_value, "value")
|
||||
sql_query (sql_select_users_with_profile_item, l_parameters)
|
||||
end
|
||||
if not has_error then
|
||||
create l_uids.make (0)
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if
|
||||
attached sql_read_integer_64 (1) as l_uid and then
|
||||
l_uid > 0
|
||||
then
|
||||
l_uids.force (l_uid)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
if
|
||||
not has_error and
|
||||
l_uids /= Void and
|
||||
attached api as l_cms_api
|
||||
then
|
||||
create {ARRAYED_LIST [CMS_USER]} Result.make (l_uids.count)
|
||||
across
|
||||
l_uids as ic
|
||||
loop
|
||||
if attached l_cms_api.user_api.user_by_id (ic.item) as u then
|
||||
Result.force (u)
|
||||
else
|
||||
check known_user: False end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL; a_item_value: READABLE_STRING_GENERAL)
|
||||
-- Save user profile item `a_item_name:a_item_value` for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_item_name, "key")
|
||||
l_parameters.put (a_item_value, "value")
|
||||
|
||||
reset_error
|
||||
if user_profile_item (a_user, a_item_name) = Void then
|
||||
sql_insert (sql_insert_user_profile_item, l_parameters)
|
||||
else
|
||||
sql_modify (sql_update_user_profile_item, l_parameters)
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- Save user profile `a_profile' for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
p: detachable CMS_USER_PROFILE
|
||||
l_item: like user_profile_item
|
||||
l_is_new: BOOLEAN
|
||||
l_has_diff: BOOLEAN
|
||||
do
|
||||
p := user_profile (a_user)
|
||||
|
||||
create l_parameters.make (3)
|
||||
|
||||
reset_error
|
||||
across
|
||||
a_profile as ic
|
||||
until
|
||||
has_error
|
||||
loop
|
||||
l_item := ic.item
|
||||
-- No previous profile, or no item with same name, or same value
|
||||
l_has_diff := True
|
||||
if p = Void then
|
||||
l_is_new := True
|
||||
elseif p.has_key (ic.key) then
|
||||
l_is_new := False
|
||||
l_has_diff := attached p.item (ic.key) as l_prev_item and then not l_prev_item.same_string (l_item)
|
||||
else
|
||||
l_is_new := True
|
||||
end
|
||||
if l_has_diff then
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (ic.key, "key")
|
||||
l_parameters.put (l_item, "value")
|
||||
|
||||
if l_is_new then
|
||||
sql_insert (sql_insert_user_profile_item, l_parameters)
|
||||
else
|
||||
sql_modify (sql_update_user_profile_item, l_parameters)
|
||||
end
|
||||
l_parameters.wipe_out
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature {NONE} -- Queries
|
||||
|
||||
sql_select_user_profile_items: STRING = "SELECT key, value FROM user_profiles WHERE uid=:uid;"
|
||||
-- user profile items for :uid;
|
||||
|
||||
sql_select_user_profile_item: STRING = "SELECT key, value FROM user_profiles WHERE uid=:uid AND key=:key"
|
||||
-- user profile items for :uid;
|
||||
|
||||
sql_select_users_with_profile_item: STRING = "SELECT uid FROM user_profiles WHERE key=:key and value=:value"
|
||||
-- users with profile item named :key and value :value;
|
||||
|
||||
sql_select_users_with_profile_item_named: STRING = "SELECT uid FROM user_profiles WHERE key=:key"
|
||||
-- users with profile item named :key;
|
||||
|
||||
sql_insert_user_profile_item: STRING = "INSERT INTO user_profiles (uid, key, value) VALUES (:uid, :key, :value);"
|
||||
-- new user profile item for :uid;
|
||||
|
||||
sql_update_user_profile_item: STRING = "UPDATE user_profiles SET value = :value WHERE uid = :uid AND key = :key;"
|
||||
-- user profile items for :uid;
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_AUTH_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
local
|
||||
tok: READABLE_STRING_GENERAL
|
||||
do
|
||||
if
|
||||
attached req.http_authorization as l_auth and then
|
||||
l_auth.starts_with_general ("Bearer ")
|
||||
then
|
||||
tok := l_auth.substring (8, l_auth.count)
|
||||
if attached api.user_api.users_with_profile_item ("access_token", tok) as lst then
|
||||
if lst.count = 1 then
|
||||
api.set_user (lst.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,182 @@
|
||||
note
|
||||
description: "Summary description for {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ACCESS_TOKEN_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
l_uid: READABLE_STRING_GENERAL
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
|
||||
l_uid := p_uid.value
|
||||
if req.is_post_request_method then
|
||||
post_access_token (l_uid, req, res)
|
||||
elseif req.is_get_request_method then
|
||||
get_access_token (l_uid, req, res)
|
||||
else
|
||||
send_bad_request (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_bad_request ("Missing {uid} parameter", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
user_by_uid (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
do
|
||||
Result := api.user_api.user_by_id_or_name (a_uid)
|
||||
end
|
||||
|
||||
feature -- Request execution
|
||||
|
||||
get_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
if attached user_by_uid (a_uid) as l_user then
|
||||
if attached api.user as u then
|
||||
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
|
||||
rep := new_access_token_webapi_response (l_user, user_access_token (l_user), req, res)
|
||||
if attached {WSF_STRING} req.item ("destination") as dest then
|
||||
rep.set_redirection (dest.url_encoded_value)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- Only admin, or current user can see its access_token!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_not_found ("User not found", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
post_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
l_access_token: detachable READABLE_STRING_32
|
||||
rep: like new_webapi_response
|
||||
do
|
||||
if attached user_by_uid (a_uid) as l_user then
|
||||
if attached api.user as u then
|
||||
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
|
||||
if attached req.path_parameter ("application") then
|
||||
|
||||
end
|
||||
-- l_access_token := user_access_token (l_user)
|
||||
l_access_token := new_key (40)
|
||||
|
||||
-- if l_access_token /= Void then
|
||||
-- l_access_token := "Updated-" + (create {UUID_GENERATOR}).generate_uuid.out
|
||||
-- else
|
||||
-- l_access_token := "New-" + (create {UUID_GENERATOR}).generate_uuid.out
|
||||
-- end
|
||||
set_user_access_token (l_user, l_access_token)
|
||||
|
||||
rep := new_access_token_webapi_response (l_user, l_access_token, req, res)
|
||||
if attached {WSF_STRING} req.item ("destination") as dest then
|
||||
rep.set_redirection (dest.url_encoded_value)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- Only admin, or current user can create the user access_token!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_not_found ("User not found", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
user_access_token (a_user: CMS_USER): detachable READABLE_STRING_8
|
||||
do
|
||||
if
|
||||
attached api.user_api.user_profile_item ("access_token", a_user) as l_access_token and then
|
||||
not l_access_token.is_whitespace and then
|
||||
l_access_token.is_valid_as_string_8
|
||||
then
|
||||
Result := l_access_token.to_string_8
|
||||
else
|
||||
-- Result := new_key (40)
|
||||
end
|
||||
end
|
||||
|
||||
set_user_access_token (a_user: CMS_USER; a_access_token: READABLE_STRING_GENERAL)
|
||||
do
|
||||
api.user_api.save_user_profile_item (a_user, "access_token", a_access_token)
|
||||
end
|
||||
|
||||
new_access_token_webapi_response (a_user: CMS_USER; a_access_token: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE): like new_webapi_response
|
||||
local
|
||||
tb: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
Result := new_webapi_response (req, res)
|
||||
if a_access_token /= Void then
|
||||
Result.add_string_field ("access_token", a_access_token)
|
||||
else
|
||||
Result.add_string_field ("access_token", "NONE")
|
||||
Result.add_link ("new_access_token", "user/" + a_user.id.out + "/access_token", req.percent_encoded_path_info)
|
||||
end
|
||||
create tb.make_equal (3)
|
||||
tb.force (a_user.name, "name")
|
||||
tb.force (a_user.id, "uid")
|
||||
Result.add_table_iterator_field ("user", tb)
|
||||
|
||||
-- Result.add_string_field ("username", a_user.name)
|
||||
-- Result.add_integer_64_field ("uid", a_user.id)
|
||||
Result.add_self (req.percent_encoded_path_info)
|
||||
add_user_links_to (a_user, Result)
|
||||
end
|
||||
|
||||
new_key (len: INTEGER): STRING_8
|
||||
local
|
||||
rand: RANDOM
|
||||
n: INTEGER
|
||||
v: NATURAL_32
|
||||
do
|
||||
create rand.set_seed ((create {DATE_TIME}.make_now_utc).seconds)
|
||||
rand.start
|
||||
create Result.make (len)
|
||||
from
|
||||
n := 1
|
||||
until
|
||||
n = len
|
||||
loop
|
||||
rand.forth
|
||||
v := (rand.item \\ 16).to_natural_32
|
||||
check 0 <= v and v <= 15 end
|
||||
if v < 9 then
|
||||
Result.append_code (48 + v) -- 48 '0'
|
||||
else
|
||||
Result.append_code (97 + v - 9) -- 97 'a'
|
||||
end
|
||||
n := n + 1
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
note
|
||||
description: "Summary description for {CMS_BASIC_WEBAPI_AUTH_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_BASIC_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_AUTH_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
local
|
||||
l_auth: HTTP_AUTHORIZATION
|
||||
do
|
||||
create l_auth.make (req.http_authorization)
|
||||
if
|
||||
l_auth.is_basic and then
|
||||
attached l_auth.login as l_auth_login and then
|
||||
attached l_auth.password as l_auth_password
|
||||
then
|
||||
if
|
||||
api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
|
||||
attached api.user_api.user_by_name (l_auth_login) as l_user
|
||||
then
|
||||
api.set_user (l_user)
|
||||
else
|
||||
-- not authenticated due to bad login or password.
|
||||
end
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_MODULE_WEBAPI}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_MODULE_WEBAPI
|
||||
|
||||
inherit
|
||||
CMS_MODULE_WEBAPI [CMS_CORE_MODULE]
|
||||
redefine
|
||||
permissions,
|
||||
filters
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Security
|
||||
|
||||
permissions: LIST [READABLE_STRING_8]
|
||||
-- List of permission ids, used by this module, and declared.
|
||||
do
|
||||
Result := Precursor
|
||||
Result.force ("admin users")
|
||||
Result.force ("view users")
|
||||
end
|
||||
|
||||
feature {NONE} -- Router/administration
|
||||
|
||||
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_root: CMS_ROOT_WEBAPI_HANDLER
|
||||
do
|
||||
create l_root.make (a_api)
|
||||
a_router.handle ("", l_root, a_router.methods_get)
|
||||
a_router.handle ("/", l_root, a_router.methods_get)
|
||||
a_router.handle ("/user/{uid}/access_token", create {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post)
|
||||
a_router.handle ("/user/{uid}", create {CMS_USER_WEBAPI_HANDLER}.make (a_api), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Access: filter
|
||||
|
||||
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
|
||||
-- Possibly list of Filter's module.
|
||||
do
|
||||
create {ARRAYED_LIST [WSF_FILTER]} Result.make (2)
|
||||
Result.extend (create {CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
Result.extend (create {CMS_BASIC_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Summary description for {CMS_ROOT_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ROOT_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
rep := new_webapi_response (req, res)
|
||||
rep.add_string_field ("site_name", api.setup.site_name)
|
||||
if attached api.user as u then
|
||||
add_user_links_to (u, rep)
|
||||
end
|
||||
rep.add_self (req.percent_encoded_path_info)
|
||||
rep.execute
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
do
|
||||
if req.is_get_request_method then
|
||||
execute_get (req, res)
|
||||
else
|
||||
send_bad_request (Void, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
l_user: detachable CMS_USER
|
||||
do
|
||||
if attached api.user as u then
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
|
||||
if p_uid.is_integer then
|
||||
l_user := api.user_api.user_by_id (p_uid.integer_value)
|
||||
else
|
||||
l_user := api.user_api.user_by_name (p_uid.value)
|
||||
end
|
||||
-- if l_user = Void and p_uid.is_case_insensitive_equal ("me") then
|
||||
-- l_user := u
|
||||
-- end
|
||||
if l_user /= Void then
|
||||
if l_user.same_as (u) or api.has_permissions (<<"admin users", "view users">>) then
|
||||
rep := new_webapi_response (req, res)
|
||||
rep.add_string_field ("uid", u.id.out)
|
||||
|
||||
rep.add_string_field ("name", u.name)
|
||||
if attached u.email as l_email then
|
||||
rep.add_string_field ("email", l_email)
|
||||
end
|
||||
if attached u.profile_name as l_profile_name then
|
||||
rep.add_string_field ("profile_name", l_profile_name)
|
||||
end
|
||||
add_user_links_to (u, rep)
|
||||
else
|
||||
rep := new_wepapi_error_response ("denied", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
|
||||
end
|
||||
else
|
||||
rep := new_wepapi_error_response ("Not found", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
end
|
||||
else
|
||||
rep := new_wepapi_error_response ("Bad request", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- FIXME: use specific Web API response!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -84,6 +84,11 @@ feature -- Operation
|
||||
sql_storage.sql_modify (a_sql_statement, a_params)
|
||||
end
|
||||
|
||||
sql_delete (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
do
|
||||
sql_storage.sql_delete (a_sql_statement, a_params)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
sql_start
|
||||
@@ -133,6 +138,6 @@ feature -- Conversion
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -138,6 +138,11 @@ feature -- Operation
|
||||
deferred
|
||||
end
|
||||
|
||||
sql_delete (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- Execute sql delete `a_sql_statement' with optional parameters `a_params'.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
sql_script_content (a_path: PATH): detachable STRING
|
||||
@@ -462,6 +467,6 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -6,29 +6,8 @@ note
|
||||
deferred class
|
||||
CMS_ADMINISTRABLE
|
||||
|
||||
feature -- Administration
|
||||
|
||||
module_administration: like administration
|
||||
-- Associated administration module.
|
||||
do
|
||||
Result := internal_module_administration
|
||||
if Result = Void then
|
||||
Result := administration
|
||||
internal_module_administration := Result
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
internal_module_administration: detachable like module_administration
|
||||
-- Cached version of `module_administration`.
|
||||
|
||||
feature {NONE} -- Administration
|
||||
|
||||
administration: CMS_MODULE_ADMINISTRATION [CMS_MODULE]
|
||||
-- Administration module.
|
||||
deferred
|
||||
end
|
||||
inherit
|
||||
CMS_WITH_MODULE_ADMINISTRATION
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
|
||||
+125
-9
@@ -54,6 +54,10 @@ feature {NONE} -- Initialize
|
||||
s := setup.administration_base_path
|
||||
administration_base_path_location := s.shared_substring (2, s.count)
|
||||
|
||||
-- Webapi backend
|
||||
s := setup.webapi_base_path
|
||||
webapi_base_path_location := s.shared_substring (2, s.count)
|
||||
|
||||
-- Initialize contents.
|
||||
initialize_content_types
|
||||
|
||||
@@ -398,6 +402,55 @@ feature -- Access: url
|
||||
site_url: IMMUTABLE_STRING_8
|
||||
-- Site url
|
||||
|
||||
feature -- Access: WebAPI
|
||||
|
||||
is_webapi_request (req: WSF_REQUEST): BOOLEAN
|
||||
do
|
||||
Result := setup.webapi_enabled and then req.percent_encoded_path_info.starts_with_general (setup.webapi_base_path)
|
||||
end
|
||||
|
||||
webapi_path (a_relative_path: detachable READABLE_STRING_8): STRING_8
|
||||
require
|
||||
is_webapi_enabled: setup.webapi_enabled
|
||||
do
|
||||
create Result.make_from_string (setup.webapi_base_path)
|
||||
if a_relative_path /= Void then
|
||||
if a_relative_path.is_empty then
|
||||
Result.append_character ('/')
|
||||
else
|
||||
if a_relative_path[1] /= '/' then
|
||||
Result.append_character ('/')
|
||||
end
|
||||
Result.append (a_relative_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
webapi_path_location (a_relative_location: detachable READABLE_STRING_8): STRING_8
|
||||
require
|
||||
is_webapi_enabled: setup.webapi_enabled
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
do
|
||||
create Result.make_from_string (webapi_base_path_location)
|
||||
if a_relative_location /= Void then
|
||||
if a_relative_location.is_empty then
|
||||
Result.append_character ('/')
|
||||
else
|
||||
if a_relative_location[1] /= '/' then
|
||||
Result.append_character ('/')
|
||||
end
|
||||
Result.append (a_relative_location)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation/WebAPI.
|
||||
|
||||
webapi_base_path_location: IMMUTABLE_STRING_8
|
||||
-- Webapi path without first slash!
|
||||
|
||||
feature -- Access: Administration
|
||||
|
||||
is_administration_request (req: WSF_REQUEST): BOOLEAN
|
||||
do
|
||||
Result := req.percent_encoded_path_info.starts_with_general (setup.administration_base_path)
|
||||
@@ -435,13 +488,20 @@ feature -- Access: url
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Url implementation.
|
||||
feature {NONE} -- Implementation/ Administration
|
||||
|
||||
administration_base_path_location: IMMUTABLE_STRING_8
|
||||
-- Administration path without first slash!
|
||||
|
||||
feature -- CMS links
|
||||
|
||||
webapi_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
require
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
do
|
||||
Result := local_link (a_title, webapi_path_location (a_relative_location))
|
||||
end
|
||||
|
||||
administration_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
require
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
@@ -510,9 +570,17 @@ feature -- Settings
|
||||
|
||||
switch_to_site_mode
|
||||
do
|
||||
if is_administration_mode then
|
||||
if not is_site_mode then
|
||||
setup.set_site_mode
|
||||
is_administration_mode := False
|
||||
mode := mode_site
|
||||
end
|
||||
end
|
||||
|
||||
switch_to_webapi_mode
|
||||
do
|
||||
if not is_webapi_mode then
|
||||
setup.set_webapi_mode
|
||||
mode := mode_webapi
|
||||
end
|
||||
end
|
||||
|
||||
@@ -520,12 +588,32 @@ feature -- Settings
|
||||
do
|
||||
if not is_administration_mode then
|
||||
setup.set_administration_mode
|
||||
is_administration_mode := True
|
||||
mode := mode_administration
|
||||
end
|
||||
end
|
||||
|
||||
mode: NATURAL_8
|
||||
|
||||
mode_site: NATURAL_8 = 0
|
||||
mode_administration: NATURAL_8 = 1
|
||||
mode_webapi: NATURAL_8 = 2
|
||||
|
||||
is_site_mode: BOOLEAN
|
||||
do
|
||||
Result := mode = mode_site
|
||||
end
|
||||
|
||||
is_administration_mode: BOOLEAN
|
||||
-- Is administration mode?
|
||||
do
|
||||
Result := mode = mode_administration
|
||||
end
|
||||
|
||||
is_webapi_mode: BOOLEAN
|
||||
-- Is webapi mode?
|
||||
do
|
||||
Result := mode = mode_webapi
|
||||
end
|
||||
|
||||
is_debug: BOOLEAN
|
||||
-- Is debug mode enabled?
|
||||
@@ -969,6 +1057,12 @@ feature {CMS_EXECUTION} -- Hooks
|
||||
else
|
||||
l_module := Void
|
||||
end
|
||||
elseif is_webapi_mode then
|
||||
if attached {CMS_WITH_WEBAPI} l_module as wapi then
|
||||
l_module := wapi.module_webapi
|
||||
else
|
||||
l_module := Void
|
||||
end
|
||||
end
|
||||
if l_module /= Void then
|
||||
if attached {CMS_HOOK_AUTO_REGISTER} l_module as l_auto then
|
||||
@@ -1205,7 +1299,7 @@ feature -- Theming path helpers
|
||||
Result := theme_location_for (a_theme_name).extended ("assets")
|
||||
end
|
||||
|
||||
feature -- Environment/ module
|
||||
feature -- Environment/ module
|
||||
|
||||
module_configuration (a_module: CMS_MODULE; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
do
|
||||
@@ -1215,20 +1309,42 @@ feature -- Environment/ module
|
||||
module_configuration_by_name (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration reader for `a_module', and if `a_name' is set, using name `a_name'.
|
||||
local
|
||||
k: STRING_32
|
||||
p: detachable PATH
|
||||
l_cache: like module_configuration_cache
|
||||
do
|
||||
-- Search first in site/config/modules/$module_name/($app|$module_name).(json|ini)
|
||||
-- if none, look as sub configuration if $app /= Void
|
||||
-- and then in site/modules/$module_name/config/($app|$module_name).(json|ini)
|
||||
-- and if non in sub config if $app /= Void
|
||||
p := site_location.extended ("config").extended ("modules").extended (a_module_name)
|
||||
Result := module_configuration_by_name_in_location (a_module_name, p, a_name)
|
||||
if Result = Void then
|
||||
p := module_location_by_name (a_module_name).extended ("config")
|
||||
create k.make_from_string_general (a_module_name)
|
||||
if a_name /= Void then
|
||||
k.append_character (':')
|
||||
k.append_string_general (a_name)
|
||||
end
|
||||
l_cache := module_configuration_cache
|
||||
if l_cache /= Void then
|
||||
l_cache.search (k)
|
||||
else
|
||||
create l_cache.make_caseless (1)
|
||||
module_configuration_cache := l_cache
|
||||
end
|
||||
if l_cache.found then
|
||||
Result := l_cache.found_item
|
||||
else
|
||||
p := site_location.extended ("config").extended ("modules").extended (a_module_name)
|
||||
Result := module_configuration_by_name_in_location (a_module_name, p, a_name)
|
||||
if Result = Void then
|
||||
p := module_location_by_name (a_module_name).extended ("config")
|
||||
Result := module_configuration_by_name_in_location (a_module_name, p, a_name)
|
||||
end
|
||||
l_cache.force (Result, k)
|
||||
end
|
||||
end
|
||||
|
||||
module_configuration_cache: detachable STRING_TABLE [detachable CONFIG_READER]
|
||||
-- Cache for `module_configuration(_by_name)` function.
|
||||
|
||||
module_configuration_by_name_in_location (a_module_name: READABLE_STRING_GENERAL; a_dir: PATH; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration reader from "$a_dir/($a_module_name|$a_name).(json|ini)" location.
|
||||
local
|
||||
|
||||
+147
-17
@@ -19,6 +19,7 @@ inherit
|
||||
execute_default,
|
||||
filter_execute,
|
||||
initialize,
|
||||
initialize_filter,
|
||||
initialize_router
|
||||
end
|
||||
|
||||
@@ -61,6 +62,13 @@ feature {NONE} -- Initialization
|
||||
create {WSF_MAINTENANCE_FILTER} filter
|
||||
end
|
||||
|
||||
initialize_filter
|
||||
-- Initialize `filter`.
|
||||
do
|
||||
create_filter
|
||||
-- setup_filter: delayed to `initialize_execution`.
|
||||
end
|
||||
|
||||
initialize_router
|
||||
-- Initialize `router`.
|
||||
do
|
||||
@@ -147,20 +155,34 @@ feature -- Settings: router
|
||||
configure_api_file_handler (l_router)
|
||||
end
|
||||
|
||||
setup_router_for_administration
|
||||
-- <Precursor>
|
||||
setup_router_and_filter_for_webapi
|
||||
local
|
||||
l_api: like api
|
||||
l_router: like router
|
||||
l_module: CMS_MODULE
|
||||
f, l_filter, l_last_filter: WSF_FILTER
|
||||
do
|
||||
l_api := api
|
||||
l_router := router
|
||||
|
||||
l_api.logger.put_debug (generator + ".setup_router_for_administration", Void)
|
||||
l_api.logger.put_debug (generator + ".setup_router_for_webapi", Void)
|
||||
|
||||
-- Configure root of api handler.
|
||||
l_router.set_base_url (l_api.administration_path (Void))
|
||||
l_router.set_base_url (l_api.webapi_path (Void))
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include routes from modules.
|
||||
across
|
||||
@@ -169,10 +191,86 @@ feature -- Settings: router
|
||||
l_module := ic.item
|
||||
if
|
||||
l_module.is_initialized and then
|
||||
attached {CMS_ADMINISTRABLE} l_module as l_administration and then
|
||||
attached l_administration.module_administration as adm
|
||||
attached {CMS_WITH_WEBAPI} l_module as l_webapi and then
|
||||
attached l_webapi.module_webapi as wapi
|
||||
then
|
||||
adm.setup_router (l_router, l_api)
|
||||
wapi.setup_router (l_router, l_api)
|
||||
if attached wapi.filters (l_api) as l_filters then
|
||||
across
|
||||
l_filters as f_ic
|
||||
loop
|
||||
f := f_ic.item
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setup_router_and_filter_for_administration
|
||||
-- <Precursor>
|
||||
local
|
||||
l_api: like api
|
||||
l_router: like router
|
||||
l_module: CMS_MODULE
|
||||
f, l_filter, l_last_filter: WSF_FILTER
|
||||
do
|
||||
l_api := api
|
||||
l_router := router
|
||||
|
||||
l_api.logger.put_debug (generator + ".setup_router_and_filter_for_administration", Void)
|
||||
|
||||
-- Configure root of api handler.
|
||||
l_router.set_base_url (l_api.administration_path (Void))
|
||||
|
||||
-- Apply normal filters
|
||||
setup_filter
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include routes from modules.
|
||||
across
|
||||
modules as ic
|
||||
loop
|
||||
l_module := ic.item
|
||||
if
|
||||
l_module.is_initialized
|
||||
then
|
||||
if
|
||||
attached {CMS_WITH_MODULE_ADMINISTRATION} l_module as l_administration and then
|
||||
attached l_administration.module_administration as adm
|
||||
then
|
||||
adm.setup_router (l_router, l_api)
|
||||
if attached adm.filters (l_api) as l_filters then
|
||||
across
|
||||
l_filters as f_ic
|
||||
loop
|
||||
f := f_ic.item
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
-- elseif
|
||||
-- attached {CMS_WITH_WEBAPI} l_module as l_wapi and then
|
||||
-- attached l_wapi.module_webapi as wapi
|
||||
-- then
|
||||
-- wapi.setup_router (l_router, l_api)
|
||||
end
|
||||
end
|
||||
end
|
||||
map_uri ("/install", create {CMS_ADMIN_INSTALL_HANDLER}.make (api), l_router.methods_head_get)
|
||||
@@ -234,6 +332,8 @@ feature -- Request execution
|
||||
request.set_uploaded_file_path (api.temp_location)
|
||||
if api.is_administration_request (request) then
|
||||
initialize_administration_execution
|
||||
elseif api.is_webapi_request (request) then
|
||||
initialize_webapi_execution
|
||||
else
|
||||
initialize_site_execution
|
||||
end
|
||||
@@ -244,15 +344,24 @@ feature -- Request execution
|
||||
do
|
||||
api.switch_to_site_mode
|
||||
api.initialize_execution
|
||||
setup_filter
|
||||
setup_router
|
||||
end
|
||||
|
||||
initialize_webapi_execution
|
||||
-- Initialize for site execution.
|
||||
do
|
||||
api.switch_to_webapi_mode
|
||||
api.initialize_execution
|
||||
setup_router_and_filter_for_webapi
|
||||
end
|
||||
|
||||
initialize_administration_execution
|
||||
-- Initialize for administration execution.
|
||||
do
|
||||
api.switch_to_administration_mode
|
||||
api.initialize_execution
|
||||
setup_router_for_administration
|
||||
setup_router_and_filter_for_administration
|
||||
end
|
||||
|
||||
execute
|
||||
@@ -277,7 +386,6 @@ feature -- Filters
|
||||
-- Create `filter'.
|
||||
local
|
||||
f, l_filter: detachable WSF_FILTER
|
||||
l_module: CMS_MODULE
|
||||
l_api: like api
|
||||
do
|
||||
l_api := api
|
||||
@@ -294,6 +402,32 @@ feature -- Filters
|
||||
-- f.set_next (l_filter)
|
||||
-- l_filter := f
|
||||
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'.
|
||||
local
|
||||
f, l_filter, l_last_filter: detachable WSF_FILTER
|
||||
l_module: CMS_MODULE
|
||||
l_api: like api
|
||||
do
|
||||
l_api := api
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include filters from modules
|
||||
across
|
||||
modules as ic
|
||||
@@ -305,18 +439,14 @@ feature -- Filters
|
||||
then
|
||||
across l_m_filters as f_ic loop
|
||||
f := f_ic.item
|
||||
f.set_next (l_filter)
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
-- f.set_next (l_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'.
|
||||
do
|
||||
-- filter := l_filter
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -80,7 +80,7 @@ feature -- Status
|
||||
is_initialized: BOOLEAN
|
||||
-- Is Current module initialized?
|
||||
|
||||
feature {CMS_API, CMS_MODULE_ADMINISTRATION} -- Access: API
|
||||
feature {CMS_API, CMS_MODULE} -- Access: API
|
||||
|
||||
module_api: detachable CMS_MODULE_API
|
||||
-- Eventual module api.
|
||||
|
||||
@@ -14,6 +14,8 @@ inherit
|
||||
rename
|
||||
is_initialized as module_is_initialized,
|
||||
is_enabled as module_is_enabled
|
||||
redefine
|
||||
module_api
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
@@ -53,6 +55,14 @@ feature -- Status
|
||||
Result := module.is_enabled
|
||||
end
|
||||
|
||||
feature {CMS_API, CMS_MODULE_ADMINISTRATION, CMS_MODULE_WEBAPI} -- Access: API
|
||||
|
||||
module_api: detachable CMS_MODULE_API
|
||||
-- Eventual module api.
|
||||
do
|
||||
Result := module.module_api
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Interface providing administration module."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_WITH_MODULE_ADMINISTRATION
|
||||
|
||||
feature -- Administration
|
||||
|
||||
module_administration: like administration
|
||||
-- Associated administration module.
|
||||
do
|
||||
Result := internal_module_administration
|
||||
if Result = Void then
|
||||
Result := administration
|
||||
internal_module_administration := Result
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
internal_module_administration: detachable like module_administration
|
||||
-- Cached version of `module_administration`.
|
||||
|
||||
feature {NONE} -- Administration
|
||||
|
||||
administration: CMS_MODULE_ADMINISTRATION [CMS_MODULE]
|
||||
-- Administration module.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
note
|
||||
description: "[
|
||||
Processes a HTTP request, and depending on header, authenticate a current user or not.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as auth_execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API)
|
||||
-- Initialize Current handler with `a_api'.
|
||||
do
|
||||
api := a_api
|
||||
end
|
||||
|
||||
feature -- API Service
|
||||
|
||||
api: CMS_API
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
auth_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
-- If user is already authenticated, do not try current authenticating filter
|
||||
-- and go to next filter directly.
|
||||
if api.user_is_authenticated then
|
||||
execute_next (req, res)
|
||||
else
|
||||
execute (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
require
|
||||
no_user_authenticated: api.user = Void
|
||||
deferred
|
||||
end
|
||||
|
||||
set_current_user (u: CMS_USER)
|
||||
do
|
||||
api.set_user (u)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
note
|
||||
description: "[
|
||||
Generic CMS Response.
|
||||
Generic CMS Web Response.
|
||||
It builds the content to get process to render the output.
|
||||
]"
|
||||
date: "$Date$"
|
||||
@@ -10,64 +10,33 @@ deferred class
|
||||
CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
CMS_URL_UTILITIES
|
||||
CMS_RESPONSE_I
|
||||
redefine
|
||||
make, initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
CMS_URL_UTILITIES
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
status_code := {HTTP_STATUS_CODE}.ok
|
||||
api := a_api
|
||||
request := req
|
||||
response := res
|
||||
create header.make
|
||||
create values.make (3)
|
||||
site_url := a_api.site_url
|
||||
if attached a_api.base_url as l_base_url then
|
||||
base_url := l_base_url
|
||||
end
|
||||
base_path := a_api.base_path
|
||||
initialize
|
||||
Precursor (req, res, a_api)
|
||||
end
|
||||
|
||||
initialize
|
||||
local
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
Precursor
|
||||
get_theme
|
||||
create menu_system.make
|
||||
initialize_block_region_settings
|
||||
|
||||
s := request.percent_encoded_path_info
|
||||
if not s.is_empty and then s[1] = '/' then
|
||||
create location.make_from_string (s.substring (2, s.count))
|
||||
else
|
||||
create location.make_from_string (s)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
request: WSF_REQUEST
|
||||
|
||||
response: WSF_RESPONSE
|
||||
|
||||
status_code: INTEGER
|
||||
|
||||
header: WSF_HEADER
|
||||
|
||||
main_content: detachable STRING_8
|
||||
|
||||
feature -- Settings
|
||||
|
||||
is_administration_mode: BOOLEAN
|
||||
-- Is administration mode?
|
||||
do
|
||||
Result := api.is_administration_mode
|
||||
end
|
||||
|
||||
feature -- Access: metadata
|
||||
|
||||
title: detachable READABLE_STRING_32
|
||||
@@ -77,96 +46,19 @@ feature -- Access: metadata
|
||||
|
||||
description: detachable READABLE_STRING_32
|
||||
|
||||
keywords: detachable READABLE_STRING_32
|
||||
|
||||
publication_date: detachable DATE_TIME
|
||||
-- Optional publication date.
|
||||
|
||||
modification_date: detachable DATE_TIME
|
||||
-- Optional modification date.
|
||||
|
||||
additional_page_head_lines: detachable LIST [READABLE_STRING_8]
|
||||
-- HTML>head>...extra lines
|
||||
|
||||
redirection: detachable READABLE_STRING_8
|
||||
-- Location for eventual redirection.
|
||||
|
||||
redirection_delay: NATURAL
|
||||
-- Optional redirection delay in seconds.
|
||||
|
||||
feature -- Access: query
|
||||
|
||||
location: IMMUTABLE_STRING_8
|
||||
-- Associated cms local location.
|
||||
|
||||
request_url (opts: detachable CMS_API_OPTIONS): STRING_8
|
||||
-- Current request location as a url.
|
||||
do
|
||||
Result := url (location, opts)
|
||||
end
|
||||
|
||||
feature -- API
|
||||
|
||||
api: CMS_API
|
||||
-- Current CMS API.
|
||||
|
||||
setup: CMS_SETUP
|
||||
-- Current setup
|
||||
do
|
||||
Result := api.setup
|
||||
end
|
||||
|
||||
formats: CMS_FORMATS
|
||||
-- Available content formats.
|
||||
do
|
||||
Result := api.formats
|
||||
end
|
||||
|
||||
feature -- URL utilities
|
||||
|
||||
is_front: BOOLEAN
|
||||
-- Is current response related to "front" page?
|
||||
local
|
||||
l_path_info: READABLE_STRING_8
|
||||
do
|
||||
l_path_info := request.percent_encoded_path_info
|
||||
if attached setup.front_page_path as l_front_page_path then
|
||||
Result := l_front_page_path.same_string (l_path_info)
|
||||
else
|
||||
if base_path.same_string (l_path_info) then
|
||||
Result := True
|
||||
else
|
||||
Result := l_path_info.is_empty or else l_path_info.same_string ("/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
site_url: IMMUTABLE_STRING_8
|
||||
-- Absolute site url.
|
||||
-- Always ends with '/'
|
||||
|
||||
base_url: detachable IMMUTABLE_STRING_8
|
||||
-- Base url if any.
|
||||
--| Usually it is Void, but it could be
|
||||
--| /project/demo/
|
||||
|
||||
base_path: IMMUTABLE_STRING_8
|
||||
-- Base path, default to "/".
|
||||
-- Always ends with '/'
|
||||
-- Could be /project/demo/
|
||||
|
||||
feature -- Access: CMS
|
||||
|
||||
site_name: STRING_32
|
||||
do
|
||||
Result := setup.site_name
|
||||
end
|
||||
|
||||
front_page_url: READABLE_STRING_8
|
||||
do
|
||||
Result := absolute_url ("/", Void)
|
||||
end
|
||||
|
||||
values: CMS_VALUE_TABLE
|
||||
-- Associated values indexed by string name.
|
||||
|
||||
@@ -177,72 +69,6 @@ feature -- Specific values
|
||||
Result := values.item ("optional_content_type")
|
||||
end
|
||||
|
||||
feature -- User access
|
||||
|
||||
is_authenticated: BOOLEAN
|
||||
-- Is user authenticated?
|
||||
do
|
||||
Result := user /= Void
|
||||
end
|
||||
|
||||
user: detachable CMS_USER
|
||||
-- Active user if authenticated.
|
||||
do
|
||||
Result := api.user
|
||||
end
|
||||
|
||||
set_user (u: CMS_USER)
|
||||
-- Set active user to `u'.
|
||||
require
|
||||
attached_u: u /= Void
|
||||
do
|
||||
api.set_user (u)
|
||||
end
|
||||
|
||||
unset_user
|
||||
-- Unset active user.
|
||||
do
|
||||
api.unset_user
|
||||
end
|
||||
|
||||
feature -- Permission
|
||||
|
||||
has_permission_on_link (a_link: CMS_LINK): BOOLEAN
|
||||
-- Does current user has permission to access link `a_link'?
|
||||
do
|
||||
Result := True
|
||||
if
|
||||
attached {CMS_LOCAL_LINK} a_link as lnk and then
|
||||
attached lnk.permission_arguments as l_perms
|
||||
then
|
||||
Result := has_permissions (l_perms)
|
||||
end
|
||||
end
|
||||
|
||||
has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Does current user has permission `a_permission' ?
|
||||
do
|
||||
Result := user_has_permission (user, a_permission)
|
||||
end
|
||||
|
||||
has_permissions (a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
|
||||
-- Does current user has any of the permissions `a_permission_list' ?
|
||||
do
|
||||
Result := user_has_permissions (user, a_permission_list)
|
||||
end
|
||||
|
||||
user_has_permission (a_user: detachable CMS_USER; a_permission: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Does `a_user' has permission `a_permission' ?
|
||||
do
|
||||
Result := api.user_has_permission (a_user, a_permission)
|
||||
end
|
||||
|
||||
user_has_permissions (a_user: detachable CMS_USER; a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
|
||||
-- Does `a_user' has any of the permissions `a_permission_list' ?
|
||||
do
|
||||
Result := api.user_has_permissions (a_user, a_permission_list)
|
||||
end
|
||||
|
||||
feature -- Head customization
|
||||
|
||||
add_additional_head_line (s: READABLE_STRING_8; a_allow_duplication: BOOLEAN)
|
||||
@@ -322,27 +148,6 @@ feature -- Element change
|
||||
description := d
|
||||
end
|
||||
|
||||
set_keywords (s: like keywords)
|
||||
do
|
||||
keywords := s
|
||||
end
|
||||
|
||||
set_publication_date (dt: like publication_date)
|
||||
do
|
||||
publication_date := dt
|
||||
if dt /= Void and modification_date = Void then
|
||||
modification_date := dt
|
||||
end
|
||||
end
|
||||
|
||||
set_modification_date (dt: like modification_date)
|
||||
do
|
||||
modification_date := dt
|
||||
if dt /= Void and publication_date = Void then
|
||||
publication_date := dt
|
||||
end
|
||||
end
|
||||
|
||||
set_main_content (s: like main_content)
|
||||
do
|
||||
main_content := s
|
||||
@@ -365,34 +170,6 @@ feature -- Element change
|
||||
values.remove (k)
|
||||
end
|
||||
|
||||
set_redirection (a_location: READABLE_STRING_8)
|
||||
-- Set `redirection' to `a_location'.
|
||||
do
|
||||
redirection := a_location
|
||||
end
|
||||
|
||||
set_redirection_delay (nb_secs: NATURAL)
|
||||
do
|
||||
redirection_delay := nb_secs
|
||||
end
|
||||
|
||||
feature -- Logging
|
||||
|
||||
log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK)
|
||||
local
|
||||
-- l_log: CMS_LOG
|
||||
do
|
||||
debug
|
||||
to_implement ("Add implementation")
|
||||
end
|
||||
-- create l_log.make (a_category, a_message, a_level, Void)
|
||||
-- if a_link /= Void then
|
||||
-- l_log.set_link (a_link)
|
||||
-- end
|
||||
-- l_log.set_info (request.http_user_agent)
|
||||
-- service.storage.save_log (l_log)
|
||||
end
|
||||
|
||||
feature -- Menu
|
||||
|
||||
menu_system: CMS_MENU_SYSTEM
|
||||
@@ -904,16 +681,6 @@ feature -- Blocks
|
||||
Result.set_is_raw (True)
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
hooks: CMS_HOOK_CORE_MANAGER
|
||||
-- Manager handling hook subscriptions.
|
||||
obsolete
|
||||
"Use api.hooks [2017-05-31]"
|
||||
do
|
||||
Result := api.hooks
|
||||
end
|
||||
|
||||
feature -- Menu: change
|
||||
|
||||
add_to_main_menu (lnk: CMS_LINK)
|
||||
@@ -938,22 +705,6 @@ feature -- Menu: change
|
||||
m.extend (lnk)
|
||||
end
|
||||
|
||||
feature -- Internationalization (i18n)
|
||||
|
||||
translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32
|
||||
-- Translated text `a_text' according to expected context (lang, ...)
|
||||
-- and adapt according to options eventually set by `opts'.
|
||||
do
|
||||
Result := api.translation (a_text, opts)
|
||||
end
|
||||
|
||||
formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32
|
||||
-- Format `a_text' using arguments `args'.
|
||||
--| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact"
|
||||
do
|
||||
Result := api.formatted_string (a_text, args)
|
||||
end
|
||||
|
||||
feature -- Message
|
||||
|
||||
add_message (a_msg: READABLE_STRING_8; a_category: detachable READABLE_STRING_8)
|
||||
@@ -1068,19 +819,6 @@ feature {NONE} -- Theme helpers
|
||||
internal_wsf_theme: detachable WSF_THEME
|
||||
-- Once per object for `wsf_theme'.
|
||||
|
||||
feature -- Element Change
|
||||
|
||||
set_status_code (a_status: INTEGER)
|
||||
-- Set `status_code' with `a_status'.
|
||||
note
|
||||
EIS: "src=eiffel:?class=HTTP_STATUS_CODE"
|
||||
do
|
||||
to_implement ("Feature to test if a_status is a valid status code!!!.")
|
||||
status_code := a_status
|
||||
ensure
|
||||
status_code_set: status_code = a_status
|
||||
end
|
||||
|
||||
feature -- Cache managment
|
||||
|
||||
clear_cache (a_cache_id_list: detachable ITERABLE [READABLE_STRING_GENERAL])
|
||||
@@ -1366,32 +1104,8 @@ feature -- Generation
|
||||
a_lnk.set_is_forbidden (not has_permission_on_link (a_lnk))
|
||||
end
|
||||
|
||||
feature -- Helpers: cms link
|
||||
|
||||
administration_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
require
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
do
|
||||
Result := api.administration_link (a_title, a_relative_location)
|
||||
end
|
||||
|
||||
local_link (a_title: READABLE_STRING_GENERAL; a_location: READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
do
|
||||
Result := api.local_link (a_title, a_location)
|
||||
end
|
||||
|
||||
user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
|
||||
do
|
||||
Result := api.user_local_link (u, a_opt_title)
|
||||
end
|
||||
|
||||
feature -- Helpers: html links
|
||||
|
||||
user_profile_name, user_display_name (u: CMS_USER): READABLE_STRING_32
|
||||
do
|
||||
Result := api.user_display_name (u)
|
||||
end
|
||||
|
||||
user_html_link (u: CMS_USER): STRING
|
||||
require
|
||||
u_with_name: not u.name.is_whitespace
|
||||
@@ -1399,43 +1113,6 @@ feature -- Helpers: html links
|
||||
Result := api.user_html_link (u)
|
||||
end
|
||||
|
||||
feature -- Helpers: URLs
|
||||
|
||||
location_absolute_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
|
||||
-- Absolute URL for `a_location'.
|
||||
--| Options `opts' could be
|
||||
--| - absolute: True|False => return absolute url
|
||||
--| - query: string => append "?query"
|
||||
--| - fragment: string => append "#fragment"
|
||||
do
|
||||
Result := api.location_absolute_url (a_location, opts)
|
||||
end
|
||||
|
||||
location_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
|
||||
-- URL for `a_location'.
|
||||
--| Options `opts' could be
|
||||
--| - absolute: True|False => return absolute url
|
||||
--| - query: string => append "?query"
|
||||
--| - fragment: string => append "#fragment"
|
||||
do
|
||||
Result := api.location_url (a_location, opts)
|
||||
end
|
||||
|
||||
module_resource_url (a_module: CMS_MODULE; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
|
||||
-- Url for resource `a_path` associated with module `a_module`.
|
||||
require
|
||||
a_valid_valid: a_path.is_empty or else a_path.starts_with ("/")
|
||||
do
|
||||
Result := url ("/module/" + a_module.name + a_path, opts)
|
||||
end
|
||||
|
||||
user_url (u: CMS_USER): like url
|
||||
require
|
||||
u_with_id: u.has_id
|
||||
do
|
||||
Result := api.user_url (u)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
@@ -1499,7 +1176,6 @@ feature {NONE} -- Execution
|
||||
|
||||
on_terminated
|
||||
do
|
||||
|
||||
end
|
||||
|
||||
note
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
note
|
||||
description: "[
|
||||
Generic CMS Response.
|
||||
It builds the content to get process to render the output.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_RESPONSE_I
|
||||
|
||||
inherit
|
||||
CMS_URL_UTILITIES
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
status_code := {HTTP_STATUS_CODE}.ok
|
||||
api := a_api
|
||||
request := req
|
||||
response := res
|
||||
create header.make
|
||||
site_url := a_api.site_url
|
||||
if attached a_api.base_url as l_base_url then
|
||||
base_url := l_base_url
|
||||
end
|
||||
base_path := a_api.base_path
|
||||
initialize
|
||||
end
|
||||
|
||||
initialize
|
||||
local
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
s := request.percent_encoded_path_info
|
||||
if not s.is_empty and then s[1] = '/' then
|
||||
create location.make_from_string (s.substring (2, s.count))
|
||||
else
|
||||
create location.make_from_string (s)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
request: WSF_REQUEST
|
||||
|
||||
response: WSF_RESPONSE
|
||||
|
||||
status_code: INTEGER
|
||||
|
||||
header: WSF_HEADER
|
||||
|
||||
feature -- Settings
|
||||
|
||||
is_site_mode: BOOLEAN
|
||||
-- Is site mode?
|
||||
do
|
||||
Result := api.is_site_mode
|
||||
end
|
||||
|
||||
is_webapi_mode: BOOLEAN
|
||||
-- Is Web API mode?
|
||||
do
|
||||
Result := api.is_webapi_mode
|
||||
end
|
||||
|
||||
is_administration_mode: BOOLEAN
|
||||
-- Is administration mode?
|
||||
do
|
||||
Result := api.is_administration_mode
|
||||
end
|
||||
|
||||
feature -- Access: metadata
|
||||
|
||||
keywords: detachable READABLE_STRING_32
|
||||
|
||||
publication_date: detachable DATE_TIME
|
||||
-- Optional publication date.
|
||||
|
||||
modification_date: detachable DATE_TIME
|
||||
-- Optional modification date.
|
||||
|
||||
redirection: detachable READABLE_STRING_8
|
||||
-- Location for eventual redirection.
|
||||
|
||||
redirection_delay: NATURAL
|
||||
-- Optional redirection delay in seconds.
|
||||
|
||||
feature -- Access: query
|
||||
|
||||
location: IMMUTABLE_STRING_8
|
||||
-- Associated cms local location.
|
||||
|
||||
request_url (opts: detachable CMS_API_OPTIONS): STRING_8
|
||||
-- Current request location as a url.
|
||||
do
|
||||
Result := url (location, opts)
|
||||
end
|
||||
|
||||
feature -- API
|
||||
|
||||
api: CMS_API
|
||||
-- Current CMS API.
|
||||
|
||||
setup: CMS_SETUP
|
||||
-- Current setup
|
||||
do
|
||||
Result := api.setup
|
||||
end
|
||||
|
||||
feature -- URL utilities
|
||||
|
||||
is_front: BOOLEAN
|
||||
-- Is current response related to "front" page?
|
||||
local
|
||||
l_path_info: READABLE_STRING_8
|
||||
do
|
||||
l_path_info := request.percent_encoded_path_info
|
||||
if attached setup.front_page_path as l_front_page_path then
|
||||
Result := l_front_page_path.same_string (l_path_info)
|
||||
else
|
||||
if base_path.same_string (l_path_info) then
|
||||
Result := True
|
||||
else
|
||||
Result := l_path_info.is_empty or else l_path_info.same_string ("/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
site_url: IMMUTABLE_STRING_8
|
||||
-- Absolute site url.
|
||||
-- Always ends with '/'
|
||||
|
||||
base_url: detachable IMMUTABLE_STRING_8
|
||||
-- Base url if any.
|
||||
--| Usually it is Void, but it could be
|
||||
--| /project/demo/
|
||||
|
||||
base_path: IMMUTABLE_STRING_8
|
||||
-- Base path, default to "/".
|
||||
-- Always ends with '/'
|
||||
-- Could be /project/demo/
|
||||
|
||||
feature -- Access: CMS
|
||||
|
||||
site_name: STRING_32
|
||||
do
|
||||
Result := setup.site_name
|
||||
end
|
||||
|
||||
front_page_url: READABLE_STRING_8
|
||||
do
|
||||
Result := absolute_url ("/", Void)
|
||||
end
|
||||
|
||||
feature -- User access
|
||||
|
||||
is_authenticated: BOOLEAN
|
||||
-- Is user authenticated?
|
||||
do
|
||||
Result := user /= Void
|
||||
end
|
||||
|
||||
user: detachable CMS_USER
|
||||
-- Active user if authenticated.
|
||||
do
|
||||
Result := api.user
|
||||
end
|
||||
|
||||
set_user (u: CMS_USER)
|
||||
-- Set active user to `u'.
|
||||
require
|
||||
attached_u: u /= Void
|
||||
do
|
||||
api.set_user (u)
|
||||
end
|
||||
|
||||
unset_user
|
||||
-- Unset active user.
|
||||
do
|
||||
api.unset_user
|
||||
end
|
||||
|
||||
feature -- Permission
|
||||
|
||||
has_permission_on_link (a_link: CMS_LINK): BOOLEAN
|
||||
-- Does current user has permission to access link `a_link'?
|
||||
do
|
||||
Result := True
|
||||
if
|
||||
attached {CMS_LOCAL_LINK} a_link as lnk and then
|
||||
attached lnk.permission_arguments as l_perms
|
||||
then
|
||||
Result := has_permissions (l_perms)
|
||||
end
|
||||
end
|
||||
|
||||
has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Does current user has permission `a_permission' ?
|
||||
do
|
||||
Result := user_has_permission (user, a_permission)
|
||||
end
|
||||
|
||||
has_permissions (a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
|
||||
-- Does current user has any of the permissions `a_permission_list' ?
|
||||
do
|
||||
Result := user_has_permissions (user, a_permission_list)
|
||||
end
|
||||
|
||||
user_has_permission (a_user: detachable CMS_USER; a_permission: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Does `a_user' has permission `a_permission' ?
|
||||
do
|
||||
Result := api.user_has_permission (a_user, a_permission)
|
||||
end
|
||||
|
||||
user_has_permissions (a_user: detachable CMS_USER; a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
|
||||
-- Does `a_user' has any of the permissions `a_permission_list' ?
|
||||
do
|
||||
Result := api.user_has_permissions (a_user, a_permission_list)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_keywords (s: like keywords)
|
||||
do
|
||||
keywords := s
|
||||
end
|
||||
|
||||
set_publication_date (dt: like publication_date)
|
||||
do
|
||||
publication_date := dt
|
||||
if dt /= Void and modification_date = Void then
|
||||
modification_date := dt
|
||||
end
|
||||
end
|
||||
|
||||
set_modification_date (dt: like modification_date)
|
||||
do
|
||||
modification_date := dt
|
||||
if dt /= Void and publication_date = Void then
|
||||
publication_date := dt
|
||||
end
|
||||
end
|
||||
|
||||
set_redirection (a_location: READABLE_STRING_8)
|
||||
-- Set `redirection' to `a_location'.
|
||||
do
|
||||
redirection := a_location
|
||||
end
|
||||
|
||||
set_redirection_delay (nb_secs: NATURAL)
|
||||
do
|
||||
redirection_delay := nb_secs
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
hooks: CMS_HOOK_CORE_MANAGER
|
||||
-- Manager handling hook subscriptions.
|
||||
obsolete
|
||||
"Use api.hooks [2017-05-31]"
|
||||
do
|
||||
Result := api.hooks
|
||||
end
|
||||
|
||||
feature -- Internationalization (i18n)
|
||||
|
||||
translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32
|
||||
-- Translated text `a_text' according to expected context (lang, ...)
|
||||
-- and adapt according to options eventually set by `opts'.
|
||||
do
|
||||
Result := api.translation (a_text, opts)
|
||||
end
|
||||
|
||||
formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32
|
||||
-- Format `a_text' using arguments `args'.
|
||||
--| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact"
|
||||
do
|
||||
Result := api.formatted_string (a_text, args)
|
||||
end
|
||||
|
||||
feature -- Element Change
|
||||
|
||||
set_status_code (a_status: INTEGER)
|
||||
-- Set `status_code' with `a_status'.
|
||||
note
|
||||
EIS: "src=eiffel:?class=HTTP_STATUS_CODE"
|
||||
do
|
||||
status_code := a_status
|
||||
ensure
|
||||
status_code_set: status_code = a_status
|
||||
end
|
||||
|
||||
feature -- Helpers: cms link
|
||||
|
||||
webapi_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
require
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
do
|
||||
Result := api.webapi_link (a_title, a_relative_location)
|
||||
end
|
||||
|
||||
administration_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
require
|
||||
no_first_slash: a_relative_location = Void or else not a_relative_location.starts_with_general ("/")
|
||||
do
|
||||
Result := api.administration_link (a_title, a_relative_location)
|
||||
end
|
||||
|
||||
local_link (a_title: READABLE_STRING_GENERAL; a_location: READABLE_STRING_8): CMS_LOCAL_LINK
|
||||
do
|
||||
Result := api.local_link (a_title, a_location)
|
||||
end
|
||||
|
||||
user_local_link (u: CMS_USER; a_opt_title: detachable READABLE_STRING_GENERAL): CMS_LOCAL_LINK
|
||||
do
|
||||
Result := api.user_local_link (u, a_opt_title)
|
||||
end
|
||||
|
||||
feature -- Helpers: html links
|
||||
|
||||
user_profile_name, user_display_name (u: CMS_USER): READABLE_STRING_32
|
||||
do
|
||||
Result := api.user_display_name (u)
|
||||
end
|
||||
|
||||
feature -- Helpers: URLs
|
||||
|
||||
location_absolute_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
|
||||
-- Absolute URL for `a_location'.
|
||||
--| Options `opts' could be
|
||||
--| - absolute: True|False => return absolute url
|
||||
--| - query: string => append "?query"
|
||||
--| - fragment: string => append "#fragment"
|
||||
do
|
||||
Result := api.location_absolute_url (a_location, opts)
|
||||
end
|
||||
|
||||
location_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
|
||||
-- URL for `a_location'.
|
||||
--| Options `opts' could be
|
||||
--| - absolute: True|False => return absolute url
|
||||
--| - query: string => append "?query"
|
||||
--| - fragment: string => append "#fragment"
|
||||
do
|
||||
Result := api.location_url (a_location, opts)
|
||||
end
|
||||
|
||||
module_resource_url (a_module: CMS_MODULE; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
|
||||
-- Url for resource `a_path` associated with module `a_module`.
|
||||
require
|
||||
a_valid_valid: a_path.is_empty or else a_path.starts_with ("/")
|
||||
do
|
||||
Result := url ("/module/" + a_module.name + a_path, opts)
|
||||
end
|
||||
|
||||
user_url (u: CMS_USER): like url
|
||||
require
|
||||
u_with_id: u.has_id
|
||||
do
|
||||
Result := api.user_url (u)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -7,7 +7,6 @@ class
|
||||
BAD_REQUEST_ERROR_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
@@ -37,6 +36,6 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -7,7 +7,6 @@ class
|
||||
FORBIDDEN_ERROR_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
|
||||
@@ -7,7 +7,6 @@ class
|
||||
INTERNAL_SERVER_ERROR_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
@@ -36,7 +35,7 @@ feature -- Execution
|
||||
set_main_content ("<em>Internal Server Error</em>")
|
||||
end
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ class
|
||||
NOT_FOUND_ERROR_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
@@ -36,7 +35,7 @@ feature -- Execution
|
||||
set_main_content ("<em>The requested page %"" + request.request_uri + "%"could not be found.</em>")
|
||||
end
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ class
|
||||
NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
@@ -38,7 +37,7 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ feature -- Execution
|
||||
do
|
||||
end
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
note
|
||||
description: "Summary description for Hyper media {HM_WEBAPI_RESPONSE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
HM_WEBAPI_RESPONSE
|
||||
|
||||
inherit
|
||||
WEBAPI_RESPONSE
|
||||
|
||||
feature -- Element change
|
||||
|
||||
add_self (a_href: READABLE_STRING_8)
|
||||
deferred
|
||||
end
|
||||
|
||||
add_string_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
|
||||
deferred
|
||||
end
|
||||
|
||||
add_boolean_field (a_name: READABLE_STRING_GENERAL; a_value: BOOLEAN)
|
||||
deferred
|
||||
end
|
||||
|
||||
add_integer_64_field (a_name: READABLE_STRING_GENERAL; a_value: INTEGER_64)
|
||||
deferred
|
||||
end
|
||||
|
||||
add_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: ITERABLE [detachable ANY])
|
||||
deferred
|
||||
end
|
||||
|
||||
add_table_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
|
||||
deferred
|
||||
end
|
||||
|
||||
add_link (rel: READABLE_STRING_8; a_attname: detachable READABLE_STRING_8; a_att_href: READABLE_STRING_8)
|
||||
deferred
|
||||
end
|
||||
|
||||
add_templated_link (rel: READABLE_STRING_8; a_attname: detachable READABLE_STRING_8; a_att_href: READABLE_STRING_8)
|
||||
deferred
|
||||
end
|
||||
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -7,7 +7,6 @@ class
|
||||
HOME_CMS_RESPONSE
|
||||
|
||||
inherit
|
||||
|
||||
CMS_RESPONSE
|
||||
redefine
|
||||
custom_prepare
|
||||
@@ -32,5 +31,8 @@ feature -- Execution
|
||||
set_title (Void)
|
||||
set_page_title (Void)
|
||||
end
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
note
|
||||
description: "Summary description for JSON {JSON_WEBAPI_RESPONSE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JSON_WEBAPI_RESPONSE
|
||||
|
||||
inherit
|
||||
HM_WEBAPI_RESPONSE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create resource.make_empty
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
resource: JSON_OBJECT
|
||||
|
||||
feature -- Element change
|
||||
|
||||
add_self (a_href: READABLE_STRING_8)
|
||||
do
|
||||
add_link ("self", Void, a_href)
|
||||
end
|
||||
|
||||
feature -- Fields
|
||||
|
||||
-- add_field (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY)
|
||||
-- do
|
||||
-- resource.put (new_resource_item (a_value), a_name)
|
||||
-- end
|
||||
|
||||
add_string_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
|
||||
do
|
||||
resource.put_string (a_value, a_name)
|
||||
end
|
||||
|
||||
add_boolean_field (a_name: READABLE_STRING_GENERAL; a_value: BOOLEAN)
|
||||
do
|
||||
resource.put_boolean (a_value, a_name)
|
||||
end
|
||||
|
||||
add_integer_64_field (a_name: READABLE_STRING_GENERAL; a_value: INTEGER_64)
|
||||
do
|
||||
resource.put_integer (a_value, a_name)
|
||||
end
|
||||
|
||||
add_natural_64_field (a_name: READABLE_STRING_GENERAL; a_value: NATURAL_64)
|
||||
do
|
||||
resource.put_natural (a_value, a_name)
|
||||
end
|
||||
|
||||
add_real_64_field (a_name: READABLE_STRING_GENERAL; a_value: REAL_64)
|
||||
do
|
||||
resource.put_real (a_value, a_name)
|
||||
end
|
||||
|
||||
add_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: ITERABLE [detachable ANY])
|
||||
do
|
||||
resource.put (new_resource_item (a_value), a_name)
|
||||
end
|
||||
|
||||
add_table_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
|
||||
do
|
||||
resource.put (new_resource_item (a_value), a_name)
|
||||
end
|
||||
|
||||
feature -- Links
|
||||
|
||||
add_link (rel: READABLE_STRING_8; a_attname: detachable READABLE_STRING_8 ; a_att_href: READABLE_STRING_8)
|
||||
local
|
||||
lnks: JSON_OBJECT
|
||||
lnk: JSON_OBJECT
|
||||
do
|
||||
if attached {JSON_OBJECT} resource.item ("_links") as j_links then
|
||||
lnks := j_links
|
||||
else
|
||||
create lnks.make_with_capacity (1)
|
||||
resource.put (lnks, "_links")
|
||||
end
|
||||
create lnk.make_with_capacity (2)
|
||||
if a_attname /= Void then
|
||||
lnk.put_string (a_attname, "name")
|
||||
end
|
||||
|
||||
lnk.put_string (a_att_href, "href")
|
||||
lnks.put (lnk, rel)
|
||||
end
|
||||
|
||||
add_templated_link (rel: READABLE_STRING_8; a_attname: detachable READABLE_STRING_8; a_att_href: READABLE_STRING_8)
|
||||
local
|
||||
lnks: JSON_OBJECT
|
||||
lnk: JSON_OBJECT
|
||||
do
|
||||
if attached {JSON_OBJECT} resource.item ("_links") as j_links then
|
||||
lnks := j_links
|
||||
else
|
||||
create lnks.make_with_capacity (1)
|
||||
resource.put (lnks, "_links")
|
||||
end
|
||||
create lnk.make_with_capacity (2)
|
||||
if a_attname /= Void then
|
||||
lnk.put_string (a_attname, "name")
|
||||
end
|
||||
|
||||
lnk.put_string (a_att_href, "href")
|
||||
lnk.put_boolean (True, "templated")
|
||||
lnks.put (lnk, rel)
|
||||
end
|
||||
|
||||
|
||||
feature -- Execution
|
||||
|
||||
process
|
||||
local
|
||||
m: WSF_PAGE_RESPONSE
|
||||
do
|
||||
create m.make_with_body (resource.representation)
|
||||
m.header.put_content_type ("application/json")
|
||||
response.send (m)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation factory
|
||||
|
||||
new_resource_item (a_value: detachable ANY): JSON_VALUE
|
||||
local
|
||||
l_serializer: JSON_REFLECTOR_SERIALIZER
|
||||
ctx: JSON_SERIALIZER_CONTEXT
|
||||
do
|
||||
create {JSON_NULL} Result
|
||||
|
||||
create l_serializer
|
||||
create ctx
|
||||
ctx.set_default_serializer (l_serializer)
|
||||
ctx.set_is_type_name_included (False)
|
||||
ctx.register_serializer (create {TABLE_ITERABLE_JSON_SERIALIZER [detachable ANY, READABLE_STRING_GENERAL]}, {TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]})
|
||||
ctx.register_serializer (create {ITERABLE_JSON_SERIALIZER [detachable ANY]}, {ITERABLE [detachable ANY]})
|
||||
Result := l_serializer.to_json (a_value, ctx)
|
||||
-- if a_value = Void then
|
||||
-- create {JSON_NULL} Result
|
||||
-- elseif attached {READABLE_STRING_GENERAL} a_value as s then
|
||||
-- create {JSON_STRING} Result.make_from_string_general (s)
|
||||
-- elseif attached {BOOLEAN} a_value as b then
|
||||
-- create {JSON_BOOLEAN} Result.make (b)
|
||||
-- elseif attached {NUMERIC} a_value as num then
|
||||
---- if attached {INTEGER_64} num as i64 then
|
||||
---- add_integer_64_field (a_name, i64)
|
||||
---- elseif attached {INTEGER_32} num as i32 then
|
||||
---- add_integer_64_field (a_name, i32.as_integer_64)
|
||||
---- elseif attached {INTEGER_16} num as i16 then
|
||||
---- add_integer_64_field (a_name, i16.as_integer_64)
|
||||
---- elseif attached {INTEGER_8} num as i8 then
|
||||
---- add_integer_64_field (a_name, i8.as_integer_64)
|
||||
---- elseif attached {NATURAL_64} num as n64 then
|
||||
---- add_natural_64_field (a_name, n64)
|
||||
---- elseif attached {NATURAL_32} num as n32 then
|
||||
---- add_natural_64_field (a_name, n32.as_natural_64)
|
||||
---- elseif attached {NATURAL_16} num as n16 then
|
||||
---- add_natural_64_field (a_name, n16.as_natural_64)
|
||||
---- elseif attached {NATURAL_8} num as n8 then
|
||||
---- add_natural_64_field (a_name, n8.as_natural_64)
|
||||
---- elseif attached {REAL_64} num as r64 then
|
||||
---- add_real_64_field (a_name, r64)
|
||||
---- elseif attached {REAL_32} num as r32 then
|
||||
---- add_real_64_field (a_name, r32.to_double
|
||||
---- else
|
||||
---- check is_basic_numeric_type: False end
|
||||
---- add_string_field (a_name, num.out)
|
||||
---- end
|
||||
-- elseif attached {CHARACTER_8} a_value as ch8 then
|
||||
---- add_string_field (a_name, ch8.out)
|
||||
-- elseif attached {CHARACTER_32} a_value as ch32 then
|
||||
---- add_string_field (a_name, ch32.out)
|
||||
-- elseif attached {POINTER} a_value as ptr then
|
||||
---- add_string_field (a_name, ptr.out)
|
||||
-- elseif attached {ITERABLE [detachable ANY]} a_value as arr then
|
||||
---- add_iterator_field (a_name, arr)
|
||||
-- elseif attached {TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]} a_value as tb then
|
||||
---- add_table_iterator_field (a_name, tb)
|
||||
-- else
|
||||
-- check is_supported_type: False end
|
||||
-- end
|
||||
end
|
||||
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
note
|
||||
description: "Summary description for Microdata {MD_WEBAPI_RESPONSE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MD_WEBAPI_RESPONSE
|
||||
|
||||
inherit
|
||||
HM_WEBAPI_RESPONSE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create resource.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
resource: MD_DOCUMENT
|
||||
|
||||
feature -- Element change
|
||||
|
||||
add_self (a_href: READABLE_STRING_8)
|
||||
do
|
||||
add_field ("self", a_href)
|
||||
end
|
||||
|
||||
add_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
|
||||
local
|
||||
p: MD_PROPERTY
|
||||
do
|
||||
create p.make ("self", a_value, Void)
|
||||
resource.put (p)
|
||||
end
|
||||
|
||||
add_link (rel: READABLE_STRING_8; a_attname: READABLE_STRING_8 ; a_att_href: READABLE_STRING_8)
|
||||
local
|
||||
i: MD_ITEM
|
||||
do
|
||||
-- TODO
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
local
|
||||
m: WSF_PAGE_RESPONSE
|
||||
do
|
||||
-- TODO
|
||||
create m.make_with_body ("NOT IMPLEMENTED")
|
||||
m.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
|
||||
m.header.put_content_type ("application/xhtml+xml")
|
||||
response.send (m)
|
||||
end
|
||||
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
note
|
||||
description: "[
|
||||
Generic Web API Response.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEBAPI_RESPONSE
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE_I
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_root: BOOLEAN
|
||||
-- Is current response related to root api endpoint?
|
||||
local
|
||||
l_path_info: READABLE_STRING_8
|
||||
do
|
||||
l_path_info := request.percent_encoded_path_info
|
||||
if l_path_info.ends_with_general ("/") then
|
||||
l_path_info := l_path_info.substring (1, l_path_info.count - 1)
|
||||
end
|
||||
Result := l_path_info.same_string (api.setup.webapi_base_path)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
do
|
||||
api.hooks.invoke_webapi_response_alter (Current)
|
||||
process
|
||||
end
|
||||
|
||||
process
|
||||
-- Execute Current webapi response.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,90 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that ...
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_MODULE_WEBAPI [G -> CMS_MODULE]
|
||||
|
||||
inherit
|
||||
CMS_MODULE
|
||||
rename
|
||||
is_initialized as module_is_initialized,
|
||||
is_enabled as module_is_enabled
|
||||
redefine
|
||||
module_api
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_module: G)
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
module := a_module
|
||||
version := a_module.version
|
||||
description := a_module.description
|
||||
package := a_module.package
|
||||
|
||||
module_is_initialized := a_module.is_initialized
|
||||
module_is_enabled := a_module.is_enabled
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
module: G
|
||||
|
||||
name: STRING
|
||||
do
|
||||
Result := module.name
|
||||
end
|
||||
|
||||
feature {CMS_API, CMS_MODULE_ADMINISTRATION, CMS_MODULE_WEBAPI} -- Access: API
|
||||
|
||||
module_api: detachable CMS_MODULE_API
|
||||
-- Eventual module api.
|
||||
do
|
||||
Result := module.module_api
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_initialized: BOOLEAN
|
||||
-- Is Current module initialized?
|
||||
do
|
||||
Result := module.is_initialized
|
||||
end
|
||||
|
||||
is_enabled: BOOLEAN
|
||||
-- Is Current module enabled?
|
||||
do
|
||||
Result := module.is_enabled
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
if a_router.base_url /= Void then
|
||||
setup_webapi_router (a_router, a_api)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Router/administration
|
||||
|
||||
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- Setup url dispatching for Current module web API.
|
||||
-- (note: `a_router` is already based with webapi path prefix).
|
||||
require
|
||||
is_initialized: is_initialized
|
||||
router_has_base_url: a_router.base_url /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
note
|
||||
description: "Summary description for {CMS_WEBAPI_AUTH_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,100 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that ...
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
WSF_HANDLER
|
||||
|
||||
CMS_API_ACCESS
|
||||
|
||||
CMS_ENCODERS
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API)
|
||||
-- Initialize Current handler with `a_api'.
|
||||
do
|
||||
api := a_api
|
||||
end
|
||||
|
||||
feature -- API Service
|
||||
|
||||
api: CMS_API
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_webapi_response (req: WSF_REQUEST; res: WSF_RESPONSE): HM_WEBAPI_RESPONSE
|
||||
do
|
||||
-- create {MD_WEBAPI_RESPONSE} Result.make (req, res, api)
|
||||
create {JSON_WEBAPI_RESPONSE} Result.make (req, res, api)
|
||||
end
|
||||
|
||||
new_wepapi_error_response (msg: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE): HM_WEBAPI_RESPONSE
|
||||
do
|
||||
Result := new_webapi_response (req, res)
|
||||
if msg /= Void then
|
||||
Result.add_string_field ("error", msg)
|
||||
else
|
||||
Result.add_string_field ("error", "True")
|
||||
end
|
||||
end
|
||||
|
||||
send_not_found (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
if m /= Void then
|
||||
rep := new_wepapi_error_response (m, req, res)
|
||||
else
|
||||
rep := new_wepapi_error_response ("Not found", req, res)
|
||||
end
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
rep.execute
|
||||
end
|
||||
|
||||
send_access_denied (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
if m /= Void then
|
||||
rep := new_wepapi_error_response (m, req, res)
|
||||
else
|
||||
rep := new_wepapi_error_response ("Access denied", req, res)
|
||||
end
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
|
||||
rep.execute
|
||||
end
|
||||
|
||||
send_bad_request (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
if m /= Void then
|
||||
rep := new_wepapi_error_response (m, req, res)
|
||||
else
|
||||
rep := new_wepapi_error_response ("Bad request", req, res)
|
||||
end
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request)
|
||||
rep.execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Builder
|
||||
|
||||
add_user_links_to (u: CMS_USER; rep: HM_WEBAPI_RESPONSE)
|
||||
do
|
||||
rep.add_link ("account", "user/" + u.id.out, rep.api.webapi_path ("/user/" + u.id.out))
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Interface providing webapi module."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_WITH_WEBAPI
|
||||
|
||||
feature -- Administration
|
||||
|
||||
module_webapi: like webapi
|
||||
-- Associated web api module.
|
||||
do
|
||||
Result := internal_module_webapi
|
||||
if Result = Void then
|
||||
Result := webapi
|
||||
internal_module_webapi := Result
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
internal_module_webapi: detachable like module_webapi
|
||||
-- Cached version of `module_webapi`.
|
||||
|
||||
feature {NONE} -- Web API
|
||||
|
||||
webapi: CMS_MODULE_WEBAPI [CMS_MODULE]
|
||||
-- Web API module.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
Reference in New Issue
Block a user