diff --git a/examples/demo/site/config/modules/feed_aggregator/feeds.json b/examples/demo/site/config/modules/feed_aggregator/feeds.json index 31d1230..b1fb13e 100644 --- a/examples/demo/site/config/modules/feed_aggregator/feeds.json +++ b/examples/demo/site/config/modules/feed_aggregator/feeds.json @@ -1,25 +1,28 @@ { - "ids": ["eiffel", "forum", "stackoverflow"], + "ids": ["eiffel", "forum"], "feeds": { "eiffel": { "title": "Eiffel related posts.", "expiration": "21600", + "size": 10, "locations": [ "https://bertrandmeyer.com/feed/", - "https://room.eiffel.com/blog/feed" + "https://room.eiffel.com/blog/feed", + "https://room.eiffel.com/article/feed", + "https://room.eiffel.com/library/feed" ] , "categories": ["Eiffel"] - ,"option_description": "disabled" + ,"option_description": "enabled" }, "forum": { - "title": "Eiffel Users Group", - "expiration": "43200", - "location": "https://groups.google.com/forum/feed/eiffel-users/msgs/atom.xml?num=15" - }, - "stackoverflow": { - "title": "Test", - "expiration": "3600", - "location": "http://stackoverflow.com/feeds/tag?tagnames=eiffel&sort=newest" + "title": "Eiffel Forum", + "expiration": "21600", + "size": 10, + "locations": [ + "https://groups.google.com/forum/feed/eiffel-users/msgs/atom.xml?num=15". + "http://stackoverflow.com/feeds/tag?tagnames=eiffel&sort=newest" + ] + ,"option_description": "enabled" } } } diff --git a/modules/feed_aggregator/feed_aggregation.e b/modules/feed_aggregator/feed_aggregation.e index debbad5..cabecfc 100644 --- a/modules/feed_aggregator/feed_aggregation.e +++ b/modules/feed_aggregator/feed_aggregation.e @@ -17,6 +17,7 @@ feature {NONE} -- Initialization create {ARRAYED_LIST [READABLE_STRING_8]} locations.make (0) expiration := 60*60 description_enabled := True + size := 10 end feature -- Access @@ -28,6 +29,9 @@ feature -- Access -- Suggested expiration time in seconds (default: 1 hour). -- If negative then never expires. + size: INTEGER + -- Number of entries to display per page. + description: detachable IMMUTABLE_STRING_32 -- Optional description. @@ -58,6 +62,12 @@ feature -- Element change expiration := nb_seconds end + set_size (nb: INTEGER) + -- Set `size' to `nb'. + do + size := nb + end + set_description_enabled (b: BOOLEAN) -- Set `description_enabled' to `b'. do diff --git a/modules/feed_aggregator/feed_aggregator_api.e b/modules/feed_aggregator/feed_aggregator_api.e index a3a97d9..0bd870d 100644 --- a/modules/feed_aggregator/feed_aggregator_api.e +++ b/modules/feed_aggregator/feed_aggregator_api.e @@ -56,6 +56,11 @@ feature -- Access agg.set_expiration (l_expiration.to_integer) end end + if attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".size") as l_size then + if l_size.is_integer then + agg.set_size (l_size.to_integer) + end + end if attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".option_description") as l_description_opt then agg.set_description_enabled (not l_description_opt.is_case_insensitive_equal_general ("disabled")) end diff --git a/modules/feed_aggregator/feed_aggregator_module.e b/modules/feed_aggregator/feed_aggregator_module.e index 2bcdce4..9c1d7ef 100644 --- a/modules/feed_aggregator/feed_aggregator_module.e +++ b/modules/feed_aggregator/feed_aggregator_module.e @@ -219,7 +219,7 @@ feature -- Hook else s := a_block_id end - if attached feed_to_html (s, 5, True, a_response) as l_content then + if attached feed_to_html (s, 0, True, a_response) as l_content then create b.make (a_block_id, Void, l_content, Void) b.set_is_raw (True) a_response.add_block (b, "feed_" + s) @@ -241,130 +241,53 @@ feature -- Hook e: FEED_ITEM l_cache: CMS_FILE_STRING_8_CACHE lnk: detachable FEED_LINK - l_today: DATE + vis: FEED_TO_XHTML_VISITOR + s: STRING do if attached feed_aggregator_api as l_feed_api then if attached l_feed_api.aggregation (a_feed_id) as l_agg then create l_cache.make (a_response.api.files_location.extended (".cache").extended (name).extended ("feed__" + a_feed_id + "__" + a_count.out + "_" + with_feed_info.out)) Result := l_cache.item if Result = Void or l_cache.expired (Void, l_agg.expiration) then - create l_today.make_now_utc - create Result.make_from_string ("
") + + create Result.make (1024) Result.append ("") - if with_feed_info then - if attached l_agg.description as l_desc then - Result.append ("
") - Result.append_string_general (l_desc) - Result.append ("
") - end - end - Result.append ("") + if with_feed_info then + create s.make_empty + if attached l_agg.description as l_desc then + s.append ("
") + s.append_string_general (l_desc) + s.append ("
") + end + vis.set_header (s) + end + create s.make_empty + s.append_string ("") + s.append_string (a_response.link ("See more ...", "feed_aggregation/" + a_response.url_encoded (a_feed_id), Void)) + s.append_string ("") + vis.set_footer (s) - - Result.append ("
%N") + if attached l_feed_api.aggregation_feed (l_agg) as l_feed then + l_feed.accept (vis) + end l_cache.put (Result) end end end end - append_date_time_to (dt: DATE_TIME; a_today: DATE; a_output: STRING_GENERAL) - do - if dt.year /= a_today.year then - a_output.append (dt.year.out) - a_output.append (",") - end - a_output.append (" ") - append_month_mmm_to (dt.month, a_output) - a_output.append (" ") - if dt.day < 10 then - a_output.append ("0") - end - a_output.append (dt.day.out) - end - - append_month_mmm_to (m: INTEGER; s: STRING_GENERAL) - require - 1 <= m and m <= 12 - do - inspect m - when 1 then s.append ("Jan") - when 2 then s.append ("Feb") - when 3 then s.append ("Mar") - when 4 then s.append ("Apr") - when 5 then s.append ("May") - when 6 then s.append ("Jun") - when 7 then s.append ("Jul") - when 8 then s.append ("Aug") - when 9 then s.append ("Sep") - when 10 then s.append ("Oct") - when 11 then s.append ("Nov") - when 12 then s.append ("Dec") - else - -- Error - end - end - feature -- Hook response_alter (a_response: CMS_RESPONSE) diff --git a/modules/feed_aggregator/site/files/css/feed_aggregator.css b/modules/feed_aggregator/site/files/css/feed_aggregator.css index abccac2..d0e322e 100644 --- a/modules/feed_aggregator/site/files/css/feed_aggregator.css +++ b/modules/feed_aggregator/site/files/css/feed_aggregator.css @@ -1,11 +1,14 @@ div.feed ul { list-style: none; position: relative; + padding: 0; + margin: 0; width: 99%; } div.feed li { /* border-top: solid 1px #ddd; */ - margin-bottom: 5px; + padding: 0; + margin: 0 0 5px 0; } div.feed li a { font-weight: bold; @@ -32,6 +35,7 @@ div.feed li:hover { margin-bottom: 23px; } div.feed li:hover .description { + padding: 5px; position: absolute; height: auto; overflow-y: scroll; diff --git a/modules/feed_aggregator/site/files/scss/feed_aggregator.scss b/modules/feed_aggregator/site/files/scss/feed_aggregator.scss index 90b855f..82c2b81 100644 --- a/modules/feed_aggregator/site/files/scss/feed_aggregator.scss +++ b/modules/feed_aggregator/site/files/scss/feed_aggregator.scss @@ -2,11 +2,14 @@ div.feed { ul { list-style: none; position: relative; + padding: 0; + margin: 0; width: 99%; } li { /* border-top: solid 1px #ddd; */ - margin-bottom: 5px; + padding: 0; + margin: 0 0 5px 0; a { font-weight: bold; @@ -32,6 +35,7 @@ div.feed { &:hover { margin-bottom: 23px; .description { + padding: 5px; position: absolute; height: auto; overflow-y: scroll; diff --git a/modules/recent_changes/cms_recent_changes_module.e b/modules/recent_changes/cms_recent_changes_module.e index 99a78a4..bf26f46 100644 --- a/modules/recent_changes/cms_recent_changes_module.e +++ b/modules/recent_changes/cms_recent_changes_module.e @@ -1,5 +1,5 @@ note - description: "CMS module that bring support for recent changes." + description: "CMS module that brings support for recent changes." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" @@ -19,6 +19,8 @@ inherit CMS_HOOK_RESPONSE_ALTER + CMS_HOOK_BLOCK + create make @@ -49,10 +51,132 @@ feature -- Access: router -- do a_router.handle ("/recent_changes/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_recent_changes (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/recent_changes/feed", create {WSF_URI_AGENT_HANDLER}.make (agent handle_recent_changes_feed (a_api, ?, ?)), a_router.methods_head_get) + end + +feature -- Hook + + block_list: ITERABLE [like {CMS_BLOCK}.name] + -- List of block names, managed by current object. + -- If prefixed by "?", condition will be check + -- to determine if it should be displayed (and computed) or not. + do + Result := <<"?recent_changes">> + end + + get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + -- Get block object identified by `a_block_id' and associate with `a_response'. + local + b: CMS_CONTENT_BLOCK + s, l_content: STRING + gen: FEED_TO_XHTML_VISITOR + do + if a_block_id.same_string_general ("recent_changes") then + create l_content.make (1024) + create gen.make (l_content) + + create s.make_empty + s.append_string ("") + s.append_string (a_response.link ("See more ...", "recent_changes/", Void)) + s.append_string ("") + gen.set_footer (s) + + recent_changes_feed (a_response, 10, Void).accept (gen) + + create b.make (a_block_id, Void, l_content, Void) + a_response.put_block (b, Void, False) + end + end + + recent_changes_feed (a_response: CMS_RESPONSE; a_size: NATURAL_32; a_source: detachable READABLE_STRING_8): FEED + local + l_changes: CMS_RECENT_CHANGE_CONTAINER + ch: CMS_RECENT_CHANGE_ITEM + l_user: detachable CMS_USER + l_feed: FEED + l_feed_item: FEED_ITEM + lnk: FEED_LINK + nb: NATURAL_32 + do + l_user := Void -- Public access for the feed! + create l_changes.make (a_size, create {DATE_TIME}.make_now_utc, a_source) + if attached a_response.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then + across + lst as ic + loop + if attached {CMS_RECENT_CHANGES_HOOK} ic.item as h then + if attached h.recent_changes_sources as h_sources then + if + a_source = Void + or else across h_sources as h_ic some h_ic.item.is_case_insensitive_equal (a_source) end + then + h.populate_recent_changes (l_changes, l_user) + end + end + end + end + end + create l_feed.make ("CMS Recent changes") + l_feed.set_date (create {DATE_TIME}.make_now_utc) + nb := a_size + across + l_changes as ic + until + nb = 0 + loop + ch := ic.item + create l_feed_item.make (ch.link.title) + l_feed_item.set_date (ch.date) + l_feed_item.set_description (ch.information) + l_feed_item.set_category (ch.source) + create lnk.make (a_response.absolute_url (ch.link.location, Void)) + l_feed_item.links.force (lnk, "") + l_feed.extend (l_feed_item) + nb := nb - 1 + end + l_feed.sort + Result := l_feed end feature -- Handler + handle_recent_changes_feed (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + htdate: HTTP_DATE + l_content: STRING + l_until_date: detachable DATE_TIME + l_until_date_timestamp: INTEGER_64 + l_filter_source: detachable READABLE_STRING_8 + l_size: NATURAL_32 + mesg: CMS_CUSTOM_RESPONSE_MESSAGE + do + if attached {WSF_STRING} req.query_parameter ("date") as p_until_date then + l_until_date_timestamp := p_until_date.value.to_integer_64 + create htdate.make_from_timestamp (l_until_date_timestamp) + l_until_date := htdate.date_time + end + if attached {WSF_STRING} req.query_parameter ("source") as p_filter then + l_filter_source := p_filter.url_encoded_value + if l_filter_source.is_empty then + l_filter_source := Void + end + end + if attached {WSF_STRING} req.query_parameter ("size") as p_size then + l_size := p_size.integer_value.to_natural_32 + end + if l_size = 0 then + l_size := 25 + end + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create l_content.make (1024) + recent_changes_feed (r, l_size, l_filter_source).accept (create {ATOM_FEED_GENERATOR}.make (l_content)) + create mesg.make ({HTTP_STATUS_CODE}.ok) + mesg.set_payload (l_content) + res.send (mesg) + end + handle_recent_changes (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE @@ -146,7 +270,7 @@ feature -- Handler create l_date_field.make_with_text ("date", l_until_date_timestamp.out) l_form.extend (l_date_field) end - + create l_submit.make_with_text ("op", "Filter") l_form.extend (l_submit) l_form.extend_html_text ("
") @@ -243,7 +367,7 @@ feature -- Handler end l_content.append (">))) - l_content.append ("%">More ...") + l_content.append ("%">See more ...") end end @@ -268,6 +392,7 @@ feature -- Hooks configuration do a_response.hooks.subscribe_to_menu_system_alter_hook (Current) a_response.hooks.subscribe_to_response_alter_hook (Current) + a_response.hooks.subscribe_to_block_hook (Current) end feature -- Hook diff --git a/modules/recent_changes/recent_changes-safe.ecf b/modules/recent_changes/recent_changes-safe.ecf index a2f8acd..0e341f5 100644 --- a/modules/recent_changes/recent_changes-safe.ecf +++ b/modules/recent_changes/recent_changes-safe.ecf @@ -16,6 +16,7 @@ + diff --git a/src/hooks/cms_hook_core_manager.e b/src/hooks/cms_hook_core_manager.e index 2772a25..18f9f03 100644 --- a/src/hooks/cms_hook_core_manager.e +++ b/src/hooks/cms_hook_core_manager.e @@ -141,6 +141,8 @@ feature -- Hook: block local bl: READABLE_STRING_8 bl_optional: BOOLEAN + l_ok: BOOLEAN + l_block_cache: detachable TUPLE [block: CMS_CACHE_BLOCK; region: READABLE_STRING_8; expired: BOOLEAN] do if attached subscribers ({CMS_HOOK_BLOCK}) as lst then across @@ -150,15 +152,24 @@ feature -- Hook: block across h.block_list as blst loop + l_ok := False bl := blst.item bl_optional := bl.count > 0 and bl[1] = '?' if bl_optional then bl := bl.substring (2, bl.count) if a_response.is_block_included (bl, False) then - h.get_block_view (bl, a_response) + l_ok := True end else - h.get_block_view (bl, a_response) + l_ok := True + end + if l_ok then + l_block_cache := a_response.block_cache (bl) + if l_block_cache /= Void and then not l_block_cache.expired then + a_response.add_block (l_block_cache.block, l_block_cache.region) + else + h.get_block_view (bl, a_response) + end end end end diff --git a/src/kernel/content/cms_cache_block.e b/src/kernel/content/cms_cache_block.e new file mode 100644 index 0000000..29e885c --- /dev/null +++ b/src/kernel/content/cms_cache_block.e @@ -0,0 +1,99 @@ +note + description: "[ + CMS_BLOCK implemented with a `cache' + as caching solution. + ]" + date: "$Date: 2014-11-18 10:13:13 +0100 (mar., 18 nov. 2014) $" + revision: "$Revision: 96110 $" + +class + CMS_CACHE_BLOCK + +inherit + CMS_BLOCK + +create + make + +feature {NONE} -- Initialization + + make (a_name: like name; a_cache: like cache) + require + a_name_not_blank: not a_name.is_whitespace + do + is_enabled := True + name := a_name + cache := a_cache + set_is_raw (True) + end + +feature -- Access + + name: READABLE_STRING_8 + -- + + title: detachable READABLE_STRING_32 + -- + + cache: CMS_CACHE [READABLE_STRING_8] + -- Cache content. + +feature -- Status report + + is_empty: BOOLEAN + -- Is current block empty? + do + Result := is_raw and not cache.exists + end + + is_raw: BOOLEAN assign set_is_raw + -- Is raw? + -- If True, do not get wrapped it with block specific div + +feature -- Element change + + set_is_raw (b: BOOLEAN) + do + is_raw := b + end + + set_name (n: like name) + -- Set `name' to `n'. + require + not n.is_whitespace + do + name := n + end + + set_title (a_title: like title) + -- Set `title' to `a_title'. + do + title := a_title + end + +feature -- Conversion + + to_html (a_theme: CMS_THEME): STRING_8 + do + debug + print ("REUSE CACHE for [" + name + "]!!!%N") + end + -- Why in this particular case theme is not used to generate the content? + if attached cache.item as l_content then + Result := l_content + else + Result := "" + check exists: False end + end + end +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 3d026c5..960d3d1 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -453,6 +453,29 @@ feature -- Blocks initialization end end + block_cache (a_block_id: READABLE_STRING_8): detachable TUPLE [block: CMS_CACHE_BLOCK; region: READABLE_STRING_8; expired: BOOLEAN] + -- Cached version of block `a_block_id'. + 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.files_location.extended (".cache").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 + feature -- Blocks regions regions: STRING_TABLE [CMS_BLOCK_REGION] @@ -786,6 +809,7 @@ feature -- Generation 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 do -- Menu create {CMS_LOCAL_LINK} lnk.make ("Home", "") @@ -884,7 +908,13 @@ feature -- Generation end end end - page.add_to_region (theme.block_html (ic.item), reg_ic.item.name) + l_block_html := theme.block_html (ic.item) + if attached {CMS_CACHE_BLOCK} ic.item then + + elseif attached block_cache (ic.item.name) as l_cache_block then + l_cache_block.block.cache.put (l_block_html) + end + page.add_to_region (l_block_html, reg_ic.item.name) end end