[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> Ajax.php (source)

   1  <?php
   2  
   3  namespace dokuwiki;
   4  
   5  use dokuwiki\Extension\Event;
   6  use dokuwiki\Ui\MediaDiff;
   7  use dokuwiki\Ui\Index;
   8  use dokuwiki\Ui;
   9  use dokuwiki\Utf8\Sort;
  10  
  11  /**
  12   * Manage all builtin AJAX calls
  13   *
  14   * @todo The calls should be refactored out to their own proper classes
  15   * @package dokuwiki
  16   */
  17  class Ajax
  18  {
  19      /**
  20       * Execute the given call
  21       *
  22       * @param string $call name of the ajax call
  23       */
  24      public function __construct($call)
  25      {
  26          $callfn = 'call' . ucfirst($call);
  27          if (method_exists($this, $callfn)) {
  28              $this->$callfn();
  29          } else {
  30              $evt = new Event('AJAX_CALL_UNKNOWN', $call);
  31              if ($evt->advise_before()) {
  32                  echo "AJAX call '" . hsc($call) . "' unknown!\n";
  33              } else {
  34                  $evt->advise_after();
  35                  unset($evt);
  36              }
  37          }
  38      }
  39  
  40      /**
  41       * Searches for matching pagenames
  42       *
  43       * @author Andreas Gohr <andi@splitbrain.org>
  44       */
  45      protected function callQsearch()
  46      {
  47          global $lang;
  48          global $INPUT;
  49  
  50          $maxnumbersuggestions = 50;
  51  
  52          $query = $INPUT->post->str('q');
  53          if (empty($query)) $query = $INPUT->get->str('q');
  54          if (empty($query)) return;
  55  
  56          $query = urldecode($query);
  57  
  58          $data = ft_pageLookup($query, true, useHeading('navigation'));
  59  
  60          if ($data === []) return;
  61  
  62          echo '<strong>' . $lang['quickhits'] . '</strong>';
  63          echo '<ul>';
  64          $counter = 0;
  65          foreach ($data as $id => $title) {
  66              if (useHeading('navigation')) {
  67                  $name = $title;
  68              } else {
  69                  $ns = getNS($id);
  70                  if ($ns) {
  71                      $name = noNS($id) . ' (' . $ns . ')';
  72                  } else {
  73                      $name = $id;
  74                  }
  75              }
  76              echo '<li>' . html_wikilink(':' . $id, $name) . '</li>';
  77  
  78              $counter++;
  79              if ($counter > $maxnumbersuggestions) {
  80                  echo '<li>...</li>';
  81                  break;
  82              }
  83          }
  84          echo '</ul>';
  85      }
  86  
  87      /**
  88       * Support OpenSearch suggestions
  89       *
  90       * @link   http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.0
  91       * @author Mike Frysinger <vapier@gentoo.org>
  92       */
  93      protected function callSuggestions()
  94      {
  95          global $INPUT;
  96  
  97          $query = cleanID($INPUT->post->str('q'));
  98          if (empty($query)) $query = cleanID($INPUT->get->str('q'));
  99          if (empty($query)) return;
 100  
 101          $data = ft_pageLookup($query);
 102          if ($data === []) return;
 103          $data = array_keys($data);
 104  
 105          // limit results to 15 hits
 106          $data = array_slice($data, 0, 15);
 107          $data = array_map('trim', $data);
 108          $data = array_map('noNS', $data);
 109          $data = array_unique($data);
 110          Sort::sort($data);
 111  
 112          /* now construct a json */
 113          $suggestions = [
 114              $query, // the original query
 115              $data, // some suggestions
 116              [], // no description
 117              [], // no urls
 118          ];
 119  
 120          header('Content-Type: application/x-suggestions+json');
 121          echo json_encode($suggestions, JSON_THROW_ON_ERROR);
 122      }
 123  
 124      /**
 125       * Refresh a page lock and save draft
 126       *
 127       * Andreas Gohr <andi@splitbrain.org>
 128       */
 129      protected function callLock()
 130      {
 131          global $ID;
 132          global $INFO;
 133          global $INPUT;
 134  
 135          $ID = cleanID($INPUT->post->str('id'));
 136          if (empty($ID)) return;
 137  
 138          $INFO = pageinfo();
 139  
 140          $response = [
 141              'errors' => [],
 142              'lock' => '0',
 143              'draft' => '',
 144          ];
 145          if (!$INFO['writable']) {
 146              $response['errors'][] = 'Permission to write this page has been denied.';
 147              echo json_encode($response);
 148              return;
 149          }
 150  
 151          if (!checklock($ID)) {
 152              lock($ID);
 153              $response['lock'] = '1';
 154          }
 155  
 156          $draft = new Draft($ID, $INFO['client']);
 157          if ($draft->saveDraft()) {
 158              $response['draft'] = $draft->getDraftMessage();
 159          } else {
 160              $response['errors'] = array_merge($response['errors'], $draft->getErrors());
 161          }
 162          echo json_encode($response, JSON_THROW_ON_ERROR);
 163      }
 164  
 165      /**
 166       * Delete a draft
 167       *
 168       * @author Andreas Gohr <andi@splitbrain.org>
 169       */
 170      protected function callDraftdel()
 171      {
 172          global $INPUT;
 173          $id = cleanID($INPUT->str('id'));
 174          if (empty($id)) return;
 175  
 176          $client = $INPUT->server->str('REMOTE_USER');
 177          if (!$client) $client = clientIP(true);
 178  
 179          $draft = new Draft($id, $client);
 180          if ($draft->isDraftAvailable() && checkSecurityToken()) {
 181              $draft->deleteDraft();
 182          }
 183      }
 184  
 185      /**
 186       * Return subnamespaces for the Mediamanager
 187       *
 188       * @author Andreas Gohr <andi@splitbrain.org>
 189       */
 190      protected function callMedians()
 191      {
 192          global $conf;
 193          global $INPUT;
 194  
 195          // wanted namespace
 196          $ns = cleanID($INPUT->post->str('ns'));
 197          $dir = utf8_encodeFN(str_replace(':', '/', $ns));
 198  
 199          $lvl = count(explode(':', $ns));
 200  
 201          $data = [];
 202          search($data, $conf['mediadir'], 'search_index', ['nofiles' => true], $dir);
 203          foreach (array_keys($data) as $item) {
 204              $data[$item]['level'] = $lvl + 1;
 205          }
 206          echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
 207      }
 208  
 209      /**
 210       * Return list of files for the Mediamanager
 211       *
 212       * @author Andreas Gohr <andi@splitbrain.org>
 213       */
 214      protected function callMedialist()
 215      {
 216          global $NS;
 217          global $INPUT;
 218  
 219          $NS = cleanID($INPUT->post->str('ns'));
 220          $sort = $INPUT->post->bool('recent') ? 'date' : 'natural';
 221          if ($INPUT->post->str('do') == 'media') {
 222              tpl_mediaFileList();
 223          } else {
 224              tpl_mediaContent(true, $sort);
 225          }
 226      }
 227  
 228      /**
 229       * Return the content of the right column
 230       * (image details) for the Mediamanager
 231       *
 232       * @author Kate Arzamastseva <pshns@ukr.net>
 233       */
 234      protected function callMediadetails()
 235      {
 236          global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT;
 237          $fullscreen = true;
 238          require_once (DOKU_INC . 'lib/exe/mediamanager.php');
 239  
 240          $image = '';
 241          if ($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
 242          if (isset($IMG)) $image = $IMG;
 243          if (isset($JUMPTO)) $image = $JUMPTO;
 244          $rev = false;
 245          if (isset($REV) && !$JUMPTO) $rev = $REV;
 246  
 247          html_msgarea();
 248          tpl_mediaFileDetails($image, $rev);
 249      }
 250  
 251      /**
 252       * Returns image diff representation for mediamanager
 253       *
 254       * @author Kate Arzamastseva <pshns@ukr.net>
 255       */
 256      protected function callMediadiff()
 257      {
 258          global $INPUT;
 259  
 260          $image = '';
 261          if ($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
 262          (new MediaDiff($image))->preference('fromAjax', true)->show();
 263      }
 264  
 265      /**
 266       * Manages file uploads
 267       *
 268       * @author Kate Arzamastseva <pshns@ukr.net>
 269       */
 270      protected function callMediaupload()
 271      {
 272          global $NS, $MSG, $INPUT;
 273  
 274          $id = '';
 275          if (isset($_FILES['qqfile']['tmp_name'])) {
 276              $id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
 277          } elseif ($INPUT->get->has('qqfile')) {
 278              $id = $INPUT->get->str('qqfile');
 279          }
 280  
 281          $id = cleanID($id);
 282  
 283          $NS = $INPUT->str('ns');
 284          $ns = $NS . ':' . getNS($id);
 285  
 286          $AUTH = auth_quickaclcheck("$ns:*");
 287          if ($AUTH >= AUTH_UPLOAD) {
 288              io_createNamespace("$ns:xxx", 'media');
 289          }
 290  
 291          if (isset($_FILES['qqfile']['error']) && $_FILES['qqfile']['error']) unset($_FILES['qqfile']);
 292  
 293          $res = false;
 294          if (isset($_FILES['qqfile']['tmp_name'])) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
 295          if ($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
 296  
 297          if ($res) {
 298              $result = [
 299                  'success' => true,
 300                  'link' => media_managerURL(['ns' => $ns, 'image' => $NS . ':' . $id], '&'),
 301                  'id' => $NS . ':' . $id,
 302                  'ns' => $NS
 303              ];
 304          } else {
 305              $error = '';
 306              if (isset($MSG)) {
 307                  foreach ($MSG as $msg) {
 308                      $error .= $msg['msg'];
 309                  }
 310              }
 311              $result = ['error' => $error, 'ns' => $NS];
 312          }
 313  
 314          header('Content-Type: application/json');
 315          echo json_encode($result, JSON_THROW_ON_ERROR);
 316      }
 317  
 318      /**
 319       * Return sub index for index view
 320       *
 321       * @author Andreas Gohr <andi@splitbrain.org>
 322       */
 323      protected function callIndex()
 324      {
 325          global $conf;
 326          global $INPUT;
 327  
 328          // wanted namespace
 329          $ns = cleanID($INPUT->post->str('idx'));
 330          $dir = utf8_encodeFN(str_replace(':', '/', $ns));
 331  
 332          $lvl = count(explode(':', $ns));
 333  
 334          $data = [];
 335          search($data, $conf['datadir'], 'search_index', ['ns' => $ns], $dir);
 336          foreach (array_keys($data) as $item) {
 337              $data[$item]['level'] = $lvl + 1;
 338          }
 339          $idx = new Index();
 340          echo html_buildlist($data, 'idx', [$idx,'formatListItem'], [$idx,'tagListItem']);
 341      }
 342  
 343      /**
 344       * List matching namespaces and pages for the link wizard
 345       *
 346       * @author Andreas Gohr <gohr@cosmocode.de>
 347       */
 348      protected function callLinkwiz()
 349      {
 350          global $conf;
 351          global $lang;
 352          global $INPUT;
 353  
 354          $q = ltrim(trim($INPUT->post->str('q')), ':');
 355          $id = noNS($q);
 356          $ns = getNS($q);
 357  
 358          $ns = cleanID($ns);
 359  
 360          $id = cleanID($id);
 361  
 362          $nsd = utf8_encodeFN(str_replace(':', '/', $ns));
 363  
 364          $data = [];
 365          if ($q !== '' && $ns === '') {
 366              // use index to lookup matching pages
 367              $pages = ft_pageLookup($id, true);
 368  
 369              // If 'useheading' option is 'always' or 'content',
 370              // search page titles with original query as well.
 371              if ($conf['useheading'] == '1' || $conf['useheading'] == 'content') {
 372                  $pages = array_merge($pages, ft_pageLookup($q, true, true));
 373                  asort($pages, SORT_STRING);
 374              }
 375  
 376              // result contains matches in pages and namespaces
 377              // we now extract the matching namespaces to show
 378              // them seperately
 379              $dirs = [];
 380  
 381              foreach ($pages as $pid => $title) {
 382                  if (strpos(getNS($pid), $id) !== false) {
 383                      // match was in the namespace
 384                      $dirs[getNS($pid)] = 1; // assoc array avoids dupes
 385                  } else {
 386                      // it is a matching page, add it to the result
 387                      $data[] = ['id' => $pid, 'title' => $title, 'type' => 'f'];
 388                  }
 389                  unset($pages[$pid]);
 390              }
 391              foreach (array_keys($dirs) as $dir) {
 392                  $data[] = ['id' => $dir, 'type' => 'd'];
 393              }
 394          } else {
 395              $opts = [
 396                  'depth' => 1,
 397                  'listfiles' => true,
 398                  'listdirs' => true,
 399                  'pagesonly' => true,
 400                  'firsthead' => true,
 401                  'sneakyacl' => $conf['sneaky_index']
 402              ];
 403              if ($id) $opts['filematch'] = '^.*\/' . $id;
 404              if ($id) $opts['dirmatch'] = '^.*\/' . $id;
 405              search($data, $conf['datadir'], 'search_universal', $opts, $nsd);
 406  
 407              // add back to upper
 408              if ($ns) {
 409                  array_unshift(
 410                      $data,
 411                      ['id' => getNS($ns), 'type' => 'u']
 412                  );
 413              }
 414          }
 415  
 416          // fixme sort results in a useful way ?
 417  
 418          if (!count($data)) {
 419              echo $lang['nothingfound'];
 420              exit;
 421          }
 422  
 423          // output the found data
 424          $even = 1;
 425          foreach ($data as $item) {
 426              $even *= -1; //zebra
 427  
 428              if (($item['type'] == 'd' || $item['type'] == 'u') && $item['id'] !== '') $item['id'] .= ':';
 429              $link = wl($item['id']);
 430  
 431              echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">';
 432  
 433              if ($item['type'] == 'u') {
 434                  $name = $lang['upperns'];
 435              } else {
 436                  $name = hsc($item['id']);
 437              }
 438  
 439              echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>';
 440  
 441              if (!blank($item['title'])) {
 442                  echo '<span>' . hsc($item['title']) . '</span>';
 443              }
 444              echo '</div>';
 445          }
 446      }
 447  }