1 <?php
2 /**
3  * XML feed export
4  *
5  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6  * @author     Andreas Gohr <andi@splitbrain.org>
7  * @author     Esther Brunner <wikidesign@gmail.com>
8  */
9 
10 if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../');
11 require_once(DOKU_INC . 'inc/init.php');
12 session_write_close();
13 
14 $plugin = $INPUT->str('plugin');
15 $fn = $INPUT->str('fn');
16 $ns = cleanID($INPUT->str('ns'));
17 $num = $INPUT->int('num');
18 $other = $INPUT->str('tag') . $INPUT->str('user');
19 $title = $INPUT->str('title');
20 $type = $INPUT->str('type', $conf['rss_type']);
21 
22 switch ($type) {
23     case 'rss':
24         $type = 'RSS0.91';
25         $mime = 'text/xml';
26         break;
27     case 'rss2':
28         $type = 'RSS2.0';
29         $mime = 'text/xml';
30         break;
31     case 'atom':
32         $type = 'ATOM0.3';
33         $mime = 'application/xml';
34         break;
35     case 'atom1':
36         $type = 'ATOM1.0';
37         $mime = 'application/atom+xml';
38         break;
39     default:
40         $type = 'RSS1.0';
41         $mime = 'application/xml';
42 }
43 
44 // the feed is dynamic - we need a cache for each combo
45 // (but most people just use the default feed so it's still effective)
46 $cache = getCacheName($plugin . $fn . $ns . $num . $other . $type . $INPUT->server->str('REMOTE_USER'), '.feed');
47 $cmod = @filemtime($cache); // 0 if not exists
48 if ($cmod && (@filemtime(DOKU_CONF . 'local.php') > $cmod
49         || @filemtime(DOKU_CONF . 'dokuwiki.php') > $cmod)
50 ) {
51     // ignore cache if feed prefs may have changed
52     $cmod = 0;
53 }
54 
55 // check cacheage and deliver if nothing has changed since last
56 // time or the update interval has not passed, also handles conditional requests
57 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
58 header('Pragma: public');
59 header('Content-Type: application/xml; charset=utf-8');
60 
61 if ($cmod && (
62         ($cmod + $conf['rss_update'] > time())
63         || (
64             ($cmod > @filemtime($conf['changelog']))
65             &&
66             //discussion has its own changelog
67             ($plugin !== 'discussion' || $cmod > @filemtime($conf['metadir'] . '/_comments.changes'))
68         )
69     )) {
70     http_conditionalRequest($cmod);
71     if ($conf['allowdebug']) header("X-CacheUsed: $cache");
72     print io_readFile($cache);
73     exit;
74 } else {
75     http_conditionalRequest(time());
76 }
77 
78 // create new feed
79 $rss = new UniversalFeedCreator();
80 $rss->title = $title;
81 if ($ns) {
82     $rss->title .= ' ' . ucwords(str_replace(array('_', ':'), array(' ', ': '), $ns));
83 } elseif ($other) {
84     $rss->title .= ' ' . ucwords(str_replace('_', ' ', $other));
85 }
86 $rss->title .= ' ยท ' . $conf['title'];
87 $rss->link = DOKU_URL;
88 $rss->syndicationURL = DOKU_PLUGIN . 'feed/feed.php';
89 $rss->cssStyleSheet = DOKU_URL . 'lib/exe/css.php?s=feed';
90 
91 $image = new FeedImage();
92 $image->title = $conf['title'];
93 $image->url = DOKU_URL . "lib/images/favicon.ico";
94 $image->link = DOKU_URL;
95 $rss->image = $image;
96 
97 if ($po = plugin_load('helper', $plugin)) {
98     feed_getPages($rss, $po, $ns, $num, $fn);
99 }
100 $feed = $rss->createFeed($type);
101 
102 // save cachefile
103 io_saveFile($cache, $feed);
104 
105 // finally deliver
106 print $feed;
107 
108 /* ---------- */
109 
110 /**
111  * Add pages given by plugin to feed object
112  *
113  * @param UniversalFeedCreator $rss
114  * @param \dokuwiki\Extension\PluginInterface $po
115  * @param string $ns
116  * @param int $num
117  * @param string $fn
118  * @return bool
119  * @author Andreas Gohr <andi@splitbrain.org>
120  * @author Esther Brunner <wikidesign@gmail.com>
121  *
122  */
123 function feed_getPages($rss, $po, $ns, $num, $fn)
124 {
125     global $conf;
126 
127     if ((!$num) || (!is_numeric($num))) $num = $conf['recent'];
128 
129     // get the pages for our namespace
130     $pages = $po->$fn($ns, $num);
131     if (!$pages) return false;
132 
133     foreach ($pages as $page) {
134         $item = new FeedItem();
135 
136         list($id, /* $hash */) = explode('#', $page['id'], 2);
137         $meta = p_get_metadata($id);
138 
139         // title
140         if ($page['title']) {
141             $item->title = $page['title'];
142         } elseif ($meta['title']) {
143             $item->title = $meta['title'];
144         } else {
145             $item->title = ucwords($id);
146         }
147 
148         // link
149         $item->link = wl($page['id'], '', true, '&') . '#' . $page['anchor'];
150 
151         // description
152         if ($page['desc']) {
153             $description = $page['desc'];
154         } else {
155             $description = $meta['description']['abstract'];
156         }
157         if (get_class($po) == 'helper_plugin_discussion') {
158             //discussion plugins returns striped parsed text, inclusive encoded chars. Don't double encode.
159             $description = htmlspecialchars($description, ENT_COMPAT, 'UTF-8', $double_encode = false);
160         } else {
161             $description = htmlspecialchars($description);
162         }
163         $item->description = $description;
164 
165         // date
166         $item->date = date('r', $page['date']);
167 
168         // category
169         if ($page['cat']) {
170             $item->category = $page['cat'];
171         } elseif ($meta['subject']) {
172             if (is_array($meta['subject'])) {
173                 $item->category = $meta['subject'][0];
174             } else {
175                 $item->category = $meta['subject'];
176             }
177         }
178 
179         // creator
180         if ($page['user']) {
181             $item->author = $page['user'];
182         } else {
183             $item->author = $meta['creator'];
184         }
185 
186         $rss->addItem($item);
187     }
188     return true;
189 }
190