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