[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Ui/ -> Revisions.php (source)

   1  <?php
   2  
   3  namespace dokuwiki\Ui;
   4  
   5  use dokuwiki\ChangeLog\PageChangeLog;
   6  use dokuwiki\ChangeLog\MediaChangeLog;
   7  use dokuwiki\Form\Form;
   8  
   9  /**
  10   * DokuWiki Revisions Interface
  11   *
  12   * @package dokuwiki\Ui
  13   */
  14  class Revisions extends Ui
  15  {
  16      protected $first;
  17      protected $media_id;
  18  
  19      /** 
  20       * Revisions Ui constructor
  21       *
  22       * @param int $first  skip the first n changelog lines
  23       * @param bool|string $media_id  id of media, or false for current page
  24       */
  25      public function __construct($first = 0, $media_id = false)
  26      {
  27          $this->first    = $first;
  28          $this->media_id = $media_id;
  29      }
  30  
  31      /**
  32       * Display list of old revisions
  33       *
  34       * @author Andreas Gohr <andi@splitbrain.org>
  35       * @author Ben Coburn <btcoburn@silicodon.net>
  36       * @author Kate Arzamastseva <pshns@ukr.net>
  37       * @author Satoshi Sahara <sahara.satoshi@gmail.com>
  38       *
  39       * @return void
  40       */
  41      public function show()
  42      {
  43          global $ID;
  44  
  45          if ($this->media_id) {
  46              return $this->showMediaRevisions($this->media_id);
  47          } else {
  48              return $this->showPageRevisions($ID);
  49          }
  50      }
  51  
  52      /**
  53       * Display a list of Media Revisions in the MediaManager
  54       *
  55       * @param string $id  media id
  56       * @return void
  57       */
  58      protected function showMediaRevisions($id)
  59      {
  60          global $lang;
  61  
  62          // get revisions, and set correct pagenation parameters (first, hasNext)
  63          $first   = $this->first;
  64          $hasNext = false;
  65          $revisions = $this->getRevisions($first, $hasNext);
  66  
  67          // create the form
  68          $form = new Form([
  69                  'id' => 'page__revisions', // must not be "media__revisions"
  70                  'action' => media_managerURL(['image' => $id], '&'),
  71                  'class'  => 'changes',
  72          ]);
  73          $form->setHiddenField('mediado', 'diff'); // required for media revisions
  74          $form->addTagOpen('div')->addClass('no');
  75  
  76          // start listing
  77          $form->addTagOpen('ul');
  78          foreach ($revisions as $info) {
  79              $rev = $info['date'];
  80              $class = ($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) ? 'minor' : '';
  81              $form->addTagOpen('li')->addClass($class);
  82              $form->addTagOpen('div')->addClass('li');
  83  
  84              if (isset($info['current'])) {
  85                 $form->addCheckbox('rev2[]')->val('current');
  86              } elseif (file_exists(mediaFN($id, $rev))) {
  87                  $form->addCheckbox('rev2[]')->val($rev);
  88              } else {
  89                  $form->addCheckbox('')->val($rev)->attr('disabled','disabled');
  90              }
  91              $form->addHTML(' ');
  92  
  93              $objRevInfo = $this->getObjRevInfo($info);
  94              $html = implode(' ', [
  95                  $objRevInfo->editDate(),          // edit date and time
  96                  $objRevInfo->difflink(),          // link to diffview icon
  97                  $objRevInfo->itemName(),          // name of page or media
  98                  '<div>',
  99                  $objRevInfo->editSummary(),       // edit summary
 100                  $objRevInfo->editor(),            // editor info
 101                  html_sizechange($info['sizechange']), // size change indicator
 102                  $objRevInfo->currentIndicator(),  // current indicator (only when k=1)
 103                  '</div>',
 104              ]);
 105              $form->addHTML($html);
 106  
 107              $form->addTagClose('div');
 108              $form->addTagClose('li');
 109          }
 110          $form->addTagClose('ul');  // end of revision list
 111  
 112          // show button for diff view
 113          $form->addButton('do[diff]', $lang['diff2'])->attr('type', 'submit');
 114  
 115          $form->addTagClose('div'); // close div class=no
 116  
 117          print $form->toHTML('Revisions');
 118  
 119          // provide navigation for pagenated revision list (of pages and/or media files)
 120          print $this->htmlNavigation($id, $first, $hasNext);
 121      }
 122  
 123      /**
 124       * Display a list of Page Revisions
 125       *
 126       * @return void
 127       */
 128      protected function showPageRevisions($id)
 129      {
 130          global $lang;
 131  
 132          // get revisions, and set correct pagenation parameters (first, hasNext)
 133          $first   = $this->first;
 134          $hasNext = false;
 135          $revisions = $this->getRevisions($first, $hasNext);
 136  
 137          // print intro
 138          print p_locale_xhtml('revisions');
 139  
 140          // create the form
 141          $form = new Form([
 142                  'id' => 'page__revisions',
 143                  'class' => 'changes',
 144          ]);
 145          $form->addTagOpen('div')->addClass('no');
 146  
 147          // start listing
 148          $form->addTagOpen('ul');
 149          foreach ($revisions as $info) {
 150              $rev = $info['date'];
 151              $class = ($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) ? 'minor' : '';
 152              $form->addTagOpen('li')->addClass($class);
 153              $form->addTagOpen('div')->addClass('li');
 154  
 155              if (page_exists($id, $rev)) {
 156                  $form->addCheckbox('rev2[]')->val($rev);
 157              } else {
 158                  $form->addCheckbox('')->val($rev)->attr('disabled','disabled');
 159              }
 160              $form->addHTML(' ');
 161  
 162              $objRevInfo = $this->getObjRevInfo($info);
 163              $html = implode(' ', [
 164                  $objRevInfo->editDate(),          // edit date and time
 165                  $objRevInfo->difflink(),          // link to diffview icon
 166                  $objRevInfo->itemName(),          // name of page or media
 167                  $objRevInfo->editSummary(),       // edit summary
 168                  $objRevInfo->editor(),            // editor info
 169                  $objRevInfo->sizechange(),        // size change indicator
 170                  $objRevInfo->currentIndicator(),  // current indicator (only when k=1)
 171              ]);
 172              $form->addHTML($html);
 173              $form->addTagClose('div');
 174              $form->addTagClose('li');
 175          }
 176          $form->addTagClose('ul');  // end of revision list
 177  
 178          // show button for diff view
 179          $form->addButton('do[diff]', $lang['diff2'])->attr('type', 'submit');
 180  
 181          $form->addTagClose('div'); // close div class=no
 182  
 183          print $form->toHTML('Revisions');
 184  
 185          // provide navigation for pagenated revision list (of pages and/or media files)
 186          print $this->htmlNavigation($id, $first, $hasNext);
 187      }
 188  
 189  
 190      /**
 191       * Get revisions, and set correct pagenation parameters (first, hasNext)
 192       *
 193       * @param int  $first
 194       * @param bool $hasNext
 195       * @return array  revisions to be shown in a pagenated list
 196       * @see also https://www.dokuwiki.org/devel:changelog
 197       */
 198      protected function getRevisions(&$first, &$hasNext)
 199      {
 200          global $INFO, $conf;
 201  
 202          if ($this->media_id) {
 203              $changelog = new MediaChangeLog($this->media_id);
 204          } else {
 205              $changelog = new PageChangeLog($INFO['id']);
 206          }
 207  
 208          $revisions = [];
 209  
 210          /* we need to get one additional log entry to be able to
 211           * decide if this is the last page or is there another one.
 212           * see also Ui\Recent::getRecents()
 213           */
 214          $revlist = $changelog->getRevisions($first, $conf['recent'] +1);
 215          if (count($revlist) == 0 && $first != 0) {
 216              $first = 0;
 217              $revlist = $changelog->getRevisions($first, $conf['recent'] +1);
 218          }
 219          $exists = ($this->media_id) ? file_exists(mediaFN($this->media_id)) : $INFO['exists'];
 220          if ($first === 0 && $exists) {
 221              // add current page or media as revision[0]
 222              if ($this->media_id) {
 223                  $rev = filemtime(fullpath(mediaFN($this->media_id)));
 224                  $changelog->setChunkSize(1024);
 225                  $revinfo = $changelog->getRevisionInfo($rev) ?: array(
 226                          'date' => $rev,
 227                          'ip'   => null,
 228                          'type' => null,
 229                          'id'   => $this->media_id,
 230                          'user' => null,
 231                          'sum'  => null,
 232                          'extra' => null,
 233                          'sizechange' => null,
 234                  );
 235                  $revisions[] = $revinfo + array(
 236                          'media' => true,
 237                          'current' => true,
 238                  );
 239              } else {
 240                  $revisions[] = array(
 241                          'date' => $INFO['lastmod'],
 242                          'ip'   => null,
 243                          'type' => $INFO['meta']['last_change']['type'],
 244                          'id'   => $INFO['id'],
 245                          'user' => $INFO['editor'],
 246                          'sum'  => $INFO['sum'],
 247                          'extra' => null,
 248                          'sizechange' => $INFO['meta']['last_change']['sizechange'],
 249                          'current' => true,
 250                  );
 251              }
 252          }
 253  
 254          // decide if this is the last page or is there another one
 255          $hasNext = false;
 256          if (count($revlist) > $conf['recent']) {
 257              $hasNext = true;
 258              array_pop($revlist); // remove one additional log entry
 259          }
 260  
 261          // append each revison info array to the revisions
 262          foreach ($revlist as $rev) {
 263              if ($this->media_id) {
 264                  $revisions[] = $changelog->getRevisionInfo($rev) + array('media' => true);
 265              } else {
 266                  $revisions[] = $changelog->getRevisionInfo($rev);
 267              }
 268          }
 269          return $revisions;
 270      }
 271  
 272      /**
 273       * Navigation buttons for Pagenation (prev/next)
 274       *
 275       * @param string $id  page id or media id
 276       * @param int  $first
 277       * @param bool $hasNext
 278       * @return array  html
 279       */
 280      protected function htmlNavigation($id, $first, $hasNext)
 281      {
 282          global $conf;
 283  
 284          $html = '<div class="pagenav">';
 285          $last = $first + $conf['recent'];
 286          if ($first > 0) {
 287              $first = max($first - $conf['recent'], 0);
 288              $html.= '<div class="pagenav-prev">';
 289              if ($this->media_id) {
 290                  $html.= html_btn('newer', $id, "p", media_managerURL(['first' => $first], '&', false, true));
 291              } else {
 292                  $html.= html_btn('newer', $id, "p" ,['do' => 'revisions', 'first' => $first]);
 293              }
 294              $html.= '</div>';
 295          }
 296          if ($hasNext) {
 297              $html.= '<div class="pagenav-next">';
 298              if ($this->media_id) {
 299                  $html.= html_btn('older', $id, "n", media_managerURL(['first' => $last], '&', false, true));
 300              } else {
 301                  $html.= html_btn('older', $id, "n", ['do' => 'revisions', 'first' => $last]);
 302              }
 303              $html.= '</div>';
 304          }
 305          $html.= '</div>';
 306          return $html;
 307      }
 308  
 309      /**
 310       * Returns instance of objRevInfo
 311       *
 312       * @param array $info  Revision info structure of a page or media file
 313       * @return objRevInfo object (anonymous class)
 314       */
 315      protected function getObjRevInfo(array $info)
 316      {
 317          return new class ($info) // anonymous class (objRevInfo)
 318          {
 319              protected $info;
 320  
 321              public function __construct(array $info)
 322              {
 323                  $this->info = $info;
 324              }
 325  
 326              // current indicator
 327              public function currentIndicator()
 328              {
 329                  global $lang;
 330                  return ($this->info['current']) ? '('.$lang['current'].')' : '';
 331              }
 332  
 333              // edit date and time of the page or media file
 334              public function editDate()
 335              {
 336                  return '<span class="date">'. dformat($this->info['date']) .'</span>';
 337              }
 338  
 339              // edit summary
 340              public function editSummary()
 341              {
 342                  return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>';
 343              }
 344  
 345              // editor of the page or media file
 346              public function editor()
 347              {
 348                  // slightly different with display of Ui\Recent, i.e. external edit
 349                  global $lang;
 350                  $html = '<span class="user">';
 351                  if (!$this->info['user'] && !$this->info['ip']) {
 352                      $html.= '('.$lang['external_edit'].')';
 353                  } elseif ($this->info['user']) {
 354                      $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>';
 355                      if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>';
 356                  } else {
 357                      $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>';
 358                  }
 359                  $html.= '</span>';
 360                  return $html;
 361              }
 362  
 363              // name of the page or media file
 364              public function itemName()
 365              {
 366                  // slightly different with display of Ui\Recent, i.e. revison may not exists
 367                  $id = $this->info['id'];
 368                  $rev = $this->info['date'];
 369  
 370                  if (isset($this->info['media'])) {
 371                      // media file revision
 372                      if (isset($this->info['current'])) {
 373                          $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&');
 374                          $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
 375                      } elseif (file_exists(mediaFN($id, $rev))) {
 376                          $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&');
 377                          $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
 378                      } else {
 379                          $html = $id;
 380                      }
 381                      return $html;
 382                  } else {
 383                      // page revision
 384                      $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
 385                      if (!$display_name) $display_name = $id;
 386                      if ($this->info['current'] || page_exists($id, $rev)) {
 387                          $href = wl($id, "rev=$rev", false, '&');
 388                          $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>';
 389                      } else {
 390                          $html = $display_name;
 391                      }
 392                      return $html;
 393                  }
 394              }
 395  
 396              // icon difflink
 397              public function difflink()
 398              {
 399                  global $lang;
 400                  $id = $this->info['id'];
 401                  $rev = $this->info['date'];
 402  
 403                  if (isset($this->info['media'])) {
 404                      // media file revision
 405                      if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) {
 406                          $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
 407                      } else {
 408                          $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&');
 409                          $html = '<a href="'.$href.'" class="diff_link">'
 410                                . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
 411                                . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
 412                                . '</a> ';
 413                      }
 414                      return $html;
 415                  } else {
 416                      // page revision
 417                      if ($this->info['current'] || !page_exists($id, $rev)) {
 418                          $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
 419                      } else {
 420                          $href = wl($id, "rev=$rev,do=diff", false, '&');
 421                          $html = '<a href="'.$href.'" class="diff_link">'
 422                                . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
 423                                . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />'
 424                                . '</a>';
 425                      }
 426                      return $html;
 427                  }
 428              }
 429  
 430              // size change
 431              public function sizeChange()
 432              {
 433                  $class = 'sizechange';
 434                  $value = filesize_h(abs($this->info['sizechange']));
 435                  if ($this->info['sizechange'] > 0) {
 436                      $class .= ' positive';
 437                      $value = '+' . $value;
 438                  } elseif ($this->info['sizechange'] < 0) {
 439                      $class .= ' negative';
 440                      $value = '-' . $value;
 441                  } else {
 442                      $value = '±' . $value;
 443                  }
 444                  return '<span class="'.$class.'">'.$value.'</span>';
 445              }
 446          }; // end of anonymous class (objRevInfo)
 447      }
 448  
 449  }