1<?php 2 3namespace dokuwiki\Ui; 4 5use dokuwiki\ChangeLog\ChangeLog; 6 7/** 8 * DokuWiki Revisions Interface 9 * parent class of PageRevisions and MediaRevisions 10 * 11 * @package dokuwiki\Ui 12 */ 13abstract class Revisions extends Ui 14{ 15 /* @var string */ 16 protected $id; // page id or media id 17 18 /* @var ChangeLog */ 19 protected $changelog; // PageChangeLog or MediaChangeLog object 20 21 /** 22 * Revisions Ui constructor 23 * 24 * @param string $id page id or media id 25 */ 26 public function __construct($id) 27 { 28 $this->id = $id; 29 $this->setChangeLog(); 30 } 31 32 /** 33 * set class property changelog 34 */ 35 abstract protected function setChangeLog(); 36 37 /** 38 * Get revisions, and set correct pagination parameters (first, hasNext) 39 * 40 * @param int $first 41 * @param bool $hasNext 42 * @return array revisions to be shown in a pagenated list 43 * @see also https://www.dokuwiki.org/devel:changelog 44 */ 45 protected function getRevisions(&$first, &$hasNext) 46 { 47 global $conf; 48 49 $changelog =& $this->changelog; 50 $revisions = []; 51 52 $currentRevInfo = $changelog->getCurrentRevisionInfo(); 53 if (!$currentRevInfo) return $revisions; 54 55 $num = $conf['recent']; 56 if ($first == 0) { 57 // add extrenal or existing last revision that is excluded from $changelog->getRevisions() 58 if (array_key_exists('timestamp', $currentRevInfo) || ( 59 $currentRevInfo['type'] != DOKU_CHANGE_TYPE_DELETE && 60 $currentRevInfo['date'] == $changelog->lastRevision() ) 61 ) { 62 $revisions[] = $currentRevInfo; 63 $num = $num - 1; 64 } 65 } 66 /* we need to get one additional log entry to be able to 67 * decide if this is the last page or is there another one. 68 * see also Ui\Recent::getRecents() 69 */ 70 $revlist = $changelog->getRevisions($first, $num + 1); 71 if (count($revlist) == 0 && $first > 0) { 72 // resets to zero if $first requested a too high number 73 $first = 0; 74 return $this->getRevisions($first, $hasNext); 75 } 76 77 // decide if this is the last page or is there another one 78 $hasNext = false; 79 if (count($revlist) > $num) { 80 $hasNext = true; 81 array_pop($revlist); // remove one additional log entry 82 } 83 84 // append each revison info array to the revisions 85 foreach ($revlist as $rev) { 86 $revisions[] = $changelog->getRevisionInfo($rev); 87 } 88 return $revisions; 89 } 90 91 /** 92 * Navigation buttons for Pagenation (prev/next) 93 * 94 * @param int $first 95 * @param bool $hasNext 96 * @param callable $callback returns array of hidden fields for the form button 97 * @return string html 98 */ 99 protected function navigation($first, $hasNext, $callback) 100 { 101 global $conf; 102 103 $html = '<div class="pagenav">'; 104 $last = $first + $conf['recent']; 105 if ($first > 0) { 106 $first = max($first - $conf['recent'], 0); 107 $html.= '<div class="pagenav-prev">'; 108 $html.= html_btn('newer', $this->id, "p", $callback($first)); 109 $html.= '</div>'; 110 } 111 if ($hasNext) { 112 $html.= '<div class="pagenav-next">'; 113 $html.= html_btn('older', $this->id, "n", $callback($last)); 114 $html.= '</div>'; 115 } 116 $html.= '</div>'; 117 return $html; 118 } 119 120 /** 121 * Returns instance of objRevInfo 122 * 123 * @param array $info Revision info structure of a page or media file 124 * @return objRevInfo object (anonymous class) 125 */ 126 public function getObjRevInfo(array $info) 127 { 128 return new class ($info) // anonymous class (objRevInfo) 129 { 130 protected $info; 131 132 public function __construct(array $info) 133 { 134 $info['item'] = strrpos($info['id'], '.') ? 'media' : 'page'; 135 $info['current'] = $info['current'] ?? false; 136 // revision info may have timestamp key when external edits occurred 137 $info['timestamp'] = $info['timestamp'] ?? true; 138 $this->info = $info; 139 } 140 141 // current indicator 142 public function currentIndicator() 143 { 144 global $lang; 145 return ($this->info['current']) ? '('.$lang['current'].')' : ''; 146 } 147 148 // edit date and time of the page or media file 149 public function editDate() 150 { 151 global $lang; 152 $date = dformat($this->info['date']); 153 if ($this->info['timestamp'] === false) { 154 // externally deleted or older file restored 155 $date = preg_replace('/[0-9a-zA-Z]/','_', $date); 156 } 157 return '<span class="date">'. $date .'</span>'; 158 } 159 160 // edit summary 161 public function editSummary() 162 { 163 return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>'; 164 } 165 166 // editor of the page or media file 167 public function editor() 168 { 169 // slightly different with display of Ui\Recent, i.e. external edit 170 global $lang; 171 $html = '<span class="user">'; 172 if (!$this->info['user'] && !$this->info['ip']) { 173 $html.= '('.$lang['external_edit'].')'; 174 } elseif ($this->info['user']) { 175 $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>'; 176 if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>'; 177 } else { 178 $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>'; 179 } 180 $html.= '</span>'; 181 return $html; 182 } 183 184 // name of the page or media file 185 public function itemName() 186 { 187 // slightly different with display of Ui\Recent, i.e. revison may not exists 188 $id = $this->info['id']; 189 $rev = $this->info['date']; 190 191 switch ($this->info['item']) { 192 case 'media': // media file revision 193 if ($this->info['current']) { 194 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&'); 195 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 196 } elseif (file_exists(mediaFN($id, $rev))) { 197 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&'); 198 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 199 } else { 200 $html = $id; 201 } 202 return $html; 203 case 'page': // page revision 204 $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id; 205 if (!$display_name) $display_name = $id; 206 if ($this->info['type'] == DOKU_CHANGE_TYPE_DELETE) { 207 // externally deleted or older file restored 208 $href = wl($id, "", false, '&'); 209 $html = '<a href="'.$href.'" class="wikilink2">'.$display_name.'</a>'; 210 } elseif ($this->info['current'] || page_exists($id, $rev)) { 211 $href = wl($id, "rev=$rev", false, '&'); 212 $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>'; 213 } else { 214 $html = $display_name; 215 } 216 return $html; 217 } 218 return ''; 219 } 220 221 // icon difflink 222 public function difflink() 223 { 224 global $lang; 225 $id = $this->info['id']; 226 $rev = $this->info['date']; 227 228 switch ($this->info['item']) { 229 case 'media': // media file revision 230 if ($this->info['current'] || !file_exists(mediaFN($id, $rev))) { 231 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 232 } else { 233 $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&'); 234 $html = '<a href="'.$href.'" class="diff_link">' 235 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 236 . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 237 . '</a> '; 238 } 239 return $html; 240 case 'page': // page revision 241 if ($this->info['current'] || !page_exists($id, $rev)) { 242 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 243 } else { 244 $href = wl($id, "rev=$rev,do=diff", false, '&'); 245 $html = '<a href="'.$href.'" class="diff_link">' 246 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 247 . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />' 248 . '</a>'; 249 } 250 return $html; 251 } 252 return ''; 253 } 254 255 // size change 256 public function sizeChange() 257 { 258 $class = 'sizechange'; 259 $value = filesize_h(abs($this->info['sizechange'])); 260 if ($this->info['sizechange'] > 0) { 261 $class .= ' positive'; 262 $value = '+' . $value; 263 } elseif ($this->info['sizechange'] < 0) { 264 $class .= ' negative'; 265 $value = '-' . $value; 266 } else { 267 $value = '±' . $value; 268 } 269 return '<span class="'.$class.'">'.$value.'</span>'; 270 } 271 }; // end of anonymous class (objRevInfo) 272 } 273 274} 275