Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6b7c73588 | ||
|
|
4d91e449d7 | ||
|
|
471528e887 | ||
|
|
5117466529 | ||
|
|
053cc000cd | ||
|
|
db585a4873 |
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,6 +3,9 @@
|
||||
"project": "demo-safe.ecf",
|
||||
"location": ".",
|
||||
"site_directory": "site",
|
||||
"themes": {
|
||||
"admin": { "location": "../../themes/admin", "mode": "link" }
|
||||
},
|
||||
"modules": {
|
||||
"core": { "location": "../../modules/core" },
|
||||
"admin": { "location": "../../modules/admin" },
|
||||
|
||||
@@ -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
|
||||
@@ -10,6 +10,9 @@ deferred class
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as auth_execute
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
@@ -25,8 +28,22 @@ feature -- API Service
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
auth_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
-- If user is already authenticated, do not try current authenticating filter
|
||||
-- and go to next filter directly.
|
||||
if api.user_is_authenticated then
|
||||
execute_next (req, res)
|
||||
else
|
||||
execute (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
require
|
||||
no_user_authenticated: api.user = Void
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -214,10 +212,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 +226,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 +262,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 +279,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 +292,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 +318,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
|
||||
|
||||
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)
|
||||
);
|
||||
@@ -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
|
||||
|
||||
@@ -110,7 +110,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)
|
||||
|
||||
@@ -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)
|
||||
if
|
||||
l_user /= Void
|
||||
then
|
||||
create view_response.make (req, res, api)
|
||||
view_response.execute
|
||||
else
|
||||
send_not_found (req, res)
|
||||
end
|
||||
l_user := user_path_parameter (req)
|
||||
if
|
||||
l_user /= Void
|
||||
then
|
||||
(create {CMS_USER_VIEW_RESPONSE}.make_with_user (l_user, req, res, api)).execute
|
||||
else
|
||||
send_bad_request (req, res)
|
||||
send_not_found (req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -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,52 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API)
|
||||
-- Initialize Current handler with `a_api'.
|
||||
do
|
||||
api := a_api
|
||||
end
|
||||
|
||||
feature -- API Service
|
||||
|
||||
api: CMS_API
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
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
|
||||
56
src/modules/core/webapi/cms_core_basic_webapi_auth_filter.e
Normal file
56
src/modules/core/webapi/cms_core_basic_webapi_auth_filter.e
Normal file
@@ -0,0 +1,56 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_BASIC_WEBAPI_AUTH_FILTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_BASIC_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_api: CMS_API)
|
||||
-- Initialize Current handler with `a_api'.
|
||||
do
|
||||
api := a_api
|
||||
end
|
||||
|
||||
feature -- API Service
|
||||
|
||||
api: CMS_API
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
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
|
||||
93
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
93
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
@@ -0,0 +1,93 @@
|
||||
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_CORE_WEBAPI_ROOT_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_CORE_WEBAPI_ACCESS_TOKEN_HANDLER}.make (a_api), a_router.methods_get_post)
|
||||
a_router.handle ("/user/{uid}", create {CMS_CORE_WEBAPI_USER_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_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
Result.extend (create {CMS_CORE_BASIC_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
end
|
||||
|
||||
--feature -- Helpers
|
||||
|
||||
-- new_webapi_response (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API): 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; api: CMS_API): HM_WEBAPI_RESPONSE
|
||||
-- do
|
||||
-- Result := new_webapi_response (req, res, api)
|
||||
-- if msg /= Void then
|
||||
-- Result.add_string_field ("error", msg)
|
||||
-- else
|
||||
-- Result.add_string_field ("error", "True")
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- send_access_denied (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
|
||||
-- local
|
||||
-- rep: HM_WEBAPI_RESPONSE
|
||||
-- do
|
||||
-- rep := new_webapi_response (req, res, api)
|
||||
-- if m /= Void then
|
||||
-- rep.add_string_field ("error", m)
|
||||
-- else
|
||||
-- rep.add_string_field ("error", "Access denied")
|
||||
-- end
|
||||
-- rep.execute
|
||||
-- end
|
||||
|
||||
-- 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
|
||||
177
src/modules/core/webapi/cms_core_webapi_access_token_handler.e
Normal file
177
src/modules/core/webapi/cms_core_webapi_access_token_handler.e
Normal file
@@ -0,0 +1,177 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_WEBAPI_ACCESS_TOKEN_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_WEBAPI_ACCESS_TOKEN_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): 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: 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)
|
||||
Result.add_string_field ("access_token", a_access_token)
|
||||
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
|
||||
36
src/modules/core/webapi/cms_core_webapi_root_handler.e
Normal file
36
src/modules/core/webapi/cms_core_webapi_root_handler.e
Normal file
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_WEBAPI_ROOT_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_WEBAPI_ROOT_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_core_webapi_user_handler.e
Normal file
80
src/modules/core/webapi/cms_core_webapi_user_handler.e
Normal file
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_WEBAPI_USER_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_WEBAPI_USER_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
|
||||
@@ -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 ("/")
|
||||
@@ -512,7 +572,15 @@ feature -- Settings
|
||||
do
|
||||
if is_administration_mode then
|
||||
setup.set_site_mode
|
||||
is_administration_mode := False
|
||||
mode := mode_site
|
||||
end
|
||||
end
|
||||
|
||||
switch_to_webapi_mode
|
||||
do
|
||||
if 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
|
||||
|
||||
@@ -19,6 +19,7 @@ inherit
|
||||
execute_default,
|
||||
filter_execute,
|
||||
initialize,
|
||||
initialize_filter,
|
||||
initialize_router
|
||||
end
|
||||
|
||||
@@ -61,6 +62,13 @@ feature {NONE} -- Initialization
|
||||
create {WSF_MAINTENANCE_FILTER} filter
|
||||
end
|
||||
|
||||
initialize_filter
|
||||
-- Initialize `filter`.
|
||||
do
|
||||
create_filter
|
||||
-- setup_filter: delayed to `initialize_execution`.
|
||||
end
|
||||
|
||||
initialize_router
|
||||
-- Initialize `router`.
|
||||
do
|
||||
@@ -147,20 +155,34 @@ feature -- Settings: router
|
||||
configure_api_file_handler (l_router)
|
||||
end
|
||||
|
||||
setup_router_for_administration
|
||||
-- <Precursor>
|
||||
setup_router_and_filter_for_webapi
|
||||
local
|
||||
l_api: like api
|
||||
l_router: like router
|
||||
l_module: CMS_MODULE
|
||||
f, l_filter, l_last_filter: WSF_FILTER
|
||||
do
|
||||
l_api := api
|
||||
l_router := router
|
||||
|
||||
l_api.logger.put_debug (generator + ".setup_router_for_administration", Void)
|
||||
l_api.logger.put_debug (generator + ".setup_router_for_webapi", Void)
|
||||
|
||||
-- Configure root of api handler.
|
||||
l_router.set_base_url (l_api.administration_path (Void))
|
||||
l_router.set_base_url (l_api.webapi_path (Void))
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include routes from modules.
|
||||
across
|
||||
@@ -169,10 +191,86 @@ feature -- Settings: router
|
||||
l_module := ic.item
|
||||
if
|
||||
l_module.is_initialized and then
|
||||
attached {CMS_ADMINISTRABLE} l_module as l_administration and then
|
||||
attached l_administration.module_administration as adm
|
||||
attached {CMS_WITH_WEBAPI} l_module as l_webapi and then
|
||||
attached l_webapi.module_webapi as wapi
|
||||
then
|
||||
adm.setup_router (l_router, l_api)
|
||||
wapi.setup_router (l_router, l_api)
|
||||
if attached wapi.filters (l_api) as l_filters then
|
||||
across
|
||||
l_filters as f_ic
|
||||
loop
|
||||
f := f_ic.item
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setup_router_and_filter_for_administration
|
||||
-- <Precursor>
|
||||
local
|
||||
l_api: like api
|
||||
l_router: like router
|
||||
l_module: CMS_MODULE
|
||||
f, l_filter, l_last_filter: WSF_FILTER
|
||||
do
|
||||
l_api := api
|
||||
l_router := router
|
||||
|
||||
l_api.logger.put_debug (generator + ".setup_router_and_filter_for_administration", Void)
|
||||
|
||||
-- Configure root of api handler.
|
||||
l_router.set_base_url (l_api.administration_path (Void))
|
||||
|
||||
-- Apply normal filters
|
||||
setup_filter
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include routes from modules.
|
||||
across
|
||||
modules as ic
|
||||
loop
|
||||
l_module := ic.item
|
||||
if
|
||||
l_module.is_initialized
|
||||
then
|
||||
if
|
||||
attached {CMS_WITH_MODULE_ADMINISTRATION} l_module as l_administration and then
|
||||
attached l_administration.module_administration as adm
|
||||
then
|
||||
adm.setup_router (l_router, l_api)
|
||||
if attached adm.filters (l_api) as l_filters then
|
||||
across
|
||||
l_filters as f_ic
|
||||
loop
|
||||
f := f_ic.item
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
-- elseif
|
||||
-- attached {CMS_WITH_WEBAPI} l_module as l_wapi and then
|
||||
-- attached l_wapi.module_webapi as wapi
|
||||
-- then
|
||||
-- wapi.setup_router (l_router, l_api)
|
||||
end
|
||||
end
|
||||
end
|
||||
map_uri ("/install", create {CMS_ADMIN_INSTALL_HANDLER}.make (api), l_router.methods_head_get)
|
||||
@@ -234,6 +332,8 @@ feature -- Request execution
|
||||
request.set_uploaded_file_path (api.temp_location)
|
||||
if api.is_administration_request (request) then
|
||||
initialize_administration_execution
|
||||
elseif api.is_webapi_request (request) then
|
||||
initialize_webapi_execution
|
||||
else
|
||||
initialize_site_execution
|
||||
end
|
||||
@@ -244,15 +344,24 @@ feature -- Request execution
|
||||
do
|
||||
api.switch_to_site_mode
|
||||
api.initialize_execution
|
||||
setup_filter
|
||||
setup_router
|
||||
end
|
||||
|
||||
initialize_webapi_execution
|
||||
-- Initialize for site execution.
|
||||
do
|
||||
api.switch_to_webapi_mode
|
||||
api.initialize_execution
|
||||
setup_router_and_filter_for_webapi
|
||||
end
|
||||
|
||||
initialize_administration_execution
|
||||
-- Initialize for administration execution.
|
||||
do
|
||||
api.switch_to_administration_mode
|
||||
api.initialize_execution
|
||||
setup_router_for_administration
|
||||
setup_router_and_filter_for_administration
|
||||
end
|
||||
|
||||
execute
|
||||
@@ -277,7 +386,6 @@ feature -- Filters
|
||||
-- Create `filter'.
|
||||
local
|
||||
f, l_filter: detachable WSF_FILTER
|
||||
l_module: CMS_MODULE
|
||||
l_api: like api
|
||||
do
|
||||
l_api := api
|
||||
@@ -294,6 +402,32 @@ feature -- Filters
|
||||
-- f.set_next (l_filter)
|
||||
-- l_filter := f
|
||||
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'.
|
||||
local
|
||||
f, l_filter, l_last_filter: detachable WSF_FILTER
|
||||
l_module: CMS_MODULE
|
||||
l_api: like api
|
||||
do
|
||||
l_api := api
|
||||
|
||||
-- Find insertion location for new filter
|
||||
-- i.e just before the CMS_EXECUTION filter.
|
||||
from
|
||||
l_filter := filter
|
||||
until
|
||||
l_last_filter /= Void or not attached l_filter.next as l_next_filter
|
||||
loop
|
||||
if l_next_filter.next = Void then
|
||||
l_last_filter := l_next_filter
|
||||
else
|
||||
l_filter := l_next_filter
|
||||
end
|
||||
end
|
||||
|
||||
-- Include filters from modules
|
||||
across
|
||||
modules as ic
|
||||
@@ -305,18 +439,14 @@ feature -- Filters
|
||||
then
|
||||
across l_m_filters as f_ic loop
|
||||
f := f_ic.item
|
||||
f.set_next (l_filter)
|
||||
l_filter.set_next (f)
|
||||
f.set_next (l_last_filter)
|
||||
-- f.set_next (l_filter)
|
||||
l_filter := f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'.
|
||||
do
|
||||
-- filter := l_filter
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
|
||||
53
src/service/response/hm_webapi_response.e
Normal file
53
src/service/response/hm_webapi_response.e
Normal file
@@ -0,0 +1,53 @@
|
||||
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: READABLE_STRING_8; a_att_href: READABLE_STRING_8)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
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
|
||||
|
||||
|
||||
175
src/service/response/json_webapi_response.e
Normal file
175
src/service/response/json_webapi_response.e
Normal file
@@ -0,0 +1,175 @@
|
||||
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_string_field ("self", 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: 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)
|
||||
end
|
||||
create lnk.make_with_capacity (2)
|
||||
lnk.put_string (a_attname, "name")
|
||||
lnk.put_string (a_att_href, "href")
|
||||
lnks.put (lnk, rel)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
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
|
||||
17
src/service/response/webapi_response.e
Normal file
17
src/service/response/webapi_response.e
Normal file
@@ -0,0 +1,17 @@
|
||||
note
|
||||
description: "[
|
||||
Generic Web API Response.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEBAPI_RESPONSE
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE_I
|
||||
|
||||
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/service/webapi/cms_module_webapi.e
Normal file
80
src/service/webapi/cms_module_webapi.e
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
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 -- 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
|
||||
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';
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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
|
||||
|
||||
copy_directory (l_src_dir, l_dest_dir, True)
|
||||
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