extensions/SMWAskAPI/api/SMWAsk_API.php (190 lines of code) (raw):
<?php
/**
* Web-based API for executing semantic queries (#ask)
*
* @todo add some missing options to the semantic search (start, limit...)?
*
* @file
* @ingroup SMWAskAPI
* @author pierremz
*
*/
if (!defined('MEDIAWIKI')) {
exit(1);
}
require_once ( "$IP/includes/api/ApiBase.php" );
global $wgAPIModules;
/**
* If set, this variable control which name the action will be available under, in
* the API - default is 'ask'
*/
global $wgSMWAskAPI_ActionName;
if (!isset($wgSMWAskAPI_ActionName) || is_null($wgSMWAskAPI_ActionName)) {
$wgSMWAskAPI_ActionName = 'ask';
}
$wgAPIModules[$wgSMWAskAPI_ActionName] = 'SMWAskAPI';
/**
* Web-based API for executing semantic queries (#ask)
*
* @ingroup SMWWriter API
* @author denny
*/
class SMWAskAPI extends ApiBase {
// Parameters holding the querystring
private static $PARAM_QUERY = 'q';
// Parameters holding the printouts (ie. the semantic properties to display along with results)
private static $PARAM_PRINTOUTS = 'po';
// Parameters holding the query limit: max items to return (0 means default)
private static $PARAM_LIMIT = 'limit';
// Parameters holding the query offset: number of the first item to display
private static $PARAM_OFFSET = 'offset';
// Action name in the API
private $ACTION_NAME;
public function __construct($query, $moduleName) {
parent :: __construct($query, $moduleName);
global $wgSMWAskAPI_ActionName;
$this->ACTION_NAME = $wgSMWAskAPI_ActionName;
}
public function execute() {
global $wgUser;
$params = $this->extractRequestParams();
if (is_null($params[SMWAskAPI::$PARAM_QUERY]))
$this->dieUsage('Must specify a semantic query (' . SMWAskAPI::$PARAM_QUERY . ')', 0);
$query = $params[SMWAskAPI::$PARAM_QUERY];
if (!is_null($params[SMWAskAPI::$PARAM_PRINTOUTS])) {
$props = explode("|", $params[SMWAskAPI::$PARAM_PRINTOUTS]);
} else {
$props = array();
}
if( !isset($params[SMWAskAPI::$PARAM_LIMIT]) || is_null($limit = $params[SMWAskAPI::$PARAM_LIMIT]) || $limit == '' ){
$limit = null;// null = 'infinity'
}else if ( ($limit=intval($limit)) < 0 ){
$this->dieUsage('Invalid ' . SMWAskAPI::$PARAM_LIMIT . '='.$limit.': if set, must be a non-negative integer. 0 value means default', 0);
}
if( !isset($params[SMWAskAPI::$PARAM_OFFSET]) || is_null($offset = $params[SMWAskAPI::$PARAM_OFFSET]) ){
$offset = 0;// 0 is default value
}else if ( ($offset=intval($offset)) < 0 ){
$this->dieUsage('Invalid ' . SMWAskAPI::$PARAM_OFFSET . '='.$offset.': if set, must be a non-negative integer. Default value is 0', 0);
}
$res = $this->ask($query, $props, $limit, $offset);
$result = array();
$ask_errors = $res->getErrors();
if (empty($ask_errors)) {
$result['result'] = 'Success';
} else {
$result['result'] = 'Error';
$this->getResult()->setIndexedTagName($ask_errors, 'list-item');
$this->getResult()->addValue(array($this->ACTION_NAME), 'errors', $ask_errors);
}
$this->getResult()->addValue(array($this->ACTION_NAME, 'query'), SMWAskAPI::$PARAM_QUERY, $query);
if (count($props) > 0) {
$this->getResult()->setIndexedTagName($props, 'list-item');
$this->getResult()->addValue(array($this->ACTION_NAME, 'query'), SMWAskAPI::$PARAM_PRINTOUTS, $props);
}
if( ! is_null($limit) ){
$this->getResult()->addValue(array($this->ACTION_NAME, 'query'), SMWAskAPI::$PARAM_LIMIT, $limit);
}
if( $offset!=0 ){
$this->getResult()->addValue(array($this->ACTION_NAME, 'query'), SMWAskAPI::$PARAM_OFFSET, $offset);
}
if ( $res->getCount() > 0 || $res->hasFurtherResults() ) {
// We set the num of *displayed* results
$this->getResult()->addValue(array($this->ACTION_NAME, 'results'), 'count', $res->getCount());
// If more results can be found, say it
if( $res->hasFurtherResults() ){
$this->getResult()->addValue(array($this->ACTION_NAME, 'results'), 'hasMore', 'true');
}
$items = array();
while (( $r = $res->getNext() ) !== false) {
$items[] = $this->resultToItem($r);
}
$this->getResult()->setIndexedTagName($items, 'list-item');
$this->getResult()->addValue(array($this->ACTION_NAME, 'results'), 'items', $items);
}
$this->getResult()->addValue(null, $this->ACTION_NAME, $result);
}
/**
* Transform a results' row (ie. a result) into
* an item, which is a simple associative array
*
* @param <type> $r
* @return string
*/
private function resultToItem($r) {
// variables used to reconstruct URI from page title
global $wgServer, $wgScriptPath;
$rowsubject = false; // the wiki page value that this row is about
$item = array(); // contains Property-Value pairs to characterize an Item
$item['properties'] = array();
foreach ($r as $field) {
$pr = $field->getPrintRequest();
if ($rowsubject === false) {
$rowsubject = $field->getResultSubject();
$item['title'] = $rowsubject->getShortText(null, null);
}
if ($pr->getMode() != SMWPrintRequest::PRINT_THIS) {
$values = array();
while (( $value = $field->getNextObject() ) !== false) {
switch ($value->getTypeID()) {
case '_geo':
$values[] = $value->getWikiValue();
break;
case '_num':
$values[] = $value->getValueKey();
break;
case '_dat':
$values[] = $value->getYear() . "-" . str_pad($value->getMonth(), 2, '0', STR_PAD_LEFT) . "-" . str_pad($value->getDay(), 2, '0', STR_PAD_LEFT) . " " . $value->getTimeString();
break;
default:
$values[] = $value->getShortText(null, null);
}
}
$this->addPropToItem($item, str_replace(" ", "_", strtolower($pr->getLabel())), $values);
}
}
if ($rowsubject !== false) { // stuff in the page URI and some category data
$item['uri'] = $wgServer . $wgScriptPath . '/index.php?title=' . $rowsubject->getPrefixedText();
$page_cats = smwfGetStore()->getPropertyValues($rowsubject, SMWPropertyValue::makeProperty('_INST')); // TODO: set limit to 1 here
if (count($page_cats) > 0) {
$this->addPropToItem($item, 'type', array(reset($page_cats)->getShortHTMLText()));
}
}
return $item;
}
private function addPropToItem(&$item, $propname, $propvalues) {
switch (sizeof($propvalues)) {
case 0: return;
case 1:
if ($propvalues[0] != '') {
$item['properties'][$propname] = $propvalues[0]; //todo leave it as an array?
}
break;
default:
$this->getResult()->setIndexedTagName($propvalues, 'list-item');
$item['properties'][$propname] = $propvalues;
}
}
/**
* #ask itself.
* Delegate the treatments to SMWQueryProcessor
*
* @var SMWQueryProcessor
* @param <type> $m_querystring
* @param <type> $props
* @return <type>
*/
private function ask($m_querystring, $props = array(), $limit=null, $offset=0) {
$rawparams = array();
if ($m_querystring != '') {
$rawparams[] = $m_querystring;
}
foreach ($props as $prop) {
$prop = trim($prop);
if (( $prop != '' ) && ( $prop != '?' )) {
if ($prop { 0 } != '?') {
$prop = '?' . $prop;
}
$rawparams[] = $prop;
}
}
$m_params = array();
$m_printouts = array();
SMWQueryProcessor::processFunctionParams($rawparams, $m_querystring, $m_params, $m_printouts);
$m_params['offset'] = $offset;
if( ! is_null($limit) ){
$m_params['limit'] = $limit;
}
$queryobj = SMWQueryProcessor::createQuery($m_querystring, $m_params, SMWQueryProcessor::SPECIAL_PAGE, null, $m_printouts);
return smwfGetStore()->getQueryResult($queryobj);
}
public function mustBePosted() {
return false;
}
public function isWriteMode() {
return false;
}
protected function getAllowedParams() {
return array(
SMWAskAPI::$PARAM_QUERY => null,
SMWAskAPI::$PARAM_PRINTOUTS => null,
SMWAskAPI::$PARAM_LIMIT => null,
SMWAskAPI::$PARAM_OFFSET => null
);
}
protected function getParamDescription() {
return array(
SMWAskAPI::$PARAM_QUERY => 'Semantic query',
SMWAskAPI::$PARAM_PRINTOUTS => 'Printouts : pipe-separate list of semantic properties to display with results (optional)',
SMWAskAPI::$PARAM_LIMIT => 'Max number of items to return (optional - 0 is an accepted value ; leave empty to get all results)',
SMWAskAPI::$PARAM_OFFSET => 'Number of items to skip when displaying results (optional - default is 0)'
);
}
protected function getDescription() {
return 'Execute a semantic query (#ask)';
}
protected function getExamples() {
return array(
'api.php?action=' . $this->ACTION_NAME . '&' . SMWAskAPI::$PARAM_QUERY . '=[[Special_need::fraises]]&' . SMWAskAPI::$PARAM_PRINTOUTS . '=Special_need|dummyproperty',
'api.php?action=' . $this->ACTION_NAME . '&' . SMWAskAPI::$PARAM_QUERY . '=[[Special_need::fraises]]&' . SMWAskAPI::$PARAM_PRINTOUTS . '=Special_need|dummyproperty&' . SMWAskAPI::$PARAM_LIMIT . '=20&' . SMWAskAPI::$PARAM_OFFSET . '=2',
);
}
public function getVersion() {
return __CLASS__ . '$' . SMWASKAPI_VERSION; //TODO what is the norm here?
}
}