Completed
Push — psr2 ( 1f4834...b196d8 )
by Andreas
06:14 queued 03:30
created

Upgrade to new PHP Analysis Engine

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
use dokuwiki\ChangeLog\MediaChangeLog;
0 ignored issues
show
This use statement conflicts with another class in this namespace, MediaChangeLog.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
13
use dokuwiki\ChangeLog\PageChangeLog;
0 ignored issues
show
This use statement conflicts with another class in this namespace, PageChangeLog.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
14
15
if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/');
16
require_once(DOKU_INC.'inc/init.php');
17
18
//close session
19
session_write_close();
20
21
//feed disabled?
22
if(!actionOK('rss')) {
23
    http_status(404);
24
    echo '<error>RSS feed is disabled.</error>';
25
    exit;
26
}
27
28
// get params
29
$opt = rss_parseOptions();
30
31
// the feed is dynamic - we need a cache for each combo
32
// (but most people just use the default feed so it's still effective)
33
$key   = join('', array_values($opt)).'$'.$_SERVER['REMOTE_USER'].'$'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'];
34
$cache = new cache($key, '.feed');
35
36
// prepare cache depends
37
$depends['files'] = getConfigFiles('main');
38
$depends['age']   = $conf['rss_update'];
39
$depends['purge'] = $INPUT->bool('purge');
40
41
// check cacheage and deliver if nothing has changed since last
42
// time or the update interval has not passed, also handles conditional requests
43
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
44
header('Pragma: public');
45
header('Content-Type: application/xml; charset=utf-8');
46
header('X-Robots-Tag: noindex');
47
if($cache->useCache($depends)) {
48
    http_conditionalRequest($cache->_time);
49
    if($conf['allowdebug']) header("X-CacheUsed: $cache->cache");
50
    print $cache->retrieveCache();
51
    exit;
52
} else {
53
    http_conditionalRequest(time());
54
}
55
56
// create new feed
57
$rss                 = new UniversalFeedCreator();
58
$rss->title          = $conf['title'].(($opt['namespace']) ? ' '.$opt['namespace'] : '');
59
$rss->link           = DOKU_URL;
60
$rss->syndicationURL = DOKU_URL.'feed.php';
61
$rss->cssStyleSheet  = DOKU_URL.'lib/exe/css.php?s=feed';
0 ignored issues
show
The property cssStyleSheet does not seem to exist in UniversalFeedCreator.

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.

Loading history...
62
63
$image        = new FeedImage();
64
$image->title = $conf['title'];
65
$image->url   = tpl_getMediaFile(array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico'), true);
66
$image->link  = DOKU_URL;
67
$rss->image   = $image;
68
69
$data  = null;
70
$modes = array(
71
    'list'   => 'rssListNamespace',
72
    'search' => 'rssSearch',
73
    'recent' => 'rssRecentChanges'
74
);
75
if(isset($modes[$opt['feed_mode']])) {
76
    $data = $modes[$opt['feed_mode']]($opt);
77
} else {
78
    $eventData = array(
79
        'opt'  => &$opt,
80
        'data' => &$data,
81
    );
82
    $event     = new Doku_Event('FEED_MODE_UNKNOWN', $eventData);
83
    if($event->advise_before(true)) {
84
        echo sprintf('<error>Unknown feed mode %s</error>', hsc($opt['feed_mode']));
85
        exit;
86
    }
87
    $event->advise_after();
88
}
89
90
rss_buildItems($rss, $data, $opt);
91
$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 @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
92
93
// save cachefile
94
$cache->storeCache($feed);
95
96
// finally deliver
97
print $feed;
98
99
// ---------------------------------------------------------------- //
100
101
/**
102
 * Get URL parameters and config options and return an initialized option array
103
 *
104
 * @author Andreas Gohr <[email protected]>
105
 */
106
function rss_parseOptions() {
107
    global $conf;
108
    global $INPUT;
109
110
    $opt = array();
111
112
    foreach(array(
113
                // Basic feed properties
114
                // Plugins may probably want to add new values to these
115
                // properties for implementing own feeds
116
117
                // One of: list, search, recent
118
                'feed_mode'    => array('str', 'mode', 'recent'),
119
                // One of: diff, page, rev, current
120
                'link_to'      => array('str', 'linkto', $conf['rss_linkto']),
121
                // One of: abstract, diff, htmldiff, html
122
                'item_content' => array('str', 'content', $conf['rss_content']),
123
124
                // Special feed properties
125
                // These are only used by certain feed_modes
126
127
                // String, used for feed title, in list and rc mode
128
                'namespace'    => array('str', 'ns', null),
129
                // Positive integer, only used in rc mode
130
                'items'        => array('int', 'num', $conf['recent']),
131
                // Boolean, only used in rc mode
132
                'show_minor'   => array('bool', 'minor', false),
133
                // String, only used in list mode
134
                'sort'         => array('str', 'sort', 'natural'),
135
                // String, only used in search mode
136
                'search_query' => array('str', 'q', null),
137
                // One of: pages, media, both
138
                'content_type' => array('str', 'view', $conf['rss_media'])
139
140
            ) as $name => $val) {
141
        $opt[$name] = $INPUT->{$val[0]}($val[1], $val[2], true);
142
    }
143
144
    $opt['items']      = max(0, (int) $opt['items']);
145
    $opt['show_minor'] = (bool) $opt['show_minor'];
146
    $opt['sort'] = valid_input_set('sort', array('default' => 'natural', 'date'), $opt);
147
148
    $opt['guardmail'] = ($conf['mailguard'] != '' && $conf['mailguard'] != 'none');
149
150
    $type = $INPUT->valid(
151
        'type',
152
        array( 'rss', 'rss2', 'atom', 'atom1', 'rss1'),
153
        $conf['rss_type']
154
    );
155
    switch($type) {
156
        case 'rss':
157
            $opt['feed_type'] = 'RSS0.91';
158
            $opt['mime_type'] = 'text/xml';
159
            break;
160
        case 'rss2':
161
            $opt['feed_type'] = 'RSS2.0';
162
            $opt['mime_type'] = 'text/xml';
163
            break;
164
        case 'atom':
165
            $opt['feed_type'] = 'ATOM0.3';
166
            $opt['mime_type'] = 'application/xml';
167
            break;
168
        case 'atom1':
169
            $opt['feed_type'] = 'ATOM1.0';
170
            $opt['mime_type'] = 'application/atom+xml';
171
            break;
172
        default:
173
            $opt['feed_type'] = 'RSS1.0';
174
            $opt['mime_type'] = 'application/xml';
175
    }
176
177
    $eventData = array(
178
        'opt' => &$opt,
179
    );
180
    trigger_event('FEED_OPTS_POSTPROCESS', $eventData);
181
    return $opt;
182
}
183
184
/**
185
 * Add recent changed pages to a feed object
186
 *
187
 * @author Andreas Gohr <[email protected]>
188
 * @param  FeedCreator $rss the FeedCreator Object
189
 * @param  array       $data the items to add
190
 * @param  array       $opt  the feed options
191
 */
192
function rss_buildItems(&$rss, &$data, $opt) {
193
    global $conf;
194
    global $lang;
195
    /* @var DokuWiki_Auth_Plugin $auth */
196
    global $auth;
197
198
    $eventData = array(
199
        'rss'  => &$rss,
200
        'data' => &$data,
201
        'opt'  => &$opt,
202
    );
203
    $event     = new Doku_Event('FEED_DATA_PROCESS', $eventData);
204
    if($event->advise_before(false)) {
205
        foreach($data as $ditem) {
206
            if(!is_array($ditem)) {
207
                // not an array? then only a list of IDs was given
208
                $ditem = array('id' => $ditem);
209
            }
210
211
            $item = new FeedItem();
212
            $id   = $ditem['id'];
213
            if(!$ditem['media']) {
214
                $meta = p_get_metadata($id);
215
            } else {
216
                $meta = array();
217
            }
218
219
            // add date
220
            if($ditem['date']) {
221
                $date = $ditem['date'];
222
            } elseif ($ditem['media']) {
223
                $date = @filemtime(mediaFN($id));
224
            } elseif (file_exists(wikiFN($id))) {
225
                $date = @filemtime(wikiFN($id));
226
            } elseif($meta['date']['modified']) {
227
                $date = $meta['date']['modified'];
228
            } else {
229
                $date = 0;
230
            }
231
            if($date) $item->date = date('r', $date);
232
233
            // add title
234
            if($conf['useheading'] && $meta['title']) {
235
                $item->title = $meta['title'];
236
            } else {
237
                $item->title = $ditem['id'];
238
            }
239
            if($conf['rss_show_summary'] && !empty($ditem['sum'])) {
240
                $item->title .= ' - '.strip_tags($ditem['sum']);
241
            }
242
243
            // add item link
244
            switch($opt['link_to']) {
245
                case 'page':
246
                    if($ditem['media']) {
247
                        $item->link = media_managerURL(
248
                            array(
249
                                 'image' => $id,
250
                                 'ns'    => getNS($id),
251
                                 'rev'   => $date
252
                            ), '&', true
253
                        );
254
                    } else {
255
                        $item->link = wl($id, 'rev='.$date, true, '&');
256
                    }
257
                    break;
258
                case 'rev':
259
                    if($ditem['media']) {
260
                        $item->link = media_managerURL(
261
                            array(
262
                                 'image'       => $id,
263
                                 'ns'          => getNS($id),
264
                                 'rev'         => $date,
265
                                 'tab_details' => 'history'
266
                            ), '&', true
267
                        );
268
                    } else {
269
                        $item->link = wl($id, 'do=revisions&rev='.$date, true, '&');
270
                    }
271
                    break;
272
                case 'current':
273
                    if($ditem['media']) {
274
                        $item->link = media_managerURL(
275
                            array(
276
                                 'image' => $id,
277
                                 'ns'    => getNS($id)
278
                            ), '&', true
279
                        );
280
                    } else {
281
                        $item->link = wl($id, '', true, '&');
282
                    }
283
                    break;
284
                case 'diff':
285
                default:
286
                    if($ditem['media']) {
287
                        $item->link = media_managerURL(
288
                            array(
289
                                 'image'       => $id,
290
                                 'ns'          => getNS($id),
291
                                 'rev'         => $date,
292
                                 'tab_details' => 'history',
293
                                 'mediado'     => 'diff'
294
                            ), '&', true
295
                        );
296
                    } else {
297
                        $item->link = wl($id, 'rev='.$date.'&do=diff', true, '&');
298
                    }
299
            }
300
301
            // add item content
302
            switch($opt['item_content']) {
303
                case 'diff':
304
                case 'htmldiff':
305
                    if($ditem['media']) {
306
                        $medialog = new MediaChangeLog($id);
307
                        $revs  = $medialog->getRevisions(0, 1);
308
                        $rev   = $revs[0];
309
                        $src_r = '';
310
                        $src_l = '';
311
312
                        if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)), 300)) {
313
                            $more  = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
314
                            $src_r = ml($id, $more, true, '&amp;', true);
315
                        }
316
                        if($rev && $size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)), 300)) {
317
                            $more  = 'rev='.$rev.'&w='.$size[0].'&h='.$size[1];
318
                            $src_l = ml($id, $more, true, '&amp;', true);
319
                        }
320
                        $content = '';
321
                        if($src_r) {
322
                            $content = '<table>';
323
                            $content .= '<tr><th width="50%">'.$rev.'</th>';
324
                            $content .= '<th width="50%">'.$lang['current'].'</th></tr>';
325
                            $content .= '<tr align="center"><td><img src="'.$src_l.'" alt="" /></td><td>';
326
                            $content .= '<img src="'.$src_r.'" alt="'.$id.'" /></td></tr>';
327
                            $content .= '</table>';
328
                        }
329
330
                    } else {
331
                        require_once(DOKU_INC.'inc/DifferenceEngine.php');
332
                        $pagelog = new PageChangeLog($id);
333
                        $revs = $pagelog->getRevisions(0, 1);
334
                        $rev  = $revs[0];
335
336
                        if($rev) {
337
                            $df = new Diff(explode("\n", rawWiki($id, $rev)),
338
                                           explode("\n", rawWiki($id, '')));
339
                        } else {
340
                            $df = new Diff(array(''),
341
                                           explode("\n", rawWiki($id, '')));
342
                        }
343
344
                        if($opt['item_content'] == 'htmldiff') {
345
                            // note: no need to escape diff output, TableDiffFormatter provides 'safe' html
346
                            $tdf     = new TableDiffFormatter();
347
                            $content = '<table>';
348
                            $content .= '<tr><th colspan="2" width="50%">'.$rev.'</th>';
349
                            $content .= '<th colspan="2" width="50%">'.$lang['current'].'</th></tr>';
350
                            $content .= $tdf->format($df);
351
                            $content .= '</table>';
352
                        } else {
353
                            // note: diff output must be escaped, UnifiedDiffFormatter provides plain text
354
                            $udf     = new UnifiedDiffFormatter();
355
                            $content = "<pre>\n".hsc($udf->format($df))."\n</pre>";
356
                        }
357
                    }
358
                    break;
359
                case 'html':
360
                    if($ditem['media']) {
361
                        if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
362
                            $more    = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
363
                            $src     = ml($id, $more, true, '&amp;', true);
364
                            $content = '<img src="'.$src.'" alt="'.$id.'" />';
365
                        } else {
366
                            $content = '';
367
                        }
368
                    } else {
369
                        if (@filemtime(wikiFN($id)) === $date) {
370
                            $content = p_wiki_xhtml($id, '', false);
371
                        } else {
372
                            $content = p_wiki_xhtml($id, $date, false);
373
                        }
374
                        // no TOC in feeds
375
                        $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s', '', $content);
376
377
                        // add alignment for images
378
                        $content = preg_replace('/(<img .*?class="medialeft")/s', '\\1 align="left"', $content);
379
                        $content = preg_replace('/(<img .*?class="mediaright")/s', '\\1 align="right"', $content);
380
381
                        // make URLs work when canonical is not set, regexp instead of rerendering!
382
                        if(!$conf['canonical']) {
383
                            $base    = preg_quote(DOKU_REL, '/');
384
                            $content = preg_replace('/(<a href|<img src)="('.$base.')/s', '$1="'.DOKU_URL, $content);
385
                        }
386
                    }
387
388
                    break;
389
                case 'abstract':
390
                default:
391
                    if($ditem['media']) {
392
                        if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
393
                            $more    = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
394
                            $src     = ml($id, $more, true, '&amp;', true);
395
                            $content = '<img src="'.$src.'" alt="'.$id.'" />';
396
                        } else {
397
                            $content = '';
398
                        }
399
                    } else {
400
                        $content = $meta['description']['abstract'];
401
                    }
402
            }
403
            $item->description = $content; //FIXME a plugin hook here could be senseful
404
405
            // add user
406
            # FIXME should the user be pulled from metadata as well?
407
            $user         = @$ditem['user']; // the @ spares time repeating lookup
408
            if(blank($user)) {
409
                $item->author = 'Anonymous';
410
                $item->authorEmail = '[email protected]';
411
            } else {
412
                $item->author = $user;
413
                $item->authorEmail = $user . '@undisclosed.example.com';
414
415
                // get real user name if configured
416
                if($conf['useacl'] && $auth) {
417
                    $userInfo = $auth->getUserData($user);
418
                    if($userInfo) {
419
                        switch($conf['showuseras']) {
420
                            case 'username':
421
                            case 'username_link':
422
                                $item->author = $userInfo['name'];
423
                                break;
424
                            default:
425
                                $item->author = $user;
426
                                break;
427
                        }
428
                    } else {
429
                        $item->author = $user;
430
                    }
431
                }
432
            }
433
434
            // add category
435
            if(isset($meta['subject'])) {
436
                $item->category = $meta['subject'];
437
            } else {
438
                $cat = getNS($id);
439
                if($cat) $item->category = $cat;
0 ignored issues
show
Bug Best Practice introduced by
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 ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
440
            }
441
442
            // finally add the item to the feed object, after handing it to registered plugins
443
            $evdata = array(
444
                'item'  => &$item,
445
                'opt'   => &$opt,
446
                'ditem' => &$ditem,
447
                'rss'   => &$rss
448
            );
449
            $evt    = new Doku_Event('FEED_ITEM_ADD', $evdata);
450
            if($evt->advise_before()) {
451
                $rss->addItem($item);
452
            }
453
            $evt->advise_after(); // for completeness
454
        }
455
    }
456
    $event->advise_after();
457
}
458
459
/**
460
 * Add recent changed pages to the feed object
461
 *
462
 * @author Andreas Gohr <[email protected]>
463
 */
464
function rssRecentChanges($opt) {
465
    global $conf;
466
    $flags = RECENTS_SKIP_DELETED;
467
    if(!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS;
468
    if($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES;
469
    if($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED;
470
471
    $recents = getRecents(0, $opt['items'], $opt['namespace'], $flags);
472
    return $recents;
473
}
474
475
/**
476
 * Add all pages of a namespace to the feed object
477
 *
478
 * @author Andreas Gohr <[email protected]>
479
 */
480
function rssListNamespace($opt) {
481
    require_once(DOKU_INC.'inc/search.php');
482
    global $conf;
483
484
    $ns = ':'.cleanID($opt['namespace']);
485
    $ns = utf8_encodeFN(str_replace(':', '/', $ns));
486
487
    $data = array();
488
    $search_opts = array(
489
        'depth' => 1,
490
        'pagesonly' => true,
491
        'listfiles' => true
492
    );
493
    search($data, $conf['datadir'], 'search_universal', $search_opts, $ns, $lvl = 1, $opt['sort']);
494
495
    return $data;
496
}
497
498
/**
499
 * Add the result of a full text search to the feed object
500
 *
501
 * @author Andreas Gohr <[email protected]>
502
 */
503
function rssSearch($opt) {
504
    if(!$opt['search_query']) return array();
505
506
    require_once(DOKU_INC.'inc/fulltext.php');
507
    $data = ft_pageSearch($opt['search_query'], $poswords);
508
    $data = array_keys($data);
509
510
    return $data;
511
}
512
513
//Setup VIM: ex: et ts=4 :
514