note
	description: "Summary description for {BAD_REQUEST_ERROR_CMS_RESPONSE}."
	date: "$Date: 2017-09-06 20:40:40 +0000 (Wed, 06 Sep 2017) $"
	revision: "$Revision: 100739 $"

class 
	BAD_REQUEST_ERROR_CMS_RESPONSE

create 
	make

feature {NONE} -- Initialization

	default_create
			-- Process instances of classes with no creation clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	initialize
			-- (from CMS_RESPONSE)
		do
			Precursor
			get_theme
			create menu_system.make
			initialize_block_region_settings
		end

	make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
			-- (from CMS_RESPONSE)
		do
			create values.make (3)
			Precursor (req, res, a_api)
		end
	
feature -- Access

	generating_type: TYPE [detachable BAD_REQUEST_ERROR_CMS_RESPONSE]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end

	header: WSF_HEADER
			-- (from CMS_RESPONSE_I)

	main_content: detachable STRING_8
			-- (from CMS_RESPONSE)

	request: WSF_REQUEST
			-- (from CMS_RESPONSE_I)

	response: WSF_RESPONSE
			-- (from CMS_RESPONSE_I)

	status_code: INTEGER_32
			-- (from CMS_RESPONSE_I)
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal (other: BAD_REQUEST_ERROR_CMS_RESPONSE): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_equal (other: BAD_REQUEST_ERROR_CMS_RESPONSE): BOOLEAN
			-- Is other attached to an object considered
			-- equal to current object?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal (other: BAD_REQUEST_ERROR_CMS_RESPONSE): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end
	
feature -- Status report

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: BAD_REQUEST_ERROR_CMS_RESPONSE)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: BAD_REQUEST_ERROR_CMS_RESPONSE)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: BAD_REQUEST_ERROR_CMS_RESPONSE
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: BAD_REQUEST_ERROR_CMS_RESPONSE)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: BAD_REQUEST_ERROR_CMS_RESPONSE
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: BAD_REQUEST_ERROR_CMS_RESPONSE
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature -- Basic operations

	frozen as_attached: attached BAD_REQUEST_ERROR_CMS_RESPONSE
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable BAD_REQUEST_ERROR_CMS_RESPONSE
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature -- API

	api: CMS_API
			-- Current CMS API.
			-- (from CMS_RESPONSE_I)

	formats: CMS_FORMATS
			-- Available content formats.
			-- (from CMS_RESPONSE)
		do
			Result := api.formats
		end

	setup: CMS_SETUP
			-- Current setup
			-- (from CMS_RESPONSE_I)
		do
			Result := api.setup
		end
	
feature -- Access: CMS

	front_page_url: READABLE_STRING_8
			-- (from CMS_RESPONSE_I)
		do
			Result := absolute_url ("/", Void)
		end

	site_name: STRING_32
			-- (from CMS_RESPONSE_I)
		do
			Result := setup.site_name.as_string_32
		end

	values: CMS_VALUE_TABLE
			-- Associated values indexed by string name.
			-- (from CMS_RESPONSE)
	
feature -- Access: metadata

	add_metadata (v: READABLE_STRING_32; k: READABLE_STRING_GENERAL)
			-- Add v as metadata k, allows mutiple occurrences.
			-- (from CMS_RESPONSE_I)
		local
			md: like metadata
		do
			md := metadata
			if md = Void then
				create md.make (1)
				metadata := md
			end
			md.add (v, k)
		end

	additional_page_head_lines: detachable LIST [READABLE_STRING_8]
			-- HTML>head>...extra lines
			-- (from CMS_RESPONSE)

	description: detachable READABLE_STRING_32
			-- (from CMS_RESPONSE)

	keywords: detachable ARRAYED_LIST [READABLE_STRING_32]
			-- (from CMS_RESPONSE_I)

	modification_date: detachable DATE_TIME
			-- Optional modification date.
			-- (from CMS_RESPONSE_I)

	page_title: detachable READABLE_STRING_32
			-- Page title
			-- (from CMS_RESPONSE)

	publication_date: detachable DATE_TIME
			-- Optional publication date.
			-- (from CMS_RESPONSE_I)

	redirection: detachable READABLE_STRING_8
			-- Location for eventual redirection.
			-- (from CMS_RESPONSE_I)

	redirection_delay: NATURAL_32
			-- Optional redirection delay in seconds.
			-- (from CMS_RESPONSE_I)

	set_metadata (v: READABLE_STRING_32; k: READABLE_STRING_GENERAL)
			-- Set v as metadata k, replace any previous occurrences (if any).
			-- (from CMS_RESPONSE_I)
		local
			md: like metadata
		do
			md := metadata
			if md = Void then
				create md.make (1)
				metadata := md
			end
			md.put (v, k)
		end

	title: detachable READABLE_STRING_32
			-- (from CMS_RESPONSE)
	
feature -- Access: query

	location: IMMUTABLE_STRING_8
			-- Associated cms local location.
			-- (from CMS_RESPONSE_I)

	request_url (opts: detachable CMS_API_OPTIONS): STRING_8
			-- Current request location as a url.
			-- (from CMS_RESPONSE_I)
		do
			Result := url (location, opts)
		end
	
feature -- Block management

	block_cache (a_block_id: READABLE_STRING_8): detachable TUPLE [cache_block: CMS_CACHE_BLOCK; region: READABLE_STRING_8; expired: BOOLEAN]
			-- Cached version of block a_block_id.
			-- (from CMS_RESPONSE)
		local
			l_cache: CMS_FILE_STRING_8_CACHE
		do
			if attached setup.text_item ("blocks." + a_block_id + ".expiration") as nb_secs and then nb_secs.is_integer then
				if attached block_region_preference (a_block_id, "none") as l_region and then not l_region.same_string_general ("none") then
					create l_cache.make (api.cache_location.extended ("_blocks").extended (a_block_id).appended_with_extension ("html"))
					if l_cache.exists and then not l_cache.expired (Void, nb_secs.to_integer) then
						Result := [create {CMS_CACHE_BLOCK}.make (a_block_id, l_cache), l_region, False]
					else
						Result := [create {CMS_CACHE_BLOCK}.make (a_block_id, l_cache), l_region, True]
					end
				end
			end
		end

	block_conditions (a_block_id: READABLE_STRING_8): detachable ARRAYED_LIST [CMS_BLOCK_EXPRESSION_CONDITION]
			-- Condition associated with a_block_id in configuration, if any.
			-- (from CMS_RESPONSE)
		do
			if attached setup.text_item ("blocks." + a_block_id + ".condition") as s then
				create Result.make (1)
				Result.force (create {CMS_BLOCK_EXPRESSION_CONDITION}.make (s))
			end
			if attached setup.text_list_item ("blocks." + a_block_id + ".conditions") as lst then
				if Result = Void then
					create Result.make (lst.count)
				end
				across
					lst as ic
				loop
					Result.force (create {CMS_BLOCK_EXPRESSION_CONDITION}.make (ic.item))
				end
			end
		end

	block_options (a_block_id: READABLE_STRING_8): detachable STRING_TABLE [READABLE_STRING_32]
			-- Options associated with a_block_id in configuration, if any.
			-- (from CMS_RESPONSE)
		do
			if attached setup.text_table_item ("blocks." + a_block_id + ".options") as tb then
				Result := tb
			end
		end

	clear_block_caches (a_block_id_list: detachable ITERABLE [READABLE_STRING_GENERAL])
			-- Clear cache for block a_block_id_list if set,
			-- otherwise clear all block caches if a_block_id_list is Void.
			-- (from CMS_RESPONSE)
		local
			p, pb: PATH
			dir: DIRECTORY
			l_cache: CMS_FILE_STRING_8_CACHE
		do
			p := api.cache_location.extended ("_blocks")
			if a_block_id_list /= Void then
				across
					a_block_id_list as ic
				loop
					pb := p.extended (ic.item).appended_with_extension ("html")
					create l_cache.make (pb)
					if l_cache.exists then
						l_cache.delete
					end
				end
			else
				create dir.make_with_path (p)
				dir.recursive_delete
			end
			add_notice_message ("Blocks cache cleared.")
		end

	is_block_included (a_block_id: READABLE_STRING_8; dft: BOOLEAN): BOOLEAN
			-- Is block a_block_id included in current response?
			-- If no preference, return dft.
			-- (from CMS_RESPONSE)
		do
			if attached block_conditions (a_block_id) as l_conditions then
				Result := across
					l_conditions as ic
				some
					ic.item.satisfied_for_response (Current)
				end
			else
				Result := dft
			end
		end

	update_block (a_block: CMS_BLOCK)
			-- Update parameters for block a_block according to configuration.
			-- (from CMS_RESPONSE)
		do
			if attached setup.text_item ("blocks." + a_block.name + ".weight") as w and then w.is_integer then
				a_block.set_weight (w.to_integer)
			end
			if attached setup.text_item ("blocks." + a_block.name + ".title") as l_title then
				if l_title.same_string ("<none>".as_string_32) then
					a_block.set_title (Void)
				else
					a_block.set_title (l_title)
				end
			end
		end
	
feature {CMS_HOOK_CORE_MANAGER} -- Block management: internal

	block_alias_table: detachable STRING_TABLE [LIST [READABLE_STRING_8]]
			-- Table of included block aliases, if any.
			-- note: { block_id => [ alias-names ..] }
			-- (from CMS_RESPONSE)
		local
			k, v: READABLE_STRING_GENERAL
			l_block_id, l_alias_id: READABLE_STRING_8
			lst: detachable LIST [READABLE_STRING_8]
		do
			Result := internal_block_alias_table
			if Result = Void and then attached setup.text_table_item ("blocks.&aliases") as tb then
				create Result.make (tb.count)
				across
					tb as ic
				loop
					k := ic.key
					v := ic.item
					if v.is_valid_as_string_8 then
						l_block_id := v.to_string_8
						if k.is_valid_as_string_8 then
							l_alias_id := k.to_string_8
							if is_block_included (l_alias_id, False) then
								lst := Result.item (l_block_id)
								if lst = Void then
									create {ARRAYED_LIST [READABLE_STRING_8]} lst.make (1)
								end
								lst.force (l_alias_id)
								Result.force (lst, l_block_id)
							end
						else
							check
								valid_alias_id: False
							end
						end
					else
						check
							valid_block_id: False
						end
					end
				end
			end
		end

	internal_block_alias_table: like block_alias_table
			-- Internal memory cache for block_alias_table.
			-- (from CMS_RESPONSE)
	
feature {NONE} -- Blocks

	put_core_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8; is_block_included_by_default: BOOLEAN; a_alias_table: like block_alias_table)
			-- Add block b to associated region or a_default_region if provided
			-- and check optional associated condition.
			-- If no condition then use is_block_included_by_default to
			-- decide if block is included or not.
			-- (from CMS_RESPONSE)
		local
			l_region: detachable like block_region
		do
			if is_block_included (b.name, is_block_included_by_default) then
				l_region := block_region (b, a_default_region)
				l_region.extend (b)
				blocks.force (b, b.name)
			end
			if a_alias_table /= Void and then attached a_alias_table.item (b.name) as l_aliases then
				across
					l_aliases as ic
				loop
					add_block (create {CMS_ALIAS_BLOCK}.make_with_block (ic.item, b), a_default_region)
				end
			end
		end
	
feature -- Blocks

	add_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8)
			-- Add block b to associated region or a_default_region if provided.
			-- WARNING: ignore any block condition! USE WITH CARE!
			-- (from CMS_RESPONSE)
		local
			l_region: detachable like block_region
		do
			l_region := block_region (b, a_default_region)
			l_region.extend (b)
			blocks.force (b, b.name)
		end

	content_block: CMS_CONTENT_BLOCK
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			if attached main_content as l_content then
				s := l_content
			else
				s := ""
				debug
					s := "No Content"
				end
			end
			create Result.make ("content", Void, s, Void)
			Result.set_weight (-1)
			Result.set_is_raw (True)
		end

	get_blocks
			-- Get block from CMS core, and from modules.
			-- (from CMS_RESPONSE)
		local
			l_region: CMS_BLOCK_REGION
			b: CMS_BLOCK
		do
			get_core_blocks
			get_module_blocks
			across
				regions as reg_ic
			loop
				l_region := reg_ic.item
				across
					l_region.blocks as ic
				loop
					update_block (ic.item)
				end
				l_region.sort
			end
			debug ("cms")
				create {CMS_CONTENT_BLOCK} b.make ("made_with", Void, "Made with <a href=%"https://www.eiffel.org/%">EWF</a>", Void)
				b.set_weight (99)
				put_block (b, "footer", True)
			end
		end

	get_core_blocks
			-- Get blocks provided by the CMS core.
			-- (from CMS_RESPONSE)
		local
			l_alias_table: like block_alias_table
		do
			l_alias_table := block_alias_table
			put_core_block (top_header_block, "top", True, l_alias_table)
			put_core_block (header_block, "header", True, l_alias_table)
			if attached message_block as m then
				put_core_block (m, "content", True, l_alias_table)
			end
			if attached primary_tabs_block as m then
				put_core_block (m, "content", True, l_alias_table)
			end
			add_block (content_block, "content")
			if attached management_menu_block as l_block then
				put_core_block (l_block, "sidebar_first", True, l_alias_table)
			end
			if attached navigation_menu_block as l_block then
				put_core_block (l_block, "sidebar_first", True, l_alias_table)
			end
			if attached user_menu_block as l_block then
				put_core_block (l_block, "sidebar_second", True, l_alias_table)
			end
		end

	get_module_blocks
			-- Get blocks provided by modules.
			-- (from CMS_RESPONSE)
		do
			api.hooks.invoke_block (Current)
		end

	header_block: CMS_CONTENT_BLOCK
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
			l_hb: STRING_8
		do
			create s.make_from_string (theme.menu_html (primary_menu, True, Void))
			create l_hb.make_empty
			create Result.make ("header", Void, l_hb, Void)
			Result.set_weight (-4)
			Result.set_is_raw (True)
		end

	horizontal_primary_menu_html: STRING_8
			-- (from CMS_RESPONSE)
		do
			create Result.make_empty
			Result.append ("<div id=%"menu-bar%">")
			Result.append (theme.menu_html (primary_menu, True, Void))
			Result.append ("</div>")
		end

	horizontal_primary_tabs_html: STRING_8
			-- (from CMS_RESPONSE)
		do
			create Result.make_empty
			Result.append ("<div id=%"tabs-bar%">")
			Result.append (theme.menu_html (primary_tabs, True, Void))
			Result.append ("</div>")
		end

	management_menu_block: detachable CMS_MENU_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached management_menu as m and then not m.is_empty then
				create Result.make (m)
			end
		end

	message_block: detachable CMS_CONTENT_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached message as m and then not m.is_empty then
				create Result.make ("message", Void, "<div id=%"message%">" + m + "</div>", Void)
				Result.set_is_raw (True)
				Result.set_weight (-3)
			end
		end

	message_html: detachable STRING_8
			-- (from CMS_RESPONSE)
		do
			if attached message as m and then not m.is_empty then
				Result := "<div id=%"message%">" + m + "</div>"
			end
		end

	navigation_menu_block: detachable CMS_MENU_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached navigation_menu as m and then not m.is_empty then
				create Result.make (m)
			end
		end

	primary_menu_block: detachable CMS_MENU_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached primary_menu as m and then not m.is_empty then
				create Result.make (m)
			end
		end

	primary_tabs_block: detachable CMS_MENU_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached primary_tabs as m and then not m.is_empty then
				create Result.make (m)
				Result.is_horizontal := True
				Result.set_is_raw (True)
				Result.set_weight (-2)
				Result.add_css_class ("tabs")
			end
		end

	put_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8; is_block_included_by_default: BOOLEAN)
			-- Add block b to associated region or a_default_region if provided
			-- and check optional associated condition.
			-- If no condition then use is_block_included_by_default to
			-- decide if block is included or not.
			-- (from CMS_RESPONSE)
		do
			if is_block_included (b.name, is_block_included_by_default) then
				add_block (b, a_default_region)
			end
		end

	remove_block (b: CMS_BLOCK)
			-- Remove block b from associated region.
			-- (from CMS_RESPONSE)
		local
			l_region: detachable like block_region
			l_found: BOOLEAN
		do
			across
				regions as reg_ic
			until
				l_found
			loop
				l_region := reg_ic.item
				l_found := l_region.blocks.has (b)
				if l_found then
					l_region.remove (b)
				end
			end
			blocks.remove (b.name)
		end

	top_header_block: CMS_CONTENT_BLOCK
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			create s.make_empty
			create Result.make ("page_top", Void, s, Void)
			Result.set_weight (-5)
			Result.set_is_raw (True)
		end

	user_menu_block: detachable CMS_MENU_BLOCK
			-- (from CMS_RESPONSE)
		do
			if attached user_menu as m and then not m.is_empty then
				create Result.make (m)
			end
		end
	
feature -- Blocks initialization

	block_region_preference (a_block_id: READABLE_STRING_8; a_default_region: READABLE_STRING_8): READABLE_STRING_8
			-- Region associated with a_block_id in configuration, if any.
			-- (from CMS_RESPONSE)
		do
			Result := setup.string_8_item_or_default ("blocks." + a_block_id + ".region", a_default_region)
		end

	initialize_block_region_settings
			-- (from CMS_RESPONSE)
		local
			l_table: like block_region_settings
		do
			debug ("refactor_fixme")
				fixme ("CHECK:Can we use the same structure as in theme.info?")
				fixme ("let the user choose ...")
			end
			create regions.make_caseless (5)
			create blocks.make (10)
			create l_table.make_caseless (10)
			l_table ["top"] := block_region_preference ("top", "top").as_string_8
			l_table ["header"] := block_region_preference ("header", "header").as_string_8
			l_table ["highlighted"] := block_region_preference ("highlighted", "highlighted").as_string_8
			l_table ["help"] := block_region_preference ("help", "help").as_string_8
			l_table ["content"] := block_region_preference ("content", "content").as_string_8
			l_table ["footer"] := block_region_preference ("footer", "footer").as_string_8
			l_table ["management"] := block_region_preference ("management", "sidebar_first").as_string_8
			l_table ["navigation"] := block_region_preference ("navigation", "sidebar_first").as_string_8
			l_table ["user"] := block_region_preference ("user", "sidebar_first").as_string_8
			l_table ["bottom"] := block_region_preference ("bottom", "page_bottom").as_string_8
			block_region_settings := l_table
		end
	
feature -- Blocks regions

	block_region (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8): CMS_BLOCK_REGION
			-- Region associated with block b, or else a_default_region if provided.
			-- (from CMS_RESPONSE)
		local
			l_region_name: detachable READABLE_STRING_8
		do
			l_region_name := block_region_settings.item (b.name)
			if l_region_name = Void then
				if attached setup.text_item ("blocks." + b.name + ".region") as l_setup_name then
					l_region_name := l_setup_name.as_string_8
					block_region_settings.force (l_region_name.as_string_8, b.name)
				elseif a_default_region /= Void then
					l_region_name := a_default_region
				else
					l_region_name := b.name.as_lower
				end
			end
			if attached regions.item (l_region_name) as res then
				Result := res
			else
				create Result.make (l_region_name)
				regions.force (Result, l_region_name)
			end
		end

	block_region_settings: STRING_TABLE [STRING_8]
			-- (from CMS_RESPONSE)

	blocks: STRING_TABLE [CMS_BLOCK]
			-- Blocks indexed by their block id.
			-- (from CMS_RESPONSE)

	regions: STRING_TABLE [CMS_BLOCK_REGION]
			-- Layout regions, that contains blocks.
			-- (from CMS_RESPONSE)
	
feature -- Cache managment

	clear_cache (a_cache_id_list: detachable ITERABLE [READABLE_STRING_GENERAL])
			-- Clear caches identified by a_cache_id_list,
			-- or clear all caches if a_cache_id_list is Void.	
			-- (from CMS_RESPONSE)
		do
			if has_permissions (<<"clear blocks cache", "admin core caches">>) then
				clear_block_caches (a_cache_id_list)
			end
		end
	
feature -- Change: metadata

	metadata: detachable CMS_RESPONSE_METADATA
			-- Optional metadata.
			-- For instance:
			--	type or og:type: Optional public type of the response page (related to http://ogp.me/)
			-- (from CMS_RESPONSE_I)
	
feature -- Core

	based_path (p: STRING_8): STRING_8
			-- Path p in the context of the base_url
			-- (from CMS_URL_UTILITIES)
		do
			if attached base_url as l_base_url then
				create Result.make_from_string (l_base_url)
				if p.is_empty then
				else
					if p [1] = '/' then
						Result.append (p.substring (2, p.count))
					else
						Result.append (p)
					end
				end
			else
				Result := p
			end
		end
	
feature -- Element Change

	set_status_code (a_status: INTEGER_32)
			-- Set status_code with a_status.
			-- (from CMS_RESPONSE_I)
		do
			status_code := a_status
		ensure -- from CMS_RESPONSE_I
			status_code_set: status_code = a_status
		end
	
feature -- Element change				

	add_keyword (a_keyword: READABLE_STRING_GENERAL)
			-- Add keyword keyword to the existing list of keywords.
			-- (from CMS_RESPONSE_I)
		local
			lst: like keywords
		do
			lst := keywords
			if lst = Void then
				create lst.make (1)
				keywords := lst
			end
			lst.force (a_keyword.to_string_32)
		end

	set_description (d: like description)
			-- (from CMS_RESPONSE)
		do
			description := d
		end

	set_main_content (s: like main_content)
			-- (from CMS_RESPONSE)
		do
			main_content := s
		end

	set_modification_date (dt: like modification_date)
			-- (from CMS_RESPONSE_I)
		do
			modification_date := dt
			if dt /= Void and publication_date = Void then
				publication_date := dt
			end
		end

	set_optional_content_type (a_content_type: detachable ANY)
			-- (from CMS_RESPONSE)
		do
			set_value (a_content_type, "optional_content_type")
		end

	set_page_title (t: like page_title)
			-- (from CMS_RESPONSE)
		do
			page_title := t
		end

	set_publication_date (dt: like publication_date)
			-- (from CMS_RESPONSE_I)
		do
			publication_date := dt
			if dt /= Void and modification_date = Void then
				modification_date := dt
			end
		end

	set_redirection (a_location: READABLE_STRING_8)
			-- Set redirection to a_location.
			-- (from CMS_RESPONSE_I)
		do
			redirection := a_location
		end

	set_redirection_delay (nb_secs: NATURAL_32)
			-- (from CMS_RESPONSE_I)
		do
			redirection_delay := nb_secs
		end

	set_title (t: like title)
			-- (from CMS_RESPONSE)
		do
			title := t
			set_page_title (t)
		end

	set_value (v: detachable ANY; k: READABLE_STRING_GENERAL)
			-- Set value v associated with name k.
			-- (from CMS_RESPONSE)
		do
			values.force (v, k)
		end

	unset_value (k: READABLE_STRING_GENERAL)
			-- Unset value associated with name k.
			-- (from CMS_RESPONSE)
		do
			values.remove (k)
		end
	
feature -- Encoder

	Html_encoder: HTML_ENCODER
			-- Shared HTML encoder.
			-- (from SHARED_HTML_ENCODER)
		once
			create Result
		end

	Percent_encoder: WSF_PERCENT_ENCODER
			-- Shared Percent encoding engine.
			-- (from SHARED_WSF_PERCENT_ENCODER)
		once
			create Result
		end
	
feature -- Encoders

	html_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
			-- a_string encoded for html output.
			-- (from CMS_ENCODERS)
		do
			Result := Html_encoder.general_encoded_string (a_string)
		end

	percent_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
			-- a_string encoded with percent encoding, mainly used for url.
			-- Was declared in CMS_ENCODERS as synonym of url_encoded.
			-- (from CMS_ENCODERS)
		do
			Result := Percent_encoder.percent_encoded_string (a_string)
		end

	safe_html_encoded (a_string: detachable READABLE_STRING_GENERAL): STRING_8
			-- a_string encoded for html output or empty string.
			-- (from CMS_ENCODERS)
		do
			if a_string /= Void then
				Result := html_encoded (a_string)
			else
				Result := ""
			end
		end

	url_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
			-- a_string encoded with percent encoding, mainly used for url.
			-- Was declared in CMS_ENCODERS as synonym of percent_encoded.
			-- (from CMS_ENCODERS)
		do
			Result := Percent_encoder.percent_encoded_string (a_string)
		end

	utf_8_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
			-- a_string encoded using UTF-8.
			-- (from CMS_ENCODERS)
		local
			utf: UTF_CONVERTER
		do
			Result := utf.utf_32_string_to_utf_8_string_8 (a_string)
		end
	
feature -- Execution

	execute
			-- (from CMS_RESPONSE)
		do
			begin
			process
			terminate
		end

	process
			-- Computed response message.
		do
			set_title ("Bad request".as_string_32)
			set_page_title ("Bad request".as_string_32)
			set_main_content ("<em>Bad request.</em>")
		end
	
feature {NONE} -- Execution		

	begin
			-- (from CMS_RESPONSE)
		do
		end

	on_terminated
			-- (from CMS_RESPONSE)
		do
		end

	frozen terminate
			-- (from CMS_RESPONSE)
		local
			cms_page: CMS_HTML_PAGE
			page: CMS_HTML_PAGE_RESPONSE
			h: HTTP_HEADER
			l_new_location: detachable READABLE_STRING_8
			l_redirection_delay: like redirection_delay
		do
			if attached redirection as l_location then
				if l_location.has_substring ("://") then
					l_new_location := l_location
				else
					l_new_location := location_absolute_url (l_location, Void)
				end
				l_redirection_delay := redirection_delay
				if l_redirection_delay > 0 then
					add_additional_head_line ("<meta http-equiv=%"refresh%" content=%"" + l_redirection_delay.out + ";url=" + l_new_location + "%" />", True)
				end
			end
			if attached {READABLE_STRING_GENERAL} optional_content_type as l_type then
				create cms_page.make_typed (api.utf_8_encoded (l_type))
			else
				create cms_page.make
			end
			prepare (cms_page)
			create page.make (theme.page_html (cms_page))
			page.set_status_code (status_code)
			h := page.header
			h.put_content_length (page.html.count)
			h.put_current_date
			if l_new_location /= Void and l_redirection_delay = 0 then
				response.redirect_now (l_new_location)
			else
				h.put_header_object (header)
				response.send (page)
			end
			on_terminated
		end
	
feature -- Generation

	common_prepare (page: CMS_HTML_PAGE)
			-- Common preparation for page page.
			-- (from CMS_RESPONSE)
		do
			debug ("refactor_fixme")
				fixme ("Fix generation common")
			end
			page.set_title (title)
			debug ("cms")
				if title = Void then
					page.set_title ({STRING_32}"CMS::" + request.path_info)
				end
			end
			across
				builtin_variables as ic
			loop
				page.register_variable (ic.item, ic.key)
			end
			page.register_variable (absolute_url ("", Void), "site_url")
			page.register_variable (base_path, "base_path")
			page.register_variable (absolute_url ("", Void), "host")
			page.register_variable (request.is_https, "is_https")
			if attached title as l_title then
				page.register_variable (l_title, "site_title")
			else
				page.register_variable (site_name, "site_title")
			end
			page.set_is_front (is_front)
			page.set_is_https (request.is_https)
			page.register_variable (is_administration_mode, "is_administration_mode")
			page.register_variable (api.theme_path, "theme_path")
			page.register_variable (horizontal_primary_menu_html, "primary_nav")
			page.register_variable (horizontal_primary_tabs_html, "primary_tabs")
			if attached page_title as l_page_title then
				page.register_variable (l_page_title, "page_title")
			end
		end

	custom_prepare (page: CMS_HTML_PAGE)
			-- Common preparation for page page that can be redefined by descendants.
		do
			set_status_code ({HTTP_STATUS_CODE}.bad_request)
			page.register_variable (absolute_url (request.percent_encoded_path_info.as_string_8, Void), "request")
			page.set_status_code (status_code)
			page.register_variable (status_code.out, "code")
		end

	get_local_link_active_status (a_lnk: CMS_LOCAL_LINK)
			-- Get a_lnk.is_active value according to request data.
			-- (from CMS_RESPONSE)
		local
			qs: STRING_8
			l_is_active: BOOLEAN
		do
			create qs.make_from_string (request.percent_encoded_path_info)
			if qs.starts_with ("/") then
				qs.remove_head (1)
			end
			l_is_active := qs.same_string (a_lnk.location)
			if not l_is_active then
				if attached request.query_string as l_query_string and then not l_query_string.is_empty then
					qs.append_character ('?')
					qs.append (l_query_string)
				end
				l_is_active := qs.same_string (a_lnk.location)
			end
			a_lnk.set_is_active (l_is_active)
			a_lnk.set_is_forbidden (not has_permission_on_link (a_lnk))
		end

	prepare (page: CMS_HTML_PAGE)
			-- (from CMS_RESPONSE)
		local
			lnk: CMS_LINK
			l_region: CMS_BLOCK_REGION
			l_menu_list_prepared: ARRAYED_LIST [CMS_LINK_COMPOSITE]
			l_empty_blocks: detachable ARRAYED_LIST [CMS_BLOCK]
			l_block_html: STRING_8
		do
			create {CMS_LOCAL_LINK} lnk.make ("Home", "")
			lnk.set_weight (-10)
			add_to_primary_menu (lnk)
			api.hooks.invoke_menu_system_alter (menu_system, Current)
			if api.enabled_modules.count <= 1 then
				lnk := api.administration_link ("Install", "install")
				if lnk.location.same_string (location) then
				else
					add_to_primary_menu (lnk)
				end
			end
			create l_menu_list_prepared.make (0)
			get_blocks
			across
				regions as reg_ic
			loop
				l_region := reg_ic.item
				across
					l_region.blocks as ic
				loop
					if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then
						l_menu_list_prepared.force (l_menu_block.menu)
						prepare_links (l_menu_block.menu)
						if l_menu_block.menu.is_empty then
							if l_empty_blocks = Void then
								create l_empty_blocks.make (1)
							end
							l_empty_blocks.force (l_menu_block)
						end
					end
				end
				if l_empty_blocks /= Void then
					across
						l_empty_blocks as ic
					loop
						l_region.remove (ic.item)
					end
					l_empty_blocks := Void
				end
			end
			across
				menu_system as ic
			loop
				if not l_menu_list_prepared.has (ic.item) then
					l_menu_list_prepared.force (ic.item)
					prepare_links (ic.item)
				end
			end
			l_menu_list_prepared.wipe_out
			across
				menu_system as ic
			loop
				ic.item.sort
			end
			common_prepare (page)
			custom_prepare (page)
			api.hooks.invoke_response_alter (Current)
			api.hooks.invoke_value_table_alter (values, Current)
			page.register_variable (page, "page")
			across
				values as ic
			loop
				page.register_variable (ic.item, ic.key)
			end
			across
				regions as reg_ic
			loop
				l_region := reg_ic.item
				across
					l_region.blocks as ic
				loop
					if attached {CMS_SMARTY_TEMPLATE_BLOCK} ic.item as l_tpl_block then
						across
							page.variables as var_ic
						loop
							if not l_tpl_block.values.has (var_ic.key) then
								l_tpl_block.set_value (var_ic.item, var_ic.key)
							end
						end
					end
					l_block_html := theme.block_html (ic.item)
					if attached {CMS_CACHE_BLOCK} ic.item then
						do_nothing
					elseif attached block_cache (ic.item.name) as l_block_cache_info then
						l_block_cache_info.cache_block.set_cache_content (l_block_html)
					end
					page.add_to_region (l_block_html, reg_ic.item.name)
				end
			end
			if attached additional_page_head_lines as l_head_lines then
				across
					l_head_lines as hl
				loop
					page.head_lines.force (hl.item.as_string_8)
				end
			end
		end

	prepare_links (a_comp: CMS_LINK_COMPOSITE)
			-- Update the active status recursively on a_comp.
			-- (from CMS_RESPONSE)
		local
			to_remove: ARRAYED_LIST [CMS_LINK]
			ln: CMS_LINK
			l_comp_link: detachable CMS_LOCAL_LINK
		do
			if attached {CMS_LOCAL_LINK} a_comp as lnk then
				l_comp_link := lnk
				get_local_link_active_status (lnk)
			end
			if attached a_comp.items as l_items then
				create to_remove.make (0)
				across
					l_items as ic
				loop
					ln := ic.item
					if attached {CMS_LOCAL_LINK} ln as l_local then
						get_local_link_active_status (l_local)
					end
					if ln.is_forbidden then
						to_remove.force (ln)
					else
						if (ln.is_expanded or ln.is_collapsed) and then attached {CMS_LINK_COMPOSITE} ln as l_comp then
							prepare_links (l_comp)
						end
						if l_comp_link /= Void then
							if ln.is_expanded or (not ln.is_expandable and ln.is_active) then
								l_comp_link.set_expanded (True)
							end
						end
					end
				end
				across
					to_remove as ic
				loop
					a_comp.remove (ic.item)
				end
			end
			if l_comp_link /= Void and then l_comp_link.is_active then
				l_comp_link.set_expanded (True)
			end
		end
	
feature -- Head customization

	add_additional_head_line (s: READABLE_STRING_8; a_allow_duplication: BOOLEAN)
			-- (from CMS_RESPONSE)
		local
			lst: like additional_page_head_lines
		do
			lst := additional_page_head_lines
			if lst = Void then
				create {ARRAYED_LIST [like additional_page_head_lines.item]} lst.make (1)
				additional_page_head_lines := lst
			end
			if a_allow_duplication or else across
				lst as c
			all
				not c.item.same_string (s)
			end then
				lst.extend (s)
			end
		end

	add_javascript_content (a_script: STRING_8)
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			create s.make_from_string ("<script type=%"text/javascript%">%N")
			s.append (a_script)
			s.append ("%N</script>")
			add_additional_head_line (s, True)
		end

	add_javascript_url (a_src: STRING_8)
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			create s.make_from_string ("<script type=%"text/javascript%" src=%"")
			s.append (a_src)
			s.append ("%"></script>")
			add_additional_head_line (s, False)
		end

	add_style (a_href: STRING_8; a_media: detachable STRING_8)
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			create s.make_from_string ("<link rel=%"stylesheet%" href=%"")
			s.append (a_href)
			s.append ("%" type=%"text/css%"")
			if a_media /= Void then
				s.append (" media=%"" + a_media + "%"")
			end
			s.append ("/>")
			add_additional_head_line (s, False)
		end

	add_style_content (a_style_content: STRING_8)
			-- Add style content a_style_content in the head, using <style> tag.
			-- (from CMS_RESPONSE)
		local
			s: STRING_8
		do
			create s.make_from_string ("<style>%N")
			s.append (a_style_content)
			s.append ("%N</style>")
			add_additional_head_line (s, True)
		end
	
feature -- Helper conversions to and from string		

	date_time_from_string (s: READABLE_STRING_GENERAL): detachable DATE_TIME
			-- Date time from string s, if valid.
			-- (from CMS_ENCODERS)
		local
			hd: HTTP_DATE
		do
			create hd.make_from_string (s)
			check
					not hd.has_error
			end
			if not hd.has_error then
				Result := hd.date_time
			end
		end

	date_time_to_string (dt: DATE_TIME): STRING_8
			-- Date time dt converted to standard output (using RFC1123)
			-- (from CMS_ENCODERS)
		local
			hd: HTTP_DATE
		do
			create hd.make_from_date_time (dt)
			Result := hd.rfc1123_string
		end

	list_from_csv_string (a_csv: READABLE_STRING_32): LIST [READABLE_STRING_32]
			-- List of Comma-separated-value string items.
			-- (from CMS_ENCODERS)
		local
			i, j, n: INTEGER_32
			s: STRING_32
		do
			from
				i := 1
				n := a_csv.count
				create s.make_empty
				create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (1)
				Result.force (s)
			until
				i > n
			loop
				inspect a_csv [i]
				when ',' then
					create s.make_empty
					Result.force (s)
				when '"' then
					j := a_csv.index_of ('"'.to_character_32, i + 1)
					if j > 0 then
						s.append (a_csv.substring (i + 1, j - 1))
						i := j
					else
						s.extend (a_csv [i])
					end
				else
					s.extend (a_csv [i])
				end
				i := i + 1
			end
		end

	list_to_csv_string (a_strings: ITERABLE [READABLE_STRING_GENERAL]): STRING_32
			-- a_strings as comma separated value string.
			-- (from CMS_ENCODERS)
		local
			s: READABLE_STRING_GENERAL
		do
			create Result.make (0)
			across
				a_strings as ic
			loop
				s := ic.item
				if not Result.is_empty then
					Result.append_character (','.to_character_32)
				end
				if s.has (','.to_character_32) then
					Result.append_character ('"'.to_character_32)
					Result.append_string_general (s)
					Result.append_character ('"'.to_character_32)
				else
					Result.append_string_general (s)
				end
			end
		end
	
feature -- Helpers / security vulnerabilities

	secure_text (a_text: STRING_GENERAL)
			-- Clean a_text from XSS vulnerabilities.
			-- (from CMS_ENCODERS)
		do
			(create {SECURITY_HTML_CONTENT_FILTER}).filter (a_text)
		end

	secured_html_content (a_html_content: READABLE_STRING_8): STRING_8
			-- a_html_content cleaned from XSS vulnerabilities.
			-- (from CMS_ENCODERS)
		do
			create Result.make_from_string (a_html_content)
			secure_text (Result)
		end

	secured_url_content (a_url_content: READABLE_STRING_8): STRING_8
			-- a_url_content cleaned from XSS vulnerabilities.
			-- (from CMS_ENCODERS)
		do
			create Result.make_from_string (a_url_content)
			secure_text (Result)
		end
	
feature -- Helpers: URLs	

	location_absolute_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- Absolute URL for a_location.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.location_absolute_url (a_location, opts)
		end

	location_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- URL for a_location.
			-- (from CMS_RESPONSE_I)
		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.
			-- (from CMS_RESPONSE_I)
		require -- from CMS_RESPONSE_I
			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
			-- (from CMS_RESPONSE_I)
		require -- from CMS_RESPONSE_I
			u_with_id: u.has_id
		do
			Result := api.user_url (u)
		end
	
feature -- Helpers: cms link

	administration_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
			-- (from CMS_RESPONSE_I)
		require -- from CMS_RESPONSE_I
			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
			-- (from CMS_RESPONSE_I)
		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
			-- (from CMS_RESPONSE_I)
		do
			Result := api.user_local_link (u, a_opt_title)
		end

	webapi_link (a_title: READABLE_STRING_GENERAL; a_relative_location: detachable READABLE_STRING_8): CMS_LOCAL_LINK
			-- (from CMS_RESPONSE_I)
		require -- from CMS_RESPONSE_I
			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
	
feature -- Helpers: html links

	user_display_name (u: CMS_USER): READABLE_STRING_32
			-- Was declared in CMS_RESPONSE_I as synonym of user_profile_name.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.user_display_name (u)
		end

	user_html_link (u: CMS_USER): STRING_8
			-- (from CMS_RESPONSE)
		require -- from CMS_RESPONSE
			u_with_name: not u.name.is_whitespace
		do
			Result := api.user_html_link (u)
		end

	user_profile_name (u: CMS_USER): READABLE_STRING_32
			-- Was declared in CMS_RESPONSE_I as synonym of user_display_name.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.user_display_name (u)
		end
	
feature -- Hooks

	hooks: CMS_HOOK_CORE_MANAGER
		obsolete "Use api.hooks [2017-05-31]"
			-- Manager handling hook subscriptions.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.hooks
		end
	
feature -- Internationalization (i18n)

	formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32
			-- Format a_text using arguments args.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.formatted_string (a_text, args)
		end

	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.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.translation (a_text, opts)
		end
	
feature -- Link

	append_link_to_html (a_text: detachable READABLE_STRING_GENERAL; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS; a_html: STRING_8)
			-- (from CMS_URL_UTILITIES)
		local
			l_html: BOOLEAN
			t: READABLE_STRING_GENERAL
		do
			l_html := True
			if opts /= Void then
				l_html := opts.boolean_item ("html", l_html)
			end
			a_html.append ("<a href=%"" + checked_url (url (a_path, opts)) + "%">")
			if a_text = Void then
				t := a_path
			else
				t := a_text
			end
			if l_html then
				a_html.append (html_encoded (t))
			else
				a_html.append (checked_plain (t))
			end
			a_html.append ("</a>")
		end

	append_link_with_raw_text_to_html (a_raw_text: detachable READABLE_STRING_8; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS; a_html: STRING_8)
			-- (from CMS_URL_UTILITIES)
		local
			l_html: BOOLEAN
			t: READABLE_STRING_8
		do
			l_html := True
			if opts /= Void then
				l_html := opts.boolean_item ("html", l_html)
			end
			a_html.append ("<a href=%"" + checked_url (url (a_path, opts)) + "%">")
			if a_raw_text = Void then
				t := a_path
			else
				t := a_raw_text
			end
			a_html.append (t)
			a_html.append ("</a>")
		end

	link (a_text: detachable READABLE_STRING_GENERAL; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- HTML link with title a_text and href a_path.
			-- opts is used for additional settings.
			-- (from CMS_URL_UTILITIES)
		do
			create Result.make (32)
			append_link_to_html (a_text, a_path, opts, Result)
		end

	link_with_raw_text (a_raw_text: detachable READABLE_STRING_8; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- HTML link with title the html code a_raw_text and href a_path.
			-- opts is used for additional settings.	
			-- (from CMS_URL_UTILITIES)
		do
			create Result.make (32)
			append_link_with_raw_text_to_html (a_raw_text, a_path, opts, Result)
		end
	
feature -- Markers

	fixme (comment: READABLE_STRING_8)
			-- Mark code that has to be "fixed" with comment.
			-- (from REFACTORING_HELPER)
		require -- from REFACTORING_HELPER
			comment_not_void: comment /= Void
		do
			debug ("refactor_fixme")
				Io.Error.put_string ("FIXME: ")
				Io.Error.put_string (comment)
				Io.Error.put_new_line
			end
		ensure -- from REFACTORING_HELPER
			instance_free: class
		end

	to_implement (comment: READABLE_STRING_8)
			-- Mark code that has to be "implemented" with comment.
			-- (from REFACTORING_HELPER)
		require -- from REFACTORING_HELPER
			comment_not_void: comment /= Void
		do
			debug ("refactor_fixme")
				Io.Error.put_string ("TO_BE_IMPLEMENTED: ")
				Io.Error.put_string (comment)
				Io.Error.put_new_line
			end
		ensure -- from REFACTORING_HELPER
			instance_free: class
		end

	to_implement_assertion (comment: READABLE_STRING_8): BOOLEAN
			-- Mark assertion that has to be "implemented" with comment.
			-- (from REFACTORING_HELPER)
		require -- from REFACTORING_HELPER
			comment_not_void: comment /= Void
		do
			debug ("refactor_fixme")
				Io.Error.put_string ("ASSERTION_TO_BE_IMPLEMENTED: ")
				Io.Error.put_string (comment)
				Io.Error.put_new_line
			end
			Result := True
		ensure -- from REFACTORING_HELPER
			instance_free: class
		end
	
feature -- Menu

	main_menu: CMS_MENU
		obsolete "Use `primary_menu' [2017-05-31]"
			-- (from CMS_RESPONSE)
		do
			Result := primary_menu
		end

	management_menu: CMS_MENU
			-- (from CMS_RESPONSE)
		do
			Result := menu_system.management_menu
		end

	menu_system: CMS_MENU_SYSTEM
			-- (from CMS_RESPONSE)

	navigation_menu: CMS_MENU
			-- (from CMS_RESPONSE)
		do
			Result := menu_system.navigation_menu
		end

	primary_menu: CMS_MENU
			-- (from CMS_RESPONSE)
		do
			Result := menu_system.primary_menu
		end

	primary_tabs: CMS_MENU
			-- (from CMS_RESPONSE)
		do
			Result := menu_system.primary_tabs
		end

	user_menu: CMS_MENU
			-- (from CMS_RESPONSE)
		do
			Result := menu_system.user_menu
		end
	
feature -- Menu: change

	add_to_main_menu (lnk: CMS_LINK)
		obsolete "use add_to_primary_menu [2017-05-31]"
			-- (from CMS_RESPONSE)
		do
			add_to_primary_menu (lnk)
		end

	add_to_menu (lnk: CMS_LINK; m: CMS_MENU)
			-- (from CMS_RESPONSE)
		do
			m.extend (lnk)
		end

	add_to_primary_menu (lnk: CMS_LINK)
			-- (from CMS_RESPONSE)
		do
			add_to_menu (lnk, primary_menu)
		end

	add_to_primary_tabs (lnk: CMS_LINK)
			-- (from CMS_RESPONSE)
		do
			add_to_menu (lnk, primary_tabs)
		end
	
feature -- Message

	add_debug_message (a_msg: READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		do
			if api.is_debug then
				add_message (a_msg, "debug")
			end
		end

	add_error_message (a_msg: READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		do
			add_message (a_msg, "error")
		end

	add_message (a_msg: READABLE_STRING_8; a_category: detachable READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		local
			m: like message
		do
			m := message
			if m = Void then
				create m.make (a_msg.count + 9)
				message := m
			end
			if a_category /= Void then
				m.append ("<li class=%"" + a_category + "%">")
			else
				m.append ("<li>")
			end
			m.append (a_msg + "</li>")
		end

	add_notice_message (a_msg: READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		do
			add_message (a_msg, "notice")
		end

	add_success_message (a_msg: READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		do
			add_message (a_msg, "success")
		end

	add_warning_message (a_msg: READABLE_STRING_8)
			-- (from CMS_RESPONSE)
		do
			add_message (a_msg, "warning")
		end

	message: detachable STRING_8
			-- (from CMS_RESPONSE)

	report_form_errors (fd: WSF_FORM_DATA)
			-- (from CMS_RESPONSE)
		require -- from CMS_RESPONSE
			has_error: not fd.is_valid
		do
			if attached fd.errors as errs then
				across
					errs as err
				loop
					if attached err.item as e then
						if attached e.field as l_field then
							if attached e.message as e_msg then
								add_error_message (e_msg)
							else
								add_error_message ("Field [" + l_field.name + "] is invalid.")
							end
						elseif attached e.message as e_msg then
							add_error_message (e_msg)
						end
					end
				end
			end
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		do
			Result := tagged_out
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		do
			if o /= Void then
				Io.put_string (o.out)
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Permission

	has_permission (a_permission: READABLE_STRING_GENERAL): BOOLEAN
			-- Does current user has permission a_permission ?
			-- (from CMS_RESPONSE_I)
		do
			Result := user_has_permission (user, a_permission)
		end

	has_permission_on_link (a_link: CMS_LINK): BOOLEAN
			-- Does current user has permission to access link a_link?
			-- (from CMS_RESPONSE_I)
		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_permissions (a_permission_list: ITERABLE [READABLE_STRING_GENERAL]): BOOLEAN
			-- Does current user has any of the permissions a_permission_list ?
			-- (from CMS_RESPONSE_I)
		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 ?
			-- (from CMS_RESPONSE_I)
		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 ?
			-- (from CMS_RESPONSE_I)
		do
			Result := api.user_has_permissions (a_user, a_permission_list)
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature -- Response builtin variables

	builtin_variables: STRING_TABLE [detachable ANY]
			-- builtin variables value indexed by name.
			-- (from CMS_RESPONSE)
		do
			Result := api.builtin_variables
			Result ["site_url"] := site_url
			Result ["host"] := site_url
			Result ["is_https"] := request.is_https
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_8
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ")
				create l_exc
				l_msg.append (generating_type.name)
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
feature -- Settings

	is_administration_mode: BOOLEAN
			-- Is administration mode?
			-- (from CMS_RESPONSE_I)
		do
			Result := api.is_administration_mode
		end

	is_site_mode: BOOLEAN
			-- Is site mode?
			-- (from CMS_RESPONSE_I)
		do
			Result := api.is_site_mode
		end

	is_webapi_mode: BOOLEAN
			-- Is Web API mode?
			-- (from CMS_RESPONSE_I)
		do
			Result := api.is_webapi_mode
		end
	
feature -- Specific values

	optional_content_type: detachable ANY
			-- (from CMS_RESPONSE)
		do
			Result := values.item ("optional_content_type")
		end
	
feature -- Theme

	get_theme
			-- (from CMS_RESPONSE)
		local
			l_info: CMS_THEME_INFORMATION
		do
			if attached setup.theme_information_location as fn then
				create l_info.make (fn)
			else
				create l_info.make_default
			end
			if l_info.engine.is_case_insensitive_equal_general ("smarty") then
				create {SMARTY_CMS_THEME} theme.make (api, l_info, site_url)
			else
				create {MISSING_CMS_THEME} theme.make (api, l_info, site_url)
				status_code := {HTTP_STATUS_CODE}.service_unavailable
				to_implement ("Check how to add the Retry-after, http://tools.ietf.org/html/rfc7231#section-6.6.4 and http://tools.ietf.org/html/rfc7231#section-7.1.3")
			end
		end

	theme: CMS_THEME
			-- Current theme
			-- (from CMS_RESPONSE)
	
feature -- Theme helpers

	wsf_theme: WSF_THEME
			-- WSF Theme from CMS theme for Current response.
			-- (from CMS_RESPONSE)
		local
			t: like internal_wsf_theme
		do
			t := internal_wsf_theme
			if t = Void then
				create {CMS_TO_WSF_THEME} t.make (Current, theme)
				internal_wsf_theme := t
			end
			Result := t
		end
	
feature {NONE} -- Theme helpers		

	internal_wsf_theme: detachable WSF_THEME
			-- Once per object for wsf_theme.
			-- (from CMS_RESPONSE)
	
feature -- URL utilities

	base_path: IMMUTABLE_STRING_8
			-- Base path, default to "/".
			-- Always ends with '/'
			-- Could be /project/demo/
			-- (from CMS_RESPONSE_I)

	base_url: detachable IMMUTABLE_STRING_8
			-- Base url if any.
			-- (from CMS_RESPONSE_I)

	is_front: BOOLEAN
			-- Is current response related to "front" page?
			-- (from CMS_RESPONSE_I)
		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 '/'
			-- (from CMS_RESPONSE_I)
	
feature -- Url

	absolute_url (a_path: STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- (from CMS_URL_UTILITIES)
		local
			l_opts: detachable CMS_API_OPTIONS
		do
			l_opts := opts
			if l_opts = Void then
				create l_opts.make (1)
			end
			l_opts.force (True, "absolute")
			Result := url (a_path, l_opts)
		end

	checked_plain (a_text: READABLE_STRING_GENERAL): STRING_8
			-- (from CMS_URL_UTILITIES)
		do
			Result := html_encoded (a_text)
		end

	checked_url (a_url: READABLE_STRING_8): READABLE_STRING_8
			-- (from CMS_URL_UTILITIES)
		do
			Result := a_url
		end

	url (a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING_8
			-- URL for path a_path and optional parameters from opts.
			-- (from CMS_URL_UTILITIES)
		local
			q, f: detachable STRING_8
			l_abs: BOOLEAN
		do
			l_abs := False
			if opts /= Void then
				l_abs := opts.boolean_item ("absolute", l_abs)
				if attached opts.item ("query") as l_query then
					if attached {READABLE_STRING_8} l_query as s_value then
						q := s_value.as_string_8
					elseif attached {ITERABLE [TUPLE [key: READABLE_STRING_GENERAL; value: READABLE_STRING_GENERAL]]} l_query as lst then
						create q.make_empty
						across
							lst as c
						loop
							if q.is_empty then
							else
								q.append_character ('&')
							end
							q.append (url_encoded (c.item.key))
							q.append_character ('=')
							q.append (url_encoded (c.item.value))
						end
					end
				end
				if attached opts.string_item ("fragment") as s_frag then
					f := s_frag.as_string_8
				end
			end
			if l_abs then
				if a_path.substring_index ("://", 1) = 0 then
					create Result.make_from_string (site_url)
					if a_path.is_empty then
					elseif Result.ends_with ("/") then
						if a_path [1] = '/' then
							Result.append_string (a_path.substring (2, a_path.count))
						else
							Result.append_string (a_path)
						end
					else
						if a_path [1] = '/' then
							Result.append_string (a_path)
						else
							Result.append_character ('/')
							Result.append_string (a_path)
						end
					end
				else
					Result := a_path.as_string_8
				end
			else
				Result := based_path (a_path.as_string_8)
			end
			if q /= Void then
				Result.append ("?" + q)
			end
			if f /= Void then
				Result.append ("#" + f)
			end
		end
	
feature -- User access

	is_authenticated: BOOLEAN
			-- Is user authenticated?
			-- (from CMS_RESPONSE_I)
		do
			Result := user /= Void
		end

	set_user (u: CMS_USER)
			-- Set active user to u.
			-- (from CMS_RESPONSE_I)
		require -- from CMS_RESPONSE_I
			attached_u: u /= Void
		do
			api.set_user (u)
		end

	unset_user
			-- Unset active user.
			-- (from CMS_RESPONSE_I)
		do
			api.unset_user
		end

	user: detachable CMS_USER
			-- Active user if authenticated.
			-- (from CMS_RESPONSE_I)
		do
			Result := api.user
		end
	
invariant
		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

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 -- class BAD_REQUEST_ERROR_CMS_RESPONSE

Generated by ISE EiffelStudio