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