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