note description: "[ This class implements the CMS service It could be used to implement the main EWF service, or even for a specific handler. ]" class CMS_SERVICE inherit WSF_SERVICE create make feature {NONE} -- Initialization make (a_setup: CMS_SETUP) local cfg: detachable CMS_CONFIGURATION do cfg := a_setup.configuration if cfg = Void then create cfg.make end configuration := cfg base_url := a_setup.base_url site_id := cfg.site_id site_url := cfg.site_url ("") site_name := cfg.site_name ("EWF::CMS") site_email := cfg.site_email ("webmaster") site_dir := cfg.root_location site_var_dir := cfg.var_location files_location := cfg.files_location themes_location := cfg.themes_location theme_name := cfg.theme_name ("default") set_script_url (cfg.site_script_url (Void)) -- Temporary value compute_theme_resource_location create content_types.make (3) modules := a_setup.modules storage := a_setup.storage session_manager := a_setup.session_manager auth_engine := a_setup.auth_engine mailer := a_setup.mailer initialize_storage initialize_auth_engine initialize_session_manager initialize_mailer initialize_router initialize_modules end initialize_session_manager -- local -- dn: DIRECTORY_NAME do -- create dn.make_from_string (site_var_dir) -- dn.extend ("_storage_") -- dn.extend ("_sessions_") -- create {WSF_FS_SESSION_MANAGER} session_manager.make_with_folder (dn.string) end initialize_storage do if not storage.has_user then initialize_users end end initialize_users require has_no_user: not storage.has_user local u: CMS_USER ur: CMS_USER_ROLE do create u.make_new ("admin") u.set_password ("istrator") storage.save_user (u) create ur.make_with_id (1, "anonymous") storage.save_user_role (ur) create ur.make_with_id (2, "authenticated") ur.add_permission ("create page") ur.add_permission ("edit page") storage.save_user_role (ur) end initialize_mailer local -- ch_mailer: CMS_CHAIN_MAILER -- st_mailer: CMS_STORAGE_MAILER do -- create st_mailer.make (storage) -- create ch_mailer.make (st_mailer) -- ch_mailer.set_next (create {CMS_SENDMAIL_MAILER}) -- mailer := ch_mailer end initialize_router local -- h: CMS_HANDLER file_hdl: CMS_FILE_SYSTEM_HANDLER dn: DIRECTORY_NAME do create router.make (10) router.set_base_url (base_url) router.map (create {WSF_URI_MAPPING}.make ("/", create {CMS_HANDLER}.make (agent handle_home))) router.map (create {WSF_URI_MAPPING}.make ("/favicon.ico", create {CMS_HANDLER}.make (agent handle_favicon))) create file_hdl.make (files_location) file_hdl.disable_index file_hdl.set_max_age (8*60*60) router.map (create {WSF_STARTS_WITH_MAPPING}.make ("/files/", file_hdl)) create dn.make_from_string (theme_resource_location) create file_hdl.make (theme_resource_location) file_hdl.set_max_age (8*60*60) router.map (create {WSF_STARTS_WITH_MAPPING}.make ("/theme/", file_hdl)) end initialize_modules do across modules as m loop if m.item.is_enabled then m.item.register (Current) if attached {CMS_HOOK_AUTO_REGISTER} m.item as h_auto then h_auto.hook_auto_register (Current) end end end end initialize_auth_engine do -- create {CMS_STORAGE_AUTH_ENGINE} auth_engine.make (storage) end feature -- Access configuration: CMS_CONFIGURATION auth_engine: CMS_AUTH_ENGINE modules: LIST [CMS_MODULE] feature -- Hook: menu_alter add_menu_alter_hook (h: like menu_alter_hooks.item) local lst: like menu_alter_hooks do lst := menu_alter_hooks if lst = Void then create lst.make (1) menu_alter_hooks := lst end if not lst.has (h) then lst.force (h) end end menu_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_MENU_ALTER] call_menu_alter_hooks (m: CMS_MENU_SYSTEM; a_execution: CMS_EXECUTION) do if attached menu_alter_hooks as lst then across lst as c loop c.item.menu_alter (m, a_execution) end end end feature -- Hook: form_alter add_form_alter_hook (h: like form_alter_hooks.item) local lst: like form_alter_hooks do lst := form_alter_hooks if lst = Void then create lst.make (1) form_alter_hooks := lst end if not lst.has (h) then lst.force (h) end end form_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_FORM_ALTER] call_form_alter_hooks (f: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_execution: CMS_EXECUTION) do if attached form_alter_hooks as lst then across lst as c loop c.item.form_alter (f, a_form_data, a_execution) end end end feature -- Hook: block add_block_hook (h: like block_hooks.item) local lst: like block_hooks do lst := block_hooks if lst = Void then create lst.make (1) block_hooks := lst end if not lst.has (h) then lst.force (h) end end block_hooks: detachable ARRAYED_LIST [CMS_HOOK_BLOCK] hook_block_view (a_execution: CMS_EXECUTION) do if attached block_hooks as lst then across lst as c loop across c.item.block_list as blst loop c.item.get_block_view (blst.item, a_execution) end end end end feature -- Router site_id: READABLE_STRING_8 site_name: READABLE_STRING_32 site_email: READABLE_STRING_8 site_url: READABLE_STRING_8 site_dir: READABLE_STRING_8 site_var_dir: READABLE_STRING_8 files_location: READABLE_STRING_8 themes_location: READABLE_STRING_8 compute_theme_resource_location local dn: DIRECTORY_NAME do create dn.make_from_string (themes_location) dn.extend (theme_name) dn.extend ("res") theme_resource_location := dn.string end theme_resource_location: READABLE_STRING_8 theme_name: READABLE_STRING_32 router: WSF_ROUTER map_uri_template (tpl: STRING; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) do router.map (create {WSF_URI_TEMPLATE_MAPPING}.make_from_template (tpl, create {CMS_HANDLER}.make (proc))) end map_uri (a_uri: STRING; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) do router.map (create {WSF_URI_MAPPING}.make (a_uri, create {CMS_HANDLER}.make (proc))) end feature -- URL related front_path: STRING do if attached base_url as l_base_url then Result := l_base_url + "/" else Result := "/" end end urls_set: BOOLEAN initialize_urls (req: WSF_REQUEST) local u: like base_url do if not urls_set then u := base_url if u = Void then u := "" end urls_set := True if site_url.is_empty then site_url := req.absolute_script_url (u) end set_script_url (req.script_url (u)) end end base_url: detachable READABLE_STRING_8 -- Base url (related to the script path). script_url: detachable READABLE_STRING_8 set_script_url (a_url: like script_url) local s: STRING_8 do if a_url = Void then script_url := Void elseif not a_url.is_empty then if a_url.ends_with ("/") then create s.make_from_string (a_url) else create s.make (a_url.count + 1) s.append (a_url) s.append_character ('/') end script_url := s end ensure attached script_url as l_url implies l_url.ends_with ("/") end feature -- Report is_front_page (req: WSF_REQUEST): BOOLEAN do Result := req.path_info.same_string (front_path) end feature {CMS_EXECUTION, CMS_MODULE} -- Security report user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN -- Anonymous or user `u' has permission for `s' ? --| `s' could be "create page", do Result := storage.user_has_permission (u, s) end feature -- Storage session_controller (req: WSF_REQUEST): CMS_SESSION_CONTROLER -- New session controller for request `req' do create Result.make (req, session_manager, site_id) end session_manager: WSF_SESSION_MANAGER -- CMS Session manager storage: CMS_STORAGE 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 create l_log.make (a_category, a_message, a_level, Void) if a_link /= Void then l_log.set_link (a_link) end storage.save_log (l_log) end feature -- Content type content_types: ARRAYED_LIST [CMS_CONTENT_TYPE] -- Available content types add_content_type (a_type: CMS_CONTENT_TYPE) do content_types.force (a_type) end content_type (a_name: READABLE_STRING_8): detachable CMS_CONTENT_TYPE do across content_types as t until Result /= Void loop if t.item.name.same_string (a_name) then Result := t.item end end end feature -- Notification mailer: NOTIFICATION_MAILER feature -- Core Execution handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE) local fres: WSF_FILE_RESPONSE fn: FILE_NAME do create fn.make_from_string (theme_resource_location) fn.set_file_name ("favicon.ico") create fres.make (fn.string) fres.set_expires_in_seconds (7 * 24 * 60 * 60) -- 7 jours res.send (fres) end handle_home (req: WSF_REQUEST; res: WSF_RESPONSE) do (create {HOME_CMS_EXECUTION}.make (req, res, Current)).execute end execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Default request handler if no other are relevant local e: CMS_EXECUTION sess: WSF_ROUTER_SESSION do initialize_urls (req) create sess router.dispatch (req, res, sess) if not sess.dispatched then create {NOT_FOUND_CMS_EXECUTION} e.make (req, res, Current) e.execute end end end