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:
11
cms-safe.ecf
11
cms-safe.ecf
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-17-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-17-0 http://www.eiffel.com/developers/xml/configuration-1-17-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
|
||||
<target name="cms">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -7,9 +7,12 @@
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="all">
|
||||
<option warning="true">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<capability>
|
||||
<concurrency support="scoop" use="scoop"/>
|
||||
<void_safety support="all" use="all"/>
|
||||
</capability>
|
||||
<mapping old_name="CMS_LAYOUT" new_name="CMS_ENVIRONMENT"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
||||
@@ -20,9 +23,11 @@
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\web\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n-safe.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="kmp_matcher" location="$ISE_LIBRARY\library\text\regexp\kmp_matcher\kmp_matcher-safe.ecf"/>
|
||||
<library name="microdata" location="$ISE_LIBRARY\contrib\library\text\parser\microdata\microdata.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
|
||||
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty-safe.ecf" readonly="false"/>
|
||||
|
||||
1
cms.ecf
1
cms.ecf
@@ -20,6 +20,7 @@
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf" readonly="false"/>
|
||||
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\web\authentication\http_authorization\http_authorization.ecf"/>
|
||||
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
|
||||
<library name="kmp_matcher" location="$ISE_LIBRARY\library\text\regexp\kmp_matcher\kmp_matcher.ecf"/>
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"project": "demo-safe.ecf",
|
||||
"location": ".",
|
||||
"site_directory": "site",
|
||||
"themes": {
|
||||
"admin": { "location": "../../themes/admin", "mode": "link" }
|
||||
},
|
||||
"modules": {
|
||||
"demo": { "location": "modules/demo" },
|
||||
"core": { "location": "../../modules/core" },
|
||||
"admin": { "location": "../../modules/admin" },
|
||||
"auth": { "location": "../../modules/auth" },
|
||||
|
||||
@@ -57,13 +57,15 @@ output=site\db\mails
|
||||
#openid.token=
|
||||
#oauth.token=
|
||||
|
||||
[webapi]
|
||||
mode=on
|
||||
|
||||
[administration]
|
||||
base_path=/roc-admin
|
||||
#theme=admin
|
||||
theme=admin
|
||||
# CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none)
|
||||
installation_access=all
|
||||
|
||||
[dev]
|
||||
# masquerade: all, none, permission. Default is none.
|
||||
masquerade=none
|
||||
masquerade=all
|
||||
|
||||
@@ -98,19 +98,6 @@ feature -- Roles
|
||||
roles: detachable LIST [CMS_USER_ROLE]
|
||||
-- If set, list of roles for current user.
|
||||
|
||||
feature -- Access: data
|
||||
|
||||
item (k: READABLE_STRING_GENERAL): detachable ANY assign put
|
||||
-- Additional item data associated with key `k'.
|
||||
do
|
||||
if attached items as tb then
|
||||
Result := tb.item (k)
|
||||
end
|
||||
end
|
||||
|
||||
items: detachable STRING_TABLE [detachable ANY]
|
||||
-- Additional data.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_id: BOOLEAN
|
||||
@@ -223,29 +210,6 @@ feature -- Element change: roles
|
||||
roles := lst
|
||||
end
|
||||
|
||||
feature -- Change element: data
|
||||
|
||||
put (d: like item; k: READABLE_STRING_GENERAL)
|
||||
-- Associate data item `d' with key `k'.
|
||||
local
|
||||
tb: like items
|
||||
do
|
||||
tb := items
|
||||
if tb = Void then
|
||||
create tb.make (1)
|
||||
items := tb
|
||||
end
|
||||
tb.force (d, k)
|
||||
end
|
||||
|
||||
remove (k: READABLE_STRING_GENERAL)
|
||||
-- Remove data item associated with key `k'.
|
||||
do
|
||||
if attached items as tb then
|
||||
tb.remove (k)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status change
|
||||
|
||||
mark_not_active
|
||||
|
||||
72
library/model/src/user/cms_user_profile.e
Normal file
72
library/model/src/user/cms_user_profile.e
Normal file
@@ -0,0 +1,72 @@
|
||||
note
|
||||
description: "[
|
||||
User profile used to extend information associated with a {CMS_USER}.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE
|
||||
|
||||
inherit
|
||||
TABLE_ITERABLE [READABLE_STRING_32, READABLE_STRING_GENERAL]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Create Current profile.
|
||||
do
|
||||
create items.make (0)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item alias "[]" (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- Profile item associated with key `k`.
|
||||
do
|
||||
Result := items.item (k)
|
||||
end
|
||||
|
||||
has_key (k: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has a profile item associated with key `k`?
|
||||
do
|
||||
Result := items.has (k)
|
||||
end
|
||||
|
||||
count: INTEGER
|
||||
do
|
||||
Result := items.count
|
||||
end
|
||||
|
||||
is_empty: BOOLEAN
|
||||
do
|
||||
Result := items.is_empty
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
force (v: READABLE_STRING_GENERAL; k: READABLE_STRING_GENERAL)
|
||||
-- Associated value `v` with key `k`.
|
||||
do
|
||||
items.force (v.to_string_32, k)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_32, READABLE_STRING_GENERAL]
|
||||
-- Fresh cursor associated with current structure
|
||||
do
|
||||
Result := items.new_cursor
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
items: STRING_TABLE [READABLE_STRING_32]
|
||||
|
||||
;note
|
||||
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -110,6 +110,12 @@ feature -- Query
|
||||
sql_post_execution
|
||||
end
|
||||
|
||||
sql_delete (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- <Precursor>
|
||||
do
|
||||
sql_modify (a_sql_statement, a_params)
|
||||
end
|
||||
|
||||
sql_rows_count: INTEGER
|
||||
-- Number of rows for last sql execution.
|
||||
do
|
||||
|
||||
@@ -230,6 +230,12 @@ feature -- Operation
|
||||
end
|
||||
end
|
||||
|
||||
sql_delete (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
|
||||
-- <Precursor>
|
||||
do
|
||||
sql_modify (a_sql_statement, a_params)
|
||||
end
|
||||
|
||||
sqlite_arguments (a_params: STRING_TABLE [detachable ANY]): ARRAYED_LIST [SQLITE_BIND_ARG [ANY]]
|
||||
local
|
||||
k: READABLE_STRING_GENERAL
|
||||
|
||||
29
modules/auth/cms_auth_strategy_filter.e
Normal file
29
modules/auth/cms_auth_strategy_filter.e
Normal file
@@ -0,0 +1,29 @@
|
||||
note
|
||||
description: "Summary description for {CMS_AUTH_FILTER_WITH_LOGOUT}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_AUTH_STRATEGY_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER
|
||||
redefine
|
||||
set_current_user
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
auth_strategy: STRING
|
||||
deferred
|
||||
end
|
||||
|
||||
set_current_user (u: CMS_USER)
|
||||
do
|
||||
Precursor (u)
|
||||
-- Record auth strategy:
|
||||
api.set_execution_variable ({CMS_AUTHENTICATION_MODULE}.auth_strategy_execution_variable_name, auth_strategy)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -124,8 +124,6 @@ feature -- Router
|
||||
a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password(a_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post)
|
||||
a_router.handle ("/account/change/{field}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_change_field (a_api, ?, ?)), a_router.methods_get_post)
|
||||
|
||||
a_router.handle ("/user/{uid}", create {CMS_USER_HANDLER}.make (a_api), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Hooks configuration
|
||||
@@ -206,6 +204,23 @@ feature -- Hooks configuration
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Handler / Constants
|
||||
|
||||
auth_strategy_execution_variable_name: STRING = "auth_strategy"
|
||||
-- Exevc
|
||||
|
||||
auth_strategy (req: WSF_REQUEST): detachable READABLE_STRING_8
|
||||
-- Strategy used by current authentication.
|
||||
-- note: if user is authenticated..
|
||||
do
|
||||
if
|
||||
attached {READABLE_STRING_GENERAL} req.execution_variable (auth_strategy_execution_variable_name) as s and then
|
||||
s.is_valid_as_string_8
|
||||
then
|
||||
Result := s.to_string_8
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Handler
|
||||
|
||||
handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
@@ -214,10 +229,13 @@ feature -- Handler
|
||||
l_user: detachable CMS_USER
|
||||
b: STRING
|
||||
lnk: CMS_LOCAL_LINK
|
||||
f: CMS_FORM
|
||||
tf: WSF_FORM_TEXT_INPUT
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
create b.make_empty
|
||||
l_user := r.user
|
||||
create f.make (r.location, "roccms-user-view")
|
||||
if attached smarty_template_block (Current, "account_info", api) as l_tpl_block then
|
||||
l_tpl_block.set_weight (-10)
|
||||
r.add_block (l_tpl_block, "content")
|
||||
@@ -225,6 +243,30 @@ feature -- Handler
|
||||
debug ("cms")
|
||||
r.add_warning_message ("Error with block [resources_page]")
|
||||
end
|
||||
if l_user /= Void then
|
||||
create tf.make_with_text ("username", l_user.name)
|
||||
tf.set_label ("Username")
|
||||
f.extend (tf)
|
||||
if attached l_user.email as l_email then
|
||||
create tf.make_with_text ("email", l_email.to_string_32)
|
||||
tf.set_label ("Email")
|
||||
f.extend (tf)
|
||||
end
|
||||
if attached l_user.profile_name as l_prof_name then
|
||||
create tf.make_with_text ("profile_name", l_prof_name)
|
||||
tf.set_label ("Profile name")
|
||||
f.extend (tf)
|
||||
end
|
||||
create tf.make_with_text ("creation", api.formatted_date_time_yyyy_mm_dd (l_user.creation_date))
|
||||
tf.set_label ("Creation date")
|
||||
f.extend (tf)
|
||||
|
||||
if attached l_user.last_login_date as dt then
|
||||
create tf.make_with_text ("last_login", api.formatted_date_time_ago (dt))
|
||||
tf.set_label ("Last login")
|
||||
f.extend (tf)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if r.is_authenticated then
|
||||
@@ -237,6 +279,9 @@ feature -- Handler
|
||||
r.add_to_primary_tabs (lnk)
|
||||
end
|
||||
|
||||
api.hooks.invoke_form_alter (f, Void, r)
|
||||
f.append_to_html (r.wsf_theme, b)
|
||||
|
||||
r.set_main_content (b)
|
||||
|
||||
if l_user = Void then
|
||||
@@ -251,10 +296,12 @@ feature -- Handler
|
||||
l_user: detachable CMS_USER
|
||||
b: STRING
|
||||
lnk: CMS_LOCAL_LINK
|
||||
l_form: CMS_FORM
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
create b.make_empty
|
||||
l_user := r.user
|
||||
create l_form.make (r.location, "roccms-user-edit")
|
||||
if attached smarty_template_block (Current, "account_edit", api) as l_tpl_block then
|
||||
l_tpl_block.set_weight (-10)
|
||||
r.add_block (l_tpl_block, "content")
|
||||
@@ -262,6 +309,7 @@ feature -- Handler
|
||||
debug ("cms")
|
||||
r.add_warning_message ("Error with block [resources_page]")
|
||||
end
|
||||
-- Build CMS form...
|
||||
end
|
||||
create lnk.make ("View", "account/")
|
||||
lnk.set_weight (1)
|
||||
@@ -287,6 +335,8 @@ feature -- Handler
|
||||
f.append_to_html (r.wsf_theme, b)
|
||||
end
|
||||
|
||||
l_form.append_to_html (r.wsf_theme, b)
|
||||
|
||||
r.set_main_content (b)
|
||||
|
||||
if l_user = Void then
|
||||
@@ -336,7 +386,7 @@ feature -- Handler
|
||||
loc: STRING
|
||||
do
|
||||
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
|
||||
if attached {READABLE_STRING_8} api.execution_variable ("auth_strategy") as l_auth_strategy then
|
||||
if attached auth_strategy (req) as l_auth_strategy then
|
||||
loc := l_auth_strategy
|
||||
else
|
||||
loc := ""
|
||||
|
||||
47
modules/auth/cms_authentication_module_webapi.e
Normal file
47
modules/auth/cms_authentication_module_webapi.e
Normal file
@@ -0,0 +1,47 @@
|
||||
note
|
||||
description: "Summary description for {CMS_AUTHENTICATION_MODULE_WEBAPI}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_AUTHENTICATION_MODULE_WEBAPI
|
||||
|
||||
inherit
|
||||
CMS_MODULE_WEBAPI [CMS_AUTHENTICATION_MODULE]
|
||||
redefine
|
||||
permissions
|
||||
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")
|
||||
end
|
||||
|
||||
feature {NONE} -- Router/administration
|
||||
|
||||
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
a_router.handle ("/info", create {WSF_URI_AGENT_HANDLER}.make (agent handle_info (?, ?, a_api)), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Request handling
|
||||
|
||||
handle_info (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
|
||||
local
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
do
|
||||
create m.make
|
||||
m.set_body ("{%"info%":%"" + api.setup.site_name + "%"}")
|
||||
res.send (m)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -19,6 +19,8 @@ inherit
|
||||
setup_hooks
|
||||
end
|
||||
|
||||
CMS_WITH_WEBAPI
|
||||
|
||||
CMS_HOOK_BLOCK
|
||||
|
||||
create
|
||||
@@ -33,6 +35,13 @@ feature {NONE} -- Initialization
|
||||
description := "Service to manage basic authentication"
|
||||
end
|
||||
|
||||
feature {CMS_EXECUTION} -- Administration
|
||||
|
||||
webapi: CMS_BASIC_AUTH_MODULE_WEBAPI
|
||||
do
|
||||
create Result.make (Current)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "basic_auth"
|
||||
|
||||
36
modules/basic_auth/cms_basic_auth_module_webapi.e
Normal file
36
modules/basic_auth/cms_basic_auth_module_webapi.e
Normal file
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Summary description for {CMS_BASIC_AUTH_MODULE_WEBAPI}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_BASIC_AUTH_MODULE_WEBAPI
|
||||
|
||||
inherit
|
||||
CMS_MODULE_WEBAPI [CMS_BASIC_AUTH_MODULE]
|
||||
redefine
|
||||
filters
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Router/administration
|
||||
|
||||
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
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 (1)
|
||||
Result.extend (create {CMS_BASIC_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
|
||||
@@ -9,7 +9,7 @@ class
|
||||
CMS_BASIC_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER_I
|
||||
CMS_AUTH_STRATEGY_FILTER
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ inherit
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_FILTER
|
||||
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get
|
||||
@@ -34,7 +31,6 @@ feature -- execute
|
||||
-- Execute request handler.
|
||||
do
|
||||
execute_methods (req, res)
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
|
||||
6
modules/core/site/scripts/user_profile.sql
Normal file
6
modules/core/site/scripts/user_profile.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE user_profiles(
|
||||
`uid` INTEGER NOT NULL CHECK("uid">=0),
|
||||
`key` VARCHAR(255) NOT NULL,
|
||||
`value` TEXT,
|
||||
CONSTRAINT PK_uid_key PRIMARY KEY (uid,key)
|
||||
);
|
||||
@@ -57,7 +57,7 @@ feature {CMS_API} -- Module Initialization
|
||||
create feed_aggregator_api.make (api)
|
||||
end
|
||||
|
||||
feature {CMS_API} -- Access: API
|
||||
feature {CMS_API, CMS_MODULE} -- Access: API
|
||||
|
||||
feed_aggregator_api: detachable FEED_AGGREGATOR_API
|
||||
-- Eventual module api.
|
||||
|
||||
@@ -10,7 +10,7 @@ class
|
||||
CMS_OAUTH_20_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER_I
|
||||
CMS_AUTH_STRATEGY_FILTER
|
||||
rename
|
||||
make as make_filter
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ class
|
||||
CMS_OPENID_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER_I
|
||||
CMS_AUTH_STRATEGY_FILTER
|
||||
rename
|
||||
make as make_filter
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ class
|
||||
CMS_SESSION_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_AUTH_FILTER_I
|
||||
CMS_AUTH_STRATEGY_FILTER
|
||||
rename
|
||||
make as make_filter
|
||||
end
|
||||
|
||||
@@ -11,8 +11,6 @@ inherit
|
||||
|
||||
CMS_PROXY_STORAGE_SQL
|
||||
|
||||
CMS_SESSION_AUTH_STORAGE_I
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
6
modules/user_profile/site/scripts/user_profile.sql
Normal file
6
modules/user_profile/site/scripts/user_profile.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE user_profiles(
|
||||
`uid` INTEGER NOT NULL CHECK("uid">=0),
|
||||
`key` VARCHAR(255) NOT NULL.
|
||||
`value` TEXT.
|
||||
CONSTRAINT PK_uid_key PRIMARY KEY (uid,key)
|
||||
);
|
||||
@@ -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)
|
||||
|
||||
30
src/hooks/cms_hook_webapi_response_alter.e
Normal file
30
src/hooks/cms_hook_webapi_response_alter.e
Normal file
@@ -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)"
|
||||
87
src/modules/core/cms_user_profile_api.e
Normal file
87
src/modules/core/cms_user_profile_api.e
Normal file
@@ -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
|
||||
@@ -57,15 +57,21 @@ feature -- execute
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
-- User id passed as path parameter for request `req'.
|
||||
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
|
||||
Result := s.to_integer_64
|
||||
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
|
||||
@@ -76,28 +82,23 @@ feature -- HTTP Methods
|
||||
-- <Precursor>
|
||||
local
|
||||
l_user: detachable CMS_USER
|
||||
l_uid: INTEGER_64
|
||||
view_response: CMS_USER_VIEW_RESPONSE
|
||||
do
|
||||
if api.has_permission ("view user") then
|
||||
-- Display existing node
|
||||
l_uid := user_id_path_parameter (req)
|
||||
if l_uid > 0 then
|
||||
l_user := api.user_api.user_by_id (l_uid)
|
||||
l_user := user_path_parameter (req)
|
||||
if
|
||||
l_user /= Void
|
||||
then
|
||||
create view_response.make (req, res, api)
|
||||
view_response.execute
|
||||
(create {CMS_USER_VIEW_RESPONSE}.make_with_user (l_user, req, res, api)).execute
|
||||
else
|
||||
send_not_found (req, res)
|
||||
end
|
||||
else
|
||||
send_bad_request (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
|
||||
@@ -10,7 +10,19 @@ inherit
|
||||
CMS_RESPONSE
|
||||
|
||||
create
|
||||
make
|
||||
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
|
||||
|
||||
@@ -33,16 +45,11 @@ feature -- Process
|
||||
-- Computed response message.
|
||||
local
|
||||
b: STRING_8
|
||||
uid: INTEGER_64
|
||||
user_api: CMS_USER_API
|
||||
f: CMS_FORM
|
||||
do
|
||||
user_api := api.user_api
|
||||
create b.make_empty
|
||||
uid := user_id_path_parameter (request)
|
||||
if
|
||||
uid > 0 and then
|
||||
attached user_api.user_by_id (uid) as l_user
|
||||
attached associated_user as l_user
|
||||
then
|
||||
if
|
||||
api.has_permission ("view user")
|
||||
@@ -67,7 +74,6 @@ feature -- Process Edit
|
||||
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)
|
||||
@@ -79,12 +85,14 @@ feature -- Process Edit
|
||||
populate_form (Result, a_user)
|
||||
end
|
||||
|
||||
populate_form (a_form: WSF_FORM; a_user: detachable CMS_USER)
|
||||
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
|
||||
@@ -97,7 +105,33 @@ feature -- Process Edit
|
||||
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
|
||||
@@ -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
|
||||
182
src/modules/core/webapi/cms_access_token_webapi_handler.e
Normal file
182
src/modules/core/webapi/cms_access_token_webapi_handler.e
Normal file
@@ -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
|
||||
43
src/modules/core/webapi/cms_basic_webapi_auth_filter.e
Normal file
43
src/modules/core/webapi/cms_basic_webapi_auth_filter.e
Normal file
@@ -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
|
||||
57
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
57
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
@@ -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
|
||||
36
src/modules/core/webapi/cms_root_webapi_handler.e
Normal file
36
src/modules/core/webapi/cms_root_webapi_handler.e
Normal file
@@ -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
|
||||
80
src/modules/core/webapi/cms_user_webapi_handler.e
Normal file
80
src/modules/core/webapi/cms_user_webapi_handler.e
Normal file
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -1215,19 +1309,41 @@ 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
|
||||
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.
|
||||
|
||||
@@ -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 {CMS_WITH_WEBAPI} l_module as l_webapi and then
|
||||
attached l_webapi.module_webapi as wapi
|
||||
then
|
||||
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)
|
||||
|
||||
36
src/service/cms_with_module_administration.e
Normal file
36
src/service/cms_with_module_administration.e
Normal file
@@ -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
|
||||
@@ -6,10 +6,13 @@ note
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_AUTH_FILTER_I
|
||||
CMS_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as auth_execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
@@ -25,20 +28,28 @@ feature -- API Service
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
auth_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
deferred
|
||||
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
|
||||
|
||||
auth_strategy: STRING
|
||||
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)
|
||||
-- Record auth strategy:
|
||||
api.set_execution_variable ("auth_strategy", auth_strategy)
|
||||
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
|
||||
|
||||
376
src/service/response/cms_response_i.e
Normal file
376
src/service/response/cms_response_i.e
Normal 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
src/service/response/hm_webapi_response.e
Normal file
51
src/service/response/hm_webapi_response.e
Normal 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
|
||||
@@ -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
src/service/response/json_webapi_response.e
Normal file
201
src/service/response/json_webapi_response.e
Normal 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
src/service/response/md_webapi_response.e
Normal file
70
src/service/response/md_webapi_response.e
Normal 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
src/service/response/webapi_response.e
Normal file
44
src/service/response/webapi_response.e
Normal 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
|
||||
90
src/service/webapi/cms_module_webapi.e
Normal file
90
src/service/webapi/cms_module_webapi.e
Normal 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
|
||||
15
src/service/webapi/cms_webapi_auth_filter.e
Normal file
15
src/service/webapi/cms_webapi_auth_filter.e
Normal file
@@ -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
src/service/webapi/cms_webapi_handler.e
Normal file
100
src/service/webapi/cms_webapi_handler.e
Normal 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
src/service/webapi/cms_with_webapi.e
Normal file
36
src/service/webapi/cms_with_webapi.e
Normal 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
|
||||
107
themes/admin/assets/css/style.css
Normal file
107
themes/admin/assets/css/style.css
Normal file
@@ -0,0 +1,107 @@
|
||||
div {
|
||||
background-color: #ffdddd;
|
||||
}
|
||||
|
||||
ul.horizontal li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#header #primary.menu ul li {
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
#header #primary.menu ul li a {
|
||||
color: #555;
|
||||
text-decoration: none;
|
||||
}
|
||||
#header #primary.menu ul li a:hover {
|
||||
color: black;
|
||||
}
|
||||
#header #primary.menu ul.horizontal {
|
||||
border-bottom: solid 1px #ddd;
|
||||
}
|
||||
#header #primary.menu ul.horizontal li {
|
||||
border-top: solid 3px #fff;
|
||||
}
|
||||
#header #primary.menu ul.horizontal li:hover {
|
||||
background-color: #ffe;
|
||||
border-top: solid 3px #999;
|
||||
}
|
||||
#header #primary.menu ul.horizontal li.active {
|
||||
font-weight: bold;
|
||||
border-top: solid 3px #ddd;
|
||||
background-color: #ddd;
|
||||
}
|
||||
#header #primary.menu ul.horizontal li.active:hover {
|
||||
border-top: solid 3px blue;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 20px;
|
||||
}
|
||||
#content #highlighted {
|
||||
position: relative;
|
||||
border: solid 1px #ddd;
|
||||
background-color: #ffc;
|
||||
width: 70%;
|
||||
left: 15%;
|
||||
right: 15%;
|
||||
padding: 5px;
|
||||
font-style: italic;
|
||||
}
|
||||
#content .preview {
|
||||
border: solid 1px red;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 5px;
|
||||
margin: 3px;
|
||||
/* border: solid 1px #ccc; */
|
||||
}
|
||||
.sidebar#sidebar_first {
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
border-right: solid 1px #ddd;
|
||||
}
|
||||
.sidebar#sidebar_second {
|
||||
width: 250px;
|
||||
float: right;
|
||||
}
|
||||
.sidebar + .main {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
#primary-tabs ul.horizontal {
|
||||
list-style-type: none;
|
||||
}
|
||||
#primary-tabs ul.horizontal li {
|
||||
display: inline;
|
||||
padding: 2px 5px;
|
||||
border: solid 1px #ccf;
|
||||
}
|
||||
#primary-tabs ul.horizontal li.active {
|
||||
border-color: #99f #99f #ddd;
|
||||
border-style: solid solid none;
|
||||
border-width: 2px 1px 0;
|
||||
padding: 2px 7px 1px;
|
||||
}
|
||||
|
||||
#message li.error {
|
||||
background-color: #f99;
|
||||
border: solid 1px red;
|
||||
padding: 5px 2px 5px 2px;
|
||||
}
|
||||
|
||||
table.with_border thead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
table.with_border td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
BIN
themes/admin/assets/favicon.ico
Normal file
BIN
themes/admin/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 994 B |
6
themes/admin/assets/js/jquery-1.10.2.min.js
vendored
Normal file
6
themes/admin/assets/js/jquery-1.10.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
themes/admin/assets/js/popup_search.js
Normal file
8
themes/admin/assets/js/popup_search.js
Normal file
@@ -0,0 +1,8 @@
|
||||
$(document).ready(function() {
|
||||
$('#gcse_search_form').submit(function() {
|
||||
window.open('', 'formpopup', 'width=600,height=600,resizeable,scrollbars');
|
||||
this.target = 'formpopup';
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
110
themes/admin/assets/scss/style.scss
Normal file
110
themes/admin/assets/scss/style.scss
Normal file
@@ -0,0 +1,110 @@
|
||||
ul.horizontal {
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
#header {
|
||||
#primary.menu {
|
||||
ul {
|
||||
li {
|
||||
color: #555;
|
||||
a {
|
||||
color: #555;
|
||||
text-decoration: none;
|
||||
&:hover { color: black; }
|
||||
}
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
&.horizontal {
|
||||
border-bottom: solid 1px #ddd;
|
||||
li {
|
||||
border-top: solid 3px #fff;
|
||||
&:hover {
|
||||
background-color: #ffe;
|
||||
border-top: solid 3px #999;
|
||||
}
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
border-top: solid 3px #ddd;
|
||||
background-color: #ddd;
|
||||
}
|
||||
&.active:hover {
|
||||
border-top: solid 3px blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#content {
|
||||
margin-left: 20px;
|
||||
#highlighted {
|
||||
position: relative;
|
||||
border: solid 1px #ddd;
|
||||
background-color: #ffc;
|
||||
width: 70%;
|
||||
left: 15%;
|
||||
right: 15%;
|
||||
padding: 5px;
|
||||
font-style: italic;
|
||||
}
|
||||
.preview {
|
||||
border: solid 1px red;
|
||||
}
|
||||
}
|
||||
.sidebar {
|
||||
padding: 5px;
|
||||
margin: 3px;
|
||||
/* border: solid 1px #ccc; */
|
||||
&#sidebar_first {
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
border-right: solid 1px #ddd;
|
||||
}
|
||||
&#sidebar_second {
|
||||
width: 250px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&+.main {
|
||||
margin-left: 200px;
|
||||
}
|
||||
}
|
||||
#primary-tabs {
|
||||
ul.horizontal {
|
||||
list-style-type: none;
|
||||
li {
|
||||
display: inline;
|
||||
padding: 2px 5px;
|
||||
border: solid 1px #ccf;
|
||||
}
|
||||
li.active {
|
||||
border-color: #99f #99f #ddd;
|
||||
border-style: solid solid none;
|
||||
border-width: 2px 1px 0;
|
||||
padding: 2px 7px 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
#message li.error {
|
||||
background-color: #f99;
|
||||
border: solid 1px red;
|
||||
padding: 5px 2px 5px 2px;
|
||||
}
|
||||
|
||||
table.with_border {
|
||||
thead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
td {
|
||||
border: solid 1px #ccc;
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
}
|
||||
38
themes/admin/debug.tpl
Normal file
38
themes/admin/debug.tpl
Normal file
@@ -0,0 +1,38 @@
|
||||
{assign name="debug_enabled" value="True"/}
|
||||
{if condition="$debug_enabled"}
|
||||
<!-- start debug -->
|
||||
{literal}
|
||||
<style>
|
||||
div.cms-debug>span {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
color: #ccc;
|
||||
padding: 5px;
|
||||
}
|
||||
div.cms-debug:hover>span {
|
||||
color: red;
|
||||
}
|
||||
div.cms-debug>span+ul {
|
||||
display: none;
|
||||
border: solid 2px red;
|
||||
background-color: #ccc;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
div.cms-debug:hover>span+ul {
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 5px;
|
||||
left: 1%; right: 1%;
|
||||
width: 98%;
|
||||
}
|
||||
</style>
|
||||
{/literal}
|
||||
<div class="cms-debug"><span>Show debug</span>
|
||||
<ul>
|
||||
{assign name="kpage" value="page"/}{assign name="kregions" value="regions"/}{foreach key="k" item="i" from="$page.variables"}{unless condition="$k ~ $kpage"}{unless condition="$k ~ $kregions"}<li><strong>{$k/}</strong>={htmlentities}{$i/}{/htmlentities}</li>{/unless}{/unless}
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
<!-- end debug -->
|
||||
{/if}
|
||||
101
themes/admin/page.tpl
Normal file
101
themes/admin/page.tpl
Normal file
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- EWF CMS -->
|
||||
<link rel="stylesheet" href="{$theme_path/}css/style.css">
|
||||
|
||||
<!-- jQuery dep -->
|
||||
<script src="{$theme_path/}js/jquery-1.10.2.min.js"></script>
|
||||
<script src="{$theme_path/}js/popup_search.js"></script>
|
||||
|
||||
{if isset="$head"}{$head/}{/if}
|
||||
{if isset="$styles"}{$styles/}{/if}
|
||||
{if isset="$scripts"}{$scripts/}{/if}
|
||||
{if isset="$head_lines"}{$head_lines/}{/if}
|
||||
|
||||
<!-- bootstrap framework -->
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
||||
<!-- Optional theme -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
|
||||
|
||||
<title>{$head_title/}</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Page Top -->
|
||||
{if isset="$region_top"}
|
||||
{$region_top/}
|
||||
{/if}
|
||||
<!-- Body -->
|
||||
<div class='container-fluid'>
|
||||
|
||||
<!-- Page Header -->
|
||||
<div id="header">
|
||||
{if isset="$page.primary_nav"}
|
||||
{$page.primary_nav/}
|
||||
{/if}
|
||||
</div>
|
||||
<!-- Page search -->
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-md-offset-9">
|
||||
<form action="{$site_url/}gcse" class="search-form" id="gcse_search_form">
|
||||
<div class="form-group has-feedback">
|
||||
<input type="search" class="form-control" name="q" id="gcse_search" placeholder="search" value="{htmlentities}{$cms_search_query/}{/htmlentities}" >
|
||||
<span class="glyphicon glyphicon-search form-control-feedback"></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- General Page Content -->
|
||||
<div id='content' class='row-fluid'>
|
||||
<!-- Left Sidebar sidebar_first -->
|
||||
{unless isempty="$page.region_sidebar_first"}
|
||||
<div id="sidebar_first" class="sidebar">{$page.region_sidebar_first/}</div>
|
||||
{/unless}
|
||||
<!-- Right Sidebar sidebar_second-->
|
||||
{unless isempty="$page.region_sidebar_second"}
|
||||
<div id="sidebar_second" class="sidebar">{$page.region_sidebar_second/}</div>
|
||||
{/unless}
|
||||
|
||||
<!-- Highlighted, Help, Content -->
|
||||
<div id='main' class='span8 main'>
|
||||
<!-- Highlighted Section -->
|
||||
{unless isempty="$page.region_highlighted"}
|
||||
<div id="highlighted">{$page.region_highlighted/}</div>
|
||||
{/unless}
|
||||
<!-- Help Section -->
|
||||
{unless isempty="$page.region_help"}
|
||||
<div id="help">{$page.region_help/}</div>
|
||||
{/unless}
|
||||
|
||||
<!-- Main Content Section -->
|
||||
{unless isempty="$page_title"}<h1 class="page-title">{$page_title/}</h1>{/unless}
|
||||
{$page.region_content/}
|
||||
{if condition="$page.is_front"}
|
||||
{if isset="$page.region_feed_news"}
|
||||
<div class="column" style="width: 45%; float: left">{$page.region_feed_news/}</div>
|
||||
{/if}
|
||||
{if isset="$page.region_feed_forum"}
|
||||
<div class="column" style="width: 45%; float: left">{$page.region_feed_forum/}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Page footer -->
|
||||
{$page.region_footer/}
|
||||
|
||||
<!-- Page Bottom -->
|
||||
{$page.region_bottom/}
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
||||
|
||||
{include file="debug.tpl"/}
|
||||
</body>
|
||||
</html>
|
||||
14
themes/admin/theme.info
Normal file
14
themes/admin/theme.info
Normal file
@@ -0,0 +1,14 @@
|
||||
name=admin
|
||||
engine=smarty
|
||||
author=jocelyn fiat
|
||||
version=0.1
|
||||
regions[page_top] = Top
|
||||
regions[header] = Header
|
||||
regions[content] = Content
|
||||
regions[highlighted] = Highlighted
|
||||
regions[help] = Help
|
||||
regions[footer] = Footer
|
||||
regions[sidebar_first] = first sidebar
|
||||
regions[sidebar_second] = second sidebar
|
||||
regions[page_bottom] = Bottom
|
||||
navigation=default_nav
|
||||
0
themes/min/assets/css/style.css
Normal file
0
themes/min/assets/css/style.css
Normal file
BIN
themes/min/assets/favicon.ico
Normal file
BIN
themes/min/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 994 B |
0
themes/min/assets/scss/style.scss
Normal file
0
themes/min/assets/scss/style.scss
Normal file
66
themes/min/page.tpl
Normal file
66
themes/min/page.tpl
Normal file
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- EWF CMS -->
|
||||
<link rel="stylesheet" href="{$theme_path/}css/style.css">
|
||||
{if isset="$head"}{$head/}{/if}
|
||||
{if isset="$styles"}{$styles/}{/if}
|
||||
{if isset="$scripts"}{$scripts/}{/if}
|
||||
{if isset="$head_lines"}{$head_lines/}{/if}
|
||||
<title>{$head_title/}</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Page Top -->
|
||||
{if isset="$region_top"}
|
||||
{$region_top/}
|
||||
{/if}
|
||||
<!-- Body -->
|
||||
<div>
|
||||
<!-- Page Header -->
|
||||
<div id="header">
|
||||
{if isset="$page.primary_nav"}
|
||||
{$page.primary_nav/}
|
||||
{/if}
|
||||
</div>
|
||||
{if isset="$page.regions.search"}
|
||||
<!-- Page search -->
|
||||
<div class="search">{$page.regions.search/}</div>
|
||||
{/if}
|
||||
<!-- General Page Content -->
|
||||
<div id='content'>
|
||||
<!-- Left Sidebar sidebar_first -->
|
||||
{unless isempty="$page.region_sidebar_first"}
|
||||
<div id="sidebar_first" class="sidebar">{$page.region_sidebar_first/}</div>
|
||||
{/unless}
|
||||
<!-- Right Sidebar sidebar_second-->
|
||||
{unless isempty="$page.region_sidebar_second"}
|
||||
<div id="sidebar_second" class="sidebar">{$page.region_sidebar_second/}</div>
|
||||
{/unless}
|
||||
|
||||
<!-- Highlighted, Help, Content -->
|
||||
<div id='main'>
|
||||
<!-- Highlighted Section -->
|
||||
{unless isempty="$page.region_highlighted"}
|
||||
<div id="highlighted">{$page.region_highlighted/}</div>
|
||||
{/unless}
|
||||
<!-- Help Section -->
|
||||
{unless isempty="$page.region_help"}
|
||||
<div id="help">{$page.region_help/}</div>
|
||||
{/unless}
|
||||
|
||||
<!-- Main Content Section -->
|
||||
{unless isempty="$page_title"}<h1 class="page-title">{$page_title/}</h1>{/unless}
|
||||
{$page.region_content/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Page footer -->
|
||||
{$page.region_footer/}
|
||||
|
||||
<!-- Page Bottom -->
|
||||
{$page.region_bottom/}
|
||||
</body>
|
||||
</html>
|
||||
15
themes/min/theme.info
Normal file
15
themes/min/theme.info
Normal file
@@ -0,0 +1,15 @@
|
||||
name=min
|
||||
engine=smarty
|
||||
author=jfiat
|
||||
version=0.1
|
||||
regions[page_top] = Top
|
||||
regions[header] = Header
|
||||
regions[search] = Search
|
||||
regions[content] = Content
|
||||
regions[highlighted] = Highlighted
|
||||
regions[help] = Help
|
||||
regions[footer] = Footer
|
||||
regions[sidebar_first] = first sidebar
|
||||
regions[sidebar_second] = second sidebar
|
||||
regions[page_bottom] = Bottom
|
||||
navigation=default_nav
|
||||
@@ -16,7 +16,7 @@ feature -- Access
|
||||
|
||||
help: STRING_32
|
||||
once
|
||||
Result := "[--module|-m <MODULE_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>] [--site-dir <CMS_SITE_PATH>]"
|
||||
Result := "[--module|-m <MODULE_PATH>] [--theme <THEME_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>] [--site-dir <CMS_SITE_PATH>]"
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
@@ -25,6 +25,7 @@ feature -- Status report
|
||||
-- Is the submitted install command valid?
|
||||
local
|
||||
i, n: INTEGER
|
||||
optional_theme: BOOLEAN
|
||||
optional_module: BOOLEAN
|
||||
optional_config: BOOLEAN
|
||||
cms_path: BOOLEAN
|
||||
@@ -41,10 +42,14 @@ feature -- Status report
|
||||
optional_config := True
|
||||
elseif args [i].same_string ("--config") then
|
||||
optional_config := True
|
||||
elseif args [i].same_string ("-m") then
|
||||
optional_module := True
|
||||
elseif args [i].same_string ("--module") then
|
||||
elseif
|
||||
args [i].same_string ("-m") or args [i].same_string ("--module")
|
||||
then
|
||||
optional_module := True
|
||||
elseif
|
||||
args [i].same_string ("--theme")
|
||||
then
|
||||
optional_theme := True
|
||||
elseif args [i].same_string ("-d") then
|
||||
cms_path := True
|
||||
elseif args [i].same_string ("--dir") then
|
||||
@@ -56,14 +61,14 @@ feature -- Status report
|
||||
Result := True
|
||||
else
|
||||
if n <= 5 then
|
||||
if (cms_path and (optional_module)) then
|
||||
if cms_path and optional_module then
|
||||
-- valid command
|
||||
Result := True
|
||||
else
|
||||
print ("Error check the optional argument --module|-m and --dir|-d")
|
||||
end
|
||||
elseif n <= 3 then
|
||||
if (cms_path and not optional_module) then
|
||||
if cms_path and not optional_module then
|
||||
Result := True
|
||||
else
|
||||
print ("Error missing value for dir")
|
||||
@@ -96,6 +101,16 @@ feature -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
theme_name (a_path: PATH): STRING_32
|
||||
do
|
||||
-- FIXME: better implementation needed. Either based on "a" new theme.info file, or parsing the .ecf
|
||||
if attached a_path.entry as e then
|
||||
Result := e.name
|
||||
else
|
||||
Result := a_path.name
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (args: ARRAY [READABLE_STRING_32])
|
||||
@@ -103,13 +118,16 @@ feature -- Execution
|
||||
-- Pattern: module_src/site/* => cms/site/modules/$module_name/*
|
||||
local
|
||||
l_config_path, l_site_path, l_cms_path, p: detachable PATH
|
||||
l_module_source_locations: ARRAYED_LIST [PATH]
|
||||
l_module_source_locations, l_theme_source_locations: ARRAYED_LIST [ROC_INSTALL_COPY_PARAMETERS]
|
||||
l_site_dir: DIRECTORY
|
||||
l_modules_dir: DIRECTORY
|
||||
l_themes_dir: DIRECTORY
|
||||
l_dest_dir: DIRECTORY
|
||||
l_cp_params: ROC_INSTALL_COPY_PARAMETERS
|
||||
i,n: INTEGER
|
||||
do
|
||||
create l_module_source_locations.make (1)
|
||||
create l_theme_source_locations.make (1)
|
||||
from
|
||||
i := 1
|
||||
n := args.upper
|
||||
@@ -148,6 +166,13 @@ feature -- Execution
|
||||
if i <= n then
|
||||
l_module_source_locations.force (create {PATH}.make_from_string (args[i]))
|
||||
end
|
||||
elseif
|
||||
arg.same_string ("--theme")
|
||||
then
|
||||
i := i + 1
|
||||
if i <= n then
|
||||
l_theme_source_locations.force (create {PATH}.make_from_string (args[i]))
|
||||
end
|
||||
elseif
|
||||
arg.same_string ("-v")
|
||||
or arg.same_string ("--verbose")
|
||||
@@ -192,7 +217,34 @@ feature -- Execution
|
||||
if not p.is_absolute then
|
||||
p := l_cms_path.extended_path (p)
|
||||
end
|
||||
l_module_source_locations.extend (p.canonical_path)
|
||||
create l_cp_params.make_with_location (p.canonical_path)
|
||||
if attached cfg.resolved_text_item ({STRING_32} "modules." + ic.item + ".mode") as l_mode and then l_mode.is_case_insensitive_equal_general ("link") then
|
||||
l_cp_params.set_link_mode (True)
|
||||
end
|
||||
l_module_source_locations.extend (l_cp_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
if attached cfg.resolved_text_table_item ("themes") as tb then
|
||||
across
|
||||
tb as ic
|
||||
loop
|
||||
l_theme_source_locations.extend (create {PATH}.make_from_string (ic.item))
|
||||
end
|
||||
elseif attached cfg.table_keys ("themes") as tb_keys then
|
||||
across
|
||||
tb_keys as ic
|
||||
loop
|
||||
if attached cfg.resolved_text_item ({STRING_32} "themes." + ic.item + ".location") as l_loc then
|
||||
create p.make_from_string (l_loc)
|
||||
if not p.is_absolute then
|
||||
p := l_cms_path.extended_path (p)
|
||||
end
|
||||
create l_cp_params.make_with_location (p.canonical_path)
|
||||
if attached cfg.resolved_text_item ({STRING_32} "modules." + ic.item + ".mode") as l_mode and then l_mode.is_case_insensitive_equal_general ("link") then
|
||||
l_cp_params.set_link_mode (True)
|
||||
end
|
||||
l_theme_source_locations.extend (l_cp_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -211,7 +263,7 @@ feature -- Execution
|
||||
loop
|
||||
if
|
||||
attached ic.item as l_module_source_path and then
|
||||
attached module_name (l_module_source_path) as l_mod_name
|
||||
attached module_name (l_module_source_path.location) as l_mod_name
|
||||
then
|
||||
-- Install configuration files.
|
||||
create l_site_dir.make_with_path (l_site_path)
|
||||
@@ -249,6 +301,45 @@ feature -- Execution
|
||||
print ("Error: could not retrieve module name.%N")
|
||||
end
|
||||
end
|
||||
across
|
||||
l_theme_source_locations as ic
|
||||
loop
|
||||
if
|
||||
attached ic.item as l_theme_source_path and then
|
||||
attached theme_name (l_theme_source_path.location) as l_theme_name
|
||||
then
|
||||
-- Install configuration files.
|
||||
create l_site_dir.make_with_path (l_site_path)
|
||||
if l_site_dir.exists then
|
||||
create l_themes_dir.make_with_path (l_site_path.extended ("themes"))
|
||||
if not l_themes_dir.exists then
|
||||
l_themes_dir.create_dir
|
||||
end
|
||||
|
||||
create l_dest_dir.make_with_path (l_themes_dir.path.extended (l_theme_name))
|
||||
if not l_dest_dir.exists then
|
||||
l_dest_dir.create_dir
|
||||
end
|
||||
print ("Install theme ")
|
||||
print (l_theme_name)
|
||||
print (" in %"")
|
||||
print (l_dest_dir.path.name)
|
||||
print ("%":%N")
|
||||
install_theme_elements (l_theme_source_path, l_dest_dir.path, Void)
|
||||
print (" - ")
|
||||
print (directories_count.out + " director" + if directories_count > 1 then "ies" else "y" end + ", ")
|
||||
print (files_count.out + " file" + if files_count > 1 then "s" else "" end)
|
||||
if files_changes_count > 0 then
|
||||
print (" (+" + files_changes_count.out + ")")
|
||||
end
|
||||
print (".%N")
|
||||
else
|
||||
print ({STRING_32} "The CMS Application located at " + l_cms_path.name + "does not have the site or themes folders.%N")
|
||||
end
|
||||
else
|
||||
print ("Error: could not retrieve theme name.%N")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
roc_configuration (a_cfg_location: PATH): detachable CONFIG_READER
|
||||
@@ -259,7 +350,7 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
install_module_elements (a_module_source_path: PATH; a_cms_module_target_path: PATH; a_element: detachable READABLE_STRING_GENERAL)
|
||||
install_module_elements (a_module_source_path: ROC_INSTALL_COPY_PARAMETERS; a_cms_module_target_path: PATH; a_element: detachable READABLE_STRING_GENERAL)
|
||||
-- Install module site files from `a_module_source_path' to cms application `a_cms_module_target_path' under expected modules folder.
|
||||
-- If `a_element' is set, take into account only sub folder `a_element'.
|
||||
local
|
||||
@@ -267,7 +358,7 @@ feature -- Execution
|
||||
l_dest_dir: DIRECTORY
|
||||
l_src_dir: DIRECTORY
|
||||
do
|
||||
l_path := a_module_source_path.extended ("site")
|
||||
l_path := a_module_source_path.location.extended ("site")
|
||||
if a_element /= Void then
|
||||
-- Copy all files under "site/$a_element" into "site/modules/$module_name/$a_element" location.
|
||||
create l_src_dir.make_with_path (l_path.extended (a_element))
|
||||
@@ -284,7 +375,43 @@ feature -- Execution
|
||||
directories_count := -1
|
||||
files_changes_count := 0
|
||||
|
||||
if a_module_source_path.is_link_mode then
|
||||
copy_directory (l_src_dir, l_dest_dir, True)
|
||||
else
|
||||
link_directory (l_src_dir, l_dest_dir, True)
|
||||
end
|
||||
end
|
||||
|
||||
install_theme_elements (a_theme_source_path: ROC_INSTALL_COPY_PARAMETERS; a_cms_theme_target_path: PATH; a_element: detachable READABLE_STRING_GENERAL)
|
||||
-- Install theme site files from `a_theme_source_path' to cms application `a_cms_theme_target_path' under expected "themes" folder.
|
||||
-- If `a_element' is set, take into account only sub folder `a_element'.
|
||||
local
|
||||
l_path: PATH
|
||||
l_dest_dir: DIRECTORY
|
||||
l_src_dir: DIRECTORY
|
||||
do
|
||||
l_path := a_theme_source_path.location
|
||||
if a_element /= Void then
|
||||
-- Copy all files under "site/$a_element" into "site/themes/$theme_name/$a_element" location.
|
||||
create l_src_dir.make_with_path (l_path.extended (a_element))
|
||||
create l_dest_dir.make_with_path (a_cms_theme_target_path.extended (a_element))
|
||||
else
|
||||
-- Copy all files under "site" into "site/themes/$theme_name/" location.
|
||||
create l_src_dir.make_with_path (l_path)
|
||||
create l_dest_dir.make_with_path (a_cms_theme_target_path)
|
||||
end
|
||||
if not l_dest_dir.exists then
|
||||
l_dest_dir.create_dir
|
||||
end
|
||||
files_count := 0
|
||||
directories_count := -1
|
||||
files_changes_count := 0
|
||||
|
||||
if a_theme_source_path.is_link_mode then
|
||||
copy_directory (l_src_dir, l_dest_dir, True)
|
||||
else
|
||||
link_directory (l_src_dir, l_dest_dir, True)
|
||||
end
|
||||
end
|
||||
|
||||
is_verbose: BOOLEAN
|
||||
@@ -333,6 +460,13 @@ feature {NONE} -- System/copy files
|
||||
end
|
||||
end
|
||||
|
||||
link_directory (a_src: DIRECTORY; a_dest: DIRECTORY; is_recursive: BOOLEAN)
|
||||
-- Link all elements from `a_src' to `a_dest'.
|
||||
do
|
||||
-- TODO: implement symbolic link ...
|
||||
copy_directory (a_src, a_dest, is_recursive)
|
||||
end
|
||||
|
||||
copy_file_in_directory (a_file: FILE; a_dir: PATH)
|
||||
-- Copy file `a_file' to dir `a_dir'.
|
||||
local
|
||||
@@ -386,6 +520,6 @@ feature {NONE} -- System/copy files
|
||||
|
||||
|
||||
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
|
||||
|
||||
39
tools/roc/roc_install_copy_parameters.e
Normal file
39
tools/roc/roc_install_copy_parameters.e
Normal file
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "Summary description for {ROC_INSTALL_COPY_PARAMETERS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ROC_INSTALL_COPY_PARAMETERS
|
||||
|
||||
create
|
||||
make_with_location
|
||||
|
||||
convert
|
||||
make_with_location ({PATH})
|
||||
|
||||
feature {NONE} -- Creation
|
||||
|
||||
make_with_location (a_location: PATH)
|
||||
do
|
||||
location := a_location
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
location: PATH
|
||||
|
||||
is_link_mode: BOOLEAN
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_link_mode (b: BOOLEAN)
|
||||
do
|
||||
is_link_mode := b
|
||||
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