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