[ Index ]

PHP Cross Reference of Textpattern 4.0.8

title

Body

[close]

/textpattern/lib/ -> txplib_wrapper.php (source)

   1  <?php
   2  
   3  /*
   4  $HeadURL: https://textpattern.googlecode.com/svn/releases/4.0.8/source/textpattern/lib/txplib_wrapper.php $
   5  $LastChangedRevision: 2931 $
   6  */
   7  
   8  /**
   9   * Textpattern Wrapper Class for Textpattern 4.0.x
  10   *
  11   * Main goal for this class is to be used as a textpattern data wrapper by
  12   * any code which needs to have access to the textpattern articles data,
  13   * like XML-RPC, Atom, Moblogging or other external implementations.
  14   *
  15   * @link http://txp.kusor.com/wrapper
  16   * @author Pedro Palazon - http://kusor.net/
  17   * @copyright 2005-2008 The Textpattern Development Team - http://textpattern.com
  18   */
  19  
  20  # This class requires to include some Textpattern files in order to work properly.
  21  # See RPC Server implementation to view an example of the required files and predefined variables.
  22  
  23  if (!defined('txpath')) die('txpath is undefined.');
  24  include_once txpath.'/include/txp_auth.php';
  25  # Include constants.php?
  26  if (!defined('LEAVE_TEXT_UNTOUCHED')) define('LEAVE_TEXT_UNTOUCHED', 0);
  27  if (!defined('USE_TEXTILE')) define('USE_TEXTILE', 1);
  28  if (!defined('CONVERT_LINEBREAKS')) define('CONVERT_LINEBREAKS', 2);
  29  
  30  class TXP_Wrapper
  31  {
  32      /**
  33       * @var string The current user
  34       *
  35       * Remeber to use allways $this->txp_user when checking for permissions with this class
  36       */
  37      var $txp_user = null;
  38      /**
  39       * @var boolean Is the user authenticated
  40       */
  41      var $loggedin = false;
  42      /**
  43       * @var array Predefined Textpattern vars to be populated
  44       */
  45      var $vars = array(
  46          'ID','Title','Title_html','Body','Body_html','Excerpt','Excerpt_html','textile_excerpt','Image',
  47          'textile_body', 'Keywords','Status','Posted','Section','Category1','Category2',
  48          'Annotate','AnnotateInvite','AuthorID','Posted','override_form',
  49          'url_title','custom_1','custom_2','custom_3','custom_4','custom_5',
  50          'custom_6','custom_7','custom_8','custom_9','custom_10'
  51      );
  52  
  53      //Class constructor
  54      /**
  55       * Class constructor
  56       * @param string $txp_user the user login name
  57       * @param strign $txpass user password
  58       *
  59       * @see _validate
  60       */
  61  	function TXP_Wrapper($txp_user, $txpass = NULL)
  62      {
  63          if ($this->_validate($txp_user, $txpass))
  64          {
  65              $this->txp_user = $txp_user;
  66              $this->loggedin = true;
  67          }
  68      }
  69  
  70      //Delete the article given the id
  71      /**
  72       * Delete the article given the id
  73       * @param mixed(string|integer) $article_id the ID of the article to delete
  74       * @return boolean true on success deletion
  75       */
  76  	function deleteArticleID($article_id)
  77      {
  78          $article_id = assert_int($article_id);
  79          if ($this->loggedin && has_privs('article.delete', $this->txp_user)) {
  80              return safe_delete('textpattern', "ID = $article_id");
  81          }
  82          elseif ($this->loggedin && has_privs('article.delete.own', $this->txp_user))
  83          {
  84              $r = safe_field('ID', 'textpattern', "ID = $article_id AND AuthorID='".doSlash($this->txp_user)."'");
  85              if ($r || has_privs('article.delete', $this->txp_user))
  86              {
  87                  return safe_delete('textpattern', "ID = $article_id");
  88              }
  89          }
  90          return false;
  91      }
  92  
  93      //Retrieves a list of articles matching the given criteria
  94      /**
  95       * Retrieves a list of articles matching the given criteria
  96       * @param string $what SQL column names to retrieve
  97       * @param string $where SQL condition to match
  98       * @param string $offset SQL offset
  99       * @param string $limit SQL limit
 100       * @param boolean $slash escape SQL column names and condition
 101       * @return mixed array on success, false on failure
 102       */
 103  	function getArticleList($what='*', $where='1', $offset='0', $limit='10', $slash=true)
 104      {
 105  
 106          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
 107          {
 108              $offset = assert_int($offset);
 109              $limit = assert_int($limit);
 110  
 111              if ($slash) {
 112                  $where = doSlash($where);
 113                  $what = doSlash($what);
 114              }
 115  
 116              if (has_privs('article.edit', $this->txp_user)) {
 117                  $rs = safe_rows_start($what, 'textpattern', $where." order by Posted desc LIMIT $offset, $limit");
 118              }else{
 119                  $rs = safe_rows_start($what, 'textpattern', $where." AND AuthorID='".doSlash($this->txp_user)."' order by Posted desc LIMIT $offset, $limit");
 120              }
 121              $out = array();
 122              if ($rs)
 123              {
 124                  while ($a = nextRow($rs))
 125                  {
 126                      $out[]= $a;
 127                  }
 128              }
 129              return $out;
 130          }
 131          return false;
 132      }
 133  
 134      //Retrieves an article matching the given criteria
 135      /**
 136       * Retrieves an article matching the given criteria
 137       * @param string $what SQL column names to retrieve
 138       * @param string $where SQL condition to match
 139       * @param boolean $slash escape SQL column names and condition
 140       * @return mixed array on success, false on failure
 141       */
 142  	function getArticle($what='*', $where='1', $slash=true)
 143      {
 144          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
 145          {
 146              if ($slash) {
 147                  $what  = doSlash($what);
 148                  $where = doSlash($where);
 149              }
 150              // Higer user groups should be able to edit any article
 151              if (has_privs('article.edit', $this->txp_user)) {
 152                  return safe_row($what, 'textpattern', $where);
 153              }else {
 154                  // While restricted users should be able to edit their own articles only
 155                  return safe_row($what, 'textpattern', $where." AND AuthorID='".doSlash($this->txp_user)."'");
 156              }
 157          }
 158          return false;
 159      }
 160  
 161      //Same thing, but handy shortcut known the ID
 162      /**
 163       * Same thing, but handy shortcut known the ID
 164       * @param mixed(string|integer) $article_id the ID of the article
 165       * @param string $what SQL column names to retrieve
 166       * @return mixed array on success, false on failure
 167       */
 168  	function getArticleID($article_id, $what='*')
 169      {
 170          if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
 171          {
 172              $article_id = assert_int($article_id);
 173              if (has_privs('article.edit', $this->txp_user)) {
 174                  return safe_row(doSlash($what), 'textpattern', "ID = $article_id");
 175              }else{
 176                  return safe_row(doSlash($what), 'textpattern', "ID = $article_id AND AuthorID='".doSlash($this->txp_user)."'");
 177              }
 178          }
 179          return false;
 180      }
 181  
 182      //Updates an existing article
 183      /**
 184       * Updates an existing article
 185       * @param array $params the article fields to update
 186       * @param mixed(string|integer) $article_id the ID of the article to update
 187       * @return mixed integer article id on success, false on failure
 188       * @see _setArticle
 189       */
 190  	function updateArticleID($article_id, $params)
 191      {
 192          $article_id = assert_int($article_id);
 193  
 194          $r = safe_field('ID', 'textpattern', "AuthorID='".doSlash($this->txp_user)."' AND ID = $article_id");
 195  
 196          if ($this->loggedin && $r && has_privs('article.edit.own', $this->txp_user))
 197          {    //Unprivileged user
 198              //Check if can edit published arts
 199              $r = assert_int($r);
 200              $oldstatus = safe_field('Status', 'textpattern', "ID = $r");
 201              if (($oldstatus=='4' || $oldstatus == '5') && !has_privs('article.edit.published', $this->txp_user)) return false;
 202              //If can, let's go
 203              return $this->_setArticle($params, $article_id);
 204          }
 205          elseif ($this->loggedin && has_privs('article.edit', $this->txp_user))
 206          {//Admin editing. Desires are behest.
 207              return $this->_setArticle($params, $article_id);
 208          }
 209  
 210          return false;
 211      }
 212  
 213      //Creates a new article
 214      /**
 215       * Creates a new article
 216       * @param array $params the article fields
 217       * @return mixed integer article id on success, false on failure
 218       * @see _setArticle
 219       */
 220  	function newArticle($params)
 221      {
 222          if ($this->loggedin && has_privs('article', $this->txp_user))
 223          {
 224              //Prevent junior authors to publish articles
 225              if (($params['Status']=='4' || $params['Status']=='5') && !has_privs('article.publish', $this->txp_user))
 226              {
 227                  $params['Status']='3';
 228              }
 229  
 230              return $this->_setArticle($params);
 231          }
 232          return false;
 233      }
 234  
 235      //Get full sections information
 236      /**
 237       * Get full sections information
 238       * @return mixed array on success, false on failure
 239       */
 240  	function getSectionsList()
 241      {
 242          if ($this->loggedin && has_privs('article', $this->txp_user))
 243          {
 244              return safe_rows('*', 'txp_section',"name!='default'");
 245          }
 246          return false;
 247      }
 248  
 249      //Get one section
 250      /**
 251       * Get one section
 252       * @param string $name the section name
 253       * @return mixed array on success, false on failure
 254       */
 255  	function getSection($name)
 256      {
 257          if ($this->loggedin && has_privs('article', $this->txp_user))
 258          {
 259              $name = doSlash($name);
 260              return safe_row('*', 'txp_section',"name='$name'");
 261          }
 262          return false;
 263      }
 264  
 265      //Get full categories information
 266      /**
 267       * Get full categories information
 268       * @return mixed array on success, false on failure
 269       */
 270  	function getCategoryList()
 271      {
 272          if ($this->loggedin && has_privs('article', $this->txp_user))
 273          {
 274              return safe_rows('*', 'txp_category',"name!='root' AND type='article'");
 275          }
 276          return false;
 277      }
 278      /**
 279       * Get one category by category name
 280       * @param string $name the category name
 281       * @return mixed array on success, false on failure
 282       */
 283  	function getCategory($name)
 284      {
 285          if ($this->loggedin && has_privs('article', $this->txp_user))
 286          {
 287              $name = doSlash($name);
 288              return safe_row('*', 'txp_category',"name='$name' AND type='article'");
 289          }
 290          return false;
 291      }
 292      /**
 293       * Get one category by category id
 294       * @param mixed(string|integer) $id category id
 295       * @return mixed array on success, false on failure
 296       */
 297  	function getCategoryID($id)
 298      {
 299          if ($this->loggedin && has_privs('article', $this->txp_user))
 300          {
 301              $id = assert_int($id);
 302              return safe_row('*', 'txp_category',"id = $id");
 303          }
 304          return false;
 305      }
 306      /**
 307       * Get one category by category title
 308       * @param string $title the category title
 309       * @return mixed array on success, false on failure
 310       */
 311  	function getCategoryTitle($title)
 312      {
 313          if ($this->loggedin && has_privs('article', $this->txp_user))
 314          {
 315              $title = doSlash($title);
 316              return safe_row('*', 'txp_category',"title='$title' AND type='article'");
 317          }
 318          return false;
 319      }
 320      //Get full information for current user
 321      /**
 322       * Get full information for current user
 323       * @return mixed array on success, false on failure
 324       */
 325  	function getUser()
 326      {
 327          if ($this->loggedin)
 328          {
 329              return safe_row('*', 'txp_users',"name='$this->txp_user'");
 330          }
 331          return false;
 332      }
 333  
 334      //Retrieves a template with the given name
 335      /**
 336       * Retrieves a template with the given name
 337       * @param string $name the template name
 338       */
 339  	function getTemplate($name)
 340      {
 341          if ($this->loggedin && has_privs('page', $this->txp_user))
 342          {
 343              $name = doSlash($name);
 344              return safe_field('user_html', 'txp_page', "name='$name'");
 345          }
 346          return false;
 347      }
 348      //Updates a template with the given name
 349      /**
 350       * Updates a template with the given name
 351       * @param string $name the template name
 352       * @param string $html the template contents
 353       * @return boolean true on success
 354       */
 355  	function setTemplate($name, $html)
 356      {
 357          if ($this->loggedin && has_privs('page', $this->txp_user))
 358          {
 359              $name = doSlash($name);
 360              $html = doSlash($html);
 361              return safe_update('txp_page', "user_html='$html'", "name='$name'");
 362          }
 363          return false;
 364      }
 365  
 366      // Intended to update article non content fields, like categories
 367      // section or Keywords
 368      /**
 369       * Intended to update article non content fields, like categories, section or Keywords
 370       * @param mixed(string|integer) $article_id the ID of the article to update
 371       * @param string $field the name of the field to update
 372       * @param mixed $value desired value for that field
 373       * @return boolean true on success
 374       */
 375  	function updateArticleField($article_id, $field, $value)
 376      {
 377          $disallow = array('Body','Body_html','Title','Title_html','Excerpt',
 378                      'Excerpt_html','textile_excerpt','textile_body','LastMod',
 379                      'LastModID', 'feed_time', 'uid');
 380          if ($this->loggedin && has_privs('article.edit', $this->txp_user) && !in_array(doSlash($field),$disallow))
 381          {
 382              $field = doSlash($field);
 383              $value = doSlash($value);
 384  
 385              if($field == 'Posted')
 386              {
 387                  $value = strtotime($value)-tz_offset();
 388                  $value = "from_unixtime($value)";
 389                  $sql = "Posted = $value";
 390              }elseif ($field == 'Status'){
 391                  $value = assert_int($value);
 392                  if (!has_privs('article.publish', $this->txp_user) && $value >=4) $value = 3;
 393                  $sql = "Status = $value";
 394              }else{
 395                  $sql = "$field='$value'";
 396              }
 397  
 398  
 399              $sql.= ", LastMod = now(),
 400                      LastModID = '$this->txp_user'";
 401              $article_id = assert_int($article_id);
 402              $rs = safe_update('textpattern', $sql, "ID = $article_id");
 403              //Do not update lastmod pref here. No new content at all.
 404              return $rs;
 405          }
 406          return false;
 407      }
 408  
 409  // -------------------------------------------------------------
 410  // Private. Real action takes place here.
 411  // -------------------------------------------------------------
 412      /**
 413       * Executes the real action for @see udpateArticleId and @see newArticle
 414       * @param array $incoming containing the desired article fields
 415       * @param mixed(string|integer) $article_id the ID of the article to update
 416       * @return mixed integer article id on success, false otherwise
 417       * @access private
 418       */
 419  	function _setArticle($incoming, $article_id = null)
 420      {
 421          global $txpcfg;
 422  
 423          $prefs = get_prefs();
 424  
 425          extract($prefs);
 426  
 427          if (!empty($incoming['Section']) && !$this->getSection($incoming['Section'])) {
 428              return false;
 429          }
 430  
 431          if (!empty($incoming['Category1']) && !$this->getCategory($incoming['Category1'])) {
 432              return false;
 433          }
 434  
 435          if (!empty($incoming['Category2']) && !$this->getCategory($incoming['Category2'])) {
 436              return false;
 437          }
 438  
 439          if ($article_id!==null) {
 440              $article_id = assert_int($article_id);
 441          }
 442  
 443  
 444          //All validation rules assumed to be passed before this point.
 445          //Do content processing here
 446  
 447  
 448          $incoming_with_markup = $this->textile_main_fields($incoming, $use_textile);
 449  
 450          $incoming['Title'] = $incoming_with_markup['Title'];
 451  
 452          if (empty($incoming['Body_html']) && !empty($incoming['Body']))
 453          {
 454              $incoming['Body_html'] = $incoming_with_markup['Body_html'];
 455          }
 456  
 457          if (empty($incoming['Excerpt_html']) && !empty($incoming['Excerpt']))
 458          {
 459                  $incoming['Excerpt_html'] = $incoming_with_markup['Excerpt_html'];
 460          }
 461  
 462          unset($incoming_with_markup);
 463  
 464          if (empty($incoming['Posted'])) {
 465              if ($article_id===null) {
 466                  $when = (!$article_id)? 'now()': '';
 467                  $incoming['Posted'] = $when;
 468              }else{
 469                  # do not override post time for existing articles unless Posted is present
 470                  unset($incoming['Posted']);
 471              }
 472          } else {
 473              $when = strtotime($incoming['Posted'])-tz_offset();
 474              $when = "from_unixtime($when)";
 475          }
 476  
 477  
 478          if ($incoming['Title'] || $incoming['Body'] || $incoming['Excerpt']) {
 479              //Build SQL then and run query
 480  
 481              //Prevent data erase if not defined on the update action
 482              //but it was on the DB from a previous creation/edition time
 483              if ($article_id){
 484  
 485                  $old = safe_row('*','textpattern', "ID = $article_id");
 486                  //Status should be defined previously. Be sure of that.
 487                  if (!has_privs('article.publish', $this->txp_user) && $incoming['Status']==4 && $old['Status']!=4) $incoming['Status'] = 3;
 488  
 489                  foreach ($old as $key=>$val)
 490                  {
 491                       if (!isset($incoming[$key])) $incoming[$key] = $val;
 492                  }
 493  
 494              }else{
 495                  //Status should be defined previously. Be sure of that.
 496                  if (!has_privs('article.publish', $this->txp_user) && $incoming['Status']==4) $incoming['Status'] = 3;
 497              }
 498  
 499              if (empty($incoming['Section']) && $article_id)
 500              {
 501                  $incoming['Section'] = safe_field('Section','textpattern',"ID = $article_id");
 502              }
 503  
 504              $incoming = $this->_check_keys($incoming,
 505                  array(
 506                      'AuthorID' => $this->txp_user,
 507                      'Annotate' => $comments_on_default,
 508                      'AnnotateInvite' => $comments_default_invite,
 509                      'textile_body' => $use_textile,
 510                      'textile_excerpt' => $use_textile,
 511                      'url_title' => stripSpace($incoming['Title'])
 512                  )
 513              );
 514  
 515              //Build the SQL query
 516              $sql = array();
 517  
 518              foreach ($incoming as $key => $val)
 519              {
 520                  if($key == 'Posted' && $val == 'now()')
 521                  {
 522                      $sql[]= "$key = $val";
 523                  }elseif ($key!='ID' && $key!='uid' && $key!='feed_time' && $key!='LastMod' && $key!='LastModID')
 524                  {
 525                      $sql[]= "$key = '".doSlash($val)."'";
 526                  }
 527              }
 528              $sql[]= 'LastMod = now()';
 529              $sql[]= "LastModID = '".doSlash($this->txp_user)."'";
 530              if (!$article_id) $sql[]= "uid = '".doSlash(md5(uniqid(rand(),true)))."'";
 531              if (!$article_id)
 532              {
 533                  if (empty($incoming['Posted']))
 534                  {
 535                      $sql[]= "feed_time = curdate()";
 536                  }else{
 537                      $when = strtotime($incoming['Posted'])-tz_offset();
 538                      $when = strftime("%Y-%m-%d", $when);
 539                      $sql[]= "feed_time ='".doSlash($when)."'";
 540                  }
 541              }
 542              $sql = join(', ', $sql);
 543  
 544              $rs = ($article_id)?
 545                     safe_update('textpattern', $sql, "ID = $article_id"):
 546                     safe_insert('textpattern', $sql);
 547  
 548             $oldstatus = ($article_id)? $old['Status'] : '';
 549  
 550             if (!$article_id && $rs) $article_id = $rs;
 551  
 552             if (($incoming['Status']>=4 && !$article_id) || ($oldstatus!=4 && $article_id)) {
 553                  safe_update("txp_prefs", "val = now()", "name = 'lastmod'");
 554                  //@$this->_sendPings();
 555             }
 556             return $article_id;
 557          }
 558  
 559          return false;
 560      }
 561  
 562  // -------------------------------------------------------------
 563  // Private
 564  // -------------------------------------------------------------
 565  
 566      /**
 567       * Attemp to validates the user with the provided password
 568       * or takes it from the global scope, assuming the user is logged in
 569       * @param string $user login name of the user to validate
 570       * @param string(optional) $password for that user
 571       * @access private
 572       * @return boolean, true if user is logged in
 573       */
 574  	function _validate($user,$password = NULL) {
 575  
 576          if ($password!==NULL)
 577          {
 578              $r = txp_validate($user, $password);
 579          }else{
 580              $r = true;
 581          }
 582          if ($r) {
 583              // update the last access time
 584              $safe_user = addslashes($user);
 585              safe_update("txp_users", "last_access = now()", "name = '$safe_user'");
 586              return true;
 587          }
 588          return false;
 589      }
 590  
 591  // -------------------------------------------------------------
 592  // Keep this apart for now. Maybe future changes ob this?
 593  // -------------------------------------------------------------
 594  // This is duplicated code from txp_article.php too
 595  
 596  	function _sendPings()
 597      {
 598          global $prefs, $txpcfg;
 599          extract($prefs);
 600  
 601          include_once txpath.'/lib/IXRClass.php';
 602  
 603          if ($ping_textpattern_com) {
 604              $tx_client = new IXR_Client('http://textpattern.com/xmlrpc/');
 605              $tx_client->query('ping.Textpattern', $sitename, hu);
 606          }
 607  
 608          if ($ping_weblogsdotcom==1) {
 609              $wl_client = new IXR_Client('http://rpc.pingomatic.com/');
 610              $wl_client->query('weblogUpdates.ping', $sitename, hu);
 611          }
 612      }
 613  
 614      /**
 615       * Check if the given parameters are the appropiated ones for the articles
 616       * @access private
 617       * @param $incoming array the incoming associative array
 618       * @param $default associative array containing default values for the desired keys
 619       * @return array properly striped off the fields which don't match the defined ones.
 620       */
 621  	function _check_keys($incoming, $default = array())
 622      {
 623          $out = array();
 624          # strip off unsuited keys
 625          foreach ($incoming as $key => $val)
 626          {
 627              if (in_array($key, $this->vars))
 628              {
 629                  $out[$key] = $val;
 630              }
 631          }
 632  
 633          foreach ($this->vars as $def_key)
 634          {
 635              # Add those ones inexistent in the incoming array
 636              if (!array_key_exists($def_key,$out))
 637              {
 638                  $out[$def_key] = '';
 639              }
 640              # setup the provided default value, if any, only when the incoming value is empty
 641              if (array_key_exists($def_key, $default) && empty($out[$def_key]))
 642              {
 643                  $out[$def_key] = $default[$def_key];
 644              }
 645          }
 646          return $out;
 647      }
 648  
 649      /**
 650       * Apply textile to the main article fields
 651       * (duplicated from txp_article.php!)
 652       * @param array containing the $incoming vars array
 653       * @param global use_textile preference
 654       * @return array the same one containing the formatted fields
 655       */
 656  
 657  	function textile_main_fields($incoming, $use_textile = 1)
 658      {
 659          global $txpcfg;
 660  
 661          include_once txpath.'/lib/classTextile.php';
 662          $textile = new Textile();
 663  
 664          if (!empty($event) and $event == 'article')
 665          {
 666              $incoming['Title_plain'] = $incoming['Title'];
 667          }
 668  
 669          if ($incoming['textile_body'] == USE_TEXTILE)
 670          {
 671              $incoming['Title'] = $textile->TextileThis($incoming['Title'],'',1);
 672          }
 673  
 674          $incoming['url_title'] = preg_replace('|[\x00-\x1f#%+/?\x7f]|', '', $incoming['url_title']);
 675  
 676          $incoming['Body_html'] = TXP_Wrapper::format_field($incoming['Body'],$incoming['textile_body'],$textile);
 677  
 678          $incoming['Excerpt_html'] = TXP_Wrapper::format_field($incoming['Excerpt'],$incoming['textile_excerpt'],$textile);
 679  
 680          return $incoming;
 681      }
 682  
 683      # Try to avoid code duplication when formating fields.
 684      /**
 685       * Apply markup to a given fields
 686       *
 687       * @param string $field raw field contents
 688       * @param integer $format format type to apply
 689       * @param object $textile instance
 690       * @return string html formated field
 691       */
 692  
 693  	function format_field($field, $format,$textile)
 694      {
 695          switch ($format){
 696              case LEAVE_TEXT_UNTOUCHED: $html = trim($field); break;
 697              case CONVERT_LINEBREAKS: $html = nl2br(trim($field)); break;
 698              case USE_TEXTILE:
 699                  $html = $textile->TextileThis($field);
 700              break;
 701          }
 702          return $html;
 703      }
 704  
 705  }
 706  
 707  ?>


Generated: Thu May 21 23:03:01 2009 Cross-referenced by PHPXref 0.7