1<?php 2 3namespace dokuwiki\Ui; 4 5use dokuwiki\ChangeLog\PageChangeLog; 6use dokuwiki\Form\Form; 7 8/** 9 * DokuWiki PageRevisions Interface 10 * 11 * @package dokuwiki\Ui 12 */ 13class PageRevisions extends Ui 14{ 15 /* @var string */ 16 protected $id; 17 18 /** 19 * PageRevisions Ui constructor 20 * 21 * @param string $id id of page 22 */ 23 public function __construct($id) 24 { 25 global $ID, $INFO; 26 if (!$id) $id = $INFO['id']; 27 $this->id = $id; 28 } 29 30 /** 31 * Display list of old revisions of the page 32 * 33 * @author Andreas Gohr <andi@splitbrain.org> 34 * @author Ben Coburn <btcoburn@silicodon.net> 35 * @author Kate Arzamastseva <pshns@ukr.net> 36 * @author Satoshi Sahara <sahara.satoshi@gmail.com> 37 * 38 * @param int $first skip the first n changelog lines 39 * @return void 40 */ 41 public function show($first = 0) 42 { 43 global $lang; 44 45 // get revisions, and set correct pagenation parameters (first, hasNext) 46 if ($first === null) $first = 0; 47 $hasNext = false; 48 $revisions = $this->getRevisions($first, $hasNext); 49 50 // print intro 51 print p_locale_xhtml('revisions'); 52 53 // create the form 54 $form = new Form([ 55 'id' => 'page__revisions', 56 'class' => 'changes', 57 ]); 58 $form->addTagOpen('div')->addClass('no'); 59 60 // start listing 61 $form->addTagOpen('ul'); 62 foreach ($revisions as $info) { 63 $rev = $info['date']; 64 $class = ($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) ? 'minor' : ''; 65 $form->addTagOpen('li')->addClass($class); 66 $form->addTagOpen('div')->addClass('li'); 67 68 if (page_exists($this->id, $rev)) { 69 $form->addCheckbox('rev2[]')->val($rev); 70 } else { 71 $form->addCheckbox('')->val($rev)->attr('disabled','disabled'); 72 } 73 $form->addHTML(' '); 74 75 $objRevInfo = $this->getObjRevInfo($info); 76 $html = implode(' ', [ 77 $objRevInfo->editDate(), // edit date and time 78 $objRevInfo->difflink(), // link to diffview icon 79 $objRevInfo->itemName(), // name of page or media 80 $objRevInfo->editSummary(), // edit summary 81 $objRevInfo->editor(), // editor info 82 $objRevInfo->sizechange(), // size change indicator 83 $objRevInfo->currentIndicator(), // current indicator (only when k=1) 84 ]); 85 $form->addHTML($html); 86 $form->addTagClose('div'); 87 $form->addTagClose('li'); 88 } 89 $form->addTagClose('ul'); // end of revision list 90 91 // show button for diff view 92 $form->addButton('do[diff]', $lang['diff2'])->attr('type', 'submit'); 93 94 $form->addTagClose('div'); // close div class=no 95 96 print $form->toHTML('Revisions'); 97 98 // provide navigation for pagenated revision list (of pages and/or media files) 99 print $this->htmlNavigation($first, $hasNext); 100 } 101 102 /** 103 * Get revisions, and set correct pagenation parameters (first, hasNext) 104 * 105 * @param int $first 106 * @param bool $hasNext 107 * @return array revisions to be shown in a pagenated list 108 * @see also https://www.dokuwiki.org/devel:changelog 109 */ 110 protected function getRevisions(&$first, &$hasNext) 111 { 112 global $INFO, $conf; 113 114 $changelog = new PageChangeLog($INFO['id']); 115 116 $revisions = []; 117 118 /* we need to get one additional log entry to be able to 119 * decide if this is the last page or is there another one. 120 * see also Ui\Recent::getRecents() 121 */ 122 $revlist = $changelog->getRevisions($first, $conf['recent'] +1); 123 if (count($revlist) == 0 && $first != 0) { 124 $first = 0; 125 $revlist = $changelog->getRevisions($first, $conf['recent'] +1); 126 } 127 $exists = $INFO['exists']; 128 if ($first === 0 && $exists) { 129 // add current page as revision[0] 130 $revisions[] = array( 131 'date' => $INFO['lastmod'], 132 'ip' => null, 133 'type' => $INFO['meta']['last_change']['type'], 134 'id' => $INFO['id'], 135 'user' => $INFO['editor'], 136 'sum' => $INFO['sum'], 137 'extra' => null, 138 'sizechange' => $INFO['meta']['last_change']['sizechange'], 139 'current' => true, 140 ); 141 } 142 143 // decide if this is the last page or is there another one 144 $hasNext = false; 145 if (count($revlist) > $conf['recent']) { 146 $hasNext = true; 147 array_pop($revlist); // remove one additional log entry 148 } 149 150 // append each revison info array to the revisions 151 foreach ($revlist as $rev) { 152 $revisions[] = $changelog->getRevisionInfo($rev); 153 } 154 return $revisions; 155 } 156 157 /** 158 * Navigation buttons for Pagenation (prev/next) 159 * 160 * @param int $first 161 * @param bool $hasNext 162 * @return array html 163 */ 164 protected function htmlNavigation($first, $hasNext) 165 { 166 global $conf; 167 168 $html = '<div class="pagenav">'; 169 $last = $first + $conf['recent']; 170 if ($first > 0) { 171 $first = max($first - $conf['recent'], 0); 172 $html.= '<div class="pagenav-prev">'; 173 $html.= html_btn('newer', $this->id, "p" ,['do' => 'revisions', 'first' => $first]); 174 $html.= '</div>'; 175 } 176 if ($hasNext) { 177 $html.= '<div class="pagenav-next">'; 178 $html.= html_btn('older', $this->id, "n", ['do' => 'revisions', 'first' => $last]); 179 $html.= '</div>'; 180 } 181 $html.= '</div>'; 182 return $html; 183 } 184 185 /** 186 * Returns instance of objRevInfo 187 * 188 * @param array $info Revision info structure of a page or media file 189 * @return objRevInfo object (anonymous class) 190 */ 191 protected function getObjRevInfo(array $info) 192 { 193 return new class ($info) // anonymous class (objRevInfo) 194 { 195 protected $info; 196 197 public function __construct(array $info) 198 { 199 $this->info = $info; 200 } 201 202 // current indicator 203 public function currentIndicator() 204 { 205 global $lang; 206 return ($this->info['current']) ? '('.$lang['current'].')' : ''; 207 } 208 209 // edit date and time of the page or media file 210 public function editDate() 211 { 212 return '<span class="date">'. dformat($this->info['date']) .'</span>'; 213 } 214 215 // edit summary 216 public function editSummary() 217 { 218 return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>'; 219 } 220 221 // editor of the page or media file 222 public function editor() 223 { 224 // slightly different with display of Ui\Recent, i.e. external edit 225 global $lang; 226 $html = '<span class="user">'; 227 if (!$this->info['user'] && !$this->info['ip']) { 228 $html.= '('.$lang['external_edit'].')'; 229 } elseif ($this->info['user']) { 230 $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>'; 231 if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>'; 232 } else { 233 $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>'; 234 } 235 $html.= '</span>'; 236 return $html; 237 } 238 239 // name of the page or media file 240 public function itemName() 241 { 242 // slightly different with display of Ui\Recent, i.e. revison may not exists 243 $id = $this->info['id']; 244 $rev = $this->info['date']; 245 246 if (isset($this->info['media'])) { 247 // media file revision 248 if (isset($this->info['current'])) { 249 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&'); 250 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 251 } elseif (file_exists(mediaFN($id, $rev))) { 252 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&'); 253 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 254 } else { 255 $html = $id; 256 } 257 return $html; 258 } else { 259 // page revision 260 $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id; 261 if (!$display_name) $display_name = $id; 262 if ($this->info['current'] || page_exists($id, $rev)) { 263 $href = wl($id, "rev=$rev", false, '&'); 264 $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>'; 265 } else { 266 $html = $display_name; 267 } 268 return $html; 269 } 270 } 271 272 // icon difflink 273 public function difflink() 274 { 275 global $lang; 276 $id = $this->info['id']; 277 $rev = $this->info['date']; 278 279 if (isset($this->info['media'])) { 280 // media file revision 281 if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) { 282 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 283 } else { 284 $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&'); 285 $html = '<a href="'.$href.'" class="diff_link">' 286 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 287 . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 288 . '</a> '; 289 } 290 return $html; 291 } else { 292 // page revision 293 if ($this->info['current'] || !page_exists($id, $rev)) { 294 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 295 } else { 296 $href = wl($id, "rev=$rev,do=diff", false, '&'); 297 $html = '<a href="'.$href.'" class="diff_link">' 298 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 299 . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />' 300 . '</a>'; 301 } 302 return $html; 303 } 304 } 305 306 // size change 307 public function sizeChange() 308 { 309 $class = 'sizechange'; 310 $value = filesize_h(abs($this->info['sizechange'])); 311 if ($this->info['sizechange'] > 0) { 312 $class .= ' positive'; 313 $value = '+' . $value; 314 } elseif ($this->info['sizechange'] < 0) { 315 $class .= ' negative'; 316 $value = '-' . $value; 317 } else { 318 $value = '±' . $value; 319 } 320 return '<span class="'.$class.'">'.$value.'</span>'; 321 } 322 }; // end of anonymous class (objRevInfo) 323 } 324 325} 326