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