mTemplate = trim( $params['template'] );
$this->mNamedArgs = $params['named args'];
$this->m_tlstart = smwfNormalTitleDBKey( $params['timelinestart'] );
$this->m_tlend = smwfNormalTitleDBKey( $params['timelineend'] );
$this->m_tlbands = $params['timelinebands'];
$this->m_tlpos = strtolower( trim( $params['timelineposition'] ) );
// str_replace makes sure this is only one value, not mutliple CSS fields (prevent CSS attacks)
// / FIXME: this is either unsafe or redundant, since Timeline is Wiki-compatible. If the JavaScript makes user inputs to CSS then it is bad even if we block this injection path.
$this->m_tlsize = htmlspecialchars( str_replace( ';', ' ', strtolower( $params['timelinesize'] ) ) );
}
public function getName() {
// Give grep a chance to find the usages:
// srf_printername_timeline, srf_printername_eventline
return wfMessage( 'srf_printername_' . $this->mFormat )->text();
}
protected function getResultText( SMWQueryResult $res, $outputmode ) {
SMWOutputs::requireHeadItem( SMW_HEADER_STYLE );
SMWOutputs::requireResource( 'ext.srf.timeline' );
$isEventline = 'eventline' == $this->mFormat;
$id = uniqid();
if ( !$isEventline && ( $this->m_tlstart == '' ) ) { // seek defaults
foreach ( $res->getPrintRequests() as $pr ) {
if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) && ( $pr->getTypeID() == '_dat' ) ) {
$dataValue = $pr->getData();
$date_value = $dataValue->getDataItem()->getLabel();
if ( ( $this->m_tlend == '' ) && ( $this->m_tlstart != '' ) &&
( $this->m_tlstart != $date_value ) ) {
$this->m_tlend = $date_value;
} elseif ( ( $this->m_tlstart == '' ) && ( $this->m_tlend != $date_value ) ) {
$this->m_tlstart = $date_value;
}
}
}
}
// print header
$result = "
m_tlsize\">";
$result .= '';
foreach ( $this->m_tlbands as $band ) {
$result .= '' . htmlspecialchars( $band ) . '';
// just print any "band" given, the JavaScript will figure out what to make of it
}
// print all result rows
if ( ( $this->m_tlstart != '' ) || $isEventline ) {
$result .= $this->getEventsHTML( $res, $outputmode, $isEventline );
}
// no further results displayed ...
// print footer
$result .= '
';
// yes, our code can be viewed as HTML if requested, no more parsing needed
$this->isHTML = $outputmode == SMW_OUTPUT_HTML;
return $result;
}
/**
* Returns the HTML for the events.
*
* @since 1.5.3
*
* @param SMWQueryResult $res
* @param $outputmode
* @param boolean $isEventline
*
* @return string
*/
protected function getEventsHTML( SMWQueryResult $res, $outputmode, $isEventline ) {
global $curarticle, $cururl; // why not, code flow has reached max insanity already
$positions = []; // possible positions, collected to select one for centering
$curcolor = 0; // color cycling is used for eventline
$result = '';
$output = false; // true if output for the popup was given on current line
if ( $isEventline ) {
$events = [];
} // array of events that are to be printed
while ( $row = $res->getNext() ) { // Loop over the objcts (pages)
$hastime = false; // true as soon as some startdate value was found
$hastitle = false; // true as soon as some label for the event was found
$curdata = ''; // current *inner* print data (within some event span)
$curmeta = ''; // current event meta data
$cururl = '';
$curarticle = ''; // label of current article, if it was found; needed only for eventline labeling
$first_col = true;
if ( $this->mTemplate != '' ) {
$this->hasTemplates = true;
$template_text = '';
$i = 0;
}
foreach ( $row as $field ) { // Loop over the returned properties
$first_value = true;
$pr = $field->getPrintRequest();
$dataValue = $pr->getData();
if ( $dataValue == '' ) {
$date_value = null;
} else {
$date_value = $dataValue->getDataItem()->getLabel();
}
while ( ( $object = $field->getNextDataValue() ) !== false ) { // Loop over property values
$event = $this->handlePropertyValue(
$object,
$outputmode,
$pr,
$first_col,
$hastitle,
$hastime,
$first_value,
$isEventline,
$curmeta,
$curdata,
$date_value,
$output,
$positions
);
if ( $this->mTemplate != '' ) {
$template_text .= '|' . ( $this->mNamedArgs ? '?' . $field->getPrintRequest()->getLabel(
) : $i + 1 ) . '=';
if ( !$first_value ) {
$template_text .= ', ';
}
$template_text .= $object->getShortText( SMW_OUTPUT_WIKI, $this->getLinker( $first_value ) );
$i++;
}
if ( $event !== false ) {
$events[] = $event;
}
$first_value = false;
}
if ( $output ) {
$curdata .= '
';
}
$output = false;
$first_col = false;
}
if ( $this->mTemplate != '' ) {
$curdata = '{{' . $this->mTemplate . $template_text . '}}';
}
if ( $hastime ) {
$result .= Html::rawElement(
'span',
[ 'class' => 'smwtlevent', 'style' => 'display:none;' ],
$curmeta . Html::element(
'span',
[ 'class' => 'smwtlcoloricon' ],
$curcolor
) . $curdata
);
}
if ( $isEventline ) {
foreach ( $events as $event ) {
$result .= '' . $event[0] . '' . $curarticle . '' . $curcolor . '';
if ( $curarticle != '' ) {
$result .= '' . $curarticle . ' ';
}
$result .= $curdata . '';
$positions[$event[2]] = $event[0];
}
$events = [];
$curcolor = ( $curcolor + 1 ) % 10;
}
}
if ( count( $positions ) > 0 ) {
ksort( $positions );
$positions = array_values( $positions );
switch ( $this->m_tlpos ) {
case 'start':
$result .= '' . $positions[0] . '';
break;
case 'end':
$result .= '' . $positions[count(
$positions
) - 1] . '';
break;
case 'today':
break; // default
case 'middle':
default:
$result .= '' . $positions[ceil(
count( $positions ) / 2
) - 1] . '';
break;
}
}
return $result;
}
/**
* Hanldes a single property value. Returns an array with data for a single event or false.
*
* FIXME: 13 arguments, of which a whole bunch are byref... not a good design :)
*
* @since 1.5.3
*
* @param SMWDataValue $object
* @param $outputmode
* @param SMWPrintRequest $pr
* @param boolean $first_col
* @param boolean &$hastitle
* @param boolean &$hastime
* @param boolean $first_value
* @param boolean $isEventline
* @param string &$curmeta
* @param string &$curdata
* @param &$date_value
* @param boolean &$output
* @param array &$positions
*
* @return false or array
*/
protected function handlePropertyValue( SMWDataValue $object, $outputmode, SMWPrintRequest $pr, $first_col,
&$hastitle, &$hastime, $first_value, $isEventline, &$curmeta, &$curdata, $date_value, &$output, array &$positions ) {
global $curarticle, $cururl;
$event = false;
$l = $this->getLinker( $first_col );
if ( !$hastitle && $object->getTypeID(
) != '_wpg' ) { // "linking" non-pages in title positions confuses timeline scripts, don't try this
$l = null;
}
if ( $object->getTypeID() == '_wpg' ) { // use shorter "LongText" for wikipage
$objectlabel = $object->getLongText( $outputmode, $l );
} else {
$objectlabel = $object->getShortText( $outputmode, $l );
}
$urlobject = ( $l !== null );
$header = '';
if ( $first_value ) {
// find header for current value:
if ( $this->mShowHeaders && ( '' != $pr->getLabel() ) ) {
$header = $pr->getText( $outputmode, $this->mLinker ) . ': ';
}
// is this a start date?
if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) &&
( $date_value == $this->m_tlstart ) ) {
// FIXME: Timeline scripts should support XSD format explicitly. They
// currently seem to implement iso8601 which deviates from XSD in cases.
// NOTE: We can assume $object to be an SMWDataValue in this case.
$curmeta .= Html::element(
'span',
[ 'class' => 'smwtlstart' ],
$object->getXMLSchemaDate()
);
$positions[$object->getHash()] = $object->getXMLSchemaDate();
$hastime = true;
}
// is this the end date?
if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) &&
( $date_value == $this->m_tlend ) ) {
// NOTE: We can assume $object to be an SMWDataValue in this case.
$curmeta .= Html::element(
'span',
[ 'class' => 'smwtlend' ],
$object->getXMLSchemaDate( false )
);
}
// find title for displaying event
if ( !$hastitle ) {
$curmeta .= Html::rawElement(
'span',
[
'class' => $urlobject ? 'smwtlurl' : 'smwtltitle'
],
$objectlabel
);
if ( $pr->getMode() == SMWPrintRequest::PRINT_THIS ) {
$curarticle = $object->getLongText( $outputmode, $l );
$cururl = $object->getDataItem()->getTitle()->getFullUrl();
}
// NOTE: type Title of $object implied
$hastitle = true;
}
} elseif ( $output ) {
// it *can* happen that output is false here, if the subject was not printed (fixed subject query) and mutliple items appear in the first row
$curdata .= ', ';
}
if ( !$first_col || !$first_value || $isEventline ) {
$curdata .= $header . $objectlabel;
$output = true;
}
if ( $isEventline && ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) && ( $pr->getTypeID(
) == '_dat' ) && ( '' != $pr->getLabel(
) ) && ( $date_value != $this->m_tlstart ) && ( $date_value != $this->m_tlend ) ) {
$event = [
$object->getXMLSchemaDate(),
$pr->getLabel(),
$object->getDataItem()->getSortKey(),
];
}
return $event;
}
/**
* @see SMWResultPrinter::getParamDefinitions
*
* @since 1.8
*
* @param $definitions array of IParamDefinition
*
* @return array of IParamDefinition|array
*/
public function getParamDefinitions( array $definitions ) {
$params = parent::getParamDefinitions( $definitions );
$params['timelinesize'] = [
'default' => '300px',
'message' => 'srf_paramdesc_timelinesize',
];
$params['timelineposition'] = [
'default' => 'middle',
'message' => 'srf_paramdesc_timelineposition',
'values' => [ 'start', 'middle', 'end', 'today' ],
];
$params['timelinestart'] = [
'default' => '',
'message' => 'srf_paramdesc_timelinestart',
];
$params['timelineend'] = [
'default' => '',
'message' => 'srf_paramdesc_timelineend',
];
$params['timelinebands'] = [
'islist' => true,
'default' => [ 'MONTH', 'YEAR' ],
'message' => 'srf_paramdesc_timelinebands',
'values' => [ 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR', 'DECADE' ],
];
$params['template'] = [
'message' => 'smw-paramdesc-template',
'default' => '',
];
$params['named args'] = [
'type' => 'boolean',
'message' => 'smw-paramdesc-named_args',
'default' => false,
];
return $params;
}
}