]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/RssWriter.php
Add support for generation of RSSified RecentChanges.
[SourceForge/phpwiki.git] / lib / RssWriter.php
1 <?php rcs_id('$Id: RssWriter.php,v 1.1 2001-12-07 22:15:43 dairiki Exp $');
2 /*
3  * Code for creating RSS 1.0.
4  */
5
6 // FIXME: this should probably be improved/cleaned then moved into its own file.
7 //
8 /**
9  * An XML element.
10  */
11 class XmlElement
12 {
13     function XmlElement ($name, $attr = false, $content = false) {
14         $this->_name = $name;
15         $this->_attr = array();
16         if (is_array($attr)) {
17             $this->set($attr);
18         }
19         $this->_content = array();
20         if ($content) {
21             $this->add($content);
22         }
23     }
24
25     function set ($attr, $value = false) {
26         if (is_array($attr)) {
27             assert($value === false);
28             foreach ($attr as $a => $v)
29                 $this->set($a, $v);
30         }
31         else {
32             assert(is_string($attr));
33             assert(is_string($value));
34             $this->_attr[$attr] = $value;
35         }
36     }
37
38     function get ($attr) {
39         if (isset($this->_attr[$attr]))
40             return $this->_attr[$attr];
41         else
42             return false;
43     }
44     
45     function add ($content) {
46         if (!is_array($content))
47             $content = array($content);
48         foreach ($content as $c)
49             $this->_content[] = $c;
50     }
51
52     function asString ($indent = '') {
53         $begin[0] = $indent . '<' . $this->_name;
54         $nchars = strlen($begin[0]) + 1;
55         
56         reset($this->_attr);
57         while (list ($attr, $value) = each($this->_attr)) {
58             $q = sprintf('%s="%s"',
59                               $attr, $this->_quote_attribute($value));
60             $nchars += strlen($q) + 1;
61             $begin[] = $q;
62         }
63
64         if ($nchars > 79) {
65             $xml = join("\n$indent    ", $begin);
66         }
67         else {
68             $xml = join(" ", $begin);
69         }
70         
71         if (($n = count($this->_content)) > 0) {
72             $xml .= ">";
73
74             $c = $this->_content[0];
75             if (is_object($c)) {
76                 $xml .= "\n" . $c->asString($indent . "  ");
77                 $break_lines = true;
78             }
79             else {
80                 $xml .= $this->_quote($c);
81                 $break_lines = false;
82             }
83
84             for ($i = 1; $i < $n; $i++) {
85                 $c = $this->_content[$i];
86                 if (is_string($c)) {
87                     $xml .= "\n$indent" . $this->_quote($c);
88                 }
89                 else {
90                     $xml .= "\n" . $c->asString($indent . "  ");
91                 }
92                 $break_lines = true;
93             }
94             if ($break_lines) {
95                 $xml .= "\n$indent";
96             }
97             $xml .= sprintf("</%s>", $this->_name);
98         }
99         else {
100             $xml .= "/>";
101         }
102         return $xml;
103     }
104
105     function _quote ($string) {
106         return str_replace('<', '&lt;',
107                            str_replace('>', '&gt;',
108                                        str_replace('&', '&amp;', $string)));
109     }
110
111     function _quote_attribute ($value) {
112         return str_replace('"', '&quot;', $this->_quote($value));
113     }
114     
115 };
116
117
118 /**
119  * A class for writing RSS 1.0.
120  *
121  * @see http://purl.org/rss/1.0/spec,
122  *      http://www.usemod.com/cgi-bin/mb.pl?ModWiki
123  */
124 class RssWriter extends XmlElement
125 {
126     function RssWriter () {
127         $this->XmlElement('rdf:RDF',
128                           array('xmlns' => "http://purl.org/rss/1.0/",
129                                 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'));
130
131         $this->_modules = array(
132             //Standards
133             'content'   => "http://purl.org/rss/1.0/modules/content/",
134             'dc'        => "http://purl.org/dc/elements/1.1/",
135             'sy'        => "http://purl.org/rss/1.0/modules/syndication/",
136             //Proposed
137             'wiki'      => "http://purl.org/rss/1.0/modules/wiki/",
138             'ag'        => "http://purl.org/rss/1.0/modules/aggregation/",
139             'annotate'  => "http://purl.org/rss/1.0/modules/annotate/",
140             'audio'     => "http://media.tangent.org/rss/1.0/",
141             'cp'        => "http://my.theinfo.org/changed/1.0/rss/",
142             'rss091'    => "http://purl.org/rss/1.0/modules/rss091/",
143             'slash'     => "http://purl.org/rss/1.0/modules/slash/",
144             'taxo'      => "http://purl.org/rss/1.0/modules/taxonomy/",
145             'thr'       => "http://purl.org/rss/1.0/modules/threading/"
146             );
147
148         $this->_uris_seen = array();
149         $this->_items = array();
150     }
151
152     function registerModule($alias, $uri) {
153         assert(!isset($this->_modules[$alias]));
154         $this->_modules[$alias] = $uri;
155     }
156         
157     // Args should include:
158     //  'title', 'link', 'description'
159     // and can include:
160     //  'URI'
161     function channel($properties, $uri = false) {
162         $this->_channel = $this->__node('channel', $properties, $uri);
163     }
164     
165     // Args should include:
166     //  'title', 'link'
167     // and can include:
168     //  'description', 'URI'
169     function addItem($properties, $uri = false) {
170         $this->_items[] = $this->__node('item', $properties, $uri);
171     }
172
173     // Args should include:
174     //  'url', 'title', 'link'
175     // and can include:
176     //  'URI'
177     function image($properties, $uri = false) {
178         $this->_image = $this->__node('image', $properties, $uri);
179
180     }
181
182     // Args should include:
183     //  'title', 'description', 'name', and 'link'
184     // and can include:
185     //  'URI'
186     function textinput($properties, $uri = false) {
187         $this->_textinput = $this->__node('textinput', $properties, $uri);
188     }
189
190     /**
191      * Finish construction of RSS.
192      */
193     function finish() {
194         if (isset($this->_finished))
195             return;
196
197         $channel = &$this->_channel;
198         $items = &$this->_items;
199
200         if ($items) {
201             $seq = new XmlElement('rdf:Seq');
202             foreach ($items as $item)
203                 $seq->add($this->__ref('rdf:li', $item));
204             $channel->add(new XmlElement('items', false, $seq));
205         }
206      
207         if (isset($this->_image)) {
208             $channel->add($this->__ref('image', $this->_image));
209             $items[] = $this->_image;
210         }
211         if (isset($this->_textinput)) {
212             $channel->add($this->__ref('textinput', $this->_textinput));
213             $items[] = $this->_textinput;
214         }
215
216         $this->add($channel);
217         if ($items)
218             $this->add($items);
219
220         $this->_finished = true;
221     }
222             
223     /**
224      * Get XML representation of RSS.
225      */
226     function asXML () {
227         $this->finish();
228         return $this->asString();
229     }
230
231     /**
232      * Create a new RDF <i>typedNode</i>.
233      */
234     function __node($type, $properties, $uri = false) {
235         if (! $uri)
236             $uri = $properties['link'];
237         $attr['rdf:about'] = $this->__uniquify_uri($uri);
238         return new XmlElement($type, $attr,
239                               $this->__elementize($properties));
240     }
241
242     /**
243      * Check object URI for uniqueness, create a unique URI if needed.
244      */
245     function __uniquify_uri ($uri) {
246         if (!$uri || isset($this->_uris_seen[$uri])) {
247             $n = count($this->_uris_seen);
248             $uri = $this->_channel->get('rdf:about') . "#uri$n";
249             assert(!isset($this->_uris_seen[$uri]));
250         }
251         $this->_uris_seen[$uri] = true;
252         return $uri;
253     }
254
255     /**
256      * Convert hash of RDF properties to <i>propertyElt</i>s.
257      */
258     function __elementize ($elements) {
259         $out = array();
260         foreach ($elements as $prop => $val) {
261             $this->__check_predicate($prop);
262             $out[] = new XmlElement($prop, false, $val);
263         }
264         return $out;
265     }
266
267     /**
268      * Check property predicates for XMLNS sanity.
269      */
270     function __check_predicate ($name) {
271         if (preg_match('/^([^:]+):[^:]/', $name, $m)) {
272             $ns = $m[1];
273             if (! $this->get("xmlns:$ns")) {
274                 if (!isset($this->_modules[$ns]))
275                     die("$name: unknown namespace ($ns)");
276                 $this->set("xmlns:$ns", $this->_modules[$ns]);
277             }
278         }
279     }
280
281     /**
282      * Create a <i>propertyElt</i> which references another node in the RSS.
283      */
284     function __ref($predicate, $reference) {
285         $attr['rdf:resource'] = $reference->get('rdf:about');
286         return new XmlElement($predicate, $attr);
287     }
288 };
289
290
291 // (c-file-style: "gnu")
292 // Local Variables:
293 // mode: php
294 // tab-width: 8
295 // c-basic-offset: 4
296 // c-hanging-comment-ender-p: nil
297 // indent-tabs-mode: nil
298 // End:   
299 ?>