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