[ 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                  if (isset($INFO['meta']['last_change'])) {
 241                      $type = $INFO['meta']['last_change']['type'];
 242                      $sizechange = $INFO['meta']['last_change']['sizechange'];
 243                  } else {
 244                      $type = $sizechange = null;
 245                  }
 246  
 247                  $revisions[] = array(
 248                          'date' => $INFO['lastmod'],
 249                          'ip'   => null,
 250                          'type' => $type,
 251                          'id'   => $INFO['id'],
 252                          'user' => $INFO['editor'],
 253                          'sum'  => $INFO['sum'],
 254                          'extra' => null,
 255                          'sizechange' => $sizechange,
 256                          'current' => true,
 257                  );
 258              }
 259          }
 260  
 261          // decide if this is the last page or is there another one
 262          $hasNext = false;
 263          if (count($revlist) > $conf['recent']) {
 264              $hasNext = true;
 265              array_pop($revlist); // remove one additional log entry
 266          }
 267  
 268          // append each revison info array to the revisions
 269          foreach ($revlist as $rev) {
 270              if ($this->media_id) {
 271                  $revisions[] = $changelog->getRevisionInfo($rev) + array('media' => true);
 272              } else {
 273                  $revisions[] = $changelog->getRevisionInfo($rev);
 274              }
 275          }
 276          return $revisions;
 277      }
 278  
 279      /**
 280       * Navigation buttons for Pagenation (prev/next)
 281       *
 282       * @param string $id  page id or media id
 283       * @param int  $first
 284       * @param bool $hasNext
 285       * @return array  html
 286       */
 287      protected function htmlNavigation($id, $first, $hasNext)
 288      {
 289          global $conf;
 290  
 291          $html = '<div class="pagenav">';
 292          $last = $first + $conf['recent'];
 293          if ($first > 0) {
 294              $first = max($first - $conf['recent'], 0);
 295              $html.= '<div class="pagenav-prev">';
 296              if ($this->media_id) {
 297                  $html.= html_btn('newer', $id, "p", media_managerURL(['first' => $first], '&', false, true));
 298              } else {
 299                  $html.= html_btn('newer', $id, "p" ,['do' => 'revisions', 'first' => $first]);
 300              }
 301              $html.= '</div>';
 302          }
 303          if ($hasNext) {
 304              $html.= '<div class="pagenav-next">';
 305              if ($this->media_id) {
 306                  $html.= html_btn('older', $id, "n", media_managerURL(['first' => $last], '&', false, true));
 307              } else {
 308                  $html.= html_btn('older', $id, "n", ['do' => 'revisions', 'first' => $last]);
 309              }
 310              $html.= '</div>';
 311          }
 312          $html.= '</div>';
 313          return $html;
 314      }
 315  
 316      /**
 317       * Returns instance of objRevInfo
 318       *
 319       * @param array $info  Revision info structure of a page or media file
 320       * @return objRevInfo object (anonymous class)
 321       */
 322      protected function getObjRevInfo(array $info)
 323      {
 324          return new class ($info) // anonymous class (objRevInfo)
 325          {
 326              protected $info;
 327  
 328              public function __construct(array $info)
 329              {
 330                  if (!isset($info['current'])) {
 331                      $info['current'] = false;
 332                  }
 333                  $this->info = $info;
 334              }
 335  
 336              // current indicator
 337              public function currentIndicator()
 338              {
 339                  global $lang;
 340                  return ($this->info['current']) ? '('.$lang['current'].')' : '';
 341              }
 342  
 343              // edit date and time of the page or media file
 344              public function editDate()
 345              {
 346                  return '<span class="date">'. dformat($this->info['date']) .'</span>';
 347              }
 348  
 349              // edit summary
 350              public function editSummary()
 351              {
 352                  return '<span class="sum">'.' – '. hsc($this->info['sum']).'</span>';
 353              }
 354  
 355              // editor of the page or media file
 356              public function editor()
 357              {
 358                  // slightly different with display of Ui\Recent, i.e. external edit
 359                  global $lang;
 360                  $html = '<span class="user">';
 361                  if (!$this->info['user'] && !$this->info['ip']) {
 362                      $html.= '('.$lang['external_edit'].')';
 363                  } elseif ($this->info['user']) {
 364                      $html.= '<bdi>'. editorinfo($this->info['user']) .'</bdi>';
 365                      if (auth_ismanager()) $html.= ' <bdo dir="ltr">('. $this->info['ip'] .')</bdo>';
 366                  } else {
 367                      $html.= '<bdo dir="ltr">'. $this->info['ip'] .'</bdo>';
 368                  }
 369                  $html.= '</span>';
 370                  return $html;
 371              }
 372  
 373              // name of the page or media file
 374              public function itemName()
 375              {
 376                  // slightly different with display of Ui\Recent, i.e. revison may not exists
 377                  $id = $this->info['id'];
 378                  $rev = $this->info['date'];
 379  
 380                  if (isset($this->info['media'])) {
 381                      // media file revision
 382                      if (isset($this->info['current'])) {
 383                          $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view'], '&');
 384                          $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
 385                      } elseif (file_exists(mediaFN($id, $rev))) {
 386                          $href = media_managerURL(['image'=> $id, 'tab_details'=> 'view', 'rev'=> $rev], '&');
 387                          $html = '<a href="'.$href.'" class="wikilink1">'.$id.'</a>';
 388                      } else {
 389                          $html = $id;
 390                      }
 391                      return $html;
 392                  } else {
 393                      // page revision
 394                      $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
 395                      if (!$display_name) $display_name = $id;
 396                      if ($this->info['current'] || page_exists($id, $rev)) {
 397                          $href = wl($id, "rev=$rev", false, '&');
 398                          $html = '<a href="'.$href.'" class="wikilink1">'.$display_name.'</a>';
 399                      } else {
 400                          $html = $display_name;
 401                      }
 402                      return $html;
 403                  }
 404              }
 405  
 406              // icon difflink
 407              public function difflink()
 408              {
 409                  global $lang;
 410                  $id = $this->info['id'];
 411                  $rev = $this->info['date'];
 412  
 413                  if (isset($this->info['media'])) {
 414                      // media file revision
 415                      if (isset($this->info['current']) || !file_exists(mediaFN($id, $rev))) {
 416                          $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
 417                      } else {
 418                          $href = media_managerURL(['image'=> $id, 'rev'=> $rev, 'mediado'=>'diff'], '&');
 419                          $html = '<a href="'.$href.'" class="diff_link">'
 420                                . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
 421                                . ' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
 422                                . '</a> ';
 423                      }
 424                      return $html;
 425                  } else {
 426                      // page revision
 427                      if ($this->info['current'] || !page_exists($id, $rev)) {
 428                          $html = '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
 429                      } else {
 430                          $href = wl($id, "rev=$rev,do=diff", false, '&');
 431                          $html = '<a href="'.$href.'" class="diff_link">'
 432                                . '<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
 433                                . ' title="'.$lang['diff'].'" alt="'.$lang['diff'].'" />'
 434                                . '</a>';
 435                      }
 436                      return $html;
 437                  }
 438              }
 439  
 440              // size change
 441              public function sizeChange()
 442              {
 443                  $class = 'sizechange';
 444                  $value = filesize_h(abs($this->info['sizechange']));
 445                  if ($this->info['sizechange'] > 0) {
 446                      $class .= ' positive';
 447                      $value = '+' . $value;
 448                  } elseif ($this->info['sizechange'] < 0) {
 449                      $class .= ' negative';
 450                      $value = '-' . $value;
 451                  } else {
 452                      $value = '±' . $value;
 453                  }
 454                  return '<span class="'.$class.'">'.$value.'</span>';
 455              }
 456          }; // end of anonymous class (objRevInfo)
 457      }
 458  
 459  }