Passed
Push — master ( 605f3e...8a46f0 )
by Mark
02:07
created

action.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * DokuSIOC - SIOC plugin for DokuWiki
4
 *
5
 * DokuSIOC integrates the SIOC ontology within DokuWiki and provides an
6
 * alternate RDF/XML views of the wiki documents.
7
 *
8
 * For DokuWiki we can't use the Triplify script because DokuWiki has not a RDBS
9
 * backend. But the wiki API provides enough methods to get the data out, so
10
 * DokuSIOC as a plugin uses the export hook to provide accessible data as
11
 * RDF/XML, using the SIOC ontology as vocabulary.
12
 * @copyright 2009 Michael Haschke
13
 * @copyright 2020 mprins
14
 * LICENCE
15
 *
16
 * This program is free software: you can redistribute it and/or modify it under
17
 * the terms of the GNU General Public License as published by the Free Software
18
 * Foundation, version 2 of the License.
19
 *
20
 * This program is distributed in the hope that it will be useful, but WITHOUT
21
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
23
 *
24
 * @link      http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU General Public License 2.0 (GPLv2)
25
 *
26
 */
27
28
class action_plugin_dokusioc extends DokuWiki_Action_Plugin
29
{
30
31
    private $agentlink = 'http://eye48.com/go/dokusioc?v=0.1.2';
32
33
    /**
34
     * Register it's handlers with the DokuWiki's event controller
35
     */
36
    public function register(Doku_Event_Handler $controller): void
37
    {
38
        //print_r(headers_list()); die();
39
        // test the requested action
40
        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'checkAction', $controller);
41
    }
42
43
    /* -- Event handlers ---------------------------------------------------- */
44
45
    public function checkAction($action, $controller)
46
    {
47
        global $INFO;
48
        //print_r($INFO); die();
49
        //print_r(headers_list()); die();
50
51
        if ($action->data === 'export_siocxml') {
52
            // give back rdf
53
            $this->exportSioc();
54
        } elseif (($action->data === 'show' || $action->data === 'index') && $INFO['perm'] && !defined(
55
                'DOKU_MEDIADETAIL'
56
            ) && ($INFO['exists'] || getDwUserInfo($INFO['id'], $this)) && !isHiddenPage($INFO['id'])) {
57
            if ($this->isRdfXmlRequest()) {
58
                // forward to rdfxml document if requested
59
                // print_r(headers_list()); die();
60
                $location = $this->createRdfLink();
61
                if (function_exists('header_remove')) {
62
                    header_remove();
63
                }
64
                header('Location: ' . $location['href'], true, 303);
65
                exit();
66
            } else {
67
                // add meta link to html head
68
                $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'createRdfLink');
69
            }
70
        }
71
        /*
72
        else
73
        {
74
            print_r(array($action->data, $INFO['perm'], defined('DOKU_MEDIADETAIL'), $INFO['exists'],
75
                    getDwUserInfo($INFO['id'],$this), isHiddenPage($INFO['id'])));
76
            die();
77
        }
78
        */
79
    }
80
81
    public function exportSioc()
82
    {
83
        global $ID, $INFO;
84
85
        if (isHiddenPage($ID)) {
86
            $this->exit("HTTP/1.0 404 Not Found");
87
        }
88
89
        $sioc_type = $this->getContenttype();
90
91
        // Test for valid types
92
        if (!(($sioc_type == 'post' && $INFO['exists']) || $sioc_type == 'user' || $sioc_type == 'container')) {
93
            $this->exit("HTTP/1.0 404 Not Found");
94
        }
95
96
        // Test for permission
97
        if (!$INFO['perm']) {
98
            // not enough rights to see the wiki page
99
            $this->exit("HTTP/1.0 401 Unauthorized");
100
        }
101
102
        // Forward to URI with explicit type attribut
103
        if (!isset($_GET['type'])) {
104
            header('Location:' . $_SERVER['REQUEST_URI'] . '&type=' . $sioc_type, true, 302);
105
        }
106
107
        // Include SIOC libs
108
        require_once(__DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sioc_inc.php');
109
        require_once(__DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sioc_dokuwiki.php');
110
111
        // Create exporter
112
113
        $rdf              = new SIOCExporter();
114
        $rdf->profile_url = normalizeUri(
115
            getAbsUrl(
116
                exportlink(
117
                    $ID,
118
                    'siocxml',
119
                    array('type' => $sioc_type),
120
                    false,
121
                    '&'
122
                )
123
            )
124
        );
125
        $rdf->setURLParameters('type', 'id', 'page', false);
126
127
        // Create SIOC-RDF content
128
129
        switch ($sioc_type) {
130
            case 'container':
131
                $rdf = $this->exportContainercontent($rdf);
132
                break;
133
134
            case 'user':
135
                $rdf = $this->exportUsercontent($rdf);
136
                break;
137
138
            case 'post':
139
            default:
140
                $rdf = $this->exportPostcontent($rdf);
141
                break;
142
        }
143
144
        // export
145
        if ($this->getConf('noindx')) {
146
            header("X-Robots-Tag: noindex", true);
147
        }
148
        $rdf->export();
149
150
        //print_r(headers_list()); die();
151
        die();
152
    }
153
154
    private function exit($headermsg)
155
    {
156
        header($headermsg);
157
        die();
158
    }
159
160
    /* -- public class methods ---------------------------------------------- */
161
162
    private function getContenttype()
163
    {
164
        global $ID, $conf;
165
166
        // check for type if unknown
167
        if (!($_GET['type'] ?? "")) {
168
            $userinfo = getDwUserInfo($ID, $this);
169
170
            if ($userinfo) {
171
                $type = 'user';
172
            } elseif (isset($_GET['do']) && $_GET['do'] == 'index') {
173
                $type = 'container';
174
            } else {
175
                $type = 'post';
176
            }
177
        } else {
178
            $type = $_GET['type'];
179
        }
180
181
        return $type;
182
    }
183
184
    private function exportContainercontent($exporter)
185
    {
186
        global $ID, $INFO, $conf;
187
188
        if ($ID == $conf['start']) {
189
            $title = $conf['title'];
190
        } elseif (isset($INFO['meta']['title'])) {
191
            $title = $INFO['meta']['title'];
192
        } else {
193
            $title = $ID;
194
        }
195
196
        $exporter->setParameters(
197
            'Container: ' . $title,
198
            getAbsUrl(),
199
            getAbsUrl() . 'doku.php?',
200
            'utf-8',
201
            $this->agentlink
202
        );
203
204
        // create container object
205
        $wikicontainer = new SIOCDokuWikiContainer(
206
            $ID, normalizeUri($exporter->siocURL('container', $ID))
207
        );
208
209
        /* container is type=wiki */
210
        if ($ID == $conf['start']) {
211
            $wikicontainer->isWiki();
212
        }
213
        /* sioc:name              */
214
        if ($INFO['exists']) {
215
            $wikicontainer->addTitle($INFO['meta']['title']);
216
        }
217
        /* has_parent             */
218
        if ($INFO['namespace']) {
219
            $wikicontainer->addParent($INFO['namespace']);
220
        }
221
222
        // search next level entries (posts, sub containers) in container
223
        require_once(DOKU_INC . 'inc/search.php');
224
        $dir        = utf8_encodeFN(str_replace(':', '/', $ID));
225
        $entries    = array();
226
        $posts      = array();
227
        $containers = array();
228
        search($entries, $conf['datadir'], 'search_index', array('ns' => $ID), $dir);
229
        foreach ($entries as $entry) {
230
            if ($entry['type'] === 'f') {
231
                // wikisite
232
                $posts[] = $entry;
233
            } elseif ($entry['type'] === 'd') {
234
                // sub container
235
                $containers[] = $entry;
236
            }
237
        }
238
239
        // without sub content it can't be a container (so it does not exist as a container)
240
        if (count($posts) + count($containers) == 0) {
241
            $this->exit("HTTP/1.0 404 Not Found");
242
        }
243
244
        if (count($posts) > 0) {
245
            $wikicontainer->addArticles($posts);
246
        }
247
        if (count($containers) > 0) {
248
            $wikicontainer->addContainers($containers);
249
        }
250
251
        //print_r($containers);die();
252
253
        // add container to exporter
254
        $exporter->addObject($wikicontainer);
255
256
        return $exporter;
257
    }
258
259
    /* -- private helpers --------------------------------------------------- */
260
261
    private function exportUsercontent($exporter)
262
    {
263
        global $ID;
264
265
        // get user info
266
        $userinfo = getDwUserInfo($ID, $this);
267
268
        // no userinfo means there is no user space or user does not exists
269
        if ($userinfo === false) {
270
            $this->exit("HTTP/1.0 404 Not Found");
271
        }
272
273
        $exporter->setParameters(
274
            'Account: ' . $userinfo['name'],
275
            getAbsUrl(),
276
            getAbsUrl() . 'doku.php?',
277
            'utf-8',
278
            $this->agentlink
279
        );
280
        // create user object
281
        //print_r($userinfo); die();
282
        // $id, $url, $userid, $name, $email
283
        $wikiuser = new SIOCDokuWikiUser(
284
            $ID, normalizeUri($exporter->siocURL('user', $ID)), $userinfo['user'], $userinfo['name'], $userinfo['mail']
285
        );
286
        /* TODO: avatar (using Gravatar) */ /* TODO: creator_of */
287
        // add user to exporter
288
        $exporter->addObject($wikiuser);
289
290
        //print_r(headers_list());die();
291
        return $exporter;
292
    }
293
294
    private function exportPostcontent($exporter)
295
    {
296
        global $ID, $INFO, $REV, $conf;
297
298
        $exporter->setParameters(
299
            $INFO['meta']['title'] . ($REV ? ' (rev ' . $REV . ')' : ''),
300
            $this->getDokuUrl(),
301
            $this->getDokuUrl() . 'doku.php?',
302
            'utf-8',
303
            $this->agentlink
304
        );
305
306
        // create user object
307
        $dwuserpage_id = cleanID($this->getConf('userns')) . ($conf['useslash'] ? '/' : ':') . $INFO['editor'];
308
        // create wiki page object
309
        $wikipage = new SIOCDokuWikiArticle(
310
            $ID, // id
311
            normalizeUri(
312
                $exporter->siocURL(
313
                    'post',
314
                    $ID . ($REV ? $exporter->_urlseparator . 'rev' . $exporter->_urlequal . $REV : '')
315
                )
316
            ), // url
317
            $INFO['meta']['title'] . ($REV ? ' (rev ' . $REV . ')' : ''), // subject
318
            rawWiki($ID, $REV) // body (content)
319
        );
320
        /* encoded content   */
321
        $wikipage->addContentEncoded(p_cached_output(wikiFN($ID, $REV), 'xhtml'));
322
        /* created           */
323
        if (isset($INFO['meta']['date']['created'])) {
324
            $wikipage->addCreated(date('c', $INFO['meta']['date']['created']));
325
        }
326
        /* or modified       */
327
        if (isset($INFO['meta']['date']['modified'])) {
328
            $wikipage->addModified(date('c', $INFO['meta']['date']['modified']));
329
        }
330
        /* creator/modifier  */
331
        if ($INFO['editor'] && $this->getConf('userns')) {
332
            $wikipage->addCreator(array('foaf:maker' => '#' . $INFO['editor'], 'sioc:modifier' => $dwuserpage_id));
333
        }
334
        /* is creator        */
335
        if (isset($INFO['meta']['date']['created'])) {
336
            $wikipage->isCreator();
337
        }
338
        /* intern wiki links */
339
        $wikipage->addLinks($INFO['meta']['relation']['references']);
340
341
        // contributors - only for last revision b/c of wrong meta data for older revisions
342
        if (!$REV && $this->getConf('userns') && isset($INFO['meta']['contributor'])) {
343
            $cont_temp = array();
344
            $cont_ns   = $this->getConf('userns') . ($conf['useslash'] ? '/' : ':');
345
            foreach ($INFO['meta']['contributor'] as $cont_id => $cont_name) {
346
                $cont_temp[$cont_ns . $cont_id] = $cont_name;
347
            }
348
            $wikipage->addContributors($cont_temp);
349
        }
350
351
        // backlinks - only for last revision
352
        if (!$REV) {
353
            require_once(DOKU_INC . 'inc/fulltext.php');
354
            $backlinks = ft_backlinks($ID);
355
            if (count($backlinks) > 0) {
356
                $wikipage->addBacklinks($backlinks);
357
            }
358
        }
359
360
        // TODO: addLinksExtern
361
362
        /* previous and next revision */
363
        $changelog = new PageChangeLog($ID);
364
        $pagerevs  = $changelog->getRevisions(0, $conf['recent'] + 1);
365
        $prevrev   = false;
366
        $nextrev   = false;
367
        if (!$REV) {
368
            // latest revision, previous rev is on top in array
369
            $prevrev = 0;
370
        } else {
371
            // other revision
372
            $currentrev = array_search($REV, $pagerevs);
373
            if ($currentrev !== false) {
374
                $prevrev = $currentrev + 1;
375
                $nextrev = $currentrev - 1;
376
            }
377
        }
378
        if ($prevrev !== false && $prevrev > -1 && page_exists($ID, $pagerevs[$prevrev])) {
379
            /* previous revision*/
380
            $wikipage->addVersionPrevious($pagerevs[$prevrev]);
381
        }
382
        if ($nextrev !== false && $nextrev > -1 && page_exists($ID, $pagerevs[$nextrev])) {
383
            /* next revision*/
384
            $wikipage->addVersionNext($pagerevs[$nextrev]);
385
        }
386
387
        /* latest revision   */
388
        if ($REV) {
389
            $wikipage->addVersionLatest();
390
        }
391
        // TODO: topics
392
        /* has_container     */
393
        if ($INFO['namespace']) {
394
            $wikipage->addContainer($INFO['namespace']);
395
        }
396
        /* has_space         */
397
        if ($this->getConf('owners')) {
398
            $wikipage->addSite($this->getConf('owners'));
399
        }
400
        // TODO: dc:contributor / has_modifier
401
        // TODO: attachment (e.g. pictures in that dwns)
402
403
        // add wiki page to exporter
404
        $exporter->addObject($wikipage);
405
        //if ($INFO['editor'] && $this->getConf('userns')) $exporter->addObject($pageuser);
406
407
        return $exporter;
408
    }
409
410
    private function getDokuUrl($url = null)
411
    {
412
        return getAbsUrl($url);
413
    }
414
415
    public function isRdfXmlRequest(): bool
416
    {
417
        if (!isset($_SERVER['HTTP_ACCEPT']) return false;
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_RETURN on line 417 at column 44
Loading history...
418
        
419
        // get accepted types
420
        $http_accept = trim($_SERVER['HTTP_ACCEPT']);
421
422
        // save accepted types in array
423
        $accepted = explode(',', $http_accept);
424
425
        if ($this->getConf('softck') && strpos($_SERVER['HTTP_ACCEPT'], 'application/rdf+xml') !== false) {
426
            return true;
427
        }
428
429
        if (count($accepted) > 0) {
430
            // hard check, only serve RDF if it is requested first or equal to first type
431
432
            // extract accepting ratio
433
            $test_accept = array();
434
            foreach ($accepted as $format) {
435
                $formatspec = explode(';', $format);
436
                $k          = trim($formatspec[0]);
437
                if (count($formatspec) === 2) {
438
                    $test_accept[$k] = trim($formatspec[1]);
439
                } else {
440
                    $test_accept[$k] = 'q=1.0';
441
                }
442
            }
443
444
            // sort by ratio
445
            arsort($test_accept);
446
            $accepted_order = array_keys($test_accept);
447
448
            if ($accepted_order[0] === 'application/rdf+xml' || (array_key_exists(
449
                        'application/rdf+xml',
450
                        $test_accept
451
                    ) && $test_accept['application/rdf+xml'] === 'q=1.0')) {
452
                return true;
453
            }
454
        }
455
456
        // print_r($accepted_order);print_r($test_accept);die();
457
458
        return false;
459
    }
460
461
    /**
462
     */
463
    public function createRdfLink($event = null, $param = null)
464
    {
465
        global $ID, $INFO, $conf;
466
467
        // Test for hidden pages
468
469
        if (isHiddenPage($ID)) {
470
            return false;
471
        }
472
473
        // Get type of SIOC content
474
475
        $sioc_type = $this->getContenttype();
476
477
        // Test for valid types
478
479
        if (!(($sioc_type === 'post' && $INFO['exists']) || $sioc_type === 'user' || $sioc_type === 'container')) {
480
            return false;
481
        }
482
483
        // Test for permission
484
485
        if (!$INFO['perm']) {
486
            // not enough rights to see the wiki page
487
            return false;
488
        }
489
490
        $userinfo = getDwUserInfo($ID, $this);
491
492
        // Create attributes for meta link
493
494
        $metalink['type'] = 'application/rdf+xml';
495
        $metalink['rel']  = 'meta';
496
497
        switch ($sioc_type) {
498
            case 'container':
499
                $title     = htmlentities(
500
                    "Container '" . ($INFO['meta']['title'] ?? $ID) . "' (SIOC document as RDF/XML)"
501
                );
502
                $queryAttr = array('type' => 'container');
503
                break;
504
505
            case 'user':
506
                $title     = htmlentities($userinfo['name']);
507
                $queryAttr = array('type' => 'user');
508
                break;
509
510
            case 'post':
511
            default:
512
                $title     = htmlentities($INFO['meta']['title'] ?? $ID);
513
                $queryAttr = array('type' => 'post');
514
                if (isset($_GET['rev']) && $_GET['rev'] === (int)$_GET['rev']) {
515
                    $queryAttr['rev'] = $_GET['rev'];
516
                }
517
                break;
518
        }
519
520
        $metalink['title'] = $title;
521
        $metalink['href']  = normalizeUri(getAbsUrl(exportlink($ID, 'siocxml', $queryAttr, false, '&')));
522
523
        if ($event !== null) {
524
            $event->data['link'][] = $metalink;
525
526
            // set canocial link for type URIs to prevent indexing double content
527
            if ($_GET['type'] ?? "") {
528
                $event->data['link'][] = array('rel' => 'canonical', 'href' => getAbsUrl(wl($ID)));
529
            }
530
        }
531
532
        return $metalink;
533
    }
534
}
535
536
// TODO cleanup and just have this unconditionally
537
if (!function_exists('getAbsUrl')) {
538
    /**
539
     * @param $url
540
     * @return string
541
     * @deprecated cleanup, use build-in function
542
     */
543
    function getAbsUrl($url = null): string
544
    {
545
        if ($url === null) {
546
            $url = DOKU_BASE;
547
        }
548
        return str_replace(DOKU_BASE, DOKU_URL, $url);
549
    }
550
}
551
552
if (!function_exists('getDwUserEmail')) {
553
    /**
554
     * @param $user
555
     * @return string
556
     * @deprecated not used, will be removed
557
     */
558
    function getDwUserEmail($user): string
559
    {
560
        global $auth;
561
        if ($info = $auth->getUserData($user)) {
562
            return $info['mail'];
563
        } else {
564
            return false;
565
        }
566
    }
567
}
568
569
if (!function_exists('getDwUserInfo')) {
570
    /**
571
     * @param $id
572
     * @param $pobj
573
     * @param $key
574
     * @return array|false
575
     * @deprecated cleanup, use build-in function
576
     */
577
    function getDwUserInfo($id, $pobj, $key = null)
578
    {
579
        global $auth, $conf;
580
581
        if (!$pobj->getConf('userns')) {
582
            return false;
583
        }
584
585
        // get user id
586
        $userid = str_replace(cleanID($pobj->getConf('userns')) . ($conf['useslash'] ? '/' : ':'), '', $id);
587
588
        if ($info = $auth->getUserData($userid)) {
589
            if ($key) {
590
                return $info['key'];
591
            } else {
592
                return $info;
593
            }
594
        } else {
595
            return false;
596
        }
597
    }
598
}
599
600
// sort query attributes by name
601
if (!function_exists('normalizeUri')) {
602
    /**
603
     * @param $uri
604
     * @return string
605
     * @deprecated cleanup, use build-in function
606
     */
607
    function normalizeUri($uri): string
608
    {
609
        // part URI
610
        $parts = explode('?', $uri);
611
612
        // part query
613
        if (isset($parts[1])) {
614
            $query = $parts[1];
615
616
            // test separator
617
            $sep = '&';
618
            if (strpos($query, '&amp;') !== false) {
619
                $sep = '&amp;';
620
            }
621
            $attr = explode($sep, $query);
622
623
            sort($attr);
624
625
            $parts[1] = implode($sep, $attr);
626
        }
627
628
        return implode('?', $parts);
629
    }
630
}
631
632