These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * XML feed export |
||
4 | * |
||
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) |
||
6 | * @author Andreas Gohr <[email protected]> |
||
7 | * |
||
8 | * @global array $conf |
||
9 | * @global Input $INPUT |
||
10 | */ |
||
11 | |||
12 | if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/'); |
||
13 | require_once(DOKU_INC.'inc/init.php'); |
||
14 | |||
15 | //close session |
||
16 | session_write_close(); |
||
17 | |||
18 | //feed disabled? |
||
19 | if(!actionOK('rss')) { |
||
20 | http_status(404); |
||
21 | echo '<error>RSS feed is disabled.</error>'; |
||
22 | exit; |
||
23 | } |
||
24 | |||
25 | // get params |
||
26 | $opt = rss_parseOptions(); |
||
27 | |||
28 | // the feed is dynamic - we need a cache for each combo |
||
29 | // (but most people just use the default feed so it's still effective) |
||
30 | $key = join('', array_values($opt)).'$'.$_SERVER['REMOTE_USER'].'$'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT']; |
||
31 | $cache = new cache($key, '.feed'); |
||
32 | |||
33 | // prepare cache depends |
||
34 | $depends['files'] = getConfigFiles('main'); |
||
35 | $depends['age'] = $conf['rss_update']; |
||
36 | $depends['purge'] = $INPUT->bool('purge'); |
||
37 | |||
38 | // check cacheage and deliver if nothing has changed since last |
||
39 | // time or the update interval has not passed, also handles conditional requests |
||
40 | header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); |
||
41 | header('Pragma: public'); |
||
42 | header('Content-Type: application/xml; charset=utf-8'); |
||
43 | header('X-Robots-Tag: noindex'); |
||
44 | if($cache->useCache($depends)) { |
||
45 | http_conditionalRequest($cache->_time); |
||
46 | if($conf['allowdebug']) header("X-CacheUsed: $cache->cache"); |
||
47 | print $cache->retrieveCache(); |
||
48 | exit; |
||
49 | } else { |
||
50 | http_conditionalRequest(time()); |
||
51 | } |
||
52 | |||
53 | // create new feed |
||
54 | $rss = new UniversalFeedCreator(); |
||
55 | $rss->title = $conf['title'].(($opt['namespace']) ? ' '.$opt['namespace'] : ''); |
||
56 | $rss->link = DOKU_URL; |
||
57 | $rss->syndicationURL = DOKU_URL.'feed.php'; |
||
58 | $rss->cssStyleSheet = DOKU_URL.'lib/exe/css.php?s=feed'; |
||
0 ignored issues
–
show
|
|||
59 | |||
60 | $image = new FeedImage(); |
||
61 | $image->title = $conf['title']; |
||
62 | $image->url = tpl_getMediaFile(array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico'), true); |
||
63 | $image->link = DOKU_URL; |
||
64 | $rss->image = $image; |
||
65 | |||
66 | $data = null; |
||
67 | $modes = array( |
||
68 | 'list' => 'rssListNamespace', |
||
69 | 'search' => 'rssSearch', |
||
70 | 'recent' => 'rssRecentChanges' |
||
71 | ); |
||
72 | if(isset($modes[$opt['feed_mode']])) { |
||
73 | $data = $modes[$opt['feed_mode']]($opt); |
||
74 | } else { |
||
75 | $eventData = array( |
||
76 | 'opt' => &$opt, |
||
77 | 'data' => &$data, |
||
78 | ); |
||
79 | $event = new Doku_Event('FEED_MODE_UNKNOWN', $eventData); |
||
80 | if($event->advise_before(true)) { |
||
81 | echo sprintf('<error>Unknown feed mode %s</error>', hsc($opt['feed_mode'])); |
||
82 | exit; |
||
83 | } |
||
84 | $event->advise_after(); |
||
85 | } |
||
86 | |||
87 | rss_buildItems($rss, $data, $opt); |
||
88 | $feed = $rss->createFeed($opt['feed_type']); |
||
0 ignored issues
–
show
The call to
FeedCreator::createFeed() has too many arguments starting with $opt['feed_type'] .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
89 | |||
90 | // save cachefile |
||
91 | $cache->storeCache($feed); |
||
92 | |||
93 | // finally deliver |
||
94 | print $feed; |
||
95 | |||
96 | // ---------------------------------------------------------------- // |
||
97 | |||
98 | /** |
||
99 | * Get URL parameters and config options and return an initialized option array |
||
100 | * |
||
101 | * @author Andreas Gohr <[email protected]> |
||
102 | */ |
||
103 | function rss_parseOptions() { |
||
104 | global $conf; |
||
105 | global $INPUT; |
||
106 | |||
107 | $opt = array(); |
||
108 | |||
109 | foreach(array( |
||
110 | // Basic feed properties |
||
111 | // Plugins may probably want to add new values to these |
||
112 | // properties for implementing own feeds |
||
113 | |||
114 | // One of: list, search, recent |
||
115 | 'feed_mode' => array('str', 'mode', 'recent'), |
||
116 | // One of: diff, page, rev, current |
||
117 | 'link_to' => array('str', 'linkto', $conf['rss_linkto']), |
||
118 | // One of: abstract, diff, htmldiff, html |
||
119 | 'item_content' => array('str', 'content', $conf['rss_content']), |
||
120 | |||
121 | // Special feed properties |
||
122 | // These are only used by certain feed_modes |
||
123 | |||
124 | // String, used for feed title, in list and rc mode |
||
125 | 'namespace' => array('str', 'ns', null), |
||
126 | // Positive integer, only used in rc mode |
||
127 | 'items' => array('int', 'num', $conf['recent']), |
||
128 | // Boolean, only used in rc mode |
||
129 | 'show_minor' => array('bool', 'minor', false), |
||
130 | // String, only used in list mode |
||
131 | 'sort' => array('str', 'sort', 'natural'), |
||
132 | // String, only used in search mode |
||
133 | 'search_query' => array('str', 'q', null), |
||
134 | // One of: pages, media, both |
||
135 | 'content_type' => array('str', 'view', $conf['rss_media']) |
||
136 | |||
137 | ) as $name => $val) { |
||
138 | $opt[$name] = $INPUT->{$val[0]}($val[1], $val[2], true); |
||
139 | } |
||
140 | |||
141 | $opt['items'] = max(0, (int) $opt['items']); |
||
142 | $opt['show_minor'] = (bool) $opt['show_minor']; |
||
143 | $opt['sort'] = valid_input_set('sort', array('default' => 'natural', 'date'), $opt); |
||
144 | |||
145 | $opt['guardmail'] = ($conf['mailguard'] != '' && $conf['mailguard'] != 'none'); |
||
146 | |||
147 | $type = $INPUT->valid( |
||
148 | 'type', |
||
149 | array( 'rss', 'rss2', 'atom', 'atom1', 'rss1'), |
||
150 | $conf['rss_type'] |
||
151 | ); |
||
152 | switch($type) { |
||
153 | case 'rss': |
||
154 | $opt['feed_type'] = 'RSS0.91'; |
||
155 | $opt['mime_type'] = 'text/xml'; |
||
156 | break; |
||
157 | case 'rss2': |
||
158 | $opt['feed_type'] = 'RSS2.0'; |
||
159 | $opt['mime_type'] = 'text/xml'; |
||
160 | break; |
||
161 | case 'atom': |
||
162 | $opt['feed_type'] = 'ATOM0.3'; |
||
163 | $opt['mime_type'] = 'application/xml'; |
||
164 | break; |
||
165 | case 'atom1': |
||
166 | $opt['feed_type'] = 'ATOM1.0'; |
||
167 | $opt['mime_type'] = 'application/atom+xml'; |
||
168 | break; |
||
169 | default: |
||
170 | $opt['feed_type'] = 'RSS1.0'; |
||
171 | $opt['mime_type'] = 'application/xml'; |
||
172 | } |
||
173 | |||
174 | $eventData = array( |
||
175 | 'opt' => &$opt, |
||
176 | ); |
||
177 | trigger_event('FEED_OPTS_POSTPROCESS', $eventData); |
||
178 | return $opt; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * Add recent changed pages to a feed object |
||
183 | * |
||
184 | * @author Andreas Gohr <[email protected]> |
||
185 | * @param FeedCreator $rss the FeedCreator Object |
||
186 | * @param array $data the items to add |
||
187 | * @param array $opt the feed options |
||
188 | */ |
||
189 | function rss_buildItems(&$rss, &$data, $opt) { |
||
190 | global $conf; |
||
191 | global $lang; |
||
192 | /* @var DokuWiki_Auth_Plugin $auth */ |
||
193 | global $auth; |
||
194 | |||
195 | $eventData = array( |
||
196 | 'rss' => &$rss, |
||
197 | 'data' => &$data, |
||
198 | 'opt' => &$opt, |
||
199 | ); |
||
200 | $event = new Doku_Event('FEED_DATA_PROCESS', $eventData); |
||
201 | if($event->advise_before(false)) { |
||
202 | foreach($data as $ditem) { |
||
203 | if(!is_array($ditem)) { |
||
204 | // not an array? then only a list of IDs was given |
||
205 | $ditem = array('id' => $ditem); |
||
206 | } |
||
207 | |||
208 | $item = new FeedItem(); |
||
209 | $id = $ditem['id']; |
||
210 | if(!$ditem['media']) { |
||
211 | $meta = p_get_metadata($id); |
||
212 | } else { |
||
213 | $meta = array(); |
||
214 | } |
||
215 | |||
216 | // add date |
||
217 | if($ditem['date']) { |
||
218 | $date = $ditem['date']; |
||
219 | } elseif ($ditem['media']) { |
||
220 | $date = @filemtime(mediaFN($id)); |
||
221 | } elseif (file_exists(wikiFN($id))) { |
||
222 | $date = @filemtime(wikiFN($id)); |
||
223 | } elseif($meta['date']['modified']) { |
||
224 | $date = $meta['date']['modified']; |
||
225 | } else { |
||
226 | $date = 0; |
||
227 | } |
||
228 | if($date) $item->date = date('r', $date); |
||
229 | |||
230 | // add title |
||
231 | if($conf['useheading'] && $meta['title']) { |
||
232 | $item->title = $meta['title']; |
||
233 | } else { |
||
234 | $item->title = $ditem['id']; |
||
235 | } |
||
236 | if($conf['rss_show_summary'] && !empty($ditem['sum'])) { |
||
237 | $item->title .= ' - '.strip_tags($ditem['sum']); |
||
238 | } |
||
239 | |||
240 | // add item link |
||
241 | switch($opt['link_to']) { |
||
242 | case 'page': |
||
243 | if($ditem['media']) { |
||
244 | $item->link = media_managerURL( |
||
245 | array( |
||
246 | 'image' => $id, |
||
247 | 'ns' => getNS($id), |
||
248 | 'rev' => $date |
||
249 | ), '&', true |
||
250 | ); |
||
251 | } else { |
||
252 | $item->link = wl($id, 'rev='.$date, true, '&'); |
||
253 | } |
||
254 | break; |
||
255 | case 'rev': |
||
256 | if($ditem['media']) { |
||
257 | $item->link = media_managerURL( |
||
258 | array( |
||
259 | 'image' => $id, |
||
260 | 'ns' => getNS($id), |
||
261 | 'rev' => $date, |
||
262 | 'tab_details' => 'history' |
||
263 | ), '&', true |
||
264 | ); |
||
265 | } else { |
||
266 | $item->link = wl($id, 'do=revisions&rev='.$date, true, '&'); |
||
267 | } |
||
268 | break; |
||
269 | case 'current': |
||
270 | if($ditem['media']) { |
||
271 | $item->link = media_managerURL( |
||
272 | array( |
||
273 | 'image' => $id, |
||
274 | 'ns' => getNS($id) |
||
275 | ), '&', true |
||
276 | ); |
||
277 | } else { |
||
278 | $item->link = wl($id, '', true, '&'); |
||
279 | } |
||
280 | break; |
||
281 | case 'diff': |
||
282 | default: |
||
283 | if($ditem['media']) { |
||
284 | $item->link = media_managerURL( |
||
285 | array( |
||
286 | 'image' => $id, |
||
287 | 'ns' => getNS($id), |
||
288 | 'rev' => $date, |
||
289 | 'tab_details' => 'history', |
||
290 | 'mediado' => 'diff' |
||
291 | ), '&', true |
||
292 | ); |
||
293 | } else { |
||
294 | $item->link = wl($id, 'rev='.$date.'&do=diff', true, '&'); |
||
295 | } |
||
296 | } |
||
297 | |||
298 | // add item content |
||
299 | switch($opt['item_content']) { |
||
300 | case 'diff': |
||
301 | case 'htmldiff': |
||
302 | if($ditem['media']) { |
||
303 | $medialog = new MediaChangeLog($id); |
||
304 | $revs = $medialog->getRevisions(0, 1); |
||
305 | $rev = $revs[0]; |
||
306 | $src_r = ''; |
||
307 | $src_l = ''; |
||
308 | |||
309 | if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)), 300)) { |
||
310 | $more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id)); |
||
311 | $src_r = ml($id, $more, true, '&', true); |
||
312 | } |
||
313 | if($rev && $size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)), 300)) { |
||
314 | $more = 'rev='.$rev.'&w='.$size[0].'&h='.$size[1]; |
||
315 | $src_l = ml($id, $more, true, '&', true); |
||
316 | } |
||
317 | $content = ''; |
||
318 | if($src_r) { |
||
319 | $content = '<table>'; |
||
320 | $content .= '<tr><th width="50%">'.$rev.'</th>'; |
||
321 | $content .= '<th width="50%">'.$lang['current'].'</th></tr>'; |
||
322 | $content .= '<tr align="center"><td><img src="'.$src_l.'" alt="" /></td><td>'; |
||
323 | $content .= '<img src="'.$src_r.'" alt="'.$id.'" /></td></tr>'; |
||
324 | $content .= '</table>'; |
||
325 | } |
||
326 | |||
327 | } else { |
||
328 | require_once(DOKU_INC.'inc/DifferenceEngine.php'); |
||
329 | $pagelog = new PageChangeLog($id); |
||
330 | $revs = $pagelog->getRevisions(0, 1); |
||
331 | $rev = $revs[0]; |
||
332 | |||
333 | if($rev) { |
||
334 | $df = new Diff(explode("\n", rawWiki($id, $rev)), |
||
335 | explode("\n", rawWiki($id, ''))); |
||
336 | } else { |
||
337 | $df = new Diff(array(''), |
||
338 | explode("\n", rawWiki($id, ''))); |
||
339 | } |
||
340 | |||
341 | if($opt['item_content'] == 'htmldiff') { |
||
342 | // note: no need to escape diff output, TableDiffFormatter provides 'safe' html |
||
343 | $tdf = new TableDiffFormatter(); |
||
344 | $content = '<table>'; |
||
345 | $content .= '<tr><th colspan="2" width="50%">'.$rev.'</th>'; |
||
346 | $content .= '<th colspan="2" width="50%">'.$lang['current'].'</th></tr>'; |
||
347 | $content .= $tdf->format($df); |
||
348 | $content .= '</table>'; |
||
349 | } else { |
||
350 | // note: diff output must be escaped, UnifiedDiffFormatter provides plain text |
||
351 | $udf = new UnifiedDiffFormatter(); |
||
352 | $content = "<pre>\n".hsc($udf->format($df))."\n</pre>"; |
||
353 | } |
||
354 | } |
||
355 | break; |
||
356 | case 'html': |
||
357 | if($ditem['media']) { |
||
358 | if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) { |
||
359 | $more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id)); |
||
360 | $src = ml($id, $more, true, '&', true); |
||
361 | $content = '<img src="'.$src.'" alt="'.$id.'" />'; |
||
362 | } else { |
||
363 | $content = ''; |
||
364 | } |
||
365 | } else { |
||
366 | if (@filemtime(wikiFN($id)) === $date) { |
||
367 | $content = p_wiki_xhtml($id, '', false); |
||
368 | } else { |
||
369 | $content = p_wiki_xhtml($id, $date, false); |
||
370 | } |
||
371 | // no TOC in feeds |
||
372 | $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s', '', $content); |
||
373 | |||
374 | // add alignment for images |
||
375 | $content = preg_replace('/(<img .*?class="medialeft")/s', '\\1 align="left"', $content); |
||
376 | $content = preg_replace('/(<img .*?class="mediaright")/s', '\\1 align="right"', $content); |
||
377 | |||
378 | // make URLs work when canonical is not set, regexp instead of rerendering! |
||
379 | if(!$conf['canonical']) { |
||
380 | $base = preg_quote(DOKU_REL, '/'); |
||
381 | $content = preg_replace('/(<a href|<img src)="('.$base.')/s', '$1="'.DOKU_URL, $content); |
||
382 | } |
||
383 | } |
||
384 | |||
385 | break; |
||
386 | case 'abstract': |
||
387 | default: |
||
388 | if($ditem['media']) { |
||
389 | if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) { |
||
390 | $more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id)); |
||
391 | $src = ml($id, $more, true, '&', true); |
||
392 | $content = '<img src="'.$src.'" alt="'.$id.'" />'; |
||
393 | } else { |
||
394 | $content = ''; |
||
395 | } |
||
396 | } else { |
||
397 | $content = $meta['description']['abstract']; |
||
398 | } |
||
399 | } |
||
400 | $item->description = $content; //FIXME a plugin hook here could be senseful |
||
401 | |||
402 | // add user |
||
403 | # FIXME should the user be pulled from metadata as well? |
||
404 | $user = @$ditem['user']; // the @ spares time repeating lookup |
||
405 | if(blank($user)) { |
||
406 | $item->author = 'Anonymous'; |
||
407 | $item->authorEmail = '[email protected]'; |
||
408 | } else { |
||
409 | $item->author = $user; |
||
410 | $item->authorEmail = $user . '@undisclosed.example.com'; |
||
411 | |||
412 | // get real user name if configured |
||
413 | if($conf['useacl'] && $auth) { |
||
414 | $userInfo = $auth->getUserData($user); |
||
415 | if($userInfo) { |
||
416 | switch($conf['showuseras']) { |
||
417 | case 'username': |
||
418 | case 'username_link': |
||
419 | $item->author = $userInfo['name']; |
||
420 | break; |
||
421 | default: |
||
422 | $item->author = $user; |
||
423 | break; |
||
424 | } |
||
425 | } else { |
||
426 | $item->author = $user; |
||
427 | } |
||
428 | } |
||
429 | } |
||
430 | |||
431 | // add category |
||
432 | if(isset($meta['subject'])) { |
||
433 | $item->category = $meta['subject']; |
||
434 | } else { |
||
435 | $cat = getNS($id); |
||
436 | if($cat) $item->category = $cat; |
||
0 ignored issues
–
show
The expression
$cat of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
437 | } |
||
438 | |||
439 | // finally add the item to the feed object, after handing it to registered plugins |
||
440 | $evdata = array( |
||
441 | 'item' => &$item, |
||
442 | 'opt' => &$opt, |
||
443 | 'ditem' => &$ditem, |
||
444 | 'rss' => &$rss |
||
445 | ); |
||
446 | $evt = new Doku_Event('FEED_ITEM_ADD', $evdata); |
||
447 | if($evt->advise_before()) { |
||
448 | $rss->addItem($item); |
||
449 | } |
||
450 | $evt->advise_after(); // for completeness |
||
451 | } |
||
452 | } |
||
453 | $event->advise_after(); |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Add recent changed pages to the feed object |
||
458 | * |
||
459 | * @author Andreas Gohr <[email protected]> |
||
460 | */ |
||
461 | function rssRecentChanges($opt) { |
||
462 | global $conf; |
||
463 | $flags = RECENTS_SKIP_DELETED; |
||
464 | if(!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS; |
||
465 | if($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES; |
||
466 | if($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED; |
||
467 | |||
468 | $recents = getRecents(0, $opt['items'], $opt['namespace'], $flags); |
||
469 | return $recents; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Add all pages of a namespace to the feed object |
||
474 | * |
||
475 | * @author Andreas Gohr <[email protected]> |
||
476 | */ |
||
477 | function rssListNamespace($opt) { |
||
478 | require_once(DOKU_INC.'inc/search.php'); |
||
479 | global $conf; |
||
480 | |||
481 | $ns = ':'.cleanID($opt['namespace']); |
||
482 | $ns = utf8_encodeFN(str_replace(':', '/', $ns)); |
||
483 | |||
484 | $data = array(); |
||
485 | $search_opts = array( |
||
486 | 'depth' => 1, |
||
487 | 'pagesonly' => true, |
||
488 | 'listfiles' => true |
||
489 | ); |
||
490 | search($data, $conf['datadir'], 'search_universal', $search_opts, $ns, $lvl = 1, $opt['sort']); |
||
491 | |||
492 | return $data; |
||
493 | } |
||
494 | |||
495 | /** |
||
496 | * Add the result of a full text search to the feed object |
||
497 | * |
||
498 | * @author Andreas Gohr <[email protected]> |
||
499 | */ |
||
500 | function rssSearch($opt) { |
||
501 | if(!$opt['search_query']) return array(); |
||
502 | |||
503 | require_once(DOKU_INC.'inc/fulltext.php'); |
||
504 | $data = ft_pageSearch($opt['search_query'], $poswords); |
||
505 | $data = array_keys($data); |
||
506 | |||
507 | return $data; |
||
508 | } |
||
509 | |||
510 | //Setup VIM: ex: et ts=4 : |
||
511 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.