public
Fork of lorenjohnson/radiant-rss-reader
Description: Feed reader for Radiant CMS
Homepage: http://www.hellovenado.com
Clone URL: git://github.com/jamesmacaulay/radiant-rss-reader.git
Loren Johnson (author)
Thu Apr 17 04:16:16 -0700 2008
commit  92a7110d055edf9942bc7d069acd6a71337373b9
tree    af61b62bfa516da5467445bc2749920b0a7503b0
parent  294375aba51bce3f99ac26969f293689c22eb4ed
...
1
2
3
4
 
5
6
7
8
 
9
10
11
12
 
 
13
14
15
16
17
18
19
20
 
 
 
21
22
23
24
25
26
 
27
28
29
 
 
 
30
31
32
 
 
 
...
1
2
3
 
4
5
6
7
 
8
9
 
 
 
10
11
12
13
14
15
16
 
 
 
17
18
19
20
21
22
23
24
 
25
26
27
28
29
30
31
32
 
33
34
35
36
0
@@ -1,31 +1,35 @@
0
 * ABOUT
0
 This is a RadiantCMS extension (originally a behavior by Alessandro Preite Martinez) that
0
 adds some tags to fetch and display RSS feeds. It uses the
0
-'feedparser' module, and it is able to cache the raw feed data
0
+'ruby-feedparser' module, and it is able to cache the raw feed data
0
 and to only fetch the new feed if it has been modified (using the
0
 If-Modified-Since HTTP header).
0
 
0
-
0
+
0
 * INSTALLATION
0
-1. Copy this entire rss_reader folder to vendor/extensions in your Radiant installation
0
-2. Install the FeedParser Ruby module (http://home.gna.org/ruby-feedparser/). It can be copied to the root lib directory
0
-of your Radiant installation.
0
+1. Download the .tgz file into the root of your radiant installation.
0
+2. Expand the .tgz file, should put "feedparser.rb and a folder called 'feedparser'" into your /lib/ folder and a folder called rss_reader in /vendors/extensions/
0
  
0
 
0
 * USAGE EXAMPLE
0
 Use it in your page like this (just an example):
0
 
0
- <dl>
0
- <r:feed:items url="http://some.feed/rss" limit="5">
0
- <dt><r:feed:link /> - by <r:feed:creator />, <r:feed:date /></dt>
0
+<dl>
0
+ <r:feed:items url="http://www.somefeed.com/rss limit="5">
0
+ <dt><r:feed:link /> - by <r:feed:creator />, <r:feed:date format="%b %d"/></dt>
0
   <dd><r:feed:content /></dd>
0
  </r:feed:items>
0
  </dl>
0
 
0
 
0
-* AUTHOR
0
+* CONTRIBUTORS
0
 Port to Extension:
0
 BJ Clark (bjclark@scidept.com, http://www.scidept.com/) & Loren Johnson (loren@fn-group.com, http://www.fn-group.com)
0
 
0
+Modifications:
0
+James MacAulay (jmacaulay@gmail.com, http://jmacaulay.net/)
0
+
0
 Original Author:
0
-Alessandro Preite Martinez (ale@incal.net)
0
\ No newline at end of file
0
+Alessandro Preite Martinez (ale@incal.net)
0
+
0
+License - Creative Commons Attribution-Share Alike 2.5 License
0
lib/rss_reader.rb 100644 →
...
3
4
5
6
7
8
9
10
11
12
 
 
 
 
13
14
15
16
17
18
19
 
 
 
 
 
 
 
20
21
22
 
23
24
25
 
 
26
27
 
28
29
30
31
32
 
 
 
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 
 
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
70
71
72
73
74
75
76
77
78
 
 
 
 
 
 
 
79
80
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
83
84
85
86
87
88
89
90
 
 
 
 
 
 
91
92
93
94
95
96
 
 
 
97
98
99
...
3
4
5
 
6
 
 
 
 
 
7
8
9
10
11
12
13
14
15
 
 
16
17
18
19
20
21
22
23
24
 
25
26
 
 
27
28
29
 
30
31
32
 
 
 
33
34
35
36
37
38
39
40
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43
44
 
 
45
 
 
 
46
 
 
 
 
 
 
 
 
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
 
 
 
 
 
 
 
 
 
109
110
111
112
113
114
115
116
 
 
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
 
 
141
 
 
 
 
 
142
143
144
145
146
147
148
 
149
 
 
 
150
151
152
153
154
155
0
@@ -3,97 +3,153 @@ require 'net/http'
0
 require 'feedparser'
0
 
0
 class RssReader < Page
0
- include FeedParser
0
   
0
- CACHE_DIR = '/tmp'
0
-
0
- def fetch(uri)
0
- c = File.join(CACHE_DIR, uri.tr(':/','_'))
0
- if File.exist?(c)
0
+ def fetch_rss(uri, cache_time)
0
+ c = File.join(ActionController::Base.page_cache_directory, uri.tr(':/','_'))
0
+ if (cached_feed = feed_for(IO.read(c)) rescue nil)
0
+ return cached_feed if File.mtime(c) > (Time.now - cache_time)
0
       since = File.mtime(c).httpdate
0
     else
0
       since = "1970-01-01 00:00:00"
0
     end
0
     u = URI::parse(uri)
0
- http = Net::HTTP.start(u.host, u.port)
0
- answer = http.get("#{u}", { "If-Modified-Since" => since })
0
+ begin
0
+ http = Net::HTTP.start(u.host, u.port)
0
+ answer = http.get("#{u.path}", { "If-Modified-Since" => since })
0
+ feed = feed_for(answer.body)
0
+ rescue
0
+ return cached_feed
0
+ end
0
     case answer.code
0
     when '304'
0
- return IO.read(c)
0
+ return cached_feed
0
     when '200'
0
- File.new(c, 'w').write(answer.body)
0
- return answer.body
0
+ File.open(c,'w+') { |fp| fp << answer.body }
0
+ return feed
0
     else
0
- return ''
0
+ raise StandardError, "#{answer.code} #{answer.message}"
0
     end
0
   end
0
-
0
- def fetch_rss(uri)
0
- FeedParser::Feed.new(fetch(uri))
0
+
0
+ def feed_for(str)
0
+ FeedParser::Feed.new(str)
0
   end
0
 
0
   def cache?
0
     false
0
   end
0
 
0
- tag "feed" do |tag|
0
- tag.expand
0
- end
0
-
0
- tag "feed:items" do |tag|
0
- attr = tag.attr.symbolize_keys
0
- result = []
0
- items = fetch_rss(attr[:url]).items
0
- if attr[:limit]
0
- items = items.slice(0,attr[:limit].to_i)
0
- end
0
- items.each do |item|
0
- tag.locals.item = item
0
- result << tag.expand
0
+ tag "feed" do |tag|
0
+ tag.expand
0
     end
0
- result
0
- end
0
 
0
- tag "feed:title" do |tag|
0
- tag.locals.item.title
0
- end
0
 
0
- tag "feed:link" do |tag|
0
- options = tag.attr.dup
0
- attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
0
- attributes = " #{attributes}" unless attributes.empty?
0
- href = tag.locals.item.link
0
- text = tag.double? ? tag.expand : tag.locals.item.title
0
- %{<a href="#{href}"#{attributes}>#{text}</a>}
0
- end
0
+ # feed:items tag attributes
0
+ # =========================
0
+ #
0
+ # url: URL of the feed. No relative URLs, must be absolute.
0
+ # cache_time: length of time to cache the feed before seeing if it's been updated
0
+ # order: works just like SQL 'ORDER BY' clauses, e.g. order='creator date desc'
0
+ # orders first by creator ascending, then date descending
0
+ # limit: only return the first x items (after any ordering)
0
+ tag "feed:items" do |tag|
0
+ attr = tag.attr.symbolize_keys
0
+ result = []
0
+ begin
0
+ items = fetch_rss(attr[:url], attr[:cache_time].to_i || 900).items
0
+ rescue
0
+ return "<!-- RssReader error: #{$!} -->"
0
+ end
0
+ if attr[:order]
0
+ (tokens = attr[:order].split.map {|t| t.downcase}.reverse).each_index do |i|
0
+ t = tokens[i]
0
+ if ['title','link','content','date','creator'].include? t
0
+ items.sort! {|x,y| (tokens[i-1] == 'desc') ? (y.send(t) <=> x.send(t)) : (x.send(t) <=> y.send(t)) }
0
+ end
0
+ end
0
+ end
0
+ if attr[:limit]
0
+ items = items.slice(0,attr[:limit].to_i)
0
+ end
0
+ items.each_index do |i|
0
+ tag.locals.item = items[i]
0
+ tag.locals.last_item = items[i-1] if i > 0
0
+ result << tag.expand
0
+ end
0
+ result
0
+ end
0
+
0
+ #Contents of feed:header tag block are only rendered if item.send(attr[:for])
0
+ #is different from the last item. E.g. use like this in an ordered-by-creator feed:
0
+ #
0
+ # <r:feed:header for="creator">
0
+ # <h2><r:feed:creator /></h2>
0
+ # </r:feed:header>
0
+ # <r:feed:content />
0
+ #
0
+ # for='date' chunks by days (i.e. not hours or seconds, thankfully)
0
+ tag "feed:header" do |tag|
0
+ attr = tag.attr.symbolize_keys
0
+ grouping = attr[:for] || 'date'
0
+ unless tag.locals.last_item
0
+ tag.expand
0
+ else
0
+ if ['title','link','content','creator'].include? grouping
0
+ tag.expand if tag.locals.item.send(grouping) != tag.locals.last_item.send(grouping)
0
+ elsif grouping == 'date'
0
+ tag.expand if tag.locals.item.send(grouping).strftime("%j%Y") != tag.locals.last_item.send(grouping).strftime("%j%Y")
0
+ end
0
+ end
0
+ end
0
+
0
+ tag "feed:title" do |tag|
0
+ tag.locals.item.title
0
+ end
0
 
0
- tag "feed:content" do |tag|
0
- attr = tag.attr.symbolize_keys
0
- result = tag.locals.item.content
0
- if attr[:max_length]
0
- l = tag.locals.item.content.size()
0
- maxl = attr[:max_length].to_i
0
- if l > maxl
0
- result = tag.locals.item.content[0..maxl] + ' ...'
0
- end
0
+ tag "feed:link" do |tag|
0
+ options = tag.attr.dup
0
+ attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
0
+ attributes = " #{attributes}" unless attributes.empty?
0
+ href = tag.locals.item.link
0
+ text = tag.double? ? tag.expand : tag.locals.item.title
0
+ %{<a href="#{href}"#{attributes}>#{text}</a>}
0
     end
0
- if result and attr[:no_html]
0
- result = result.gsub(/<[^>]+>/, '')
0
+
0
+ # feed:content tag attributes
0
+ # ===========================
0
+ #
0
+ # max_length: no-nonsense truncation
0
+ # no_p: takes out just the enclosing <p></p> tags that FeedParser puts in
0
+ # no_html: takes out *all* html
0
+ #
0
+ tag "feed:content" do |tag|
0
+ attr = tag.attr.symbolize_keys
0
+ result = tag.locals.item.content
0
+ if attr[:max_length]
0
+ l = tag.locals.item.content.size()
0
+ maxl = attr[:max_length].to_i
0
+ if l > maxl
0
+ result = tag.locals.item.content[0..maxl] + ' ...'
0
+ end
0
+ end
0
+ if result
0
+ result = result.gsub(/\A<p>(.*)<\/p>\z/m,'\1') if attr[:no_p]
0
+ result = result.gsub(/<[^>]+>/, '') if attr[:no_html]
0
+ end
0
+ result
0
     end
0
- result
0
- end
0
 
0
- tag "feed:date" do |tag|
0
- # Could change this default format string to '%b %d' if you prefer
0
- format = (tag.attr['format'] || '%A, %B %d, %Y')
0
- if date = tag.locals.item.date
0
- date.strftime(format)
0
+ tag "feed:date" do |tag|
0
+ # Could change this default format string to '%b %d' if you prefer
0
+ format = (tag.attr['format'] || '%A, %B %d, %Y')
0
+ if date = tag.locals.item.date
0
+ date.strftime(format)
0
+ end
0
     end
0
- end
0
 
0
- tag "feed:creator" do |tag|
0
- tag.locals.item.creator
0
- end
0
+ tag "feed:creator" do |tag|
0
+ tag.locals.item.creator
0
+ end
0
 
0
 end
0
   

Comments

    No one has commented yet.