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