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
10if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../');
11require_once(DOKU_INC . 'inc/init.php');
12session_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
22switch ($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
48if ($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
57header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
58header('Pragma: public');
59header('Content-Type: application/xml; charset=utf-8');
60
61if ($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;
81if ($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
97if ($po = plugin_load('helper', $plugin)) {
98    feed_getPages($rss, $po, $ns, $num, $fn);
99}
100$feed = $rss->createFeed($type);
101
102// save cachefile
103io_saveFile($cache, $feed);
104
105// finally deliver
106print $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 */
123function 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