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