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