Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 214 |
CargoExhibitFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 12 |
2450 | |
0.00% |
0 / 214 |
allowedParameters | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 11 |
|||
prependDot | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
createMap | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 19 |
|||
createTimeline | |
0.00% |
0 / 1 |
72 | |
0.00% |
0 / 29 |
|||
createTabular | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 17 |
|||
createFacets | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 14 |
|||
createSearch | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 8 |
|||
createLens | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 15 |
|||
queryAndDisplay | |
0.00% |
0 / 1 |
156 | |
0.00% |
0 / 65 |
|||
automateViews | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 12 |
|||
getCoordinatesFields | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 11 |
|||
getDateFields | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 11 |
<?php | |
/** | |
* Adds Exhibit format to Cargo queries. | |
* | |
* @author @lmorillas | |
*/ | |
class CargoExhibitFormat extends CargoDeferredFormat { | |
public static function allowedParameters() { | |
return [ | |
'view' => [ 'values' => [ 'map', 'tabular', 'timeline' ] ], | |
'facets' => [ 'type' => 'string' ], | |
'datalabel' => [ 'type' => 'string' ], | |
'end' => [ 'type' => 'string' ], | |
'color' => [ 'type' => 'string' ], | |
'topunit' => [ 'values' => [ 'millisecond', 'second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'decade', 'century', 'millennium' ] ], | |
'toppx' => [ 'type' => 'int' ], | |
'bottompx' => [ 'type' => 'int' ] | |
]; | |
} | |
/** | |
* @param string $p | |
* @return string | |
*/ | |
private function prependDot( $p ) { | |
return '.' . trim( $p ); | |
} | |
private function createMap( $sqlQueries ) { | |
$maps_script = '<link rel="exhibit-extension" href="//api.simile-widgets.org/exhibit/HEAD/extensions/map/map-extension.js"/>'; | |
$this->mOutput->addHeadItem( $maps_script, $maps_script ); | |
if ( !array_key_exists( "latlng", $this->displayParams ) ) { | |
$coordFields = $this->getCoordinatesFields( $sqlQueries ); | |
if ( count( $coordFields ) > 0 ) { | |
$this->displayParams['latlng'] = $coordFields[0]; | |
} | |
} | |
$attrs = [ | |
'data-ex-role' => 'view', | |
'data-ex-view-class' => "Map", | |
'data-ex-latlng' => $this->prependDot( $this->displayParams['latlng'] ), | |
'data-ex-autoposition' => "true", | |
]; | |
if ( array_key_exists( "color", $this->displayParams ) ) { | |
$attrs["data-ex-color-key"] = $this->prependDot( $this->displayParams['color'] ); | |
} | |
return Html::element( 'div', $attrs ); | |
} | |
private function createTimeline( $sqlQueries ) { | |
$timeline_script = '<link rel="exhibit-extension" href="//api.simile-widgets.org/exhibit/HEAD/extensions/time/time-extension.js"/>'; | |
$this->mOutput->addHeadItem( $timeline_script, $timeline_script ); | |
// div | |
$attrs = []; | |
$attrs['data-ex-role'] = 'view'; | |
$attrs["data-ex-view-class"] = "Timeline"; | |
if ( !array_key_exists( "start", $this->displayParams ) ) { | |
$dateFields = $this->getDateFields( $sqlQueries ); | |
if ( count( $dateFields ) > 0 ) { | |
$this->displayParams['start'] = $dateFields[0]; | |
} | |
} | |
$attrs["data-ex-start"] = $this->prependDot( $this->displayParams['start'] ); | |
if ( array_key_exists( "end", $this->displayParams ) ) { | |
$attrs["data-ex-end"] = $this->prependDot( $this->displayParams['end'] ); | |
} | |
if ( array_key_exists( "color", $this->displayParams ) ) { | |
$attrs["data-ex-color-key"] = $this->prependDot( $this->displayParams['color'] ); | |
} | |
if ( array_key_exists( "topunit", $this->displayParams ) ) { | |
$attrs["data-ex-top-band-unit"] = $this->displayParams['topunit']; | |
} | |
if ( array_key_exists( "toppx", $this->displayParams ) ) { | |
$attrs["data-ex-top-band-pixels-per-unit"] = $this->displayParams['toppx']; | |
} | |
if ( array_key_exists( "bottompx", $this->displayParams ) ) { | |
$attrs["data-ex-bottom-band-pixels-per-unit"] = $this->displayParams['bottompx']; | |
} | |
return Html::element( 'div', $attrs ); | |
} | |
/** | |
* @param string[] $fieldList | |
* @return string HTML | |
*/ | |
private function createTabular( $fieldList ) { | |
$columnsList = []; | |
foreach ( $fieldList as $field ) { | |
if ( strpos( $field, '__' ) == false ) { | |
$columnsList[] = $field; | |
} | |
} | |
$attrs = [ | |
'data-ex-role' => 'view', | |
'data-ex-view-class' => 'Tabular', | |
'data-ex-paginate' => "true", | |
'data-ex-table-styler' => "tableStyler", | |
'data-ex-columns' => implode( ',', | |
array_map( "CargoExhibitFormat::prependDot", $columnsList ) ), | |
'data-ex-column-labels' => implode( ',', array_map( "ucfirst", $columnsList ) ) | |
]; | |
return Html::element( 'div', $attrs ); | |
} | |
private function createFacets( $facets ) { | |
// Explode facets and create the div for each of them. | |
$text = $this->createSearch(); | |
foreach ( $facets as $f ) { | |
$attrs = [ | |
'data-ex-role' => "facet", | |
'data-ex-collapsible' => "true", | |
'data-ex-expression' => '.' . $f, | |
'data-ex-show-missing' => 'false', | |
'data-ex-facet-label' => ucfirst( $f ), | |
'style' => "float: left; width: 24%; margin: 0 1% 0 0;" | |
]; | |
$text .= Html::element( 'div', $attrs ); | |
} | |
return Html::rawElement( 'div', [ "class" => "facets", "style" => "overflow: hidden; width: 100%;" ], $text ); | |
} | |
/** | |
* @return string | |
*/ | |
private function createSearch() { | |
$attrs = [ | |
'data-ex-role' => "exhibit-facet", | |
'data-ex-facet-class' => "TextSearch", | |
'data-ex-facet-label' => wfMessage( 'search' )->text(), | |
'style' => "float: left; width: 24%; margin: 0 1% 0 0;" | |
]; | |
return Html::element( 'div', $attrs ); | |
} | |
private function createLens( $fieldList ) { | |
$lensBody = '<caption><strong data-ex-content=".label"></strong></caption>'; | |
foreach ( $fieldList as $field ) { | |
if ( $field != "label" && strpos( $field, '__' ) === false && | |
strpos( $field, ' ' ) === false ) { | |
$th = "<strong>" . ucfirst( $field ) . "</strong>"; | |
$lensBody .= "<tr data-ex-if-exists=\".$field\"><td>$th</td><td data-ex-content=\".$field\"></td></tr>"; | |
} | |
} | |
$tableAttrs = [ | |
'data-ex-role' => 'lens', | |
'class' => 'cargoTable', | |
'style' => "display: none; width: 100%;" | |
]; | |
return Html::rawElement( 'table', $tableAttrs, $lensBody ); | |
} | |
/** | |
* @param CargoSQLQuery[] $sqlQueries | |
* @param array $displayParams | |
* @param array|null $querySpecificParams | |
* @return string HTML | |
* @throws MWException | |
*/ | |
public function queryAndDisplay( $sqlQueries, $displayParams, $querySpecificParams = null ) { | |
global $cgScriptPath; | |
$this->mOutput->addModules( [ 'ext.cargo.exhibit' ] ); | |
$this->mOutput->addModuleStyles( [ 'ext.cargo.main' ] ); | |
$exhibit_busy = $cgScriptPath . "/resources/images/loading.gif"; | |
// The "loading" message is just alt-text, so it doesn't really | |
// matter that it's hardcoded in English. | |
$preViewsText = '<img id="loading_exhibit" src="' . $exhibit_busy . '" alt="Loading Exhibit" style="display: none;" >'; | |
$field_list = []; | |
foreach ( $sqlQueries as $sqlQuery ) { | |
foreach ( $sqlQuery->mAliasedFieldNames as $alias => $fieldName ) { | |
$field_list[] = $alias; | |
} | |
} | |
$csv_properties = ''; | |
if ( !in_array( "label", $field_list ) ) { | |
// first field will be label! | |
$field_list[0] = 'label'; | |
$csv_properties = 'data-ex-properties="' . implode( ',', $field_list ) . '"'; | |
} | |
$queryParams = $this->sqlQueriesToQueryParams( $sqlQueries ); | |
$queryParams['format'] = 'csv'; | |
$ce = SpecialPage::getTitleFor( 'CargoExport' ); | |
$dataurl = htmlentities( $ce->getFullURL( $queryParams ) ); | |
// Data imported as csv | |
$datalink = "<link href=\"$dataurl\" type=\"text/csv\" rel=\"exhibit/data\" data-ex-has-column-titles=\"true\" $csv_properties />"; | |
$this->mOutput->addHeadItem( $datalink, $datalink ); | |
$this->displayParams = $displayParams; | |
// lens | |
$preViewsText .= $this->createLens( $field_list ); | |
// Facets | |
if ( array_key_exists( 'facets', $displayParams ) ) { | |
$facets = array_map( 'trim', explode( ',', $displayParams['facets'] ) ); | |
$preViewsText .= $this->createFacets( $facets ); | |
} else { | |
$preViewsText .= $this->createFacets( array_slice( $field_list, 0, 3 ) ); | |
} | |
if ( array_key_exists( 'datalabel', $displayParams ) ) { | |
$datalabel = trim( $displayParams['datalabel'] ); | |
// What is this used for? | |
$dataplural = $datalabel . 's'; | |
$data_label_link = <<<EOLABEL | |
<link href="#cargoExhibit" type="text/html" rel="exhibit/data" | |
data-ex-item-type="Item" data-ex-label="$datalabel" data-ex-plural-label="$dataplural" /> | |
EOLABEL; | |
$this->mOutput->addHeadItem( $data_label_link, $data_label_link ); | |
} | |
// View | |
$this->views = []; | |
if ( array_key_exists( 'view', $displayParams ) ) { | |
$this->views = array_map( 'ucfirst', array_map( 'trim', explode( ',', $displayParams['view'] ) ) ); | |
} else { | |
$this->automateViews( $sqlQueries ); | |
} | |
$viewsText = ""; | |
foreach ( $this->views as $view ) { | |
switch ( $view ) { | |
case "Timeline": | |
$viewsText .= $this->createTimeline( $sqlQueries ); | |
break; | |
case "Map": | |
$viewsText .= $this->createMap( $sqlQueries ); | |
break; | |
case "Tabular": | |
$viewsText .= $this->createTabular( $field_list ); | |
} | |
} | |
if ( count( $this->views ) > 1 ) { | |
$viewsText = Html::rawElement( 'div', | |
[ 'data-ex-role' => "viewPanel" ], | |
$viewsText ); | |
} | |
return $preViewsText . '<div id="cargoExhibit">' . $viewsText . '</div>'; | |
} | |
/** | |
* Initializes $this->views[] | |
*/ | |
private function automateViews( $sqlQueries ) { | |
// map ? | |
$coordFields = $this->getCoordinatesFields( $sqlQueries ); | |
if ( count( $coordFields ) > 0 ) { | |
$this->views[] = 'Map'; | |
$this->displayParams['latlng'] = $coordFields[0]; | |
} | |
// timeline ? | |
$dateFields = $this->getDateFields( $sqlQueries ); | |
if ( count( $dateFields ) > 0 ) { | |
$this->views[] = 'Timeline'; | |
$this->displayParams['start'] = $dateFields[0]; | |
} | |
$this->views[] = 'Tabular'; | |
} | |
private function getCoordinatesFields( $sqlQueries ) { | |
$coordinatesFields = []; | |
foreach ( $sqlQueries as $query ) { | |
$fieldDescriptions = $query->mFieldDescriptions; | |
foreach ( $fieldDescriptions as $field => $description ) { | |
if ( $description->mType == 'Coordinates' ) { | |
$coordinatesFields[] = $field; | |
} | |
} | |
} | |
return $coordinatesFields; | |
} | |
private function getDateFields( $sqlQueries ) { | |
$dateFields = []; | |
foreach ( $sqlQueries as $query ) { | |
$fieldDescriptions = $query->mFieldDescriptions; | |
foreach ( $fieldDescriptions as $field => $description ) { | |
if ( $description->mType == 'Date' || $description->mType == 'Datetime' ) { | |
$dateFields[] = $field; | |
} | |
} | |
} | |
return $dateFields; | |
} | |
} |