[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> changelog.php (source)

   1  <?php
   2  /**
   3   * Changelog handling functions
   4   *
   5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author     Andreas Gohr <andi@splitbrain.org>
   7   */
   8  
   9  /**
  10   * parses a changelog line into it's components
  11   *
  12   * @author Ben Coburn <btcoburn@silicodon.net>
  13   *
  14   * @param string $line changelog line
  15   * @return array|bool parsed line or false
  16   */
  17  function parseChangelogLine($line) {
  18      $line = rtrim($line, "\n");
  19      $tmp = explode("\t", $line);
  20      if ($tmp!==false && count($tmp)>1) {
  21          $info = array();
  22          $info['date']  = (int)$tmp[0]; // unix timestamp
  23          $info['ip']    = $tmp[1]; // IPv4 address (127.0.0.1)
  24          $info['type']  = $tmp[2]; // log line type
  25          $info['id']    = $tmp[3]; // page id
  26          $info['user']  = $tmp[4]; // user name
  27          $info['sum']   = $tmp[5]; // edit summary (or action reason)
  28          $info['extra'] = $tmp[6]; // extra data (varies by line type)
  29          if(isset($tmp[7]) && $tmp[7] !== '') { //last item has line-end||
  30              $info['sizechange'] = (int) $tmp[7];
  31          } else {
  32              $info['sizechange'] = null;
  33          }
  34          return $info;
  35      } else {
  36          return false;
  37      }
  38  }
  39  
  40  /**
  41   * Add's an entry to the changelog and saves the metadata for the page
  42   *
  43   * @param int    $date      Timestamp of the change
  44   * @param String $id        Name of the affected page
  45   * @param String $type      Type of the change see DOKU_CHANGE_TYPE_*
  46   * @param String $summary   Summary of the change
  47   * @param mixed  $extra     In case of a revert the revision (timestmp) of the reverted page
  48   * @param array  $flags     Additional flags in a key value array.
  49   *                             Available flags:
  50   *                             - ExternalEdit - mark as an external edit.
  51   * @param null|int $sizechange Change of filesize
  52   *
  53   * @author Andreas Gohr <andi@splitbrain.org>
  54   * @author Esther Brunner <wikidesign@gmail.com>
  55   * @author Ben Coburn <btcoburn@silicodon.net>
  56   */
  57  function addLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='', $extra='', $flags=null, $sizechange = null){
  58      global $conf, $INFO;
  59      /** @var Input $INPUT */
  60      global $INPUT;
  61  
  62      // check for special flags as keys
  63      if (!is_array($flags)) { $flags = array(); }
  64      $flagExternalEdit = isset($flags['ExternalEdit']);
  65  
  66      $id = cleanid($id);
  67      $file = wikiFN($id);
  68      $created = @filectime($file);
  69      $minor = ($type===DOKU_CHANGE_TYPE_MINOR_EDIT);
  70      $wasRemoved = ($type===DOKU_CHANGE_TYPE_DELETE);
  71  
  72      if(!$date) $date = time(); //use current time if none supplied
  73      $remote = (!$flagExternalEdit)?clientIP(true):'127.0.0.1';
  74      $user   = (!$flagExternalEdit)?$INPUT->server->str('REMOTE_USER'):'';
  75      if($sizechange === null) {
  76          $sizechange = '';
  77      } else {
  78          $sizechange = (int) $sizechange;
  79      }
  80  
  81      $strip = array("\t", "\n");
  82      $logline = array(
  83          'date'       => $date,
  84          'ip'         => $remote,
  85          'type'       => str_replace($strip, '', $type),
  86          'id'         => $id,
  87          'user'       => $user,
  88          'sum'        => \dokuwiki\Utf8\PhpString::substr(str_replace($strip, '', $summary), 0, 255),
  89          'extra'      => str_replace($strip, '', $extra),
  90          'sizechange' => $sizechange
  91      );
  92  
  93      $wasCreated = ($type===DOKU_CHANGE_TYPE_CREATE);
  94      $wasReverted = ($type===DOKU_CHANGE_TYPE_REVERT);
  95      // update metadata
  96      if (!$wasRemoved) {
  97          $oldmeta = p_read_metadata($id);
  98          $meta    = array();
  99          if (
 100              $wasCreated && (
 101                  empty($oldmeta['persistent']['date']['created']) ||
 102                  $oldmeta['persistent']['date']['created'] === $created
 103              )
 104          ){
 105              // newly created
 106              $meta['date']['created'] = $created;
 107              if ($user){
 108                  $meta['creator'] = isset($INFO) ? $INFO['userinfo']['name'] : null;
 109                  $meta['user']    = $user;
 110              }
 111          } elseif (($wasCreated || $wasReverted) && !empty($oldmeta['persistent']['date']['created'])) {
 112              // re-created / restored
 113              $meta['date']['created']  = $oldmeta['persistent']['date']['created'];
 114              $meta['date']['modified'] = $created; // use the files ctime here
 115              $meta['creator'] = $oldmeta['persistent']['creator'];
 116              if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
 117          } elseif (!$minor) {   // non-minor modification
 118              $meta['date']['modified'] = $date;
 119              if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
 120          }
 121          $meta['last_change'] = $logline;
 122          p_set_metadata($id, $meta);
 123      }
 124  
 125      // add changelog lines
 126      $logline = implode("\t", $logline)."\n";
 127      io_saveFile(metaFN($id,'.changes'),$logline,true); //page changelog
 128      io_saveFile($conf['changelog'],$logline,true); //global changelog cache
 129  }
 130  
 131  /**
 132   * Add's an entry to the media changelog
 133   *
 134   * @author Michael Hamann <michael@content-space.de>
 135   * @author Andreas Gohr <andi@splitbrain.org>
 136   * @author Esther Brunner <wikidesign@gmail.com>
 137   * @author Ben Coburn <btcoburn@silicodon.net>
 138   *
 139   * @param int    $date      Timestamp of the change
 140   * @param String $id        Name of the affected page
 141   * @param String $type      Type of the change see DOKU_CHANGE_TYPE_*
 142   * @param String $summary   Summary of the change
 143   * @param mixed  $extra     In case of a revert the revision (timestmp) of the reverted page
 144   * @param array  $flags     Additional flags in a key value array.
 145   *                             Available flags:
 146   *                             - (none, so far)
 147   * @param null|int $sizechange Change of filesize
 148   */
 149  function addMediaLogEntry(
 150      $date,
 151      $id,
 152      $type=DOKU_CHANGE_TYPE_EDIT,
 153      $summary='',
 154      $extra='',
 155      $flags=null,
 156      $sizechange = null)
 157  {
 158      global $conf;
 159      /** @var Input $INPUT */
 160      global $INPUT;
 161  
 162      $id = cleanid($id);
 163  
 164      if(!$date) $date = time(); //use current time if none supplied
 165      $remote = clientIP(true);
 166      $user   = $INPUT->server->str('REMOTE_USER');
 167      if($sizechange === null) {
 168          $sizechange = '';
 169      } else {
 170          $sizechange = (int) $sizechange;
 171      }
 172  
 173      $strip = array("\t", "\n");
 174      $logline = array(
 175          'date'       => $date,
 176          'ip'         => $remote,
 177          'type'       => str_replace($strip, '', $type),
 178          'id'         => $id,
 179          'user'       => $user,
 180          'sum'        => \dokuwiki\Utf8\PhpString::substr(str_replace($strip, '', $summary), 0, 255),
 181          'extra'      => str_replace($strip, '', $extra),
 182          'sizechange' => $sizechange
 183      );
 184  
 185      // add changelog lines
 186      $logline = implode("\t", $logline)."\n";
 187      io_saveFile($conf['media_changelog'],$logline,true); //global media changelog cache
 188      io_saveFile(mediaMetaFN($id,'.changes'),$logline,true); //media file's changelog
 189  }
 190  
 191  /**
 192   * returns an array of recently changed files using the
 193   * changelog
 194   *
 195   * The following constants can be used to control which changes are
 196   * included. Add them together as needed.
 197   *
 198   * RECENTS_SKIP_DELETED   - don't include deleted pages
 199   * RECENTS_SKIP_MINORS    - don't include minor changes
 200   * RECENTS_ONLY_CREATION  - only include new created pages and media
 201   * RECENTS_SKIP_SUBSPACES - don't include subspaces
 202   * RECENTS_MEDIA_CHANGES  - return media changes instead of page changes
 203   * RECENTS_MEDIA_PAGES_MIXED  - return both media changes and page changes
 204   *
 205   * @param int    $first   number of first entry returned (for paginating
 206   * @param int    $num     return $num entries
 207   * @param string $ns      restrict to given namespace
 208   * @param int    $flags   see above
 209   * @return array recently changed files
 210   *
 211   * @author Ben Coburn <btcoburn@silicodon.net>
 212   * @author Kate Arzamastseva <pshns@ukr.net>
 213   */
 214  function getRecents($first,$num,$ns='',$flags=0){
 215      global $conf;
 216      $recent = array();
 217      $count  = 0;
 218  
 219      if(!$num)
 220          return $recent;
 221  
 222      // read all recent changes. (kept short)
 223      if ($flags & RECENTS_MEDIA_CHANGES) {
 224          $lines = @file($conf['media_changelog']) ?: [];
 225      } else {
 226          $lines = @file($conf['changelog']) ?: [];
 227      }
 228      if (!is_array($lines)) {
 229          $lines = array();
 230      }
 231      $lines_position = count($lines)-1;
 232      $media_lines_position = 0;
 233      $media_lines = array();
 234  
 235      if ($flags & RECENTS_MEDIA_PAGES_MIXED) {
 236          $media_lines = @file($conf['media_changelog']) ?: [];
 237          if (!is_array($media_lines)) {
 238              $media_lines = array();
 239          }
 240          $media_lines_position = count($media_lines)-1;
 241      }
 242  
 243      $seen = array(); // caches seen lines, _handleRecent() skips them
 244  
 245      // handle lines
 246      while ($lines_position >= 0 || (($flags & RECENTS_MEDIA_PAGES_MIXED) && $media_lines_position >=0)) {
 247          if (empty($rec) && $lines_position >= 0) {
 248              $rec = _handleRecent(@$lines[$lines_position], $ns, $flags, $seen);
 249              if (!$rec) {
 250                  $lines_position --;
 251                  continue;
 252              }
 253          }
 254          if (($flags & RECENTS_MEDIA_PAGES_MIXED) && empty($media_rec) && $media_lines_position >= 0) {
 255              $media_rec = _handleRecent(
 256                  @$media_lines[$media_lines_position],
 257                  $ns,
 258                  $flags | RECENTS_MEDIA_CHANGES,
 259                  $seen
 260              );
 261              if (!$media_rec) {
 262                  $media_lines_position --;
 263                  continue;
 264              }
 265          }
 266          if (($flags & RECENTS_MEDIA_PAGES_MIXED) && @$media_rec['date'] >= @$rec['date']) {
 267              $media_lines_position--;
 268              $x = $media_rec;
 269              $x['media'] = true;
 270              $media_rec = false;
 271          } else {
 272              $lines_position--;
 273              $x = $rec;
 274              if ($flags & RECENTS_MEDIA_CHANGES) $x['media'] = true;
 275              $rec = false;
 276          }
 277          if(--$first >= 0) continue; // skip first entries
 278          $recent[] = $x;
 279          $count++;
 280          // break when we have enough entries
 281          if($count >= $num){ break; }
 282      }
 283      return $recent;
 284  }
 285  
 286  /**
 287   * returns an array of files changed since a given time using the
 288   * changelog
 289   *
 290   * The following constants can be used to control which changes are
 291   * included. Add them together as needed.
 292   *
 293   * RECENTS_SKIP_DELETED   - don't include deleted pages
 294   * RECENTS_SKIP_MINORS    - don't include minor changes
 295   * RECENTS_ONLY_CREATION  - only include new created pages and media
 296   * RECENTS_SKIP_SUBSPACES - don't include subspaces
 297   * RECENTS_MEDIA_CHANGES  - return media changes instead of page changes
 298   *
 299   * @param int    $from    date of the oldest entry to return
 300   * @param int    $to      date of the newest entry to return (for pagination, optional)
 301   * @param string $ns      restrict to given namespace (optional)
 302   * @param int    $flags   see above (optional)
 303   * @return array of files
 304   *
 305   * @author Michael Hamann <michael@content-space.de>
 306   * @author Ben Coburn <btcoburn@silicodon.net>
 307   */
 308  function getRecentsSince($from,$to=null,$ns='',$flags=0){
 309      global $conf;
 310      $recent = array();
 311  
 312      if($to && $to < $from)
 313          return $recent;
 314  
 315      // read all recent changes. (kept short)
 316      if ($flags & RECENTS_MEDIA_CHANGES) {
 317          $lines = @file($conf['media_changelog']);
 318      } else {
 319          $lines = @file($conf['changelog']);
 320      }
 321      if(!$lines) return $recent;
 322  
 323      // we start searching at the end of the list
 324      $lines = array_reverse($lines);
 325  
 326      // handle lines
 327      $seen = array(); // caches seen lines, _handleRecent() skips them
 328  
 329      foreach($lines as $line){
 330          $rec = _handleRecent($line, $ns, $flags, $seen);
 331          if($rec !== false) {
 332              if ($rec['date'] >= $from) {
 333                  if (!$to || $rec['date'] <= $to) {
 334                      $recent[] = $rec;
 335                  }
 336              } else {
 337                  break;
 338              }
 339          }
 340      }
 341  
 342      return array_reverse($recent);
 343  }
 344  
 345  /**
 346   * Internal function used by getRecents
 347   *
 348   * don't call directly
 349   *
 350   * @see getRecents()
 351   * @author Andreas Gohr <andi@splitbrain.org>
 352   * @author Ben Coburn <btcoburn@silicodon.net>
 353   *
 354   * @param string $line   changelog line
 355   * @param string $ns     restrict to given namespace
 356   * @param int    $flags  flags to control which changes are included
 357   * @param array  $seen   listing of seen pages
 358   * @return array|bool    false or array with info about a change
 359   */
 360  function _handleRecent($line,$ns,$flags,&$seen){
 361      if(empty($line)) return false;   //skip empty lines
 362  
 363      // split the line into parts
 364      $recent = parseChangelogLine($line);
 365      if ($recent===false) { return false; }
 366  
 367      // skip seen ones
 368      if(isset($seen[$recent['id']])) return false;
 369  
 370      // skip changes, of only new items are requested
 371      if($recent['type']!==DOKU_CHANGE_TYPE_CREATE && ($flags & RECENTS_ONLY_CREATION)) return false;
 372  
 373      // skip minors
 374      if($recent['type']===DOKU_CHANGE_TYPE_MINOR_EDIT && ($flags & RECENTS_SKIP_MINORS)) return false;
 375  
 376      // remember in seen to skip additional sights
 377      $seen[$recent['id']] = 1;
 378  
 379      // check if it's a hidden page
 380      if(isHiddenPage($recent['id'])) return false;
 381  
 382      // filter namespace
 383      if (($ns) && (strpos($recent['id'],$ns.':') !== 0)) return false;
 384  
 385      // exclude subnamespaces
 386      if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($recent['id']) != $ns)) return false;
 387  
 388      // check ACL
 389      if ($flags & RECENTS_MEDIA_CHANGES) {
 390          $recent['perms'] = auth_quickaclcheck(getNS($recent['id']).':*');
 391      } else {
 392          $recent['perms'] = auth_quickaclcheck($recent['id']);
 393      }
 394      if ($recent['perms'] < AUTH_READ) return false;
 395  
 396      // check existance
 397      if($flags & RECENTS_SKIP_DELETED){
 398          $fn = (($flags & RECENTS_MEDIA_CHANGES) ? mediaFN($recent['id']) : wikiFN($recent['id']));
 399          if(!file_exists($fn)) return false;
 400      }
 401  
 402      return $recent;
 403  }