[ Index ] |
PHP Cross Reference of Textpattern 4.0.8 |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu May 21 23:03:01 2009 | Cross-referenced by PHPXref 0.7 |