aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/inc/File/Resolver.php
blob: bb0c10b6a288260effd1a642ecc4bea236701adc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php

namespace dokuwiki\File;

/**
 * Resolving relative IDs to absolute ones
 */
abstract class Resolver
{
    /** @var string context page ID */
    protected $contextID;
    /** @var string namespace of context page ID */
    protected $contextNS;

    /**
     * @param string $contextID the current pageID that's the context to resolve relative IDs to
     */
    public function __construct($contextID)
    {
        $this->contextID = $contextID;
        $this->contextNS = (string)getNS($contextID);
    }

    /**
     * Resolves a given ID to be absolute
     *
     * @param string $id The ID to resolve
     * @param string|int|false $rev The revision time to use when resolving
     * @param bool $isDateAt Is the given revision only a datetime hint not an exact revision?
     * @return string
     */
    public function resolveId($id, $rev = '', $isDateAt = false)
    {
        global $conf;

        // some pre cleaning for useslash:
        if ($conf['useslash']) $id = str_replace('/', ':', $id);
        // on some systems, semicolons might be used instead of colons:
        $id = str_replace(';', ':', $id);

        $id = $this->resolvePrefix($id);
        return $this->resolveRelatives($id);
    }

    /**
     * Handle IDs starting with . or ~ and prepend the proper prefix
     *
     * @param string $id
     * @return string
     */
    protected function resolvePrefix($id)
    {
        if ($id === '') return $id;

        // relative to current page (makes the current page a start page)
        if ($id[0] === '~') {
            $id = $this->contextID . ':' . substr($id, 1);
        }

        // relative to current namespace
        if ($id[0] === '.') {
            // normalize initial dots without a colon
            $id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id);
            $id = $this->contextNS . ':' . $id;
        }

        // auto-relative, because there is a context namespace but no namespace in the ID
        if ($this->contextID !== '' && strpos($id, ':') === false) {
            $id = $this->contextNS . ':' . $id;
        }

        return $id;
    }

    /**
     * Handle . and .. within IDs
     *
     * @param string $id
     * @return string
     */
    protected function resolveRelatives($id)
    {
        $id = rtrim($id, '.'); // trailing dots are invalid
        if ($id === '') return '';
        $trail = ($id[-1] === ':') ? ':' : ''; // keep trailing colon

        $result = [];
        $parts = explode(':', $id);

        foreach ($parts as $dir) {
            if ($dir === '.') continue;
            if ($dir === '') continue;
            if ($dir === '..') {
                array_pop($result);
                continue;
            }
            $result[] = $dir;
        }

        $id = implode(':', $result);
        $id .= $trail;

        return $id;
    }
}