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