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 $revisions[] = $currentRevInfo; 58 $num = $num - 1; 59 } 60 /* we need to get one additional log entry to be able to 61 * decide if this is the last page or is there another one. 62 * see also Ui\Recent::getRecents() 63 */ 64 $revlist = $changelog->getRevisions($first, $num + 1); 65 if (count($revlist) == 0 && $first > 0) { 66 // resets to zero if $first requested a too high number 67 $first = 0; 68 return $this->getRevisions($first, $hasNext); 69 } 70 71 // decide if this is the last page or is there another one 72 $hasNext = false; 73 if (count($revlist) > $num) { 74 $hasNext = true; 75 array_pop($revlist); // remove one additional log entry 76 } 77 78 // append each revison info array to the revisions 79 foreach ($revlist as $rev) { 80 $revisions[] = $changelog->getRevisionInfo($rev); 81 } 82 return $revisions; 83 } 84 85 /** 86 * Navigation buttons for Pagenation (prev/next) 87 * 88 * @param int $first 89 * @param bool $hasNext 90 * @param callable $callback returns array of hidden fields for the form button 91 * @return string html 92 */ 93 protected function navigation($first, $hasNext, $callback) 94 { 95 global $conf; 96 97 $html = '<div class="pagenav">'; 98 $last = $first + $conf['recent']; 99 if ($first > 0) { 100 $first = max($first - $conf['recent'], 0); 101 $html.= '<div class="pagenav-prev">'; 102 $html.= html_btn('newer', $this->id, "p", $callback($first)); 103 $html.= '</div>'; 104 } 105 if ($hasNext) { 106 $html.= '<div class="pagenav-next">'; 107 $html.= html_btn('older', $this->id, "n", $callback($last)); 108 $html.= '</div>'; 109 } 110 $html.= '</div>'; 111 return $html; 112 } 113 114 /** 115 * Returns instance of objRevInfo 116 * 117 * @param array $info Revision info structure of a page or media file 118 * @return objRevInfo object (anonymous class) 119 */ 120 public function getObjRevInfo(array $info) 121 { 122 return new class ($info) // anonymous class (objRevInfo) 123 { 124 protected $info; 125 126 public function __construct(array $info) 127 { 128 $info['item'] = strrpos($info['id'], '.') ? 'media' : 'page'; 129 if (!isset($info['current'])) { 130 $info['current'] = false; 131 } 132 $this->info = $info; 133 } 134 135 // current indicator 136 public function currentIndicator() 137 { 138 global $lang; 139 return ($this->info['current']) ? '('.$lang['current'].')' : ''; 140 } 141 142 // edit date and time of the page or media file 143 public function editDate() 144 { 145 global $lang; 146 $date = dformat($this->info['date']); 147 if (($this->info['timestamp'] ?? '') == 'unknown') { 148 // exteranlly deleted or older file restored 149 $date = preg_replace('/[0-9a-zA-Z]/','_', $date); 150 } 151 return '<span class="date">'. $date .'</span>'; 152 } 153 154 // edit summary 155 public function editSummary() 156 { 157 return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>'; 158 } 159 160 // editor of the page or media file 161 public function editor() 162 { 163 // slightly different with display of Ui\Recent, i.e. external edit 164 global $lang; 165 $html = '<span class="user">'; 166 if (!$this->info['user'] && !$this->info['ip']) { 167 $html.= '('.$lang['external_edit'].')'; 168 } elseif ($this->info['user']) { 169 $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>'; 170 if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>'; 171 } else { 172 $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>'; 173 } 174 $html.= '</span>'; 175 return $html; 176 } 177 178 // name of the page or media file 179 public function itemName() 180 { 181 // slightly different with display of Ui\Recent, i.e. revison may not exists 182 $id = $this->info['id']; 183 $rev = $this->info['date']; 184 185 switch ($this->info['item']) { 186 case 'media': // media file revision 187 if (isset($this->info['current'])) { 188 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&'); 189 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 190 } elseif (file_exists(mediaFN($id, $rev))) { 191 $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&'); 192 $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>'; 193 } else { 194 $html = $id; 195 } 196 return $html; 197 case 'page': // page revision 198 $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id; 199 if (!$display_name) $display_name = $id; 200 if ($this->info['type'] == DOKU_CHANGE_TYPE_DELETE) { 201 // exteranlly deleted or older file restored 202 $href = wl($id, "", false, '&'); 203 $html = '<a href="'.$href.'" class="wikilink2">'.$display_name.'</a>'; 204 } elseif ($this->info['current'] || page_exists($id, $rev)) { 205 $href = wl($id, "rev=$rev", false, '&'); 206 $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>'; 207 } else { 208 $html = $display_name; 209 } 210 return $html; 211 } 212 return ''; 213 } 214 215 // icon difflink 216 public function difflink() 217 { 218 global $lang; 219 $id = $this->info['id']; 220 $rev = $this->info['date']; 221 222 switch ($this->info['item']) { 223 case 'media': // media file revision 224 if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) { 225 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 226 } else { 227 $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&'); 228 $html = '<a href="'.$href.'" class="diff_link">' 229 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 230 . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />' 231 . '</a> '; 232 } 233 return $html; 234 case 'page': // page revision 235 if ($this->info['current'] || !page_exists($id, $rev)) { 236 $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />'; 237 } else { 238 $href = wl($id, "rev=$rev,do=diff", false, '&'); 239 $html = '<a href="'.$href.'" class="diff_link">' 240 . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"' 241 . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />' 242 . '</a>'; 243 } 244 return $html; 245 } 246 return ''; 247 } 248 249 // size change 250 public function sizeChange() 251 { 252 $class = 'sizechange'; 253 $value = filesize_h(abs($this->info['sizechange'])); 254 if ($this->info['sizechange'] > 0) { 255 $class .= ' positive'; 256 $value = '+' . $value; 257 } elseif ($this->info['sizechange'] < 0) { 258 $class .= ' negative'; 259 $value = '-' . $value; 260 } else { 261 $value = '±' . $value; 262 } 263 return '<span class="'.$class.'">'.$value.'</span>'; 264 } 265 }; // end of anonymous class (objRevInfo) 266 } 267 268} 269