Added sql_delete routine to replace sql_modify with "DELETE FROM .." sql statement.
Fixed filter setup when a module has more than one filter.
Fixed filter setup for site,admin and webapi modes.
Added CMS_AUTH_FILTER, and check if user is already authenticated, then skip following auth filters.
Added specific webapi handler classes for root, user, access token, ...
Added user profile system to the core module.
Moved /user/{uid} from auth module to core module.
Added possibility to add html before and after a cms form. (useful to add a form before or after, as nested form are forbidden).
Now theme can be installed using roc install command.
579 lines
15 KiB
Plaintext
579 lines
15 KiB
Plaintext
note
|
|
description: "[
|
|
Class that enable to set basic configuration, application environment, core modules and themes.
|
|
]"
|
|
date: "$Date$"
|
|
revision: "$Revision$"
|
|
|
|
deferred class
|
|
CMS_SETUP
|
|
|
|
inherit
|
|
REFACTORING_HELPER
|
|
|
|
feature {NONE} -- Initialization
|
|
|
|
initialize
|
|
local
|
|
l_url: like site_url
|
|
s, l_email: detachable READABLE_STRING_8
|
|
utf: UTF_CONVERTER
|
|
do
|
|
site_location := environment.path
|
|
|
|
-- Debug mode.
|
|
is_debug := attached string_8_item ("site.debug") as l_debug and then l_debug.is_case_insensitive_equal_general ("yes")
|
|
|
|
--| Site id, used to identified a site, this could be set to a uuid, or else
|
|
site_id := string_8_item_or_default ("site.id", "_ROC_CMS_NO_ID_")
|
|
|
|
-- Site url: optional, but ending with a slash
|
|
l_url := string_8_item ("site_url")
|
|
if l_url /= Void and then not l_url.is_empty then
|
|
if l_url [l_url.count] /= '/' then
|
|
l_url := l_url + "/"
|
|
end
|
|
end
|
|
site_url := l_url
|
|
|
|
-- Site name
|
|
site_name := text_item_or_default ("site.name", "Your ROC CMS")
|
|
|
|
-- Website email used to send email.
|
|
-- used as real "From:" email.
|
|
-- Any "From:" header passed to the CMS email sender will appear as "Reply-To:"
|
|
-- or ignored if a reply-to header is already set.
|
|
l_email := string_8_item ("site.email")
|
|
if l_email = Void then
|
|
-- FIXME: find better default value!
|
|
-- Or handler configuration error (missing value)!!!
|
|
l_email := string_8_item_or_default ("mailer.from", "webmaster")
|
|
end
|
|
if l_email.has ('<') then
|
|
l_email := utf.string_32_to_utf_8_string_8 (site_name) + " <" + l_email + ">"
|
|
end
|
|
site_email := l_email
|
|
|
|
-- Email address for current web site
|
|
site_notification_email := string_8_item_or_default ("notification.email", site_email)
|
|
-- Email subject tuning.
|
|
s := string_8_item ("mailer.subject_prefix")
|
|
if s /= Void and then not s.ends_with_general (" ") then
|
|
s := s + " "
|
|
end
|
|
site_email_subject_prefix := s
|
|
|
|
|
|
-- Location for public files
|
|
if attached text_item ("files-dir") as l_files_dir then
|
|
create files_location.make_from_string (l_files_dir)
|
|
else
|
|
files_location := site_location.extended ("files")
|
|
end
|
|
|
|
-- Location for temp files
|
|
if attached text_item ("temp-dir") as l_temp_dir then
|
|
create temp_location.make_from_string (l_temp_dir)
|
|
else
|
|
temp_location := site_location.extended ("temp")
|
|
end
|
|
|
|
-- Location for cache folder
|
|
if attached text_item ("cache-dir") as l_cache_dir then
|
|
create cache_location.make_from_string (l_cache_dir)
|
|
else
|
|
cache_location := temp_location.extended ("cache")
|
|
end
|
|
|
|
-- Location for modules folders.
|
|
if attached text_item ("modules-dir") as l_modules_dir then
|
|
create modules_location.make_from_string (l_modules_dir)
|
|
else
|
|
modules_location := environment.modules_path
|
|
end
|
|
|
|
-- Location for themes folders.
|
|
if attached text_item ("themes-dir") as l_themes_dir then
|
|
create themes_location.make_from_string (l_themes_dir)
|
|
else
|
|
themes_location := environment.themes_path
|
|
end
|
|
|
|
-- Selected theme's name
|
|
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
|
|
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 administration_base_path.make_from_string (l_url)
|
|
else
|
|
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
|
|
|
|
environment: CMS_ENVIRONMENT
|
|
-- CMS environment.
|
|
|
|
layout: CMS_ENVIRONMENT
|
|
-- CMS environment.
|
|
obsolete "use `environment' [2017-05-31]"
|
|
do
|
|
Result := environment
|
|
end
|
|
|
|
feature {CMS_API} -- API Access
|
|
|
|
frozen fill_enabled_modules (api: CMS_API)
|
|
-- List of enabled modules.
|
|
local
|
|
l_enabled_modules: CMS_MODULE_COLLECTION
|
|
l_modules_to_remove: CMS_MODULE_COLLECTION
|
|
l_module: CMS_MODULE
|
|
l_core: CMS_CORE_MODULE
|
|
do
|
|
l_enabled_modules := api.enabled_modules
|
|
|
|
-- Include required CORE module
|
|
create l_core.make
|
|
l_core.enable
|
|
l_enabled_modules.extend (l_core)
|
|
|
|
-- Includes other modules.
|
|
across
|
|
modules as ic
|
|
loop
|
|
l_module := ic.item
|
|
update_module_status_from_configuration (l_module)
|
|
if not l_module.is_enabled then
|
|
if
|
|
api.is_module_enabled (l_module) -- Check from storage!
|
|
then
|
|
l_module.enable
|
|
end
|
|
end
|
|
if l_module.is_enabled then
|
|
l_enabled_modules.extend (l_module)
|
|
end
|
|
end
|
|
across
|
|
l_enabled_modules as ic
|
|
loop
|
|
l_module := ic.item
|
|
update_module_status_within (l_module, l_enabled_modules)
|
|
if not l_module.is_enabled then -- Check from storage!
|
|
if l_modules_to_remove = Void then
|
|
create l_modules_to_remove.make (1)
|
|
end
|
|
l_modules_to_remove.extend (l_module)
|
|
end
|
|
end
|
|
if l_modules_to_remove /= Void then
|
|
across
|
|
l_modules_to_remove as ic
|
|
loop
|
|
l_enabled_modules.remove (ic.item)
|
|
end
|
|
end
|
|
ensure
|
|
only_enabled_modules: across api.enabled_modules as ic all ic.item.is_enabled end
|
|
end
|
|
|
|
feature {CMS_MODULE, CMS_API, CMS_SETUP_ACCESS} -- Restricted access
|
|
|
|
modules: CMS_MODULE_COLLECTION
|
|
-- List of available modules.
|
|
deferred
|
|
end
|
|
|
|
feature {NONE} -- Implementation: update
|
|
|
|
update_module_status_within (a_module: CMS_MODULE; a_collection: CMS_MODULE_COLLECTION)
|
|
-- Update status of module `a_module', taking into account its dependencies within the collection `a_collection'.
|
|
require
|
|
a_module_is_enabled: a_module.is_enabled
|
|
do
|
|
if attached a_module.dependencies as deps then
|
|
across
|
|
deps as ic
|
|
until
|
|
not a_module.is_enabled
|
|
loop
|
|
if ic.item.is_required then
|
|
if
|
|
attached a_collection.item (ic.item.module_type) as mod and then
|
|
mod.is_enabled
|
|
then
|
|
update_module_status_within (mod, a_collection)
|
|
else
|
|
--| dependency not found or disabled
|
|
a_module.disable
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
update_module_status_from_configuration (m: CMS_MODULE)
|
|
-- Update status of module `m' according to configuration.
|
|
local
|
|
b: BOOLEAN
|
|
dft: BOOLEAN
|
|
do
|
|
-- By default, keep previous status.
|
|
if attached text_item ("modules.*") as l_mod_status then
|
|
dft := l_mod_status.is_case_insensitive_equal_general ("on")
|
|
else
|
|
dft := m.is_enabled
|
|
end
|
|
if attached text_item ("modules." + m.name) as l_mod_status then
|
|
b := l_mod_status.is_case_insensitive_equal_general ("on")
|
|
else
|
|
b := dft
|
|
end
|
|
if b then
|
|
m.enable
|
|
else
|
|
m.disable
|
|
end
|
|
end
|
|
|
|
feature -- Access: Site
|
|
|
|
site_id: READABLE_STRING_8
|
|
-- String identifying current CMS.
|
|
-- This could be used in webform, for cookie name, ...
|
|
|
|
site_name: READABLE_STRING_32
|
|
-- Name of the site.
|
|
|
|
utf_8_site_name: READABLE_STRING_8
|
|
-- `site_name' encoded with UTF-8.
|
|
local
|
|
utf: UTF_CONVERTER
|
|
do
|
|
Result := utf.utf_32_string_to_utf_8_string_8 (site_name)
|
|
end
|
|
|
|
site_properties: detachable STRING_TABLE [READABLE_STRING_32]
|
|
-- Optional site properties.
|
|
do
|
|
Result := text_table_item ("site.property")
|
|
end
|
|
|
|
site_property (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
|
do
|
|
Result := text_item ({STRING_32} "site." + a_name.as_string_32)
|
|
if Result = Void and then attached site_properties as props then
|
|
Result := props.item (a_name)
|
|
end
|
|
end
|
|
|
|
site_description: detachable READABLE_STRING_32
|
|
-- Optional site description.
|
|
do
|
|
Result := site_property ("description")
|
|
end
|
|
|
|
site_headline: detachable READABLE_STRING_32
|
|
-- Optional site headline.
|
|
do
|
|
Result := site_property ("headline")
|
|
end
|
|
|
|
site_keywords: detachable READABLE_STRING_32
|
|
-- Optional site comma separated keywords.
|
|
do
|
|
Result := site_property ("keywords")
|
|
end
|
|
|
|
site_email: READABLE_STRING_8
|
|
-- Website email address.
|
|
-- Used as "From:" address when the site is sending emails
|
|
-- cf: `CMS_SETUP.mailer'.
|
|
|
|
site_notification_email: READABLE_STRING_8
|
|
-- Email address receiving internal notification.
|
|
|
|
site_email_subject_prefix: detachable READABLE_STRING_8
|
|
-- Optional prefix for any email sent by Current site.
|
|
|
|
site_url: detachable READABLE_STRING_8
|
|
-- Optional url of current CMS site.
|
|
|
|
front_page_path: detachable READABLE_STRING_8
|
|
-- 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
|
|
|
|
is_debug: BOOLEAN
|
|
-- Is debug mode enabled?
|
|
|
|
set_site_mode
|
|
-- Switch to site mode.
|
|
--| - Change theme
|
|
--| - ..
|
|
do
|
|
if is_theme_valid (site_theme_name) then
|
|
set_theme (site_theme_name)
|
|
else
|
|
-- Keep previous theme!
|
|
end
|
|
end
|
|
|
|
set_webapi_mode
|
|
-- Switch to webapi mode.
|
|
do
|
|
set_site_mode
|
|
end
|
|
|
|
set_administration_mode
|
|
-- Switch to administration mode.
|
|
--| - Change theme
|
|
--| - ..
|
|
do
|
|
if is_theme_valid (administration_theme_name) then
|
|
set_theme (administration_theme_name)
|
|
else
|
|
-- Keep previous theme!
|
|
end
|
|
end
|
|
|
|
set_theme (a_name: READABLE_STRING_GENERAL)
|
|
-- Set theme to `a_name`.
|
|
do
|
|
theme_name := a_name.as_string_32
|
|
theme_location := theme_location_for (theme_name)
|
|
end
|
|
|
|
feature -- Query
|
|
|
|
text_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
|
-- Configuration value associated with `a_name', if any.
|
|
deferred
|
|
end
|
|
|
|
text_list_item (a_name: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
|
|
-- Configuration values associated with `a_name', if any.
|
|
deferred
|
|
end
|
|
|
|
text_table_item (a_name: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
|
|
-- Configuration indexed values associated with `a_name', if any.
|
|
deferred
|
|
end
|
|
|
|
text_item_or_default (a_name: READABLE_STRING_GENERAL; a_default_value: READABLE_STRING_GENERAL): READABLE_STRING_32
|
|
-- `text_item' associated with `a_name' or if none, `a_default_value'.
|
|
do
|
|
if attached text_item (a_name) as v then
|
|
Result := v
|
|
else
|
|
Result := a_default_value.as_string_32
|
|
end
|
|
end
|
|
|
|
string_8_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
|
|
-- Configuration value associated with `a_name', if any.
|
|
deferred
|
|
end
|
|
|
|
string_8_item_or_default (a_name: READABLE_STRING_GENERAL; a_default_value: READABLE_STRING_8): READABLE_STRING_8
|
|
-- `string_8_item' associated with `a_name' or if none, `a_default_value'.
|
|
do
|
|
if attached string_8_item (a_name) as v then
|
|
Result := v
|
|
else
|
|
Result := a_default_value
|
|
end
|
|
end
|
|
|
|
feature -- Access: directory
|
|
|
|
site_location: PATH
|
|
-- Path to CMS site root dir.
|
|
|
|
temp_location: PATH
|
|
-- Path to folder used as temporary dir.
|
|
-- (Mainly for uploaded file).
|
|
|
|
files_location: PATH
|
|
-- Path to public "files" dir.
|
|
|
|
cache_location: PATH
|
|
-- Path to internal cache dir.
|
|
|
|
modules_location: PATH
|
|
-- Path to modules.
|
|
|
|
themes_location: PATH
|
|
-- Path to themes.
|
|
|
|
feature -- Access: theme
|
|
|
|
theme_location: PATH
|
|
-- Path to a active theme.
|
|
|
|
is_theme_valid (a_theme_name: READABLE_STRING_GENERAL): BOOLEAN
|
|
-- Does `a_theme_name` exists?
|
|
local
|
|
fu: FILE_UTILITIES
|
|
do
|
|
Result := fu.directory_path_exists (theme_location_for (a_theme_name))
|
|
end
|
|
|
|
theme_information_location: PATH
|
|
-- Active theme informations.
|
|
do
|
|
Result := theme_location.extended ("theme.info")
|
|
end
|
|
|
|
theme_name: READABLE_STRING_32
|
|
-- theme name.
|
|
|
|
site_theme_name: READABLE_STRING_32
|
|
-- Site theme name.
|
|
|
|
administration_theme_name: READABLE_STRING_32
|
|
-- Administration theme name.
|
|
-- Default: same as site theme.
|
|
-- TODO: change to builtin "admin" theme?
|
|
|
|
theme_location_for (a_theme_name: READABLE_STRING_GENERAL): PATH
|
|
-- Theme directory location for theme `a_theme_name`.
|
|
do
|
|
Result := themes_location.extended (a_theme_name)
|
|
end
|
|
|
|
feature -- Access
|
|
|
|
mailer: NOTIFICATION_MAILER
|
|
-- Email processor.
|
|
deferred
|
|
end
|
|
|
|
feature -- Access: storage
|
|
|
|
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
|
|
-- Table of available storage drivers.
|
|
-- i.e: mysql, sqlite, ...
|
|
deferred
|
|
end
|
|
|
|
storage (a_error_handler: ERROR_HANDLER): detachable CMS_STORAGE
|
|
-- CMS Storage object defined according to the configuration or default.
|
|
-- Use `a_error_handler' to get eventual error information occurred during the storage
|
|
-- initialization.
|
|
local
|
|
retried: BOOLEAN
|
|
l_message: STRING
|
|
do
|
|
if not retried then
|
|
to_implement ("Refactor database setup")
|
|
if
|
|
attached (create {APPLICATION_JSON_CONFIGURATION_HELPER}).new_database_configuration (environment.application_config_path) as l_database_config and then
|
|
attached storage_drivers.item (l_database_config.driver) as l_builder
|
|
then
|
|
Result := l_builder.storage (Current, a_error_handler)
|
|
end
|
|
else
|
|
to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.")
|
|
-- error handling.
|
|
create l_message.make (1024)
|
|
if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then
|
|
if attached l_exception.description as l_description then
|
|
l_message.append (l_description.as_string_32)
|
|
l_message.append ("%N%N")
|
|
elseif attached l_exception.trace as l_trace then
|
|
l_message.append (l_trace)
|
|
l_message.append ("%N%N")
|
|
else
|
|
l_message.append (l_exception.out)
|
|
l_message.append ("%N%N")
|
|
end
|
|
else
|
|
l_message.append ("The application crash without available information")
|
|
l_message.append ("%N%N")
|
|
end
|
|
a_error_handler.add_custom_error (0, " Database Connection ", l_message)
|
|
end
|
|
rescue
|
|
retried := True
|
|
retry
|
|
end
|
|
|
|
feature -- Status Report: Modules
|
|
|
|
module_registered (m: CMS_MODULE): BOOLEAN
|
|
-- Is the module `m' registered?
|
|
do
|
|
Result := modules.has (m)
|
|
end
|
|
|
|
module_with_same_type_registered (m: CMS_MODULE): BOOLEAN
|
|
-- Is there a module `m' already registered with the same type?
|
|
do
|
|
Result := modules.has_module_with_same_type (m)
|
|
end
|
|
|
|
feature -- Element change
|
|
|
|
register_module (m: CMS_MODULE)
|
|
-- Add module `m' to `modules'.
|
|
require
|
|
module_not_registered: not module_registered (m)
|
|
no_module_with_same_type_registered: not module_with_same_type_registered (m)
|
|
deferred
|
|
ensure
|
|
module_registered: module_registered (m)
|
|
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
|