1 <?php
2 
3 namespace dokuwiki\Feed;
4 
5 use Diff;
6 use dokuwiki\ChangeLog\PageChangeLog;
7 use TableDiffFormatter;
8 use 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  */
14 class 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(-1, 2);
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