1<?php 2 3namespace dokuwiki\ChangeLog; 4 5/** 6 * Class RevisionInfo 7 * 8 * Provides methods to show Revision Information in DokuWiki Ui components: 9 * - Ui\Recent 10 * - Ui\PageRevisions 11 * - Ui\MediaRevisions 12 */ 13class RevisionInfo 14{ 15 /* @var array */ 16 protected $info; 17 18 /** 19 * Constructor 20 * 21 * @param array $info Revision Information structure with entries: 22 * - date: unix timestamp 23 * - ip: IPv4 or IPv6 address 24 * - type: change type (log line type) 25 * - id: page id 26 * - user: user name 27 * - sum: edit summary (or action reason) 28 * - extra: extra data (varies by line type) 29 * - sizechange: change of filesize 30 * additionally, 31 * - current: (optional) whether current revision or not 32 * - timestamp: (optional) set only when external edits occurred 33 * - mode: (internal use) ether "media" or "page" 34 */ 35 public function __construct($info = null) 36 { 37 if (is_array($info) && isset($info['id'])) { 38 // define strategy context 39 $info['mode'] = $info['media'] ? 'media' : 'page'; 40 } else { 41 $info = [ 42 'mode' => 'page', 43 'date' => false, 44 ]; 45 } 46 $this->info = $info; 47 } 48 49 /** 50 * Set or return whether this revision is current page or media file 51 * 52 * This method does not check exactly whether the revision is current or not. Instead, 53 * set value of associated "current" key for internal use. Some UI element like diff 54 * link button depend on relation to current page or media file. A changelog line does 55 * not indicate whether it corresponds to current page or media file. 56 * 57 * @param bool $value true if the revision is current, otherwise false 58 * @return bool 59 */ 60 public function isCurrent($value = null) 61 { 62 return (bool) $this->val('current', $value); 63 } 64 65 /** 66 * Return or set a value of associated key of revision information 67 * but does not allow to change values of existing keys 68 * 69 * @param string $key 70 * @param mixed $value 71 * @return string|null 72 */ 73 public function val($key, $value = null) 74 { 75 if (isset($value) && !array_key_exists($key, $this->info)) { 76 // setter, only for new keys 77 $this->info[$key] = $value; 78 } 79 if (array_key_exists($key, $this->info)) { 80 // getter 81 return $this->info[$key]; 82 } 83 return null; 84 } 85 86 /** 87 * Set extra key-value to the revision information 88 * but does not allow to change values of existing keys 89 * @param array $info 90 * @return void 91 */ 92 public function append(array $info) 93 { 94 foreach ($info as $key => $value) { 95 $this->val($key, $value); 96 } 97 } 98 99 100 /** 101 * file icon of the page or media file 102 * used in [Ui\recent] 103 * 104 * @return string 105 */ 106 public function showFileIcon() 107 { 108 $id = $this->val('id'); 109 if ($this->val('mode') == 'media') { 110 // media file revision 111 return media_printicon($id); 112 } elseif ($this->val('mode') == 'page') { 113 // page revision 114 return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />'; 115 } 116 } 117 118 /** 119 * edit date and time of the page or media file 120 * used in [Ui\recent, Ui\Revisions] 121 * 122 * @param bool $checkTimestamp enable timestamp check, alter formatted string when timestamp is false 123 * @return string 124 */ 125 public function showEditDate($checkTimestamp = false) 126 { 127 $formatted = dformat($this->val('date')); 128 if ($checkTimestamp && $this->val('timestamp') === false) { 129 // exact date is unknown for externally deleted file 130 // when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__" 131 $formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted); 132 } 133 return '<span class="date">'. $formatted .'</span>'; 134 } 135 136 /** 137 * edit summary 138 * used in [Ui\recent, Ui\Revisions] 139 * 140 * @return string 141 */ 142 public function showEditSummary() 143 { 144 return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>'; 145 } 146 147 /** 148 * editor of the page or media file 149 * used in [Ui\recent, Ui\Revisions] 150 * 151 * @return string 152 */ 153 public function showEditor() 154 { 155 if ($this->val('user')) { 156 $html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>'; 157 if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>'; 158 } else { 159 $html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>'; 160 } 161 return '<span class="user">'. $html. '</span>'; 162 } 163 164 /** 165 * name of the page or media file 166 * used in [Ui\recent, Ui\Revisions] 167 * 168 * @return string 169 */ 170 public function showFileName() 171 { 172 $id = $this->val('id'); 173 $rev = $this->isCurrent() ? '' : $this->val('date'); 174 175 if ($this->val('mode') == 'media') { 176 // media file revision 177 $params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id]; 178 if ($rev) $params += ['rev'=> $rev]; 179 $href = media_managerURL($params, '&'); 180 $display_name = $id; 181 $exists = file_exists(mediaFN($id, $rev)); 182 } elseif ($this->val('mode') == 'page') { 183 // page revision 184 $params = $rev ? ['rev'=> $rev] : []; 185 $href = wl($id, $params, false, '&'); 186 $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id; 187 if (!$display_name) $display_name = $id; 188 $exists = page_exists($id, $rev); 189 } 190 191 if ($exists) { 192 $class = 'wikilink1'; 193 } elseif ($this->isCurrent()) { 194 //show only not-existing link for current page, which allows for directly create a new page/upload 195 $class = 'wikilink2'; 196 } else { 197 //revision is not in attic 198 return $display_name; 199 } 200 if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) { 201 $class = 'wikilink2'; 202 } 203 return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>'; 204 } 205 206 /** 207 * Revision Title for PageDiff table headline 208 * 209 * @return string 210 */ 211 public function showRevisionTitle() 212 { 213 global $lang; 214 215 if (!$this->val('date')) return '—'; 216 217 $id = $this->val('id'); 218 $rev = $this->isCurrent() ? '' : $this->val('date'); 219 $params = ($rev) ? ['rev'=> $rev] : []; 220 221 // revision info may have timestamp key when external edits occurred 222 $date = ($this->val('timestamp') === false) 223 ? $lang['unknowndate'] 224 : dformat($this->val('date')); 225 226 227 if ($this->val('mode') == 'media') { 228 // media file revision 229 $href = ml($id, $params, false, '&'); 230 $exists = file_exists(mediaFN($id, $rev)); 231 } elseif ($this->val('mode') == 'page') { 232 // page revision 233 $href = wl($id, $params, false, '&'); 234 $exists = page_exists($id, $rev); 235 } 236 if ($exists) { 237 $class = 'wikilink1'; 238 } elseif ($this->isCurrent()) { 239 //show only not-existing link for current page, which allows for directly create a new page/upload 240 $class = 'wikilink2'; 241 } else { 242 //revision is not in attic 243 return $id.' ['.$date.']'; 244 } 245 if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) { 246 $class = 'wikilink2'; 247 } 248 return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>'; 249 } 250 251 /** 252 * diff link icon in recent changes list, to compare (this) current revision with previous one 253 * all items in "recent changes" are current revision of the page or media 254 * 255 * @return string 256 */ 257 public function showIconCompareWithPrevious() 258 { 259 global $lang; 260 $id = $this->val('id'); 261 262 $href = ''; 263 if ($this->val('mode') == 'media') { 264 // media file revision 265 // unlike page, media file does not copied to media_attic when uploaded. 266 // diff icon will not be shown when external edit occurred 267 // because no attic file to be compared with current. 268 $revs = (new MediaChangeLog($id))->getRevisions(0, 1); 269 $showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id))); 270 if ($showLink) { 271 $param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id]; 272 $href = media_managerURL($param, '&'); 273 } 274 } elseif ($this->val('mode') == 'page') { 275 // page revision 276 // when a page just created anyway, it is natural to expect no older revisions 277 // even if it had once existed but deleted before. Simply ignore to check changelog. 278 if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) { 279 $href = wl($id, ['do'=>'diff'], false, '&'); 280 } 281 } 282 283 if ($href) { 284 return '<a href="'.$href.'" class="diff_link">' 285 .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 286 .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 287 .'</a>'; 288 } else { 289 return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 290 } 291 } 292 293 /** 294 * diff link icon in revisions list, compare this revision with current one 295 * the icon does not displayed for the current revision 296 * 297 * @return string 298 */ 299 public function showIconCompareWithCurrent() 300 { 301 global $lang; 302 $id = $this->val('id'); 303 $rev = $this->isCurrent() ? '' : $this->val('date'); 304 305 $href = ''; 306 if ($this->val('mode') == 'media') { 307 // media file revision 308 if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) { 309 $param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev]; 310 $href = media_managerURL($param, '&'); 311 } 312 } elseif ($this->val('mode') == 'page') { 313 // page revision 314 if (!$this->isCurrent()) { 315 $href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&'); 316 } 317 } 318 319 if ($href) { 320 return '<a href="'.$href.'" class="diff_link">' 321 .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 322 .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 323 .'</a>'; 324 } else { 325 return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 326 } 327 } 328 329 /** 330 * icon for revision action 331 * used in [Ui\recent] 332 * 333 * @return string 334 */ 335 public function showIconRevisions() 336 { 337 global $lang; 338 339 if (!actionOK('revisions')) { 340 return ''; 341 } 342 343 $id = $this->val('id'); 344 if ($this->val('mode') == 'media') { 345 // media file revision 346 $param = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id]; 347 $href = media_managerURL($param, '&'); 348 } elseif ($this->val('mode') == 'page') { 349 // page revision 350 $href = wl($id, ['do'=>'revisions'], false, '&'); 351 } 352 return '<a href="'.$href.'" class="revisions_link">' 353 . '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"' 354 . ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />' 355 . '</a>'; 356 } 357 358 /** 359 * size change 360 * used in [Ui\recent, Ui\Revisions] 361 * 362 * @return string 363 */ 364 public function showSizeChange() 365 { 366 $class = 'sizechange'; 367 $value = filesize_h(abs($this->val('sizechange'))); 368 if ($this->val('sizechange') > 0) { 369 $class .= ' positive'; 370 $value = '+' . $value; 371 } elseif ($this->val('sizechange') < 0) { 372 $class .= ' negative'; 373 $value = '-' . $value; 374 } else { 375 $value = '±' . $value; 376 } 377 return '<span class="'.$class.'">'.$value.'</span>'; 378 } 379 380 /** 381 * current indicator, used in revision list 382 * not used in Ui\Recent because recent files are always current one 383 * 384 * @return string 385 */ 386 public function showCurrentIndicator() 387 { 388 global $lang; 389 return $this->isCurrent() ? '('.$lang['current'].')' : ''; 390 } 391 392 393} 394