Failed Conditions
Push — mster2psr2 ( d855cb )
by Michael
03:13
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\Cache\Cache;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Cache.

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