Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/lib/txplib_wrapper.php - 937 lines - 28273 bytes - Summary - Text - Print

Description: Textpattern Wrapper Class for Textpattern 4.0.x. Main goal for this class is to be used as a textpattern data wrapper by any code which needs to have access to the textpattern articles data, like XML-RPC, Atom, Moblogging or other external implementations.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * https://textpattern.com/
   6   *
   7   * Copyright (C) 2020 The Textpattern Development Team
   8   *
   9   * This file is part of Textpattern.
  10   *
  11   * Textpattern is free software; you can redistribute it and/or
  12   * modify it under the terms of the GNU General Public License
  13   * as published by the Free Software Foundation, version 2.
  14   *
  15   * Textpattern is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License
  21   * along with Textpattern. If not, see <https://www.gnu.org/licenses/>.
  22   */
  23  
  24  /**
  25   * Textpattern Wrapper Class for Textpattern 4.0.x.
  26   *
  27   * Main goal for this class is to be used as a textpattern data wrapper by
  28   * any code which needs to have access to the textpattern articles data,
  29   * like XML-RPC, Atom, Moblogging or other external implementations.
  30   *
  31   * This class requires including some Textpattern files in order to work
  32   * properly. See RPC Server implementation to view an example of the required
  33   * files and predefined variables.
  34   *
  35   * @link      https://web.archive.org/web/20141201035729/http://txp.kusor.com/wrapper
  36   * @author    Pedro Palazón
  37   * @copyright 2005-2008 The Textpattern Development Team
  38   */
  39  
  40  if (!defined('txpath')) {
  41      die('txpath is undefined.');
  42  }
  43  
  44  include_once txpath.'/include/txp_auth.php';
  45  
  46  if (!defined('LEAVE_TEXT_UNTOUCHED')) {
  47      /**
  48       * @ignore
  49       */
  50  
  51      define('LEAVE_TEXT_UNTOUCHED', 0);
  52  }
  53  
  54  if (!defined('USE_TEXTILE')) {
  55      /**
  56       * @ignore
  57       */
  58  
  59      define('USE_TEXTILE', 1);
  60  }
  61  
  62  if (!defined('CONVERT_LINEBREAKS')) {
  63      /**
  64       * @ignore
  65       */
  66  
  67      define('CONVERT_LINEBREAKS', 2);
  68  }
  69  
  70  /**
  71   * Wrapper for Textpattern.
  72   *
  73   * @package Wrapper
  74   */
  75  
  76  class TXP_Wrapper
  77  {
  78      /**
  79       * The current user.
  80       *
  81       * Remember to always use $this->txp_user when checking
  82       * for permissions with this class.
  83       *
  84       * @var string
  85       */
  86  
  87      public $txp_user = null;
  88  
  89      /**
  90       * Authenticated connection.
  91       *
  92       * @var bool
  93       */
  94  
  95      public $loggedin = false;
  96  
  97      /**
  98       * Predefined Textpattern variables to be populated.
  99       *
 100       * @var array
 101       */
 102  
 103      public $vars = array(
 104          'ID',
 105          'Title',
 106          'Title_html',
 107          'Body',
 108          'Body_html',
 109          'Excerpt',
 110          'Excerpt_html',
 111          'textile_excerpt',
 112          'Image',
 113          'textile_body',
 114          'Keywords',
 115          'Status',
 116          'Posted',
 117          'Section',
 118          'Category1',
 119          'Category2',
 120          'Annotate',
 121          'AnnotateInvite',
 122          'AuthorID',
 123          'Posted',
 124          'override_form',
 125          'url_title',
 126          'custom_1',
 127          'custom_2',
 128          'custom_3',
 129          'custom_4',
 130          'custom_5',
 131          'custom_6',
 132          'custom_7',
 133          'custom_8',
 134          'custom_9',
 135          'custom_10',
 136      );
 137  
 138      /**
 139       * Constructor.
 140       *
 141       * This is used to pass user credentials
 142       * to the wrapper.
 143       *
 144       * @param string $txp_user The user login name
 145       * @param string $txpass   User password
 146       * @example
 147       * $wrapper = new TXP_wrapper('username', 'password');
 148       */
 149  
 150      public function __construct($txp_user, $txpass = null)
 151      {
 152          if ($this->_validate($txp_user, $txpass)) {
 153              $this->txp_user = $txp_user;
 154              $this->loggedin = true;
 155          }
 156      }
 157  
 158      /**
 159       * Deletes an article with the given ID.
 160       *
 161       * @param  int $article_id The article
 162       * @return bool TRUE on success
 163       */
 164  
 165      public function deleteArticleID($article_id)
 166      {
 167          $article_id = assert_int($article_id);
 168  
 169          if ($this->loggedin && has_privs('article.delete', $this->txp_user)) {
 170              return safe_delete('textpattern', "ID = $article_id");
 171          } elseif ($this->loggedin && has_privs('article.delete.own', $this->txp_user)) {
 172              $r = safe_field("ID", 'textpattern', "ID = $article_id AND AuthorID = '".doSlash($this->txp_user)."'");
 173  
 174              if ($r || has_privs('article.delete', $this->txp_user)) {
 175                  return safe_delete('textpattern', "ID = $article_id");
 176              }
 177          }
 178  
 179          return false;
 180      }
 181  
 182      /**
 183       * Retrieves a list of articles matching the given criteria.
 184       *
 185       * This method forms an SQL query from the given arguments and returns an
 186       * array of resulting articles.
 187       *
 188       * This method requires authentication and at least 'article.edit.own'
 189       * privileges. If the user doesn't have 'article.edit' privileges,
 190       * only the user's own articles can be accessed.
 191       *
 192       * @param  string $what   The select clause
 193       * @param  string $where  The where clause
 194       * @param  int    $offset The offset
 195       * @param  int    $limit  The limit
 196       * @param  bool   $slash  If TRUE, escapes $where and $what
 197       * @return array|bool Array of articles, or FALSE on failure
 198       */
 199  
 200      public function getArticleList($what = '*', $where = '1', $offset = 0, $limit = 10, $slash = true)
 201      {
 202          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user)) {
 203              $offset = assert_int($offset);
 204              $limit = assert_int($limit);
 205  
 206              if ($slash) {
 207                  $where = doSlash($where);
 208                  $what = doSlash($what);
 209              }
 210  
 211              if (has_privs('article.edit', $this->txp_user)) {
 212                  $rs = safe_rows_start($what, 'textpattern', $where." ORDER BY Posted DESC LIMIT $offset, $limit");
 213              } else {
 214                  $rs = safe_rows_start($what, 'textpattern', $where." AND AuthorID = '".doSlash($this->txp_user)."' ORDER BY Posted DESC LIMIT $offset, $limit");
 215              }
 216  
 217              $out = array();
 218  
 219              if ($rs) {
 220                  while ($a = nextRow($rs)) {
 221                      $out[] = $a;
 222                  }
 223              }
 224  
 225              return $out;
 226          }
 227  
 228          return false;
 229      }
 230  
 231      /**
 232       * Retrieves an article matching the given criteria.
 233       *
 234       * This method forms an SQL query from the given arguments and returns an
 235       * article as an associative array.
 236       *
 237       * This method requires authentication and at least 'article.edit.own'
 238       * privileges. If the user doesn't have 'article.edit' privileges,
 239       * only the user's own articles can be accessed.
 240       *
 241       * @param  string $what  Select clause
 242       * @param  string $where Where clause
 243       * @param  bool   $slash If TRUE, escapes $where and $what
 244       * @return array|bool An article, or FALSE on failure
 245       * @see    TXP_Wrapper::getArticleList()
 246       * @example
 247       * $wrapper = new TXP_wrapper('username', 'password');
 248       * if ($r = $wrapper->getArticle())
 249       * {
 250       *     echo "Returned an article by the title '{$r['Title']}'.";
 251       * }
 252       */
 253  
 254      public function getArticle($what = '*', $where = '1', $slash = true)
 255      {
 256          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user)) {
 257              if ($slash) {
 258                  $what  = doSlash($what);
 259                  $where = doSlash($where);
 260              }
 261  
 262              // Higher user groups should be able to edit any article.
 263              if (has_privs('article.edit', $this->txp_user)) {
 264                  return safe_row($what, 'textpattern', $where);
 265              } else {
 266                  // While restricted users should be able to edit their own
 267                  // articles only.
 268                  return safe_row($what, 'textpattern', $where." AND AuthorID = '".doSlash($this->txp_user)."'");
 269              }
 270          }
 271  
 272          return false;
 273      }
 274  
 275      /**
 276       * Gets an article with the given ID.
 277       *
 278       * This method is an shortcut for TXP_Wrapper::getArticle().
 279       *
 280       * @param  int    $article_id The article
 281       * @param  string $what       The SQL select clause
 282       * @return array|bool The article, or FALSE on failure
 283       * @see    TXP_Wrapper::getArticle()
 284       * @example
 285       * $wrapper = new TXP_wrapper('username', 'password');
 286       * if ($r = $wrapper->getArticleID(11))
 287       * {
 288       *     echo "Returned an article by the ID of '{$r['ID']}'.";
 289       * }
 290       */
 291  
 292      public function getArticleID($article_id, $what = '*')
 293      {
 294          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user)) {
 295              $article_id = assert_int($article_id);
 296  
 297              if (has_privs('article.edit', $this->txp_user)) {
 298                  return safe_row(doSlash($what), 'textpattern', "ID = $article_id");
 299              } else {
 300                  return safe_row(doSlash($what), 'textpattern', "ID = $article_id AND AuthorID = '".doSlash($this->txp_user)."'");
 301              }
 302          }
 303  
 304          return false;
 305      }
 306  
 307      /**
 308       * Updates an existing article.
 309       *
 310       * This method takes an array of article fields, and updates an article with
 311       * the given ID. Supplied values are sanitised and prepared internally.
 312       *
 313       * @param  int   $article_id The article
 314       * @param  array $params     The article fields to update
 315       * @return int|bool The article id, or FALSE on failure
 316       * @example
 317       * $wrapper = new TXP_wrapper('username', 'password');
 318       * if (($id = $wrapper->updateArticleID(11, array(
 319       *     'Title' => 'New title',
 320       *     'Body'  => 'Body text.',
 321       * )) !== false)
 322       * {
 323       *     echo "Updated article '{$id}'.";
 324       * }
 325       */
 326  
 327      public function updateArticleID($article_id, $params)
 328      {
 329          $article_id = assert_int($article_id);
 330          $r = safe_field("ID", 'textpattern', "AuthorID = '".doSlash($this->txp_user)."' AND ID = $article_id");
 331  
 332          if ($this->loggedin && $r && has_privs('article.edit.own', $this->txp_user)) {
 333              // Unprivileged user, check if they can edit published articles.
 334              $r = assert_int($r);
 335              $oldstatus = safe_field("Status", 'textpattern', "ID = $r");
 336  
 337              if (($oldstatus == 4 || $oldstatus == 5) && !has_privs('article.edit.published', $this->txp_user)) {
 338                  return false;
 339              }
 340  
 341              // If they can, let's go.
 342              return $this->_setArticle($params, $article_id);
 343          } elseif ($this->loggedin && has_privs('article.edit', $this->txp_user)) {
 344              // Admin editing. Desires are behest.
 345              return $this->_setArticle($params, $article_id);
 346          }
 347  
 348          return false;
 349      }
 350  
 351      /**
 352       * Creates a new article.
 353       *
 354       * @param  array $params The article fields
 355       * @return int|bool Article ID, or FALSE on failure
 356       * @example
 357       * $wrapper = new TXP_wrapper('username', 'password');
 358       * if (($id = $wrapper->newArticle(array(
 359       *     'Title' => 'My article',
 360       *     'Body'  => 'My body text',
 361       * )) !== false)
 362       * {
 363       *     echo "Created a new article with the ID of '{$id}'.";
 364       * }
 365       */
 366  
 367      public function newArticle($params)
 368      {
 369          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 370              if (($params['Status'] == 4 || $params['Status'] == 5) && !has_privs('article.publish', $this->txp_user)) {
 371                  $params['Status'] = 3;
 372              }
 373  
 374              return $this->_setArticle($params);
 375          }
 376  
 377          return false;
 378      }
 379  
 380      /**
 381       * Gets a list of sections as an associative array.
 382       *
 383       * This method requires authentication and 'article' privileges.
 384       *
 385       * @return array|bool FALSE on failure
 386       * @example
 387       * $wrapper = new TXP_wrapper('username', 'password');
 388       * if ($sections = $wrapper->getSectionsList())
 389       * {
 390       *     foreach ($sections as $section)
 391       *     {
 392       *         echo $section['title'];
 393       *     }
 394       * }
 395       */
 396  
 397      public function getSectionsList()
 398      {
 399          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 400              return safe_rows("*", 'txp_section', "name != 'default'");
 401          }
 402  
 403          return false;
 404      }
 405  
 406      /**
 407       * Gets a section as an associative array.
 408       *
 409       * This method requires authentication and 'article' privileges.
 410       *
 411       * @param  string $name The section name
 412       * @return array|bool FALSE on failure
 413       * @example
 414       * $wrapper = new TXP_wrapper('username', 'password');
 415       * if ($section = $wrapper->getSection('my-section'))
 416       * {
 417       *     echo $section['title'];
 418       * }
 419       */
 420  
 421      public function getSection($name)
 422      {
 423          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 424              $name = doSlash($name);
 425  
 426              return safe_row("*", 'txp_section', "name = '$name'");
 427          }
 428  
 429          return false;
 430      }
 431  
 432      /**
 433       * Gets a list of categories as an associative array.
 434       *
 435       * This method requires authentication and 'article' privileges.
 436       *
 437       * @return array|bool FALSE on failure
 438       * @example
 439       * $wrapper = new TXP_wrapper('username', 'password');
 440       * if ($categories = $wrapper->getCategoryList())
 441       * {
 442       *     foreach ($categories as $category)
 443       *     {
 444       *         echo $category['title'];
 445       *     }
 446       * }
 447       */
 448  
 449      public function getCategoryList()
 450      {
 451          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 452              return safe_rows("*", 'txp_category', "name != 'root' AND type = 'article'");
 453          }
 454  
 455          return false;
 456      }
 457  
 458      /**
 459       * Gets a category as an associative array.
 460       *
 461       * This method requires authentication and 'article' privileges.
 462       *
 463       * @param  string $name The category name
 464       * @return array|bool FALSE on failure
 465       * @example
 466       * $wrapper = new TXP_wrapper('username', 'password');
 467       * if ($category = $wrapper->getCategory('my-category'))
 468       * {
 469       *     echo $category['title'];
 470       * }
 471       */
 472  
 473      public function getCategory($name)
 474      {
 475          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 476              $name = doSlash($name);
 477  
 478              return safe_row("*", 'txp_category', "name = '$name' AND type = 'article'");
 479          }
 480  
 481          return false;
 482      }
 483  
 484      /**
 485       * Gets a category as an associative array by ID.
 486       *
 487       * This method is an alternative to TXP_wrapper::getCategory().
 488       *
 489       * This method requires authentication and 'article' privileges.
 490       *
 491       * @param  string $id The category ID
 492       * @return array|bool FALSE on failure
 493       */
 494  
 495      public function getCategoryID($id)
 496      {
 497          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 498              $id = assert_int($id);
 499  
 500              return safe_row("*", 'txp_category', "id = $id");
 501          }
 502  
 503          return false;
 504      }
 505  
 506      /**
 507       * Gets a category as an associative array by title.
 508       *
 509       * This method is an alternative to TXP_wrapper::getCategory().
 510       *
 511       * This method requires authentication and 'article' privileges.
 512       *
 513       * @param  string $title The category title
 514       * @return array|bool FALSE on failure
 515       */
 516  
 517      public function getCategoryTitle($title)
 518      {
 519          if ($this->loggedin && has_privs('article', $this->txp_user)) {
 520              $title = doSlash($title);
 521  
 522              return safe_row("*", 'txp_category', "title = '$title' AND type = 'article'");
 523          }
 524  
 525          return false;
 526      }
 527  
 528      /**
 529       * Gets an array of information about the current user.
 530       *
 531       * This method requires authentication. Resulting array contains all columns
 532       * from 'txp_users' database table.
 533       *
 534       * @return array|bool FALSE on failure
 535       * @example
 536       * $wrapper = new TXP_wrapper('username', 'password');
 537       * if ($user = $wrapper->getUser())
 538       * {
 539       *     echo $user['RealName'] . ' ' . $user['email'];
 540       * }
 541       */
 542  
 543      public function getUser()
 544      {
 545          if ($this->loggedin) {
 546              return safe_row("*", 'txp_users', "name = '".$this->txp_user."'");
 547          }
 548  
 549          return false;
 550      }
 551  
 552      /**
 553       * Retrieves a page template contents with the given name.
 554       *
 555       * This method requires authentication and 'page' privileges.
 556       *
 557       * @param  string $name The template
 558       * @return string|bool The template, or FALSE on failure
 559       */
 560  
 561      public function getTemplate($name)
 562      {
 563          if ($this->loggedin && has_privs('page', $this->txp_user)) {
 564              $name = doSlash($name);
 565  
 566              return safe_field("user_html", 'txp_page', "name = '$name'");
 567          }
 568  
 569          return false;
 570      }
 571  
 572      /**
 573       * Updates a page template with the given name.
 574       *
 575       * This method requires authentication and 'page' privileges.
 576       *
 577       * @param  string $name The template name
 578       * @param  string $html The template contents
 579       * @return bool TRUE on success
 580       * @example
 581       * $wrapper = new TXP_wrapper('username', 'password');
 582       * if ($wrapper->setTemplate('default', '&lt;txp:site_name /&gt;'))
 583       * {
 584       *     echo "Page template updated.";
 585       * }
 586       */
 587  
 588      public function setTemplate($name, $html)
 589      {
 590          if ($this->loggedin && has_privs('page', $this->txp_user)) {
 591              $name = doSlash($name);
 592              $html = doSlash($html);
 593  
 594              return safe_update('txp_page', "user_html = '$html'", "name = '$name'");
 595          }
 596  
 597          return false;
 598      }
 599  
 600      /**
 601       * Intended for updating an article's non-content fields, like categories,
 602       * sections or keywords.
 603       *
 604       * This method requires authentication and 'article.edit' privileges.
 605       *
 606       * @param  int    $article_id The article
 607       * @param  string $field      The field to update
 608       * @param  mixed  $value      The new value
 609       * @return bool TRUE on success
 610       * @see    TXP_wrapper::updateArticleID()
 611       * @example
 612       * $wrapper = new TXP_wrapper('username', 'password');
 613       * if ($wrapper->updateArticleField(11, 'Section', 'new-section'))
 614       * {
 615       *     echo "Section updated.";
 616       * }
 617       */
 618  
 619      public function updateArticleField($article_id, $field, $value)
 620      {
 621          $disallow = array(
 622              'Body',
 623              'Body_html',
 624              'Title',
 625              'Title_html',
 626              'Excerpt',
 627              'Excerpt_html',
 628              'textile_excerpt',
 629              'textile_body',
 630              'LastMod',
 631              'LastModID',
 632              'feed_time',
 633              'uid',
 634          );
 635  
 636          if ($this->loggedin && has_privs('article.edit', $this->txp_user) && !in_array(doSlash($field), $disallow)) {
 637              $field = doSlash($field);
 638              $value = doSlash($value);
 639  
 640              if ($field == 'Posted') {
 641                  $value = strtotime($value) - tz_offset();
 642                  $value = "FROM_UNIXTIME($value)";
 643                  $sql = "Posted = $value";
 644              } elseif ($field == 'Status') {
 645                  $value = assert_int($value);
 646                  if (!has_privs('article.publish', $this->txp_user) && $value >= 4) {
 647                      $value = 3;
 648                  }
 649                  $sql = "Status = $value";
 650              } else {
 651                  $sql = "$field = '$value'";
 652              }
 653  
 654              $sql .= ", LastMod = NOW(), LastModID = '".$this->txp_user."'";
 655              $article_id = assert_int($article_id);
 656              $rs = safe_update('textpattern', $sql, "ID = $article_id");
 657  
 658              return $rs;
 659          }
 660  
 661          return false;
 662      }
 663  
 664      /**
 665       * Creates and updates articles.
 666       *
 667       * @param  array $incoming   The article fields
 668       * @param  int   $article_id The ID of the article to update
 669       * @return int|bool The article ID on success, or FALSE on failure
 670       * @access private
 671       * @see    TXP_wrapper::udpateArticleId()
 672       * @see    TXP_wrapper::newArticle()
 673       */
 674  
 675      public function _setArticle($incoming, $article_id = null)
 676      {
 677          global $txpcfg;
 678  
 679          $prefs = get_prefs();
 680  
 681          extract($prefs);
 682  
 683          if (!empty($incoming['Section']) && !$this->getSection($incoming['Section'])) {
 684              return false;
 685          }
 686  
 687          if (!empty($incoming['Category1']) && !$this->getCategory($incoming['Category1'])) {
 688              return false;
 689          }
 690  
 691          if (!empty($incoming['Category2']) && !$this->getCategory($incoming['Category2'])) {
 692              return false;
 693          }
 694  
 695          if ($article_id !== null) {
 696              $article_id = assert_int($article_id);
 697          }
 698  
 699          // All validation rules assumed to be passed before this point.
 700          // Do content processing here.
 701  
 702          $incoming_with_markup = $this->textile_main_fields($incoming, $use_textile);
 703  
 704          $incoming['Title'] = $incoming_with_markup['Title'];
 705  
 706          if (empty($incoming['Body_html']) && !empty($incoming['Body'])) {
 707              $incoming['Body_html'] = $incoming_with_markup['Body_html'];
 708          }
 709  
 710          if (empty($incoming['Excerpt_html']) && !empty($incoming['Excerpt'])) {
 711              $incoming['Excerpt_html'] = $incoming_with_markup['Excerpt_html'];
 712          }
 713  
 714          unset($incoming_with_markup);
 715  
 716          if (empty($incoming['Posted'])) {
 717              if ($article_id === null) {
 718                  $when = (!$article_id) ? 'NOW()' : '';
 719                  $incoming['Posted'] = $when;
 720              } else {
 721                  // Do not override post time for existing articles unless Posted
 722                  // is present.
 723                  unset($incoming['Posted']);
 724              }
 725          } else {
 726              $when = strtotime($incoming['Posted']) - tz_offset();
 727              $when = "FROM_UNIXTIME($when)";
 728          }
 729  
 730          if ($incoming['Title'] || $incoming['Body'] || $incoming['Excerpt']) {
 731              // Build SQL then and run query.
 732              // Prevent data erase if not defined on the update action but it
 733              // was on the database from a previous creation/edition time.
 734              if ($article_id) {
 735                  $old = safe_row("*", 'textpattern', "ID = $article_id");
 736  
 737                  if (!has_privs('article.publish', $this->txp_user) && $incoming['Status'] == 4 && $old['Status'] != 4) {
 738                      $incoming['Status'] = 3;
 739                  }
 740  
 741                  foreach ($old as $key => $val) {
 742                      if (!isset($incoming[$key])) {
 743                          $incoming[$key] = $val;
 744                      }
 745                  }
 746              } else {
 747                  if (!has_privs('article.publish', $this->txp_user) && $incoming['Status'] == 4) {
 748                      $incoming['Status'] = 3;
 749                  }
 750              }
 751  
 752              if (empty($incoming['Section']) && $article_id) {
 753                  $incoming['Section'] = safe_field("Section", 'textpattern', "ID = $article_id");
 754              }
 755  
 756              $incoming = $this->_check_keys($incoming, array(
 757                  'AuthorID'        => $this->txp_user,
 758                  'Annotate'        => $comments_on_default,
 759                  'AnnotateInvite'  => $comments_default_invite,
 760                  'textile_body'    => $use_textile,
 761                  'textile_excerpt' => $use_textile,
 762                  'url_title'       => stripSpace($incoming['Title']),
 763              ));
 764  
 765              // Build the SQL query.
 766              $sql = array();
 767  
 768              foreach ($incoming as $key => $val) {
 769                  if ($key == 'Posted' && $val == 'NOW()') {
 770                      $sql[] = "$key = $val";
 771                  } elseif ($key != 'ID' && $key != 'uid' && $key != 'feed_time' && $key != 'LastMod' && $key != 'LastModID') {
 772                      $sql[] = "$key = '".doSlash($val)."'";
 773                  }
 774              }
 775  
 776              $sql[] = "LastMod = NOW()";
 777              $sql[] = "LastModID = '".doSlash($this->txp_user)."'";
 778  
 779              if (!$article_id) {
 780                  $sql[] = "uid = '".doSlash(md5(uniqid(rand(), true)))."'";
 781              }
 782  
 783              if (!$article_id) {
 784                  if (empty($incoming['Posted'])) {
 785                      $sql[] = "feed_time = CURDATE()";
 786                  } else {
 787                      $when = strtotime($incoming['Posted']) - tz_offset();
 788                      $when = strftime("%Y-%m-%d", $when);
 789                      $sql[] = "feed_time = '".doSlash($when)."'";
 790                  }
 791              }
 792  
 793              $sql = join(', ', $sql);
 794  
 795              $rs = ($article_id) ? safe_update('textpattern', $sql, "ID = $article_id") : safe_insert('textpattern', $sql);
 796  
 797              $oldstatus = ($article_id) ? $old['Status'] : '';
 798  
 799              if (!$article_id && $rs) {
 800                  $article_id = $rs;
 801              }
 802  
 803              if (($incoming['Status'] >= 4 && !$article_id) || ($oldstatus != 4 && $article_id)) {
 804                  safe_update('txp_prefs', "val = NOW()", "name = 'lastmod'");
 805              }
 806  
 807              return $article_id;
 808          }
 809  
 810          return false;
 811      }
 812  
 813      /**
 814       * Validates the given user credentials.
 815       *
 816       * @param  string $user     The username
 817       * @param  string $password The password
 818       * @return bool TRUE on success
 819       * @access private
 820       */
 821  
 822      public function _validate($user, $password = null)
 823      {
 824          if ($password !== null) {
 825              $r = txp_validate($user, $password);
 826          } else {
 827              $r = true;
 828          }
 829  
 830          if ($r) {
 831              // Update the last access time.
 832              $safe_user = doSlash($user);
 833              safe_update('txp_users', "last_access = NOW()", "name = '$safe_user'");
 834  
 835              return true;
 836          }
 837  
 838          return false;
 839      }
 840  
 841      /**
 842       * Validates and filters the given article fields.
 843       *
 844       * Checks if the given parameters are appropriate for the article.
 845       *
 846       * @param  array $incoming The incoming associative array
 847       * @param  array $default  An associative array containing default values for the desired keys
 848       * @return array Filtered data array
 849       * @access private
 850       */
 851  
 852      public function _check_keys($incoming, $default = array())
 853      {
 854          $out = array();
 855  
 856          // Strip off unsuited keys.
 857          foreach ($incoming as $key => $val) {
 858              if (in_array($key, $this->vars)) {
 859                  $out[$key] = $val;
 860              }
 861          }
 862  
 863          foreach ($this->vars as $def_key) {
 864              // Add those ones nonexistent in the incoming array.
 865              if (!array_key_exists($def_key, $out)) {
 866                  $out[$def_key] = '';
 867              }
 868  
 869              // Setup the provided default value, if any, only when the incoming
 870              // value is empty.
 871              if (array_key_exists($def_key, $default) && empty($out[$def_key])) {
 872                  $out[$def_key] = $default[$def_key];
 873              }
 874          }
 875  
 876          return $out;
 877      }
 878  
 879      /**
 880       * Apply Textile to the main article fields.
 881       *
 882       * This is duplicated from txp_article.php.
 883       *
 884       * @param  array $incoming    The incoming fields
 885       * @param  bool  $use_textile Use Textile or not
 886       * @return array The $incoming array formatted
 887       * @access private
 888       */
 889  
 890      public function textile_main_fields($incoming, $use_textile = 1)
 891      {
 892          global $txpcfg;
 893  
 894          $textile = new \Textpattern\Textile\Parser();
 895  
 896          if (!empty($event) and $event == 'article') {
 897              $incoming['Title_plain'] = $incoming['Title'];
 898          }
 899  
 900          if ($incoming['textile_body'] == USE_TEXTILE) {
 901              $incoming['Title'] = $textile->textileEncode($incoming['Title']);
 902          }
 903  
 904          $incoming['url_title'] = preg_replace('|[\x00-\x1f#%+/?\x7f]|', '', $incoming['url_title']);
 905          $incoming['Body_html'] = TXP_Wrapper::format_field($incoming['Body'], $incoming['textile_body'], $textile);
 906          $incoming['Excerpt_html'] = TXP_Wrapper::format_field($incoming['Excerpt'], $incoming['textile_excerpt'], $textile);
 907  
 908          return $incoming;
 909      }
 910  
 911      /**
 912       * Formats a article field according to the given options.
 913       *
 914       * @param  string  $field  The field contents
 915       * @param  int     $format Either LEAVE_TEXT_UNTOUCHED, CONVERT_LINEBREAKS, USE_TEXTILE
 916       * @param  Textile An instance of Textile
 917       * @return string HTML formatted field
 918       * @access private
 919       */
 920  
 921      public function format_field($field, $format, $textile)
 922      {
 923          switch ($format) {
 924              case LEAVE_TEXT_UNTOUCHED:
 925                  $html = trim($field);
 926                  break;
 927              case CONVERT_LINEBREAKS:
 928                  $html = nl2br(trim($field));
 929                  break;
 930              case USE_TEXTILE:
 931                  $html = $textile->parse($field);
 932                  break;
 933          }
 934  
 935          return $html;
 936      }
 937  }

title

Description

title

Description

title

Description

title

title

Body