One drupal site to create a node on another drupal site in a multi-site setup. It looks like I'm getting the sessionid and logging in just fine, but when trying to create a "page" node, I get "Access denied". Under Services -> Settings, I have "Key Authentication", "Use keys" is unchecked, and "Use sessid" is checked.
I gave permissions for the logged in user: "create page content", "administer services", etc... I'm running Services 6.x-2.2 and XMLRPC Server 6.x-2.2
Below is my code:
<? $url = 'http://drupal2.dev/xmlrpc.php'; ?>
<?
$conn = xmlrpc($url, 'system.connect');
print_r($conn);
?>
--
<?
$login = xmlrpc($url, 'user.login', $conn['sessid'], 'superuser_name', 'superuser_password');
print_r($login);
?>
--
<?
$data=array('type'=>'page', 'title'=>'Test', 'body'=>'test');
$data_s=serialize($data);
$result = xmlrpc($url, 'node.save', $login['sessid'], $data_s);
echo $result;
//echo $data_s;
?>
<?
if($error = xmlrpc_error()){
if($error->code > 0){
$error->message = t('Outgoing HTTP request failed because the socket could not be opened.');
}
drupal_set_message(t('Operation failed because the remote site gave an error: %message (@code).',
array(
'%message' => $error->message,
'@code' => $error->code
)
)
);
}
?>
The ouput of this script is:
Array ( [sessid] => 9eebdde9bf0bfd9610cc2f03af131a9c [user] => Array ( [uid] => 0 [hostname] => ::1 [roles] => Array ( [1] => anonymous user ) [session] => [cache] => 0 ) )
-- Array ( [sessid] => c0ca4c599e41e97e7a7ceb43ee43249e [user] => Array ( [uid] => 1 [name] => eric [pass] => 13583b155536098b98df41bb69fcc53 [mail] => email@gmail.com [mode] => 0 [sort] => 0 [threshold] => 0 [theme] => [signature] => [signature_format] => 0 [created] => 1271813934 [access] => 1275867734 [login] => 1275868794 [status] => 1 [timezone] => [language] => [picture] => [init] => email@gmail.com [data] => a:0:{} [roles] => Array ( [2] => authenticated user ) ) )
Comments
I'm not sure which xmlrpc
I'm not sure which xmlrpc client you are using based on the code snippets you gave. but i finally just hardwired a drupal copy to my system for the one i use. anyway first things first, why are you serializing the node?
anyway i wrote several functions and a class to make this all easier for me.
for reference here is the part of the script that pushes the data from my local server to the remote drupal server. [or updates the node if it already exists]
function push($iwantcandy) {global $db_reader, $db_writer, $xmlrpc;
$sql1 = 'SELECT nid from drupalnodes where sid = \''.$iwantcandy['sid'].'\'';
$results = $db_reader->fetch($sql1);
foreach ($results as $result) {
if(!empty($result)) {
$node = $xmlrpc->send('node.get',array($result['nid']));
}
}
$sql2 = 'SELECT s.title,s.body,s.priority,UNIX_TIMESTAMP(s.date) as timestamp,c.tid,c.vid,d.nid from stories as s JOIN cats as c on (s.cat = c.cid) LEFT JOIN drupalnodes as d on (s.sid = d.sid) where s.sid = \'' . $iwantcandy['sid'] . '\'';
$results = $db_reader->fetch($sql2);
foreach($results as $result) {
if(is_null($result['nid'])){
$node = array(
'title' => $result['title'],
'body' => $result['body'],
'type' => 'story',
'uid' => 4,
'taxonomy' => array($result['vid'] => array($result['tid'])),
'field_story_priority' => array(array('value'=>$result['priority'])),
);
} else {
$node['taxonomy'] = '';
$node['title'] = $result['title'];
$node['body'] = $result['body'];
$node['changed'] = $result['timestamp'];
$node['taxonomy'] = array($result['vid'] => array($result['tid']));
$node['field_story_priority'] = array(array('value'=>$result['priority']));
}
$node['field_story_images'] = imageLink($iwantcandy['sid']);
putNidIntoDatabase($iwantcandy,$xmlrpc->send('node.save',array($node)));
}
}
some of the variable names came to me in dementia
and here is the services drupal class i use.
<?php
//include_once 'includes/bootstrap.inc';
//drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
//include_once 'includes/bootstrap.inc';
//include_once 'includes/common.inc';
include_once 'includes/xmlrpc.inc';
class drupalpush {
private $apiOptions = array();
private $endpoint;
public function __construct($host) {
$this->apiOptions['sessid'] = '';
$this->apiOptions['uid'] = 0;
$this->endpoint = $host;
$retVal = $this->send( 'system.connect', array() );
$this->apiOptions['sessid'] = $retVal['sessid'];
}
public function send($method,$send = array()) {
if ($method == 'system.connect') {
$protocolArgs = array( $this->endpoint, $method );
} else {
$protocolArgs = array( $this->endpoint, $method, $this->apiOptions['sessid'] );
}
$params = array_merge( $protocolArgs, $send );
return call_user_func_array( '_xmlrpc', $params );
}
public function login($user,$pass) {
xmlrpc_error( NULL, NULL, TRUE );
$retVal = $this->send( 'user.login', array($user, $pass) );
if (!$retVal && xmlrpc_errno()) {
echo 'ERROR';
return false;
} else {
$this->apiOptions['sessid'] = $retVal['sessid'];
$this->apiOptions['user'] = (object)$retVal['user'];
$this->apiOptions['uid'] = $this->apiOptions['user']->uid;
return $this->apiOptions['user'];
}
}
public function logout() {
$retVal = $this->send( 'user.logout', array() );
}
}
?>
Thanks for the code
Thanks for the code whytewolf. I'll try it out.
I'm serializing it because I thought that was what it needed. On this page "/admin/build/services/browse/node.save" , it's asking for the node to be in either JSON or Serialized PHP. If I manually paste in the serialized PHP that my code snippet produces for $data_s, which is "a:3:{s:4:"type";s:4:"page";s:5:"title";s:24:"Another Test from site 1";s:4:"body";s:12:"A test again";}" into the services node field, it does create the page node.
From all the examples I've seen(very few), it looks like you call the function like this to communicate: $result = xmlrpc($url, 'node.save', $login['sessid'], $data_s); , but I see you're using this: $xmlrpc->send('node.save',array($node)));
I'm new to Services, so I'm a little confused.
Update: I removed the serialization and just passed it as an array and it worked, but it's publishing the node as "Anonymous", which is wrong because I'm using the sessid created when I did the user.login call.
thanks for the help
expected behavior.
actually you need to set the uid in the node that your passing as the uid that is being passed to you with the user.login. i know it's convoluted. but it's how drupal really tracks who is the author of the node. with out the uid the node is said to be posted by anonymous.
one way to know for sure is to do a default node.get on a normally edited node. it will give you more information about how a node should look when you submit it. be careful about the order of things. i learned the hard way that order is important even tho it's all named arrays.
as for me using $xmlrpc->send('node.save',array($node)); I just prefer classes and objects for some things, so i built a class wrapper for the xmlrpc that keeps the session data internal so i don't have to worry about it for each call to xmlrpc. it's more a style thing. my $xmlrpc is really just an object of drupalpush which is that class in the second set of code.
"Token has expired." Message after turning on Key Auth
I now tuned on Key Authentication in Services->Settings , then changed the code to add the key and tried to call node.view. It's displaying "Token has expired."
This is all on a multi-site setup on my local computer running XAMMP, so I pretty sure it can't be a "time" issue. I also upped the "Token expiry time" setting to 500.
Here is my code:
<?
$host= 'http://drupal2.dev/xmlrpc.php';
$hash_array = array(
'domain' => 'drupal2.dev',
//'timestamp' => (string) strtotime("+2 hour"),
'timestamp' => (string) time(),
'nonce' => user_password(),
'rpc' => 'node.view',
);
//$fields=array('title', 'body');
$api_key = '28f7f9854af7c7455c143e6bbb5cc4f8';
$hash = hash_hmac('sha256', implode(';', $hash_array), $api_key);
$result = xmlrpc($host, $hash_array['rpc'], $hash, $hash_array['timestamp'], $hash_array['nonce'], 1, $fields);
print_r($result);
if($error = xmlrpc_error()){
if($error->code <= 0){
$error->message = t('Outgoing HTTP request failed because the socket could not be opened.');
}
drupal_set_message(t('Operation failed because the remote site gave an error: %message (@code).', array('%message' => $error->message,
'@code' => $error->code
)
)
);
}
?>
I solved the issue with Drupal "token has expired"
Just login to you Drupal Admin panel and go to following URL:
http://yourwebsite.com/admin/build/services/settings
On this page you will find and "Token expiry time: " setting. Change the value 30 to 180. that will solve the issue
Software Development