Add xml entity escape code to rss20.
[matthijs/upstream/blosxom-plugins.git] / gavinc / rss20
1 # Blosxom Plugin: rss20
2 # Author(s): Gavin Carr <gavin@openfusion.com.au>
3 # Version: 0.001002
4 # Requires: storydate, lastmodified2
5 # Suggests: absolute
6 # Follows:  storydate, lastmodified2
7
8 package rss20;
9
10 use strict;
11 use vars qw(
12   $flavour
13   $author_email 
14   $error_email 
15   $permalink
16   $trackback_link
17   $copyright
18   $generator_url
19 );
20
21 # --- Configuration variables -----
22
23 # What flavour string to you want to use for your feed?
24 $flavour = 'rss';
25 #$flavour = 'rss20';
26
27 # What email address should be used as the default author email?
28 $author_email = 'author@example.com';
29
30 # What email address should feed errors be reported to?
31 $error_email = '';
32
33 # What do your story permalinks look like?
34 # This must be single-quoted, to defer evaluation; blosxom namespace applies
35 $permalink = '$url$path/$fn.$default_flavour';
36
37 # What link should be used for trackbacks on a story?
38 # This must be single-quoted, to defer evaluation; blosxom namespace applies
39 $trackback_link = '';
40 # $trackback_link = '$url$path/$fn.writeback';
41 # $trackback_link = '$url$path/$fn.$feedback::trackback_flavour';
42
43 # Copyright statement; leave blank to omit.
44 $copyright = '';
45
46 # Generator that produced this feed
47 $generator_url = "http://blosxom.sourceforge.net/?v=$blosxom::version";
48
49 # --------------------------------
50
51 $error_email ||= $author_email;
52
53 # Escape <, >, and & to hex-encoded entities for max compatibility in text elements
54 # See http://www.rssboard.org/rss-profile#data-types-characterdata
55 my %escape_text = (
56   '<' => '&#x3C;',
57   '>' => '&#x3E;',
58   '&' => '&#x26;',
59 );
60 my $escape_text_re = join '|' => keys %escape_text;
61
62 # Escape <, >, and & to standard html-encoded entities for in html elements
63 my %escape_html = (
64   '<' => '&lt;',
65   '>' => '&gt;',
66   '&' => '&amp;',
67 );
68 my $escape_html_re = join '|' => keys %escape_html;
69
70 sub start { 
71   _load_templates();
72 }
73
74 sub story {
75   my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
76
77   # Don't double-encode if someone else has already done it
78   return unless $blosxom::encode_xml_entities;
79
80   # Encode and reset encode_xml_entities flag
81   $$title_ref = _escape_text( $$title_ref );
82   $$body_ref =  _escape_html( $$body_ref );
83   $blosxom::encode_xml_entities = 0;
84 }
85
86 # --- Private subroutines
87
88 sub _escape_text {
89   my ($text) = @_;
90   $text =~ s/($escape_text_re)/$escape_text{$1}/g;
91   return $text;
92 }
93
94 sub _escape_html {
95   my ($html) = @_;
96   $html =~ s/($escape_html_re)/$escape_html{$1}/g;
97   return $html;
98 }
99
100 sub _load_templates {
101   $blosxom::template{$flavour}{'content_type'} = 'text/xml; charset=$blog_encoding';
102
103   $blosxom::template{$flavour}{'date'} = "\n";
104
105   $blosxom::template{$flavour}{'head'} = <<HEAD;
106 <?xml version="1.0" encoding="$blosxom::blog_encoding"?>
107 <rss version="2.0"
108     xmlns:dc="http://purl.org/dc/elements/1.1/"
109     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
110     xmlns:admin="http://webns.net/mvcb/"
111     xmlns:atom="http://www.w3.org/2005/Atom"
112     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
113     xmlns:content="http://purl.org/rss/1.0/modules/content/">
114
115     <channel>
116         <title>$blosxom::blog_title</title>
117         <link>$blosxom::url</link>
118         <description>$blosxom::blog_description</description>
119         <dc:date>\$lastmodified2::latest_iso8601</dc:date>
120         <dc:language>$blosxom::blog_language</dc:language>
121         <dc:creator>mailto:$rss20::author_email</dc:creator>
122         <dc:rights>$rss20::copyright</dc:rights>
123         <admin:generatorAgent rdf:resource="$rss20::generator_url" />
124         <admin:errorReportsTo rdf:resource="mailto:$rss20::error_email" />
125         <atom:link href="$blosxom::url$ENV{PATH_INFO}" rel="self" type="application/rss+xml" />
126         <sy:updatePeriod>hourly</sy:updatePeriod>
127         <sy:updateFrequency>1</sy:updateFrequency>
128         <sy:updateBase>2000-01-01T12:00+00:00</sy:updateBase>
129
130 HEAD
131
132   $blosxom::template{$flavour}{'story'} = <<STORY;
133         <item>
134             <title>\$title</title>
135             <link>$rss20::permalink</link>                                                                             
136             <description>\$body</description>                                                   
137             <comments>$rss20::trackback_link</comments>                                                            
138             <guid isPermaLink="true">$rss20::permalink</guid>                                                          
139             <dc:date>\$storydate::story_iso8601</dc:date>
140         </item>
141 STORY
142
143   $blosxom::template{$flavour}{'foot'} = <<'FOOT';
144     </channel>                                    
145 </rss>  
146 FOOT
147
148   1;
149 }
150
151
152 1;
153
154 __END__
155
156 =head1 NAME
157
158 rss20 - blosxom plugin to generate an RSS 2.0 feed of your blog
159
160 =head1 DESCRIPTION
161
162 rss20 is a blosxom plugin to generate an RSS 2.0 feed of your blog. It
163 is self-contained, including the required flavours, and has a bunch of
164 configuration variables to allow for configuration.
165
166 =head2 CONFIGURATION
167
168 The following package variables can be configured:
169
170 =over 4
171
172 =item $flavour
173
174 Flavour string to use for your feed (typically 'rss' or 'rss20').
175
176 =item $author_email
177
178 Default author email address for posts.
179
180 =item $error_email
181
182 Email address to use to report errors with the feed (defaults to 
183 $author_email).
184
185 =item $permalink
186
187 Story permalink. Default is '$url$path/$fn.$default_flavour'.
188 Note that this value should probably be single quoted to defer
189 variable evaluation until rendering time.
190
191 =item $trackback_link
192
193 Story trackback or comment link. Default is ''. Useful if you
194 are using a comments plugin like C<writeback> or C<feedback>,
195 and the format you want will be plugin-dependant.
196
197 Like $permalink, this value should be single quoted to defer
198 variable evaluation till rendering time.
199
200 =item $copyright
201
202 Default copyright clause for posts, if any.
203
204 =back
205
206 =head1 USAGE
207
208 rss20 should be loaded relatively late, since you'll typically want
209 plugins that manipulate your story content to have run already. It 
210 also should be run after the 'story' or 'prefs' plugins if you want
211 to use those to customise your configuration variables.
212
213
214 =head1 SEE ALSO
215
216 Blosxom: http://blosxom.sourceforge.net/
217
218 rss20 is based on the 'atomfeed' and 'rss10' plugins, by Rael
219 Dornfest and contributors.
220
221
222 =head1 BUGS
223
224 Please report bugs either directly to the author or to the blosxom 
225 development mailing list: <blosxom-devel@lists.sourceforge.net>.
226
227
228 =head1 AUTHOR
229
230 Gavin Carr <gavin@openfusion.com.au>, http://www.openfusion.net/
231
232
233 =head1 LICENSE
234
235 Copyright 2007 Gavin Carr.
236
237 This plugin is licensed under the same terms as blosxom itself i.e.
238
239 Permission is hereby granted, free of charge, to any person obtaining a
240 copy of this software and associated documentation files (the "Software"),
241 to deal in the Software without restriction, including without limitation
242 the rights to use, copy, modify, merge, publish, distribute, sublicense,
243 and/or sell copies of the Software, and to permit persons to whom the
244 Software is furnished to do so, subject to the following conditions:
245
246 The above copyright notice and this permission notice shall be included
247 in all copies or substantial portions of the Software.
248
249 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
250 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
251 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
252 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
253 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
254 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
255 OTHER DEALINGS IN THE SOFTWARE.
256
257 =cut
258
259 # vim:ft=perl