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:
Jocelyn Fiat
2017-09-05 15:54:40 +02:00
parent 34f0aa5844
commit ac9d29b971
88 changed files with 3552 additions and 553 deletions
+2 -23
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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.
+10
View File
@@ -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
+55
View File
@@ -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
+8 -332
View File
@@ -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
+376
View File
@@ -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
+51
View File
@@ -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
+3 -1
View File
@@ -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
+201
View File
@@ -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
+70
View File
@@ -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
+44
View File
@@ -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
-524
View File
@@ -1,524 +0,0 @@
note
description: "API providing user related features."
date: "$Date$"
revision: "$Revision$"
class
CMS_USER_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create
make
feature -- Validation
is_valid_username (a_name: READABLE_STRING_32): BOOLEAN
local
c: CHARACTER_32
do
if a_name.is_empty or a_name.is_whitespace then
Result := False
elseif a_name[1].is_space then
Result := False
elseif a_name[a_name.count].is_space then
Result := False
else
Result := True
across
a_name as ic
until
not Result
loop
c := ic.item
if c.is_alpha_numeric or c = '-' or c = '_' then
else
Result := False
end
end
end
end
is_valid_profile_name (a_name: READABLE_STRING_32): BOOLEAN
local
c: CHARACTER_32
do
if a_name.is_empty or a_name.is_whitespace then
Result := False
elseif a_name[1].is_space then
Result := False
elseif a_name[a_name.count].is_space then
Result := False
else
Result := True
across
a_name as ic
until
not Result
loop
c := ic.item
if c.is_alpha_numeric or c = '-' or c = '_' or c.is_space or c = '%'' then
else
Result := False
end
end
end
end
feature -- Query
user_display_name (u: CMS_USER): READABLE_STRING_32
-- Display name for user `u`.
do
if attached u.profile_name as pn and then not pn.is_whitespace then
Result := pn
elseif not u.name.is_whitespace then
Result := u.name
else
Result := {STRING_32} "user #" + u.id.out
end
end
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)
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)
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)
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)
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)
end
users_count: INTEGER
-- Number of users.
do
Result := 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)
end
admin_user: detachable CMS_USER
-- Admin user if any.
do
if
attached user_by_id (1) as u and then
is_admin_user (u)
then
Result := u
end
end
feature -- Change User
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
require
no_id: not a_user.has_id
no_hashed_password: a_user.hashed_password = Void
do
reset_error
if
attached a_user.email as l_email
then
storage.new_user (a_user)
error_handler.append (storage.error_handler)
else
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
end
end
update_username (a_user: CMS_USER; a_new_username: READABLE_STRING_32)
-- Update username of `a_user' to `a_new_username'.
require
has_id: a_user.has_id
valid_user_name: is_valid_username (a_new_username)
user_by_name (a_new_username) = Void
do
reset_error
storage.update_username (a_user, a_new_username)
error_handler.append (storage.error_handler)
end
update_user (a_user: CMS_USER)
-- Update user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.update_user (a_user)
error_handler.append (storage.error_handler)
end
delete_user (a_user: CMS_USER)
-- Delete user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.delete_user (a_user)
error_handler.append (storage.error_handler)
end
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)
end
user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
-- Anonymous or user `a_user' has permission for `a_permission'?
--| `a_permission' could be for instance "create page".
do
if a_permission = Void then
Result := True
elseif a_user = Void then
Result := user_role_has_permission (anonymous_user_role, a_permission)
else
if is_admin_user (a_user) then
Result := True
else
Result := user_role_has_permission (authenticated_user_role, a_permission)
if not Result then
Result := across user_roles (a_user) as ic some user_role_has_permission (ic.item, a_permission) end
end
end
end
end
is_admin_user (u: CMS_USER): BOOLEAN
do
Result := u.id = 1
end
user_roles (a_user: CMS_USER): LIST [CMS_USER_ROLE]
local
l_roles: detachable LIST [CMS_USER_ROLE]
do
l_roles := a_user.roles
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)
end
Result := l_roles
end
feature -- User roles.
anonymous_user_role: CMS_USER_ROLE
do
if attached user_role_by_id (1) as l_anonymous then
Result := l_anonymous
else
create Result.make ("anonymous")
end
end
authenticated_user_role: CMS_USER_ROLE
do
if attached user_role_by_id (2) as l_authenticated then
Result := l_authenticated
else
create Result.make ("authenticated")
end
end
user_role_has_permission (a_role: CMS_USER_ROLE; a_permission: READABLE_STRING_GENERAL): BOOLEAN
do
Result := a_role.has_permission (a_permission)
end
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)
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)
end
role_permissions: HASH_TABLE [LIST [READABLE_STRING_8], STRING_8]
-- Possible known permissions indexed by modules.
local
lst, l_full_lst, l_used_permissions: LIST [READABLE_STRING_8]
do
create Result.make (cms_api.enabled_modules.count + 1)
l_used_permissions := storage.role_permissions
across
cms_api.enabled_modules as ic
loop
lst := ic.item.permissions
if attached {CMS_ADMINISTRABLE} ic.item as adm 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 (adm.module_administration.permissions)
lst := adm.module_administration.permissions
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
lst := l_full_lst
end
Result.force (lst, ic.item.name)
across
lst as p_ic
loop
from
l_used_permissions.start
until
l_used_permissions.after
loop
if l_used_permissions.item.is_case_insensitive_equal (p_ic.item) then
l_used_permissions.remove
l_used_permissions.finish
end
l_used_permissions.forth
end
end
if not l_used_permissions.is_empty then
Result.force (l_used_permissions, "")
end
end
end
roles: LIST [CMS_USER_ROLE]
-- List of possible roles.
do
Result := storage.user_roles
end
effective_roles: LIST [CMS_USER_ROLE]
-- List of possible roles, apart from anonymous and authenticated roles that are special.
local
l_roles: like roles
r: CMS_USER_ROLE
do
l_roles := storage.user_roles
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (l_roles.count)
across
l_roles as ic
loop
r := ic.item
if r.same_user_role (anonymous_user_role) or r.same_user_role (authenticated_user_role) then
-- Ignore
else
Result.force (r)
end
end
end
roles_count: INTEGER
-- Number of roles
do
Result := storage.user_roles.count
end
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)
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)
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)
end
delete_role (a_role: CMS_USER_ROLE)
do
reset_error
storage.delete_role (a_role)
error_handler.append (storage.error_handler)
end
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)
end
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)
end
remove_password (a_token: READABLE_STRING_32)
-- Remove password token `a_token', from the storage.
do
storage.remove_password (a_token)
end
feature -- User status
not_active: INTEGER = 0
-- The user is not active.
active: INTEGER = 1
-- The user is active
Trashed: INTEGER = -1
-- The user is trashed (soft delete), ready to be deleted/destroyed from storage.
feature -- Access - Temp User
temp_users_count: INTEGER
-- Number of pending users.
--! to be accepted or rehected
do
Result := storage.temp_users_count
end
temp_user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
-- User by name `a_user_name', if any.
do
Result := storage.temp_user_by_name (a_username.as_string_32)
end
temp_user_by_email (a_email: READABLE_STRING_8): detachable CMS_USER
-- User by email `a_email', if any.
do
Result := storage.temp_user_by_email (a_email)
end
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
-- User by activation token `a_token'.
do
Result := storage.temp_user_by_activation_token (a_token)
end
temp_recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_TEMP_USER]
-- List of the `a_rows' most recent users starting from `a_offset'.
do
Result := storage.temp_recent_users (params.offset.to_integer_32, params.size.to_integer_32)
end
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
do
Result := storage.token_by_temp_user_id (a_id)
end
feature -- Change Temp User
new_user_from_temp_user (a_temp_user: CMS_TEMP_USER)
-- Add a new user `a_temp_user'.
require
has_hashed_password: a_temp_user.hashed_password /= Void
has_sal: a_temp_user.salt /= Void
do
reset_error
if
attached a_temp_user.hashed_password as l_password and then
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)
else
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
end
end
new_temp_user (a_temp_user: CMS_TEMP_USER)
-- Add a new user `a_temp_user'.
require
no_id: not a_temp_user.has_id
no_hashed_password: a_temp_user.hashed_password = Void
do
reset_error
if
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)
else
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
end
end
remove_activation (a_token: READABLE_STRING_32)
-- Remove activation token `a_token', from the storage.
do
storage.remove_activation (a_token)
end
delete_temp_user (a_temp_user: CMS_TEMP_USER)
-- Delete user `a_temp_user'.
require
has_id: a_temp_user.has_id
do
reset_error
storage.delete_temp_user (a_temp_user)
error_handler.append (storage.error_handler)
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
+90
View File
@@ -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
+100
View File
@@ -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
+36
View File
@@ -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