1<?php 2 3namespace dokuwiki\ChangeLog; 4 5/** 6 * Class RevisionInfo 7 * 8 * Provides methods to show Revision Information in DokuWiki Ui compoments: 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 Infomation 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'] = strrpos($info['id'], '.') ? '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 revison 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 coresponds 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 $this->val('current', $value); 63 } 64 65 /** 66 * Return or set a value of assosiated 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 * fileicon 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 switch ($this->val('mode')) { 110 case 'media': // media file revision 111 return media_printicon($id); 112 case 'page': // page revision 113 return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />'; 114 } 115 } 116 117 /** 118 * edit date and time of the page or media file 119 * used in [Ui\recent, Ui\Revisions] 120 * 121 * @param bool $checkTimestamp enable timestamp check, alter formatted string when timestamp is false 122 * @return string 123 */ 124 public function showEditDate($checkTimestamp = false) 125 { 126 $formatted = dformat($this->val('date')); 127 if ($checkTimestamp && $this->val('timestamp') === false) { 128 // exact date is unknown for externally deleted file 129 // when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__" 130 $formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted); 131 } 132 return '<span class="date">'. $formatted .'</span>'; 133 } 134 135 /** 136 * edit summary 137 * used in [Ui\recent, Ui\Revisions] 138 * 139 * @return string 140 */ 141 public function showEditSummary() 142 { 143 return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>'; 144 } 145 146 /** 147 * editor of the page or media file 148 * used in [Ui\recent, Ui\Revisions] 149 * 150 * @return string 151 */ 152 public function showEditor() 153 { 154 if ($this->val('user')) { 155 $html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>'; 156 if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>'; 157 } else { 158 $html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>'; 159 } 160 return '<span class="user">'. $html. '</span>'; 161 } 162 163 /** 164 * name of the page or media file 165 * used in [Ui\recent, Ui\Revisions] 166 * 167 * @return string 168 */ 169 public function showFileName() 170 { 171 $id = $this->val('id'); 172 $rev = $this->isCurrent() ? '' : $this->val('date'); 173 174 switch ($this->val('mode')) { 175 case 'media': // media file revision 176 $params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id]; 177 if ($rev) $params += ['rev'=> $rev]; 178 $href = media_managerURL($params, '&'); 179 $display_name = $id; 180 $class = file_exists(mediaFN($id, $rev)) ? 'wikilink1' : 'wikilink2'; 181 break; 182 case 'page': // page revision 183 $params = $rev ? ['rev'=> $rev] : []; 184 $href = wl($id, $params, false, '&'); 185 $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id; 186 if (!$display_name) $display_name = $id; 187 $class = page_exists($id, $rev) ? 'wikilink1' : 'wikilink2'; 188 } 189 if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) { 190 $class = 'wikilink2'; 191 } 192 return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>'; 193 } 194 195 /** 196 * Revision Title for PageDiff table headline 197 * 198 * @return string 199 */ 200 public function showRevisionTitle() 201 { 202 global $lang; 203 204 if (!$this->val('date')) return '—'; 205 206 $id = $this->val('id'); 207 $rev = $this->isCurrent() ? '' : $this->val('date'); 208 $params = ($rev) ? ['rev'=> $rev] : []; 209 210 switch ($this->val('mode')) { 211 case 'media': // media file revision 212 $href = ml($id, $params, false, '&'); 213 $class = file_exists(mediaFN($id, $rev)) ? 'wikilink1' : 'wikilink2'; 214 break; 215 case 'page': // page revision 216 $href = wl($id, $params, false, '&'); 217 $class = page_exists($id, $rev) ? 'wikilink1' : 'wikilink2'; 218 } 219 if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) { 220 $class = 'wikilink2'; 221 } 222 // revision info may have timestamp key when external edits occurred 223 $date = ($this->val('timestamp') === false) 224 ? $lang['unknowndate'] 225 : dformat($this->val('date')); 226 227 return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>'; 228 } 229 230 /** 231 * difflink icon in recents list, to compare (this) current revision with previous one 232 * all items in "recent changes" are current revision of the page or media 233 * 234 * @return string 235 */ 236 public function showIconCompareWithPrevious() 237 { 238 global $lang; 239 $id = $this->val('id'); 240 241 $href = ''; 242 switch ($this->val('mode')) { 243 case 'media': // media file revision 244 // unlike page, media file does not copyed to media_attic when uploaded. 245 // diff icon will not be shown when external edit occurred 246 // because no attic file to be compared with current. 247 $revs = (new MediaChangeLog($id))->getRevisions(0, 1); 248 $showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id))); 249 if ($showLink) { 250 $param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id]; 251 $href = media_managerURL($param, '&'); 252 } 253 break; 254 case 'page': // page revision 255 // when a page just created anyway, it is natural to expect no older revisions 256 // even if it had once existed but deleted before. Simply ignore to check changelog. 257 if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) { 258 $href = wl($id, ['do'=>'diff'], false, '&'); 259 } 260 } 261 262 if ($href) { 263 return '<a href="'.$href.'" class="diff_link">' 264 .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 265 .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 266 .'</a>'; 267 } else { 268 return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 269 } 270 } 271 272 /** 273 * difflink icon in revsions list, compare this revision with current one 274 * the icon does not displayed for the current revision 275 * 276 * @return string 277 */ 278 public function showIconCompareWithCurrent() 279 { 280 global $lang; 281 $id = $this->val('id'); 282 $rev = $this->isCurrent() ? '' : $this->val('date'); 283 284 $href = ''; 285 switch ($this->val('mode')) { 286 case 'media': // media file revision 287 if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) { 288 $param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev]; 289 $href = media_managerURL($param, '&'); 290 } 291 break; 292 case 'page': // page revision 293 if (!$this->isCurrent()) { 294 $href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&'); 295 } 296 } 297 298 if ($href) { 299 return '<a href="'.$href.'" class="diff_link">' 300 .'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 301 .' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 302 .'</a>'; 303 } else { 304 return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 305 } 306 } 307 308 /** 309 * icon for revision action 310 * used in [Ui\recent] 311 * 312 * @return string 313 */ 314 public function showIconRevisions() 315 { 316 global $lang; 317 318 if (!actionOK('revisions')) { 319 return ''; 320 } 321 322 $id = $this->val('id'); 323 switch ($this->val('mode')) { 324 case 'media': // media file revision 325 $param = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id]; 326 $href = media_managerURL($param, '&'); 327 break; 328 case 'page': // page revision 329 $href = wl($id, ['do'=>'revisions'], false, '&'); 330 } 331 return '<a href="'.$href.'" class="revisions_link">' 332 . '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"' 333 . ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />' 334 . '</a>'; 335 } 336 337 /** 338 * size change 339 * used in [Ui\recent, Ui\Revisions] 340 * 341 * @return string 342 */ 343 public function showSizeChange() 344 { 345 $class = 'sizechange'; 346 $value = filesize_h(abs($this->val('sizechange'))); 347 if ($this->val('sizechange') > 0) { 348 $class .= ' positive'; 349 $value = '+' . $value; 350 } elseif ($this->val('sizechange') < 0) { 351 $class .= ' negative'; 352 $value = '-' . $value; 353 } else { 354 $value = '±' . $value; 355 } 356 return '<span class="'.$class.'">'.$value.'</span>'; 357 } 358 359 /** 360 * current indicator, used in revison list 361 * not used in Ui\Recents because recent filess are always current one 362 * 363 * @return string 364 */ 365 public function showCurrentIndicator() 366 { 367 global $lang; 368 return $this->isCurrent() ? '('.$lang['current'].')' : ''; 369 } 370 371 372} 373