Nuisances of Building a Web Service for a Drupal Website

Was building a web service that serves RSS feeds from an existing Drupal website today. The feed URL had to accept parameters like the Drupal site username and password for authentication, dates for
filtering the nodes to be rendered in the feeds. I have heard about the Services module or customizing the Drupal RSS feed as needed.

But I felt I would be able to build the feed better from a PHP script instead of Services or building a Drupal module to do this.. I would however have to include Drupal's bootstrap.inc and initiate bootstrapping through drupal_bootstrap() from
the PHP script, so that the Drupal functions will be available for use in my script. Also included Drupal's password.inc.

This was for the user_check_password() that I used to validate the username and password provided in the URL.

Another reason of choosing to build the web service through a
Drupal-Bootstrapped PHP script instead of any other method is my recent tryst with building the web-service for ArticleSpinner , that proved to be an instant hit.

Had an issue here. As I was outputting XML to be parsed by XML readers,
the <?xml version="1.0" encoding="UTF-8"?>was supposed to be the first line to reach the client. (after the header). However there was a line-break ("\n") in the output of the script before the XML was sent to the client. A little debugging revealed that it was in fact

drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

that was actually causing the line-break. This was not the case with a fresh Drupal installation. Was more than lazy to figure it out.

XML Error

XML parsing failed
XML parsing failed: syntax error (Line: 2, Character: 0)
Reparse document as HTML
Error:
XML declaration not at beginning of document Specification:
http://www.w3.org/TR/REC-xml/ 1: 2: 3: <rss version="0.91">
4: <channel>
5: <title>Site News</title>

Though that the best option would be to suppress any output from the bootstrapping. Suppressed the output (of line-break) from the Bootstrap by embedding it between ob_start() and  ob_end_clean()
as

ob_start();
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
ob_end_clean();

Another issue was with the relative paths for including the includes like bootstrap.inc. The global variable DRUPAL_ROOT is available. But now that I have the script in another folder away from (actually inside
another folder inside) Drupal's root, had to overwrite the variable with the correct path.

<?php
$CURRENT_SOURCE_FOLDER
= dirname(__FILE__);
$CURRENT_SOURCE_FOLDER = str_replace('/rss', '', $CURRENT_SOURCE_FOLDER);
define('DRUPAL_ROOT', $CURRENT_SOURCE_FOLDER);
?>

And finally sending the xml generated through Drupal's decode_entities() seemed to handle the ampersands well without causing the ampersand to break.

$rss = decode_entities($rss);

Here goes some sample code..

<?php

/*
* Check of the username and password are provided
*/
if (isset($_REQUEST['username']) && isset($_REQUEST['password'])) {
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
}
else {
echo
"Error: Username/Password not provided";
exit();
}
/*
* Check any other parameters if needed in for your query
*/
if (isset($_REQUEST['fromdate']) && isset($_REQUEST['todate'])) {
$from_date = $_REQUEST['fromdate'];
$to_date = $_REQUEST['todate'];
}
else {
echo
"Error: fromdate/todate not provided";
exit();
}
/*
* Initiate Bootstrapping */
$CURRENT_SOURCE_FOLDER = dirname(__FILE__);
$CURRENT_SOURCE_FOLDER = str_replace('/api', '', $CURRENT_SOURCE_FOLDER);
define('DRUPAL_ROOT', $CURRENT_SOURCE_FOLDER);
// include bootstrap
include_once($CURRENT_SOURCE_FOLDER . '/includes/bootstrap.inc');
include_once(
$CURRENT_SOURCE_FOLDER . '/includes/password.inc');
include_once (
$CURRENT_SOURCE_FOLDER . '/api/krumo/class.krumo.php');
ob_start();
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
ob_end_clean();
/*
* Check if user has access to the feed generated here
* I have a Boolean field in the user profile to remember it..
*/
$user = user_load_by_name(urldecode($username));
if (!isset(
$user->field_rss_feed['und'])) {
echo
"Error: RSS Feed Access not explicitly specified. Contact Site Admin";
exit();
}
else {
if (!
$user->field_rss_feed['und'][0]['value']) {
echo
"Error: Specified Account does not have RSS feed access";
exit();
}
}
//krumo($user);
if (!user_check_password($password, $user)) {
echo
"Error: Password Incorrect";
exit();
}
$header = '<?xml version="1.0" encoding="UTF-8"?\> <rss version="0.91"><channel>
<title>Site News</title>
<description>My Site</description>
<language>en-us</language>'
;
/*
* Need to Retrieve Title, Node/ArticleId, publication date, short
abstract
* source, full abstract, carried by, location,sectors, products
* * We can use EntityFieldQuery,. but as we always show only 15 items,
* using node_load shows no harm
*/
$from_date_ts = strtotime($from_date);
$to_date_ts = strtotime($to_date);
$to_date_ts = $to_date_ts + 24 * 60 * 60;
//krumo($from_date_ts);
//krumo($to_date_ts);
$result = db_query('SELECT n.nid
FROM {node} n where n.type = :nodetype
and n.created >= :from
and n.created <= :to
and n.status = 1
order by n.created desc
limit 20
'
, array(
':from' => $from_date_ts,
':to' => $to_date_ts,
'nodetype' => 'xxx',
)
);
//krumo($result);
$rss = '';
foreach (
$result as $record) {
/*
* Build the RSS to $rss
*/
}
$footer = '</channel></rss>';
/*
* I have no reason why I have the below step
* Just that I hate any ampersand in xml,. they are weird
*/
$rss = str_replace('&amp;', '', $rss);
/*
* Decode ant other entities,. like &lt;', '&amp;', '"' etc
*/
$rss = decode_entities($rss);
$rss = $header . $rss . $footer;
header("Content-type: text/xml;");
print_r(trim($rss));
?>

Comments