Okay,
I’ve been playing with making it possible to edit, update and manage blogs (and other documents later) using the IXR_Class for XML-RPC.
This is the most common xmlrpc server for blogs including Wordpress.
I had started playing with files from this thread:
http://modxcms.com/forums/index.php/topic,5827.msg42532.html#msg42532
The problem was that it wasn’t complete nor could it utilize the modx API and DBAPI from within modx.
What I have worked out is the following:
1. Create a folder in /assets/snippets/ called xmlrpc
2. Upload files attached below to the folder.
3. Create a snippet called
xmlrpc with the following code:
<?php
/* -------------------------------------------------------------
:: Snippet: xmlrpc
----------------------------------------------------------------
Short Description:
XML-RPC Server based on Incutio XML-RPC Server
---------------------------------------------------------------- */
// xmlrpc version being executed
define('XR_VERSION', '0.0.1');
// Path where xmlrpc is installed
define('XR_SPATH', 'assets/snippets/xmlrpc/');
//==============================================================================
//include snippet file
define ('XR_PATH', $modx->config['base_path'].XR_SPATH);
$output = "";
include(XR_PATH.'xmlrpc.inc.php');
return $output;
?>
4. Create a document in modx, insert the following snippet call:
[!xmlrpc!], change the template to blank and publish the document
5. Using Scribefire, MarsEdit or Qumana try to connect to the blog.
It doesn’t work--yet. I need to complete cleaning the sql queries out to use the modx api since it will be cleaner and be easier to migrate for new versions.
I need help making sure the logic is working.
I need help to troubleshoot the logging mechanism.
Here is the xmlrpc.inc.php code (included in the zip):
<?php
# fix for mozBlog and other cases where '<?xml' isn't on the very first line
$HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
//Get our XML-RPC bits going
require ("class/class-IXR.php"); //In Same Dir as XMLRPC - namely "/" or $_SERVER["DOCUMENT_ROOT"]
// Turn off all warnings and errors.
// error_reporting(0);
$post_default_title = ''; // posts submitted via the xmlrpc interface get that title
$post_default_category = 1; // posts submitted via the xmlrpc interface go into that category
$xmlrpc_logging = 1;
function logIO($io,$msg) {
global $xmlrpc_logging;
if ($xmlrpc_logging) {
$fp = fopen("xmlrpc.log","a+");
$date = gmdate("Y-m-d H:i:s ");
$iot = ($io == "I") ? " Input: " : " Output: ";
fwrite($fp, "\n\n".$date.$iot.$msg);
fclose($fp);
}
return true;
}
function starify($string) {
$i = strlen($string);
return str_repeat('*', $i);
}
logIO("I", $HTTP_RAW_POST_DATA);
function mkdir_p($target) {
// from php.net/mkdir user contributed notes
if (file_exists($target)) {
if (!is_dir($target)) {
return false;
} else {
return true;
}
}
// Attempting to create the directory may clutter up our display.
if (@mkdir($target)) {
return true;
}
// If the above failed, attempt to create the parent node, then try again.
if (mkdir_p(dirname($target))) {
return mkdir_p($target);
}
return false;
}
class modx_xmlrpc_server extends IXR_Server {
function modx_xmlrpc_server() {
$this->methods = array(
// Blogger API
'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
'blogger.getUserInfo' => 'this:blogger_getUserInfo',
'blogger.getPost' => 'this:fake',//'this:blogger_getPost',
'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
'blogger.getTemplate' => 'this:fake',//'this:blogger_getTemplate',
'blogger.setTemplate' => 'this:fake',//'this:blogger_setTemplate',
'blogger.newPost' => 'this:fake',//'this:blogger_newPost',
'blogger.editPost' => 'this:fake',//'this:blogger_editPost',
'blogger.deletePost' => 'this:fake',//'this:blogger_deletePost',
// MetaWeblog API (with MT extensions to structs)
'metaWeblog.newPost' => 'this:mw_newPost',
'metaWeblog.editPost' => 'this:mw_editPost',
'metaWeblog.getPost' => 'this:mw_getPost',
'metaWeblog.getRecentPosts' => 'this:blogger_getRecentPosts',
'metaWeblog.getCategories' => 'this:mw_getCategories',
'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
// MetaWeblog API aliases for Blogger API
// see http://www.xmlrpc.com/stories/storyReader$2460
'metaWeblog.deletePost' => 'this:blogger_deletePost',
'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
// MovableType API
'mt.getCategoryList' => 'this:blogger_getUsersBlogs',//'this:mt_getCategoryList',
'mt.getRecentPostTitles' => 'this:blogger_getRecentPosts',//'this:mt_getRecentPostTitles',
'mt.getPostCategories' => 'this:mt_getPostCategories',
'mt.setPostCategories' => 'this:mt_setPostCategories',
'mt.supportedMethods' => 'this:mt_supportedMethods',
'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
'mt.publishPost' => 'this:mt_publishPost',
// PingBack
'pingback.ping' => 'this:pingback_ping',
'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
'demo.sayHello' => 'this:sayHello',
'demo.addTwoNumbers' => 'this:addTwoNumbers'
);
//$this->methods = apply_filters('xmlrpc_methods', $this->methods);
$this->IXR_Server($this->methods);
}
function sayHello($args) {
return 'Hello!';
}
function addTwoNumbers($args) {
$number1 = $args[0];
$number2 = $args[1];
return $number1 + $number2;
}
//Check login user/pass against modx
function fake($user_login, $user_pass) {
return;
}
function user_pass_ok($user_login, $user_pass) {
logIO("I", "in user_pass_ok\n");
global $dbase,$table_prefix, $modx;
//Safely escape our user/pass so no evil stuff happens.
$user_login = $modx->db->escape($user_login);
$user_pass = $modx->db->escape($user_pass);
logIO("I", "after escape logins\n");
//Somewhere in here we want to get the user id of the user of the person attempting to login
//and if the username exists check the password
//and if the password is a match get the records for the user
//Get check admin exists based on user_login and get id and password
$ufields = "*";
$ufrom = $modx->db->config['table_prefix']."manager_users";
$uwhere = "`username` = '$user_login'";
$fetch = $modx->db->getRow($modx->db->select($ufields, $ufrom, $uwhere));
$fuser = $fetch['username'];
$fpass = $fetch['password'];
$fid = $fetch['id'];
$ua_fields = "*" ;
$ua_from = $modx->db->config['table_prefix']."user_attributes";
$ua_where = "`id` = '$fid'";
$user_atribs = $modx->db->getRow($modx->db->select($ua_fields, $ua_from, $ua_where));
logIO("I", "after db\n");
//U-oh, either no such user or >1 user. Shouldn't happen...
if($user_login != $fuser) {
logIO("I","Bad User:".$user_login);
$this->error = new IXR_Error(403, 'Bad login/pass combination.');
return (FALSE);
}
$row = mysql_fetch_assoc($rs);
$id = $row['id'];
$dbasePassword = $fetch['password'];
$failedlogins = $row['failedlogincount'];
$blocked = $row['blocked'];
$blockeduntildate = $row['blockeduntil'];
$blockedafterdate = $row['blockedafter'];
logIO("I","Good User:".$user_login."\n");
if($dbasePassword != md5($user_pass)) {
//Bad Password
logIO("I","Bad Pass:".md5($user_pass));
$this->error = new IXR_Error(403, 'Bad login/pass combination.');
return (FALSE);
}
//Lazy - ignore bad login and failed login for now
logIO("I","ID is:".$id);
return $id;
}
function login_pass_ok($user_login, $user_pass) {
$result=$this->user_pass_ok($user_login, $user_pass);
if (!$result) {
$this->error = new IXR_Error(403, 'Bad login/pass combination.');
logIO("I","Bad Pass:".$md5($user_pass));
return false;
}
return $result;
}
/* Blogger API functions
* specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
*/
/* blogger.getUsersBlogs will make more sense once we support multiple blogs */
function blogger_getUsersBlogs($args) {
global $dbase,$table_prefix;
$user_login = $args[1];
$user_pass = $args[2];
$result=$this->user_pass_ok($user_login, $user_pass);
if (!$result) {
return $this->error;
}
logIO("I","in getUsersBlogs");
//Get site_name
$sql = "SELECT setting_value from ". $dbase. "." .$table_prefix."system_settings where setting_name = 'site_name'";
$rs = mysql_query($sql);
$limit = mysql_num_rows($rs);
logIO("I","after db query:".$sql."\n");
if ($limit==0) {
$site_name = "[Unknown Site Name";
}
else {
$row = mysql_fetch_assoc($rs);
$site_name = $row['setting_value'];
}
logIO("I","SiteName:".$site_name."\n");
//Is admin user?
/* $user_data = $modx->getUserInfo($result);
if ($user_data['usertype']=="manager") {
$isAdmin=1;
}
else {
$isAdmin=0;
}
logIO("I","site name:".$site_name."\n");
logIO("I","Is Admin:".$isAdmin."\n");*/
//Get the users current folder list for xxx
$struct = array(
'isAdmin' => $isAdmin, //is admin?
'url' => $site_name,
'blogid' => '1', //NA ...
'blogName' => 'blogs name' //(id home page name)
);
return array($struct);
}
/* blogger.getUsersInfo gives your client some info about you, so you don't have to */
function blogger_getUserInfo($args) {
global $modx;
logIO("I","in getUserInfo\n");
$user_login = $args[1];
$user_pass = $args[2];
$result=$this->user_pass_ok($user_login, $user_pass);
if (!$result) {
return $this->error;
}
$user_data = $modx->getUserInfo($result);
$struct = array(
'nickname' => $user_data->user_nickname,
'userid' => $user_data->ID,
'url' => $user_data->user_url,
'email' => $user_data->user_email,
'lastname' => $user_data->user_lastname,
'firstname' => $user_data->user_firstname
);
return $struct;
}
function mysql2date($dateformatstring, $mysqlstring, $translate = true) {
$m = $mysqlstring;
if ( empty($m) ) {
return false;
}
$i = mktime(substr($m,11,2),substr($m,14,2),substr($m,17,2),substr($m,5,2),substr($m,8,2),substr($m,0,4));
if( 'U' == $dateformatstring )
return $i;
if ( -1 == $i || false == $i )
$i = 0;
$j = @date($dateformatstring, $i);
if ( !$j ) {
// for debug purposes
// echo $i." ".$mysqlstring;
}
return $j;
}
function modx_get_recent_posts($num = 10) {
global $dbase,$table_prefix, $modx;
// Set the limit clause, if we got a limit
if ($num) {
$limit = "LIMIT $num";
}
$sql = "SELECT id as ID, pagetitle as post_title, createdon as post_date, content as post_content, createdby as post_author FROM $dbase.".$table_prefix."site_content WHERE published = '1' ORDER BY post_date DESC $limit";
logIO("I","$sql");
$count=0;
$rs = mysql_query($sql);
if(@mysql_num_rows($rs)) {
while($row=mysql_fetch_array($rs)) {
$result[]=$row;$count++;
}
}
logIO("I","modx_grp:$count.");
return $result;
//return $result?$result:array();
}
function blogger_getRecentPosts($args) {
$blog_ID = $args[1]; /* though we don't use it yet */
$user_login = $args[2];
$user_pass = $args[3];
$num_posts = $args[4];
$result=$this->user_pass_ok($user_login, $user_pass);
if (!$result) {
return $this->error;
}
logIO("I","in getRecentPosts");
$posts_list = $this->modx_get_recent_posts($num_posts);
if (!$posts_list) {
$this->error = new IXR_Error(500, 'Either there are no posts, or something went wrong.');
return $this->error;
}
$count=0;
logIO("I","blogger_grp:pre posts list.");
foreach ($posts_list as $entry) {
$count++; logIO("I",$count);
$post_date = $this->mysql2date('Ymd\TH:i:s', $entry['post_date']);
//$categories = implode(',', modx_get_post_categories($entry['ID']));
$content = '<title>'.stripslashes($entry['post_title']).'</title>';
$content .= '<category>0</category>'; // '<category>'.$categories.'</category>';
$content .= stripslashes($entry['post_content']);
$struct[] = array(
'userid' => $entry['post_author'],
'dateCreated' => new IXR_Date($post_date),
'content' => $content,
'postid' => $entry['ID'],
);
logIO("I","inforeach$count");
}
$recent_posts = array();
for ($j=0; $j<count($struct); $j++) {
array_push($recent_posts, $struct[$j]);
}
logIO("I","here");
return $recent_posts;
}
}
$modx_xmlrpc_server = new modx_xmlrpc_server();
?>
If anyone wants to help bring this along my PHP skills are pretty sad but this is a very important piece of modx that could have huge applications for all end users. Think of not having to use the admin panel at all to edit an article?