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; } } ?>