Index: trunk/extensions/SemanticCompoundQueries/SCQ_QueryResult.php |
— | — | @@ -0,0 +1,35 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +if (!defined('MEDIAWIKI')) die(); |
| 5 | + |
| 6 | +/** |
| 7 | + * Subclass of SMWQueryResult - this class was mostly created in order to |
| 8 | + * get around an inconvenient print-request-compatibility check in |
| 9 | + * SMWQueryResult::addRow() |
| 10 | + * |
| 11 | + * @ingroup SemanticCompoundQueries |
| 12 | + * @author Yaron Koren |
| 13 | + */ |
| 14 | +class SCQQueryResult extends SMWQueryResult { |
| 15 | + |
| 16 | + function addResult($new_result) { |
| 17 | + // create an array of the pages already in this query result, |
| 18 | + // so that we can check against it to make sure that pages |
| 19 | + // aren't included twice |
| 20 | + $existing_page_names = array(); |
| 21 | + while ($row = $this->getNext()) { |
| 22 | + if ($row[0] instanceof SMWResultArray) { |
| 23 | + $content = $row[0]->getContent(); |
| 24 | + $existing_page_names[] = $content[0]->getLongText(SMW_OUTPUT_WIKI); |
| 25 | + } |
| 26 | + } |
| 27 | + while (($row = $new_result->getNext()) !== false) { |
| 28 | + $row[0]->display_options = $new_result->display_options; |
| 29 | + $content = $row[0]->getContent(); |
| 30 | + $page_name = $content[0]->getLongText(SMW_OUTPUT_WIKI); |
| 31 | + if (! in_array($page_name, $existing_page_names)) |
| 32 | + $this->m_content[] = $row; |
| 33 | + } |
| 34 | + reset($this->m_content); |
| 35 | + } |
| 36 | +} |
Index: trunk/extensions/SemanticCompoundQueries/SCQ_QueryProcessor.php |
— | — | @@ -0,0 +1,159 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +if (!defined('MEDIAWIKI')) die(); |
| 5 | + |
| 6 | +/** |
| 7 | + * Class that holds static functions for handling compound queries. |
| 8 | + * This class is heavily based on Semantic MediaWiki's SMWQueryProcessor, |
| 9 | + * and calls that class's functions when possible. |
| 10 | + * |
| 11 | + * @ingroup SemanticCompoundQueries |
| 12 | + * @author Yaron Koren |
| 13 | + */ |
| 14 | +class SCQQueryProcessor { |
| 15 | + |
| 16 | + public static function registerParserFunctions(&$parser) { |
| 17 | + $parser->setFunctionHook( 'compound_query', array('SCQQueryProcessor','doCompoundQuery') ); |
| 18 | + return true; // always return true, in order not to stop MW's hook processing! |
| 19 | + } |
| 20 | + |
| 21 | + /** |
| 22 | + * An alternative to explode() - that function won't work here, |
| 23 | + * because we don't want to split the string on all semicolons, just |
| 24 | + * the ones that aren't contained within square brackets |
| 25 | + */ |
| 26 | + public static function getSubParams($param) { |
| 27 | + $sub_params = array(); |
| 28 | + $sub_param = ""; |
| 29 | + $uncompleted_square_brackets = 0; |
| 30 | + for ($i = 0; $i <= strlen($param); $i++) { |
| 31 | + $c = $param[$i]; |
| 32 | + if (($c == ';') && ($uncompleted_square_brackets <= 0)) { |
| 33 | + $sub_params[] = $sub_param; |
| 34 | + $sub_param = ""; |
| 35 | + } else { |
| 36 | + $sub_param .= $c; |
| 37 | + if ($c == '[') |
| 38 | + $uncompleted_square_brackets++; |
| 39 | + elseif ($c == ']') |
| 40 | + $uncompleted_square_brackets--; |
| 41 | + } |
| 42 | + } |
| 43 | + $sub_params[] = $sub_param; |
| 44 | + return $sub_params; |
| 45 | + } |
| 46 | + |
| 47 | + /** |
| 48 | + */ |
| 49 | + public static function doCompoundQuery(&$parser) { |
| 50 | + global $smwgQEnabled, $smwgIQRunningNumber; |
| 51 | + if ($smwgQEnabled) { |
| 52 | + $smwgIQRunningNumber++; |
| 53 | + $params = func_get_args(); |
| 54 | + array_shift( $params ); // we already know the $parser ... |
| 55 | + $other_params = array(); |
| 56 | + $query_result = null; |
| 57 | + foreach ($params as $param) { |
| 58 | + // very primitive heuristic - if the parameter |
| 59 | + // includes a square bracket, then it's a |
| 60 | + // sub-query; otherwise it's a regular parameter |
| 61 | + if (strpos($param, '[') !== false) { |
| 62 | + $sub_params = SCQQueryProcessor::getSubParams($param); |
| 63 | + $next_result = SCQQueryProcessor::getQueryResultFromFunctionParams($sub_params, SMW_OUTPUT_WIKI); |
| 64 | + if ($query_result == null) |
| 65 | + $query_result = new SCQQueryResult($next_result->getPrintRequests(), new SMWQuery()); |
| 66 | + $query_result->addResult($next_result); |
| 67 | + } else { |
| 68 | + $parts = explode('=', $param, 2); |
| 69 | + if (count($parts) >= 2) { |
| 70 | + $other_params[strtolower(trim($parts[0]))] = $parts[1]; // don't trim here, some params care for " " |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + $result = SCQQueryProcessor::getResultFromQueryResult($query_result, $other_params, null, SMW_OUTPUT_WIKI); |
| 75 | + } else { |
| 76 | + wfLoadExtensionMessages('SemanticMediaWiki'); |
| 77 | + $result = smwfEncodeMessages(array(wfMsgForContent('smw_iq_disabled'))); |
| 78 | + } |
| 79 | + return $result; |
| 80 | + } |
| 81 | + |
| 82 | + static function getQueryResultFromFunctionParams($rawparams, $outputmode, $context = SMWQueryProcessor::INLINE_QUERY, $showmode = false) { |
| 83 | + SMWQueryProcessor::processFunctionParams($rawparams,$querystring,$params,$printouts,$showmode); |
| 84 | + return SCQQueryProcessor::getQueryResultFromQueryString($querystring,$params,$printouts, SMW_OUTPUT_WIKI, $context); |
| 85 | + } |
| 86 | + |
| 87 | + /** |
| 88 | + * Combine the values from two SMWQueryResult objects into one |
| 89 | + */ |
| 90 | + static function mergeSMWQueryResults($result1, $result2) { |
| 91 | + if ($result1 == null) { |
| 92 | + $result1 = new SMWQueryResult($result2->getPrintRequests(), new SMWQuery()); |
| 93 | + } |
| 94 | + $existing_page_names = array(); |
| 95 | + while ($row = $result1->getNext()) { |
| 96 | + if ($row[0] instanceof SMWResultArray) { |
| 97 | + $content = $row[0]->getContent(); |
| 98 | + $existing_page_names[] = $content[0]->getLongText(SMW_OUTPUT_WIKI); |
| 99 | + } |
| 100 | + } |
| 101 | + while (($row = $result2->getNext()) !== false) { |
| 102 | + $row[0]->display_options = $result2->display_options; |
| 103 | + $content = $row[0]->getContent(); |
| 104 | + $page_name = $content[0]->getLongText(SMW_OUTPUT_WIKI); |
| 105 | + if (! in_array($page_name, $existing_page_names)) |
| 106 | + $result1->addRow($row); |
| 107 | + } |
| 108 | + return $result1; |
| 109 | + } |
| 110 | + |
| 111 | + // this method is an exact copy of SMWQueryProcessor's function, |
| 112 | + // but it needs to be duplicated because there it's protected |
| 113 | + static function getResultFormat($params) { |
| 114 | + $format = 'auto'; |
| 115 | + if (array_key_exists('format', $params)) { |
| 116 | + $format = strtolower(trim($params['format'])); |
| 117 | + global $smwgResultFormats; |
| 118 | + if ( !array_key_exists($format, $smwgResultFormats) ) { |
| 119 | + $format = 'auto'; // If it is an unknown format, defaults to list/table again |
| 120 | + } |
| 121 | + } |
| 122 | + return $format; |
| 123 | + } |
| 124 | + |
| 125 | + static function getQueryResultFromQueryString($querystring, $params, $extraprintouts, $outputmode, $context = SMWQueryProcessor::INLINE_QUERY) { |
| 126 | + wfProfileIn('SCQQueryProcessor::getQueryResultFromQueryString'); |
| 127 | + $query = SMWQueryProcessor::createQuery($querystring, $params, $context, null, $extraprintouts); |
| 128 | + $query_result = smwfGetStore()->getQueryResult($query); |
| 129 | + $query_result->display_options = array(); |
| 130 | + foreach ($params as $key => $value) { |
| 131 | + // special handling for 'icon' field, since it requires |
| 132 | + // conversion of a name to a URL |
| 133 | + if ($key == 'icon') { |
| 134 | + $icon_title = Title::newFromText($value); |
| 135 | + $icon_image_page = new ImagePage($icon_title); |
| 136 | + $icon_url = $icon_image_page->getDisplayedFile()->getURL(); |
| 137 | + $query_result->display_options['icon'] = $icon_url; |
| 138 | + } else { |
| 139 | + $query_result->display_options[$key] = $value; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + wfProfileOut('SCQQueryProcessor::getQueryResultFromQueryString'); |
| 144 | + return $query_result; |
| 145 | + } |
| 146 | + |
| 147 | + /* |
| 148 | + * Matches getResultFromQueryResult() from SMWQueryProcessor, |
| 149 | + * except that formats of type 'debug' and 'count' aren't handled |
| 150 | + */ |
| 151 | + static function getResultFromQueryResult($res, $params, $extraprintouts, $outputmode, $context = SMWQueryProcessor::INLINE_QUERY, $format = '') { |
| 152 | + wfProfileIn('SCQQueryProcessor::getResultFromQueryResult'); |
| 153 | + $format = SCQQueryProcessor::getResultFormat($params); |
| 154 | + $printer = SMWQueryProcessor::getResultPrinter($format, $context, $res); |
| 155 | + $result = $printer->getResult($res, $params, $outputmode); |
| 156 | + wfProfileOut('SCQQueryProcessor::getResultFromQueryResult'); |
| 157 | + return $result; |
| 158 | + } |
| 159 | +} |
| 160 | + |
Index: trunk/extensions/SemanticCompoundQueries/SCQ_Settings.php |
— | — | @@ -0,0 +1,52 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Initialization file for SemanticCompoundQueries |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup SemanticCompoundQueries |
| 8 | + * @author Yaron Koren |
| 9 | + */ |
| 10 | + |
| 11 | +if (!defined('MEDIAWIKI')) die(); |
| 12 | + |
| 13 | +define('SCQ_VERSION', '0.2'); |
| 14 | + |
| 15 | +$wgExtensionCredits['parserhook'][]= array( |
| 16 | + 'name' => 'Semantic Compound Queries', |
| 17 | + 'version' => SCQ_VERSION, |
| 18 | + 'author' => 'Yaron Koren', |
| 19 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:Semantic_Compound_Queries', |
| 20 | + 'description' => 'A parser function that displays multiple semantic queries at the same time', |
| 21 | +); |
| 22 | + |
| 23 | +$wgExtensionFunctions[] = 'scqgParserFunctions'; |
| 24 | +$wgHooks['LanguageGetMagic'][] = 'scqgLanguageGetMagic'; |
| 25 | + |
| 26 | +$scqIP = $IP . '/extensions/SemanticCompoundQueries'; |
| 27 | +$wgAutoloadClasses['SCQQueryProcessor'] = $scqIP . '/SCQ_QueryProcessor.php'; |
| 28 | +$wgAutoloadClasses['SCQQueryResult'] = $scqIP . '/SCQ_QueryResult.php'; |
| 29 | + |
| 30 | +function scqgParserFunctions () { |
| 31 | + global $wgHooks, $wgParser; |
| 32 | + if( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) { |
| 33 | + $wgHooks['ParserFirstCallInit'][] = 'scqgRegisterParser'; |
| 34 | + } else { |
| 35 | + if ( class_exists( 'StubObject' ) && !StubObject::isRealObject( $wgParser ) ) { |
| 36 | + $wgParser->_unstub(); |
| 37 | + } |
| 38 | + SCQQueryProcessor::registerParserFunctions( $wgParser ); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +function scqgRegisterParser(&$parser) { |
| 43 | + $parser->setFunctionHook( 'compound_query', array('SCQQueryProcessor','doCompoundQuery') ); |
| 44 | + return true; // always return true, in order not to stop MW's hook processing! |
| 45 | +} |
| 46 | + |
| 47 | +function scqgLanguageGetMagic( &$magicWords, $langCode = "en" ) { |
| 48 | + switch ( $langCode ) { |
| 49 | + default: |
| 50 | + $magicWords['compound_query'] = array ( 0, 'compound_query' ); |
| 51 | + } |
| 52 | + return true; |
| 53 | +} |
Index: trunk/extensions/SemanticCompoundQueries/README |
— | — | @@ -0,0 +1,48 @@ |
| 2 | +Semantic Compound Queries Extension |
| 3 | + |
| 4 | + Version 0.2 |
| 5 | + Yaron Koren |
| 6 | + |
| 7 | +This is free software licensed under the GNU General Public License. Please |
| 8 | +see http://www.gnu.org/copyleft/gpl.html for further details, including the |
| 9 | +full text and terms of the license. |
| 10 | + |
| 11 | +== Overview == |
| 12 | + |
| 13 | +Semantic Compound Queries is an extension to MediaWiki that defines a |
| 14 | +parser function, '#compound_query', that displays the results of the |
| 15 | +equivalent of multiple Semantic MediaWiki #ask queries at the same time. |
| 16 | +The syntax of #compound_query resembles that of #ask, but with more than |
| 17 | +one query, and with the elements of each sub-query delimited by semicolons |
| 18 | +instead of pipes. Elements that are common across all sub-queries, like |
| 19 | +'format=' and 'width=' (for maps) should be placed after all sub-queries. |
| 20 | + |
| 21 | +A sample call to #compound query, which retrieves both biographies, along |
| 22 | +with their subject; and fiction books, along with their author; is: |
| 23 | + |
| 24 | +{{#compound_query:[[Category:Books]][[Has gentre::Biography]];?Covers subject=Subject |
| 25 | + |[[Category:Books]][[Has genre::Fiction]];?Has author=Author |
| 26 | + |format=list}} |
| 27 | + |
| 28 | + |
| 29 | +For more information, see the extension homepage at: |
| 30 | +http://www.mediawiki.org/wiki/Extension:Semantic_Compound_Queries |
| 31 | + |
| 32 | +== Requirements == |
| 33 | + |
| 34 | +This version of the Semantic Compound Queries extension requires MediaWiki 1.8 |
| 35 | +or higher and Semantic MediaWiki 1.2 or higher. |
| 36 | + |
| 37 | +== Installation == |
| 38 | + |
| 39 | +To install the extension, place the entire 'SemanticCompoundQueries' directory |
| 40 | +within your MediaWiki 'extensions' directory, then add the following |
| 41 | +line to your 'LocalSettings.php' file: |
| 42 | + |
| 43 | + require_once( "$IP/extensions/SemanticCompoundQueries/SCQ_Settings.php" ); |
| 44 | + |
| 45 | +== Contact == |
| 46 | + |
| 47 | +Comments, questions, suggestions and bug reports are welcome, and can |
| 48 | +be placed on the Talk page for the extension, or sent to Yaron at |
| 49 | +yaron57@gmail.com. |