1<?php
2
3namespace dokuwiki\Feed;
4
5use Diff;
6use dokuwiki\ChangeLog\PageChangeLog;
7use TableDiffFormatter;
8use UnifiedDiffFormatter;
9
10/**
11 * Accept more or less arbitrary data to represent a page and provide lazy loading accessors
12 * to all the data we need for feed generation.
13 */
14class FeedPageProcessor extends FeedItemProcessor
15{
16    /** @var array[] metadata */
17    protected $meta;
18
19    // region data processors
20
21    /** @inheritdoc */
22    public function getURL($linkto)
23    {
24        switch ($linkto) {
25            case 'page':
26                $opt = ['rev' => $this->getRev()];
27                break;
28            case 'rev':
29                $opt = ['rev' => $this->getRev(), 'do' => 'revisions'];
30                break;
31            case 'current':
32                $opt = [];
33                break;
34            case 'diff':
35            default:
36                $opt = ['rev' => $this->getRev(), 'do' => 'diff'];
37        }
38
39        return wl($this->getId(), $opt, true, '&');
40    }
41
42    /** @inheritdoc */
43    public function getBody($content)
44    {
45        global $lang;
46
47        switch ($content) {
48            case 'diff':
49                $diff = $this->getDiff();
50                // note: diff output must be escaped, UnifiedDiffFormatter provides plain text
51                $udf = new UnifiedDiffFormatter();
52                return "<pre>\n" . hsc($udf->format($diff)) . "\n</pre>";
53
54            case 'htmldiff':
55                $diff = $this->getDiff();
56                // note: no need to escape diff output, TableDiffFormatter provides 'safe' html
57                $tdf = new TableDiffFormatter();
58                $content = '<table>';
59                $content .= '<tr><th colspan="2" width="50%">' . dformat($this->getPrev()) . '</th>';
60                $content .= '<th colspan="2" width="50%">' . $lang['current'] . '</th></tr>';
61                $content .= $tdf->format($diff);
62                $content .= '</table>';
63                return $content;
64
65            case 'html':
66                if ($this->isExisting()) {
67                    $html = p_wiki_xhtml($this->getId(), '', false);
68                } else {
69                    $html = p_wiki_xhtml($this->getId(), $this->getRev(), false);
70                }
71                return $this->cleanHTML($html);
72
73            case 'abstract':
74            default:
75                return $this->getAbstract();
76        }
77    }
78
79    /** @inheritdoc */
80    public function getCategory()
81    {
82        $meta = $this->getMetaData();
83        return (array)($meta['subject'] ?? (string)getNS($this->getId()));
84    }
85
86    // endregion
87
88    // region data accessors
89
90    /**
91     * Get the page abstract
92     *
93     * @return string
94     */
95    public function getAbstract()
96    {
97        if (!isset($this->data['abstract'])) {
98            $meta = $this->getMetaData();
99            if (isset($meta['description']['abstract'])) {
100                $this->data['abstract'] = (string)$meta['description']['abstract'];
101            } else {
102                $this->data['abstract'] = '';
103            }
104        }
105        return $this->data['abstract'];
106    }
107
108    /** @inheritdoc */
109    public function getRev()
110    {
111        $rev = parent::getRev();
112        if ($rev) return $rev;
113
114        if (page_exists($this->id)) {
115            $this->data['rev'] = filemtime(wikiFN($this->id));
116            $this->data['exists'] = true;
117        } else {
118            $this->loadRevisions();
119        }
120        return $this->data['rev'];
121    }
122
123    /**
124     * Get the previous revision timestamp of this page
125     *
126     * @return int|null The previous revision or null if there is none
127     */
128    public function getPrev()
129    {
130        if ($this->data['prev'] ?? 0) return $this->data['prev'];
131        $this->loadRevisions();
132        return $this->data['prev'];
133    }
134
135    /**
136     * Does this page exist?
137     *
138     * @return bool
139     */
140    public function isExisting()
141    {
142        if (!isset($this->data['exists'])) {
143            $this->data['exists'] = page_exists($this->id);
144        }
145        return $this->data['exists'];
146    }
147
148    /**
149     * Get the title of this page
150     *
151     * @return string
152     */
153    public function getTitle()
154    {
155        global $conf;
156        if (!isset($this->data['title'])) {
157            if ($conf['useheading']) {
158                $this->data['title'] = p_get_first_heading($this->id);
159            } else {
160                $this->data['title'] = noNS($this->id);
161            }
162        }
163        return $this->data['title'];
164    }
165
166    // endregion
167
168    /**
169     * Get the metadata of this page
170     *
171     * @return array[]
172     */
173    protected function getMetaData()
174    {
175        if (!isset($this->meta)) {
176            $this->meta = (array)p_get_metadata($this->id);
177        }
178        return $this->meta;
179    }
180
181    /**
182     * Load the current and previous revision from the changelog
183     * @return void
184     */
185    protected function loadRevisions()
186    {
187        $changelog = new PageChangeLog($this->id);
188        $revs = $changelog->getRevisions(0, 2); // FIXME check that this returns the current one correctly
189        if (!isset($this->data['rev'])) {
190            // prefer an already set date, only set if missing
191            // it should usally not happen that neither is available
192            $this->data['rev'] = $revs[0] ?? 0;
193        }
194        // a previous revision might not exist
195        $this->data['prev'] = $revs[1] ?? null;
196    }
197
198    /**
199     * Get a diff between this and the previous revision
200     *
201     * @return Diff
202     */
203    protected function getDiff()
204    {
205        $prev = $this->getPrev();
206
207        if ($prev) {
208            return new Diff(
209                explode("\n", rawWiki($this->getId(), $prev)),
210                explode("\n", rawWiki($this->getId(), ''))
211            );
212        }
213        return new Diff([''], explode("\n", rawWiki($this->getId(), '')));
214    }
215}
216