Файл: textpattern-4.5.7/textpattern/lib/txplib_wrapper.php
Строк: 727
<?php
/*
$HeadURL: https://textpattern.googlecode.com/svn/releases/4.5.7/source/textpattern/lib/txplib_wrapper.php $
$LastChangedRevision: 2931 $
*/
/**
* 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.
*
* @link http://txp.kusor.com/wrapper
* @author Pedro Palazon - http://kusor.net/
* @copyright 2005-2008 The Textpattern Development Team - http://textpattern.com
*/
# This class requires to include some Textpattern files in order to work properly.
# See RPC Server implementation to view an example of the required files and predefined variables.
if (!defined('txpath')) die('txpath is undefined.');
include_once txpath.'/include/txp_auth.php';
# Include constants.php?
if (!defined('LEAVE_TEXT_UNTOUCHED')) define('LEAVE_TEXT_UNTOUCHED', 0);
if (!defined('USE_TEXTILE')) define('USE_TEXTILE', 1);
if (!defined('CONVERT_LINEBREAKS')) define('CONVERT_LINEBREAKS', 2);
class TXP_Wrapper
{
/**
* @var string The current user
*
* Remeber to use allways $this->txp_user when checking for permissions with this class
*/
var $txp_user = null;
/**
* @var boolean Is the user authenticated
*/
var $loggedin = false;
/**
* @var array Predefined Textpattern vars to be populated
*/
var $vars = array(
'ID','Title','Title_html','Body','Body_html','Excerpt','Excerpt_html','textile_excerpt','Image',
'textile_body', 'Keywords','Status','Posted','Section','Category1','Category2',
'Annotate','AnnotateInvite','AuthorID','Posted','override_form',
'url_title','custom_1','custom_2','custom_3','custom_4','custom_5',
'custom_6','custom_7','custom_8','custom_9','custom_10'
);
//Class constructor
/**
* Class constructor
* @param string $txp_user the user login name
* @param strign $txpass user password
*
* @see _validate
*/
function TXP_Wrapper($txp_user, $txpass = NULL)
{
if ($this->_validate($txp_user, $txpass))
{
$this->txp_user = $txp_user;
$this->loggedin = true;
}
}
//Delete the article given the id
/**
* Delete the article given the id
* @param mixed(string|integer) $article_id the ID of the article to delete
* @return boolean true on success deletion
*/
function deleteArticleID($article_id)
{
$article_id = assert_int($article_id);
if ($this->loggedin && has_privs('article.delete', $this->txp_user)) {
return safe_delete('textpattern', "ID = $article_id");
}
elseif ($this->loggedin && has_privs('article.delete.own', $this->txp_user))
{
$r = safe_field('ID', 'textpattern', "ID = $article_id AND AuthorID='".doSlash($this->txp_user)."'");
if ($r || has_privs('article.delete', $this->txp_user))
{
return safe_delete('textpattern', "ID = $article_id");
}
}
return false;
}
//Retrieves a list of articles matching the given criteria
/**
* Retrieves a list of articles matching the given criteria
* @param string $what SQL column names to retrieve
* @param string $where SQL condition to match
* @param string $offset SQL offset
* @param string $limit SQL limit
* @param boolean $slash escape SQL column names and condition
* @return mixed array on success, false on failure
*/
function getArticleList($what='*', $where='1', $offset='0', $limit='10', $slash=true)
{
if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
{
$offset = assert_int($offset);
$limit = assert_int($limit);
if ($slash) {
$where = doSlash($where);
$what = doSlash($what);
}
if (has_privs('article.edit', $this->txp_user)) {
$rs = safe_rows_start($what, 'textpattern', $where." order by Posted desc LIMIT $offset, $limit");
}else{
$rs = safe_rows_start($what, 'textpattern', $where." AND AuthorID='".doSlash($this->txp_user)."' order by Posted desc LIMIT $offset, $limit");
}
$out = array();
if ($rs)
{
while ($a = nextRow($rs))
{
$out[]= $a;
}
}
return $out;
}
return false;
}
//Retrieves an article matching the given criteria
/**
* Retrieves an article matching the given criteria
* @param string $what SQL column names to retrieve
* @param string $where SQL condition to match
* @param boolean $slash escape SQL column names and condition
* @return mixed array on success, false on failure
*/
function getArticle($what='*', $where='1', $slash=true)
{
if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
{
if ($slash) {
$what = doSlash($what);
$where = doSlash($where);
}
// Higer user groups should be able to edit any article
if (has_privs('article.edit', $this->txp_user)) {
return safe_row($what, 'textpattern', $where);
}else {
// While restricted users should be able to edit their own articles only
return safe_row($what, 'textpattern', $where." AND AuthorID='".doSlash($this->txp_user)."'");
}
}
return false;
}
//Same thing, but handy shortcut known the ID
/**
* Same thing, but handy shortcut known the ID
* @param mixed(string|integer) $article_id the ID of the article
* @param string $what SQL column names to retrieve
* @return mixed array on success, false on failure
*/
function getArticleID($article_id, $what='*')
{
if ($this->loggedin && has_privs('article.edit.own', $this->txp_user))
{
$article_id = assert_int($article_id);
if (has_privs('article.edit', $this->txp_user)) {
return safe_row(doSlash($what), 'textpattern', "ID = $article_id");
}else{
return safe_row(doSlash($what), 'textpattern', "ID = $article_id AND AuthorID='".doSlash($this->txp_user)."'");
}
}
return false;
}
//Updates an existing article
/**
* Updates an existing article
* @param array $params the article fields to update
* @param mixed(string|integer) $article_id the ID of the article to update
* @return mixed integer article id on success, false on failure
* @see _setArticle
*/
function updateArticleID($article_id, $params)
{
$article_id = assert_int($article_id);
$r = safe_field('ID', 'textpattern', "AuthorID='".doSlash($this->txp_user)."' AND ID = $article_id");
if ($this->loggedin && $r && has_privs('article.edit.own', $this->txp_user))
{ //Unprivileged user
//Check if can edit published arts
$r = assert_int($r);
$oldstatus = safe_field('Status', 'textpattern', "ID = $r");
if (($oldstatus=='4' || $oldstatus == '5') && !has_privs('article.edit.published', $this->txp_user)) return false;
//If can, let's go
return $this->_setArticle($params, $article_id);
}
elseif ($this->loggedin && has_privs('article.edit', $this->txp_user))
{//Admin editing. Desires are behest.
return $this->_setArticle($params, $article_id);
}
return false;
}
//Creates a new article
/**
* Creates a new article
* @param array $params the article fields
* @return mixed integer article id on success, false on failure
* @see _setArticle
*/
function newArticle($params)
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
//Prevent junior authors to publish articles
if (($params['Status']=='4' || $params['Status']=='5') && !has_privs('article.publish', $this->txp_user))
{
$params['Status']='3';
}
return $this->_setArticle($params);
}
return false;
}
//Get full sections information
/**
* Get full sections information
* @return mixed array on success, false on failure
*/
function getSectionsList()
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
return safe_rows('*', 'txp_section',"name!='default'");
}
return false;
}
//Get one section
/**
* Get one section
* @param string $name the section name
* @return mixed array on success, false on failure
*/
function getSection($name)
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
$name = doSlash($name);
return safe_row('*', 'txp_section',"name='$name'");
}
return false;
}
//Get full categories information
/**
* Get full categories information
* @return mixed array on success, false on failure
*/
function getCategoryList()
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
return safe_rows('*', 'txp_category',"name!='root' AND type='article'");
}
return false;
}
/**
* Get one category by category name
* @param string $name the category name
* @return mixed array on success, false on failure
*/
function getCategory($name)
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
$name = doSlash($name);
return safe_row('*', 'txp_category',"name='$name' AND type='article'");
}
return false;
}
/**
* Get one category by category id
* @param mixed(string|integer) $id category id
* @return mixed array on success, false on failure
*/
function getCategoryID($id)
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
$id = assert_int($id);
return safe_row('*', 'txp_category',"id = $id");
}
return false;
}
/**
* Get one category by category title
* @param string $title the category title
* @return mixed array on success, false on failure
*/
function getCategoryTitle($title)
{
if ($this->loggedin && has_privs('article', $this->txp_user))
{
$title = doSlash($title);
return safe_row('*', 'txp_category',"title='$title' AND type='article'");
}
return false;
}
//Get full information for current user
/**
* Get full information for current user
* @return mixed array on success, false on failure
*/
function getUser()
{
if ($this->loggedin)
{
return safe_row('*', 'txp_users',"name='$this->txp_user'");
}
return false;
}
//Retrieves a template with the given name
/**
* Retrieves a template with the given name
* @param string $name the template name
*/
function getTemplate($name)
{
if ($this->loggedin && has_privs('page', $this->txp_user))
{
$name = doSlash($name);
return safe_field('user_html', 'txp_page', "name='$name'");
}
return false;
}
//Updates a template with the given name
/**
* Updates a template with the given name
* @param string $name the template name
* @param string $html the template contents
* @return boolean true on success
*/
function setTemplate($name, $html)
{
if ($this->loggedin && has_privs('page', $this->txp_user))
{
$name = doSlash($name);
$html = doSlash($html);
return safe_update('txp_page', "user_html='$html'", "name='$name'");
}
return false;
}
// Intended to update article non content fields, like categories
// section or Keywords
/**
* Intended to update article non content fields, like categories, section or Keywords
* @param mixed(string|integer) $article_id the ID of the article to update
* @param string $field the name of the field to update
* @param mixed $value desired value for that field
* @return boolean true on success
*/
function updateArticleField($article_id, $field, $value)
{
$disallow = array('Body','Body_html','Title','Title_html','Excerpt',
'Excerpt_html','textile_excerpt','textile_body','LastMod',
'LastModID', 'feed_time', 'uid');
if ($this->loggedin && has_privs('article.edit', $this->txp_user) && !in_array(doSlash($field),$disallow))
{
$field = doSlash($field);
$value = doSlash($value);
if($field == 'Posted')
{
$value = strtotime($value)-tz_offset();
$value = "from_unixtime($value)";
$sql = "Posted = $value";
}elseif ($field == 'Status'){
$value = assert_int($value);
if (!has_privs('article.publish', $this->txp_user) && $value >=4) $value = 3;
$sql = "Status = $value";
}else{
$sql = "$field='$value'";
}
$sql.= ", LastMod = now(),
LastModID = '$this->txp_user'";
$article_id = assert_int($article_id);
$rs = safe_update('textpattern', $sql, "ID = $article_id");
//Do not update lastmod pref here. No new content at all.
return $rs;
}
return false;
}
// -------------------------------------------------------------
// Private. Real action takes place here.
// -------------------------------------------------------------
/**
* Executes the real action for @see udpateArticleId and @see newArticle
* @param array $incoming containing the desired article fields
* @param mixed(string|integer) $article_id the ID of the article to update
* @return mixed integer article id on success, false otherwise
* @access private
*/
function _setArticle($incoming, $article_id = null)
{
global $txpcfg;
$prefs = get_prefs();
extract($prefs);
if (!empty($incoming['Section']) && !$this->getSection($incoming['Section'])) {
return false;
}
if (!empty($incoming['Category1']) && !$this->getCategory($incoming['Category1'])) {
return false;
}
if (!empty($incoming['Category2']) && !$this->getCategory($incoming['Category2'])) {
return false;
}
if ($article_id!==null) {
$article_id = assert_int($article_id);
}
//All validation rules assumed to be passed before this point.
//Do content processing here
$incoming_with_markup = $this->textile_main_fields($incoming, $use_textile);
$incoming['Title'] = $incoming_with_markup['Title'];
if (empty($incoming['Body_html']) && !empty($incoming['Body']))
{
$incoming['Body_html'] = $incoming_with_markup['Body_html'];
}
if (empty($incoming['Excerpt_html']) && !empty($incoming['Excerpt']))
{
$incoming['Excerpt_html'] = $incoming_with_markup['Excerpt_html'];
}
unset($incoming_with_markup);
if (empty($incoming['Posted'])) {
if ($article_id===null) {
$when = (!$article_id)? 'now()': '';
$incoming['Posted'] = $when;
}else{
# do not override post time for existing articles unless Posted is present
unset($incoming['Posted']);
}
} else {
$when = strtotime($incoming['Posted'])-tz_offset();
$when = "from_unixtime($when)";
}
if ($incoming['Title'] || $incoming['Body'] || $incoming['Excerpt']) {
//Build SQL then and run query
//Prevent data erase if not defined on the update action
//but it was on the DB from a previous creation/edition time
if ($article_id){
$old = safe_row('*','textpattern', "ID = $article_id");
//Status should be defined previously. Be sure of that.
if (!has_privs('article.publish', $this->txp_user) && $incoming['Status']==4 && $old['Status']!=4) $incoming['Status'] = 3;
foreach ($old as $key=>$val)
{
if (!isset($incoming[$key])) $incoming[$key] = $val;
}
}else{
//Status should be defined previously. Be sure of that.
if (!has_privs('article.publish', $this->txp_user) && $incoming['Status']==4) $incoming['Status'] = 3;
}
if (empty($incoming['Section']) && $article_id)
{
$incoming['Section'] = safe_field('Section','textpattern',"ID = $article_id");
}
$incoming = $this->_check_keys($incoming,
array(
'AuthorID' => $this->txp_user,
'Annotate' => $comments_on_default,
'AnnotateInvite' => $comments_default_invite,
'textile_body' => $use_textile,
'textile_excerpt' => $use_textile,
'url_title' => stripSpace($incoming['Title'])
)
);
//Build the SQL query
$sql = array();
foreach ($incoming as $key => $val)
{
if($key == 'Posted' && $val == 'now()')
{
$sql[]= "$key = $val";
}elseif ($key!='ID' && $key!='uid' && $key!='feed_time' && $key!='LastMod' && $key!='LastModID')
{
$sql[]= "$key = '".doSlash($val)."'";
}
}
$sql[]= 'LastMod = now()';
$sql[]= "LastModID = '".doSlash($this->txp_user)."'";
if (!$article_id) $sql[]= "uid = '".doSlash(md5(uniqid(rand(),true)))."'";
if (!$article_id)
{
if (empty($incoming['Posted']))
{
$sql[]= "feed_time = curdate()";
}else{
$when = strtotime($incoming['Posted'])-tz_offset();
$when = strftime("%Y-%m-%d", $when);
$sql[]= "feed_time ='".doSlash($when)."'";
}
}
$sql = join(', ', $sql);
$rs = ($article_id)?
safe_update('textpattern', $sql, "ID = $article_id"):
safe_insert('textpattern', $sql);
$oldstatus = ($article_id)? $old['Status'] : '';
if (!$article_id && $rs) $article_id = $rs;
if (($incoming['Status']>=4 && !$article_id) || ($oldstatus!=4 && $article_id)) {
safe_update("txp_prefs", "val = now()", "name = 'lastmod'");
//@$this->_sendPings();
}
return $article_id;
}
return false;
}
// -------------------------------------------------------------
// Private
// -------------------------------------------------------------
/**
* Attemp to validates the user with the provided password
* or takes it from the global scope, assuming the user is logged in
* @param string $user login name of the user to validate
* @param string(optional) $password for that user
* @access private
* @return boolean, true if user is logged in
*/
function _validate($user,$password = NULL) {
if ($password!==NULL)
{
$r = txp_validate($user, $password);
}else{
$r = true;
}
if ($r) {
// update the last access time
$safe_user = addslashes($user);
safe_update("txp_users", "last_access = now()", "name = '$safe_user'");
return true;
}
return false;
}
// -------------------------------------------------------------
// Keep this apart for now. Maybe future changes ob this?
// -------------------------------------------------------------
// This is duplicated code from txp_article.php too
function _sendPings()
{
global $prefs, $txpcfg;
extract($prefs);
include_once txpath.'/lib/IXRClass.php';
if ($ping_textpattern_com) {
$tx_client = new IXR_Client('http://textpattern.com/xmlrpc/');
$tx_client->query('ping.Textpattern', $sitename, hu);
}
if ($ping_weblogsdotcom==1) {
$wl_client = new IXR_Client('http://rpc.pingomatic.com/');
$wl_client->query('weblogUpdates.ping', $sitename, hu);
}
}
/**
* Check if the given parameters are the appropiated ones for the articles
* @access private
* @param $incoming array the incoming associative array
* @param $default associative array containing default values for the desired keys
* @return array properly striped off the fields which don't match the defined ones.
*/
function _check_keys($incoming, $default = array())
{
$out = array();
# strip off unsuited keys
foreach ($incoming as $key => $val)
{
if (in_array($key, $this->vars))
{
$out[$key] = $val;
}
}
foreach ($this->vars as $def_key)
{
# Add those ones inexistent in the incoming array
if (!array_key_exists($def_key,$out))
{
$out[$def_key] = '';
}
# setup the provided default value, if any, only when the incoming value is empty
if (array_key_exists($def_key, $default) && empty($out[$def_key]))
{
$out[$def_key] = $default[$def_key];
}
}
return $out;
}
/**
* Apply textile to the main article fields
* (duplicated from txp_article.php!)
* @param array containing the $incoming vars array
* @param global use_textile preference
* @return array the same one containing the formatted fields
*/
function textile_main_fields($incoming, $use_textile = 1)
{
global $txpcfg;
include_once txpath.'/lib/classTextile.php';
$textile = new Textile();
if (!empty($event) and $event == 'article')
{
$incoming['Title_plain'] = $incoming['Title'];
}
if ($incoming['textile_body'] == USE_TEXTILE)
{
$incoming['Title'] = $textile->TextileThis($incoming['Title'],'',1);
}
$incoming['url_title'] = preg_replace('|[x00-x1f#%+/?x7f]|', '', $incoming['url_title']);
$incoming['Body_html'] = TXP_Wrapper::format_field($incoming['Body'],$incoming['textile_body'],$textile);
$incoming['Excerpt_html'] = TXP_Wrapper::format_field($incoming['Excerpt'],$incoming['textile_excerpt'],$textile);
return $incoming;
}
# Try to avoid code duplication when formating fields.
/**
* Apply markup to a given fields
*
* @param string $field raw field contents
* @param integer $format format type to apply
* @param object $textile instance
* @return string html formated field
*/
function format_field($field, $format,$textile)
{
switch ($format){
case LEAVE_TEXT_UNTOUCHED: $html = trim($field); break;
case CONVERT_LINEBREAKS: $html = nl2br(trim($field)); break;
case USE_TEXTILE:
$html = $textile->TextileThis($field);
break;
}
return $html;
}
}
?>