Index: trunk/extensions/SemanticMediaWiki/specials/AskSpecial/SMW_SpecialQueryCreator.php |
— | — | @@ -2,10 +2,13 @@ |
3 | 3 | |
4 | 4 | /** |
5 | 5 | * This special page for Semantic MediaWiki implements a customisable form for |
6 | | - * executing queries outside of articles. Results are generated by |
7 | | - * SMW_SpecialAsk.php. This page is currently under development as part of |
8 | | - * the Google Summer of Code 2011 Program. |
| 6 | + * executing queries outside of articles. Results and format options are generated by |
| 7 | + * SMW_SpecialAsk.php. |
9 | 8 | * |
| 9 | + * Currently adapted from current contents of SMW_SpecialAsk.php |
| 10 | + * This page is currently under development as part of the Google Summer of |
| 11 | + * Code 2011 Program. |
| 12 | + * |
10 | 13 | * @file SMW_SpecialQueryCreator.php |
11 | 14 | * @ingroup SMWSpecialPage |
12 | 15 | * @ingroup SpecialPage |
— | — | @@ -14,11 +17,16 @@ |
15 | 18 | * @author Yaron Koren |
16 | 19 | * @author Sanyam Goyal |
17 | 20 | * @author Jeroen De Dauw |
18 | | - * @author Devayon Das |
| 21 | + * |
19 | 22 | * |
20 | 23 | */ |
21 | 24 | class SMWQueryCreatorPage extends SpecialPage { |
22 | 25 | |
| 26 | + protected $m_querystring = ''; |
| 27 | + protected $m_params = array(); |
| 28 | + protected $m_printouts = array(); |
| 29 | + protected $m_editquery = false; |
| 30 | + |
23 | 31 | /** |
24 | 32 | * Constructor. |
25 | 33 | */ |
— | — | @@ -34,9 +42,845 @@ |
35 | 43 | */ |
36 | 44 | public function execute( $p ) { |
37 | 45 | global $wgOut, $wgRequest, $smwgQEnabled; |
38 | | - $this->setHeaders(); |
39 | | - $wgOut->addWikiText( "Hi. This page isn't ready for viewing yet. May I suggest you view [[Special:Ask]]"); |
| 46 | + |
| 47 | + $this->setHeaders(); |
| 48 | + wfProfileIn( 'doSpecialAsk (SMW)' ); |
| 49 | + |
| 50 | + if ( !$smwgQEnabled ) { |
| 51 | + $wgOut->addHTML( '<br />' . wfMsg( 'smw_iq_disabled' ) ); |
| 52 | + } else { |
| 53 | + if ( $wgRequest->getCheck( 'showformatoptions' ) ) { |
| 54 | + // handle Ajax action |
| 55 | + $format = $wgRequest->getVal( 'showformatoptions' ); |
| 56 | + $params = $wgRequest->getArray( 'params' ); |
| 57 | + $wgOut->disable(); |
| 58 | + echo $this->showFormatOptions( $format, $params ); |
| 59 | + } else { |
| 60 | + $this->extractQueryParameters( $p ); |
| 61 | + $this->makeHTMLResult(); |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + SMWOutputs::commitToOutputPage( $wgOut ); // make sure locally collected output data is pushed to the output! |
| 66 | + wfProfileOut( 'doSpecialAsk (SMW)' ); |
40 | 67 | } |
41 | 68 | |
| 69 | + /** |
| 70 | + * Extracts Query and option parameters, especially q (the query), |
| 71 | + * offset and limit (number of results per-page), p (the result format and options), po (printouts) and |
| 72 | + * eq(show/hide the query editor). |
| 73 | + * |
| 74 | + * This code rather hacky since there are many ways to call that special page, the most involved of |
| 75 | + * which is the way that this page calls itself when data is submitted via the form (since the shape |
| 76 | + * of the parameters then is governed by the UI structure, as opposed to being governed by reason). |
| 77 | + * |
| 78 | + * @param string $p |
| 79 | + */ |
| 80 | + protected function extractQueryParameters( $p ) { |
| 81 | + global $wgRequest, $smwgQMaxInlineLimit; |
| 82 | + |
| 83 | + // First make all inputs into a simple parameter list that can again be parsed into components later. |
| 84 | + if ( $wgRequest->getCheck( 'q' ) ) { // called by own Special, ignore full param string in that case |
| 85 | + $query_val = $wgRequest->getVal( 'p' ); |
| 86 | + |
| 87 | + if ( !empty( $query_val ) ) |
| 88 | + // p is used for any additional parameters in certain links. |
| 89 | + $rawparams = SMWInfolink::decodeParameters( $query_val, false ); |
| 90 | + else { |
| 91 | + $query_values = $wgRequest->getArray( 'p' ); |
| 92 | + |
| 93 | + if ( is_array( $query_values ) ) { |
| 94 | + foreach ( $query_values as $key => $val ) { |
| 95 | + if ( empty( $val ) ) unset( $query_values[$key] ); |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + // p is used for any additional parameters in certain links. |
| 100 | + $rawparams = SMWInfolink::decodeParameters( $query_values, false ); |
| 101 | + } |
| 102 | + } else { // called from wiki, get all parameters |
| 103 | + $rawparams = SMWInfolink::decodeParameters( $p, true ); |
| 104 | + } |
| 105 | + |
| 106 | + // Check for q= query string, used whenever this special page calls itself (via submit or plain link): |
| 107 | + $this->m_querystring = $wgRequest->getText( 'q' ); |
| 108 | + if ( $this->m_querystring != '' ) { |
| 109 | + $rawparams[] = $this->m_querystring; |
| 110 | + } |
| 111 | + |
| 112 | + // Check for param strings in po (printouts), appears in some links and in submits: |
| 113 | + $paramstring = $wgRequest->getText( 'po' ); |
| 114 | + |
| 115 | + if ( $paramstring != '' ) { // parameters from HTML inputs |
| 116 | + $ps = explode( "\n", $paramstring ); // params separated by newlines here (compatible with text-input for printouts) |
| 117 | + |
| 118 | + foreach ( $ps as $param ) { // add initial ? if omitted (all params considered as printouts) |
| 119 | + $param = trim( $param ); |
| 120 | + |
| 121 | + if ( ( $param != '' ) && ( $param { 0 } != '?' ) ) { |
| 122 | + $param = '?' . $param; |
| 123 | + } |
| 124 | + |
| 125 | + $rawparams[] = $param; |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + // Now parse parameters and rebuilt the param strings for URLs. |
| 130 | + SMWQueryProcessor::processFunctionParams( $rawparams, $this->m_querystring, $this->m_params, $this->m_printouts ); |
| 131 | + |
| 132 | + // Try to complete undefined parameter values from dedicated URL params. |
| 133 | + if ( !array_key_exists( 'format', $this->m_params ) ) { |
| 134 | + $this->m_params['format'] = 'broadtable'; |
| 135 | + } |
| 136 | + |
| 137 | + if ( !array_key_exists( 'order', $this->m_params ) ) { |
| 138 | + $order_values = $wgRequest->getArray( 'order' ); |
| 139 | + |
| 140 | + if ( is_array( $order_values ) ) { |
| 141 | + $this->m_params['order'] = ''; |
| 142 | + |
| 143 | + foreach ( $order_values as $order_value ) { |
| 144 | + if ( $order_value == '' ) $order_value = 'ASC'; |
| 145 | + $this->m_params['order'] .= ( $this->m_params['order'] != '' ? ',' : '' ) . $order_value; |
| 146 | + } |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + $this->m_num_sort_values = 0; |
| 151 | + |
| 152 | + if ( !array_key_exists( 'sort', $this->m_params ) ) { |
| 153 | + $sort_values = $wgRequest->getArray( 'sort' ); |
| 154 | + if ( is_array( $sort_values ) ) { |
| 155 | + $this->m_params['sort'] = implode( ',', $sort_values ); |
| 156 | + $this->m_num_sort_values = count( $sort_values ); |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + // Find implicit ordering for RSS -- needed for downwards compatibility with SMW <=1.1 |
| 161 | + /* |
| 162 | + if ( ($this->m_params['format'] == 'rss') && ($this->m_params['sort'] == '') && ($sortcount==0)) { |
| 163 | + foreach ($this->m_printouts as $printout) { |
| 164 | + if ((strtolower($printout->getLabel()) == "date") && ($printout->getTypeID() == "_dat")) { |
| 165 | + $this->m_params['sort'] = $printout->getTitle()->getText(); |
| 166 | + $this->m_params['order'] = 'DESC'; |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + */ |
| 171 | + |
| 172 | + if ( !array_key_exists( 'offset', $this->m_params ) ) { |
| 173 | + $this->m_params['offset'] = $wgRequest->getVal( 'offset' ); |
| 174 | + if ( $this->m_params['offset'] == '' ) $this->m_params['offset'] = 0; |
| 175 | + } |
| 176 | + |
| 177 | + if ( !array_key_exists( 'limit', $this->m_params ) ) { |
| 178 | + $this->m_params['limit'] = $wgRequest->getVal( 'limit' ); |
| 179 | + |
| 180 | + if ( $this->m_params['limit'] == '' ) { |
| 181 | + $this->m_params['limit'] = ( $this->m_params['format'] == 'rss' ) ? 10 : 20; // Standard limit for RSS. |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + $this->m_params['limit'] = min( $this->m_params['limit'], $smwgQMaxInlineLimit ); |
| 186 | + |
| 187 | + $this->m_editquery = ( $wgRequest->getVal( 'eq' ) == 'no' ) || ( $this->m_querystring == '' ); |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * Creates and adds the JavaScript and JS needed for autocompletion to $wgOut. |
| 192 | + * |
| 193 | + * @bug This method asks the store for all used and unused property names, which can be very expensive. It does not even use a limit to restrict the number of results. Cheaper ways to get the labels need to be sought. What sense does it make to add unused properties to the options on autocomplete for a query interface? |
| 194 | + * @since 1.5.2 |
| 195 | + */ |
| 196 | + protected static function addAutocompletionJavascriptAndCSS() { |
| 197 | + global $wgOut, $smwgScriptPath, $smwgJQueryIncluded, $smwgJQueryUIIncluded; |
| 198 | + |
| 199 | + // Add CSS and JavaScript for jQuery and jQuery UI. |
| 200 | + $wgOut->addExtensionStyle( "$smwgScriptPath/skins/jquery-ui/base/jquery.ui.all.css" ); |
| 201 | + |
| 202 | + $scripts = array(); |
| 203 | + |
| 204 | + if ( !$smwgJQueryIncluded ) { |
| 205 | + $realFunction = array( 'OutputPage', 'includeJQuery' ); |
| 206 | + if ( is_callable( $realFunction ) ) { |
| 207 | + $wgOut->includeJQuery(); |
| 208 | + } else { |
| 209 | + $scripts[] = "$smwgScriptPath/libs/jquery-1.4.2.min.js"; |
| 210 | + } |
| 211 | + |
| 212 | + $smwgJQueryIncluded = true; |
| 213 | + } |
| 214 | + |
| 215 | + if ( !$smwgJQueryUIIncluded ) { |
| 216 | + $scripts[] = "$smwgScriptPath/libs/jquery-ui/jquery.ui.core.min.js"; |
| 217 | + $scripts[] = "$smwgScriptPath/libs/jquery-ui/jquery.ui.widget.min.js"; |
| 218 | + $scripts[] = "$smwgScriptPath/libs/jquery-ui/jquery.ui.position.min.js"; |
| 219 | + $scripts[] = "$smwgScriptPath/libs/jquery-ui/jquery.ui.autocomplete.min.js"; |
| 220 | + $smwgJQueryUIIncluded = true; |
| 221 | + } |
| 222 | + |
| 223 | + foreach ( $scripts as $js ) { |
| 224 | + $wgOut->addScriptFile( $js ); |
| 225 | + } |
| 226 | + |
| 227 | + /* collect property names for autocomplete */ |
| 228 | + $propertyNames[] = array(); |
| 229 | + $results = smwfGetStore()->getPropertiesSpecial(); |
| 230 | + |
| 231 | + foreach ( $results as $result ) { |
| 232 | + $propertyNames[] = $result[0]->getLabel(); |
| 233 | + } |
| 234 | + |
| 235 | + $results = smwfGetStore()->getUnusedPropertiesSpecial(); |
| 236 | + |
| 237 | + foreach ( $results as $result ) { |
| 238 | + $propertyNames[] = $result->getLabel(); |
| 239 | + } |
| 240 | + |
| 241 | + sort( $propertyNames ); |
| 242 | + |
| 243 | + $properties_po = "["; |
| 244 | + foreach ( $propertyNames as $i => $property ) { |
| 245 | + if ( $i > 0 ) { |
| 246 | + $properties_po .= ", "; |
| 247 | + } |
| 248 | + $properties_po .= "'?" . $property . "'"; |
| 249 | + } |
| 250 | + $properties_po .= "]"; |
| 251 | + |
| 252 | + $javascript_autocomplete_text = <<<END |
| 253 | +<script type="text/javascript"> |
| 254 | +function split(val) { |
| 255 | + return val.split('\\n'); |
42 | 256 | } |
| 257 | +function extractLast(term) { |
| 258 | + return split(term).pop(); |
| 259 | +} |
| 260 | +function escapeQuestion(term){ |
| 261 | + if (term.substring(0, 1) == "?") { |
| 262 | + return term.substring(1); |
| 263 | + } else { |
| 264 | + return term; |
| 265 | + } |
| 266 | +} |
43 | 267 | |
| 268 | +jQuery.noConflict(); |
| 269 | +/* extending jQuery functions for custom highligting */ |
| 270 | +jQuery.ui.autocomplete.prototype._renderItem = function( ul, item) { |
| 271 | + var term_without_q = escapeQuestion(extractLast(this.term)); |
| 272 | + var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term_without_q.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"); |
| 273 | + var loc = item.label.search(re); |
| 274 | + if (loc >= 0) { |
| 275 | + var t = item.label.substr(0, loc) + '<strong>' + item.label.substr(loc, term_without_q.length) + '</strong>' + item.label.substr(loc + term_without_q.length); |
| 276 | + } else { |
| 277 | + var t = item.label; |
| 278 | + } |
| 279 | + jQuery( "<li></li>" ) |
| 280 | + .data( "item.autocomplete", item ) |
| 281 | + .append( " <a>" + t + "</a>" ) |
| 282 | + .appendTo( ul ); |
| 283 | +}; |
| 284 | + |
| 285 | +///* extending jquery functions for custom autocomplete matching */ |
| 286 | +jQuery.extend( jQuery.ui.autocomplete, { |
| 287 | + filter: function(array, term) { |
| 288 | + var matcher = new RegExp("\\\b" + jQuery.ui.autocomplete.escapeRegex(term), "i" ); |
| 289 | + return jQuery.grep( array, function(value) { |
| 290 | + return matcher.test( value.label || value.value || value ); |
| 291 | + }); |
| 292 | + } |
| 293 | +}); |
| 294 | + |
| 295 | +jQuery(document).ready(function(){ |
| 296 | + jQuery("#add_property").autocomplete({ |
| 297 | + minLength: 1, |
| 298 | + source: function(request, response) { |
| 299 | + // delegate back to autocomplete, but extract the last term |
| 300 | + response(jQuery.ui.autocomplete.filter({$properties_po}, escapeQuestion(extractLast(request.term)))); |
| 301 | + }, |
| 302 | + focus: function() { |
| 303 | + // prevent value inserted on focus |
| 304 | + return false; |
| 305 | + }, |
| 306 | + select: function(event, ui) { |
| 307 | + var terms = split( this.value ); |
| 308 | + // remove the current input |
| 309 | + terms.pop(); |
| 310 | + // add the selected item |
| 311 | + terms.push( ui.item.value ); |
| 312 | + // add placeholder to get the comma-and-space at the end |
| 313 | + terms.push(""); |
| 314 | + this.value = terms.join("\\n"); |
| 315 | + return false; |
| 316 | + } |
| 317 | + }); |
| 318 | +}); |
| 319 | +</script> |
| 320 | + |
| 321 | +END; |
| 322 | + |
| 323 | + $wgOut->addScript( $javascript_autocomplete_text ); |
| 324 | + } |
| 325 | + |
| 326 | + /** |
| 327 | + * TODO: document |
| 328 | + */ |
| 329 | + protected function makeHTMLResult() { |
| 330 | + global $wgOut, $smwgAutocompleteInSpecialAsk; |
| 331 | + |
| 332 | + $delete_msg = wfMsg( 'delete' ); |
| 333 | + |
| 334 | + // Javascript code for the dynamic parts of the page |
| 335 | + $javascript_text = <<<END |
| 336 | +<script type="text/javascript"> |
| 337 | +function updateOtherOptions(strURL) { |
| 338 | + jQuery.ajax({ url: strURL, context: document.body, success: function(data){ |
| 339 | + jQuery("#other_options").html(data); |
| 340 | + }}); |
| 341 | +} |
| 342 | + |
| 343 | +// code for handling adding and removing the "sort" inputs |
| 344 | +var num_elements = {$this->m_num_sort_values}; |
| 345 | + |
| 346 | +function addInstance(starter_div_id, main_div_id) { |
| 347 | + var starter_div = document.getElementById(starter_div_id); |
| 348 | + var main_div = document.getElementById(main_div_id); |
| 349 | + |
| 350 | + //Create the new instance |
| 351 | + var new_div = starter_div.cloneNode(true); |
| 352 | + var div_id = 'sort_div_' + num_elements; |
| 353 | + new_div.className = 'multipleTemplate'; |
| 354 | + new_div.id = div_id; |
| 355 | + new_div.style.display = 'block'; |
| 356 | + |
| 357 | + var children = new_div.getElementsByTagName('*'); |
| 358 | + var x; |
| 359 | + for (x = 0; x < children.length; x++) { |
| 360 | + if (children[x].name) |
| 361 | + children[x].name = children[x].name.replace(/_num/, '[' + num_elements + ']'); |
| 362 | + } |
| 363 | + |
| 364 | + //Create 'delete' link |
| 365 | + var remove_button = document.createElement('span'); |
| 366 | + remove_button.innerHTML = '[<a href="javascript:removeInstance(\'sort_div_' + num_elements + '\')">{$delete_msg}</a>]'; |
| 367 | + new_div.appendChild(remove_button); |
| 368 | + |
| 369 | + //Add the new instance |
| 370 | + main_div.appendChild(new_div); |
| 371 | + num_elements++; |
| 372 | +} |
| 373 | + |
| 374 | +function removeInstance(div_id) { |
| 375 | + var olddiv = document.getElementById(div_id); |
| 376 | + var parent = olddiv.parentNode; |
| 377 | + parent.removeChild(olddiv); |
| 378 | +} |
| 379 | +</script> |
| 380 | + |
| 381 | +END; |
| 382 | + |
| 383 | + $wgOut->addScript( $javascript_text ); |
| 384 | + |
| 385 | + if ( $smwgAutocompleteInSpecialAsk ) { |
| 386 | + self::addAutocompletionJavascriptAndCSS(); |
| 387 | + } |
| 388 | + |
| 389 | + $result = ''; |
| 390 | + $result_mime = false; // output in MW Special page as usual |
| 391 | + |
| 392 | + // build parameter strings for URLs, based on current settings |
| 393 | + $urltail = '&q=' . urlencode( $this->m_querystring ); |
| 394 | + |
| 395 | + $tmp_parray = array(); |
| 396 | + foreach ( $this->m_params as $key => $value ) { |
| 397 | + if ( !in_array( $key, array( 'sort', 'order', 'limit', 'offset', 'title' ) ) ) { |
| 398 | + $tmp_parray[$key] = $value; |
| 399 | + } |
| 400 | + } |
| 401 | + |
| 402 | + $urltail .= '&p=' . urlencode( SMWInfolink::encodeParameters( $tmp_parray ) ); |
| 403 | + $printoutstring = ''; |
| 404 | + |
| 405 | + foreach ( $this->m_printouts as $printout ) { |
| 406 | + $printoutstring .= $printout->getSerialisation() . "\n"; |
| 407 | + } |
| 408 | + |
| 409 | + if ( $printoutstring != '' ) $urltail .= '&po=' . urlencode( $printoutstring ); |
| 410 | + if ( array_key_exists( 'sort', $this->m_params ) ) $urltail .= '&sort=' . $this->m_params['sort']; |
| 411 | + if ( array_key_exists( 'order', $this->m_params ) ) $urltail .= '&order=' . $this->m_params['order']; |
| 412 | + |
| 413 | + if ( $this->m_querystring != '' ) { |
| 414 | + $queryobj = SMWQueryProcessor::createQuery( $this->m_querystring, $this->m_params, SMWQueryProcessor::SPECIAL_PAGE , $this->m_params['format'], $this->m_printouts ); |
| 415 | + $res = smwfGetStore()->getQueryResult( $queryobj ); |
| 416 | + |
| 417 | + // Try to be smart for rss/ical if no description/title is given and we have a concept query: |
| 418 | + if ( $this->m_params['format'] == 'rss' ) { |
| 419 | + $desckey = 'rssdescription'; |
| 420 | + $titlekey = 'rsstitle'; |
| 421 | + } elseif ( $this->m_params['format'] == 'icalendar' ) { |
| 422 | + $desckey = 'icalendardescription'; |
| 423 | + $titlekey = 'icalendartitle'; |
| 424 | + } else { $desckey = false; } |
| 425 | + |
| 426 | + if ( ( $desckey ) && ( $queryobj->getDescription() instanceof SMWConceptDescription ) && |
| 427 | + ( !isset( $this->m_params[$desckey] ) || !isset( $this->m_params[$titlekey] ) ) ) { |
| 428 | + $concept = $queryobj->getDescription()->getConcept(); |
| 429 | + |
| 430 | + if ( !isset( $this->m_params[$titlekey] ) ) { |
| 431 | + $this->m_params[$titlekey] = $concept->getText(); |
| 432 | + } |
| 433 | + |
| 434 | + if ( !isset( $this->m_params[$desckey] ) ) { |
| 435 | + $dv = end( smwfGetStore()->getPropertyValues( SMWWikiPageValue::makePageFromTitle( $concept ), new SMWDIProperty( '_CONC' ) ) ); |
| 436 | + if ( $dv instanceof SMWConceptValue ) { |
| 437 | + $this->m_params[$desckey] = $dv->getDocu(); |
| 438 | + } |
| 439 | + } |
| 440 | + } |
| 441 | + |
| 442 | + $printer = SMWQueryProcessor::getResultPrinter( $this->m_params['format'], SMWQueryProcessor::SPECIAL_PAGE ); |
| 443 | + $result_mime = $printer->getMimeType( $res ); |
| 444 | + |
| 445 | + global $wgRequest; |
| 446 | + |
| 447 | + $hidequery = $wgRequest->getVal( 'eq' ) == 'no'; |
| 448 | + |
| 449 | + // if it's an export format (like CSV, JSON, etc.), |
| 450 | + // don't actually export the data if 'eq' is set to |
| 451 | + // either 'yes' or 'no' in the query string - just |
| 452 | + // show the link instead |
| 453 | + if ( $this->m_editquery || $hidequery ) $result_mime = false; |
| 454 | + |
| 455 | + if ( $result_mime == false ) { |
| 456 | + if ( $res->getCount() > 0 ) { |
| 457 | + if ( $this->m_editquery ) $urltail .= '&eq=yes'; |
| 458 | + if ( $hidequery ) $urltail .= '&eq=no'; |
| 459 | + |
| 460 | + $navigation = $this->getNavigationBar( $res, $urltail ); |
| 461 | + $result .= '<div style="text-align: center;">' . "\n" . $navigation . "\n</div>\n"; |
| 462 | + $query_result = $printer->getResult( $res, $this->m_params, SMW_OUTPUT_HTML ); |
| 463 | + |
| 464 | + if ( is_array( $query_result ) ) { |
| 465 | + $result .= $query_result[0]; |
| 466 | + } else { |
| 467 | + $result .= $query_result; |
| 468 | + } |
| 469 | + |
| 470 | + $result .= '<div style="text-align: center;">' . "\n" . $navigation . "\n</div>\n"; |
| 471 | + } else { |
| 472 | + $result = '<div style="text-align: center;">' . wfMsg( 'smw_result_noresults' ) . '</div>'; |
| 473 | + } |
| 474 | + } else { // make a stand-alone file |
| 475 | + $result = $printer->getResult( $res, $this->m_params, SMW_OUTPUT_FILE ); |
| 476 | + $result_name = $printer->getFileName( $res ); // only fetch that after initialising the parameters |
| 477 | + } |
| 478 | + } |
| 479 | + |
| 480 | + if ( $result_mime == false ) { |
| 481 | + if ( $this->m_querystring ) { |
| 482 | + $wgOut->setHTMLtitle( $this->m_querystring ); |
| 483 | + } else { |
| 484 | + $wgOut->setHTMLtitle( wfMsg( 'ask' ) ); |
| 485 | + } |
| 486 | + |
| 487 | + $result = $this->getInputForm( $printoutstring, 'offset=' . $this->m_params['offset'] . '&limit=' . $this->m_params['limit'] . $urltail ) . $result; |
| 488 | + $wgOut->addHTML( $result ); |
| 489 | + } else { |
| 490 | + $wgOut->disable(); |
| 491 | + |
| 492 | + header( "Content-type: $result_mime; charset=UTF-8" ); |
| 493 | + |
| 494 | + if ( $result_name !== false ) { |
| 495 | + header( "content-disposition: attachment; filename=$result_name" ); |
| 496 | + } |
| 497 | + |
| 498 | + echo $result; |
| 499 | + } |
| 500 | + } |
| 501 | + |
| 502 | + /** |
| 503 | + * Generates the search box UI |
| 504 | + * |
| 505 | + * @param string $printoutstring |
| 506 | + * @param string $urltail |
| 507 | + * |
| 508 | + * @return string |
| 509 | + */ |
| 510 | + protected function getInputForm( $urltail ) { |
| 511 | + global $smwgQSortingSupport, $smwgResultFormats; |
| 512 | + |
| 513 | + $result = ''; |
| 514 | + |
| 515 | + if ( $this->m_editquery ) { |
| 516 | + $spectitle = $this->getTitleFor( 'Ask' ); |
| 517 | + $result .= '<form name="ask" action="' . $spectitle->escapeLocalURL() . '" method="get">' . "\n" . |
| 518 | + '<input type="hidden" name="title" value="' . $spectitle->getPrefixedText() . '"/>'; |
| 519 | + |
| 520 | + $result .= wfMsg('smw_qc_query_help'); |
| 521 | + |
| 522 | + // Main query and printouts. |
| 523 | + $result .= '<p><strong>' . wfMsg( 'smw_ask_queryhead' ) . "</strong></p>\n"; |
| 524 | + $result .= '<p><textarea name="q" rows="6">' . htmlspecialchars( $this->m_querystring ) . '</textarea></p>'; |
| 525 | + //show|hide additional options and querying help |
| 526 | + $result .= '<span id="show_additional_options" style="display:inline"><a href="#addtional" rel="nofollow" onclick="' . |
| 527 | + "document.getElementById('additional_options').style.display='block';" . |
| 528 | + "document.getElementById('show_additional_options').style.display='none';" . |
| 529 | + "document.getElementById('hide_additional_options').style.display='inline';" . '">' . |
| 530 | + wfMsg( 'smw_show_addnal_opts' ) . '</a></span>'; |
| 531 | + $result .= '<span id="hide_additional_options" style="display:none"><a href="#" rel="nofollow" onclick="' . |
| 532 | + "document.getElementById('additional_options').style.display='none';" . |
| 533 | + "document.getElementById('hide_additional_options').style.display='none';" . |
| 534 | + "document.getElementById('show_additional_options').style.display='inline';" . '">' . |
| 535 | + wfMsg( 'smw_hide_addnal_opts' ) . '</a></span>'; |
| 536 | + $result .=' | <a href="' . htmlspecialchars( wfMsg( 'smw_ask_doculink' ) ) . '">' . wfMsg( 'smw_ask_help' ) . '</a>'; |
| 537 | + //additional options |
| 538 | + $result .= '<div id="additional_options" style="display:none">'; |
| 539 | + $result .= '<p><strong>' . wfMsg( 'smw_ask_printhead' ) . "</strong></p>\n" . |
| 540 | + '<span style="font-weight: normal;">' . wfMsg( 'smw_ask_printdesc' ) . '</span>' . "\n" . |
| 541 | + '<p><textarea id = "add_property" name="po" cols="20" rows="6"></textarea></p>' . "\n"; |
| 542 | +// @TODO |
| 543 | + // sorting inputs |
| 544 | + if ( $smwgQSortingSupport ) { |
| 545 | + if ( ! array_key_exists( 'sort', $this->m_params ) || ! array_key_exists( 'order', $this->m_params ) ) { |
| 546 | + $orders = array(); // do not even show one sort input here |
| 547 | + } else { |
| 548 | + $sorts = explode( ',', $this->m_params['sort'] ); |
| 549 | + $orders = explode( ',', $this->m_params['order'] ); |
| 550 | + reset( $sorts ); |
| 551 | + } |
| 552 | + |
| 553 | + foreach ( $orders as $i => $order ) { |
| 554 | + $result .= "<div id=\"sort_div_$i\">" . wfMsg( 'smw_ask_sortby' ) . ' <input type="text" name="sort[' . $i . ']" value="' . |
| 555 | + htmlspecialchars( $sorts[$i] ) . "\" size=\"35\"/>\n" . '<select name="order[' . $i . ']"><option '; |
| 556 | + |
| 557 | + if ( $order == 'ASC' ) $result .= 'selected="selected" '; |
| 558 | + $result .= 'value="ASC">' . wfMsg( 'smw_ask_ascorder' ) . '</option><option '; |
| 559 | + if ( $order == 'DESC' ) $result .= 'selected="selected" '; |
| 560 | + |
| 561 | + $result .= 'value="DESC">' . wfMsg( 'smw_ask_descorder' ) . "</option></select>\n"; |
| 562 | + $result .= '[<a href="javascript:removeInstance(\'sort_div_' . $i . '\')">' . wfMsg( 'delete' ) . '</a>]' . "\n"; |
| 563 | + $result .= "</div>\n"; |
| 564 | + } |
| 565 | + |
| 566 | + $result .= '<div id="sorting_starter" style="display: none">' . wfMsg( 'smw_ask_sortby' ) . ' <input type="text" name="sort_num" size="35" />' . "\n"; |
| 567 | + $result .= ' <select name="order_num">' . "\n"; |
| 568 | + $result .= ' <option value="ASC">' . wfMsg( 'smw_ask_ascorder' ) . "</option>\n"; |
| 569 | + $result .= ' <option value="DESC">' . wfMsg( 'smw_ask_descorder' ) . "</option>\n</select>\n"; |
| 570 | + $result .= "</div>\n"; |
| 571 | + $result .= '<div id="sorting_main"></div>' . "\n"; |
| 572 | + $result .= '<a href="javascript:addInstance(\'sorting_starter\', \'sorting_main\')">' . wfMsg( 'smw_add_sortcondition' ) . '</a>' . "\n"; |
| 573 | + } |
| 574 | + |
| 575 | + $printer = SMWQueryProcessor::getResultPrinter( 'broadtable', SMWQueryProcessor::SPECIAL_PAGE ); |
| 576 | + $url = SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( "showformatoptions=' + this.value + '" ); |
| 577 | + |
| 578 | + foreach ( $this->m_params as $param => $value ) { |
| 579 | + if ( $param !== 'format' ) { |
| 580 | + $url .= '¶ms[' . Xml::escapeJsString( $param ) . ']=' . Xml::escapeJsString( $value ); |
| 581 | + } |
| 582 | + } |
| 583 | + |
| 584 | + $result .= "<br /><br />\n<p>" . wfMsg( 'smw_ask_format_as' ) . ' <input type="hidden" name="eq" value="yes"/>' . "\n" . |
| 585 | + '<select id="formatSelector" name="p[format]" onChange="JavaScript:updateOtherOptions(\'' . $url . '\')">' . "\n" . |
| 586 | + ' <option value="broadtable"' . ( $this->m_params['format'] == 'broadtable' ? ' selected' : '' ) . '>' . |
| 587 | + $printer->getName() . ' (' . wfMsg( 'smw_ask_defaultformat' ) . ')</option>' . "\n"; |
| 588 | + |
| 589 | + $formats = array(); |
| 590 | + |
| 591 | + foreach ( array_keys( $smwgResultFormats ) as $format ) { |
| 592 | + // Special formats "count" and "debug" currently not supported. |
| 593 | + if ( $format != 'broadtable' && $format != 'count' && $format != 'debug' ) { |
| 594 | + $printer = SMWQueryProcessor::getResultPrinter( $format, SMWQueryProcessor::SPECIAL_PAGE ); |
| 595 | + $formats[$format] = $printer->getName(); |
| 596 | + } |
| 597 | + } |
| 598 | + |
| 599 | + natcasesort( $formats ); |
| 600 | + |
| 601 | + foreach ( $formats as $format => $name ) { |
| 602 | + $result .= ' <option value="' . $format . '"' . ( $this->m_params['format'] == $format ? ' selected' : '' ) . '>' . $name . "</option>\n"; |
| 603 | + } |
| 604 | + |
| 605 | + $result .= "</select></p>\n"; |
| 606 | + $result .= '<fieldset><legend>' . wfMsg( 'smw_ask_otheroptions' ) . "</legend>\n"; |
| 607 | + $result .= "<div id=\"other_options\">" . $this->showFormatOptions( $this->m_params['format'], $this->m_params ) . "</div>"; |
| 608 | + $result .= "</fieldset>\n"; |
| 609 | + $urltail = str_replace( '&eq=yes', '', $urltail ) . '&eq=no'; |
| 610 | + $result .= '</div>'; |
| 611 | + |
| 612 | + $result .= '<br /><input type="submit" value="' . wfMsg( 'smw_ask_submit' ) . '"/>' . |
| 613 | + '<input type="hidden" name="eq" value="no"/>' . |
| 614 | + "\n</form>"; |
| 615 | + } else { // if $this->m_editquery == false |
| 616 | + $urltail = str_replace( '&eq=no', '', $urltail ) . '&eq=yes'; |
| 617 | + $result .= '<p>' . |
| 618 | + Html::element( |
| 619 | + 'a', |
| 620 | + array( |
| 621 | + 'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( $urltail ), |
| 622 | + 'rel' => 'nofollow' |
| 623 | + ), |
| 624 | + wfMsg( 'smw_ask_editquery' ) |
| 625 | + ) . |
| 626 | + '| ' . SMWAskPage::getEmbedToggle() . |
| 627 | + '</p>'; |
| 628 | + } |
| 629 | + |
| 630 | + return $result; |
| 631 | + } |
| 632 | + |
| 633 | + /** |
| 634 | + * TODO: document |
| 635 | + * |
| 636 | + * @return string |
| 637 | + */ |
| 638 | + protected static function getEmbedToggle() { |
| 639 | + return '<span id="embed_show"><a href="#" rel="nofollow" onclick="' . |
| 640 | + "document.getElementById('inlinequeryembed').style.display='block';" . |
| 641 | + "document.getElementById('embed_hide').style.display='inline';" . |
| 642 | + "document.getElementById('embed_show').style.display='none';" . |
| 643 | + "document.getElementById('inlinequeryembedarea').select();" . |
| 644 | + '">' . wfMsg( 'smw_ask_show_embed' ) . '</a></span>' . |
| 645 | + '<span id="embed_hide" style="display: none"><a href="#" rel="nofollow" onclick="' . |
| 646 | + "document.getElementById('inlinequeryembed').style.display='none';" . |
| 647 | + "document.getElementById('embed_show').style.display='inline';" . |
| 648 | + "document.getElementById('embed_hide').style.display='none';" . |
| 649 | + '">' . wfMsg( 'smw_ask_hide_embed' ) . '</a></span>'; |
| 650 | + } |
| 651 | + |
| 652 | + /** |
| 653 | + * Build the navigation for some given query result, reuse url-tail parameters. |
| 654 | + * |
| 655 | + * @param SMWQueryResult $res |
| 656 | + * @param string $urltail |
| 657 | + * |
| 658 | + * @return string |
| 659 | + */ |
| 660 | + protected function getNavigationBar( SMWQueryResult $res, $urltail ) { |
| 661 | + global $smwgQMaxInlineLimit; |
| 662 | + |
| 663 | + $offset = $this->m_params['offset']; |
| 664 | + $limit = $this->m_params['limit']; |
| 665 | + |
| 666 | + // Prepare navigation bar. |
| 667 | + if ( $offset > 0 ) { |
| 668 | + $navigation = Html::element( |
| 669 | + 'a', |
| 670 | + array( |
| 671 | + 'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( |
| 672 | + 'offset=' . max( 0, $offset - $limit ) . |
| 673 | + '&limit=' . $limit . $urltail |
| 674 | + ), |
| 675 | + 'rel' => 'nofollow' |
| 676 | + ), |
| 677 | + wfMsg( 'smw_result_prev' ) |
| 678 | + ); |
| 679 | + |
| 680 | + } else { |
| 681 | + $navigation = wfMsg( 'smw_result_prev' ); |
| 682 | + } |
| 683 | + |
| 684 | + $navigation .= |
| 685 | + '     <b>' . |
| 686 | + wfMsg( 'smw_result_results' ) . ' ' . ( $offset + 1 ) . |
| 687 | + '– ' . |
| 688 | + ( $offset + $res->getCount() ) . |
| 689 | + '</b>    '; |
| 690 | + |
| 691 | + if ( $res->hasFurtherResults() ) { |
| 692 | + $navigation .= Html::element( |
| 693 | + 'a', |
| 694 | + array( |
| 695 | + 'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( |
| 696 | + 'offset=' . ( $offset + $limit ) . |
| 697 | + '&limit=' . $limit . $urltail |
| 698 | + ), |
| 699 | + 'rel' => 'nofollow' |
| 700 | + ), |
| 701 | + wfMsg( 'smw_result_next' ) |
| 702 | + ); |
| 703 | + } else { |
| 704 | + $navigation .= wfMsg( 'smw_result_next' ); |
| 705 | + } |
| 706 | + |
| 707 | + $first = true; |
| 708 | + |
| 709 | + foreach ( array( 20, 50, 100, 250, 500 ) as $l ) { |
| 710 | + if ( $l > $smwgQMaxInlineLimit ) break; |
| 711 | + |
| 712 | + if ( $first ) { |
| 713 | + $navigation .= '        ('; |
| 714 | + $first = false; |
| 715 | + } else { |
| 716 | + $navigation .= ' | '; |
| 717 | + } |
| 718 | + |
| 719 | + if ( $limit != $l ) { |
| 720 | + $navigation .= Html::element( |
| 721 | + 'a', |
| 722 | + array( |
| 723 | + 'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( |
| 724 | + 'offset=' . $offset . |
| 725 | + '&limit=' . $l . $urltail |
| 726 | + ), |
| 727 | + 'rel' => 'nofollow' |
| 728 | + ), |
| 729 | + $l |
| 730 | + ); |
| 731 | + } else { |
| 732 | + $navigation .= '<b>' . $l . '</b>'; |
| 733 | + } |
| 734 | + } |
| 735 | + |
| 736 | + $navigation .= ')'; |
| 737 | + |
| 738 | + return $navigation; |
| 739 | + } |
| 740 | + |
| 741 | + /** |
| 742 | + * Display a form section showing the options for a given format, |
| 743 | + * based on the getParameters() value for that format's query printer. |
| 744 | + * |
| 745 | + * @param string $format |
| 746 | + * @param array $paramValues The current values for the parameters (name => value) |
| 747 | + * |
| 748 | + * @return string |
| 749 | + */ |
| 750 | + protected function showFormatOptions( $format, array $paramValues ) { |
| 751 | + $text = ''; |
| 752 | + |
| 753 | + $printer = SMWQueryProcessor::getResultPrinter( $format, SMWQueryProcessor::SPECIAL_PAGE ); |
| 754 | + |
| 755 | + $params = method_exists( $printer, 'getParameters' ) ? $printer->getParameters() : array(); |
| 756 | + |
| 757 | + // Ignore the format parameter, as we got a special control in the GUI for it already. |
| 758 | + unset( $params['format'] ); |
| 759 | + |
| 760 | + $optionsHtml = array(); |
| 761 | + |
| 762 | + foreach ( $params as $param ) { |
| 763 | + $param = $this->toValidatorParam( $param ); |
| 764 | + $currentValue = array_key_exists( $param->getName(), $paramValues ) ? $paramValues[$param->getName()] : false; |
| 765 | + |
| 766 | + $optionsHtml[] = |
| 767 | + Html::rawElement( |
| 768 | + 'div', |
| 769 | + array( |
| 770 | + 'style' => 'width: 30%; padding: 5px; float: left;' |
| 771 | + ), |
| 772 | + htmlspecialchars( $param->getName() ) . ': ' . |
| 773 | + $this->showFormatOption( $param, $currentValue ) . |
| 774 | + '<br />' . |
| 775 | + Html::element( 'em', array(), $param->getDescription() ) |
| 776 | + ); |
| 777 | + } |
| 778 | + |
| 779 | + for ( $i = 0, $n = count( $optionsHtml ); $i < $n; $i++ ) { |
| 780 | + if ( $i % 3 == 2 || $i == $n - 1 ) { |
| 781 | + $optionsHtml[$i] .= "<div style=\"clear: both\";></div>\n"; |
| 782 | + } |
| 783 | + } |
| 784 | + |
| 785 | + $i = 0; |
| 786 | + $rowHtml = ''; |
| 787 | + $resultHtml = ''; |
| 788 | + |
| 789 | + while ( $option = array_shift( $optionsHtml ) ) { |
| 790 | + $rowHtml .= $option; |
| 791 | + $i++; |
| 792 | + |
| 793 | + if ( $i % 3 == 0 ) { |
| 794 | + $resultHtml .= Html::rawElement( |
| 795 | + 'div', |
| 796 | + array( |
| 797 | + 'style' => 'background: ' . ( $i % 6 == 0 ? 'white' : '#dddddd' ) . ';' |
| 798 | + ), |
| 799 | + $rowHtml |
| 800 | + ); |
| 801 | + $rowHtml = ''; |
| 802 | + } |
| 803 | + } |
| 804 | + |
| 805 | + return $resultHtml; |
| 806 | + } |
| 807 | + |
| 808 | + /** |
| 809 | + * Returns a Validator style Parameter definition. |
| 810 | + * SMW 1.5.x style definitions are converted. |
| 811 | + * |
| 812 | + * @since 1.6 |
| 813 | + * |
| 814 | + * @param mixed $param |
| 815 | + * |
| 816 | + * @return Parameter |
| 817 | + */ |
| 818 | + protected function toValidatorParam( $param ) { |
| 819 | + static $typeMap = array( |
| 820 | + 'int' => Parameter::TYPE_INTEGER |
| 821 | + ); |
| 822 | + |
| 823 | + if ( !( $param instanceof Parameter ) ) { |
| 824 | + if ( !array_key_exists( 'type', $param ) ) { |
| 825 | + $param['type'] = 'string'; |
| 826 | + } |
| 827 | + |
| 828 | + $paramClass = $param['type'] == 'enum-list' ? 'ListParameter' : 'Parameter'; |
| 829 | + $paramType = array_key_exists( $param['type'], $typeMap ) ? $typeMap[$param['type']] : Parameter::TYPE_STRING; |
| 830 | + |
| 831 | + $parameter = new $paramClass( $param['name'], $paramType ); |
| 832 | + |
| 833 | + if ( array_key_exists( 'description', $param ) ) { |
| 834 | + $parameter->setDescription( $param['description'] ); |
| 835 | + } |
| 836 | + |
| 837 | + if ( array_key_exists( 'values', $param ) && is_array( $param['values'] ) ) { |
| 838 | + $parameter->addCriteria( new CriterionInArray( $param['values'] ) ); |
| 839 | + } |
| 840 | + |
| 841 | + return $parameter; |
| 842 | + } |
| 843 | + else { |
| 844 | + return $param; |
| 845 | + } |
| 846 | + } |
| 847 | + |
| 848 | + /** |
| 849 | + * Get the HTML for a single parameter input. |
| 850 | + * |
| 851 | + * @since 1.6 |
| 852 | + * |
| 853 | + * @param Parameter $parameter |
| 854 | + * @param mixed $currentValue |
| 855 | + * |
| 856 | + * @return string |
| 857 | + */ |
| 858 | + protected function showFormatOption( Parameter $parameter, $currentValue ) { |
| 859 | + $input = new ParameterInput( $parameter ); |
| 860 | + $input->setInputName( 'p[' . $parameter->getName() . ']' ); |
| 861 | + |
| 862 | + if ( $currentValue !== false ) { |
| 863 | + $input->setCurrentValue( $currentValue ); |
| 864 | + } |
| 865 | + |
| 866 | + return $input->getHtml(); |
| 867 | + } |
| 868 | + |
| 869 | + /** |
| 870 | + * Compatibility method to get the skin; MW 1.18 introduces a getSkin method in SpecialPage. |
| 871 | + * |
| 872 | + * @since 1.6 |
| 873 | + * |
| 874 | + * @return Skin |
| 875 | + */ |
| 876 | + public function getSkin() { |
| 877 | + if ( method_exists( 'SpecialPage', 'getSkin' ) ) { |
| 878 | + return parent::getSkin(); |
| 879 | + } |
| 880 | + else { |
| 881 | + global $wgUser; |
| 882 | + return $wgUser->getSkin(); |
| 883 | + } |
| 884 | + } |
| 885 | + |
| 886 | +} |
| 887 | + |
Index: trunk/extensions/SemanticMediaWiki/languages/SMW_Messages.php |
— | — | @@ -235,6 +235,10 @@ |
236 | 236 | 'querycreator' => 'Query Creator', |
237 | 237 | 'smw_show_addnal_opts' => 'Show additional options', |
238 | 238 | 'smw_hide_addnal_opts' => 'Hide additional options', |
| 239 | + 'smw_qc_query_help' => 'Enter a query using the form below to select wiki pages based on |
| 240 | +Categories (<strong>[[Category:Actor]]</strong>), |
| 241 | +Properties (<strong>[[Located in::Germany]]</strong>), |
| 242 | +Names(<strong>[[Germany]]</strong>) and Namespaces (<strong>[[Help:+]]</strong>)', |
239 | 243 | |
240 | 244 | // Messages for the search by property special |
241 | 245 | 'searchbyproperty' => 'Search by property', |