This document contains an example for a `ResultPrinter` both for the PHP and JavaScript part and before diving into the details, please make sure you have read ["Writing a result printer"](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/architecture/writing.resultprinter.md). ## PHP
namespace SMW\Query\ResultPrinters;
use SMWQueryResult as QueryResult;
use SMWDataItem as DataItem;
use SMWDataValue as DataValue;
use Html;
/**
* Boilerplate query printer
*
* Add your description here ...
*
* @license GNU GPL v2+
* @since 3.0
*
* @author mwjames
*/
class BoilerplateResultPrinter extends ResultPrinter {
/**
* @see ResultPrinter::getName
*
* {@inheritDoc}
*/
public function getName() {
// Add your result printer name here
return wfMessage( 'foo-boilerplate' )->text();
}
/**
* @see ResultPrinter::getParamDefinitions
*
* {@inheritDoc}
*/
public function getParamDefinitions( array $definitions ) {
$definitions = parent::getParamDefinitions( $definitions );
// Add your parameters here
// Example of a unit parameter
$definitions['unit'] = [
'message' => 'foo-paramdesc-unit',
'default' => '',
];
return $definitions;
}
/**
* @see ResultPrinter::getResources
*
* {@inheritDoc}
*/
protected function getResources() {
// Add resource definitions that has been registered with `Resource.php`
// Resource definitions contain scripts, styles, messages etc.
return [
'modules' => [
'foo.boilerplate'
],
'styles' => [
'foo.boilerplate.styles'
]
];
}
/**
* @see ResultPrinter::getResultText
*
* {@inheritDoc}
*/
protected function getResultText( QueryResult $queryResult, $outputMode ) {
// Data processing
// It is advisable to separate data processing from output logic
$data = $this->preprocess( $queryResult, $outputMode );
// Check if the data processing returned any results otherwise just bailout
if ( $data === [] ) {
// Add an error message to return method
return $queryResult->addErrors( 'some-error' );
} else {
// Add options if needed to format the output
// $outputMode can be specified as
// SMW_OUTPUT_HTML
// SMW_OUTPUT_FILE
// SMW_OUTPUT_WIKI
// For implementing template support this options has to be set but if you
// manipulate data via jQuery/JavaScript it is less likely that you need
// this option since templates will influence how wiki text is parsed
// but will have no influence in how a HTML representation is altered
// $this->hasTemplates = true;
$options = [
'mode' => $outputMode
];
// Return formatted results
return $this->buildHTML( $data, $options );
}
}
/**
* Returns an array with data
*
* @return array
*/
private function preprocess( QueryResult $queryResult, $outputMode ) {
$data = [];
// This is an example implementation on how to select available data from
// a result set. Please make appropriate adoptions necessary for your
// application.
// Some methods are declared as private to show case which objects are
// directly accessible within SMWQueryResult
// Get all SMWDIWikiPage objects that make up the results
// $subjects = $this->getSubjects( $queryResult->getResults() );
// Get all print requests property labels
// $labels = $this->getLabels( $queryResult->getPrintRequests() );
/**
* Get all values for all rows that belong to the result set
*
* @var ResultArray $rows
*/
while ( $rows = $queryResult->getNext() ) {
/**
* @var ResultArray $field
* @var DataValue $dataValue
*/
foreach ( $rows as $field ) {
// Initialize the array each time it passes a new row to avoid data from
// a previous row is remaining
$rowData = [];
// Get the label for the current property
$propertyLabel = $field->getPrintRequest()->getLabel();
// Get the label for the current subject
// getTitle()->getText() will return only the main text without the
// fragment(#) which can be arbitrary in case subobjects are involved
// getTitle()->getFullText() will return the text with the fragment(#)
// which is important when using subobjects
$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
// Get the data value item
$rowData[] = $this->getDataValueItem( $dataValue->getDataItem()->getDIType(), $dataValue );
}
// Example how to build a hierarchical array by collecting all values
// belonging to one subject/row using labels as array key representation
$data[$subjectLabel][$propertyLabel][] = $rowData;
}
}
// Return the data
// return array( 'labels' => $labels, 'subjects' => $subjects, 'data' => $data );
return $data;
}
/**
* A quick getway method to find all SMWDIWikiPage objects that make up the
* results
*
* @return array
*/
private function getSubjects( $result ) {
$subjects = [];
foreach ( $result as $wikiDIPage ) {
$subjects[] = $wikiDIPage->getTitle()->getText();
}
return $subjects;
}
/**
* Get all print requests property labels
*
* @return array
*/
private function getLabels( $result ) {
$printRequestsLabels = [];
foreach ( $result as $printRequests ) {
$printRequestsLabels[] = $printRequests->getLabel();
}
return $printRequestsLabels;
}
/**
* Get a single data value item
*
* @return mixed
*/
private function getDataValueItem( $type, DataValue $dataValue ) {
if ( $type == DataItem::TYPE_NUMBER ) {
// Set unit if available
$dataValue->setOutputFormat( $this->params['unit'] );
// Check if unit is available and return the converted value otherwise
// just return a plain number
if ( $dataValue->getUnit() !== '' ) {
return $dataValue->getShortWikiText();
}
return $dataValue->getNumber();
}
// For all other data types return the wikivalue
return $dataValue->getWikiValue();
}
/**
* Prepare data for the output
*
* @return string
*/
protected function buildHTML( $data, $options ) {
// The generated ID is to distinguish similar instances of the same
// printer that can appear within the same page
$id = uniqid( 'foo-boilerplate-' . rand( 1, 10000 ) );
// Used to set that the output and being treated as HTML (opposed to plain wiki text)
$this->isHTML = true;
// Correct escaping is vital to minimize possibilities of malicious code snippets
// and also a coherent string evaluation therefore it is recommended
// that data transferred to the JS plugin is JSON encoded
// Assign the ID to make a data instance readily available and distinguishable
// from other content within the same page
$requireHeadItem = [ $id => json_encode( $data ) ];
\SMWOutputs::requireHeadItem( $id, \Skin::makeVariablesScript( $requireHeadItem ) );
// Add two elements a outer wrapper that is assigned a class which the JS plugin
// can select and will fetch all instances of the same result printer and an inner
// container which is set invisible (display=none) for as long as the JS plugin
// holds the content hidden. It is normally the place where the "hard work"
// is done hidden from the user until it is ready.
// The JS plugin can prepare the output within this container without presenting
// unfinished visual content, to avoid screen clutter and improve user experience.
return Html::rawElement(
'div',
[
'class' => 'foo-boilerplate'
],
Html::element(
'div',
[
'id' => $id,
'class' => 'container',
'style' => 'display:none;'
]
)
);
}
}
## JavaScript
/**
* @license GNU GPL v2+
* @since 3.0
*
* @author mwjames
*/
( function( $, mw ) {
// Use EcmaScript 5 to improve code quality and check with jshint/jslint
// if the code adheres standard coding conventions
// Strict mode eliminates some JavaScript pitfalls
'use strict';
// Passing jshint
/*global mediaWiki:true */
/**
* @type Object
*/
foo = {};
/**
* Base constructor for objects representing a boilerplate instance
*
* @type Object
*/
// If you have default values to be set during the instantiation
// $.extend ... can be used here
foo.boilerplate = function() {};
foo.boilerplate.prototype = {
// Specify your functions and parameters
show: function( context ) {
return context.each( function() {
// Ensure variables have only local scope otherwise leaked content might
// cause issues for other plugins
var that = $( this );
// Find the container instance that was created by the PHP output
// and store it as "container" variable which all preceding steps
// working on a localized instance
var container = that.find( '.container' );
// Find the ID that connects to the current instance with the published data
var id = container.attr( 'id' );
// Fetch the stored data with help of mw.config.get() method and the current instance ID
// @see https://www.mediawiki.org/wiki/ResourceLoader/Default_modules#mediaWiki.config
var json = mw.config.get( id );
// Parse the fetched json string and convert it back into objects/arrays
var data = typeof json === 'string' ? jQuery.parseJSON( json ) : json;
// You got everything you need to work your magic
// A clean instance, data from the wiki, and a separate container
// If you need to see what data you've got from your result printer
// it is always helpful to do
// console.log( data );
// Happy coding ...
} );
}
};
/**
* Implementation and representation of the boilerplate instance
*
* @type Object
*/
// Create class instance
var boilerplate = new foo.boilerplate();
$( document ).ready(function() {
// Use the class selector to find all instances relevant to the "boilerplate" printer
// since a wiki page can have more than one instance of the same result printer
// .each() ensures instances are handled separately
$( '.foo-boilerplate' ).each(function() {
// Access methods available through the boilerplate class
boilerplate.show( $( this ) );
} );
} );
} )( jQuery, mediaWiki );