summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/includes
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/SemanticMediaWiki/includes
first commit
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/includes')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php251
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php370
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php374
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php666
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php225
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php355
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php379
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php782
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Settings.php613
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Setup.php441
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php509
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php609
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php299
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php602
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php300
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php699
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php379
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php751
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php897
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php328
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php768
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php675
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php333
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php277
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php294
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php295
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php552
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php530
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php275
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php250
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php187
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php280
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php387
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php462
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php282
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php653
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php501
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php825
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php1296
69 files changed, 23106 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php b/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php
new file mode 100644
index 00000000..3fda57a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php
@@ -0,0 +1,251 @@
+<?php
+
+namespace SMW;
+
+use Parser;
+use ParserOptions;
+use Revision;
+use Title;
+use User;
+
+/**
+ * Fetches the ParserOutput either by parsing an invoked text component,
+ * re-parsing a text revision, or accessing the ContentHandler to generate a
+ * ParserOutput object
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ContentParser {
+
+ /** @var Title */
+ protected $title;
+
+ /** @var Parser */
+ protected $parser = null;
+
+ /** @var ParserOutput */
+ protected $parserOutput = null;
+
+ /** @var Revision */
+ protected $revision = null;
+
+ /** @var array */
+ protected $errors = [];
+
+ /**
+ * @var boolean
+ */
+ private $enabledToUseContentHandler = true;
+
+ /**
+ * @var boolean
+ */
+ private $skipInTextAnnotationParser = false;
+
+ /**
+ * @note Injecting new Parser() alone will not yield an expected result and
+ * doing new Parser( $GLOBALS['wgParserConf'] brings no benefits therefore
+ * we stick to the GLOBAL as fallback if no parser is injected.
+ *
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param Parser|null $parser
+ */
+ public function __construct( Title $title, Parser $parser = null ) {
+ $this->title = $title;
+ $this->parser = $parser;
+
+ if ( $this->parser === null ) {
+ $this->parser = $GLOBALS['wgParser'];
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return Parser $parser
+ */
+ public function setParser( Parser $parser ) {
+ $this->parser = $parser;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return ContentParser
+ */
+ public function setRevision( Revision $revision = null ) {
+ $this->revision = $revision;
+ return $this;
+ }
+
+ /**
+ * @bug 62856 and #212
+ *
+ * @since 2.0
+ */
+ public function forceToUseParser() {
+ $this->enabledToUseContentHandler = false;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserOutput|null
+ */
+ public function getOutput() {
+ return $this->parserOutput;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ public function skipInTextAnnotationParser() {
+ return $this->skipInTextAnnotationParser = true;
+ }
+
+ /**
+ * Generates or fetches the ParserOutput object from an appropriate source
+ *
+ * @since 1.9
+ *
+ * @param string|null $text
+ *
+ * @return ContentParser
+ */
+ public function parse( $text = null ) {
+
+ if ( $text !== null ) {
+ return $this->parseText( $text );
+ }
+
+ if ( $this->hasContentHandler() && $this->enabledToUseContentHandler ) {
+ return $this->fetchFromContent();
+ }
+
+ return $this->fetchFromParser();
+ }
+
+ protected function parseText( $text ) {
+
+ $this->parserOutput = $this->parser->parse(
+ $text,
+ $this->getTitle(),
+ $this->makeParserOptions()
+ );
+
+ return $this;
+ }
+
+ /**
+ * @note Revision ID must be passed to the parser output to
+ * get revision variables correct
+ *
+ * @note If no content is available create an empty object
+ */
+ protected function fetchFromContent() {
+
+ if ( $this->getRevision() === null ) {
+ return $this->msgForNullRevision();
+ }
+
+ $content = $this->getRevision()->getContent( Revision::RAW );
+
+ if ( !$content ) {
+ $content = $this->getRevision()->getContentHandler()->makeEmptyContent();
+ }
+
+ $this->parserOutput = $content->getParserOutput(
+ $this->getTitle(),
+ $this->getRevision()->getId(),
+ null,
+ true
+ );
+
+ return $this;
+ }
+
+ protected function fetchFromParser() {
+
+ if ( $this->getRevision() === null ) {
+ return $this->msgForNullRevision();
+ }
+
+ $this->parserOutput = $this->parser->parse(
+ $this->getRevision()->getText(),
+ $this->getTitle(),
+ $this->makeParserOptions(),
+ true,
+ true,
+ $this->getRevision()->getID()
+ );
+
+ return $this;
+ }
+
+ protected function msgForNullRevision( $fname = __METHOD__ ) {
+ $this->errors = [ $fname . " No revision available for {$this->getTitle()->getPrefixedDBkey()}" ];
+ return $this;
+ }
+
+ protected function makeParserOptions() {
+
+ $user = null;
+
+ if ( $this->getRevision() !== null ) {
+ $user = User::newFromId( $this->getRevision()->getUser() );
+ }
+
+ $parserOptions = new ParserOptions( $user );
+
+ // Use the InterfaceMessage marker to skip InTextAnnotationParser
+ // processing
+ $parserOptions->setInterfaceMessage( $this->skipInTextAnnotationParser );
+
+ return $parserOptions;
+ }
+
+ protected function getRevision() {
+
+ if ( $this->revision instanceof Revision ) {
+ return $this->revision;
+ }
+
+ // Revision::READ_NORMAL is not specified in MW 1.19
+ if ( defined( 'Revision::READ_NORMAL' ) ) {
+ $this->revision = Revision::newFromTitle( $this->getTitle(), false, Revision::READ_NORMAL );
+ } else {
+ $this->revision = Revision::newFromTitle( $this->getTitle() );
+ }
+
+ \Hooks::run( 'SMW::Parser::ChangeRevision', [ $this->getTitle(), &$this->revision ] );
+
+ return $this->revision;
+ }
+
+ protected function hasContentHandler() {
+ return defined( 'CONTENT_MODEL_WIKITEXT' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php b/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php
new file mode 100644
index 00000000..317a6df5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Static class for hooks handled by the Semantic MediaWiki extension.
+ *
+ * @since 1.7
+ *
+ * @file SemanticMediaWiki.hooks.php
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+final class SMWExternalHooks {
+
+ /**
+ * TODO
+ *
+ * @since 1.7
+ *
+ * @return boolean
+ */
+ public static function onPageSchemasRegistration() {
+ // @codeCoverageIgnoreStart
+ $GLOBALS['wgPageSchemasHandlerClasses'][] = 'SMWPageSchemas';
+
+ return true;
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Adds links to Admin Links page.
+ *
+ * @since 1.7
+ *
+ * @param ALTree $admin_links_tree
+ *
+ * @return boolean
+ */
+ public static function addToAdminLinks( ALTree $admin_links_tree ) {
+ // @codeCoverageIgnoreStart
+ $data_structure_section = new ALSection( wfMessage( 'smw_adminlinks_datastructure' )->text() );
+
+ $smw_row = new ALRow( 'smw' );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Categories' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Properties' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'UnusedProperties' ) );
+
+ $data_structure_section->addRow( $smw_row );
+ $smw_admin_row = new ALRow( 'smw_admin' );
+ $smw_admin_row->addItem( ALItem::newFromSpecialPage( 'SMWAdmin' ) );
+
+ $data_structure_section->addRow( $smw_admin_row );
+ $smw_docu_row = new ALRow( 'smw_docu' );
+ $smw_name = wfMessage( 'specialpages-group-smw_group' )->text();
+ $smw_docu_label = wfMessage( 'adminlinks_documentation', $smw_name )->text();
+ $smw_docu_row->addItem( AlItem::newFromExternalLink( 'http://semantic-mediawiki.org/wiki/Help:User_manual', $smw_docu_label ) );
+
+ $data_structure_section->addRow( $smw_docu_row );
+ $admin_links_tree->addSection( $data_structure_section, wfMessage( 'adminlinks_browsesearch' )->text() );
+ $smw_row = new ALRow( 'smw' );
+ $displaying_data_section = new ALSection( wfMessage( 'smw_adminlinks_displayingdata' )->text() );
+ $smw_row->addItem( AlItem::newFromExternalLink(
+ 'http://semantic-mediawiki.org/wiki/Help:Inline_queries',
+ wfMessage( 'smw_adminlinks_inlinequerieshelp' )->text()
+ ) );
+
+ $displaying_data_section->addRow( $smw_row );
+ $admin_links_tree->addSection( $displaying_data_section, wfMessage( 'adminlinks_browsesearch' )->text() );
+ $browse_search_section = $admin_links_tree->getSection( wfMessage( 'adminlinks_browsesearch' )->text() );
+
+ $smw_row = new ALRow( 'smw' );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Browse' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Ask' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'SearchByProperty' ) );
+ $browse_search_section->addRow( $smw_row );
+
+ return true;
+ // @codeCoverageIgnoreEnd
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php b/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php
new file mode 100644
index 00000000..6b659346
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php
@@ -0,0 +1,370 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMWOutputs;
+
+/**
+ * Highlighter utility function for Semantic MediaWiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Highlighter {
+
+ /**
+ * Highlighter ID for no types
+ */
+ const TYPE_NOTYPE = 0;
+
+ /**
+ * Highlighter ID for properties
+ */
+ const TYPE_PROPERTY = 1;
+
+ /**
+ * Highlighter ID for text
+ */
+ const TYPE_TEXT = 2;
+
+ /**
+ * Highlighter ID for quantities
+ */
+ const TYPE_QUANTITY = 3;
+
+ /**
+ * Highlighter ID for warnings
+ */
+ const TYPE_WARNING = 4;
+
+ /**
+ * Highlighter ID for error
+ */
+ const TYPE_ERROR = 5;
+
+ /**
+ * Highlighter ID for information
+ */
+ const TYPE_INFO = 6;
+
+ /**
+ * Highlighter ID for help
+ */
+ const TYPE_HELP = 7;
+
+ /**
+ * Highlighter ID for notes
+ */
+ const TYPE_NOTE = 8;
+
+ /**
+ * Highlighter ID for service links
+ */
+ const TYPE_SERVICE = 9;
+
+ /**
+ * Highlighter ID for reference links
+ */
+ const TYPE_REFERENCE = 10;
+
+ /**
+ * @var array $options
+ */
+ private $options;
+
+ /**
+ * @var int $type
+ */
+ private $type;
+
+ /**
+ * @var string|null
+ */
+ private $language = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param int $type
+ * @param string|null $language
+ */
+ public function __construct( $type, $language = null ) {
+ $this->type = $type;
+ $this->language = $language;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string|int $type
+ * @param string|null $language
+ *
+ * @return Highlighter
+ */
+ public static function factory( $type, $language = null ) {
+ if ( $type === '' || !is_int( $type ) ) {
+ $type = self::getTypeId( $type );
+ }
+
+ return new Highlighter( $type, $language );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ * @param string|null $type
+ *
+ * @return booelan
+ */
+ public static function hasHighlighterClass( $text, $type = null ) {
+
+ if ( strpos( $text, 'smw-highlighter' ) === false ) {
+ return false;
+ }
+
+ if ( $type !== null ) {
+ return strpos( $text, 'data-type="' . self::getTypeId( $type ) . '"' ) !== false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public static function decode( $text ) {
+ // #2347, '[' is handled by the MediaWiki parser/sanitizer itself
+ return str_replace(
+ [ '&amp;', '&lt;', '&gt;', '&#160;', '<nowiki>', '</nowiki>' ],
+ [ '&', '<', '>', ' ', '', '' ],
+ $text
+ );
+ }
+
+ /**
+ * Returns html output
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHtml() {
+ SMWOutputs::requireResource( 'ext.smw.tooltips' );
+ return $this->getContainer();
+ }
+
+ /**
+ * Set content
+ *
+ * An external class that invokes content via setContent has to ensure
+ * that all data are appropriately escaped.
+ *
+ * @since 1.9
+ *
+ * @param array $content
+ *
+ * @return array
+ */
+ public function setContent( array $content ) {
+ /**
+ * @var $content
+ * $content['caption'] = a text or null
+ * $content['context'] = a text or null
+ */
+ return $this->options = array_merge( $this->getTypeConfiguration( $this->type ), $content );
+ }
+
+ /**
+ * Returns type id
+ *
+ * @since 1.9
+ *
+ * @param string $type
+ *
+ * @return integer
+ */
+ public static function getTypeId( $type ) {
+ // TODO: why do we have a htmlspecialchars here?!
+ switch ( strtolower ( htmlspecialchars ( $type ) ) ) {
+ case 'property':
+ return self::TYPE_PROPERTY;
+ case 'text':
+ return self::TYPE_TEXT;
+ case 'quantity':
+ return self::TYPE_QUANTITY;
+ case 'warning':
+ return self::TYPE_WARNING;
+ case 'error':
+ return self::TYPE_ERROR;
+ case 'info':
+ return self::TYPE_INFO;
+ case 'help':
+ return self::TYPE_HELP;
+ case 'note':
+ return self::TYPE_NOTE;
+ case 'service':
+ return self::TYPE_SERVICE;
+ case 'reference':
+ return self::TYPE_REFERENCE;
+ default:
+ return self::TYPE_NOTYPE;
+ }
+ }
+
+ /**
+ * Builds Html container
+ *
+ * Content that is being invoked has to be escaped
+ * @see Highlighter::setContent
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ private function getContainer() {
+
+ $captionclass = $this->options['captionclass'];
+
+ // 2.4+ can display context for user-defined properties, here we ensure
+ // to keep the style otherwise it displays italic which is by convention
+ // reserved for predefined properties
+ if ( $this->type === self::TYPE_PROPERTY && isset( $this->options['userDefined'] ) ) {
+ $captionclass = $this->options['userDefined'] ? 'smwtext' : $captionclass;
+ }
+
+ $language = is_string( $this->language ) ? $this->language : Message::USER_LANGUAGE;
+ $style = [];
+
+ if ( isset( $this->options['style'] ) ) {
+ $style = [ 'style' => $this->options['style'] ];
+ }
+
+ // #1875
+ // title attribute contains stripped content to allow for a display in
+ // no-js environments, the tooltip will remove the element once it is
+ // loaded
+ $title = $this->title( $this->options['content'], $language );
+
+ $html = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-highlighter',
+ 'data-type' => $this->options['type'],
+ 'data-content' => isset( $this->options['data-content'] ) ? $this->options['data-content'] : null,
+ 'data-state' => $this->options['state'],
+ 'data-title' => Message::get( $this->options['title'], Message::TEXT, $language ),
+ 'title' => $title
+ ] + $style,
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => $captionclass
+ ],
+ $this->options['caption']
+ ) . Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smwttcontent'
+ ],
+ htmlspecialchars_decode( $this->options['content'] )
+ )
+ );
+
+ return $html;
+ }
+
+ /**
+ * Returns initial configuration settings
+ *
+ * @note You could create a class per entity type but does this
+ * really make sense just to get some configuration parameters?
+ *
+ * @since 1.9
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ private function getTypeConfiguration( $type ) {
+ $settings = [];
+ $settings['type'] = $type;
+ $settings['caption'] = '';
+ $settings['content'] = '';
+
+ switch ( $type ) {
+ case self::TYPE_PROPERTY:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-property';
+ $settings['captionclass'] = 'smwbuiltin';
+ break;
+ case self::TYPE_TEXT:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_QUANTITY:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-quantity';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_NOTE:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-note';
+ $settings['captionclass'] = 'smwtticon note';
+ break;
+ case self::TYPE_WARNING:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-warning';
+ $settings['captionclass'] = 'smwtticon warning';
+ break;
+ case self::TYPE_ERROR:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-error';
+ $settings['captionclass'] = 'smwtticon error';
+ break;
+ case self::TYPE_SERVICE:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-service';
+ $settings['captionclass'] = 'smwtticon service';
+ break;
+ case self::TYPE_REFERENCE:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-reference';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_HELP:
+ case self::TYPE_INFO:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwtticon info';
+ break;
+ case self::TYPE_NOTYPE:
+ default:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwbuiltin';
+ };
+
+ return $settings;
+ }
+
+ private function title( $content, $language ) {
+
+ // Pre-process the content when used as title to avoid breaking elements
+ // (URLs etc.)
+ if ( strpos( $content, '[' ) !== false || strpos( $content, '//' ) !== false ) {
+ $content = Message::get( [ 'smw-parse', $content ], Message::PARSE, $language );
+ }
+
+ return strip_tags( htmlspecialchars_decode( str_replace( [ "[", '&#160;' ], [ "&#91;", ' ' ], $content ) ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php b/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php
new file mode 100644
index 00000000..3c83dd78
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Indicate class aliases in a way PHPStorm and Eclipse understand.
+ * This is purely an IDE helper file, and is not loaded by the extension.
+ *
+ * @since 1.9
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+throw new Exception( 'Not an actual source file' );
+
+class SMWDataItemException extends SMW\Exception\DataItemException {
+}
+
+abstract class SMWStore extends SMW\Store {
+}
+
+class SMWSemanticData extends SMW\SemanticData {
+}
+
+class SMWDIWikiPage extends SMW\DIWikiPage {
+}
+
+class SMWDIConcept extends SMW\DIConcept {
+}
+
+class SMWDIProperty extends SMW\DIProperty {
+}
+
+class SMWDISerializer extends SMW\Serializers\QueryResultSerializer {
+}
+
+class SMWUpdateJob extends SMW\UpdateJob {
+}
+
+class SMWRefreshJob extends SMW\RefreshJob {
+}
+
+abstract class SMWResultPrinter extends SMW\ResultPrinter {
+}
+
+class SMWCategoryResultPrinter extends SMW\Query\ResultPrinters\CategoryResultPrinter {
+}
+
+class SMWDSVResultPrinter extends SMW\DsvResultPrinter {
+}
+
+class SMWEmbeddedResultPrinter extends SMW\EmbeddedResultPrinter {
+}
+
+class SMWRDFResultPrinter extends SMW\RdfResultPrinter {
+}
+
+class SMWListResultPrinter extends SMW\Query\ResultPrinters\ListResultPrinter {
+}
+
+interface SMWIResultPrinter extends SMW\QueryResultPrinter {
+}
+
+class SMWSparqlDatabase4Store extends SMW\SPARQLStore\FourstoreHttpDatabaseConnector {
+}
+
+class SMWSparqlDatabaseVirtuoso extends SMW\SPARQLStore\VirtuosoHttpDatabaseConnector {
+}
+
+class SMWSparqlStore extends SMW\SPARQLStore\SPARQLStore {
+}
+
+class SMWSparqlDatabase extends SMW\SPARQLStore\GenericHttpDatabaseConnector {
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php b/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php
new file mode 100644
index 00000000..3481405e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use SMW\Query\Exception\ResultFormatNotFoundException;
+
+/**
+ * Factory for "result formats", ie classes implementing QueryResultPrinter.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5 (since 1.9, renamed in 2.5)
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class QueryPrinterFactory {
+
+ /**
+ * Returns the global instance of the factory.
+ *
+ * @since 2.5
+ *
+ * @return QueryPrinterFactory
+ */
+ public static function singleton() {
+ static $instance = null;
+
+ if ( $instance === null ) {
+ $instance = self::newFromGlobalState();
+ }
+
+ return $instance;
+ }
+
+ private static function newFromGlobalState() {
+ $instance = new self();
+
+ foreach ( $GLOBALS['smwgResultFormats'] as $formatName => $printerClass ) {
+ $instance->registerFormat( $formatName, $printerClass );
+ }
+
+ foreach ( $GLOBALS['smwgResultAliases'] as $formatName => $aliases ) {
+ $instance->registerAliases( $formatName, $aliases );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Format registry. Format names pointing to their associated QueryResultPrinter implementing classes.
+ *
+ * @var string[]
+ */
+ private $formats = [];
+
+ /**
+ * Form alias registry. Aliases pointing to their canonical format name.
+ *
+ * @var string[]
+ */
+ private $aliases = [];
+
+ /**
+ * Registers a format.
+ * If there is a format already with the provided name,
+ * it will be overridden with the newly provided data.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ * @param string $class
+ *
+ * @throws InvalidArgumentException
+ */
+ public function registerFormat( $formatName, $class ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ if ( !is_string( $class ) ) {
+ throw new InvalidArgumentException( 'Format class names can only be of type string' );
+ }
+
+ $this->formats[$formatName] = $class;
+ }
+
+ /**
+ * Registers the provided format aliases.
+ * If an aliases is already registered, it will
+ * be overridden with the newly provided data.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ * @param array $aliases
+ *
+ * @throws InvalidArgumentException
+ */
+ public function registerAliases( $formatName, array $aliases ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ foreach ( $aliases as $alias ) {
+ if ( !is_string( $alias ) ) {
+ throw new InvalidArgumentException( 'Format aliases can only be of type string' );
+ }
+
+ $this->aliases[$alias] = $formatName;
+ }
+ }
+
+ /**
+ * Returns the canonical format names.
+ *
+ * @since 2.5
+ *
+ * @return string[]
+ */
+ public function getFormats() {
+ return array_keys( $this->formats );
+ }
+
+ /**
+ * Returns if there is a format or format alias with the provided name.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return boolean
+ */
+ public function hasFormat( $formatName ) {
+ $formatName = $this->getCanonicalName( $formatName );
+ return array_key_exists( $formatName, $this->formats );
+ }
+
+ /**
+ * Returns a new instance of the handling result printer for the provided format.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ *
+ * @return QueryResultPrinter
+ * @throws ResultFormatNotFoundException
+ */
+ public function getPrinter( $formatName ) {
+ $class = $this->getPrinterClass( $formatName );
+ return new $class( $formatName );
+ }
+
+ /**
+ * Returns the QueryResultPrinter implementing class that is the printer for the provided format.
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return string
+ * @throws ResultFormatNotFoundException
+ */
+ private function getPrinterClass( $formatName ) {
+ $formatName = $this->getCanonicalName( $formatName );
+
+ if ( !array_key_exists( $formatName, $this->formats ) ) {
+ throw new ResultFormatNotFoundException( 'Unknown format name "' . $formatName . '" has no associated printer class' );
+ }
+
+ return $this->formats[$formatName];
+ }
+
+ /**
+ * Resolves format aliases into their associated canonical format name.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function getCanonicalName( $formatName ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ if ( array_key_exists( $formatName, $this->aliases ) ) {
+ $formatName = $this->aliases[$formatName];
+ }
+
+ return $formatName;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php b/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php
new file mode 100644
index 00000000..1a171fec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php
@@ -0,0 +1,374 @@
+<?php
+
+namespace SMW;
+
+use SMWDITime;
+
+/**
+ * TODO This class needs some real refactoring!
+ *
+ * @private This class should not be instantiated directly.
+ *
+ * This class determines recurring events based on invoked parameters
+ *
+ * @see https://www.semantic-mediawiki.org/wiki/Help:Recurring_events
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Yaron Koren
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class RecurringEvents {
+
+ /**
+ * Defines the property used
+ */
+ private $property = null;
+
+ /**
+ * Defines the dates
+ */
+ private $dates = [];
+
+ /**
+ * Defines remaining / unused parameters
+ */
+ private $parameters = [];
+
+ /**
+ * Defines errors
+ */
+ private $errors = [];
+
+ /**
+ * @var integer
+ */
+ private $defaultNumRecurringEvents = 25;
+
+ /**
+ * @var integer
+ */
+ private $maxNumRecurringEvents = 25;
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $defaultNumRecurringEvents
+ */
+ public function setDefaultNumRecurringEvents( $defaultNumRecurringEvents ) {
+ $this->defaultNumRecurringEvents = $defaultNumRecurringEvents;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $maxNumRecurringEvents
+ */
+ public function setMaxNumRecurringEvents( $maxNumRecurringEvents ) {
+ $this->maxNumRecurringEvents = $maxNumRecurringEvents;
+ }
+
+ /**
+ * Returns property used
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * Returns dates
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getDates() {
+ return $this->dates;
+ }
+
+ /**
+ * Returns unused parameters
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns errors
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Set error
+ *
+ * @since 1.9
+ *
+ * @return mixed
+ */
+ private function setError( $error ) {
+ $this->errors = array_merge( $error, $this->errors );
+ }
+
+ /**
+ * Returns the "Julian day" value from an object of type
+ * SMWTimeValue.
+ */
+ public function getJulianDay( $dateDataValue ) {
+ if ( is_null( $dateDataValue ) ) {
+ return null;
+ }
+ $dateDataItem = $dateDataValue->getDataItem();
+ // This might have returned an 'SMWDIError' object.
+ if ( $dateDataItem instanceof SMWDITime ) {
+ return $dateDataItem->getJD();
+ }
+ return null;
+ }
+
+ /**
+ * Parse parameters and set internal properties
+ *
+ * @since 1.9
+ *
+ * @param array $parameters
+ */
+ public function parse( array $parameters ) {
+ // Initialize variables.
+ $all_date_strings = [];
+ $start_date = $end_date = $unit = $period = $week_num = null;
+ $included_dates = [];
+ $excluded_dates = [];
+ $excluded_dates_jd = [];
+
+ // Parse parameters and assign values
+ foreach ( $parameters as $name => $values ) {
+
+ foreach ( $values as $value ) {
+ switch( $name ) {
+ case 'property':
+ $this->property = $value;
+ break;
+ case 'start':
+ $start_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $value );
+ break;
+ case 'end':
+ $end_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $value );
+ break;
+ case 'limit':
+ // Override default limit with query specific limit
+ $this->defaultNumRecurringEvents = (int)$value;
+ break;
+ case 'unit':
+ $unit = $value;
+ break;
+ case 'period':
+ $period = (int)$value;
+ break;
+ case 'week number':
+ $week_num = (int)$value;
+ break;
+ case 'include':
+ // This is for compatibility only otherwise we break
+ // to much at once. Instead of writing include=...;...
+ // it should be include=...;...|+sep=; because the
+ // ParameterParser class is conditioned to split those
+ // parameter accordingly
+ if ( strpos( $value, ';' ) ){
+ $included_dates = explode( ';', $value );
+ } else {
+ $included_dates[] = $value;
+ }
+ break;
+ case 'exclude':
+ // Some as above
+ if ( strpos( $value, ';' ) ){
+ $excluded_dates = explode( ';', $value );
+ } else {
+ $excluded_dates[] = $value;
+ }
+ break;
+ default:
+ $this->parameters[$name][] = $value;
+ }
+ }
+ }
+
+ if ( $start_date === null ) {
+ $this->errors[] = Message::get( 'smw-events-start-date-missing' );
+ return;
+ } elseif ( !( $start_date->getDataItem() instanceof SMWDITime ) ) {
+ $this->setError( $start_date->getErrors() );
+ return;
+ }
+
+ // Check property
+ if ( is_null( $this->property ) ) {
+ $this->errors[] = Message::get( 'smw-events-property-missing' );
+ return;
+ }
+
+ // Exclude dates
+ foreach ( $excluded_dates as $date_str ) {
+ $excluded_dates_jd[] = $this->getJulianDay(
+ DataValueFactory::getInstance()->newTypeIDValue( '_dat', $date_str )
+ );
+ }
+
+ // If the period is null, or outside of normal bounds, set it to 1.
+ if ( is_null( $period ) || $period < 1 || $period > 500 ) {
+ $period = 1;
+ }
+
+ // Handle 'week number', but only if it's of unit 'month'.
+ if ( $unit == 'month' && ! is_null( $week_num ) ) {
+ $unit = 'dayofweekinmonth';
+
+ if ( $week_num < -4 || $week_num > 5 || $week_num == 0 ) {
+ $week_num = null;
+ }
+ }
+
+ if ( $unit == 'dayofweekinmonth' && is_null( $week_num ) ) {
+ $week_num = ceil( $start_date->getDay() / 7 );
+ }
+
+ // Get the Julian day value for both the start and end date.
+ $end_date_jd = $this->getJulianDay( $end_date );
+
+ $cur_date = $start_date;
+ $cur_date_jd = $this->getJulianDay( $cur_date );
+ $i = 0;
+
+ do {
+ $i++;
+ $exclude_date = ( in_array( $cur_date_jd, $excluded_dates_jd ) );
+
+ if ( !$exclude_date ) {
+ $all_date_strings[] = $cur_date->getLongWikiText();
+ }
+
+ // Now get the next date.
+ // Handling is different depending on whether it's
+ // month/year or week/day since the latter is a set
+ // number of days while the former isn't.
+ if ( $unit === 'year' || $unit == 'month' ) {
+ $cur_year = $cur_date->getYear();
+ $cur_month = $cur_date->getMonth();
+ $cur_day = $start_date->getDay();
+ $cur_time = $cur_date->getTimeString();
+
+ if ( $unit == 'year' ) {
+ $cur_year += $period;
+ $display_month = $cur_month;
+ } else { // $unit === 'month'
+ $cur_month += $period;
+ $cur_year += (int)( ( $cur_month - 1 ) / 12 );
+ $cur_month %= 12;
+ $display_month = ( $cur_month == 0 ) ? 12 : $cur_month;
+ }
+
+ // If the date is greater than 28 for February, and it is not
+ // a leap year, change it to be a fixed 28 otherwise set it to
+ // 29 (for a leap year date)
+ if ( $cur_month == 2 && $cur_day > 28 ) {
+ $cur_day = !date( 'L', strtotime( "$cur_year-1-1" ) ) ? 28 : 29;
+ } elseif ( $cur_day > 30 ) {
+ // Check whether 31 is a valid day of a month
+ $cur_day = ( $display_month - 1 ) % 7 % 2 ? 30 : 31;
+ }
+
+ $date_str = "$cur_year-$display_month-$cur_day $cur_time";
+ $cur_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $date_str );
+ $all_date_strings = array_merge( $all_date_strings, $included_dates);
+ if ( $cur_date->isValid() ) {
+ $cur_date_jd = $cur_date->getDataItem()->getJD();
+ }
+ } elseif ( $unit == 'dayofweekinmonth' ) {
+ // e.g., "3rd Monday of every month"
+ $prev_month = $cur_date->getMonth();
+ $prev_year = $cur_date->getYear();
+
+ $new_month = ( $prev_month + $period ) % 12;
+ if ( $new_month == 0 ) {
+ $new_month = 12;
+ }
+
+ $new_year = $prev_year + floor( ( $prev_month + $period - 1 ) / 12 );
+ $cur_date_jd += ( 28 * $period ) - 7;
+
+ // We're sometime before the actual date now -
+ // keep incrementing by a week, until we get there.
+ do {
+ $cur_date_jd += 7;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ $right_month = ( $cur_date->getMonth() == $new_month );
+
+ if ( $week_num < 0 ) {
+ $next_week_jd = $cur_date_jd;
+
+ do {
+ $next_week_jd += 7;
+ $next_week_date = $this->getJulianDayTimeValue( $next_week_jd );
+ $right_week = ( $next_week_date->getMonth() != $new_month ) || ( $next_week_date->getYear() != $new_year );
+ } while ( !$right_week );
+
+ $cur_date_jd = $next_week_jd + ( 7 * $week_num );
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ } else {
+ $cur_week_num = ceil( $cur_date->getDay() / 7 );
+ $right_week = ( $cur_week_num == $week_num );
+
+ if ( $week_num == 5 && ( $cur_date->getMonth() % 12 == ( $new_month + 1 ) % 12 ) ) {
+ $cur_date_jd -= 7;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ $right_month = $right_week = true;
+ }
+ }
+ } while ( !$right_month || !$right_week);
+ } else { // $unit == 'day' or 'week'
+ // Assume 'day' if it's none of the above.
+ $cur_date_jd += ( $unit === 'week' ) ? 7 * $period : $period;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ }
+
+ // should we stop?
+ if ( is_null( $end_date ) ) {
+ $reached_end_date = $i > $this->defaultNumRecurringEvents;
+ } else {
+ $reached_end_date = ( $cur_date_jd > $end_date_jd ) || ( $i > $this->maxNumRecurringEvents );
+ }
+ } while ( !$reached_end_date );
+
+ // Handle the 'include' dates as well.
+ $all_date_strings = array_filter( array_merge( $all_date_strings, $included_dates ) );
+
+ // Set dates
+ $this->dates = str_replace( ' 00:00:00', '', $all_date_strings );
+ }
+
+ /**
+ * Helper function - creates an object of type SMWTimeValue based
+ * on a "Julian day" integer
+ */
+ private function getJulianDayTimeValue( $jd ) {
+ $timeDataItem = SMWDITime::newFromJD( $jd, SMWDITime::CM_GREGORIAN, SMWDITime::PREC_YMDT );
+ return DataValueFactory::getInstance()->newDataValueByItem( $timeDataItem );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php
new file mode 100644
index 00000000..18a37b30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php
@@ -0,0 +1,666 @@
+<?php
+
+use SMW\Site;
+
+/**
+ * This class mainly is a container to store URLs for the factbox in a
+ * clean way. The class provides methods for creating source code for
+ * realising them in wiki or html contexts.
+ *
+ * @license GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class SMWInfolink {
+
+ const LINK_UPPER_LENGTH_RESTRICTION = 2000;
+
+ /**
+ * The actual link target.
+ *
+ * @var string
+ */
+ protected $mTarget;
+
+ /**
+ * The label for the link.
+ *
+ * @var string
+ */
+ protected $mCaption;
+
+ /**
+ * CSS class of a span to embedd the link into,
+ * or false if no extra style is required.
+ *
+ * @var mixed
+ */
+ protected $mStyle;
+
+ /**
+ * @var array
+ */
+ private $linkAttributes = [];
+
+ /**
+ * Indicates whether $target is a page name (true) or URL (false).
+ *
+ * @var boolean
+ */
+ protected $mInternal;
+
+ /**
+ * Array of parameters, format $name => $value, if any.
+ *
+ * @var array
+ */
+ protected $mParams;
+
+ /**
+ * @var boolean
+ */
+ private $isRestricted = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCompactLink = false;
+
+ /**
+ * Create a new link to some internal page or to some external URL.
+ *
+ * @param boolean $internal Indicates whether $target is a page name (true) or URL (false).
+ * @param string $caption The label for the link.
+ * @param string $target The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ */
+ public function __construct( $internal, $caption, $target, $style = false, array $params = [] ) {
+ $this->mInternal = $internal;
+ $this->mCaption = $caption;
+ $this->mTarget = $target;
+ $this->mStyle = $style;
+ $this->mParams = $params;
+ $this->setCompactLink( $GLOBALS['smwgCompactLinkSupport'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isRestricted
+ */
+ public function isRestricted( $isRestricted ) {
+ $this->isRestricted = (bool)$isRestricted;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isCompactLink
+ */
+ public function setCompactLink( $isCompactLink = true ) {
+ $this->isCompactLink = (bool)$isCompactLink;
+ }
+
+ /**
+ * Create a new link to an internal page $target.
+ * All parameters are mere strings as used by wiki users.
+ *
+ * @param string $caption The label for the link.
+ * @param string $target The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ *
+ * @return SMWInfolink
+ */
+ public static function newInternalLink( $caption, $target, $style = false, array $params = [] ) {
+ return new SMWInfolink( true, $caption, $target, $style, $params );
+ }
+
+ /**
+ * Create a new link to an external location $url.
+ *
+ * @param string $caption The label for the link.
+ * @param string $url The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ *
+ * @return SMWInfolink
+ */
+ public static function newExternalLink( $caption, $url, $style = false, array $params = [] ) {
+ return new SMWInfolink( false, $caption, $url, $style, $params );
+ }
+
+ /**
+ * Static function to construct links to property searches.
+ *
+ * @param string $caption The label for the link.
+ * @param string $propertyName
+ * @param string $propertyValue
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newPropertySearchLink( $caption, $propertyName, $propertyValue, $style = 'smwsearch' ) {
+ global $wgContLang;
+
+ $infolink = new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':SearchByProperty',
+ $style,
+ [ ':' . $propertyName, $propertyValue ] // `:` is marking that the link was auto-generated
+ );
+
+ // Link that reaches a length restriction will most likely cause a
+ // "HTTP 414 "Request URI too long ..." therefore prevent a link creation
+ if ( mb_strlen( $propertyName . $propertyValue ) > self::LINK_UPPER_LENGTH_RESTRICTION ) {
+ $infolink->isRestricted( true );
+ }
+
+ return $infolink;
+ }
+
+ /**
+ * Static function to construct links to inverse property searches.
+ *
+ * @param string $caption The label for the link.
+ * @param string $subject
+ * @param string $propertyName
+ * @param mixed $style CSS class of a span to embed the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newInversePropertySearchLink( $caption, $subject, $propertyname, $style = false ) {
+ global $wgContLang;
+ return new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':PageProperty',
+ $style,
+ [ $subject . '::' . $propertyname ]
+ );
+ }
+
+ /**
+ * Static function to construct links to the browsing special.
+ *
+ * @param string $caption The label for the link.
+ * @param string $titleText
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newBrowsingLink( $caption, $titleText, $style = 'smwbrowse' ) {
+ global $wgContLang;
+ return new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':Browse',
+ $style,
+ [ ':' . $titleText ]
+ );
+ }
+
+ /**
+ * Set (or add) parameter values for an existing link.
+ *
+ * @param mixed $value
+ * @param mixed $key
+ */
+ public function setParameter( $value, $key = false ) {
+ if ( $key === false ) {
+ $this->mParams[] = $value;
+ } else {
+ $this->mParams[$key] = $value;
+ }
+ }
+
+ /**
+ * Get the value of some named parameter, or null if no parameter of
+ * that name exists.
+ */
+ public function getParameter( $key ) {
+ if ( array_key_exists( $key, $this->mParams ) ) {
+ return $this->mParams[$key];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Change the link text.
+ */
+ public function setCaption( $caption ) {
+ $this->mCaption = $caption;
+ }
+
+ /**
+ * Change the link's CSS class.
+ */
+ public function setStyle( $style ) {
+ $this->mStyle = $style;
+ }
+
+ /**
+ * Modify link attributes
+ *
+ * @since 3.0
+ *
+ * @param array $linkAttributes
+ */
+ public function setLinkAttributes( array $linkAttributes ) {
+ $this->linkAttributes = $linkAttributes;
+ }
+
+ /**
+ * Returns a suitable text string for displaying this link in HTML or wiki, depending
+ * on whether $outputformat is SMW_OUTPUT_WIKI or SMW_OUTPUT_HTML.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output). Some default linker will be created
+ * if needed and not provided.
+ */
+ public function getText( $outputformat, $linker = null ) {
+
+ if ( $this->isRestricted ) {
+ return '';
+ }
+
+ if ( $this->mStyle !== false ) {
+ SMWOutputs::requireResource( 'ext.smw.style' );
+ $start = "<span class=\"$this->mStyle\">";
+ $end = '</span>';
+ } else {
+ $start = '';
+ $end = '';
+ }
+
+ if ( $this->mInternal ) {
+ if ( count( $this->mParams ) > 0 ) {
+
+ $query = self::encodeParameters( $this->mParams );
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query );
+ }
+
+ $titletext = $this->mTarget . '/' . $query;
+ } else {
+ $titletext = $this->mTarget;
+ }
+
+ $title = Title::newFromText( $titletext );
+
+ if ( $title !== null ) {
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+ $link = "[[$titletext|$this->mCaption]]";
+ } elseif ( $outputformat == SMW_OUTPUT_RAW ) {
+ return $this->getURL();
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+ $link = $this->getLinker( $linker )->link( $title, $this->mCaption, $this->linkAttributes );
+ }
+ } else {
+ // Title creation failed, maybe illegal symbols or too long; make
+ // a direct URL link (only possible if offending target parts belong
+ // to some parameter that can be separated from title text, e.g.
+ // as in Special:Bla/il<leg>al -> Special:Bla&p=il&lt;leg&gt;al)
+ $title = Title::newFromText( $this->mTarget );
+
+ // Just give up due to the title being bad, normally this would
+ // indicate a software bug
+ if ( $title === null ) {
+ return '';
+ }
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query, false );
+ }
+
+ $link = '[' . $title->getFullURL( $query ) . " $this->mCaption]";
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query, true );
+ } else {
+ // #511, requires an array
+ $query = wfCgiToArray( $query );
+ }
+
+ $link = $this->getLinker( $linker )->link(
+ $title,
+ $this->mCaption,
+ $this->linkAttributes,
+ $query
+ );
+ }
+ }
+ } else {
+ $target = $this->getURL();
+
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+ $link = "[$target $this->mCaption]";
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+ $link = '<a href="' . htmlspecialchars( $target ) . "\">$this->mCaption</a>";
+ }
+ }
+
+ return $start . $link . $end;
+ }
+
+ /**
+ * Return hyperlink for this infolink in HTML format.
+ *
+ * @return string
+ */
+ public function getHTML( $linker = null ) {
+ return $this->getText( SMW_OUTPUT_HTML, $linker );
+ }
+
+ /**
+ * Return hyperlink for this infolink in wiki format.
+ *
+ * @return string
+ */
+ public function getWikiText( $linker = null ) {
+ return $this->getText( SMW_OUTPUT_WIKI, $linker );
+ }
+
+ /**
+ * Return a fully qualified URL that points to the link target (whether internal or not).
+ * This function might be used when the URL is needed outside normal links, e.g. in the HTML
+ * header or in some metadata file. For making normal links, getText() should be used.
+ *
+ * @return string
+ */
+ public function getURL() {
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $this->isCompactLink && $query !== '' ) {
+ $query = self::encodeCompactLink( $query, true );
+ }
+
+ if ( !$this->mInternal ) {
+ return $this->buildTarget( $query );
+ }
+
+ $title = Title::newFromText( $this->mTarget );
+
+ if ( $title !== null ) {
+ return $title->getFullURL( $query );
+ }
+
+ // the title was bad, normally this would indicate a software bug
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getLocalURL() {
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $this->isCompactLink && $query !== '' ) {
+ $query = self::encodeCompactLink( $query, true );
+ }
+
+ if ( !$this->mInternal ) {
+ return $this->buildTarget( $query );
+ }
+
+ $title = Title::newFromText( $this->mTarget );
+
+ if ( $title !== null ) {
+ return $title->getLocalURL( $query );
+ }
+
+ // the title was bad, normally this would indicate a software bug
+ return '';
+ }
+
+ /**
+ * Return a Linker object, using the parameter $linker if not null, and creatng a new one
+ * otherwise. $linker is usually a user skin object, while the fallback linker object is
+ * not customised to user settings.
+ *
+ * @return Linker
+ */
+ protected function getLinker( &$linker = null ) {
+ if ( is_null( $linker ) ) {
+ $linker = new Linker;
+ }
+ return $linker;
+ }
+
+ /**
+ * Encode an array of parameters, formatted as $name => $value, to a parameter
+ * string that can be used for linking. If $forTitle is true (default), then the
+ * parameters are encoded for use in a MediaWiki page title (useful for making
+ * internal links to parameterised special pages), otherwise the parameters are
+ * encoded HTTP GET style. The parameter name "x" is used to collect parameters
+ * that do not have any string keys in GET, and hence "x" should never be used
+ * as a parameter name.
+ *
+ * The function SMWInfolink::decodeParameters() can be used to undo this encoding.
+ * It is strongly recommended to not create any code that depends on the concrete
+ * way of how parameters are encoded within this function, and to always use the
+ * respective encoding/decoding methods instead.
+ *
+ * @param array $params
+ * @param boolean $forTitle
+ */
+ static public function encodeParameters( array $params, $forTitle = true ) {
+ $result = '';
+
+ if ( $forTitle ) {
+ foreach ( $params as $name => $value ) {
+ if ( is_string( $name ) && ( $name !== '' ) ) {
+ $value = $name . '=' . $value;
+ }
+ // Escape certain problematic values. Use SMW-escape
+ // (like URLencode but - instead of % to prevent double encoding by later MW actions)
+ //
+ /// : SMW's parameter separator, must not occur within params
+ // - : used in SMW-encoding strings, needs escaping too
+ // [ ] < > &lt; &gt; '' |: problematic in MW titles
+ // & : sometimes problematic in MW titles ([[&amp;]] is OK, [[&test]] is OK, [[&test;]] is not OK)
+ // (Note: '&' in strings obtained during parsing already has &entities; replaced by
+ // UTF8 anyway)
+ // ' ': are equivalent with '_' in MW titles, but are not equivalent in certain parameter values
+ // "\n": real breaks not possible in [[...]]
+ // "#": has special meaning in URLs, triggers additional MW escapes (using . for %)
+ // '%': must be escaped to prevent any impact of double decoding when replacing -
+ // by % before urldecode
+ // '?': if not escaped, strange effects were observed on some sites (printout and other
+ // parameters ignored without obvious cause); SMW-escaping is always save to do -- it just
+ // make URLs less readable
+ //
+ $value = str_replace(
+ [ '-', '#', "\n", ' ', '/', '[', ']', '<', '>', '&lt;', '&gt;', '&amp;', '\'\'', '|', '&', '%', '?', '$', "\\", ";", "_" ],
+ [ '-2D', '-23', '-0A', '-20', '-2F', '-5B', '-5D', '-3C', '-3E', '-3C', '-3E', '-26', '-27-27', '-7C', '-26', '-25', '-3F', '-24', '-5C', "-3B", "-5F" ],
+ $value
+ );
+
+ if ( $result !== '' ) {
+ $result .= '/';
+ }
+
+ $result .= $value;
+ }
+ } else { // Note: this requires to have HTTP compatible parameter names (ASCII)
+ $q = []; // collect unlabelled query parameters here
+
+ foreach ( $params as $name => $value ) {
+ if ( is_string( $name ) && ( $name !== '' ) ) {
+ $value = rawurlencode( $name ) . '=' . rawurlencode( $value );
+
+ if ( $result !== '' ) {
+ $result .= '&';
+ }
+
+ $result .= $value;
+ } else {
+ $q[] = $value;
+ }
+ }
+ if ( count( $q ) > 0 ) { // prepend encoding for unlabelled parameters
+ if ( $result !== '' ) {
+ $result = '&' . $result;
+ }
+ $result = 'x=' . rawurlencode( self::encodeParameters( $q, true ) ) . $result;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Obtain an array of parameters from the parameters given to some HTTP service.
+ * In particular, this function performs all necessary decoding as may be needed, e.g.,
+ * to recover the proper parameter strings after encoding for use in wiki title names
+ * as done by SMWInfolink::encodeParameters().
+ *
+ * If $allparams is set to true, it is assumed that further data should be obtained
+ * from the global $wgRequest, and all given parameters are read.
+ *
+ * $titleparam is the string extracted by MediaWiki from special page calls of the
+ * form Special:Something/titleparam
+ * Note: it is assumed that the given $titleparam is already urldecoded, as is normal
+ * when getting such parameters from MediaWiki. SMW-escaped parameters largely prevent
+ * double decoding effects (i.e. there are no new "%" after one pass of urldecoding)
+ *
+ * The function SMWInfolink::encodeParameters() can be used to create a suitable
+ * encoding. It is strongly recommended to not create any code that depends on the
+ * concrete way of how parameters are encoded within this function, and to always use
+ * the respective encoding/decoding methods instead.
+ *
+ * @param string $titleParam
+ * @param boolean $allParams
+ */
+ static public function decodeParameters( $titleParam = '', $allParams = false ) {
+ global $wgRequest;
+
+ $result = [];
+
+ if ( $allParams ) {
+ $result = $wgRequest->getValues();
+
+ if ( array_key_exists( 'x', $result ) ) { // Considered to be part of the title param.
+ if ( $titleParam !== '' ) {
+ $titleParam .= '/';
+ }
+ $titleParam .= $result['x'];
+ unset( $result['x'] );
+ }
+ }
+
+ if ( is_array( $titleParam ) ) {
+ return $titleParam;
+ } elseif ( $titleParam !== '' ) {
+ // unescape $p; escaping scheme: all parameters rawurlencoded, "-" and "/" urlencoded, all "%" replaced by "-", parameters then joined with /
+ $ps = explode( '/', $titleParam ); // params separated by / here (compatible with wiki link syntax)
+
+ foreach ( $ps as $p ) {
+ if ( $p !== '' ) {
+ $result[] = rawurldecode( str_replace( '-', '%', $p ) );
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return string|array
+ */
+ public static function encodeCompactLink( $value, $compound = false ) {
+
+ // Expect to gain on larger strings and set an identifier to
+ // distinguish between compressed and non compressed
+ if ( mb_strlen( $value ) > 150 ) {
+ $value = 'c:' . gzdeflate( $value, 9 );
+ }
+
+ // https://en.wikipedia.org/wiki/Base64#URL_applications
+ // The MW parser swallows `__` and transforms it into a simple `_`
+ // hence we need to encode it once more
+ $value = rtrim( str_replace( '__', '.', strtr( base64_encode( $value ), '+/', '-_' ) ), '=' );
+
+ if ( $compound ) {
+ return [ 'cl' => $value ];
+ }
+
+ return "cl:$value";
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function decodeCompactLink( $value ) {
+
+ if ( !is_string( $value ) || mb_substr( $value, 0, 3 ) !== 'cl:' ) {
+ return $value;
+ }
+
+ $value = mb_substr( $value, 3 );
+
+ $value = base64_decode(
+ str_pad( strtr( str_replace( '.', '__', $value ), '-_', '+/' ), strlen( $value ) % 4, '=', STR_PAD_RIGHT )
+ );
+
+ // Compressed?
+ if ( mb_substr( $value, 0, 2 ) === 'c:' ) {
+ $val = @gzinflate( mb_substr( $value, 2 ) );
+
+ // Guessing that MediaWiki swallowed the last `_`
+ if ( $val === false ) {
+ $val = @gzinflate( mb_substr( $value , 2 ) . '?' );
+ }
+
+ $value = $val;
+ }
+
+ // Normalize if nceessary for those that are "encoded for use in a
+ // MediaWiki page title"
+ if ( mb_substr( $value, 0, 2 ) === 'x=' ) {
+ $value = str_replace( [ 'x=', '=-&' , '&', '%2F' ], [ '' , '=-2D&', '/', '/' ], $value );
+ }
+
+ return $value;
+ }
+
+ private function buildTarget( $query ) {
+
+ $target = $this->mTarget;
+
+ if ( count( $this->mParams ) > 0 ) {
+ if ( strpos( Site::wikiurl(), '?' ) === false ) {
+ $target = $this->mTarget . '?' . $query;
+ } else {
+ $target = $this->mTarget . '&' . $query;
+ }
+ }
+
+ return $target;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php
new file mode 100644
index 00000000..2d2c1e4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * This class attempts to provide safe yet simple means for managing data that is relevant
+ * for the final HTML output of MediaWiki. In particular, this concerns additions to the HTML
+ * header in the form of scripts of stylesheets.
+ *
+ * The problem is that many components in SMW create hypertext that should eventually be displayed.
+ * The normal way of accessing such text are functions of the form getText() which return a
+ * (hypertext) string. Such a string, however, might refer to styles or scripts that are also
+ * needed. It is not possible to directly add those scripts to the MediaWiki output, since the form
+ * of this output depends on the context in which the function is called. Many functions might be
+ * called both during parsing and directly in special pages that do not use the usual parsing and
+ * caching mechanisms.
+ *
+ * Ideally, all functions that generate hypertext with dependencies would also include parameters to
+ * record required scripts. Since this would require major API changes, the current solution is to have
+ * a "temporal" global storage for the required items, managed in this class. It is not safe to use
+ * such a global store across hooks -- you never know what happens in between! Hence, every function
+ * that creates SMW outputs that may require head items must afterwards clear the temporal store by
+ * writing its contents to the according output.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+class SMWOutputs {
+
+ /**
+ * Protected member for temporarily storing header items.
+ * Format $id => $headItem where $id is used only to avoid duplicate
+ * items in the time before they are forwarded to the output.
+ */
+ protected static $headItems = [];
+
+ /**
+ * Protected member for temporarily storing additional Javascript
+ * snippets. Format $id => $scriptText where $id is used only to
+ * avoid duplicate scripts in the time before they are forwarded
+ * to the output.
+ */
+ protected static $scripts = [];
+
+ /**
+ * Protected member for temporarily storing resource modules.
+ *
+ * @var array
+ */
+ protected static $resourceModules = [];
+
+ /**
+ * Protected member for temporarily storing resource modules.
+ *
+ * @var array
+ */
+ protected static $resourceStyles = [];
+
+ /**
+ * Adds a resource module to the parser output.
+ *
+ * @since 1.5.3
+ *
+ * @param string $moduleName
+ */
+ public static function requireResource( $moduleName ) {
+ self::$resourceModules[$moduleName] = $moduleName;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $stylesName
+ */
+ public static function requireStyle( $stylesName ) {
+ self::$resourceStyles[$stylesName] = $stylesName;
+ }
+
+ /**
+ * Require the presence of header scripts, provided as strings with
+ * enclosing script tags. Note that the same could be achieved with
+ * requireHeadItems, but scripts use a special method "addScript" in
+ * MediaWiki OutputPage, hence we distinguish them.
+ *
+ * The id is used to avoid that the requirement for one script is
+ * recorded multiple times in SMWOutputs.
+ *
+ * @param string $id
+ * @param string $item
+ */
+ public static function requireScript( $id, $script ) {
+ self::$scripts[$id] = $script;
+ }
+
+ /**
+ * Adds head items that are not Resource Loader modules. Should only
+ * be used for custom head items such as RSS fedd links.
+ *
+ * The id is used to avoid that the requirement for one script is
+ * recorded multiple times in SMWOutputs.
+ *
+ * Support for calling this with the old constants SMW_HEADER_STYLE
+ * and SMW_HEADER_TOOLTIP will vanish in SMW 1.7 at the latest.
+ *
+ * @param mixed $id
+ * @param string $item
+ */
+ public static function requireHeadItem( $id, $item = '' ) {
+ if ( is_numeric( $id ) ) {
+ switch ( $id ) {
+ case SMW_HEADER_TOOLTIP:
+ self::requireResource( 'ext.smw.tooltips' );
+ break;
+ case SMW_HEADER_STYLE:
+ self::requireStyle( 'ext.smw.style' );
+ break;
+ }
+ } else {
+ self::$headItems[$id] = $item;
+ }
+ }
+
+ /**
+ * This function takes output requirements as can be found in a given ParserOutput
+ * object and puts them back in to the internal temporal requirement list from
+ * which they can be committed to some other output. It is needed when code that
+ * would normally call SMWOutputs::requireHeadItem() has need to use a full
+ * independent parser call (Parser::parse()) that produces its own parseroutput.
+ * If omitted, all output items potentially committed to this parseroutput during
+ * parsing will not be passed on to higher levels.
+ *
+ * Note that this is not required if the $parseroutput is further processed by
+ * MediaWiki, but there are cases where the output is discarded and only its text
+ * is used.
+ *
+ * @param ParserOutput $parserOutput
+ */
+ static public function requireFromParserOutput( ParserOutput $parserOutput ) {
+ // Note: we do not attempt to recover which head items where scripts here.
+
+ $parserOutputHeadItems = $parserOutput->getHeadItems();
+
+ self::$headItems = array_merge( (array)self::$headItems, $parserOutputHeadItems );
+
+ /// TODO Is the following needed?
+ if ( isset( $parserOutput->mModules ) ) {
+ foreach ( $parserOutput->mModules as $module ) {
+ self::$resourceModules[$module] = $module;
+ }
+ }
+ }
+
+ /**
+ * Actually commit the collected requirements to a given parser that is about to parse
+ * what will later be the HTML output. This makes sure that HTML output based on the
+ * parser results contains all required output items.
+ *
+ * If the parser creates output for a normal wiki page, then the committed items will
+ * also become part of the page cache so that they will correctly be added to all page
+ * outputs built from this cache later on.
+ *
+ * @param Parser $parser
+ */
+ static public function commitToParser( Parser $parser ) {
+ /// TODO find out and document when this b/c code can go away
+ if ( method_exists( $parser, 'getOutput' ) ) {
+ $po = $parser->getOutput();
+ } else {
+ $po = $parser->mOutput;
+ }
+
+ if ( isset( $po ) ) {
+ self::commitToParserOutput( $po );
+ }
+ }
+
+ /**
+ * Similar to SMWOutputs::commitToParser() but acting on a ParserOutput object.
+ *
+ * @param ParserOutput $parserOutput
+ */
+ static public function commitToParserOutput( ParserOutput $parserOutput ) {
+
+ foreach ( self::$scripts as $key => $script ) {
+ $parserOutput->addHeadItem( $script . "\n", $key );
+ }
+
+ foreach ( self::$headItems as $key => $item ) {
+ $parserOutput->addHeadItem( "\t\t" . $item . "\n", $key );
+ }
+
+ $parserOutput->addModuleStyles( array_values( self::$resourceStyles ) );
+ $parserOutput->addModules( array_values( self::$resourceModules ) );
+
+ self::$resourceStyles = [];
+ self::$resourceModules = [];
+ self::$headItems = [];
+ }
+
+ /**
+ * Actually commit the collected requirements to a given OutputPage object that
+ * will later generate the HTML output. This makes sure that HTML output contains
+ * all required output items. Note that there is no parser caching at this level of
+ * processing. In particular, data should not be committed to $wgOut in methods
+ * that run during page parsing, since these would not run next time when the page
+ * is produced from parser cache.
+ *
+ * @param OutputPage $output
+ */
+ static public function commitToOutputPage( OutputPage $output ) {
+ foreach ( self::$scripts as $script ) {
+ $output->addScript( $script );
+ }
+ foreach ( self::$headItems as $key => $item ) {
+ $output->addHeadItem( $key, "\t\t" . $item . "\n" );
+ }
+
+ $output->addModuleStyles( array_values( self::$resourceStyles ) );
+ $output->addModules( array_values( self::$resourceModules ) );
+
+ self::$resourceStyles = [];
+ self::$resourceModules = [];
+ self::$headItems = [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php
new file mode 100644
index 00000000..9e40d1fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php
@@ -0,0 +1,355 @@
+<?php
+
+use SMW\Query\PrintRequest;
+
+/**
+ * Helper class to generate HTML lists of wiki pages, with support for paged
+ * navigation using the from/until and limit settings as in MediaWiki's
+ * CategoryPage.
+ *
+ * The class attempts to allow as much code as possible to be shared among
+ * different places where similar lists are used.
+ *
+ * Some code adapted from CategoryPage.php
+ *
+ * @ingroup SMW
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SMWPageLister {
+
+ protected $mDiWikiPages;
+ protected $mDiProperty;
+ protected $mLimit;
+ protected $mFrom;
+ protected $mUntil;
+
+ /**
+ * Constructor
+ *
+ * @param $diWikiPages array of SMWDIWikiPage
+ * @param $diProperty mixed SMWDIProperty that the wikipages are values of, or null
+ * @param $limit integer maximal amount of items to display
+ * @param $from string if the results were selected starting from this string
+ * @param $until string if the results were selected reaching until this string
+ */
+ public function __construct( $diWikiPages, $diProperty, $limit, $from = '', $until = '' ) {
+ $this->mDiWikiPages = $diWikiPages;
+ $this->mDiProperty = $diProperty;
+ $this->mLimit = $limit;
+ $this->mFrom = $from;
+ $this->mUntil = $until;
+ }
+
+ /**
+ * Generates the prev/next link part to the HTML code of the top and
+ * bottom section of the page. Whether and how these links appear
+ * depends on specified boundaries, limit, and results. The title is
+ * required to create a link to the right page. The query array gives
+ * optional further parameters to append to all navigation links.
+ *
+ * @param $title Title
+ * @param $query array that associates parameter names to parameter values
+ * @return string
+ */
+ public function getNavigationLinks( Title $title, $query = [] ) {
+ global $wgLang;
+
+ $limitText = $wgLang->formatNum( $this->mLimit );
+
+ $resultCount = count( $this->mDiWikiPages );
+ $beyondLimit = ( $resultCount > $this->mLimit );
+
+ if ( !is_null( $this->mUntil ) && $this->mUntil !== '' ) {
+ if ( $beyondLimit ) {
+ $first = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $this->mDiWikiPages[1] );
+ } else {
+ $first = '';
+ }
+
+ $last = $this->mUntil;
+ } elseif ( $beyondLimit || ( !is_null( $this->mFrom ) && $this->mFrom !== '' ) ) {
+ $first = $this->mFrom;
+
+ if ( $beyondLimit ) {
+ $last = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $this->mDiWikiPages[$resultCount - 1] );
+ } else {
+ $last = '';
+ }
+ } else {
+ return '';
+ }
+
+ $prevLink = wfMessage( 'prevn', $limitText )->escaped();
+ if ( $first !== '' ) {
+ $prevLink = $this->makeSelfLink( $title, $prevLink, $query + [ 'until' => $first ] );
+ }
+
+ $nextLink = wfMessage( 'nextn', $limitText )->escaped();
+ if ( $last !== '' ) {
+ $nextLink = $this->makeSelfLink( $title, $nextLink, $query + [ 'from' => $last ] );
+ }
+
+ return "($prevLink) ($nextLink)";
+ }
+
+ /**
+ * Format an HTML link with the given text and parameters.
+ *
+ * @return string
+ */
+ protected function makeSelfLink( Title $title, $linkText, array $parameters ) {
+ return smwfGetLinker()->link( $title, $linkText, [], $parameters );
+ }
+
+ /**
+ * Make SMWRequestOptions suitable for obtaining a list of results for
+ * the given limit, and from or until string. One more result than the
+ * limit will be created, and the results may have to be reversed in
+ * order if ascending is set to false in the resulting object.
+ *
+ * @param $limit integer
+ * @param $from string can be empty if no from condition is desired
+ * @param $until string can be empty if no until condition is desired
+ * @return SMWRequestOptions
+ */
+ public static function getRequestOptions( $limit, $from, $until ) {
+ $options = new SMWRequestOptions();
+ $options->limit = $limit + 1;
+ $options->sort = true;
+
+ if ( $from !== '' ) {
+ $options->boundary = $from;
+ $options->ascending = true;
+ $options->include_boundary = true;
+ } elseif ( $until !== '' ) {
+ $options->boundary = $until;
+ $options->ascending = false;
+ $options->include_boundary = false;
+ }
+
+ return $options;
+ }
+
+ /**
+ * Make SMWQuery suitable for obtaining a list of results based on the
+ * given description, limit, and from or until string. One more result
+ * than the limit will be created, and the results may have to be
+ * reversed in order if $until is nonempty.
+ *
+ * @param $description SMWDescription main query description
+ * @param $limit integer
+ * @param $from string can be empty if no from condition is desired
+ * @param $until string can be empty if no until condition is desired
+ * @return SMWQuery
+ */
+ public static function getQuery( SMWDescription $description, $limit, $from, $until ) {
+ if ( $from !== '' ) {
+ $diWikiPage = new SMWDIWikiPage( $from, NS_MAIN, '' ); // make a dummy wiki page as boundary
+ $fromDescription = new SMWValueDescription( $diWikiPage, null, SMW_CMP_GEQ );
+ $queryDescription = new SMWConjunction( [ $description, $fromDescription ] );
+ $order = 'ASC';
+ } elseif ( $until !== '' ) {
+ $diWikiPage = new SMWDIWikiPage( $until, NS_MAIN, '' ); // make a dummy wiki page as boundary
+ $untilDescription = new SMWValueDescription( $diWikiPage, null, SMW_CMP_LESS ); // do not include boundary in this case
+ $queryDescription = new SMWConjunction( [ $description, $untilDescription ] );
+ $order = 'DESC';
+ } else {
+ $queryDescription = $description;
+ $order = 'ASC';
+ }
+
+ $queryDescription->addPrintRequest( new PrintRequest( PrintRequest::PRINT_THIS, '' ) );
+
+ $query = new SMWQuery( $queryDescription );
+ $query->sortkeys[''] = $order;
+ $query->setLimit( $limit + 1 );
+
+ return $query;
+ }
+
+ /**
+ * Format a list of data items chunked by letter, either as a
+ * bullet list or a columnar format, depending on the length.
+ *
+ * @param $cutoff integer, use columns for more results than that
+ * @return string
+ */
+ public function formatList( $cutoff = 6 ) {
+ $end = count( $this->mDiWikiPages );
+ $start = 0;
+ if ( $end > $this->mLimit ) {
+ if ( $this->mFrom !== '' ) {
+ $end -= 1;
+ } else {
+ $start += 1;
+ }
+ }
+
+ if ( count ( $this->mDiWikiPages ) > $cutoff ) {
+ return self::getColumnList( $start, $end, $this->mDiWikiPages, $this->mDiProperty );
+ } elseif ( count( $this->mDiWikiPages ) > 0 ) {
+ return self::getShortList( $start, $end, $this->mDiWikiPages, $this->mDiProperty );
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Format a list of SMWDIWikiPage objects chunked by letter in a three-column
+ * list, ordered vertically.
+ *
+ * @param $start integer
+ * @param $end integer
+ * @param $diWikiPages array of SMWDIWikiPage
+ * @param $diProperty SMWDIProperty that the wikipages are values of, or null
+ *
+ * @return string
+ */
+ public static function getColumnList( $start, $end, $diWikiPages, $diProperty, $moreCallback = null ) {
+ global $wgContLang;
+
+ if ( $diWikiPages instanceof \Iterator ) {
+ $diWikiPages = iterator_to_array( $diWikiPages );
+ }
+
+ // Divide list into three equal chunks.
+ $chunk = (int) ( ( $end - $start + 1 ) / 3 );
+
+ // Get and display header.
+ $r = '<table width="100%"><tr valign="top">';
+
+ $prevStartChar = 'none';
+
+ // Loop through the chunks.
+ for ( $startChunk = $start, $endChunk = $chunk, $chunkIndex = 0;
+ $chunkIndex < 3;
+ ++$chunkIndex, $startChunk = $endChunk, $endChunk += $chunk + 1 ) {
+ $r .= "<td width='33%'>\n";
+ $atColumnTop = true;
+
+ // output all diWikiPages
+ for ( $index = $startChunk; $index < $endChunk && $index < $end; ++$index ) {
+
+ if ( !isset( $diWikiPages[$index] ) ) {
+ continue;
+ }
+
+ $dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$index], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $dataValue->getWikiValue() );
+
+ // check for change of starting letter or beginning of chunk
+ $sortkey = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $diWikiPages[$index] );
+ $startChar = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
+
+ if ( ( $index == $startChunk ) ||
+ ( $startChar != $prevStartChar ) ) {
+ if ( $atColumnTop ) {
+ $atColumnTop = false;
+ } else {
+ $r .= "</ul>\n";
+ }
+
+ if ( $startChar == $prevStartChar ) {
+ $cont_msg = ' ' . wfMessage( 'listingcontinuesabbrev' )->escaped();
+ } else {
+ $cont_msg = '';
+ }
+
+ $r .= "<h3>" . htmlspecialchars( $startChar ) . $cont_msg . "</h3>\n<ul>";
+
+ $prevStartChar = $startChar;
+ }
+
+ $r .= "<li>" . $dataValue->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . "</li>\n";
+ }
+
+ if ( $index == $end && $moreCallback !== null ) {
+ $r .= "<li>" . call_user_func( $moreCallback ) . "</li>\n";
+ }
+
+ if ( !$atColumnTop ) {
+ $r .= "</ul>\n";
+ }
+
+ $r .= "</td>\n";
+ }
+
+ $r .= '</tr></table>';
+
+ return $r;
+ }
+
+ /**
+ * Format a list of diWikiPages chunked by letter in a bullet list.
+ *
+ * @param $start integer
+ * @param $end integer
+ * @param $diWikiPages array of SMWDataItem
+ * @param $diProperty SMWDIProperty that the wikipages are values of, or null
+ *
+ * @return string
+ */
+ public static function getShortList( $start, $end, $diWikiPages, $diProperty, $moreCallback = null ) {
+
+ if ( $diWikiPages instanceof \Iterator ) {
+ $diWikiPages = iterator_to_array( $diWikiPages );
+ }
+
+ $startDv = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$start], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $startDv->getWikiValue() );
+
+ // For a redirect, disable the DisplayTitle to show the original (aka source) page
+ if ( $diProperty !== null && $diProperty->getKey() == '_REDI' ) {
+ $startDv->setOption( 'smwgDVFeatures', ( $startDv->getOption( 'smwgDVFeatures' ) & ~SMW_DV_WPV_DTITLE ) );
+ }
+
+ $startChar = self::getFirstChar( $diWikiPages[$start] );
+
+ $r = '<h3>' . htmlspecialchars( $startChar ) . "</h3>\n" .
+ '<ul><li>' . $startDv->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</li>';
+
+ $prevStartChar = $startChar;
+ for ( $index = $start + 1; $index < $end; $index++ ) {
+ $dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$index], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $dataValue->getWikiValue() );
+
+ // For a redirect, disable the DisplayTitle to show the original (aka source) page
+ if ( $diProperty !== null && $diProperty->getKey() == '_REDI' ) {
+ $dataValue->setOption( 'smwgDVFeatures', ( $dataValue->getOption( 'smwgDVFeatures' ) & ~SMW_DV_WPV_DTITLE ) );
+ }
+
+ $startChar = self::getFirstChar( $diWikiPages[$index] );
+
+ if ( $startChar != $prevStartChar ) {
+ $r .= "</ul><h3>" . htmlspecialchars( $startChar ) . "</h3>\n<ul>";
+ $prevStartChar = $startChar;
+ }
+
+ $r .= '<li>' . $dataValue->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</li>';
+ }
+
+ if ( $moreCallback !== null ) {
+ $r .= '<li>' . call_user_func( $moreCallback ) . '</li>';
+ }
+
+ $r .= '</ul>';
+
+ return $r;
+ }
+
+ private static function getFirstChar( $dataItem ) {
+ global $wgContLang;
+
+ $sortkey = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $dataItem );
+
+ if ( $sortkey === '' ) {
+ $sortkey = $dataItem->getDBKey();
+ }
+
+ return $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php
new file mode 100644
index 00000000..8143ace2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php
@@ -0,0 +1,379 @@
+<?php
+
+/**
+ * Functions for handling Semantic MediaWiki data within the Page Schemas
+ * extension.
+ *
+ * @author Ankit Garg
+ * @author Yaron Koren
+ * @ingroup SMW
+ */
+
+class SMWPageSchemas extends PSExtensionHandler {
+
+ public static function getDisplayColor() {
+ return '#DEF';
+ }
+
+ public static function getTemplateDisplayString() {
+ return 'Connecting property';
+ }
+
+ public static function getFieldDisplayString() {
+ return 'Semantic property';
+ }
+
+ /**
+ * Returns the display info for the "connecting property" (if any)
+ * of the #subobject call (if any) in this template.
+ */
+ public static function getTemplateDisplayValues( $templateXML ) {
+ foreach ( $templateXML->children() as $tag => $child ) {
+ if ( $tag == "semanticmediawiki_ConnectingProperty" ) {
+ $propName = $child->attributes()->name;
+ $values = [];
+ return [ $propName, $values ];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the display info for the property (if any is defined)
+ * for a single field in the Page Schemas XML.
+ */
+ public static function getFieldDisplayValues( $fieldXML ) {
+ foreach ( $fieldXML->children() as $tag => $child ) {
+ if ( $tag == "semanticmediawiki_Property" ) {
+ $propName = $child->attributes()->name;
+ $values = [];
+ foreach ( $child->children() as $prop => $value ) {
+ $values[$prop] = (string)$value;
+ }
+ return [ $propName, $values ];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the set of SMW property data from the entire page schema.
+ */
+ static function getAllPropertyData( $pageSchemaObj ) {
+ $propertyDataArray = [];
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $psTemplateFields = $psTemplate->getFields();
+ foreach ( $psTemplateFields as $psTemplateField ) {
+ $prop_array = $psTemplateField->getObject('semanticmediawiki_Property');
+ if ( empty( $prop_array ) ) {
+ continue;
+ }
+ // If property name is blank, set it to the
+ // field name.
+ if ( !array_key_exists( 'name', $prop_array ) || empty( $prop_array['name'] ) ) {
+ $prop_array['name'] = $psTemplateField->getName();
+ }
+ $propertyDataArray[] = $prop_array;
+ }
+ }
+ return $propertyDataArray;
+ }
+
+ /**
+ * Constructs XML for the "connecting property", based on what was
+ * submitted in the 'edit schema' form.
+ */
+ public static function createTemplateXMLFromForm() {
+ global $wgRequest;
+
+ $xmlPerTemplate = [];
+ foreach ( $wgRequest->getValues() as $var => $val ) {
+ if ( substr( $var, 0, 24 ) == 'smw_connecting_property_' ) {
+ $templateNum = substr( $var, 24 );
+ $xml = '<semanticmediawiki_ConnectingProperty name="' . $val . '" />';
+ $xmlPerTemplate[$templateNum] = $xml;
+ }
+ }
+ return $xmlPerTemplate;
+ }
+
+ static function getConnectingPropertyName( $psTemplate ) {
+ // TODO - there should be a more direct way to get
+ // this data.
+ $smwConnectingPropertyArray = $psTemplate->getObject( 'semanticmediawiki_ConnectingProperty' );
+ return PageSchemas::getValueFromObject( $smwConnectingPropertyArray, 'name' );
+ }
+
+ /**
+ * Sets the list of property pages defined by the passed-in
+ * Page Schemas object.
+ */
+ public static function getPagesToGenerate( $pageSchemaObj ) {
+ $pagesToGenerate = [];
+
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $smwConnectingPropertyName = self::getConnectingPropertyName( $psTemplate );
+ if ( is_null( $smwConnectingPropertyName ) ) {
+ continue;
+ }
+ $pagesToGenerate[] = Title::makeTitleSafe( SMW_NS_PROPERTY, $smwConnectingPropertyName );
+ }
+
+ $propertyDataArray = self::getAllPropertyData( $pageSchemaObj );
+ foreach ( $propertyDataArray as $propertyData ) {
+ $title = Title::makeTitleSafe( SMW_NS_PROPERTY, $propertyData['name'] );
+ $pagesToGenerate[] = $title;
+ }
+ return $pagesToGenerate;
+ }
+
+ /**
+ * Constructs XML for the SMW property, based on what was submitted
+ * in the 'edit schema' form.
+ */
+ public static function createFieldXMLFromForm() {
+ global $wgRequest;
+
+ $fieldNum = -1;
+ $xmlPerField = [];
+ foreach ( $wgRequest->getValues() as $var => $val ) {
+ if ( substr( $var, 0, 18 ) == 'smw_property_name_' ) {
+ $fieldNum = substr( $var, 18 );
+ $xml = '<semanticmediawiki_Property name="' . $val . '" >';
+ } elseif ( substr( $var, 0, 18 ) == 'smw_property_type_'){
+ $xml .= '<Type>' . $val . '</Type>';
+ } elseif ( substr( $var, 0, 16 ) == 'smw_linked_form_') {
+ if ( $val !== '' ) {
+ $xml .= '<LinkedForm>' . $val . '</LinkedForm>';
+ }
+ } elseif ( substr( $var, 0, 11 ) == 'smw_values_') {
+ if ( $val !== '' ) {
+ // replace the comma substitution character that has no chance of
+ // being included in the values list - namely, the ASCII beep
+ $listSeparator = ',';
+ $allowed_values_str = str_replace( "\\$listSeparator", "\a", $val );
+ $allowed_values_array = explode( $listSeparator, $allowed_values_str );
+ foreach ( $allowed_values_array as $value ) {
+ // replace beep back with comma, trim
+ $value = str_replace( "\a", $listSeparator, trim( $value ) );
+ $xml .= '<AllowedValue>' . $value . '</AllowedValue>';
+ }
+ }
+ $xml .= '</semanticmediawiki_Property>';
+ $xmlPerField[$fieldNum] = $xml;
+ }
+ }
+ return $xmlPerField;
+ }
+
+ /**
+ * Returns the HTML necessary for getting information about the
+ * "connecting property" within the Page Schemas 'editschema' page.
+ */
+ public static function getTemplateEditingHTML( $psTemplate) {
+ // Only display this if the Semantic Internal Objects extension
+ // isn't displaying something similar.
+ if ( class_exists( 'SIOPageSchemas' ) ) {
+ return null;
+ }
+
+ $prop_array = [];
+ $hasExistingValues = false;
+ if ( !is_null( $psTemplate ) ) {
+ $prop_array = $psTemplate->getObject( 'semanticmediawiki_ConnectingProperty' );
+ if ( !is_null( $prop_array ) ) {
+ $hasExistingValues = true;
+ }
+ }
+ $text = '<p>' . 'Name of property to connect this template\'s fields to the rest of the page:' . ' ' . '(should only be used if this template can have multiple instances)' . ' ';
+ $propName = PageSchemas::getValueFromObject( $prop_array, 'name' );
+ $text .= Html::input( 'smw_connecting_property_num', $propName, [ 'size' => 15 ] ) . "\n";
+
+ return [ $text, $hasExistingValues ];
+ }
+
+ /**
+ * Returns the HTML necessary for getting information about a regular
+ * semantic property within the Page Schemas 'editschema' page.
+ */
+ public static function getFieldEditingHTML( $psTemplateField ) {
+ global $smwgContLang;
+
+ $prop_array = [];
+ $hasExistingValues = false;
+ if ( !is_null( $psTemplateField ) ) {
+ $prop_array = $psTemplateField->getObject('semanticmediawiki_Property');
+ if ( !is_null( $prop_array ) ) {
+ $hasExistingValues = true;
+ }
+ }
+ $html_text = '<p>' . wfMessage( 'ps-optional-name' )->text() . ' ';
+ $propName = PageSchemas::getValueFromObject( $prop_array, 'name' );
+ $html_text .= Html::input( 'smw_property_name_num', $propName, [ 'size' => 15 ] ) . "\n";
+ $propType = PageSchemas::getValueFromObject( $prop_array, 'Type' );
+ $select_body = "";
+ $datatype_labels = $smwgContLang->getDatatypeLabels();
+ foreach ( $datatype_labels as $label ) {
+ $optionAttrs = [];
+ if ( $label == $propType) {
+ $optionAttrs['selected'] = 'selected';
+ }
+ $select_body .= "\t" . Xml::element( 'option', $optionAttrs, $label ) . "\n";
+ }
+ $propertyDropdownAttrs = [
+ 'id' => 'property_dropdown',
+ 'name' => 'smw_property_type_num',
+ 'value' => $propType
+ ];
+ $html_text .= "Type: " . Xml::tags( 'select', $propertyDropdownAttrs, $select_body ) . "</p>\n";
+
+ // This can't be last, because of the hacky way the XML is
+ // ocnstructed from this form's output.
+ if ( defined( 'SF_VERSION' ) ) {
+ $html_text .= '<p>' . wfMessage( 'sf_createproperty_linktoform' )->text() . ' ';
+ $linkedForm = PageSchemas::getValueFromObject( $prop_array, 'LinkedForm' );
+ $html_text .= Html::input( 'smw_linked_form_num', $linkedForm, [ 'size' => 15 ] ) . "\n";
+ $html_text .= "(for Page properties only)</p>\n";
+ }
+
+ $html_text .= '<p>If you want this property to only be allowed to have certain values, enter the list of allowed values, separated by commas (if a value contains a comma, replace it with "\,"):</p>';
+ $allowedValsInputAttrs = [
+ 'size' => 80
+ ];
+ $allowedValues = PageSchemas::getValueFromObject( $prop_array, 'allowed_values' );
+ if ( is_null( $allowedValues ) ) {
+ $allowed_val_string = '';
+ } else {
+ $allowed_val_string = implode( ', ', $allowedValues );
+ }
+ $html_text .= '<p>' . Html::input( 'smw_values_num', $allowed_val_string, 'text', $allowedValsInputAttrs ) . "</p>\n";
+
+ return [ $html_text, $hasExistingValues ];
+ }
+
+ /**
+ * Creates the property page for each property specified in the
+ * passed-in Page Schemas XML object.
+ */
+ public static function generatePages( $pageSchemaObj, $selectedPages ) {
+ global $smwgContLang, $wgUser;
+
+ $datatypeLabels = $smwgContLang->getDatatypeLabels();
+ $pageTypeLabel = $datatypeLabels['_wpg'];
+
+ $jobs = [];
+ $jobParams = [];
+ $jobParams['user_id'] = $wgUser->getId();
+
+ // First, create jobs for all "connecting properties".
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $smwConnectingPropertyName = self::getConnectingPropertyName( $psTemplate );
+ if ( is_null( $smwConnectingPropertyName ) ) {
+ continue;
+ }
+ $propTitle = Title::makeTitleSafe( SMW_NS_PROPERTY, $smwConnectingPropertyName );
+ if ( !in_array( $propTitle, $selectedPages ) ) {
+ continue;
+ }
+
+ $jobParams['page_text'] = self::createPropertyText( $pageTypeLabel, null, null );
+ $jobs[] = new PSCreatePageJob( $propTitle, $jobParams );
+ }
+
+ // Second, create jobs for all regular properties.
+ $propertyDataArray = self::getAllPropertyData( $pageSchemaObj );
+ foreach ( $propertyDataArray as $propertyData ) {
+ $propTitle = Title::makeTitleSafe( SMW_NS_PROPERTY, $propertyData['name'] );
+ if ( !in_array( $propTitle, $selectedPages ) ) {
+ continue;
+ }
+ $propertyType = array_key_exists( 'Type', $propertyData ) ? $propertyData['Type'] : null;
+ $propertyAllowedValues = array_key_exists( 'allowed_values', $propertyData ) ? $propertyData['allowed_values'] : null;
+ $propertyLinkedForm = array_key_exists( 'LinkedForm', $propertyData ) ? $propertyData['LinkedForm'] : null;
+ $jobParams['page_text'] = self::createPropertyText( $propertyType, $propertyAllowedValues, $propertyLinkedForm );
+ $jobs[] = new PSCreatePageJob( $propTitle, $jobParams );
+ }
+ if ( class_exists( 'JobQueueGroup' ) ) {
+ JobQueueGroup::singleton()->push( $jobs );
+ } else {
+ // MW <= 1.20
+ Job::batchInsert( $jobs );
+ }
+ }
+
+ /**
+ * Creates the text for a property page.
+ */
+ static public function createPropertyText( $propertyType, $allowedValues, $linkedForm = null ) {
+ /**
+ * @var SMWLanguage $smwgContLang
+ */
+ global $smwgContLang, $wgContLang;
+
+ $propLabels = $smwgContLang->getPropertyLabels();
+ $hasTypeLabel = $propLabels['_TYPE'];
+ $typeTag = "[[$hasTypeLabel::$propertyType]]";
+ $text = wfMessage( 'smw-createproperty-isproperty', $typeTag )->inContentLanguage()->text();
+
+ if ( $linkedForm !== '' && defined( 'SF_VERSION' ) ) {
+ global $sfgContLang;
+ $sfPropLabels = $sfgContLang->getPropertyLabels();
+ $defaultFormTag = "[[{$sfPropLabels[SF_SP_HAS_DEFAULT_FORM]}::$linkedForm]]";
+ $text .= ' ' . wfMessage( 'sf_property_linkstoform', $defaultFormTag )->inContentLanguage()->text();
+ }
+
+ if ( $allowedValues != null) {
+ $text .= "\n\n" . wfMessage( 'smw-createproperty-allowedvals', $wgContLang->formatNum( count( $allowedValues ) ) )->inContentLanguage()->text();
+
+ foreach ( $allowedValues as $value ) {
+ $prop_labels = $smwgContLang->getPropertyLabels();
+ $text .= "\n* [[" . $prop_labels['_PVAL'] . "::$value]]";
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Returns either the "connecting property", or a field property, based
+ * on the XML passed from the Page Schemas extension.
+ */
+ public static function createPageSchemasObject( $tagName, $xml ) {
+ if ( $tagName == "semanticmediawiki_ConnectingProperty" ) {
+ foreach ( $xml->children() as $tag => $child ) {
+ if ( $tag == $tagName ) {
+ $smw_array = [];
+ $propName = $child->attributes()->name;
+ $smw_array['name'] = (string)$propName;
+ foreach ( $child->children() as $prop => $value ) {
+ $smw_array[$prop] = (string)$value;
+ }
+ return $smw_array;
+ }
+ }
+ } elseif ( $tagName == "semanticmediawiki_Property" ) {
+ foreach ( $xml->children() as $tag => $child ) {
+ if ( $tag == $tagName ) {
+ $smw_array = [];
+ $propName = $child->attributes()->name;
+ $smw_array['name'] = (string)$propName;
+ $allowed_values = [];
+ $count = 0;
+ foreach ( $child->children() as $prop => $value ) {
+ if ( $prop == "AllowedValue" ) {
+ $allowed_values[$count++] = $value;
+ } else {
+ $smw_array[$prop] = (string)$value;
+ }
+ }
+ $smw_array['allowed_values'] = $allowed_values;
+ return $smw_array;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php b/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php
new file mode 100644
index 00000000..e54ec20e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php
@@ -0,0 +1,782 @@
+<?php
+
+namespace SMW;
+
+use MWException;
+use SMW\DataModel\SubSemanticData;
+use SMW\Exception\SemanticDataImportException;
+use SMWContainerSemanticData;
+use SMWDataItem;
+use SMWDataValue;
+use SMWDIContainer;
+
+/**
+ * Class for representing chunks of semantic data for one given
+ * subject. This consists of property-value pairs, grouped by property,
+ * and possibly by SMWSemanticData objects about subobjects.
+ *
+ * Data about subobjects can be added in two ways: by directly adding it
+ * using addSubSemanticData() or by adding a property value of type
+ * SMWDIContainer.
+ *
+ * By its very design, the container is unable to hold inverse properties.
+ * For one thing, it would not be possible to identify them with mere keys.
+ * Since SMW cannot annotate pages with inverses, this is not a limitation.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SemanticData {
+
+ /**
+ * Returns the last modified timestamp the data were stored to the Store or
+ * have been fetched from cache.
+ */
+ const OPT_LAST_MODIFIED = 'opt.last.modified';
+
+ /**
+ * Identifies that a data block was created by a user.
+ */
+ const PROC_USER = 'proc.user';
+
+ /**
+ * Identifies that a data block was initiated by a delete request.
+ */
+ const PROC_DELETE = 'proc.delete';
+
+ /**
+ * Cache for the localized version of the namespace prefix "Property:".
+ *
+ * @var string
+ */
+ static protected $mPropertyPrefix = '';
+
+ /**
+ * States whether this is a stub object. Stubbing might happen on
+ * serialisation to save DB space.
+ *
+ * @todo Check why this is public and document this here. Or fix it.
+ *
+ * @var boolean
+ */
+ public $stubObject;
+
+ /**
+ * Array mapping property keys (string) to arrays of SMWDataItem
+ * objects.
+ *
+ * @var SMWDataItem[]
+ */
+ protected $mPropVals = [];
+
+ /**
+ * Array mapping property keys (string) to DIProperty objects.
+ *
+ * @var DIProperty[]
+ */
+ protected $mProperties = [];
+
+ /**
+ * States whether the container holds any normal properties.
+ *
+ * @var boolean
+ */
+ protected $mHasVisibleProps = false;
+
+ /**
+ * States whether the container holds any displayable predefined
+ * $mProperties (as opposed to predefined properties without a display
+ * label). For some settings we need this to decide if a Factbox is
+ * displayed.
+ *
+ * @var boolean
+ */
+ protected $mHasVisibleSpecs = false;
+
+ /**
+ * States whether repeated values should be avoided. Not needing
+ * duplicate elimination (e.g. when loading from store) can save some
+ * time, especially in subclasses like SMWSqlStubSemanticData, where
+ * the first access to a data item is more costy.
+ *
+ * @note This setting is merely for optimization. The SMW data model
+ * never cares about the multiplicity of identical data assignments.
+ *
+ * @var boolean
+ */
+ protected $mNoDuplicates;
+
+ /**
+ * DIWikiPage object that is the subject of this container.
+ * Subjects can never be null (and this is ensured in all methods setting
+ * them in this class).
+ *
+ * @var DIWikiPage
+ */
+ protected $mSubject;
+
+ /**
+ * Semantic data associated to subobjects of the subject of this
+ * SMWSemanticData.
+ * These key-value pairs of subObjectName (string) =>SMWSemanticData.
+ *
+ * @since 1.8
+ * @var SubSemanticData
+ */
+ protected $subSemanticData;
+
+ /**
+ * Internal flag that indicates if this semantic data will accept
+ * subdata. Semantic data objects that are subdata already do not allow
+ * (second level) subdata to be added. This ensures that all data is
+ * collected on the top level, and in particular that there is only one
+ * way to represent the same data with subdata. This is also useful for
+ * diff computation.
+ */
+ protected $subDataAllowed = true;
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * Cache the hash to ensure a minimal impact in case of repeated usage. Any
+ * removal or insert action will reset the hash to null to ensure it is
+ * recreated in corresponds to changed nature of the data.
+ *
+ * @var string|null
+ */
+ private $hash = null;
+
+ /**
+ * @var Options
+ */
+ protected $options;
+
+ /**
+ * @var array
+ */
+ protected $extensionData = [];
+
+ /**
+ * This is kept public to keep track of the depth during a recursive processing
+ * when accessed through the SubSemanticData instance.
+ *
+ * @var integer
+ */
+ public $subContainerDepthCounter = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param DIWikiPage $subject to which this data refers
+ * @param boolean $noDuplicates stating if duplicate data should be avoided
+ */
+ public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
+ $this->clear();
+ $this->mSubject = $subject;
+ $this->mNoDuplicates = $noDuplicates;
+ $this->subSemanticData = new SubSemanticData( $subject, $noDuplicates );
+ }
+
+ /**
+ * This object is added to the parser output of MediaWiki, but it is
+ * not useful to have all its data as part of the parser cache since
+ * the data is already stored in more accessible format in SMW. Hence
+ * this implementation of __sleep() makes sure only the subject is
+ * serialised, yielding a minimal stub data container after
+ * unserialisation. This is a little safer than serialising nothing:
+ * if, for any reason, SMW should ever access an unserialised parser
+ * output, then the Semdata container will at least look as if properly
+ * initialised (though empty).
+ *
+ * @return array
+ */
+ public function __sleep() {
+ return [ 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mHasVisibleProps', 'mHasVisibleSpecs', 'options', 'extensionData' ];
+ }
+
+ /**
+ * Return subject to which the stored semantic annotations refer to.
+ *
+ * @return DIWikiPage subject
+ */
+ public function getSubject() {
+ return $this->mSubject;
+ }
+
+ /**
+ * Get the array of all properties that have stored values.
+ *
+ * @return array of DIProperty objects
+ */
+ public function getProperties() {
+ ksort( $this->mProperties, SORT_STRING );
+ return $this->mProperties;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasProperty( DIProperty $property ) {
+ return isset( $this->mProperties[$property->getKey()] ) || array_key_exists( $property->getKey(), $this->mProperties );
+ }
+
+ /**
+ * Get the array of all stored values for some property.
+ *
+ * @param DIProperty $property
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( DIProperty $property ) {
+ if ( $property->isInverse() ) { // we never have any data for inverses
+ return [];
+ }
+
+ if ( array_key_exists( $property->getKey(), $this->mPropVals ) ) {
+ return array_values( $this->mPropVals[$property->getKey()] );
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setExtensionData( $key, $value ) {
+ $this->extensionData[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed|null
+ */
+ public function getExtensionData( $key ) {
+
+ if ( !isset( $this->extensionData[$key] ) ) {
+ return null;
+ }
+
+ return $this->extensionData[$key];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ if ( $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * Returns collected errors occurred during processing
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Adds an error array
+ *
+ * @since 1.9
+ *
+ * @return array|string
+ */
+ public function addError( $error ) {
+ $this->errors = array_merge( $this->errors, (array)$error );
+ }
+
+ /**
+ * Generate a hash value to simplify the comparison of this data
+ * container with other containers. Subdata is taken into account.
+ *
+ * The hash uses PHP's md5 implementation, which is among the fastest
+ * hash algorithms that PHP offers.
+ *
+ * @note This function may be used to obtain keys for SemanticData
+ * objects or to do simple equality tests. Equal hashes with very
+ * high probability indicate equal data.
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ if ( $this->hash !== null ) {
+ return $this->hash;
+ }
+
+ return $this->hash = Hash::createFromSemanticData( $this );
+ }
+
+ /**
+ * @see SubSemanticData::getSubSemanticData
+ *
+ * @since 1.8
+ *
+ * @return ContainerSemanticData[]
+ */
+ public function getSubSemanticData() {
+
+ // Remove the check in 3.0
+ $subSemanticData = $this->subSemanticData;
+
+ // Avoids an issue where the serialized array from a previous usage is
+ // returned from a __wakeup, where now a SubSemanticData (#2177) is expected.
+ if ( !$subSemanticData instanceof SubSemanticData ) {
+ $this->subSemanticData = new SubSemanticData( $this->mSubject, $this->mNoDuplicates );
+ $this->subSemanticData->copyDataFrom( $subSemanticData );
+ }
+
+ return $this->subSemanticData->getSubSemanticData();
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function clearSubSemanticData() {
+
+ if ( $this->subContainerDepthCounter > 0 ) {
+ $this->subContainerDepthCounter--;
+ }
+
+ if ( $this->subSemanticData !== null ) {
+ $this->subSemanticData->clear();
+ }
+ }
+
+ /**
+ * Return true if there are any visible properties.
+ *
+ * @note While called "visible" this check actually refers to the
+ * function DIProperty::isShown(). The name is kept for
+ * compatibility.
+ *
+ * @return boolean
+ */
+ public function hasVisibleProperties() {
+ return $this->mHasVisibleProps;
+ }
+
+ /**
+ * Return true if there are any special properties that can
+ * be displayed.
+ *
+ * @note While called "visible" this check actually refers to the
+ * function DIProperty::isShown(). The name is kept for
+ * compatibility.
+ *
+ * @return boolean
+ */
+ public function hasVisibleSpecialProperties() {
+ return $this->mHasVisibleSpecs;
+ }
+
+ /**
+ * Store a value for a property identified by its SMWDataItem object.
+ *
+ * @note There is no check whether the type of the given data item
+ * agrees with the type of the property. Since property types can
+ * change, all parts of SMW are prepared to handle mismatched data item
+ * types anyway.
+ *
+ * @param $property DIProperty
+ * @param $dataItem SMWDataItem
+ */
+ public function addPropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
+
+ $this->hash = null;
+
+ if( $dataItem instanceof SMWDIContainer ) {
+ $this->addSubSemanticData( $dataItem->getSemanticData() );
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if( $property->getKey() === DIProperty::TYPE_MODIFICATION_DATE ) {
+ $this->setOption( self::OPT_LAST_MODIFIED, $dataItem->getMwTimestamp() );
+ }
+
+ if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
+ return;
+ }
+
+ if ( !array_key_exists( $property->getKey(), $this->mPropVals ) ) {
+ $this->mPropVals[$property->getKey()] = [];
+ $this->mProperties[$property->getKey()] = $property;
+ }
+
+ if ( $this->mNoDuplicates ) {
+ $this->mPropVals[$property->getKey()][$dataItem->getHash()] = $dataItem;
+ } else {
+ $this->mPropVals[$property->getKey()][] = $dataItem;
+ }
+
+ if ( !$property->isUserDefined() ) {
+ if ( $property->isShown() ) {
+ $this->mHasVisibleSpecs = true;
+ $this->mHasVisibleProps = true;
+ }
+ } else {
+ $this->mHasVisibleProps = true;
+ }
+
+ // Account for things like DISPLAYTITLE or DEFAULTSORT which are only set
+ // after #subobject has been processed therefore keep them in-memory
+ // for a post process
+ if ( $this->mSubject->getSubobjectName() === '' && $property->getKey() === DIProperty::TYPE_SORTKEY ) {
+ foreach ( $this->getSubSemanticData() as $subSemanticData ) {
+ $subSemanticData->setExtensionData( 'sort.extension', $dataItem->getString() );
+ }
+ }
+ }
+
+ /**
+ * Store a value for a given property identified by its text label
+ * (without namespace prefix).
+ *
+ * @param $propertyName string
+ * @param $dataItem SMWDataItem
+ */
+ public function addPropertyValue( $propertyName, SMWDataItem $dataItem ) {
+ $propertyKey = smwfNormalTitleDBKey( $propertyName );
+
+ if ( array_key_exists( $propertyKey, $this->mProperties ) ) {
+ $property = $this->mProperties[$propertyKey];
+ } else {
+ if ( self::$mPropertyPrefix === '' ) {
+ global $wgContLang;
+ self::$mPropertyPrefix = $wgContLang->getNsText( SMW_NS_PROPERTY ) . ':';
+ } // explicitly use prefix to cope with things like [[Property:User:Stupid::somevalue]]
+
+ $propertyDV = DataValueFactory::getInstance()->newPropertyValueByLabel( self::$mPropertyPrefix . $propertyName );
+
+ if ( !$propertyDV->isValid() ) { // error, maybe illegal title text
+ return;
+ }
+
+ $property = $propertyDV->getDataItem();
+ }
+
+ $this->addPropertyObjectValue( $property, $dataItem );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param SMWDataValue $dataValue
+ */
+ public function addDataValue( SMWDataValue $dataValue ) {
+
+ if ( !$dataValue->getProperty() instanceof DIProperty || !$dataValue->isValid() ) {
+
+ $processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $this->getSubject()
+ );
+
+ $processingErrorMsgHandler->addToSemanticData(
+ $this,
+ $processingErrorMsgHandler->newErrorContainerFromDataValue( $dataValue )
+ );
+
+ return $this->addError( $dataValue->getErrors() );
+ }
+
+ $this->addPropertyObjectValue(
+ $dataValue->getProperty(),
+ $dataValue->getDataItem()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Subobject $subobject
+ */
+ public function addSubobject( Subobject $subobject ) {
+ $this->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+ }
+
+ /**
+ * Remove a value for a property identified by its SMWDataItem object.
+ * This method removes a property-value specified by the property and
+ * dataitem. If there are no more property-values for this property it
+ * also removes the property from the mProperties.
+ *
+ * @note There is no check whether the type of the given data item
+ * agrees with the type of the property. Since property types can
+ * change, all parts of SMW are prepared to handle mismatched data item
+ * types anyway.
+ *
+ * @param $property DIProperty
+ * @param $dataItem SMWDataItem
+ *
+ * @since 1.8
+ */
+ public function removePropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
+
+ $this->hash = null;
+
+ //delete associated subSemanticData
+ if( $dataItem instanceof SMWDIContainer ) {
+ $this->removeSubSemanticData( $dataItem->getSemanticData() );
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
+ return;
+ }
+
+ if ( !array_key_exists( $property->getKey(), $this->mPropVals ) || !array_key_exists( $property->getKey(), $this->mProperties ) ) {
+ return;
+ }
+
+ if ( $this->mNoDuplicates ) {
+ //this didn't get checked for my tests, but should work
+ unset( $this->mPropVals[$property->getKey()][$dataItem->getHash()] );
+ } else {
+ foreach( $this->mPropVals[$property->getKey()] as $index => $di ) {
+ if( $di->equals( $dataItem ) ) {
+ unset( $this->mPropVals[$property->getKey()][$index] );
+ }
+ }
+ $this->mPropVals[$property->getKey()] = array_values( $this->mPropVals[$property->getKey()] );
+ }
+
+ if ( $this->mPropVals[$property->getKey()] === [] ) {
+ unset( $this->mProperties[$property->getKey()] );
+ unset( $this->mPropVals[$property->getKey()] );
+ }
+ }
+
+ /**
+ * Removes a property and all the values associated with this property.
+ *
+ * @since 2.5
+ *
+ * @param $property DIProperty
+ */
+ public function removeProperty( DIProperty $property ) {
+
+ $this->hash = null;
+ $key = $property->getKey();
+
+ // Inverse properties cannot be used for an annotation
+ if ( $property->isInverse() ) {
+ return;
+ }
+
+ if ( !isset( $this->mProperties[$key] ) || !isset( $this->mPropVals[$key] ) ) {
+ return;
+ }
+
+ // Find and remove associated assignments (e.g. _ASK as subobject
+ // contains _ASKSI ...)
+ foreach ( $this->mPropVals[$key] as $dataItem ) {
+
+ if ( !$dataItem instanceof DIWikiPage || $dataItem->getSubobjectName() === '' ) {
+ continue;
+ }
+
+ if ( ( $subSemanticData = $this->findSubSemanticData( $dataItem->getSubobjectName() ) ) !== null ) {
+ $this->removeSubSemanticData( $subSemanticData );
+ }
+ }
+
+ unset( $this->mPropVals[$key] );
+ unset( $this->mProperties[$key] );
+ }
+
+ /**
+ * Delete all data other than the subject.
+ */
+ public function clear() {
+ $this->mPropVals = [];
+ $this->mProperties = [];
+ $this->mHasVisibleProps = false;
+ $this->mHasVisibleSpecs = false;
+ $this->stubObject = false;
+ $this->clearSubSemanticData();
+ $this->hash = null;
+ $this->options = null;
+ }
+
+ /**
+ * Return true if this SemanticData is empty.
+ * This is the case when the subject has neither property values nor
+ * data for subobjects.
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isEmpty() {
+ return $this->getProperties() === [] && $this->getSubSemanticData() === [];
+ }
+
+ /**
+ * Add all data from the given SMWSemanticData.
+ * Only works if the imported SMWSemanticData has the same subject as
+ * this SMWSemanticData; an exception is thrown otherwise.
+ *
+ * @since 1.7
+ *
+ * @param SemanticData $semanticData object to copy from
+ *
+ * @throws SemanticDataImportException
+ */
+ public function importDataFrom( SemanticData $semanticData ) {
+
+ if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
+ throw new SemanticDataImportException( "SemanticData can only represent data about one subject. Importing data for another subject is not possible." );
+ }
+
+ $this->hash = null;
+
+ // Shortcut when copying into empty objects that don't ask for
+ // more duplicate elimination:
+ if ( count( $this->mProperties ) == 0 &&
+ ( $semanticData->mNoDuplicates >= $this->mNoDuplicates ) ) {
+ $this->mProperties = $semanticData->getProperties();
+ $this->mPropVals = [];
+
+ foreach ( $this->mProperties as $property ) {
+ $this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
+ }
+
+ $this->mHasVisibleProps = $semanticData->hasVisibleProperties();
+ $this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
+ } else {
+ foreach ( $semanticData->getProperties() as $property ) {
+ $values = $semanticData->getPropertyValues( $property );
+
+ foreach ( $values as $dataItem ) {
+ $this->addPropertyObjectValue( $property, $dataItem);
+ }
+ }
+ }
+
+ foreach( $semanticData->getSubSemanticData() as $semData ) {
+ $this->addSubSemanticData( $semData );
+ }
+ }
+
+ /**
+ * Removes data from the given SMWSemanticData.
+ * If the subject of the data that is to be removed is not equal to the
+ * subject of this SMWSemanticData, it will just be ignored (nothing to
+ * remove). Likewise, removing data that is not present does not change
+ * anything.
+ *
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ */
+ public function removeDataFrom( SemanticData $semanticData ) {
+ if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
+ return;
+ }
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $this->removeProperty( $property );
+ }
+
+ foreach( $semanticData->getSubSemanticData() as $semData ) {
+ $this->removeSubSemanticData( $semData );
+ }
+ }
+
+ /**
+ * @see SubSemanticData::hasSubSemanticData
+ * @since 1.9
+ *
+ * @param string $subobjectName|null
+ *
+ * @return boolean
+ */
+ public function hasSubSemanticData( $subobjectName = null ) {
+ return $this->subSemanticData->hasSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * @see SubSemanticData::findSubSemanticData
+ * @since 1.9
+ *
+ * @param string $subobjectName
+ *
+ * @return SMWContainerSemanticData|null
+ */
+ public function findSubSemanticData( $subobjectName ) {
+ return $this->subSemanticData->findSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * @see SubSemanticData::addSubSemanticData
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ * @throws SubSemanticDataException
+ */
+ public function addSubSemanticData( SemanticData $semanticData ) {
+ $this->hash = null;
+ $this->subSemanticData->addSubSemanticData( $semanticData );
+ }
+
+ /**
+ * @see SubSemanticData::removeSubSemanticData
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ */
+ public function removeSubSemanticData( SemanticData $semanticData ) {
+ $this->hash = null;
+ $this->subSemanticData->removeSubSemanticData( $semanticData );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php b/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php
new file mode 100644
index 00000000..64d0b3d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php
@@ -0,0 +1,613 @@
+<?php
+
+namespace SMW;
+
+use SMW\Exception\SettingNotFoundException;
+
+/**
+ * Encapsulate Semantic MediaWiki settings to access values through a
+ * specified interface
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Settings extends Options {
+
+ /**
+ * @var Settings
+ */
+ private static $instance = null;
+
+ /**
+ * @var array
+ */
+ private $iterate = [];
+
+ /**
+ * Assemble individual SMW related settings into one accessible array for
+ * easy instantiation since we don't have unique way of accessing only
+ * SMW related settings ( e.g. $smwgSettings['...']) we need this method
+ * as short cut to invoke only smwg* related settings
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromGlobals();
+ * $settings->get( 'smwgDefaultStore' );
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @return Settings
+ */
+ public static function newFromGlobals() {
+
+ $configuration = [
+ 'smwgIP' => $GLOBALS['smwgIP'],
+ 'smwgExtraneousLanguageFileDir' => $GLOBALS['smwgExtraneousLanguageFileDir'],
+ 'smwgServicesFileDir' => $GLOBALS['smwgServicesFileDir'],
+ 'smwgResourceLoaderDefFiles' => $GLOBALS['smwgResourceLoaderDefFiles'],
+ 'smwgMaintenanceDir' => $GLOBALS['smwgMaintenanceDir'],
+ 'smwgConfigFileDir' => $GLOBALS['smwgConfigFileDir'],
+ 'smwgImportFileDirs' => $GLOBALS['smwgImportFileDirs'],
+ 'smwgImportReqVersion' => $GLOBALS['smwgImportReqVersion'],
+ 'smwgSemanticsEnabled' => $GLOBALS['smwgSemanticsEnabled'],
+ 'smwgUpgradeKey' => $GLOBALS['smwgUpgradeKey'],
+ 'smwgJobQueueWatchlist' => $GLOBALS['smwgJobQueueWatchlist'],
+ 'smwgEnabledCompatibilityMode' => $GLOBALS['smwgEnabledCompatibilityMode'],
+ 'smwgDefaultStore' => $GLOBALS['smwgDefaultStore'],
+ 'smwgDefaultLoggerRole' => $GLOBALS['smwgDefaultLoggerRole'],
+ 'smwgLocalConnectionConf' => $GLOBALS['smwgLocalConnectionConf'],
+ 'smwgSparqlRepositoryConnector' => $GLOBALS['smwgSparqlRepositoryConnector'],
+ 'smwgSparqlCustomConnector' => $GLOBALS['smwgSparqlCustomConnector'],
+ 'smwgSparqlEndpoint' => $GLOBALS['smwgSparqlEndpoint'],
+ 'smwgSparqlDefaultGraph' => $GLOBALS['smwgSparqlDefaultGraph'],
+ 'smwgSparqlRepositoryConnectorForcedHttpVersion' => $GLOBALS['smwgSparqlRepositoryConnectorForcedHttpVersion'],
+ 'smwgSparqlReplicationPropertyExemptionList' => $GLOBALS['smwgSparqlReplicationPropertyExemptionList'],
+ 'smwgSparqlQFeatures' => $GLOBALS['smwgSparqlQFeatures'],
+ 'smwgNamespaceIndex' => $GLOBALS['smwgNamespaceIndex'],
+ 'smwgFactboxFeatures' => $GLOBALS['smwgFactboxFeatures'],
+ 'smwgShowFactbox' => $GLOBALS['smwgShowFactbox'],
+ 'smwgShowFactboxEdit' => $GLOBALS['smwgShowFactboxEdit'],
+ 'smwgCompactLinkSupport' => $GLOBALS['smwgCompactLinkSupport'],
+ 'smwgDefaultNumRecurringEvents' => $GLOBALS['smwgDefaultNumRecurringEvents'],
+ 'smwgMaxNumRecurringEvents' => $GLOBALS['smwgMaxNumRecurringEvents'],
+ 'smwgSearchByPropertyFuzzy' => $GLOBALS['smwgSearchByPropertyFuzzy'],
+ 'smwgPagingLimit' => $GLOBALS['smwgPagingLimit'],
+ 'smwgPropertyListLimit' => $GLOBALS['smwgPropertyListLimit'],
+ 'smwgQEnabled' => $GLOBALS['smwgQEnabled'],
+ 'smwgQMaxLimit' => $GLOBALS['smwgQMaxLimit'],
+ 'smwgIgnoreQueryErrors' => $GLOBALS['smwgIgnoreQueryErrors'],
+ 'smwgQSubcategoryDepth' => $GLOBALS['smwgQSubcategoryDepth'],
+ 'smwgQSubpropertyDepth' => $GLOBALS['smwgQSubpropertyDepth'],
+ 'smwgQEqualitySupport' => $GLOBALS['smwgQEqualitySupport'],
+ 'smwgQDefaultNamespaces' => $GLOBALS['smwgQDefaultNamespaces'],
+ 'smwgQComparators' => $GLOBALS['smwgQComparators'],
+ 'smwgQFilterDuplicates' => $GLOBALS['smwgQFilterDuplicates'],
+ 'smwStrictComparators' => $GLOBALS['smwStrictComparators'],
+ 'smwgQStrictComparators' => $GLOBALS['smwgQStrictComparators'],
+ 'smwgQMaxSize' => $GLOBALS['smwgQMaxSize'],
+ 'smwgQMaxDepth' => $GLOBALS['smwgQMaxDepth'],
+ 'smwgQFeatures' => $GLOBALS['smwgQFeatures'],
+ 'smwgQDefaultLimit' => $GLOBALS['smwgQDefaultLimit'],
+ 'smwgQUpperbound' => $GLOBALS['smwgQUpperbound'],
+ 'smwgQMaxInlineLimit' => $GLOBALS['smwgQMaxInlineLimit'],
+ 'smwgQPrintoutLimit' => $GLOBALS['smwgQPrintoutLimit'],
+ 'smwgQDefaultLinking' => $GLOBALS['smwgQDefaultLinking'],
+ 'smwgQConceptCaching' => $GLOBALS['smwgQConceptCaching'],
+ 'smwgQConceptMaxSize' => $GLOBALS['smwgQConceptMaxSize'],
+ 'smwgQConceptMaxDepth' => $GLOBALS['smwgQConceptMaxDepth'],
+ 'smwgQConceptFeatures' => $GLOBALS['smwgQConceptFeatures'],
+ 'smwgQConceptCacheLifetime' => $GLOBALS['smwgQConceptCacheLifetime'],
+ 'smwgQExpensiveThreshold' => $GLOBALS['smwgQExpensiveThreshold'],
+ 'smwgQExpensiveExecutionLimit' => $GLOBALS['smwgQExpensiveExecutionLimit'],
+ 'smwgRemoteReqFeatures' => $GLOBALS['smwgRemoteReqFeatures'],
+ 'smwgQuerySources' => $GLOBALS['smwgQuerySources'],
+ 'smwgQTemporaryTablesAutoCommitMode' => $GLOBALS['smwgQTemporaryTablesAutoCommitMode'],
+ 'smwgQSortFeatures' => $GLOBALS['smwgQSortFeatures'],
+ 'smwgResultFormats' => $GLOBALS['smwgResultFormats'],
+ 'smwgResultFormatsFeatures' => $GLOBALS['smwgResultFormatsFeatures'],
+ 'smwgResultAliases' => $GLOBALS['smwgResultAliases'],
+ 'smwgPDefaultType' => $GLOBALS['smwgPDefaultType'],
+ 'smwgAllowRecursiveExport' => $GLOBALS['smwgAllowRecursiveExport'],
+ 'smwgExportBacklinks' => $GLOBALS['smwgExportBacklinks'],
+ 'smwgExportResourcesAsIri' => $GLOBALS['smwgExportResourcesAsIri'],
+ 'smwgExportBCNonCanonicalFormUse' => $GLOBALS['smwgExportBCNonCanonicalFormUse'],
+ 'smwgExportBCAuxiliaryUse' => $GLOBALS['smwgExportBCAuxiliaryUse'],
+ 'smwgMaxNonExpNumber' => $GLOBALS['smwgMaxNonExpNumber'],
+ 'smwgEnableUpdateJobs' => $GLOBALS['smwgEnableUpdateJobs'],
+ 'smwgNamespacesWithSemanticLinks' => $GLOBALS['smwgNamespacesWithSemanticLinks'],
+ 'smwgPageSpecialProperties' => $GLOBALS['smwgPageSpecialProperties'],
+ 'smwgChangePropagationWatchlist' => $GLOBALS['smwgChangePropagationWatchlist'],
+ 'smwgDataTypePropertyExemptionList' => $GLOBALS['smwgDataTypePropertyExemptionList'],
+ 'smwgDefaultOutputFormatters' => $GLOBALS['smwgDefaultOutputFormatters'],
+ 'smwgTranslate' => $GLOBALS['smwgTranslate'],
+ 'smwgAutoRefreshSubject' => $GLOBALS['smwgAutoRefreshSubject'],
+ 'smwgAdminFeatures' => $GLOBALS['smwgAdminFeatures'],
+ 'smwgAutoRefreshOnPurge' => $GLOBALS['smwgAutoRefreshOnPurge'],
+ 'smwgAutoRefreshOnPageMove' => $GLOBALS['smwgAutoRefreshOnPageMove'],
+ 'smwgContLang' => isset( $GLOBALS['smwgContLang'] ) ? $GLOBALS['smwgContLang'] : '',
+ 'smwgMaxPropertyValues' => $GLOBALS['smwgMaxPropertyValues'],
+ 'smwgNamespace' => $GLOBALS['smwgNamespace'],
+ 'smwgMasterStore' => isset( $GLOBALS['smwgMasterStore'] ) ? $GLOBALS['smwgMasterStore'] : '',
+ 'smwgIQRunningNumber' => isset( $GLOBALS['smwgIQRunningNumber'] ) ? $GLOBALS['smwgIQRunningNumber'] : 0,
+ 'smwgCacheUsage' => $GLOBALS['smwgCacheUsage'],
+ 'smwgMainCacheType' => $GLOBALS['smwgMainCacheType'],
+ 'smwgEntityLookupCacheType' => $GLOBALS['smwgEntityLookupCacheType'],
+ 'smwgEntityLookupCacheLifetime' => $GLOBALS['smwgEntityLookupCacheLifetime'],
+ 'smwgEntityLookupFeatures' => $GLOBALS['smwgEntityLookupFeatures'],
+ 'smwgFixedProperties' => $GLOBALS['smwgFixedProperties'],
+ 'smwgPropertyLowUsageThreshold' => $GLOBALS['smwgPropertyLowUsageThreshold'],
+ 'smwgPropertyZeroCountDisplay' => $GLOBALS['smwgPropertyZeroCountDisplay'],
+ 'smwgQueryProfiler' => $GLOBALS['smwgQueryProfiler'],
+ 'smwgEnabledSpecialPage' => $GLOBALS['smwgEnabledSpecialPage'],
+ 'smwgFallbackSearchType' => $GLOBALS['smwgFallbackSearchType'],
+ 'smwgEnabledEditPageHelp' => $GLOBALS['smwgEnabledEditPageHelp'],
+ 'smwgEnabledDeferredUpdate' => $GLOBALS['smwgEnabledDeferredUpdate'],
+ 'smwgEnabledQueryDependencyLinksStore' => $GLOBALS['smwgEnabledQueryDependencyLinksStore'],
+ 'smwgQueryDependencyPropertyExemptionList' => $GLOBALS['smwgQueryDependencyPropertyExemptionList'],
+ 'smwgQueryDependencyAffiliatePropertyDetectionList' => $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionList'],
+ 'smwgParserFeatures' => $GLOBALS['smwgParserFeatures'],
+ 'smwgDVFeatures' => $GLOBALS['smwgDVFeatures'],
+ 'smwgEnabledFulltextSearch' => $GLOBALS['smwgEnabledFulltextSearch'],
+ 'smwgFulltextDeferredUpdate' => $GLOBALS['smwgFulltextDeferredUpdate'],
+ 'smwgFulltextSearchTableOptions' => $GLOBALS['smwgFulltextSearchTableOptions'],
+ 'smwgFulltextSearchPropertyExemptionList' => $GLOBALS['smwgFulltextSearchPropertyExemptionList'],
+ 'smwgFulltextSearchMinTokenSize' => $GLOBALS['smwgFulltextSearchMinTokenSize'],
+ 'smwgFulltextLanguageDetection' => $GLOBALS['smwgFulltextLanguageDetection'],
+ 'smwgFulltextSearchIndexableDataTypes' => $GLOBALS['smwgFulltextSearchIndexableDataTypes'],
+ 'smwgQueryResultCacheType' => $GLOBALS['smwgQueryResultCacheType'],
+ 'smwgQueryResultCacheLifetime' => $GLOBALS['smwgQueryResultCacheLifetime'],
+ 'smwgQueryResultNonEmbeddedCacheLifetime' => $GLOBALS['smwgQueryResultNonEmbeddedCacheLifetime'],
+ 'smwgQueryResultCacheRefreshOnPurge' => $GLOBALS['smwgQueryResultCacheRefreshOnPurge'],
+ 'smwgEditProtectionRight' => $GLOBALS['smwgEditProtectionRight'],
+ 'smwgCreateProtectionRight' => $GLOBALS['smwgCreateProtectionRight'],
+ 'smwgSimilarityLookupExemptionProperty' => $GLOBALS['smwgSimilarityLookupExemptionProperty'],
+ 'smwgPropertyInvalidCharacterList' => $GLOBALS['smwgPropertyInvalidCharacterList'],
+ 'smwgPropertyReservedNameList' => $GLOBALS['smwgPropertyReservedNameList'],
+ 'smwgEntityCollation' => $GLOBALS['smwgEntityCollation'],
+ 'smwgExperimentalFeatures' => $GLOBALS['smwgExperimentalFeatures'],
+ 'smwgFieldTypeFeatures' => $GLOBALS['smwgFieldTypeFeatures'],
+ 'smwgChangePropagationProtection' => $GLOBALS['smwgChangePropagationProtection'],
+ 'smwgUseComparableContentHash' => $GLOBALS['smwgUseComparableContentHash'],
+ 'smwgBrowseFeatures' => $GLOBALS['smwgBrowseFeatures'],
+ 'smwgCategoryFeatures' => $GLOBALS['smwgCategoryFeatures'],
+ 'smwgURITypeSchemeList' => $GLOBALS['smwgURITypeSchemeList'],
+ 'smwgSchemaTypes' => $GLOBALS['smwgSchemaTypes'],
+ 'smwgElasticsearchConfig' => $GLOBALS['smwgElasticsearchConfig'],
+ 'smwgElasticsearchProfile' => $GLOBALS['smwgElasticsearchProfile'],
+ 'smwgElasticsearchEndpoints' => $GLOBALS['smwgElasticsearchEndpoints'],
+ 'smwgPostEditUpdate' => $GLOBALS['smwgPostEditUpdate'],
+ 'smwgSpecialAskFormSubmitMethod' => $GLOBALS['smwgSpecialAskFormSubmitMethod'],
+ 'smwgSupportSectionTag' => $GLOBALS['smwgSupportSectionTag'],
+ ];
+
+ self::initLegacyMapping( $configuration );
+
+ \Hooks::run( 'SMW::Config::BeforeCompletion', [ &$configuration ] );
+
+ if ( self::$instance === null ) {
+ self::$instance = self::newFromArray( $configuration );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Factory method for immediate instantiation of a settings object for a
+ * given array
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromArray( array( 'Foo' => 'Bar' ) );
+ * $settings->get( 'Foo' );
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @return Settings
+ */
+ public static function newFromArray( array $settings ) {
+ return new self( $settings );
+ }
+
+ /**
+ * Returns settings for a given key (nested settings are supported)
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromArray( array(
+ * 'Foo' => 'Bar'
+ * 'Parent' => array(
+ * 'Child' => array( 'Lisa', 'Lula', array( 'Lila' ) )
+ * )
+ * );
+ *
+ * $settings->get( 'Child' ) will return array( 'Lisa', 'Lula', array( 'Lila' ) )
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param string $key
+ *
+ * @return mixed
+ * @throws SettingNotFoundException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return parent::get( $key );
+ }
+
+ // If the key wasn't matched it could be because of a nested array
+ // hence iterate and verify otherwise throw an exception
+ return $this->doIterate( $key, $this->toArray() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function safeGet( $key, $default = false ) {
+
+ try {
+ $r = $this->get( $key );
+ } catch ( SettingNotFoundException $e ) {
+ return $default;
+ }
+
+ return $r;
+ }
+
+ /**
+ * @since 1.9
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * Iterates over a nested array to find an element
+ */
+ private function doIterate( $key, $options ) {
+
+ if ( isset( $this->iterate[$key] ) ) {
+ return $this->iterate[$key];
+ }
+
+ $iterator = new \RecursiveIteratorIterator(
+ new \RecursiveArrayIterator( $options ),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
+
+ foreach( $iterator as $it => $value ) {
+ if ( $key === $it ) {
+ return $this->iterate[$key] = $value;
+ }
+ }
+
+ throw new SettingNotFoundException( "'{$key}' is not a valid settings key" );
+ }
+
+ private static function initLegacyMapping( &$configuration ) {
+
+ if ( isset( $GLOBALS['smwgAdminRefreshStore'] ) && $GLOBALS['smwgAdminRefreshStore'] === false ) {
+ $configuration['smwgAdminFeatures'] = $configuration['smwgAdminFeatures'] & ~SMW_ADM_REFRESH;
+ }
+
+ // smwgParserFeatures
+ if ( isset( $GLOBALS['smwgEnabledInTextAnnotationParserStrictMode'] ) && $GLOBALS['smwgEnabledInTextAnnotationParserStrictMode'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_STRICT;
+ }
+
+ if ( isset( $GLOBALS['smwgInlineErrors'] ) && $GLOBALS['smwgInlineErrors'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_INL_ERROR;
+ }
+
+ if ( isset( $GLOBALS['smwgShowHiddenCategories'] ) && $GLOBALS['smwgShowHiddenCategories'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_HID_CATS;
+ }
+
+ // smwgFactboxFeatures
+ if ( isset( $GLOBALS['smwgFactboxUseCache'] ) && $GLOBALS['smwgFactboxUseCache'] === false ) {
+ $configuration['smwgFactboxFeatures'] = $configuration['smwgFactboxFeatures'] & ~SMW_FACTBOX_CACHE;
+ }
+
+ if ( isset( $GLOBALS['smwgFactboxCacheRefreshOnPurge'] ) && $GLOBALS['smwgFactboxCacheRefreshOnPurge'] === false ) {
+ $configuration['smwgFactboxFeatures'] = $configuration['smwgFactboxFeatures'] & ~SMW_FACTBOX_PURGE_REFRESH;
+ }
+
+ // smwgLinksInValues
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === SMW_LINV_PCRE ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === SMW_LINV_OBFU ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === true ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ // smwgCategoryFeatures
+ if ( isset( $GLOBALS['smwgUseCategoryRedirect'] ) && $GLOBALS['smwgUseCategoryRedirect'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_REDIRECT;
+ }
+
+ if ( isset( $GLOBALS['smwgCategoriesAsInstances'] ) && $GLOBALS['smwgCategoriesAsInstances'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_INSTANCE;
+ }
+
+ if ( isset( $GLOBALS['smwgUseCategoryHierarchy'] ) && $GLOBALS['smwgUseCategoryHierarchy'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_HIERARCHY;
+ }
+
+ if ( isset( $GLOBALS['smwgQueryDependencyPropertyExemptionlist'] ) ) {
+ $configuration['smwgQueryDependencyPropertyExemptionList'] = $GLOBALS['smwgQueryDependencyPropertyExemptionlist'];
+ }
+
+ if ( isset( $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionlist'] ) ) {
+ $configuration['smwgQueryDependencyAffiliatePropertyDetectionList'] = $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionlist'];
+ }
+
+ // smwgPropertyListLimit
+ if ( isset( $GLOBALS['smwgSubPropertyListLimit'] ) ) {
+ $configuration['smwgPropertyListLimit']['subproperty'] = $GLOBALS['smwgSubPropertyListLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgRedirectPropertyListLimit'] ) ) {
+ $configuration['smwgPropertyListLimit']['redirect'] = $GLOBALS['smwgRedirectPropertyListLimit'];
+ }
+
+ // smwgCacheUsage
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgStatisticsCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.statistics'] = $GLOBALS['smwgCacheUsage']['smwgStatisticsCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgStatisticsCache'] ) && $GLOBALS['smwgCacheUsage']['smwgStatisticsCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.statistics'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.properties'] = $GLOBALS['smwgCacheUsage']['smwgPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.properties'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.unusedproperties'] = $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.unusedproperties'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.wantedproperties'] = $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.wantedproperties'] = false;
+ }
+
+ // smwgQueryProfiler
+ if ( isset( $GLOBALS['smwgQueryProfiler']['smwgQueryDurationEnabled'] ) && $GLOBALS['smwgQueryProfiler']['smwgQueryDurationEnabled'] === true ) {
+ $configuration['smwgQueryProfiler'] = $configuration['smwgQueryProfiler'] | SMW_QPRFL_DUR;
+ }
+
+ if ( isset( $GLOBALS['smwgQueryProfiler']['smwgQueryParametersEnabled'] ) && $GLOBALS['smwgQueryProfiler']['smwgQueryParametersEnabled'] === true ) {
+ $configuration['smwgQueryProfiler'] = $configuration['smwgQueryProfiler'] | SMW_QPRFL_PARAMS;
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDatabaseConnector'] ) ) {
+ $configuration['smwgSparqlRepositoryConnector'] = $GLOBALS['smwgSparqlDatabaseConnector'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDatabase'] ) ) {
+ $configuration['smwgSparqlCustomConnector'] = $GLOBALS['smwgSparqlDatabase'];
+ }
+
+ if ( isset( $GLOBALS['smwgDeclarationProperties'] ) ) {
+ $configuration['smwgChangePropagationWatchlist'] = $GLOBALS['smwgDeclarationProperties'];
+ }
+
+ // smwgBrowseFeatures
+ if ( isset( $GLOBALS['smwgToolboxBrowseLink'] ) && $GLOBALS['smwgToolboxBrowseLink'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_TLINK;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseShowInverse'] ) && $GLOBALS['smwgBrowseShowInverse'] === true ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] | SMW_BROWSE_SHOW_INVERSE;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseShowAll'] ) && $GLOBALS['smwgBrowseShowAll'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_SHOW_INCOMING;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseByApi'] ) && $GLOBALS['smwgBrowseByApi'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_USE_API;
+ }
+
+ // smwgQSortFeatures
+ if ( isset( $GLOBALS['smwgQSortingSupport'] ) && $GLOBALS['smwgQSortingSupport'] === false ) {
+ $configuration['smwgQSortFeatures'] = $configuration['smwgQSortFeatures'] & ~SMW_QSORT;
+ }
+
+ if ( isset( $GLOBALS['smwgQRandSortingSupport'] ) && $GLOBALS['smwgQRandSortingSupport'] === false ) {
+ $configuration['smwgQSortFeatures'] = $configuration['smwgQSortFeatures'] & ~SMW_QSORT_RANDOM;
+ }
+
+ if ( isset( $GLOBALS['smwgImportFileDir'] ) ) {
+ $configuration['smwgImportFileDirs'] = (array)$GLOBALS['smwgImportFileDir'];
+ }
+
+ // smwgValueLookupFeatures
+ if ( isset( $GLOBALS['smwgValueLookupCacheType'] ) ) {
+ $configuration['smwgEntityLookupCacheType'] = $GLOBALS['smwgValueLookupCacheType'];
+ }
+
+ if ( isset( $GLOBALS['smwgValueLookupCacheLifetime'] ) ) {
+ $configuration['smwgEntityLookupCacheLifetime'] = $GLOBALS['smwgValueLookupCacheLifetime'];
+ }
+
+ if ( isset( $GLOBALS['smwgValueLookupFeatures'] ) ) {
+ $configuration['smwgEntityLookupFeatures'] = $GLOBALS['smwgValueLookupFeatures'];
+ }
+
+ // smwgPagingLimit
+ if ( isset( $GLOBALS['smwgTypePagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['type'] = $GLOBALS['smwgTypePagingLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgConceptPagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['concept'] = $GLOBALS['smwgConceptPagingLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgPropertyPagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['property'] = $GLOBALS['smwgPropertyPagingLimit'];
+ }
+
+ // smwgSparqlEndpoint
+ if ( isset( $GLOBALS['smwgSparqlQueryEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['query'] = $GLOBALS['smwgSparqlQueryEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlUpdateEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['update'] = $GLOBALS['smwgSparqlUpdateEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDataEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['data'] = $GLOBALS['smwgSparqlDataEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheType'] ) ) {
+ $configuration['smwgMainCacheType'] = $GLOBALS['smwgCacheType'];
+ }
+
+ $jobQueueWatchlist = [];
+
+ // FIXME Remove with 3.1
+ foreach ( $GLOBALS['smwgJobQueueWatchlist'] as $job ) {
+ if ( strpos( $job, 'SMW\\' ) !== false ) {
+ $jobQueueWatchlist[$job] = \SMW\MediaWiki\JobQueue::mapLegacyType( $job );
+ }
+ }
+
+ // Deprecated mapping used in DeprecationNoticeTaskHandler to detect and
+ // output notices
+ $GLOBALS['smwgDeprecationNotices']['smw'] = [
+ 'notice' => [
+ 'smwgAdminRefreshStore' => '3.1.0',
+ 'smwgQueryDependencyPropertyExemptionlist' => '3.1.0',
+ 'smwgQueryDependencyAffiliatePropertyDetectionlist' => '3.1.0',
+ 'smwgSubPropertyListLimit' => '3.1.0',
+ 'smwgRedirectPropertyListLimit' => '3.1.0',
+ 'smwgSparqlDatabaseConnector' => '3.1.0',
+ 'smwgSparqlDatabase' => '3.1.0',
+ 'smwgDeclarationProperties' => '3.1.0',
+ 'smwgToolboxBrowseLink' => '3.1.0',
+ 'smwgBrowseShowInverse' => '3.1.0',
+ 'smwgBrowseShowAll' => '3.1.0',
+ 'smwgBrowseByApi' => '3.1.0',
+ 'smwgEnabledInTextAnnotationParserStrictMode' => '3.1.0',
+ 'smwgInlineErrors' => '3.1.0',
+ 'smwgShowHiddenCategories' => '3.1.0',
+ 'smwgUseCategoryRedirect' => '3.1.0',
+ 'smwgCategoriesAsInstances' => '3.1.0',
+ 'smwgUseCategoryHierarchy' => '3.1.0',
+ 'smwgQSortingSupport' => '3.1.0',
+ 'smwgQRandSortingSupport' => '3.1.0',
+ 'smwgLinksInValues' => '3.1.0',
+ 'smwgTypePagingLimit' => '3.1.0',
+ 'smwgConceptPagingLimit' => '3.1.0',
+ 'smwgPropertyPagingLimit' => '3.1.0',
+ 'smwgSparqlQueryEndpoint' => '3.1.0',
+ 'smwgSparqlUpdateEndpoint' => '3.1.0',
+ 'smwgSparqlDataEndpoint' => '3.1.0',
+ 'smwgCacheType' => '3.1.0',
+ 'smwgFactboxUseCache' => '3.1.0',
+ 'smwgFactboxCacheRefreshOnPurge' => '3.1.0',
+ 'options' => [
+ 'smwgCacheUsage' => [
+ 'smwgStatisticsCache' => '3.1.0',
+ 'smwgStatisticsCacheExpiry' => '3.1.0',
+ 'smwgPropertiesCache' => '3.1.0',
+ 'smwgPropertiesCacheExpiry' => '3.1.0',
+ 'smwgUnusedPropertiesCache' => '3.1.0',
+ 'smwgUnusedPropertiesCacheExpiry' => '3.1.0',
+ 'smwgWantedPropertiesCache' => '3.1.0',
+ 'smwgWantedPropertiesCacheExpiry' => '3.1.0',
+ ],
+ 'smwgQueryProfiler' => [
+ 'smwgQueryDurationEnabled' => '3.1.0',
+ 'smwgQueryParametersEnabled' => '3.1.0'
+ ]
+ ]
+ ],
+ 'replacement' => [
+ 'smwgAdminRefreshStore' => 'smwgAdminFeatures',
+ 'smwgQueryDependencyPropertyExemptionlist' => 'smwgQueryDependencyPropertyExemptionList',
+ 'smwgQueryDependencyAffiliatePropertyDetectionlist' => 'smwgQueryDependencyAffiliatePropertyDetectionList',
+ 'smwgSubPropertyListLimit' => 'smwgPropertyListLimit',
+ 'smwgRedirectPropertyListLimit' => 'smwgPropertyListLimit',
+ 'smwgSparqlDatabaseConnector' => 'smwgSparqlRepositoryConnector',
+ 'smwgSparqlDatabase' => 'smwgSparqlCustomConnector',
+ 'smwgDeclarationProperties' => 'smwgChangePropagationWatchlist',
+ 'smwgToolboxBrowseLink' => 'smwgBrowseFeatures',
+ 'smwgBrowseShowInverse' => 'smwgBrowseFeatures',
+ 'smwgBrowseShowAll' => 'smwgBrowseFeatures',
+ 'smwgBrowseByApi' => 'smwgBrowseFeatures',
+ 'smwgEnabledInTextAnnotationParserStrictMode' => 'smwgParserFeatures',
+ 'smwgInlineErrors' => 'smwgParserFeatures',
+ 'smwgShowHiddenCategories' => 'smwgParserFeatures',
+ 'smwgLinksInValues' => 'smwgParserFeatures',
+ 'smwgUseCategoryRedirect' => 'smwgCategoryFeatures',
+ 'smwgCategoriesAsInstances' => 'smwgCategoryFeatures',
+ 'smwgUseCategoryHierarchy' => 'smwgCategoryFeatures',
+ 'smwgQSortingSupport' => 'smwgQSortFeatures',
+ 'smwgQRandSortingSupport' => 'smwgQSortFeatures',
+ 'smwgImportFileDir' => 'smwgImportFileDirs',
+ 'smwgValueLookupCacheType' => 'smwgEntityLookupCacheType',
+ 'smwgValueLookupCacheLifetime' => 'smwgEntityLookupCacheLifetime',
+ 'smwgValueLookupFeatures' => 'smwgEntityLookupFeatures',
+ 'smwgTypePagingLimit' => 'smwgPagingLimit',
+ 'smwgConceptPagingLimit' => 'smwgPagingLimit',
+ 'smwgPropertyPagingLimit' => 'smwgPagingLimit',
+ 'smwgSparqlQueryEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgSparqlUpdateEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgSparqlDataEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgCacheType' => 'smwgMainCacheType',
+ 'smwgFactboxUseCache' => 'smwgFactboxFeatures',
+ 'smwgFactboxCacheRefreshOnPurge' => 'smwgFactboxFeatures',
+ 'options' => [
+ 'smwgCacheUsage' => [
+ 'smwgStatisticsCacheExpiry' => 'special.statistics',
+ 'smwgPropertiesCacheExpiry' => 'special.properties',
+ 'smwgUnusedPropertiesCacheExpiry' => 'special.unusedproperties',
+ 'smwgWantedPropertiesCacheExpiry' => 'special.wantedproperties',
+ ],
+ 'smwgQueryProfiler' => [
+ 'smwgQueryDurationEnabled' => 'SMW_QPRFL_DUR',
+ 'smwgQueryParametersEnabled' => 'SMW_QPRFL_PARAMS'
+ ]
+ ] + ( $jobQueueWatchlist !== [] ? [ 'smwgJobQueueWatchlist' => $jobQueueWatchlist ] : [] )
+ ],
+ 'removal' => [
+ 'smwgOnDeleteAction' => '2.4.0',
+ 'smwgAutocompleteInSpecialAsk' => '3.0.0',
+ 'smwgSparqlDatabaseMaster' => '3.0.0',
+ 'smwgHistoricTypeNamespace' => '3.0.0',
+ 'smwgEnabledHttpDeferredJobRequest' => '3.0.0'
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php b/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php
new file mode 100644
index 00000000..33d215a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php
@@ -0,0 +1,441 @@
+<?php
+
+namespace SMW;
+
+use Hooks;
+use SMW\Connection\ConnectionManager;
+use SMW\MediaWiki\Hooks\HookRegistry;
+use SMW\SQLStore\Installer;
+
+/**
+ * Extension setup and registration
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+final class Setup {
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory;
+
+ /**
+ * @since 1.9
+ *
+ * @param ApplicationFactory $applicationFactory
+ */
+ public function __construct( ApplicationFactory $applicationFactory ) {
+ $this->applicationFactory = $applicationFactory;
+ }
+
+ /**
+ * Runs at the earliest possible event to initialize functions or hooks that
+ * are otherwise too late for the hook system to be recognized.
+ *
+ * @since 3.0
+ */
+ public static function initExtension( &$vars ) {
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Localisation#Localising_namespaces_and_special_page_aliases
+ */
+ $vars['wgMessagesDirs']['SemanticMediaWiki'] = $vars['smwgIP'] . 'i18n';
+ $vars['wgExtensionMessagesFiles']['SemanticMediaWikiAlias'] = $vars['smwgIP'] . 'i18n/extra/SemanticMediaWiki.alias.php';
+ $vars['wgExtensionMessagesFiles']['SemanticMediaWikiMagic'] = $vars['smwgIP'] . 'i18n/extra/SemanticMediaWiki.magic.php';
+
+ HookRegistry::initExtension( $vars );
+ }
+
+ /**
+ * @see HookRegistry::initExtension
+ */
+ public static function getAPIModules() {
+
+ if ( !ApplicationFactory::getInstance()->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return [];
+ }
+
+ return [
+ 'smwinfo' => '\SMW\MediaWiki\Api\Info',
+ 'smwtask' => '\SMW\MediaWiki\Api\Task',
+ 'smwbrowse' => '\SMW\MediaWiki\Api\Browse',
+ 'ask' => '\SMW\MediaWiki\Api\Ask',
+ 'askargs' => '\SMW\MediaWiki\Api\AskArgs',
+ 'browsebysubject' => '\SMW\MediaWiki\Api\BrowseBySubject',
+ 'browsebyproperty' => '\SMW\MediaWiki\Api\BrowseByProperty'
+ ];
+ }
+
+ /**
+ * @see HookRegistry::initExtension
+ */
+ public static function initSpecialPageList( array &$specialPages ) {
+
+ if ( !ApplicationFactory::getInstance()->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ $specials = [
+ 'Ask' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialAsk'
+ ],
+ 'Browse' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialBrowse'
+ ],
+ 'PageProperty' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialPageProperty'
+ ],
+ 'SearchByProperty' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialSearchByProperty'
+ ],
+ 'ProcessingErrorList' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialProcessingErrorList'
+ ],
+ 'PropertyLabelSimilarity' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialPropertyLabelSimilarity'
+ ],
+ 'SMWAdmin' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialAdmin'
+ ],
+ 'Concepts' => [
+ 'page' => 'SMW\SpecialConcepts'
+ ],
+ 'ExportRDF' => [
+ 'page' => 'SMWSpecialOWLExport'
+ ],
+ 'Types' => [
+ 'page' => 'SMWSpecialTypes'
+ ],
+ 'URIResolver' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialURIResolver'
+ ],
+ 'Properties' => [
+ 'page' => 'SMW\SpecialProperties'
+ ],
+ 'UnusedProperties' => [
+ 'page' => 'SMW\SpecialUnusedProperties'
+ ],
+ 'WantedProperties' => [
+ 'page' => 'SMW\SpecialWantedProperties'
+ ],
+ 'DeferredRequestDispatcher' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialDeferredRequestDispatcher'
+ ],
+ ];
+
+ // Register data
+ foreach ( $specials as $special => $page ) {
+ $specialPages[$special] = $page['page'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function isEnabled() {
+ return defined( 'SMW_VERSION' ) && $GLOBALS['smwgSemanticsEnabled'];
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function isValid( $isCli = false ) {
+ return Installer::isGoodSchema( $isCli );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$vars
+ */
+ public function loadSchema( &$vars ) {
+ Installer::loadSchema( $vars );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array &$vars
+ * @param string $directory
+ */
+ public function init( &$vars, $directory ) {
+
+ $this->initMessageCallbackHandler();
+
+ if ( $this->isValid() === false ) {
+ smwfAbort(
+ Message::get( [ 'smw-upgrade-error', $vars['smwgUpgradeKey'] ], Message::PARSE ) .
+ '<h3>' . Message::get( 'smw-upgrade-error-why-title' ) . '</h3>' .
+ Message::get( 'smw-upgrade-error-why-explain', Message::PARSE ) .
+ '<h3>' . Message::get( 'smw-upgrade-error-how-title' ) . '</h3>' .
+ Message::get( 'smw-upgrade-error-how-explain', Message::PARSE )
+ );
+ }
+
+ $this->addDefaultConfigurations( $vars );
+
+ if ( CompatibilityMode::extensionNotEnabled() ) {
+ CompatibilityMode::disableSemantics();
+ }
+
+ $this->initConnectionProviders( );
+
+ $this->registerJobClasses( $vars );
+ $this->registerPermissions( $vars );
+
+ $this->registerParamDefinitions( $vars );
+ $this->registerFooterIcon( $vars, $directory );
+ $this->registerHooks( $vars, $directory );
+
+ Hooks::run( 'SMW::Setup::AfterInitializationComplete', [ &$vars ] );
+ }
+
+ private function addDefaultConfigurations( &$vars ) {
+
+ $vars['wgLogTypes'][] = 'smw';
+ $vars['wgFilterLogTypes']['smw'] = true;
+
+ $vars['smwgMasterStore'] = null;
+ $vars['smwgIQRunningNumber'] = 0;
+
+ if ( !isset( $vars['smwgNamespace'] ) ) {
+ $vars['smwgNamespace'] = parse_url( $vars['wgServer'], PHP_URL_HOST );
+ }
+
+ foreach ( $vars['smwgResourceLoaderDefFiles'] as $key => $file ) {
+ if ( is_readable( $file ) ) {
+ $vars['wgResourceModules'] = array_merge( $vars['wgResourceModules'], include ( $file ) );
+ }
+ }
+ }
+
+ private function initConnectionProviders() {
+
+ $mwCollaboratorFactory = $this->applicationFactory->newMwCollaboratorFactory();
+ $connectionManager = $this->applicationFactory->getConnectionManager();
+
+ $connectionManager->registerConnectionProvider(
+ DB_MASTER,
+ $mwCollaboratorFactory->newLoadBalancerConnectionProvider( DB_MASTER )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ DB_SLAVE,
+ $mwCollaboratorFactory->newLoadBalancerConnectionProvider( DB_SLAVE )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ 'mw.db',
+ $mwCollaboratorFactory->newConnectionProvider( 'mw.db' )
+ );
+
+ // Connection can be used to redirect queries to another DB cluster
+ $connectionManager->registerConnectionProvider(
+ 'mw.db.queryengine',
+ $mwCollaboratorFactory->newConnectionProvider( 'mw.db.queryengine' )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ 'elastic',
+ $this->applicationFactory->singleton( 'ElasticFactory' )->newConnectionProvider()
+ );
+ }
+
+ private function initMessageCallbackHandler() {
+
+ Message::registerCallbackHandler( Message::TEXT, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ return call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language )->text();
+ } );
+
+ Message::registerCallbackHandler( Message::ESCAPED, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ return call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language )->escaped();
+ } );
+
+ Message::registerCallbackHandler( Message::PARSE, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ $message = call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language );
+
+ // 1.27+
+ // [GlobalTitleFail] MessageCache::parse called by ...
+ // Message::parseText/MessageCache::parse with no title set.
+ //
+ // Message::setInterfaceMessageFlag "... used to restore the flag
+ // after setting a language"
+ return $message->setInterfaceMessageFlag( true )->title( $GLOBALS['wgTitle'] )->parse();
+ } );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgJobClasses
+ */
+ private function registerJobClasses( &$vars ) {
+
+ $jobClasses = [
+
+ 'smw.update' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'smw.refresh' => 'SMW\MediaWiki\Jobs\RefreshJob',
+ 'smw.updateDispatcher' => 'SMW\MediaWiki\Jobs\UpdateDispatcherJob',
+ 'smw.parserCachePurge' => 'SMW\MediaWiki\Jobs\ParserCachePurgeJob',
+ 'smw.fulltextSearchTableUpdate' => 'SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob',
+ 'smw.entityIdDisposer' => 'SMW\MediaWiki\Jobs\EntityIdDisposerJob',
+ 'smw.propertyStatisticsRebuild' => 'SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob',
+ 'smw.fulltextSearchTableRebuild' => 'SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob',
+ 'smw.changePropagationDispatch' => 'SMW\MediaWiki\Jobs\ChangePropagationDispatchJob',
+ 'smw.changePropagationUpdate' => 'SMW\MediaWiki\Jobs\ChangePropagationUpdateJob',
+ 'smw.changePropagationClassUpdate' => 'SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob',
+ 'smw.elasticIndexerRecovery' => 'SMW\Elastic\Indexer\IndexerRecoveryJob',
+ 'smw.elasticFileIngest' => 'SMW\Elastic\Indexer\FileIngestJob',
+
+ // Legacy 3.0-
+ 'SMW\UpdateJob' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'SMW\RefreshJob' => 'SMW\MediaWiki\Jobs\RefreshJob',
+ 'SMW\UpdateDispatcherJob' => 'SMW\MediaWiki\Jobs\UpdateDispatcherJob',
+ 'SMW\ParserCachePurgeJob' => 'SMW\MediaWiki\Jobs\ParserCachePurgeJob',
+ 'SMW\FulltextSearchTableUpdateJob' => 'SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob',
+ 'SMW\EntityIdDisposerJob' => 'SMW\MediaWiki\Jobs\EntityIdDisposerJob',
+ 'SMW\PropertyStatisticsRebuildJob' => 'SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob',
+ 'SMW\FulltextSearchTableRebuildJob' => 'SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob',
+ 'SMW\ChangePropagationDispatchJob' => 'SMW\MediaWiki\Jobs\ChangePropagationDispatchJob',
+ 'SMW\ChangePropagationUpdateJob' => 'SMW\MediaWiki\Jobs\ChangePropagationUpdateJob',
+ 'SMW\ChangePropagationClassUpdateJob' => 'SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob',
+
+ // Legacy 2.0-
+ 'SMWUpdateJob' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'SMWRefreshJob' => 'SMW\MediaWiki\Jobs\RefreshJob'
+ ];
+
+ foreach ( $jobClasses as $job => $class ) {
+ $vars['wgJobClasses'][$job] = $class;
+ }
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgAvailableRights
+ * @see https://www.mediawiki.org/wiki/Manual:$wgGroupPermissions
+ */
+ private function registerPermissions( &$vars ) {
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ $rights = [
+ 'smw-admin' => [
+ 'sysop',
+ 'smwadministrator'
+ ],
+ 'smw-patternedit' => [
+ 'smwcurator'
+ ],
+ 'smw-schemaedit' => [
+ 'smwcurator'
+ ],
+ 'smw-pageedit' => [
+ 'smwcurator'
+ ],
+ // 'smw-watchlist' => [
+ // 'smwcurator'
+ // ],
+ ];
+
+ foreach ( $rights as $right => $roles ) {
+
+ // Rights
+ $vars['wgAvailableRights'][] = $right;
+
+ // User group rights
+ foreach ( $roles as $role ) {
+ if ( !isset( $vars['wgGroupPermissions'][$role][$right] ) ) {
+ $vars['wgGroupPermissions'][$role][$right] = true;
+ }
+ }
+ }
+
+ // Add an additional protection level restricting edit/move/etc
+ if ( ( $editProtectionRight = $this->applicationFactory->getSettings()->get( 'smwgEditProtectionRight' ) ) !== false ) {
+ $vars['wgRestrictionLevels'][] = $editProtectionRight;
+ }
+ }
+
+ private function registerParamDefinitions( &$vars ) {
+ $vars['wgParamDefinitions']['smwformat'] = [
+ 'definition'=> 'SMWParamFormat',
+ ];
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgFooterIcons
+ */
+ private function registerFooterIcon( &$vars, $path ) {
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ if( isset( $vars['wgFooterIcons']['poweredby']['semanticmediawiki'] ) ) {
+ return;
+ }
+
+ $src = '';
+
+ if ( is_file( $path . '/res/DataURI.php' ) && ( $dataURI = include $path . '/res/DataURI.php' ) !== [] ) {
+ $src = $dataURI['footer'];
+ }
+
+ $vars['wgFooterIcons']['poweredby']['semanticmediawiki'] = [
+ 'src' => $src,
+ 'url' => 'https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki',
+ 'alt' => 'Powered by Semantic MediaWiki'
+ ];
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgHooks
+ *
+ * @note $wgHooks contains a list of hooks which specifies for every event an
+ * array of functions to be called.
+ */
+ private function registerHooks( &$vars, $directory ) {
+
+ $hookRegistry = new HookRegistry( $vars, $directory );
+ $hookRegistry->register();
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ // Old-style registration
+ $vars['wgHooks']['AdminLinks'][] = 'SMWExternalHooks::addToAdminLinks';
+ $vars['wgHooks']['PageSchemasRegisterHandlers'][] = 'SMWExternalHooks::onPageSchemasRegistration';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php b/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php
new file mode 100644
index 00000000..f1d129a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use SMW\Exception\SubSemanticDataException;
+use SMWContainerSemanticData;
+use SMWDataValue;
+use SMWDIContainer;
+use Title;
+
+/**
+ * @see http://www.semantic-mediawiki.org/wiki/Help:Subobject
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Subobject {
+
+ /**
+ * @var Title
+ */
+ protected $title;
+
+ /**
+ * @var SMWContainerSemanticData
+ */
+ protected $semanticData;
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ */
+ public function __construct( Title $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * Returns the Title object
+ *
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->getSemanticData()->getSubject();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getSubobjectId() {
+ return $this->getSemanticData()->getSubject()->getSubobjectName();
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array|string $error
+ */
+ public function addError( $error ) {
+
+ if ( is_string( $error ) ) {
+ $error = [ md5( $error ) => $error ];
+ }
+
+ // Preserve the keys, avoid using array_merge to avert a possible
+ // Fatal error: Allowed memory size of ... bytes exhausted ... Subobject.php on line 89
+ $this->errors += $error;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $identifier
+ *
+ * @return self
+ * @throws InvalidArgumentException
+ */
+ public function setEmptyContainerForId( $identifier ) {
+
+ if ( $identifier === '' ) {
+ throw new InvalidArgumentException( 'Expected a valid (non-empty) indentifier' );
+ }
+
+ $subWikiPage = new DIWikiPage(
+ $this->title->getDBkey(),
+ $this->title->getNamespace(),
+ $this->title->getInterwiki(),
+ $identifier
+ );
+
+ $this->semanticData = new SMWContainerSemanticData( $subWikiPage );
+
+ return $this;
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ public function setSemanticData( $identifier ) {
+ $this->setEmptyContainerForId( $identifier );
+ }
+
+ /**
+ * Returns semantic data container for a subobject
+ *
+ * @since 1.9
+ *
+ * @return SMWContainerSemanticData
+ */
+ public function getSemanticData() {
+
+ if ( !( $this->semanticData instanceof SMWContainerSemanticData ) ) {
+ throw new SubSemanticDataException( 'The semantic data container is not initialized' );
+ }
+
+ return $this->semanticData;
+ }
+
+ /**
+ * Returns the property data item for the subobject
+ *
+ * @since 1.9
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return new DIProperty( DIProperty::TYPE_SUBOBJECT );
+ }
+
+ /**
+ * Returns the container data item for the subobject
+ *
+ * @since 1.9
+ *
+ * @return SMWDIContainer
+ */
+ public function getContainer() {
+ return new SMWDIContainer( $this->getSemanticData() );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param DataValue $dataValue
+ *
+ * @throws SubSemanticDataException
+ */
+ public function addDataValue( SMWDataValue $dataValue ) {
+
+ if ( !( $this->semanticData instanceof SMWContainerSemanticData ) ) {
+ throw new SubSemanticDataException( 'The semantic data container is not initialized' );
+ }
+
+ $this->semanticData->addDataValue( $dataValue );
+ $this->addError( $this->semanticData->getErrors() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php
new file mode 100644
index 00000000..ea6cb70c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW;
+
+use SMWDataItem;
+
+/**
+ * This class implements Concept data items.
+ *
+ * @note These special data items for storing concept declaration data in SMW
+ * should vanish at some point since Container values could encode this data
+ * just as well.
+ *
+ * @since 1.6
+ *
+ * @ingroup SMWDataItems
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class DIConcept extends \SMWDataItem {
+
+ /**
+ * Query string for this concept. Possibly long.
+ * @var string
+ */
+ protected $m_concept;
+ /**
+ * Documentation for this concept. Possibly long.
+ * @var string
+ */
+ protected $m_docu;
+ /**
+ * Flags of query features.
+ * @var integer
+ */
+ protected $m_features;
+ /**
+ * Size of the query.
+ * @var integer
+ */
+ protected $m_size;
+ /**
+ * Depth of the query.
+ * @var integer
+ */
+ protected $m_depth;
+
+ /**
+ * Status
+ * @var integer
+ */
+ protected $cacheStatus;
+
+ /**
+ * Date
+ * @var integer
+ */
+ protected $cacheDate;
+
+ /**
+ * Count
+ * @var integer
+ */
+ protected $cacheCount;
+
+ /**
+ * @param string $concept the concept query string
+ * @param string $docu user documentation
+ * @param integer $queryefeatures flags about query features
+ * @param integer $size concept query size
+ * @param integer $depth concept query depth
+ */
+ public function __construct( $concept, $docu, $queryfeatures, $size, $depth ) {
+ $this->m_concept = $concept;
+ $this->m_docu = $docu;
+ $this->m_features = $queryfeatures;
+ $this->m_size = $size;
+ $this->m_depth = $depth;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_CONCEPT;
+ }
+
+ public function getConceptQuery() {
+ return $this->m_concept;
+ }
+
+ public function getDocumentation() {
+ return $this->m_docu;
+ }
+
+ public function getQueryFeatures() {
+ return $this->m_features;
+ }
+
+ public function getSize() {
+ return $this->m_size;
+ }
+
+ public function getDepth() {
+ return $this->m_depth;
+ }
+
+ public function getSortKey() {
+ return $this->m_docu;
+ }
+
+ public function getSerialization() {
+ return serialize( $this );
+ }
+
+ /**
+ * Sets cache status
+ *
+ * @since 1.9
+ *
+ * @param string
+ */
+ public function setCacheStatus( $status ) {
+ $this->cacheStatus = $status;
+ }
+
+ /**
+ * Sets cache date
+ *
+ * @since 1.9
+ *
+ * @param string
+ */
+ public function setCacheDate( $date ) {
+ $this->cacheDate = $date;
+ }
+
+ /**
+ * Sets cache count
+ *
+ * @since 1.9
+ *
+ * @param int
+ */
+ public function setCacheCount( $count ) {
+ $this->cacheCount = $count;
+ }
+
+ /**
+ * Returns cache status
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheStatus() {
+ return $this->cacheStatus;
+ }
+
+ /**
+ * Returns cache date
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheDate() {
+ return $this->cacheDate;
+ }
+
+ /**
+ * Returns cache count
+ *
+ * @since 1.9
+ *
+ * @return int
+ */
+ public function getCacheCount() {
+ return $this->cacheCount;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return DIConcept
+ */
+ public static function doUnserialize( $serialization ) {
+ $result = unserialize( $serialization );
+ if ( $result === false ) {
+ throw new DataItemException( "Unserialization failed." );
+ }
+ return $result;
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_CONCEPT ) {
+ return false;
+ }
+ return $di->getSerialization() === $this->getSerialization();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php
new file mode 100644
index 00000000..fee4d0ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php
@@ -0,0 +1,77 @@
+<?php
+
+use Onoi\Tesa\Normalizer;
+
+/**
+ * @ingroup SMWDataItems
+ */
+
+/**
+ * This class implements blob (long string) data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIBlob extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var string
+ */
+ protected $m_string;
+
+ public function __construct( $string ) {
+ $this->m_string = trim( $string );
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_BLOB;
+ }
+
+ public function getString() {
+ return $this->m_string;
+ }
+
+ public static function normalize( $text ) {
+ return Normalizer::convertDoubleWidth(
+ Normalizer::applyTransliteration(
+ Normalizer::toLowercase( $text )
+ )
+ );
+ }
+
+ public function getSortKey() {
+ return $this->m_string;
+ }
+
+ /**
+ * @see SMWDataItem::getSortKeyDataItem()
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ return $this;
+ }
+
+ public function getSerialization() {
+ return $this->m_string;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIBlob
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDIBlob( $serialization );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( !( $di instanceof SMWDIBlob ) ) {
+ return false;
+ }
+
+ return $di->getString() === $this->m_string;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php
new file mode 100644
index 00000000..8bddf9fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements Boolean data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIBoolean extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var bool
+ */
+ protected $m_boolean;
+
+ public function __construct( $boolean ) {
+ if ( !is_bool( $boolean ) ) {
+ throw new DataItemException( "Initialization value '$boolean' is not a boolean." );
+ }
+
+ $this->m_boolean = ( $boolean == true );
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_BOOLEAN;
+ }
+
+ public function getBoolean() {
+ return $this->m_boolean;
+ }
+
+ public function getSerialization() {
+ return $this->m_boolean ? 't' : 'f';
+ }
+
+ public function getSortKey() {
+ return $this->m_boolean ? 1 : 0;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIBoolean
+ */
+ public static function doUnserialize( $serialization ) {
+ if ( $serialization == 't' ) {
+ return new SMWDIBoolean( true );
+ } elseif ( $serialization == 'f' ) {
+ return new SMWDIBoolean( false );
+ } else {
+ throw new DataItemException( "Boolean data item unserialised from illegal value '$serialization'" );
+ }
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_BOOLEAN ) {
+ return false;
+ }
+ return $di->getBoolean() === $this->m_boolean;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php
new file mode 100644
index 00000000..f7f230e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\DIProperty;
+use SMW\Exception\DataItemException;
+use SMWDIBlob as DIBlob;
+
+/**
+ * This class implements container data items that can store SMWSemanticData
+ * objects. Containers are not dataitems in the proper sense: they do not
+ * represent a single, opaque value that can be assigned to a property. Rather,
+ * a container represents a "subobject" with a number of property-value
+ * assignments. When a container is stored, these individual data assignments
+ * are stored -- the data managed by SMW never contains any "container", just
+ * individual property assignments for the subobject. Likewise, when a container
+ * is used in search, it is interpreted as a patterns of possible property
+ * assignments, and this pattern is searched for.
+ *
+ * The data encapsulated in a container data item is essentially an
+ * SMWSemanticData object of class SMWContainerSemanticData. This class allows
+ * the subject to be kept anonymous if not known (if no context page is
+ * available for finding a suitable subobject name). See the repsective
+ * documentation for details.
+ *
+ * Being a mere placeholder/template for other data, an SMWDIContainer is not
+ * immutable as the other basic data items. New property-value pairs can always
+ * be added to the internal SMWContainerSemanticData.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIContainer extends SMWDataItem {
+
+ /**
+ * Internal value.
+ *
+ * @var SMWSemanticData
+ */
+ protected $m_semanticData;
+
+ /**
+ * Constructor. The given SMWContainerSemanticData object will be owned
+ * by the constructed object afterwards, and in particular will not
+ * allow further changes.
+ *
+ * @param $semanticData SMWContainerSemanticData
+ */
+ public function __construct( SMWContainerSemanticData $semanticData ) {
+ $this->m_semanticData = $semanticData;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_CONTAINER;
+ }
+
+ public function getSemanticData() {
+ return $this->m_semanticData;
+ }
+
+ public function getSortKey() {
+ return '';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $sortKey
+ */
+ public function setSortKey( $sortKey ) {
+ $this->m_semanticData->addPropertyObjectValue(
+ new DIProperty( '_SKEY' ),
+ new DIBlob( $this->m_semanticData->getSubject()->getSortKey() . '#' . $sortKey )
+ );
+ }
+
+ public function getSerialization() {
+ return serialize( $this->m_semanticData );
+ }
+
+ /**
+ * Get a hash string for this data item.
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ $hash = $this->getValueHash( $this->m_semanticData );
+ sort( $hash );
+
+ return md5( implode( '#', $hash ) );
+
+ // We want a value hash, not an entity hash!!
+ // return $this->m_semanticData->getHash();
+ }
+
+ private function getValueHash( $semanticData ) {
+
+ $hash = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $hash[] = $property->getKey();
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $di ) {
+ $hash[] = $di->getHash();
+ }
+ }
+
+ foreach ( $semanticData->getSubSemanticData() as $data ) {
+ $hash[] = $this->getValueHash( $data );
+ }
+
+ return $hash;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ *
+ * @return SMWDIContainer
+ */
+ public static function doUnserialize( $serialization ) {
+ /// TODO May issue an E_NOTICE when problems occur; catch this
+ $data = unserialize( $serialization );
+ if ( !( $data instanceof SMWContainerSemanticData ) ) {
+ throw new DataItemException( "Could not unserialize SMWDIContainer from the given string." );
+ }
+ return new SMWDIContainer( $data );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_CONTAINER ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php
new file mode 100644
index 00000000..099b306a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+/**
+ * This class implements error list data items. These data items are used to
+ * pass around lists of error messages within the application. They are not
+ * meant to be stored or exported, but they can be useful to a user.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIError extends SMWDataItem {
+
+ /**
+ * List of error messages. Should always be safe for HTML.
+ * @var array of strings
+ */
+ protected $m_errors;
+
+ /**
+ * @var string
+ */
+ private $userValue;
+
+ public function __construct( $errors, $userValue = '' ) {
+ $this->m_errors = $errors;
+ $this->userValue = $userValue;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_ERROR;
+ }
+
+ public function getErrors() {
+ return $this->m_errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getUserValue() {
+ return $this->userValue;
+ }
+
+ public function getSortKey() {
+ return 'error';
+ }
+
+ public function getString() {
+ return $this->getSerialization();
+ }
+
+ public function getSerialization() {
+ return serialize( $this->m_errors );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @todo Be more careful with unserialization. It can create E_NOTICEs.
+ * @return SMWDIError
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDIError( unserialize( $serialization ) );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_ERROR ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php
new file mode 100644
index 00000000..208366e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php
@@ -0,0 +1,185 @@
+<?php
+
+use SMW\Exception\DataItemException;
+
+/**
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWDIGeoCoord extends SMWDataItem {
+
+ /**
+ * @var float
+ */
+ private $latitude;
+
+ /**
+ * @var float
+ */
+ private $longitude;
+
+ /**
+ * @var float|null
+ */
+ private $altitude = null;
+
+ /**
+ * Takes a latitude and longitude, and optionally an altitude. These can be provided in 2 forms:
+ * * An associative array with lat, lon and alt keys
+ * * Lat, lon and alt arguments
+ *
+ * The second way to provide the arguments, as well as the altitude argument, where introduced in SMW 1.7.
+ */
+ public function __construct() {
+ $args = func_get_args();
+
+ $count = count( $args );
+
+ if ( $count === 1 && is_array( $args[0] ) ) {
+ if ( array_key_exists( 'lat', $args[0] ) && array_key_exists( 'lon', $args[0] ) ) {
+ $this->setLatitude( $args[0]['lat'] );
+ $this->setLongitude( $args[0]['lon'] );
+
+ if ( array_key_exists( 'alt', $args[0] ) ) {
+ $this->altitude = (float)$args[0]['alt'];
+ }
+ }
+ else {
+ throw new DataItemException( 'Invalid coordinate data passed to the SMWDIGeoCoord constructor' );
+ }
+ }
+ elseif ( $count === 2 || $count === 3 ) {
+ $this->setLatitude( $args[0] );
+ $this->setLongitude( $args[1] );
+
+ if ( $count === 3 ) {
+ $this->altitude = (float)$args[2];
+ }
+ }
+ else {
+ throw new DataItemException( 'Invalid coordinate data passed to the SMWDIGeoCoord constructor' );
+ }
+ }
+
+ private function setLatitude( $latitude ) {
+ if ( is_int( $latitude ) ) {
+ $latitude = (float)$latitude;
+ }
+
+ if ( !is_float( $latitude ) ) {
+ throw new DataItemException( '$latitude should be a float' );
+ }
+
+ $this->latitude = $latitude;
+ }
+
+ private function setLongitude( $longitude ) {
+ if ( is_int( $longitude ) ) {
+ $longitude = (float)$longitude;
+ }
+
+ if ( !is_float( $longitude ) ) {
+ throw new DataItemException( '$longitude should be a float' );
+ }
+
+ $this->longitude = $longitude;
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getDIType()
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_GEO;
+ }
+
+ /**
+ * Returns the coordinate set as an array with lat and long (and alt) keys
+ * pointing to float values.
+ *
+ * @return array
+ */
+ public function getCoordinateSet() {
+ $coords = [ 'lat' => $this->latitude, 'lon' => $this->longitude ];
+
+ if ( !is_null( $this->altitude ) ) {
+ $coords['alt'] = $this->altitude;
+ }
+
+ return $coords;
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getSortKey()
+ */
+ public function getSortKey() {
+ return $this->latitude . ',' . $this->longitude . ( $this->altitude !== null ? ','. $this->altitude : '' );
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getSerialization()
+ */
+ public function getSerialization() {
+ return implode( ',', $this->getCoordinateSet() );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @note PHP can convert any string to some number, so we do not do
+ * validation here (because this would require less efficient parsing).
+ *
+ * @param string $serialization
+ *
+ * @return self
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( ',', $serialization );
+ $count = count( $parts );
+
+ if ( $count !== 2 && $count !== 3 ) {
+ throw new DataItemException( 'Unserialization of coordinates failed' );
+ }
+
+ $coords = [ 'lat' => (float)$parts[0], 'lon' => (float)$parts[1] ];
+
+ if ( $count === 3 ) {
+ $coords['alt'] = (float)$parts[2];
+ }
+
+ return new self( $coords );
+ }
+
+ /**
+ * @return float
+ */
+ public function getLatitude() {
+ return $this->latitude;
+ }
+
+ /**
+ * @return float
+ */
+ public function getLongitude() {
+ return $this->longitude;
+ }
+
+ /**
+ * Returns the altitude if set, null otherwise.
+ *
+ * @return float|null
+ */
+ public function getAltitude() {
+ return $this->altitude;
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_GEO ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php
new file mode 100644
index 00000000..7105f31c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements number data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDINumber extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var float|int
+ */
+ protected $m_number;
+
+ public function __construct( $number ) {
+ if ( !is_numeric( $number ) ) {
+ throw new DataItemException( "Initialization value '$number' is not a number." );
+ }
+ $this->m_number = $number;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_NUMBER;
+ }
+
+ public function getNumber() {
+ return $this->m_number;
+ }
+
+ public function getSortKey() {
+ return $this->m_number;
+ }
+
+ /**
+ * @see SMWDataItem::getSortKeyDataItem()
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ return $this;
+ }
+
+ public function getSerialization() {
+ return strval( $this->m_number );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @note PHP can convert any string to some number, so we do not do
+ * validation here (because this would require less efficient parsing).
+ * @return SMWDINumber
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDINumber( floatval( $serialization ) );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_NUMBER ) {
+ return false;
+ }
+
+ return $di->getNumber() === $this->m_number;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php
new file mode 100644
index 00000000..06e076df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php
@@ -0,0 +1,509 @@
+<?php
+
+namespace SMW;
+
+use RuntimeException;
+use SMW\Exception\DataTypeLookupException;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\Exception\PropertyLabelNotResolvedException;
+use SMWDataItem;
+use SMWDIUri;
+
+/**
+ * This class implements Property item
+ *
+ * @note PropertyRegistry class manages global registrations of predefined
+ * (built-in) properties, and maintains an association of property IDs, localized
+ * labels, and aliases.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class DIProperty extends SMWDataItem {
+
+ /**
+ * @see PropertyRegistry::registerPredefinedProperties
+ */
+ const TYPE_SUBOBJECT = '_SOBJ';
+ const TYPE_ERROR = '_ERRP';
+ const TYPE_CATEGORY = '_INST';
+ const TYPE_SUBCATEGORY = '_SUBC';
+ const TYPE_SORTKEY = '_SKEY';
+ const TYPE_MODIFICATION_DATE = '_MDAT';
+ const TYPE_CREATION_DATE = '_CDAT';
+ const TYPE_LAST_EDITOR = '_LEDT';
+ const TYPE_NEW_PAGE = '_NEWP';
+ const TYPE_HAS_TYPE = '_TYPE';
+ const TYPE_CONVERSION = '_CONV';
+ const TYPE_ASKQUERY = '_ASK';
+ const TYPE_MEDIA = '_MEDIA';
+ const TYPE_MIME = '_MIME';
+ const TYPE_DISPLAYTITLE = '_DTITLE';
+
+ /**
+ * Change propagation
+ */
+ const TYPE_CHANGE_PROP = '_CHGPRO';
+
+ /**
+ * Either an internal SMW property key (starting with "_") or the DB
+ * key of a property page in the wiki.
+ * @var string
+ */
+ private $m_key;
+
+ /**
+ * Whether to take the inverse of this property or not.
+ * @var boolean
+ */
+ private $m_inverse;
+
+ /**
+ * @var string
+ */
+ private $propertyValueType;
+
+ /**
+ * Interwiki prefix for when a property represents a non-local entity
+ *
+ * @var string
+ */
+ private $interwiki = '';
+
+ /**
+ * Initialise a property. This constructor checks that keys of
+ * predefined properties do really exist (in the current configuration
+ * of the wiki). No check is performed to see if a user label is in
+ * fact the label or alias of a predefined property. If this should be
+ * done, the function self::newFromUserLabel() can be used.
+ *
+ * @param $key string key for the property (internal SMW key or wikipage DB key)
+ * @param $inverse boolean states if the inverse of the property is constructed
+ */
+ public function __construct( $key, $inverse = false ) {
+
+ if ( ( $key === '' ) || ( $key{0} == '-' ) ) {
+ throw new PropertyLabelNotResolvedException( "Illegal property key \"$key\"." );
+ }
+
+ if ( $key{0} == '_' ) {
+ if ( !PropertyRegistry::getInstance()->isRegistered( $key ) ) {
+ throw new PredefinedPropertyLabelMismatchException( "There is no predefined property with \"$key\"." );
+ }
+ }
+
+ $this->m_key = $key;
+ $this->m_inverse = $inverse;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_PROPERTY;
+ }
+
+ /**
+ * @return string
+ */
+ public function getKey() {
+ return $this->m_key;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isInverse() {
+ return $this->m_inverse;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSortKey() {
+ return $this->m_key;
+ }
+
+ /**
+ * Specifies whether values of this property should be shown in the
+ * Factbox. A property may wish to prevent this if either
+ * (1) its information is really dull, e.g. being a mere copy of
+ * information that is obvious from other things that are shown, or
+ * (2) the property is set in a hook after parsing, so that it is not
+ * reliably available when Factboxes are displayed. If a property is
+ * internal so it should never be observed by users, then it is better
+ * to just not associate any translated label with it, so it never
+ * appears anywhere.
+ *
+ * Examples of properties that are not shown include Modification date
+ * (not available in time), and Has improper value for (errors are
+ * shown directly on the page anyway).
+ *
+ * @return boolean
+ */
+ public function isShown() {
+
+ if ( $this->isUserDefined() ) {
+ return true;
+ }
+
+ return PropertyRegistry::getInstance()->isVisible( $this->m_key );
+ }
+
+ /**
+ * Return true if this is a usual wiki property that is defined by a
+ * wiki page, and not a property that is pre-defined in the wiki.
+ *
+ * @return boolean
+ */
+ public function isUserDefined() {
+ return $this->m_key{0} != '_';
+ }
+
+ /**
+ * Whether a user can freely use this property for value annotation or
+ * not.
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isUserAnnotable() {
+
+ // A user defined property is generally assumed to be unrestricted for
+ // usage
+ if ( $this->isUserDefined() ) {
+ return true;
+ }
+
+ return PropertyRegistry::getInstance()->isAnnotable( $this->m_key );
+ }
+
+ /**
+ * Find a user-readable label for this property, or return '' if it is
+ * a predefined property that has no label. For inverse properties, the
+ * label starts with a "-".
+ *
+ * @return string
+ */
+ public function getLabel() {
+ $prefix = $this->m_inverse ? '-' : '';
+
+ if ( $this->isUserDefined() ) {
+ return $prefix . str_replace( '_', ' ', $this->m_key );
+ }
+
+ return $prefix . PropertyRegistry::getInstance()->findPropertyLabelById( $this->m_key );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCanonicalLabel() {
+ $prefix = $this->m_inverse ? '-' : '';
+
+ if ( $this->isUserDefined() ) {
+ return $prefix . str_replace( '_', ' ', $this->m_key );
+ }
+
+ return $prefix . PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
+ }
+
+ /**
+ * Borrowing the skos:prefLabel definition where a preferred label is expected
+ * to have only one label per given language (skos:altLabel can have many
+ * alternative labels)
+ *
+ * An empty string signals that no preferred label is available in the current
+ * user language.
+ *
+ * @since 2.5
+ *
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getPreferredLabel( $languageCode = '' ) {
+
+ $label = PropertyRegistry::getInstance()->findPreferredPropertyLabelFromIdByLanguageCode(
+ $this->m_key,
+ $languageCode
+ );
+
+ if ( $label !== '' ) {
+ return ( $this->m_inverse ? '-' : '' ) . $label;
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $interwiki
+ */
+ public function setInterwiki( $interwiki ) {
+ $this->interwiki = $interwiki;
+ }
+
+ /**
+ * Get an object of type DIWikiPage that represents the page which
+ * relates to this property, or null if no such page exists. The latter
+ * can happen for special properties without user-readable label.
+ *
+ * It is possible to construct subobjects of the property's wikipage by
+ * providing an optional subobject name.
+ *
+ * @param string $subobjectName
+ * @return DIWikiPage|null
+ */
+ public function getDiWikiPage( $subobjectName = '' ) {
+
+ $dbkey = $this->m_key;
+
+ if ( !$this->isUserDefined() ) {
+ $dbkey = $this->getLabel();
+ }
+
+ return $this->newDIWikiPage( $dbkey, $subobjectName );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $subobjectName
+ *
+ * @return DIWikiPage|null
+ */
+ public function getCanonicalDiWikiPage( $subobjectName = '' ) {
+
+ if ( $this->isUserDefined() ) {
+ $dbkey = $this->m_key;
+ } elseif ( $this->m_key === $this->findPropertyTypeID() ) {
+ // If _dat as property [[Date::...]] refers directly to its _dat type
+ // then use the en-label as canonical representation
+ $dbkey = PropertyRegistry::getInstance()->findPropertyLabelFromIdByLanguageCode( $this->m_key, 'en' );
+ } else {
+ $dbkey = PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
+ }
+
+ return $this->newDIWikiPage( $dbkey, $subobjectName );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DIProperty
+ */
+ public function getRedirectTarget() {
+
+ if ( $this->m_inverse ) {
+ return $this;
+ }
+
+ return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $this );
+ }
+
+ /**
+ * @deprecated since 3.0, use DIProperty::setPropertyValueType
+ */
+ public function setPropertyTypeId( $valueType ) {
+ return $this->setPropertyValueType( $valueType );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $valueType
+ *
+ * @return self
+ * @throws DataTypeLookupException
+ * @throws RuntimeException
+ */
+ public function setPropertyValueType( $valueType ) {
+
+ if ( !DataTypeRegistry::getInstance()->isRegistered( $valueType ) ) {
+ throw new DataTypeLookupException( "{$valueType} is an unknown type id" );
+ }
+
+ if ( $this->isUserDefined() && $this->propertyValueType === null ) {
+ $this->propertyValueType = $valueType;
+ return $this;
+ }
+
+ if ( !$this->isUserDefined() && $valueType === PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key ) ) {
+ $this->propertyValueType = $valueType;
+ return $this;
+ }
+
+ throw new RuntimeException( 'DataType cannot be altered for a predefined property' );
+ }
+
+ /**
+ * @deprecated since 3.0, use DIProperty::findPropertyValueType
+ */
+ public function findPropertyTypeId() {
+ return $this->findPropertyValueType();
+ }
+
+ /**
+ * Find the property's type ID, either by looking up its predefined ID
+ * (if any) or by retrieving the relevant information from the store.
+ * If no type is stored for a user defined property, the global default
+ * type will be used.
+ *
+ * @since 3.0
+ *
+ * @return string type ID
+ */
+ public function findPropertyValueType() {
+
+ if ( isset( $this->propertyValueType ) ) {
+ return $this->propertyValueType;
+ }
+
+ if ( !$this->isUserDefined() ) {
+ return $this->propertyValueType = PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key );
+ }
+
+ $diWikiPage = new DIWikiPage( $this->getKey(), SMW_NS_PROPERTY, $this->interwiki );
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $typearray = $applicationFactory->getPropertySpecificationLookup()->getSpecification(
+ $this,
+ new self( '_TYPE' )
+ );
+
+ if ( is_array( $typearray ) && count( $typearray ) >= 1 ) { // some types given, pick one (hopefully unique)
+ $typeDataItem = reset( $typearray );
+
+ if ( $typeDataItem instanceof SMWDIUri ) {
+ $this->propertyValueType = $typeDataItem->getFragment();
+ } else {
+ // This is important. If a page has an invalid assignment to "has type", no
+ // value will be stored, so the elseif case below occurs. But if the value
+ // is retrieved within the same run, then the error value for "has type" is
+ // cached and thus this case occurs. This is why it is important to tolerate
+ // this case -- it is not necessarily a DB error.
+ $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' );
+ }
+ } else { // no type given
+ $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' );
+ }
+
+ return $this->propertyValueType;
+ }
+
+ /**
+ * @see DataItem::getSerialization
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ return ( $this->m_inverse ? '-' : '' ) . $this->m_key;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ *
+ * @param string $serialization
+ *
+ * @return DIProperty
+ */
+ public static function doUnserialize( $serialization ) {
+ $inverse = false;
+
+ if ( $serialization{0} == '-' ) {
+ $serialization = substr( $serialization, 1 );
+ $inverse = true;
+ }
+
+ return new self( $serialization, $inverse );
+ }
+
+ /**
+ * @param SMWDataItem $di
+ *
+ * @return boolean
+ */
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_PROPERTY ) {
+ return false;
+ }
+
+ return $di->getKey() === $this->m_key;
+ }
+
+ /**
+ * Construct a property from a user-supplied label. The main difference
+ * to the normal constructor of DIProperty is that it is checked
+ * whether the label refers to a known predefined property.
+ * Note that this function only gives access to the registry data that
+ * DIProperty stores, but does not do further parsing of user input.
+ *
+ * To process wiki input, SMWPropertyValue should be used.
+ *
+ * @param $label string label for the property
+ * @param $inverse boolean states if the inverse of the property is constructed
+ *
+ * @return DIProperty object
+ */
+ public static function newFromUserLabel( $label, $inverse = false, $languageCode = false ) {
+
+ if ( $label !== '' && $label{0} == '-' ) {
+ $label = substr( $label, 1 );
+ $inverse = true;
+ }
+
+ // Special handling for when the user value contains a @LCODE marker
+ if ( ( $annotatedLanguageCode = Localizer::getAnnotatedLanguageCodeFrom( $label ) ) !== false ) {
+ $languageCode = $annotatedLanguageCode;
+ }
+
+ $id = false;
+ $label = str_replace( '_', ' ', $label );
+
+ if ( $languageCode ) {
+ $id = PropertyRegistry::getInstance()->findPropertyIdFromLabelByLanguageCode(
+ $label,
+ $languageCode
+ );
+ }
+
+ if ( $id !== false ) {
+ return new self( $id, $inverse );
+ }
+
+ $id = PropertyRegistry::getInstance()->findPropertyIdByLabel(
+ $label
+ );
+
+ if ( $id === false ) {
+ return new self( str_replace( ' ', '_', $label ), $inverse );
+ }
+
+ return new self( $id, $inverse );
+ }
+
+ private function newDIWikiPage( $dbkey, $subobjectName ) {
+
+ // If an inverse marker is present just omit the marker so a normal
+ // property page link can be produced independent of its directionality
+ if ( $dbkey !== '' && $dbkey{0} == '-' ) {
+ $dbkey = substr( $dbkey, 1 );
+ }
+
+ try {
+ return new DIWikiPage( str_replace( ' ', '_', $dbkey ), SMW_NS_PROPERTY, $this->interwiki, $subobjectName );
+ } catch ( DataItemException $e ) {
+ return null;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php
new file mode 100644
index 00000000..00a473d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php
@@ -0,0 +1,609 @@
+<?php
+
+use SMW\DataValues\Time\CalendarModel;
+use SMW\DataValues\Time\JulianDay;
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements time data items.
+ * Such data items represent a unique point in time, given in either Julian or
+ * Gregorian notation (possibly proleptic), and a precision setting that states
+ * which of the components year, month, day, time were specified expicitly.
+ * Even when not specified, the data item always assumes default values for the
+ * missing parts, so the item really captures one point in time, no intervals.
+ * Times are always assumed to be in UTC.
+ *
+ * "Y0K issue": Neither the Gregorian nor the Julian calendar assume a year 0,
+ * i.e. the year 1 BC(E) was followed by 1 AD/CE. See
+ * http://en.wikipedia.org/wiki/Year_zero
+ * This implementation adheres to this convention and disallows year 0. The
+ * stored year numbers use positive numbers for CE and negative numbers for
+ * BCE. This is not just relevant for the question of how many years have
+ * (exactly) passed since a given date, but also for the location of leap
+ * years.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDITime extends SMWDataItem implements CalendarModel {
+
+ const PREC_Y = SMW_PREC_Y;
+ const PREC_YM = SMW_PREC_YM;
+ const PREC_YMD = SMW_PREC_YMD;
+ const PREC_YMDT = SMW_PREC_YMDT;
+
+ /**
+ * The year before which we do not accept anything but year numbers and
+ * largely discourage calendar models.
+ */
+ const PREHISTORY = -10000;
+
+ /**
+ * Maximal number of days in a given month.
+ * @var array
+ */
+ protected static $m_daysofmonths = [ 1 => 31, 2 => 29, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 ];
+
+ /**
+ * Precision SMWDITime::PREC_Y, SMWDITime::PREC_YM,
+ * SMWDITime::PREC_YMD, or SMWDITime::PREC_YMDT.
+ * @var integer
+ */
+ protected $m_precision;
+ /**
+ * Calendar model: SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN.
+ * @var integer
+ */
+ protected $m_model;
+ /**
+ * Number of year, possibly negative.
+ * @var integer
+ */
+ protected $m_year;
+ /**
+ * Number of month.
+ * @var integer
+ */
+ protected $m_month;
+ /**
+ * Number of day.
+ * @var integer
+ */
+ protected $m_day;
+ /**
+ * Hours of the day.
+ * @var integer
+ */
+ protected $m_hours;
+ /**
+ * Minutes of the hour.
+ * @var integer
+ */
+ protected $m_minutes;
+ /**
+ * Seconds of the minute.
+ * @var integer
+ */
+ protected $m_seconds;
+
+ /**
+ * @var integer
+ */
+ protected $timezone;
+
+ /**
+ * @var integer|null
+ */
+ protected $era = null;
+
+ /**
+ * @var integer
+ */
+ protected $julianDay = null;
+
+ /**
+ * Create a time data item. All time components other than the year can
+ * be false to indicate that they are not specified. This will affect
+ * the internal precision setting. The missing values are initialised
+ * to minimal values (0 or 1) for internal calculations.
+ *
+ * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @param $year integer number of the year (possibly negative)
+ * @param $month mixed integer number or false
+ * @param $day mixed integer number or false
+ * @param $hour mixed integer number or false
+ * @param $minute mixed integer number or false
+ * @param $second mixed integer number or false
+ * @param integer|false $timezone
+ *
+ * @todo Implement more validation here.
+ */
+ public function __construct( $calendarmodel, $year, $month = false, $day = false,
+ $hour = false, $minute = false, $second = false, $timezone = false ) {
+
+ if ( ( $calendarmodel != self::CM_GREGORIAN ) && ( $calendarmodel != self::CM_JULIAN ) ) {
+ throw new DataItemException( "Unsupported calendar model constant \"$calendarmodel\"." );
+ }
+
+ if ( $year == 0 ) {
+ throw new DataItemException( "There is no year 0 in Gregorian and Julian calendars." );
+ }
+
+ $this->m_model = $calendarmodel;
+ $this->m_year = intval( $year );
+ $this->m_month = $month != false ? intval( $month ) : 1;
+ $this->m_day = $day != false ? intval( $day ) : 1;
+ $this->m_hours = $hour !== false ? intval( $hour ) : 0;
+ $this->m_minutes = $minute !== false ? intval( $minute ) : 0;
+ $this->m_seconds = $second !== false ? floatval( $second ) : 0;
+
+ $this->timezone = $timezone !== false ? $timezone : 0;
+ $year = strval( $year );
+ $this->era = $year{0} === '+' ? 1 : ( $year{0} === '-' ? -1 : 0 );
+
+ if ( $this->isOutOfBoundsBySome() ) {
+ throw new DataItemException( "Part of the date is out of bounds." );
+ }
+
+ if ( $this->isOutOfBoundsByDayNumberOfMonth() ) {
+ throw new DataItemException( "Month {$this->m_month} in year {$this->m_year} did not have {$this->m_day} days in this calendar model." );
+ }
+
+ $this->setPrecisionLevelBy( $month, $day, $hour );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_TIME;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getCalendarModel() {
+ return $this->m_model;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getTimezone() {
+ return $this->timezone;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getPrecision() {
+ return $this->m_precision;
+ }
+
+ /**
+ * Indicates whether a user explicitly used an era marker even for a positive
+ * year.
+ *
+ * - [-1] indicates BC(E)
+ * - [0]/null indicates no era marker
+ * - [1] indicates AD/CE was used
+ *
+ * @since 2.4
+ *
+ * @return integer
+ */
+ public function getEra() {
+ return $this->era;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getYear() {
+ return $this->m_year;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getMonth() {
+ return $this->m_month;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getDay() {
+ return $this->m_day;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getHour() {
+ return $this->m_hours;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getMinute() {
+ return $this->m_minutes;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getSecond() {
+ return $this->m_seconds;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCalendarModelLiteral() {
+
+ $literal = [
+ self::CM_GREGORIAN => '',
+ self::CM_JULIAN => 'JL'
+ ];
+
+ return $literal[$this->m_model];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DateTime $dateTime
+ *
+ * @return SMWDITime|false
+ */
+ public static function newFromDateTime( DateTime $dateTime ) {
+
+ $calendarModel = self::CM_JULIAN;
+
+ $year = $dateTime->format( 'Y' );
+ $month = $dateTime->format( 'm' );
+ $day = $dateTime->format( 'd' );
+
+ if ( ( $year > 1582 ) ||
+ ( ( $year == 1582 ) && ( $month > 10 ) ) ||
+ ( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
+ $calendarModel = self::CM_GREGORIAN;
+ }
+
+ return self::doUnserialize( $calendarModel . '/' . $dateTime->format( 'Y/m/d/H/i/s.u' ) );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DateTime
+ */
+ public function asDateTime() {
+
+ $year = str_pad( $this->m_year, 4, '0', STR_PAD_LEFT );
+
+ // Avoid "Failed to parse time string (-900-02-02 00:00:00) at
+ // position 7 (-): Double timezone specification"
+ if ( $this->m_year < 0 ) {
+ $year = '-' . str_pad( $this->m_year * -1, 4, '0', STR_PAD_LEFT );
+ }
+
+ // Avoid "Failed to parse time string (1300-11-02 12:03:25.888499949) at
+ // at position 11 (1): The timezone could not ..."
+ $seconds = number_format( str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), 7, '.', '' );
+
+ $time = $year . '-' .
+ str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ) . '-' .
+ str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ) . ' ' .
+ str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ) . ':' .
+ str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ) . ':' .
+ $seconds;
+
+ return new DateTime( $time );
+ }
+
+ /**
+ * Creates and returns a new instance of SMWDITime from a MW timestamp.
+ *
+ * @since 1.8
+ *
+ * @param string $timestamp must be in format
+ *
+ * @return SMWDITime|false
+ */
+ public static function newFromTimestamp( $timestamp ) {
+ $timestamp = wfTimestamp( TS_MW, (string)$timestamp );
+
+ if ( $timestamp === false ) {
+ return false;
+ }
+
+ return new self(
+ self::CM_GREGORIAN,
+ substr( $timestamp, 0, 4 ),
+ substr( $timestamp, 4, 2 ),
+ substr( $timestamp, 6, 2 ),
+ substr( $timestamp, 8, 2 ),
+ substr( $timestamp, 10, 2 ),
+ substr( $timestamp, 12, 2 )
+ );
+ }
+
+ /**
+ * Returns a MW timestamp representation of the value.
+ *
+ * @since 1.6.2
+ *
+ * @param $outputtype
+ *
+ * @return mixed
+ */
+ public function getMwTimestamp( $outputtype = TS_UNIX ) {
+ return wfTimestamp(
+ $outputtype,
+ implode( '', [
+ str_pad( $this->m_year, 4, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ),
+ ] )
+ );
+ }
+
+ /**
+ * Get the data in the specified calendar model. This might require
+ * conversion.
+ * @note Conversion can be unreliable for very large absolute year
+ * numbers when the internal calculations hit floating point accuracy.
+ * Callers might want to avoid this (calendar models make little sense
+ * in such cases anyway).
+ * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return SMWDITime
+ */
+ public function getForCalendarModel( $calendarmodel ) {
+ if ( $calendarmodel == $this->m_model ) {
+ return $this;
+ } else {
+ return self::newFromJD( $this->getJD(), $calendarmodel, $this->m_precision );
+ }
+ }
+
+ /**
+ * Return a number that helps comparing time data items. For
+ * dates in the Julian Day era (roughly from 4713 BCE onwards), we use
+ * the Julian Day number. For earlier dates, the (negative) year number
+ * with a fraction for the date is used (times are ignored). This
+ * avoids calculation errors that would occur for very ancient dates
+ * if the JD number was used there.
+ * @return double sortkey
+ */
+ public function getSortKey() {
+ $jd = ( $this->m_year >= -4713 ) ? $jd = $this->getJD() : -1;
+ if ( $jd > 0 ) {
+ return $jd;
+ } else {
+ return $this->m_year - 1 + ( $this->m_month - 1 ) / 12 + ( $this->m_day - 1 ) / 12 / 31;
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return double
+ */
+ public function getJD() {
+
+ if ( $this->julianDay !== null ) {
+ return $this->julianDay;
+ }
+
+ $this->julianDay = JulianDay::getJD(
+ $this->getCalendarModel(),
+ $this->getYear(),
+ $this->getMonth(),
+ $this->getDay(),
+ $this->getHour(),
+ $this->getMinute(),
+ $this->getSecond()
+ );
+
+ return $this->julianDay;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ $result = strval( $this->m_model ) . '/' . ( $this->era > 0 ? '+' : '' ) . strval( $this->m_year );
+
+ if ( $this->m_precision >= self::PREC_YM ) {
+ $result .= '/' . strval( $this->m_month );
+ }
+
+ if ( $this->m_precision >= self::PREC_YMD ) {
+ $result .= '/' . strval( $this->m_day );
+ }
+
+ if ( $this->m_precision >= self::PREC_YMDT ) {
+ $result .= '/' . strval( $this->m_hours ) . '/' . strval( $this->m_minutes ) . '/' . strval( $this->m_seconds ) . '/' . strval( $this->timezone );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create a data item from the provided serialization string.
+ *
+ * @return SMWDITime
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( '/', $serialization, 8 );
+ $values = [];
+
+ if ( count( $parts ) <= 1 ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
+ }
+
+ for ( $i = 0; $i < 8; $i += 1 ) {
+
+ $values[$i] = false;
+
+ // Can contain something like '1/1970/1/12/11/43/0/Asia/Tokyo'
+ if ( $i == 7 && isset( $parts[$i] ) ) {
+ $values[$i] = strval( $parts[$i] );
+ continue;
+ }
+
+ if ( $i < count( $parts ) ) {
+
+ if ( $parts[$i] !== '' && !is_numeric( $parts[$i] ) ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid datetime specification." );
+ }
+
+ // 6 == seconds, we want to keep microseconds
+ $values[$i] = $i == 6 ? floatval( $parts[$i] ) : intval( $parts[$i] );
+
+ // Find out whether the input contained an explicit AD/CE era marker
+ if ( $i == 1 ) {
+ $values[$i] = ( $parts[1]{0} === '+' ? '+' : '' ) . $values[$i];
+ }
+ }
+ }
+
+ return new self( $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6], $values[7] );
+ }
+
+ /**
+ * Create a new time dataItem from a specified Julian Day number,
+ * calendar model, presicion.
+ *
+ * @param double $jdValue
+ * @param integer|null $calendarmodel
+ * @param integer|null $precision
+ *
+ * @return DITime object
+ */
+ public static function newFromJD( $jdValue, $calendarModel = null, $precision = null, $timezone = false ) {
+
+ $hour = $minute = $second = false;
+ $year = $month = $day = false;
+ $jdValue = JulianDay::format( $jdValue );
+
+ if ( $precision === null ) {
+ $precision = strpos( strval( $jdValue ), '.5' ) !== false ? self::PREC_YMD : self::PREC_YMDT;
+ }
+
+ list( $calendarModel, $year, $month, $day ) = JulianDay::JD2Date( $jdValue, $calendarModel );
+
+ if ( $precision <= self::PREC_YM ) {
+ $day = false;
+ if ( $precision === self::PREC_Y ) {
+ $month = false;
+ }
+ }
+
+ if ( $precision === self::PREC_YMDT ) {
+ list( $hour, $minute, $second ) = JulianDay::JD2Time( $jdValue );
+ }
+
+ return new self( $calendarModel, $year, $month, $day, $hour, $minute, $second, $timezone );
+ }
+
+ /**
+ * Find out whether the given year number is a leap year.
+ * This calculation assumes that neither calendar has a year 0.
+ * @param $year integer year number
+ * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return boolean
+ */
+ static public function isLeapYear( $year, $calendarmodel ) {
+ $astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
+ if ( $calendarmodel == self::CM_JULIAN ) {
+ return ( $astroyear % 4 ) == 0;
+ } else {
+ return ( ( $astroyear % 400 ) == 0 ) ||
+ ( ( ( $astroyear % 4 ) == 0 ) && ( ( $astroyear % 100 ) != 0 ) );
+ }
+ }
+
+ /**
+ * Find out how many days the given month had in the given year
+ * based on the specified calendar model.
+ * This calculation assumes that neither calendar has a year 0.
+ * @param $month integer month number
+ * @param $year integer year number
+ * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return boolean
+ */
+ static public function getDayNumberForMonth( $month, $year, $calendarmodel ) {
+ if ( $month !== 2 ) {
+ return self::$m_daysofmonths[$month];
+ } elseif ( self::isLeapYear( $year, $calendarmodel ) ) {
+ return 29;
+ } else {
+ return 28;
+ }
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_TIME ) {
+ return false;
+ }
+
+ return $di->getSortKey() === $this->getSortKey();
+ }
+
+ private function isOutOfBoundsBySome() {
+ return ( $this->m_hours < 0 ) || ( $this->m_hours > 23 ) ||
+ ( $this->m_minutes < 0 ) || ( $this->m_minutes > 59 ) ||
+ ( $this->m_seconds < 0 ) || ( $this->m_seconds > 59 ) ||
+ ( $this->m_month < 1 ) || ( $this->m_month > 12 );
+ }
+
+ private function isOutOfBoundsByDayNumberOfMonth() {
+ return $this->m_day > self::getDayNumberForMonth( $this->m_month, $this->m_year, $this->m_model );
+ }
+
+ private function setPrecisionLevelBy( $month, $day, $hour ) {
+ if ( $month === false ) {
+ $this->m_precision = self::PREC_Y;
+ } elseif ( $day === false ) {
+ $this->m_precision = self::PREC_YM;
+ } elseif ( $hour === false ) {
+ $this->m_precision = self::PREC_YMD;
+ } else {
+ $this->m_precision = self::PREC_YMDT;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php
new file mode 100644
index 00000000..d1aaa67d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php
@@ -0,0 +1,165 @@
+<?php
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements URI data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIUri extends SMWDataItem {
+
+ /**
+ * URI scheme such as "html" or "mailto".
+ * @var string
+ */
+ protected $m_scheme;
+ /**
+ * "Hierpart" of the URI (usually some authority and path).
+ * @var string
+ */
+ protected $m_hierpart;
+ /**
+ * Query part of the URI.
+ * @var string
+ */
+ protected $m_query;
+ /**
+ * Fragment part of the URI.
+ * @var string
+ */
+ protected $m_fragment;
+
+ /**
+ * Initialise a URI by providing its scheme (e.g. "html"), 'hierpart'
+ * following "scheme:" (e.g. "//username@example.org/path), query (e.g.
+ * "q=Search+term", and fragment (e.g. "section-one"). The complete URI
+ * with these examples would be
+ * http://username@example.org/path?q=Search+term#section-one
+ * @param $scheme string for the scheme
+ * @param $hierpart string for the "hierpart"
+ * @param $query string for the query
+ * @param $fragment string for the fragment
+ *
+ * @todo Implement more validation here.
+ */
+ public function __construct( $scheme, $hierpart, $query, $fragment, $strict = true ) {
+ if ( $strict && ( ( $scheme === '' ) || ( preg_match( '/[^a-zA-Z]/u', $scheme ) ) ) ) {
+ throw new DataItemException( "Illegal URI scheme \"$scheme\"." );
+ }
+ if ( $strict && $hierpart === '' ) {
+ throw new DataItemException( "Illegal URI hierpart \"$hierpart\"." );
+ }
+ $this->m_scheme = $scheme;
+ $this->m_hierpart = $hierpart;
+ $this->m_query = $query;
+ $this->m_fragment = $fragment;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_URI;
+ }
+
+ /// @todo This should be changed to the spelling getUri().
+ public function getURI() {
+ $schemesWithDoubleslesh = [
+ 'http', 'https', 'ftp'
+ ];
+
+ $uri = $this->m_scheme . ':'
+ . ( in_array( $this->m_scheme, $schemesWithDoubleslesh ) ? '//' : '' )
+ . $this->m_hierpart
+ . ( $this->m_query ? '?' . $this->m_query : '' )
+ . ( $this->m_fragment ? '#' . $this->m_fragment : '' );
+
+ // #1878
+ // https://tools.ietf.org/html/rfc3986
+ // Normalize spaces to use `_` instead of %20 and so ensure
+ // that http://example.org/Foo bar === http://example.org/Foo_bar === http://example.org/Foo%20bar
+ return str_replace( [ ' ', '%20'], '_', $uri );
+ }
+
+ public function getScheme() {
+ return $this->m_scheme;
+ }
+
+ public function getHierpart() {
+ return $this->m_hierpart;
+ }
+
+ public function getQuery() {
+ return $this->m_query;
+ }
+
+ public function getFragment() {
+ return $this->m_fragment;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSortKey() {
+ return urldecode( $this->getURI() );
+ }
+
+ public function getSerialization() {
+ return $this->getURI();
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIUri
+ */
+ public static function doUnserialize( $serialization ) {
+
+ // try to split "schema:rest"
+ $parts = explode( ':', $serialization, 2 );
+ $strict = true;
+
+ if ( $serialization !== null && count( $parts ) <= 1 ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
+ }
+
+ if ( $serialization === null ) {
+ $parts = [ '', 'NO_VALUE' ];
+ $strict = false;
+ }
+
+ $scheme = $parts[0];
+
+ // try to split "hier-part?queryfrag"
+ $parts = explode( '?', $parts[1], 2 );
+
+ if ( count( $parts ) == 2 ) {
+ $hierpart = $parts[0];
+ // try to split "query#frag"
+ $parts = explode( '#', $parts[1], 2 );
+ $query = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ } else {
+ $query = '';
+ // try to split "hier-part#frag"
+ $parts = explode( '#', $parts[0], 2 );
+ $hierpart = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ }
+
+ $hierpart = ltrim( $hierpart, '/' );
+
+ return new SMWDIUri( $scheme, $hierpart, $query, $fragment, $strict );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_URI ) {
+ return false;
+ }
+
+ return $di->getURI() === $this->getURI();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php
new file mode 100644
index 00000000..863cbef1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace SMW;
+
+use SMW\Exception\DataItemDeserializationException;
+use SMW\Exception\DataItemException;
+use SMWDataItem;
+use Title;
+
+/**
+ * This class implements wiki page data items.
+ *
+ * @since 1.6
+ * @ingroup SMWDataItems
+ *
+ * @author Markus Krötzsch
+ */
+class DIWikiPage extends SMWDataItem {
+
+ /**
+ * MediaWiki DB key string
+ * @var string
+ */
+ protected $m_dbkey;
+
+ /**
+ * MediaWiki namespace integer.
+ * @var integer
+ */
+ protected $m_namespace;
+
+ /**
+ * MediaWiki interwiki prefix.
+ * @var string
+ */
+ protected $m_interwiki;
+
+ /**
+ * Name for subobjects of pages, or empty string if the given object is
+ * the page itself (not a subobject).
+ * @var string
+ */
+ protected $m_subobjectname;
+
+ /**
+ * @var string
+ */
+ private $sortkey = null;
+
+ /**
+ * @var string
+ */
+ private $contextReference = null;
+
+ /**
+ * @var string
+ */
+ private $pageLanguage = null;
+
+ /**
+ * @var integer
+ */
+ private $id = 0;
+
+ /**
+ * Constructor. We do not bother with too much detailed validation here,
+ * regarding the known namespaces, canonicity of the dbkey (namespace
+ * exrtacted?), validity of interwiki prefix (known?), and general use
+ * of allowed characters (may depend on MW configuration). All of this
+ * would be more work than it is worth, since callers will usually be
+ * careful and since errors here do not have major consequences.
+ *
+ * @param string $dbkey
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobjectname
+ */
+ public function __construct( $dbkey, $namespace, $interwiki = '', $subobjectname = '' ) {
+ // Check if the provided value holds an integer
+ // (it can be of type string or float as well, as long as the value is an int)
+ if ( !ctype_digit( ltrim( (string)$namespace, '-' ) ) ) {
+ throw new DataItemException( "Given namespace '$namespace' is not an integer." );
+ }
+
+ // Check for a potential fragment such as Foo#Bar, Bar#_49c8ab
+ if ( strpos( $dbkey, '#' ) !== false ) {
+ list( $dbkey, $subobjectname ) = explode( '#', $dbkey );
+ }
+
+ $this->m_dbkey = str_replace( ' ', '_', $dbkey );
+ $this->m_namespace = (int)$namespace; // really make this an integer
+ $this->m_interwiki = $interwiki;
+ $this->m_subobjectname = $subobjectname;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_WIKIPAGE;
+ }
+
+ public function getDBkey() {
+ return $this->m_dbkey;
+ }
+
+ public function getNamespace() {
+ return $this->m_namespace;
+ }
+
+ public function getInterwiki() {
+ return $this->m_interwiki;
+ }
+
+ public function getSubobjectName() {
+ return $this->m_subobjectname;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $sortkey
+ */
+ public function setSortKey( $sortkey ) {
+ $this->sortkey = str_replace( '_', ' ', $sortkey );
+ }
+
+ /**
+ * Get the sortkey of the wiki page data item. Note that this is not
+ * the sortkey that might have been set for the corresponding wiki
+ * page. To obtain the latter, query for the values of the property
+ * "new SMW\DIProperty( '_SKEY' )".
+ */
+ public function getSortKey() {
+
+ if ( $this->sortkey === null || $this->sortkey === '' ) {
+ $this->sortkey = str_replace( '_', ' ', $this->m_dbkey );
+ }
+
+ return $this->sortkey;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $contextReference
+ */
+ public function setContextReference( $contextReference ) {
+ $this->contextReference = $contextReference;
+ }
+
+ /**
+ * Returns a reference for the processing context (parser etc.).
+ *
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getContextReference() {
+ return $this->contextReference;
+ }
+
+ /**
+ * Returns the page content language
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getPageLanguage() {
+
+ if ( $this->pageLanguage === null ) {
+ $this->pageLanguage = false;
+
+ if ( ( $title = $this->getTitle() ) !== null ) {
+ $this->pageLanguage = $title->getPageLanguage()->getCode();
+ }
+ }
+
+ return $this->pageLanguage;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $id
+ */
+ public function setId( $id ) {
+ $this->id = (int)$id;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Create a MediaWiki Title object for this DIWikiPage. The result
+ * can be null if an error occurred.
+ *
+ * @return Title|null
+ */
+ public function getTitle() {
+ return Title::makeTitleSafe(
+ $this->m_namespace,
+ $this->m_dbkey,
+ $this->m_subobjectname,
+ $this->m_interwiki
+ );
+ }
+
+ /**
+ * Returns the base part (without a fragment) of a wikipage representation.
+ *
+ * @since 2.4
+ *
+ * @return DIWikiPage
+ */
+ public function asBase() {
+ return new self (
+ $this->m_dbkey,
+ $this->m_namespace,
+ $this->m_interwiki
+ );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ $segments = [
+ $this->m_dbkey,
+ $this->m_namespace,
+ $this->m_interwiki
+ ];
+
+ $segments[] = $this->m_subobjectname;
+
+ return implode( '#', $segments );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type ID.
+ *
+ * @param string $serialization
+ *
+ * @return DIWikiPage
+ * @throws DataItemDeserializationException
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( '#', $serialization, 4 );
+
+ if ( count( $parts ) == 3 ) {
+ return new self( $parts[0], intval( $parts[1] ), $parts[2] );
+ } elseif ( count( $parts ) == 4 ) {
+ return new self( $parts[0], intval( $parts[1] ), $parts[2], $parts[3] );
+ } else {
+ throw new DataItemDeserializationException( "Unserialization failed: the string \"$serialization\" was not understood." );
+ }
+ }
+
+ /**
+ * Create a data item from a MediaWiki Title.
+ *
+ * @param Title $title
+ * @return DIWikiPage
+ */
+ public static function newFromTitle( Title $title ) {
+ return new self(
+ $title->getDBkey(),
+ $title->getNamespace(),
+ $title->getInterwiki(),
+ str_replace( ' ', '_', $title->getFragment() )
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $text
+ * @param integer namespace
+ *
+ * @return DIWikiPage
+ */
+ public static function newFromText( $text, $namespace = NS_MAIN ) {
+ return new self( $text, $namespace );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_WIKIPAGE ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php
new file mode 100644
index 00000000..96137956
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php
@@ -0,0 +1,236 @@
+<?php
+
+use SMW\Options;
+
+/**
+ * This group contains all parts of SMW that relate to the processing of dataitems
+ * of various types.
+ *
+ * @defgroup SMWDataItems SMWDataItems
+ * @ingroup SMW
+ */
+
+/**
+ * Objects of this type represent all that is known about a certain piece of
+ * data that could act as the value of some property. Data items only represent
+ * the stored data, and are thus at the core of SMW's data model. Data items
+ * are always immutable, i.e. they must not be changed after creation (and this
+ * is mostly enforced by the API with some minor exceptions).
+ *
+ * The set of available data items is fixed and cannot be extended. These are
+ * the kinds of information that SMW can process. Their concrete use and
+ * handling might depend on the context in which they are used. In particular,
+ * property values may be influences by settings made for their property. This
+ * aspect, however, is not part of the data item API.
+ *
+ * @since 1.6
+ *
+ * @ingroup SMWDataItems
+ */
+abstract class SMWDataItem {
+
+ /// Data item ID that can be used to indicate that no data item class is appropriate
+ const TYPE_NOTYPE = 0;
+ /// Data item ID for SMWDINumber
+ const TYPE_NUMBER = 1;
+ /// Data item ID for SMWDIBlob
+ const TYPE_BLOB = 2;
+ /// Data item ID for SMWDIBoolean
+ const TYPE_BOOLEAN = 4;
+ /// Data item ID for SMWDIUri
+ const TYPE_URI = 5;
+ /// Data item ID for SMWDITimePoint
+ const TYPE_TIME = 6;
+ /// Data item ID for SMWDIGeoCoord
+ const TYPE_GEO = 7;
+ /// Data item ID for SMWDIContainer
+ const TYPE_CONTAINER = 8;
+ /// Data item ID for SMWDIWikiPage
+ const TYPE_WIKIPAGE = 9;
+ /// Data item ID for SMWDIConcept
+ const TYPE_CONCEPT = 10;
+ /// Data item ID for SMWDIProperty
+ const TYPE_PROPERTY = 11;
+ /// Data item ID for SMWDIError
+ const TYPE_ERROR = 12;
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * Convenience method that returns a constant that defines the concrete
+ * class that implements this data item. Used to switch when processing
+ * data items.
+ * @return integer that specifies the basic type of data item
+ */
+ abstract public function getDIType();
+
+ /**
+ * Return a value that can be used for sorting data of this type.
+ * If the data is of a numerical type, the sorting must be done in
+ * numerical order. If the data is a string, the data must be sorted
+ * alphabetically.
+ *
+ * @note Every data item returns a sort key, even if there is no
+ * natural linear order for the type. SMW must order listed data
+ * in some way in any case. If there is a natural order (e.g. for
+ * Booleans where false < true), then the sortkey must agree with
+ * this order (e.g. for Booleans where false maps to 0, and true
+ * maps to 1).
+ *
+ * @note Wiki pages are a special case in SMW. They are ordered by a
+ * sortkey that is assigned to them as a property value. When pages are
+ * sorted, this data should be used if possible.
+ *
+ * @return float|string
+ */
+ abstract public function getSortKey();
+
+ /**
+ * Method to compare two SMWDataItems
+ * This should result true only if they are of the same DI type
+ * and have the same internal value
+ *
+ * @since 1.8
+ *
+ * @param SMWDataItem $di
+ * @return boolean
+ */
+ abstract public function equals( SMWDataItem $di );
+
+ /**
+ * Create a data item that represents the sortkey, i.e. either an
+ * SMWDIBlob or an SMWDINumber. For efficiency, these subclasses
+ * overwrite this method to return themselves.
+ *
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ $sortKey = $this->getSortKey();
+
+ if ( is_numeric( $sortKey ) ) {
+ return new SMWDINumber( $sortKey );
+ }
+
+ return new SMWDIBlob( $sortKey );
+ }
+
+ /**
+ * Get a UTF-8 encoded string serialization of this data item.
+ * The serialisation should be concise and need not be pretty, but it
+ * must allow unserialization. Each subclass of SMWDataItem implements
+ * a static method doUnserialize() for this purpose.
+ * @return string
+ */
+ abstract public function getSerialization();
+
+ /**
+ * Get a hash string for this data item. Might be overwritten in
+ * subclasses to obtain shorter or more efficient hashes.
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->getSerialization();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getSerialization();
+ }
+
+ /**
+ * Create a data item of the given dataitem ID based on the the
+ * provided serialization string and (optional) typeid.
+ *
+ * @param integer $diType dataitem ID
+ * @param string $serialization
+ *
+ * @return SMWDataItem
+ */
+ public static function newFromSerialization( $diType, $serialization ) {
+ $diClass = self::getDataItemClassNameForId( $diType );
+ return call_user_func( [ $diClass, 'doUnserialize' ], $serialization );
+ }
+
+ /**
+ * Gets the class name of the data item that has the provided type id.
+ *
+ * @param integer $diType Element of the SMWDataItem::TYPE_ enum
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return string
+ */
+ public static function getDataItemClassNameForId( $diType ) {
+ switch ( $diType ) {
+ case self::TYPE_NUMBER:
+ return SMWDINumber::class;
+ case self::TYPE_BLOB:
+ return SMWDIBlob::class;
+ case self::TYPE_BOOLEAN:
+ return SMWDIBoolean::class;
+ case self::TYPE_URI:
+ return SMWDIUri::class;
+ case self::TYPE_TIME:
+ return SMWDITime::class;
+ case self::TYPE_GEO:
+ return SMWDIGeoCoord::class;
+ case self::TYPE_CONTAINER:
+ return SMWDIContainer::class;
+ case self::TYPE_WIKIPAGE:
+ return SMWDIWikiPage::class;
+ case self::TYPE_CONCEPT:
+ return SMWDIConcept::class;
+ case self::TYPE_PROPERTY:
+ return SMWDIProperty::class;
+ case self::TYPE_ERROR:
+ return SMWDIError::class;
+ case self::TYPE_NOTYPE: default:
+ throw new InvalidArgumentException( "The value \"$diType\" is not a valid dataitem ID." );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string|null $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ if ( $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php
new file mode 100644
index 00000000..01128751
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue is used as a container for concept descriptions as used
+ * on Concept pages with the #concept parserfunction. It has a somewhat
+ * non-standard interface as compared to other datavalues, but this is not
+ * an issue.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWConceptValue extends SMWDataValue {
+
+ protected function parseUserValue( $value ) {
+ throw new Exception( 'Concepts cannot be initialized from user-provided strings. This should not happen.' );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataItem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_CONCEPT ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = $dataItem->getConceptQuery(); // probably useless
+
+ return true;
+ }
+
+ protected function clear() {
+ $this->m_dataitem = new \SMW\DIConcept( '', '', 0, -1, -1, $this->m_typeid );
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return $this->m_caption;
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return $this->getShortWikiText( $linker ); // should be save (based on xsdvalue)
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ } else {
+ return $this->m_caption;
+ }
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ } else {
+ return $this->m_caption; // should be save (based on xsdvalue)
+ }
+ }
+
+ public function getWikiValue() {
+ /// This should not be used for anything. This class does not support wiki values.
+ return str_replace( [ '&lt;', '&gt;', '&amp;' ], [ '<', '>', '&' ], $this->m_dataitem->getConceptQuery() );
+ }
+
+ /// Return the concept's defining text (in SMW query syntax)
+ public function getConceptText() {
+ return $this->m_dataitem->getConceptQuery();
+ }
+
+ /// Return the optional concept documentation.
+ public function getDocu() {
+ return $this->m_dataitem->getDocumentation();
+ }
+
+ /// Return the concept's size (a metric used to estimate computation complexity).
+ public function getSize() {
+ return $this->m_dataitem->getSize();
+ }
+
+ /// Return the concept's depth (a metric used to estimate computation complexity).
+ public function getDepth() {
+ return $this->m_dataitem->getDepth();
+ }
+
+ /// Return the concept's query feature bit field (a metric used to estimate computation complexity).
+ public function getQueryFeatures() {
+ return $this->m_dataitem->getQueryFeatures();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php
new file mode 100644
index 00000000..1ad6a3ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements error datavalues, a kind of pseudo data value that
+ * is used in places where a data value is expected but no more meaningful
+ * value could be created. It is always invalid and never gets stored or
+ * exported, but it can help to transport an error message.
+ *
+ * @note SMWDataValue will return a data item of type SMWDIError for invalid
+ * data values. Hence this is the DI type of this DV, even if not mentioned in
+ * this file.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWErrorValue extends SMWDataValue {
+
+ public function __construct( $typeid, $errormsg = '', $uservalue = '', $caption = false ) {
+ parent::__construct( $typeid );
+ $this->m_caption = ( $caption !== false ) ? $caption : $uservalue;
+ if ( $errormsg !== '' ) {
+ $this->addErrorMsg( $errormsg );
+ }
+ }
+
+ protected function parseUserValue( $value ) {
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+ $this->addErrorMsg( [ 'smw-datavalue-parse-error', $value ] );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_ERROR ) {
+ $this->addError( $dataItem->getErrors() );
+ $this->m_caption = $this->getErrorText();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return $this->m_caption;
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return htmlspecialchars( $this->getShortWikiText( $linker ) );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->getErrorText();
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->getErrorText();
+ }
+
+ public function getWikiValue() {
+
+ if ( $this->m_dataitem !== null ) {
+ return $this->m_dataitem->getString();
+ }
+
+ return $this->getErrorText();
+ }
+
+ public function isValid() {
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php
new file mode 100644
index 00000000..cfa75cfb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php
@@ -0,0 +1,602 @@
+<?php
+
+use SMW\DataValues\Number\IntlNumberFormatter;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Localizer;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements numerical datavalues, and supports optional
+ * unit conversions. It parses and manages unit strings, since even plain
+ * numbers may have (not further specified) units that are stored. However,
+ * only subclasses implement full unit conversion by extending the methods
+ * convertToMainUnit() and makeConversionValues().
+ *
+ * Units work as follows: a unit is a string, but many such strings might
+ * refer to the same unit of measurement. There is always one string, that
+ * canonically represents the unit, and we will call this version of writing
+ * the unit the /unit id/. IDs for units are needed for tasks like duplicate
+ * avoidance. If no conversion information is given, any unit is its own ID.
+ * In any case, units are /normalised/, i.e. given a more standardised meaning
+ * before being processed. All units, IDs or otherwise, should be suitable for
+ * printout in wikitext, and main IDs should moreover be suitable for printout
+ * in HTML.
+ *
+ * Subclasses that support unit conversion may interpret the output format set
+ * via setOutputFormat() to allow a unit to be selected for display. Note that
+ * this setting does not affect the internal representation of the value
+ * though. So choosing a specific output format will change the behavior of
+ * output functions like getLongWikiText(), but not of functions that access
+ * the value itself, such as getUnit() or getDBKeys().
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ *
+ * @todo Wiki-HTML-conversion for unit strings must be revisited, as the current
+ * solution might be unsafe.
+ */
+class SMWNumberValue extends SMWDataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_num';
+
+ /**
+ * Internal state to ensure no precision limitation is applied to an output
+ */
+ const NO_DISP_PRECISION_LIMIT = 'num.no.displayprecision.limit';
+
+ /**
+ * Separator related constants
+ */
+ const DECIMAL_SEPARATOR = 'decimal.separator';
+ const THOUSANDS_SEPARATOR = 'thousands.separator';
+
+ /**
+ * Array with entries unit=>value, mapping a normalized unit to the
+ * converted value. Used for conversion tooltips.
+ * @var array
+ */
+ protected $m_unitvalues;
+
+ /**
+ * Whether the unit is preferred as prefix or not
+ *
+ * @var array
+ */
+ protected $prefixalUnitPreference = [];
+
+ /**
+ * Canonical identifier for the unit that the user gave as input. Used
+ * to avoid printing this in conversion tooltips again. If the
+ * outputformat was set to show another unit, then the values of
+ * $m_caption and $m_unitin will be updated as if the formatted string
+ * had been the original user input, i.e. the two values reflect what
+ * is currently printed.
+ * @var string
+ */
+ protected $m_unitin;
+
+ /**
+ * @var integer|null
+ */
+ protected $precision = null;
+
+ /**
+ * @var IntlNumberFormatter
+ */
+ private $intlNumberFormatter;
+
+ /**
+ * @var ValueFormatter
+ */
+ private $valueFormatter;
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( $typeid );
+ $this->intlNumberFormatter = IntlNumberFormatter::getInstance();
+ $this->intlNumberFormatter->reset();
+ }
+
+ /**
+ * Parse a string of the form "number unit" where unit is optional. The
+ * results are stored in the $number and $unit parameters. Returns an
+ * error code.
+ * @param $value string to parse
+ * @param $number call-by-ref parameter that will be set to the numerical value
+ * @param $unit call-by-ref parameter that will be set to the "unit" string (after the number)
+ * @return integer 0 (no errors), 1 (no number found at all), 2 (number
+ * too large for this platform)
+ */
+ public function parseNumberValue( $value, &$number, &$unit, &$asPrefix = false ) {
+
+ $intlNumberFormatter = $this->getNumberFormatter();
+
+ // Parse to find $number and (possibly) $unit
+ $kiloseparator = $intlNumberFormatter->getSeparatorByLanguage(
+ self::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE
+ );
+
+ $decseparator = $intlNumberFormatter->getSeparatorByLanguage(
+ self::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE
+ );
+
+ // #753
+ $regex = '/([-+]?\s*(?:' .
+ // Either numbers like 10,000.99 that start with a digit
+ '\d+(?:\\' . $kiloseparator . '\d\d\d)*(?:\\' . $decseparator . '\d+)?' .
+ // or numbers like .001 that start with the decimal separator
+ '|\\' . $decseparator . '\d+' .
+ ')\s*(?:[eE][-+]?\d+)?)/u';
+
+ // #1718 Whether to preserve spaces in unit labels or not (e.g. sq mi, sqmi)
+ $space = $this->isEnabledFeature( SMW_DV_NUMV_USPACE ) ? ' ' : '';
+
+ $parts = preg_split(
+ $regex,
+ trim( str_replace( [ '&nbsp;', '&#160;', '&thinsp;', ' ' ], $space, $value ) ),
+ 2,
+ PREG_SPLIT_DELIM_CAPTURE
+ );
+
+ if ( count( $parts ) >= 2 ) {
+ $numstring = str_replace( $kiloseparator, '', preg_replace( '/\s*/u', '', $parts[1] ) ); // simplify
+ if ( $decseparator != '.' ) {
+ $numstring = str_replace( $decseparator, '.', $numstring );
+ }
+ list( $number ) = sscanf( $numstring, "%f" );
+ if ( count( $parts ) >= 3 ) {
+ $asPrefix = $parts[0] !== '';
+ $unit = $this->normalizeUnit( $parts[0] !== '' ? $parts[0] : $parts[2] );
+ }
+ }
+
+ if ( ( count( $parts ) == 1 ) || ( $numstring === '' ) ) { // no number found
+ return 1;
+ } elseif ( is_infinite( $number ) ) { // number is too large for this platform
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+ // Set caption
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ if ( $value !== '' && $value{0} === ':' ) {
+ $this->addErrorMsg( [ 'smw-datavalue-invalid-number', $value ] );
+ return;
+ }
+
+ $this->m_unitin = false;
+ $this->m_unitvalues = false;
+ $number = $unit = '';
+ $error = $this->parseNumberValue( $value, $number, $unit );
+
+ if ( $error == 1 ) { // no number found
+ $this->addErrorMsg( [ 'smw_nofloat', $value ] );
+ } elseif ( $error == 2 ) { // number is too large for this platform
+ $this->addErrorMsg( [ 'smw_infinite', $value ] );
+ } elseif ( $this->getTypeID() === '_num' && $unit !== '' ) {
+ $this->addErrorMsg( [ 'smw-datavalue-number-textnotallowed', $unit, $number ] );
+ } elseif ( $number === null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-number-nullnotallowed', $value ] ); // #1628
+ } elseif ( $this->convertToMainUnit( $number, $unit ) === false ) { // so far so good: now convert unit and check if it is allowed
+ $this->addErrorMsg( [ 'smw_unitnotallowed', $unit ] );
+ } // note that convertToMainUnit() also sets m_dataitem if valid
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_NUMBER ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = false;
+ $this->m_unitin = false;
+ $this->makeUserValue();
+ $this->m_unitvalues = false;
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::setOutputFormat
+ *
+ * @param $string $formatstring
+ */
+ public function setOutputFormat( $formatstring ) {
+
+ if ( $formatstring == $this->m_outformat ) {
+ return null;
+ }
+
+ // #1591
+ $this->findPreferredLanguageFrom( $formatstring );
+
+ // #1335
+ $this->m_outformat = $this->findPrecisionFrom( $formatstring );
+
+ if ( $this->isValid() ) { // update caption/unitin for this format
+ $this->m_caption = false;
+ $this->m_unitin = false;
+ $this->makeUserValue();
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return float
+ */
+ public function getNumber() {
+
+ if ( !$this->isValid() ) {
+ return 999999999999999;
+ }
+
+ return $this->m_dataitem->getNumber();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return float
+ */
+ public function getLocalizedFormattedNumber( $value ) {
+ return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision() );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return float
+ */
+ public function getNormalizedFormattedNumber( $value ) {
+ return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision(), IntlNumberFormatter::VALUE_FORMAT );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @return string
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ *
+ * @return string
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ *
+ * @return string
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ *
+ * @return string
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ *
+ * @return string
+ */
+ public function getWikiValue() {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::VALUE );
+ }
+
+ /**
+ * @see DataVelue::getInfolinks
+ *
+ * @return array
+ */
+ public function getInfolinks() {
+
+ // When generating an infoLink, use the normalized value without any
+ // precision limitation
+ $this->setOption( self::NO_DISP_PRECISION_LIMIT, true );
+ $this->setOption( self::OPT_CONTENT_LANGUAGE, Message::CONTENT_LANGUAGE );
+
+ $infoLinks = parent::getInfolinks();
+
+ $this->setOption( self::NO_DISP_PRECISION_LIMIT, false );
+
+ return $infoLinks;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCanonicalMainUnit() {
+ return $this->m_unitin;
+ }
+
+ /**
+ * Returns array of converted unit-value-pairs that can be
+ * printed.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getConvertedUnitValues() {
+ $this->makeConversionValues();
+ return $this->m_unitvalues;
+ }
+
+ /**
+ * Return the unit in which the returned value is to be interpreted.
+ * This string is a plain UTF-8 string without wiki or html markup.
+ * The returned value is a canonical ID for the main unit.
+ * Returns the empty string if no unit is given for the value.
+ * Overwritten by subclasses that support units.
+ */
+ public function getUnit() {
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $unit
+ *
+ * @return boolean
+ */
+ public function hasPrefixalUnitPreference( $unit ) {
+ return isset( $this->prefixalUnitPreference[$unit] ) && $this->prefixalUnitPreference[$unit];
+ }
+
+ /**
+ * Create links to mapping services based on a wiki-editable message.
+ * The parameters available to the message are:
+ * $1: string of numerical value in English punctuation
+ * $2: string of integer version of value, in English punctuation
+ *
+ * @return array
+ */
+ protected function getServiceLinkParams() {
+ if ( $this->isValid() ) {
+ return [ strval( $this->m_dataitem->getNumber() ), strval( round( $this->m_dataitem->getNumber() ) ) ];
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * Transform a (typically unit-) string into a normalised form,
+ * so that, e.g., "km²" and "km<sup>2</sup>" do not need to be
+ * distinguished.
+ */
+ public function normalizeUnit( $unit ) {
+ $unit = str_replace( [ '[[', ']]' ], '', trim( $unit ) ); // allow simple links to be used inside annotations
+ $unit = str_replace( [ '²', '<sup>2</sup>' ], '&sup2;', $unit );
+ $unit = str_replace( [ '³', '<sup>3</sup>' ], '&sup3;', $unit );
+ return smwfXMLContentEncode( $unit );
+ }
+
+ /**
+ * Compute the value based on the given input number and unit string.
+ * If the unit is not supported, return false, otherwise return true.
+ * This is called when parsing user input, where the given unit value
+ * has already been normalized.
+ *
+ * This class does not support any (non-empty) units, but subclasses
+ * may overwrite this behavior.
+ * @param $number float value obtained by parsing user input
+ * @param $unit string after the numericla user input
+ * @return boolean specifying if the unit string is allowed
+ */
+ protected function convertToMainUnit( $number, $unit ) {
+ $this->m_dataitem = new SMWDINumber( $number );
+ $this->m_unitin = '';
+ return ( $unit === '' );
+ }
+
+ /**
+ * This method creates an array of unit-value-pairs that should be
+ * printed. Units are the keys and should be canonical unit IDs.
+ * The result is stored in $this->m_unitvalues. Again, any class that
+ * requires effort for doing this should first check whether the array
+ * is already set (i.e. not false) before doing any work.
+ * Note that the values should be plain numbers. Output formatting is done
+ * later when needed. Also, it should be checked if the value is valid
+ * before trying to calculate with its contents.
+ * This method also must call or implement convertToMainUnit().
+ *
+ * Overwritten by subclasses that support units.
+ */
+ protected function makeConversionValues() {
+ $this->m_unitvalues = [ '' => $this->m_dataitem->getNumber() ];
+ }
+
+ /**
+ * This method is used when no user input was given to find the best
+ * values for m_unitin and m_caption. After conversion,
+ * these fields will look as if they were generated from user input,
+ * and convertToMainUnit() will have been called (if not, it would be
+ * blocked by the presence of m_unitin).
+ *
+ * Overwritten by subclasses that support units.
+ */
+ protected function makeUserValue() {
+ $this->m_caption = '';
+
+ $number = $this->m_dataitem->getNumber();
+
+ // -u is the format for displaying the unit only
+ if ( $this->m_outformat == '-u' ) {
+ $this->m_caption = '';
+ } elseif ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ) {
+ $this->m_caption = $this->getLocalizedFormattedNumber( $number );
+ } else {
+ $this->m_caption = $this->getNormalizedFormattedNumber( $number );
+ }
+
+ // no unit ever, so nothing to do about this
+ $this->m_unitin = '';
+ }
+
+ /**
+ * Return an array of major unit strings (ids only recommended) supported by
+ * this datavalue.
+ *
+ * Overwritten by subclasses that support units.
+ */
+ public function getUnitList() {
+ return [ '' ];
+ }
+
+ protected function getPreferredDisplayPrecision() {
+
+ // Don't restrict the value with a display precision
+ if ( $this->getProperty() === null || $this->getOption( self::NO_DISP_PRECISION_LIMIT ) ) {
+ return false;
+ }
+
+ if ( $this->precision === null ) {
+ $this->precision = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getDisplayPrecision(
+ $this->getProperty()
+ );
+ }
+
+ return $this->precision;
+ }
+
+ private function findPrecisionFrom( $formatstring ) {
+
+ if ( strpos( $formatstring, '-' ) === false ) {
+ return $formatstring;
+ }
+
+ $parts = explode( '-', $formatstring );
+
+ // Find precision from annotated -p<number of digits> formatstring which
+ // has priority over a possible _PREC value
+ foreach ( $parts as $key => $value ) {
+ if ( strpos( $value, 'p' ) !== false && is_numeric( substr( $value, 1 ) ) ) {
+ $this->precision = strval( substr( $value, 1 ) );
+ unset( $parts[$key] );
+ }
+ }
+
+ // Rebuild formatstring without a possible p element to ensure other
+ // options can be used in combination such as -n-p2 etc.
+ return implode( '-', $parts );
+ }
+
+ private function getNumberFormatter() {
+
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::USER_LANGUAGE,
+ $this->getOption( self::OPT_USER_LANGUAGE )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ self::THOUSANDS_SEPARATOR,
+ $this->getOption( self::THOUSANDS_SEPARATOR )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ self::DECIMAL_SEPARATOR,
+ $this->getOption( self::DECIMAL_SEPARATOR )
+ );
+
+ return $this->intlNumberFormatter;
+ }
+
+ private function findPreferredLanguageFrom( &$formatstring ) {
+ // Localized preferred user language
+ if ( strpos( $formatstring, 'LOCL' ) !== false && ( $languageCode = Localizer::getLanguageCodeFrom( $formatstring ) ) !== false ) {
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::PREFERRED_LANGUAGE,
+ $languageCode
+ );
+ }
+
+ // Remove any remaining
+ $formatstring = str_replace( [ '#LOCL', 'LOCL' ], '', $formatstring );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php
new file mode 100644
index 00000000..7c255aaa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements special processing suitable for defining the list
+ * of properties that is required for SMWRecordValue objects. The input is a
+ * plain semicolon-separated list of property names, optionally with the
+ * namespace prefix.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWPropertyListValue extends SMWDataValue {
+ /**
+ * List of properte data items that are stored.
+ * @var array of SMWDIProperty
+ */
+ protected $m_diProperties;
+
+ protected function parseUserValue( $value ) {
+ global $wgContLang;
+
+ $this->m_diProperties = [];
+ $stringValue = '';
+ $valueList = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
+ foreach ( $valueList as $propertyName ) {
+ $propertyNameParts = explode( ':', $propertyName, 2 );
+ if ( count( $propertyNameParts ) > 1 ) {
+ $namespace = smwfNormalTitleText( $propertyNameParts[0] );
+ $propertyName = $propertyNameParts[1];
+ $propertyNamespace = $wgContLang->getNsText( SMW_NS_PROPERTY );
+ if ( $namespace != $propertyNamespace ) {
+ $this->addErrorMsg( [ 'smw_wrong_namespace', $propertyNamespace ] );
+ }
+ }
+
+ $propertyName = smwfNormalTitleText( $propertyName );
+
+ try {
+ $diProperty = SMW\DIProperty::newFromUserLabel( $propertyName );
+ } catch ( SMWDataItemException $e ) {
+ $diProperty = new SMW\DIProperty( 'Error' );
+ $this->addErrorMsg( [ 'smw_noproperty', $propertyName ] );
+ }
+
+ $this->m_diProperties[] = $diProperty;
+ $stringValue .= ( $stringValue ? ';' : '' ) . $diProperty->getKey();
+ }
+
+ $this->m_dataitem = new SMWDIBlob( $stringValue );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ *
+ * @param $dataitem SMWDataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( !$dataItem instanceof SMWDIBlob ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_diProperties = [];
+
+ foreach ( explode( ';', $dataItem->getString() ) as $propertyKey ) {
+ $property = null;
+
+ try {
+ $property = new SMW\DIProperty( $propertyKey );
+ } catch ( SMWDataItemException $e ) {
+ $property = new SMW\DIProperty( 'Error' );
+ $this->addErrorMsg( [ 'smw-datavalue-propertylist-invalid-property-key', $dataItem->getString(), $propertyKey ] );
+ }
+
+ if ( $property instanceof SMWDIProperty ) {
+ // Find a possible redirect
+ $this->m_diProperties[] = $property->getRedirectTarget();
+ }
+ }
+
+ $this->m_caption = false;
+
+ return true;
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return ( $this->m_caption !== false ) ? $this->m_caption : $this->makeOutputText( 2, $linked );
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return ( $this->m_caption !== false ) ? $this->m_caption : $this->makeOutputText( 3, $linker );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->makeOutputText( 2, $linked );
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->makeOutputText( 3, $linker );
+ }
+
+ public function getWikiValue() {
+ return $this->makeOutputText( 4 );
+ }
+
+ public function getPropertyDataItems() {
+ return $this->m_diProperties;
+ }
+
+////// Internal helper functions
+
+ protected function makeOutputText( $type, $linker = null ) {
+ if ( !$this->isValid() ) {
+ return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText();
+ }
+ $result = '';
+ $sep = ( $type == 4 ) ? '; ' : ', ';
+ foreach ( $this->m_diProperties as $diProperty ) {
+ if ( $result !== '' ) {
+ $result .= $sep;
+ }
+ $propertyValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diProperty, null );
+ $result .= $this->makeValueOutputText( $type, $propertyValue, $linker );
+ }
+ return $result;
+ }
+
+ protected function makeValueOutputText( $type, $propertyValue, $linker ) {
+ switch ( $type ) {
+ case 0:
+ return $propertyValue->getShortWikiText( $linker );
+ case 1:
+ return $propertyValue->getShortHTMLText( $linker );
+ case 2:
+ return $propertyValue->getLongWikiText( $linker );
+ case 3:
+ return $propertyValue->getLongHTMLText( $linker );
+ case 4:
+ return $propertyValue->getWikiValue();
+ }
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php
new file mode 100644
index 00000000..75e1dc57
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php
@@ -0,0 +1,218 @@
+<?php
+
+use SMW\DataValues\Number\UnitConverter;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements unit support custom units, for which users have
+ * provided linear conversion factors within the wiki. Those user settings
+ * are retrieved from a property page associated with this object.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWQuantityValue extends SMWNumberValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_qty';
+
+ /**
+ * Array with format (canonical unit ID string) => (conversion factor)
+ * @var float[]|bool
+ */
+ protected $m_unitfactors = false;
+
+ /**
+ * Array with format (normalised unit string) => (canonical unit ID string)
+ * @var string[]|bool
+ */
+ protected $m_unitids = false;
+
+ /**
+ * Ordered array of (normalized) units that should be displayed in tooltips, etc.
+ * @var string[]|bool
+ */
+ protected $m_displayunits = false;
+
+ /**
+ * Main unit in canonical form (recognised by the conversion factor 1)
+ * @var string|bool
+ */
+ protected $m_mainunit = false;
+
+ protected function convertToMainUnit( $number, $unit ) {
+ $this->initConversionData();
+
+ if ( array_key_exists( $unit, $this->m_unitids ) ) {
+ $this->m_unitin = $this->m_unitids[$unit];
+ assert( $this->m_unitfactors[$this->m_unitin] != 0 /* Should be filtered by initConversionData() */ );
+ $this->m_dataitem = new SMWDINumber( $number / $this->m_unitfactors[$this->m_unitin], $this->m_typeid );
+ return true;
+ } else { // unsupported unit
+ return false;
+ }
+ }
+
+ protected function makeConversionValues() {
+ if ( $this->m_unitvalues !== false ) {
+ return; // do this only once
+ }
+
+ $this->m_unitvalues = [];
+
+ if ( !$this->isValid() ) {
+ return;
+ }
+
+ $this->initDisplayData();
+
+ if ( count( $this->m_displayunits ) == 0 ) { // no display units, just show all
+ foreach ( $this->m_unitfactors as $unit => $factor ) {
+ if ( $unit !== '' ) { // filter out the empty fallback unit that is always there
+ $this->m_unitvalues[$unit] = $this->m_dataitem->getNumber() * $factor;
+ }
+ }
+ } else {
+ foreach ( $this->m_displayunits as $unit ) {
+ /// NOTE We keep non-ID units unless the input unit is used, so display units can be used to pick
+ /// the preferred form of a unit. Doing this requires us to recompute the conversion values whenever
+ /// the m_unitin changes.
+ $unitkey = ( $this->m_unitids[$unit] == $this->m_unitin ) ? $this->m_unitids[$unit] : $unit;
+ $this->m_unitvalues[$unitkey] = $this->m_dataitem->getNumber() * $this->m_unitfactors[$this->m_unitids[$unit]];
+ }
+ }
+ }
+
+ protected function makeUserValue() {
+
+ // The normalised string of a known unit to use for printouts
+ $printunit = false;
+ $unitfactor = 1;
+
+ // Check if a known unit is given as outputformat:
+ if ( ( $this->m_outformat ) && ( $this->m_outformat != '-' ) &&
+ ( $this->m_outformat != '-n' ) && ( $this->m_outformat != '-u' ) ) { // first try given output unit
+ $wantedunit = $this->normalizeUnit( $this->m_outformat );
+ if ( array_key_exists( $wantedunit, $this->m_unitids ) ) {
+ $printunit = $wantedunit;
+ }
+ }
+
+ // Alternatively, try to use the main display unit as a default:
+ if ( $printunit === false ) {
+ $this->initDisplayData();
+ if ( count( $this->m_displayunits ) > 0 ) {
+ $printunit = reset( $this->m_displayunits );
+ }
+ }
+ // Finally, fall back to main unit:
+ if ( $printunit === false ) {
+ $printunit = $this->getUnit();
+ }
+
+ $asPrefix = isset( $this->prefixalUnitPreference[$printunit] ) && $this->prefixalUnitPreference[$printunit];
+ $this->m_unitin = isset( $this->m_unitids[$printunit] ) ? $this->m_unitids[$printunit] : 0;
+
+ // This array depends on m_unitin if displayunits were used, better
+ // invalidate it here
+ $this->m_unitvalues = false;
+ $this->m_caption = '';
+
+ if ( isset( $this->m_unitfactors[$this->m_unitin] ) ) {
+ $unitfactor = $this->m_unitfactors[$this->m_unitin];
+ }
+
+ $value = $this->m_dataitem->getNumber() * $unitfactor;
+
+ // -u is the format for displaying the unit only
+ if ( $this->m_outformat != '-u' ) {
+ $this->m_caption .= ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ? $this->getLocalizedFormattedNumber( $value ) : $this->getNormalizedFormattedNumber( $value ) );
+ }
+
+ // -n is the format for displaying the number only
+ if ( ( $printunit !== '' ) && ( $this->m_outformat != '-n' ) ) {
+
+ $sep = '';
+
+ if ( $this->m_outformat != '-u' ) {
+ $sep = ( $this->m_outformat != '-' ? '&#160;' : ' ' );
+ }
+
+ $this->m_caption = $asPrefix ? $printunit . $sep . $this->m_caption : $this->m_caption . $sep . $printunit;
+ }
+ }
+
+ public function getUnitList() {
+ $this->initConversionData();
+ return array_keys( $this->m_unitfactors );
+ }
+
+ public function getUnit() {
+ $this->initConversionData();
+ return $this->m_mainunit;
+ }
+
+/// The remaining functions are relatively "private" but are kept protected since
+/// subclasses might exploit this to, e.g., "fake" conversion factors instead of
+/// getting them from the database. A cheap way of making built-in types.
+
+ /**
+ * This method initializes $m_unitfactors, $m_unitids, and $m_mainunit.
+ */
+ protected function initConversionData() {
+
+ if ( $this->m_unitids !== false ) {
+ return;
+ }
+
+ $unitConverter = new UnitConverter( $this );
+ $unitConverter->initConversionData( $this->m_property );
+
+ if ( $unitConverter->getErrors() !== [] ) {
+ foreach ( $unitConverter->getErrors() as $error ) {
+ $this->addErrorMsg(
+ $error,
+ Message::TEXT,
+ Message::USER_LANGUAGE
+ );
+ }
+ }
+
+ $this->m_unitids = $unitConverter->getUnitIds();
+ $this->m_unitfactors = $unitConverter->getUnitFactors();
+ $this->m_mainunit = $unitConverter->getMainUnit();
+ $this->prefixalUnitPreference = $unitConverter->getPrefixalUnitPreference();
+ }
+
+ /**
+ * This method initializes $m_displayunits.
+ */
+ protected function initDisplayData() {
+ if ( $this->m_displayunits !== false ) {
+ return; // do the below only once
+ }
+ $this->initConversionData(); // needed to normalise unit strings
+ $this->m_displayunits = [];
+
+ if ( is_null( $this->m_property ) || is_null( $this->m_property->getDIWikiPage() ) ) {
+ return;
+ }
+
+ $units = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getDisplayUnits(
+ $this->getProperty()
+ );
+
+ foreach ( $units as $unit ) {
+ $unit = $this->normalizeUnit( $unit );
+ if ( array_key_exists( $unit, $this->m_unitids ) ) {
+ $this->m_displayunits[] = $unit; // do not avoid duplicates, users can handle this
+ } // note: we ignore unsuppported units -- no way to display them
+ }
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php
new file mode 100644
index 00000000..f4bab7e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php
@@ -0,0 +1,300 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\AbstractMultiValue;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDataItem as DataItem;
+use SMWDIContainer as DIContainer;
+
+/**
+ * SMWDataValue implements the handling of small sets of property-value pairs.
+ * The declaration of Records in SMW uses the order of values to encode the
+ * property that should be used, so the user only needs to enter a list of
+ * values. Internally, however, the property-value assignments are not stored
+ * with a particular order; they will only be ordered for display, following
+ * the declaration. This is why it is not supported to have Records using the
+ * same property for more than one value.
+ *
+ * The class uses DIContainer objects to return its inner state. See the
+ * documentation for DIContainer for details on how this "pseudo" data
+ * encapsulated many property assignments. Such data is stored internally
+ * like a page with various property-value assignments. Indeed, record values
+ * can be created from DIWikiPage objects (the missing information will
+ * be fetched from the store).
+ *
+ * @todo Enforce limitation of maximal number of values.
+ * @todo Enforce uniqueness of properties in declaration.
+ * @todo Complete internationalisation.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWRecordValue extends AbstractMultiValue {
+
+ /// cache for properties for the fields of this data value
+ protected $m_diProperties = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( '_rec' );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return DIProperty[]|null
+ */
+ public function getProperties() {
+ return $this->m_diProperties;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $value
+ *
+ * @return array
+ */
+ public function getValuesFromString( $value ) {
+ // #664 / T17732
+ $value = str_replace( "\;", "-3B", $value );
+
+ // Bug 21926 / T23926
+ // Values that use html entities are encoded with a semicolon
+ $value = htmlspecialchars_decode( $value, ENT_QUOTES );
+ $values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
+
+ return str_replace( "-3B", ";", $values );
+ }
+
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ return;
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData( $value );
+ $sortKeys = [];
+
+ $values = $this->getValuesFromString( $value );
+ $valueIndex = 0; // index in value array
+ $propertyIndex = 0; // index in property list
+ $empty = true;
+
+ foreach ( $this->getPropertyDataItems() as $diProperty ) {
+
+ if ( !array_key_exists( $valueIndex, $values ) || $this->getErrors() !== [] ) {
+ break; // stop if there are no values left
+ }
+
+ // generating the DVs:
+ if ( ( $values[$valueIndex] === '' ) || ( $values[$valueIndex] == '?' ) ) { // explicit omission
+ $valueIndex++;
+ } else {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $diProperty,
+ $values[$valueIndex],
+ false,
+ $containerSemanticData->getSubject()
+ );
+
+ if ( $dataValue->isValid() ) { // valid DV: keep
+ $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() );
+ $sortKeys[] = $dataValue->getDataItem()->getSortKey();
+
+ $valueIndex++;
+ $empty = false;
+ } elseif ( ( count( $values ) - $valueIndex ) == ( count( $this->m_diProperties ) - $propertyIndex ) ) {
+ $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() );
+ $this->addError( $dataValue->getErrors() );
+ ++$valueIndex;
+ }
+ }
+ ++$propertyIndex;
+ }
+
+ if ( $empty && $this->getErrors() === [] ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ }
+
+ // Remember the data to extend the sortkey
+ $containerSemanticData->setExtensionData( 'sort.data', implode( ';', $sortKeys ) );
+
+ $this->m_dataitem = new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem DataItem
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+ if ( $dataItem->getDIType() == DataItem::TYPE_CONTAINER ) {
+ $this->m_dataitem = $dataItem;
+ return true;
+ } elseif ( $dataItem->getDIType() == DataItem::TYPE_WIKIPAGE ) {
+ $semanticData = new ContainerSemanticData( $dataItem );
+ $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
+ $this->m_dataitem = new DIContainer( $semanticData );
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ if ( $this->m_caption !== false ) {
+ return $this->m_caption;
+ }
+ return $this->makeOutputText( 0, $linked );
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ if ( $this->m_caption !== false ) {
+ return $this->m_caption;
+ }
+ return $this->makeOutputText( 1, $linker );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->makeOutputText( 2, $linked );
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->makeOutputText( 3, $linker );
+ }
+
+ public function getWikiValue() {
+ return $this->makeOutputText( 4 );
+ }
+
+ /**
+ * Make sure that the content is reset in this case.
+ * @todo This is not a full reset yet (the case that property is changed after a value
+ * was set does not occur in the normal flow of things, hence this has low priority).
+ */
+ public function setProperty( DIProperty $property ) {
+ parent::setProperty( $property );
+ $this->m_diProperties = null;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIProperty[] $properties
+ */
+ public function setFieldProperties( array $properties ) {
+ foreach ( $properties as $property ) {
+ if ( $property instanceof DIProperty ) {
+ $this->m_diProperties[] = $property;
+ }
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * {@inheritDoc}
+ */
+ public function getDataItems() {
+ return parent::getDataItems();
+ }
+
+ /**
+ * Return the array (list) of properties that the individual entries of
+ * this datatype consist of.
+ *
+ * @since 1.6
+ *
+ * @todo I18N for error message.
+ *
+ * @return array of DIProperty
+ */
+ public function getPropertyDataItems() {
+
+ if ( $this->m_diProperties !== null ) {
+ return $this->m_diProperties;
+ }
+
+ $this->m_diProperties = $this->getFieldProperties( $this->m_property );
+
+ if ( $this->m_diProperties === [] ) { // TODO internalionalize
+ $this->addError( 'The list of properties to be used for the data fields has not been specified properly.' );
+ }
+
+ return $this->m_diProperties;
+ }
+
+ protected function makeOutputText( $type = 0, $linker = null ) {
+ if ( !$this->isValid() ) {
+ return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText();
+ }
+
+ $result = '';
+ $i = 0;
+ foreach ( $this->getPropertyDataItems() as $propertyDataItem ) {
+ if ( $i == 1 ) {
+ $result .= ( $type == 4 ) ? '; ' : ' (';
+ } elseif ( $i > 1 ) {
+ $result .= ( $type == 4 ) ? '; ' : ', ';
+ }
+ ++$i;
+ $propertyValues = $this->m_dataitem->getSemanticData()->getPropertyValues( $propertyDataItem ); // combining this with next line violates PHP strict standards
+ $dataItem = reset( $propertyValues );
+ if ( $dataItem !== false ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $propertyDataItem );
+ $result .= $this->makeValueOutputText( $type, $dataValue, $linker );
+ } else {
+ $result .= '?';
+ }
+ }
+ if ( ( $i > 1 ) && ( $type != 4 ) ) {
+ $result .= ')';
+ }
+
+ return $result;
+ }
+
+ protected function makeValueOutputText( $type, SMWDataValue $dataValue, $linker ) {
+ switch ( $type ) {
+ case 0:
+ return $dataValue->getShortWikiText( $linker );
+ case 1:
+ return $dataValue->getShortHTMLText( $linker );
+ case 2:
+ return $dataValue->getShortWikiText( $linker );
+ case 3:
+ return $dataValue->getShortHTMLText( $linker );
+ case 4:
+ return str_replace( ";", "\;", $dataValue->getWikiValue() );
+ }
+ }
+
+ private function newContainerSemanticData( $value ) {
+
+ if ( $this->m_contextPage === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ $containerSemanticData->skipAnonymousCheck();
+ } else {
+ $subobjectName = '_' . hash( 'md4', $value, false ); // md4 is probably fastest of PHP's hashes
+
+ $subject = new DIWikiPage(
+ $this->m_contextPage->getDBkey(),
+ $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getInterwiki(),
+ $subobjectName
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return $containerSemanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php
new file mode 100644
index 00000000..9a6962cd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php
@@ -0,0 +1,699 @@
+<?php
+
+use SMW\DataValues\Time\Components;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Localizer;
+use SMWDITime as DITime;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue captures values of dates and times, in many formats,
+ * throughout history and pre-history. The implementation can handle dates
+ * across history with full precision for storing, and substantial precision
+ * for sorting and querying. The range of supported past dates should encompass
+ * the Beginning of Time according to most of today's theories. The range of
+ * supported future dates is limited more strictly, but it does also allow
+ * year numbers in the order of 10^9.
+ *
+ * The implementation notices and stores whether parts of a date/time have been
+ * omitted (as in "2008" or "May 2007"). For all exporting and sorting
+ * purposes, incomplete dates are completed with defaults (usually using the
+ * earliest possible time, i.e. interpreting "2008" as "Jan 1 2008 00:00:00").
+ * The information on what was unspecified is kept internally for improving
+ * behavior e.g. for outputs (defaults are not printed when querying for a
+ * value). This largely uses the precision handling of DITime.
+ *
+ *
+ * Date formats
+ *
+ * Dates can be given in many formats, using numbers, month names, and
+ * abbreviated month names. The preferred interpretation of ambiguous dates
+ * ("1 2 2008" or even "1 2 3 BC") is controlled by the language file, as is
+ * the local naming of months. English month names are always supported.
+ *
+ * Dates can be given in Gregorian or Julian calendar, set by the token "Jl"
+ * or "Gr" in the input. If neither is set, a default is chosen: inputs after
+ * October 15, 1582 (the time when the Gregorian calendar was first inaugurated
+ * in some parts of the world) are considered Gr, earlier inputs are considered
+ * Jl. In addition to Jl and Gr, we support "OS" (Old Style English dates that
+ * refer to the use of Julian calendar with a displaced change of year on March
+ * 24), JD (direct numerical input in Julian Day notation), and MJD (direct
+ * numerical input in Modified Julian Day notation as used in aviation and
+ * space flight).
+ *
+ * The class does not support the input of negative year numbers but uses the
+ * markers "BC"/"BCE" and "AD"/"CE" instead. There is no year 0 in Gregorian or
+ * Julian calendars, but the class graciously considers this input to mean year
+ * 1 BC(E).
+ *
+ * For prehisoric dates before 9999 BC(E) only year numbers are allowed
+ * (nothing else makes much sense). At this time, the years of Julian and
+ * Gregorian calendar still overlap significantly, so the transition to a
+ * purely solar annotation of prehistoric years is smooth. Technically, the
+ * class will consider prehistoric dates as Gregorian but very ancient times
+ * may be interpreted as desired (probably with reference to a physical notion
+ * of time that is not dependent on revolutions of earth around the sun).
+ *
+ *
+ * Time formats
+ *
+ * Times can be in formats like "23:12:45" and "12:30" possibly with additional
+ * modifiers "am" or "pm". Timezones are supported: the class knows many
+ * international timezone monikers (e.g. CET or GMT) and also allows time
+ * offsets directly after a time (e.g. "10:30-3:30" or "14:45:23+2"). Such
+ * offsets always refer to UTC. Timezones are only used on input and are not
+ * stored as part of the value.
+ *
+ * Time offsets take leap years into account, e.g. the date
+ * "Feb 28 2004 23:00+2:00" is equivalent to "29 February 2004 01:00:00", while
+ * "Feb 28 1900 23:00+2:00" is equivalent to "1 March 1900 01:00:00".
+ *
+ * Military time format is supported. This consists of 4 or 6 numeric digits
+ * followed by a one-letter timezone code (e.g. 1240Z is equivalent to 12:40
+ * UTC).
+ *
+ *
+ * I18N
+ *
+ * Currently, neither keywords like "BCE", "Jl", or "pm", nor timezone monikers
+ * are internationalized. Timezone monikers may not require this, other than
+ * possibly for Cyrillic (added when needed). Month names are fully
+ * internationalized, but English names and abbreviations will also work in all
+ * languages. The class also supports ordinal day-of-month annotations like
+ * "st" and "rd", again only for English.
+ *
+ * I18N includes the preferred order of dates, e.g. to interpret "5 6 2010".
+ *
+ * @todo Theparsing process can encounter many kinds of well-defined problems
+ * but uses only one error message. More detailed reporting should be done.
+ * @todo Try to reuse more of MediaWiki's records, e.g. to obtain month names
+ * or to format dates. The problem is that MW is based on SIO timestamps that
+ * don't extend to very ancient or future dates, and that MW uses PHP functions
+ * that are bound to UNIX time.
+ *
+ * @author Markus Krötzsch
+ * @author Fabian Howahl
+ * @author Terry A. Hurlbut
+ * @ingroup SMWDataValues
+ */
+class SMWTimeValue extends SMWDataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_dat';
+
+ protected $m_dataitem_greg = null;
+ protected $m_dataitem_jul = null;
+
+ protected $m_wikivalue; // a suitable wiki input value
+
+ /**
+ * The following are constant (array-valued constants are not supported, hence
+ * the declaration as private static variable):
+ *
+ * @var array
+ */
+ protected static $m_formats = [
+ SMW_Y => [ 'y' ],
+ SMW_YM => [ 'y', 'm' ],
+ SMW_MY => [ 'm', 'y' ],
+ SMW_YDM => [ 'y', 'd', 'm' ],
+ SMW_YMD => [ 'y', 'm', 'd' ],
+ SMW_DMY => [ 'd', 'm', 'y' ],
+ SMW_MDY => [ 'm', 'd', 'y' ]
+ ];
+
+ /**
+ * Moment of switchover to Gregorian calendar.
+ */
+ const J1582 = 2299160.5;
+
+ /**
+ * Offset of Julian Days for Modified JD inputs.
+ */
+ const MJD_EPOCH = 2400000.5;
+
+ /**
+ * The year before which we do not accept anything but year numbers and
+ * largely discourage calendar models.
+ */
+ const PREHISTORY = -10000;
+
+ /**
+ * @see DataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+
+ $value = Localizer::convertDoubleWidth( $value );
+
+ $this->m_wikivalue = $value;
+ $this->m_dataitem = null;
+
+ // Store the caption now
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ $timeValueParser = $this->dataValueServiceFactory->getValueParser(
+ $this
+ );
+
+ $timeValueParser->clearErrors();
+
+ // Parsing is bound to the content language otherwise any change of a user
+ // preferred user language would negate the parsing results
+ $timeValueParser->setLanguageCode(
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ if ( $this->isYear( $value ) ) {
+ try {
+ $this->m_dataitem = new DITime( $this->getCalendarModel( null, $value, null, null ), $value );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid', $value, $e->getMessage() ] );
+ }
+ } elseif ( $this->isTimestamp( $value ) ) {
+ $this->m_dataitem = DITime::newFromTimestamp( $value );
+ } elseif ( ( $components = $timeValueParser->parse( $value ) ) ) {
+
+ $calendarmodel = $components->get( 'calendarmodel' );
+
+ if ( ( $calendarmodel == 'JD' ) || ( $calendarmodel == 'MJD' ) ) {
+ $this->setDateFromJD( $components );
+ } else {
+ $this->setDateFromParsedValues( $components );
+ }
+ }
+
+ foreach ( $timeValueParser->getErrors() as $err ) {
+ $this->addErrorMsg( $err );
+ }
+
+ // Make sure that m_dataitem is set in any case
+ if ( $this->m_dataitem === null ) {
+ $this->m_dataitem = new DITime( DITime::CM_GREGORIAN, 32202 );
+ }
+ }
+
+ /**
+ * Validate and interpret the date components as retrieved when parsing
+ * a user input. The method takes care of guessing how a list of values
+ * such as "10 12 13" is to be interpreted using the current language
+ * settings. The result is stored in the call-by-ref parameter
+ * $date that uses keys 'y', 'm', 'd' and contains the respective
+ * numbers as values, or false if not specified. If errors occur, error
+ * messages are added to the objects list of errors, and false is
+ * returned. Otherwise, true is returned.
+ *
+ * @param $datecomponents array of strings that might belong to the specification of a date
+ * @param $date array set to result
+ *
+ * @return boolean stating if successful
+ */
+ protected function interpretDateComponents( $datecomponents, &$date ) {
+
+ // The following code segment creates a bit vector to encode
+ // which role each digit of the entered date can take (day,
+ // year, month). The vector starts with 1 and contains three
+ // bits per date component, set ot true whenever this component
+ // could be a month, a day, or a year (this is the order).
+ // Examples:
+ // 100 component could only be a month
+ // 010 component could only be a day
+ // 001 component could only be a year
+ // 011 component could be a day or a year but no month etc.
+ // For three components, we thus get a 10 digit bit vector.
+ $datevector = 1;
+ $propercomponents = [];
+ $justfounddash = true; // avoid two dashes in a row, or dashes at the end
+ $error = false;
+ $numvalue = 0;
+ foreach ( $datecomponents as $component ) {
+ if ( $component == "-" ) {
+ if ( $justfounddash ) {
+ $error = true;
+ break;
+ }
+ $justfounddash = true;
+ } else {
+ $justfounddash = false;
+ $datevector = ( $datevector << 3 ) | $this->checkDateComponent( $component, $numvalue );
+ $propercomponents[] = $numvalue;
+ }
+ }
+
+ if ( ( $error ) || ( $justfounddash ) || ( count( $propercomponents ) == 0 ) || ( count( $propercomponents ) > 3 ) ) {
+
+ $msgKey = 'smw-datavalue-time-invalid-date-components';
+
+ if ( $justfounddash ) {
+ $msgKey .= '-dash';
+ } elseif ( count( $propercomponents ) == 0 ) {
+ $msgKey .= '-empty';
+ } elseif ( count( $propercomponents ) > 3 ) {
+ $msgKey .= '-three';
+ } else{
+ $msgKey .= '-common';
+ }
+
+ $this->addErrorMsg( [ $msgKey, $this->m_wikivalue ] );
+ return false;
+ }
+
+ // Now use the bitvector to find the preferred interpretation of the date components:
+ $dateformats = Localizer::getInstance()->getLang( $this->getOption( self::OPT_CONTENT_LANGUAGE ) )->getDateFormats();
+ $date = [ 'y' => false, 'm' => false, 'd' => false ];
+
+ foreach ( $dateformats[count( $propercomponents ) - 1] as $formatvector ) {
+ if ( !( ~$datevector & $formatvector ) ) { // check if $formatvector => $datevector ("the input supports the format")
+ $i = 0;
+ foreach ( self::$m_formats[$formatvector] as $fieldname ) {
+ $date[$fieldname] = $propercomponents[$i];
+ $i += 1;
+ }
+ break;
+ }
+ }
+
+ if ( $date['y'] === false ) { // no band matches the entered date
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-date-components-sequence', $this->m_wikivalue ] );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Initialise data from an anticipated JD value.
+ */
+ private function setDateFromJD( $components ) {
+
+ $datecomponents = $components->get( 'datecomponents' );
+ $calendarmodel = $components->get( 'calendarmodel' );
+ $era = $components->get( 'era' );
+ $hours = $components->get( 'hours' );
+
+ if ( ( $era === false ) && ( $hours === false ) && ( $components->get( 'timeoffset' ) == 0 ) ) {
+ try {
+ $jd = floatval( isset( $datecomponents[1] ) ? $datecomponents[0] . '.' . $datecomponents[1] : $datecomponents[0] );
+ if ( $calendarmodel == 'MJD' ) {
+ $jd += self::MJD_EPOCH;
+ }
+ $this->m_dataitem = DITime::newFromJD( $jd, DITime::CM_GREGORIAN, DITime::PREC_YMDT, $components->get( 'timezone' ) );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, $e->getMessage() ] );
+ }
+ } else {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, "NO_EXCEPTION" ] );
+ }
+ }
+
+ /**
+ * Initialise data from the provided intermediate results after
+ * parsing, assuming that a conventional date notation is used.
+ * If errors occur, error messages are added to the objects list of
+ * errors, and false is returned. Otherwise, true is returned.
+ *
+ * @param $datecomponents array of strings that might belong to the specification of a date
+ * @param $calendarmodesl string if model was set in input, otherwise false
+ * @param $era string '+' or '-' if provided, otherwise false
+ * @param $hours integer value between 0 and 24
+ * @param $minutes integer value between 0 and 59
+ * @param $seconds integer value between 0 and 59, or false if not given
+ * @param $timeoffset double value for time offset (e.g. 3.5), or false if not given
+ *
+ * @return boolean stating if successful
+ */
+ protected function setDateFromParsedValues( $components ) {
+
+ $datecomponents = $components->get( 'datecomponents' );
+ $calendarmodel = $components->get( 'calendarmodel' );
+ $era = $components->get( 'era' );
+ $hours = $components->get( 'hours' );
+ $minutes = $components->get( 'minutes' );
+ $seconds = $components->get( 'seconds' );
+ $microseconds = $components->get( 'microseconds' );
+ $timeoffset = $components->get( 'timeoffset' );
+ $timezone = $components->get( 'timezone' );
+
+ $date = false;
+
+ if ( !$this->interpretDateComponents( $datecomponents, $date ) ) {
+ return false;
+ }
+
+ // Handle BC: the year is negative.
+ if ( ( $era == '-' ) && ( $date['y'] > 0 ) ) { // see class documentation on BC, "year 0", and ISO conformance ...
+ $date['y'] = -( $date['y'] );
+ }
+
+ // Keep information about the era
+ if ( ( $era == '+' ) && ( $date['y'] > 0 ) ) {
+ $date['y'] = $era . $date['y'];
+ }
+
+ // Old Style is a special case of Julian calendar model where the change of the year was 25 March:
+ if ( ( $calendarmodel == 'OS' ) &&
+ ( ( $date['m'] < 3 ) || ( ( $date['m'] == 3 ) && ( $date['d'] < 25 ) ) ) ) {
+ $date['y']++;
+ }
+
+ $calmod = $this->getCalendarModel( $calendarmodel, $date['y'], $date['m'], $date['d'] );
+
+ try {
+ $this->m_dataitem = new DITime( $calmod, $date['y'], $date['m'], $date['d'], $hours, $minutes, $seconds . '.' . $microseconds, $timezone );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid', $this->m_wikivalue, $e->getMessage() ] );
+ return false;
+ }
+
+ // Having more than years or specifying a calendar model does
+ // not make sense for prehistoric dates, and our calendar
+ // conversion would not be reliable if JD numbers get too huge:
+ if ( ( $date['y'] <= self::PREHISTORY ) &&
+ ( ( $this->m_dataitem->getPrecision() > DITime::PREC_Y ) || ( $calendarmodel !== false ) ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-prehistoric', $this->m_wikivalue ] );
+ return false;
+ }
+
+ if ( $timeoffset != 0 ) {
+ $newjd = $this->m_dataitem->getJD() - $timeoffset / 24;
+ try {
+ $this->m_dataitem = DITime::newFromJD( $newjd, $calmod, $this->m_dataitem->getPrecision(), $timezone );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, $e->getMessage() ] );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check which roles a string component might play in a date, and
+ * set the call-by-ref parameter to the proper numerical
+ * representation. The component string has already been normalized to
+ * be either a plain number, a month name, or a plain number with "d"
+ * pre-pended. The result is a bit vector to indicate the possible
+ * interpretations.
+ *
+ * @param $component string
+ * @param $numvalue integer representing the components value
+ *
+ * @return integer that encodes a three-digit bit vector
+ */
+ protected static function checkDateComponent( $component, &$numvalue ) {
+
+ if ( $component === '' ) { // should not happen
+ $numvalue = 0;
+ return 0;
+ } elseif ( is_numeric( $component ) ) {
+ $numvalue = intval( $component );
+ if ( ( $numvalue >= 1 ) && ( $numvalue <= 12 ) ) {
+ return SMW_DAY_MONTH_YEAR; // can be a month, day or year
+ } elseif ( ( $numvalue >= 1 ) && ( $numvalue <= 31 ) ) {
+ return SMW_DAY_YEAR; // can be day or year
+ } else { // number can just be a year
+ return SMW_YEAR;
+ }
+ } elseif ( $component { 0 } == 'd' ) { // already marked as day
+ if ( is_numeric( substr( $component, 1 ) ) ) {
+ $numvalue = intval( substr( $component, 1 ) );
+ return ( ( $numvalue >= 1 ) && ( $numvalue <= 31 ) ) ? SMW_DAY : 0;
+ } else {
+ return 0;
+ }
+ }
+
+ $monthnum = array_search( $component, Components::$monthsShort );
+
+ if ( $monthnum !== false ) {
+ $numvalue = $monthnum + 1;
+ return SMW_MONTH;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Determine the calendar model under which an input should be
+ * interpreted based on the given input data.
+ *
+ * @param $presetmodel mixed string related to a user input calendar model (OS, Jl, Gr) or false
+ * @param $year integer of the given year (adjusted for BC(E), i.e. possibly negative)
+ * @param $month mixed integer of the month or false
+ * @param $day mixed integer of the day or false
+ *
+ * @return integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ */
+ protected function getCalendarModel( $presetmodel, $year, $month, $day ) {
+
+ // Old Style is a notational convention of Julian dates only
+ if ( $presetmodel == 'OS' ) {
+ $presetmodel = 'Jl';
+ }
+
+ if ( $presetmodel === 'Gr' || $presetmodel === 'GR' ) {
+ return DITime::CM_GREGORIAN;
+ } elseif ( $presetmodel === 'Jl' || $presetmodel === 'JL' ) {
+ return DITime::CM_JULIAN;
+ }
+
+ if ( ( $year > 1582 ) ||
+ ( ( $year == 1582 ) && ( $month > 10 ) ) ||
+ ( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
+ return DITime::CM_GREGORIAN;
+ } elseif ( $year > self::PREHISTORY ) {
+ return DITime::CM_JULIAN;
+ }
+
+ // Proleptic Julian years at some point deviate from the count of complete
+ // revolutions of the earth around the sun hence assume that earlier
+ // date years are Gregorian (where this effect is very weak). This is
+ // mostly for internal use since we will not allow users to specify
+ // calendar models at this scale
+ return DITime::CM_GREGORIAN;
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem
+ *
+ * {@inheritDoc}
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_TIME ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = false;
+ $this->m_wikivalue = false;
+
+ return true;
+ }
+
+ /**
+ * @see SMWDataValue::getShortWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getShortHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getLongWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @todo The preferred caption may not be suitable as a wiki value (i.e. not parsable).
+ * @see SMWDataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getWikiValue() {
+ return $this->m_wikivalue ? $this->m_wikivalue : strip_tags( $this->getLongWikiText() );
+ }
+
+ /**
+ * @see SMWDataValue::isNumeric
+ *
+ * {@inheritDoc}
+ */
+ public function isNumeric() {
+ return true;
+ }
+
+ /**
+ * Return the year number in the given calendar model, or false if
+ * this number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates). As everywhere in this class,
+ * there is no year 0.
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ *
+ * @return mixed typically a number but possibly false
+ */
+ public function getYear( $calendarmodel = DITime::CM_GREGORIAN ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return $dataItem->getYear();
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the month number in the given calendar model, or false if
+ * this number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates).
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ * @param $default value to return if month is not set at our level of precision
+ *
+ * @return mixed typically a number but possibly anything given as $default
+ */
+ public function getMonth( $calendarmodel = DITime::CM_GREGORIAN, $default = 1 ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return ( $dataItem->getPrecision() >= DITime::PREC_YM ) ? $dataItem->getMonth() : $default;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the day number in the given calendar model, or false if this
+ * number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates).
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ * @param $default value to return if day is not set at our level of precision
+ *
+ * @return mixed typically a number but possibly anything given as $default
+ */
+ public function getDay( $calendarmodel = DITime::CM_GREGORIAN, $default = 1 ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return ( $dataItem->getPrecision() >= DITime::PREC_YMD ) ? $dataItem->getDay() : $default;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see TimeValueFormatter::getTimeStringFromDataItem
+ *
+ * @return
+ */
+ public function getTimeString( $default = '00:00:00' ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getTimeString( $default );
+ }
+
+ /**
+ * @deprecated This method is now called getISO8601Date(). It will vanish before SMW 1.7.
+ */
+ public function getXMLSchemaDate( $mindefault = true ) {
+ return $this->getISO8601Date( $mindefault );
+ }
+
+ /**
+ * @see TimeValueFormatter::getISO8601DateFromDataItem
+ *
+ * @param $mindefault boolean determining whether values below the
+ * precision of our input should be completed with minimal or maximal
+ * conceivable values
+ *
+ * @return string
+ */
+ public function getISO8601Date( $mindefault = true ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getISO8601Date( $mindefault );
+ }
+
+ /**
+ * @see TimeValueFormatter::getMediaWikiDateFromDataItem
+ *
+ * @return string
+ */
+ public function getMediaWikiDate() {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getMediaWikiDate();
+ }
+
+ /**
+ * Get the current data in the specified calendar model. Conversion is
+ * not done for prehistoric dates (where it might lead to precision
+ * errors and produce results that are not meaningful). In this case,
+ * null might be returned if no data in the specified format is
+ * available.
+ *
+ * @param $calendarmodel integer one of DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ *
+ * @return DITime
+ */
+ public function getDataItemForCalendarModel( $calendarmodel ) {
+ if ( $this->m_dataitem->getYear() <= self::PREHISTORY ) {
+ return ( $this->m_dataitem->getCalendarModel() == $calendarmodel ) ? $this->m_dataitem : null;
+ } elseif ( $calendarmodel == DITime::CM_GREGORIAN ) {
+ if ( is_null( $this->m_dataitem_greg ) ) {
+ $this->m_dataitem_greg = $this->m_dataitem->getForCalendarModel( DITime::CM_GREGORIAN );
+ }
+ return $this->m_dataitem_greg;
+ } else {
+ if ( is_null( $this->m_dataitem_jul ) ) {
+ $this->m_dataitem_jul = $this->m_dataitem->getForCalendarModel( DITime::CM_JULIAN );
+ }
+ return $this->m_dataitem_jul;
+ }
+ }
+
+ private function isYear( $value ) {
+ return strpos( $value, ' ' ) === false && is_numeric( strval( $value ) ) && ( strval( $value ) < 0 || strlen( $value ) < 6 );
+ }
+
+ private function isTimestamp( $value ) {
+ // 1200-11-02T12:03:25 or 20120320055913
+ // avoid things like 2458119.500000 (JD)
+ return ( ( strlen( $value ) > 4 && substr( $value, 10, 1 ) === 'T' ) || ( strlen( $value ) == 14 && strpos( $value, '.' ) === false ) ) && wfTimestamp( TS_MW, $value ) !== false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php
new file mode 100644
index 00000000..94724268
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php
@@ -0,0 +1,379 @@
+<?php
+
+use SMW\Encoder;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+define( 'SMW_URI_MODE_EMAIL', 1 );
+define( 'SMW_URI_MODE_URI', 3 );
+define( 'SMW_URI_MODE_ANNOURI', 4 );
+define( 'SMW_URI_MODE_TEL', 5 );
+
+/**
+ * This datavalue implements URL/URI/ANNURI/PHONE/EMAIL datavalues suitable for
+ * defining the respective types of properties.
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ * @bug Correctly create safe HTML and Wiki text.
+ */
+class SMWURIValue extends SMWDataValue {
+
+ /**
+ * Raw value without encoding
+ */
+ const VALUE_RAW = 'uri.value.raw';
+
+ /**
+ * The value as returned by getWikitext() and getLongText().
+ * @var string
+ */
+ protected $m_wikitext;
+ /**
+ * One of the basic modes of operation for this class (emails, URL,
+ * telephone number URI, ...).
+ * @var integer
+ */
+ private $m_mode;
+
+ /**
+ * @var boolean
+ */
+ private $showUrlContextInRawFormat = true;
+
+ /**
+ * @var array
+ */
+ private $schemeList = [];
+
+ public function __construct( $typeid ) {
+ parent::__construct( $typeid );
+ switch ( $typeid ) {
+ case '_ema':
+ $this->m_mode = SMW_URI_MODE_EMAIL;
+ break;
+ case '_anu':
+ $this->m_mode = SMW_URI_MODE_ANNOURI;
+ break;
+ case '_tel':
+ $this->m_mode = SMW_URI_MODE_TEL;
+ break;
+ case '__spu':
+ case '_uri':
+ case '_url':
+ default:
+ $this->m_mode = SMW_URI_MODE_URI;
+ break;
+ }
+
+ $this->schemeList = array_flip( $GLOBALS['smwgURITypeSchemeList'] );
+ }
+
+ protected function parseUserValue( $value ) {
+ $value = trim( $value );
+ $this->m_wikitext = $value;
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $this->m_wikitext;
+ }
+
+ $scheme = $hierpart = $query = $fragment = '';
+ if ( $value === '' ) { // do not accept empty strings
+ $this->addErrorMsg( [ 'smw_emptystring' ] );
+ return;
+ }
+
+ switch ( $this->m_mode ) {
+ case SMW_URI_MODE_URI:
+ case SMW_URI_MODE_ANNOURI:
+
+ // Whether the url value was externally encoded or not
+ if ( strpos( $value, "%" ) === false ) {
+ $this->showUrlContextInRawFormat = false;
+ }
+
+ // If somehow the slash was encoded bring into one format
+ $value = str_replace( "%2F", "/", $value );
+
+ $parts = explode( ':', $value, 2 ); // try to split "schema:rest"
+ if ( count( $parts ) == 1 ) { // possibly add "http" as default
+ $value = 'http://' . $value;
+ $parts[1] = $parts[0];
+ $parts[0] = 'http';
+ }
+ // check against blacklist
+ $uri_blacklist = explode( "\n", Message::get( 'smw_uri_blacklist', Message::TEXT, Message::CONTENT_LANGUAGE ) );
+ foreach ( $uri_blacklist as $uri ) {
+ $uri = trim( $uri );
+ if ( $uri !== '' && $uri == mb_substr( $value, 0, mb_strlen( $uri ) ) ) { // disallowed URI!
+ $this->addErrorMsg( [ 'smw_baduri', $value ] );
+ return;
+ }
+ }
+ // decompose general URI components
+ $scheme = $parts[0];
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && !isset( $this->schemeList[$scheme] ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-uri-invalid-scheme', $scheme ] );
+ return;
+ }
+
+ $parts = explode( '?', $parts[1], 2 ); // try to split "hier-part?queryfrag"
+ if ( count( $parts ) == 2 ) {
+ $hierpart = $parts[0];
+ $parts = explode( '#', $parts[1], 2 ); // try to split "query#frag"
+ $query = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ } else {
+ $query = '';
+ $parts = explode( '#', $parts[0], 2 ); // try to split "hier-part#frag"
+ $hierpart = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ }
+ // We do not validate the URI characters (the data item will do this) but we do some escaping:
+ // encode most characters, but leave special symbols as given by user:
+ $hierpart = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $hierpart ) );
+ $query = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $query ) );
+ $fragment = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $fragment ) );
+ /// NOTE: we do not support raw [ (%5D) and ] (%5E), although they are needed for ldap:// (but rarely in a wiki)
+ /// NOTE: "+" gets encoded, as it is interpreted as space by most browsers when part of a URL;
+ /// this prevents tel: from working directly, but we have a datatype for this anyway.
+
+ if ( substr( $hierpart, 0, 2 ) === '//' ) {
+ $hierpart = substr( $hierpart, 2 );
+ }
+
+ // #3540
+ if ( $hierpart !== '' && $hierpart[0] === '/' ) {
+ return $this->addErrorMsg( [ 'smw-datavalue-uri-invalid-authority-path-component', $value, $hierpart ] );
+ }
+
+ break;
+ case SMW_URI_MODE_TEL:
+ $scheme = 'tel';
+
+ if ( substr( $value, 0, 4 ) === 'tel:' ) { // accept optional "tel"
+ $value = substr( $value, 4 );
+ $this->m_wikitext = $value;
+ }
+
+ $hierpart = preg_replace( '/(?<=[0-9]) (?=[0-9])/', '\1-\2', $value );
+ $hierpart = str_replace( ' ', '', $hierpart );
+ if ( substr( $hierpart, 0, 2 ) == '00' ) {
+ $hierpart = '+' . substr( $hierpart, 2 );
+ }
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && ( ( strlen( preg_replace( '/[^0-9]/', '', $hierpart ) ) < 6 ) ||
+ ( preg_match( '<[-+./][-./]>', $hierpart ) ) ||
+ ( !self::isValidTelURI( 'tel:' . $hierpart ) ) ) ) { /// TODO: introduce error-message for "bad" phone number
+ $this->addErrorMsg( [ 'smw_baduri', $this->m_wikitext ] );
+ return;
+ }
+ break;
+ case SMW_URI_MODE_EMAIL:
+ $scheme = 'mailto';
+ if ( strpos( $value, 'mailto:' ) === 0 ) { // accept optional "mailto"
+ $value = substr( $value, 7 );
+ $this->m_wikitext = $value;
+ }
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && !Sanitizer::validateEmail( $value ) ) {
+ /// TODO: introduce error-message for "bad" email
+ $this->addErrorMsg( [ 'smw_baduri', $value ] );
+ return;
+ }
+ $hierpart = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $value ) );
+ }
+
+ // Now create the URI data item:
+ try {
+ $this->m_dataitem = new SMWDIUri( $scheme, $hierpart, $query, $fragment, !$this->getOption( self::OPT_QUERY_CONTEXT ) );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw_baduri', $this->m_wikitext ] );
+ }
+ }
+
+ /**
+ * Returns true if the argument is a valid RFC 3966 phone number.
+ * Only global phone numbers are supported, and no full validation
+ * of parameters (appended via ;param=value) is performed.
+ */
+ protected static function isValidTelURI( $s ) {
+ $tel_uri_regex = '<^tel:\+[0-9./-]*[0-9][0-9./-]*(;[0-9a-zA-Z-]+=(%[0-9a-zA-Z][0-9a-zA-Z]|[0-9a-zA-Z._~:/?#[\]@!$&\'()*+,;=-])*)*$>';
+ return (bool) preg_match( $tel_uri_regex, $s );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_URI ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ if ( $this->m_mode == SMW_URI_MODE_EMAIL ) {
+ $this->m_wikitext = substr( $dataItem->getURI(), 7 );
+ } elseif ( $this->m_mode == SMW_URI_MODE_TEL ) {
+ $this->m_wikitext = substr( $dataItem->getURI(), 4 );
+ } else {
+ $this->m_wikitext = $dataItem->getURI();
+ }
+
+ $this->m_caption = $this->m_wikitext;
+ $this->showUrlContextInRawFormat = false;
+
+ return true;
+ }
+
+ public function getShortWikiText( $linked = null ) {
+
+ list( $url, $caption ) = $this->decodeUriContext( $this->m_caption, $linked );
+
+ if ( is_null( $linked ) || ( $linked === false ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_caption === '' ) ) {
+ return $caption;
+ } elseif ( $this->m_outformat == 'nowiki' ) {
+ return $this->makeNonlinkedWikiText( $caption );
+ } else {
+ return '[' . $url . ' ' . $caption . ']';
+ }
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+
+ list( $url, $caption ) = $this->decodeUriContext( $this->m_caption, $linker );
+
+ if ( is_null( $linker ) || ( !$this->isValid() ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_outformat == 'nowiki' ) ||
+ ( $this->m_caption === '' ) || $linker === false ) {
+ return $caption;
+ } else {
+ return $linker->makeExternalLink( $url, $caption );
+ }
+ }
+
+ public function getLongWikiText( $linked = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ list( $url, $wikitext ) = $this->decodeUriContext( $this->m_wikitext, $linked );
+
+ if ( is_null( $linked ) || ( $linked === false ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || $linked === false ) {
+ return $wikitext;
+ } elseif ( $this->m_outformat == 'nowiki' ) {
+ return $this->makeNonlinkedWikiText( $wikitext );
+ } else {
+ return '[' . $url . ' ' . $wikitext . ']';
+ }
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ list( $url, $wikitext ) = $this->decodeUriContext( $this->m_wikitext, $linker );
+
+ if ( is_null( $linker ) || ( !$this->isValid() ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_outformat == 'nowiki' ) || $linker === false ) {
+ return $wikitext;
+ } else {
+ return $linker->makeExternalLink( $url, $wikitext );
+ }
+ }
+
+ public function getWikiValue() {
+
+ if ( $this->getOption( self::VALUE_RAW ) ) {
+ return rawurldecode( $this->m_wikitext );
+ }
+
+ return $this->m_wikitext;
+ }
+
+ public function getURI() {
+ return $this->getUriDataitem()->getURI();
+ }
+
+ protected function getServiceLinkParams() {
+ // Create links to mapping services based on a wiki-editable message. The parameters
+ // available to the message are:
+ // $1: urlencoded version of URI/URL value (includes mailto: for emails)
+ return [ rawurlencode( $this->getUriDataitem()->getURI() ) ];
+ }
+
+ /**
+ * Get a URL for hyperlinking this URI, or the empty string if this URI
+ * is not hyperlinked in MediaWiki.
+ * @return string
+ */
+ public function getURL() {
+ global $wgUrlProtocols;
+
+ foreach ( $wgUrlProtocols as $prot ) {
+ if ( ( $prot == $this->getUriDataitem()->getScheme() . ':' ) || ( $prot == $this->getUriDataitem()->getScheme() . '://' ) ) {
+ return $this->getUriDataitem()->getURI();
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Helper function to get the current dataitem, or some dummy URI
+ * dataitem if the dataitem was not set. This makes it easier to
+ * write code that avoids errors even if the data was not
+ * initialized properly.
+ * @return SMWDIUri
+ */
+ protected function getUriDataitem() {
+ if ( isset( $this->m_dataitem ) ) {
+ return $this->m_dataitem;
+ } else { // note: use "noprotocol" to avoid accidental use in an MW link, see getURL()
+ return new SMWDIUri( 'noprotocol', 'x', '', '', $this->m_typeid );
+ }
+ }
+
+ /**
+ * Helper function that changes a URL string in such a way that it
+ * can be used in wikitext without being turned into a hyperlink,
+ * while still displaying the same characters. The use of
+ * &lt;nowiki&gt; is avoided, since the resulting strings may be
+ * inserted during parsing, after this has been stripped.
+ *
+ * @since 1.8
+ */
+ protected function makeNonlinkedWikiText( $url ) {
+ return str_replace( ':', '&#58;', $url );
+ }
+
+ private function decodeUriContext( $context, $linker ) {
+
+ // Prior to decoding turn any `-` into an internal representation to avoid
+ // potential breakage
+ if ( !$this->showUrlContextInRawFormat ) {
+ $context = Encoder::decode( str_replace( '-', '-2D', $context ) );
+ }
+
+ if ( $this->m_mode !== SMW_URI_MODE_EMAIL && $linker !== null ) {
+ $context = str_replace( '_', ' ', $context );
+ }
+
+ // Allow the display without `_` so that URIs can be split
+ // during the outout by the browser without breaking the URL itself
+ // as it contains the `_` for spaces
+ return [ $this->getURL(), $context ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php
new file mode 100644
index 00000000..c1c1579e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php
@@ -0,0 +1,751 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\Utils\Image;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements special processing suitable for defining
+ * wikipages as values of properties.
+ *
+ * The class can support general wiki pages, or pages of a fixed
+ * namespace, Whether a namespace is fixed is decided based on the
+ * type ID when the object is constructed.
+ *
+ * The short display simulates the behavior of the MediaWiki "pipe trick"
+ * but always includes fragments. This can be overwritten by setting a
+ * caption, which is also done by default when generating a value from user
+ * input. The long display always includes all relevant information. Only if a
+ * fixed namespace is used for the datatype, the namespace prefix is omitted.
+ * This behavior has changed in SMW 1.7: up to this time, short displays have
+ * always included the namespace and long displays used the pipe trick, leading
+ * to a paradoxical confusion of "long" and "short".
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWWikiPageValue extends SMWDataValue {
+
+ /**
+ * Whether text transformation should be suppressed or not.
+ */
+ const NO_TEXT_TRANSFORMATION = 'no.text.transformation';
+
+ /**
+ * Whether to use the short form or not.
+ */
+ const SHORT_FORM = 'short.form';
+
+ /**
+ * Fragment text for user-specified title. Not stored, but kept for
+ * printout on page.
+ * @var string
+ */
+ protected $m_fragment = '';
+
+ /**
+ * Full titletext with prefixes, including interwiki prefix.
+ * Set to empty string if not computed yet.
+ * @var string
+ */
+ protected $m_prefixedtext = '';
+
+ /**
+ * Cache for the related MW page ID.
+ * Set to -1 if not computed yet.
+ * @var integer
+ */
+ protected $m_id = -1;
+
+ /**
+ * Cache for the related MW title object.
+ * Set to null if not computed yet.
+ * @var Title
+ */
+ protected $m_title = null;
+
+ /**
+ * If this has a value other than NS_MAIN, the datavalue will only
+ * accept pages in this namespace. This field is initialized when
+ * creating the object (based on the type id or base on the preference
+ * of some subclass); it is not usually changed afterwards.
+ * @var integer
+ */
+ protected $m_fixNamespace = NS_MAIN;
+
+ /**
+ * @var array
+ */
+ protected $linkAttributes = [];
+
+ /**
+ * @var array
+ */
+ protected $queryParameters = [];
+
+ public function __construct( $typeid ) {
+ parent::__construct( $typeid );
+ switch ( $typeid ) {
+ case '_wpp' : case '__sup':
+ $this->m_fixNamespace = SMW_NS_PROPERTY;
+ break;
+ case '_wpc' : case '__suc': case '__sin':
+ $this->m_fixNamespace = NS_CATEGORY;
+ break;
+ case '_wpf' : case '__spf':
+ $this->m_fixNamespace = SF_NS_FORM;
+ break;
+ case '_wps' :
+ $this->m_fixNamespace = SMW_NS_SCHEMA;
+ break;
+ default: // case '_wpg':
+ $this->m_fixNamespace = NS_MAIN;
+ }
+ }
+
+ protected function parseUserValue( $value ) {
+ global $wgContLang;
+
+ // support inputs like " [[Test]] ";
+ // note that this only works when SMW_PARSER_LINV is set
+ $value = ltrim( rtrim( $value, ' ]' ), ' [' );
+
+ // #1066, Manipulate the output only for when the value has no caption
+ // assigned and only if a single :Foo is being present, ::Foo is not permitted
+ if ( $this->m_caption === false && isset( $value[2] ) && $value[0] === ':' && $value[1] !== ':' ) {
+ $value = substr( $value, 1 );
+ }
+
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ if ( $value === '' && !$this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-empty' ], Message::ESCAPED );
+ return;
+ }
+
+ // #1701 If the DV is part of a Description and an approximate search
+ // (e.g. ~foo* / ~Foo*) then use the value as-is and avoid being
+ // transformed by the Title object
+ // If the vaue contains a valid NS then use the Title to create a correct
+ // instance to distinguish [[~Foo*]] from [[Help:~Foo*]]
+ if ( $this->getOption( self::OPT_QUERY_COMP_CONTEXT ) || $this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+
+ $title = Title::newFromText( $value, $this->m_fixNamespace );
+
+ // T:P0427 If the user value says `ab c*` then make sure to use this one
+ // instead of the transformed DBKey which would be `Ab c*`
+ if ( $title !== null && $title->getNamespace() === NS_MAIN && $this->getOption( 'isCapitalLinks' ) === false ) {
+ return $this->m_dataitem = new SMWDIWikiPage( $value, NS_MAIN );
+ // If we know that it is a wikipage in a query context and the wiki
+ // requires `isCapitalLinks` then use the standard transformation so
+ // they appear as standard links even though the user input was `abc`.
+ // T:P0902 (`[[Help:]]`)
+ } elseif ( $title !== null ) {
+ return $this->m_dataitem = SMWDIWikiPage::newFromTitle( $title );
+ } elseif ( !Localizer::getInstance()->getNamespaceIndexByName( substr( $value, 0, -1 ) ) ) {
+ return $this->m_dataitem = new SMWDIWikiPage( $value, NS_MAIN );
+ }
+ }
+
+ if ( $value[0] == '#' ) {
+ if ( is_null( $this->m_contextPage ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-missing-fragment-context', $value ] );
+ return;
+ } else {
+ $this->m_title = Title::makeTitle( $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getDBkey(), substr( $value, 1 ),
+ $this->m_contextPage->getInterwiki() );
+ }
+ } else {
+ $this->m_title = Title::newFromText( $value, $this->m_fixNamespace );
+ }
+
+ /// TODO: Escape the text so users can see punctuation problems (bug 11666).
+ if ( $this->m_title === null && $this->getProperty() !== null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-property-invalid-title', $this->getProperty()->getLabel(), $value ] );
+ } elseif ( $this->m_title === null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-invalid-title', $value ] );
+ } elseif ( ( $this->m_fixNamespace != NS_MAIN ) &&
+ ( $this->m_fixNamespace != $this->m_title->getNamespace() ) ) {
+ $this->addErrorMsg( [ 'smw_wrong_namespace', $wgContLang->getNsText( $this->m_fixNamespace ) ] );
+ } else {
+ $this->m_fragment = str_replace( ' ', '_', $this->m_title->getFragment() );
+ $this->m_prefixedtext = '';
+ $this->m_id = -1; // unset id
+ $this->m_dataitem = SMWDIWikiPage::newFromTitle( $this->m_title, $this->m_typeid );
+ }
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_CONTAINER ) {
+ // might throw an exception, we just pass it through
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_WIKIPAGE ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_id = -1;
+ $this->m_title = null;
+ $this->m_fragment = $dataItem->getSubobjectName();
+ $this->m_prefixedtext = '';
+ $this->m_caption = false; // this class can handle this
+ $this->linkAttributes = [];
+
+ if ( ( $this->m_fixNamespace != NS_MAIN ) &&
+ ( $this->m_fixNamespace != $dataItem->getNamespace() ) ) {
+ $this->addErrorMsg(
+ [
+ 'smw_wrong_namespace',
+ Localizer::getInstance()->getNamespaceTextById( $this->m_fixNamespace )
+ ]
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $linkAttributes
+ */
+ public function setLinkAttributes( array $linkAttributes ) {
+ $this->linkAttributes = $linkAttributes;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $queryParameters
+ */
+ public function setQueryParameters( array $queryParameters ) {
+ $this->queryParameters = $queryParameters;
+ }
+
+ /**
+ * Display the value on a wiki page. This is used to display the value
+ * in the place where it was annotated on a wiki page. The desired
+ * behavior is that the display in this case looks as if no property
+ * annotation had been given, i.e. an annotation [[property::page|foo]]
+ * should display like [[page|foo]] in MediaWiki. But this should lead
+ * to a link, not to a category assignment. This means that:
+ *
+ * (1) If Image: is used (instead of Media:) then let MediaWiki embed
+ * the image.
+ *
+ * (2) If Category: is used, treat it as a page and link to it (do not
+ * categorize the page)
+ *
+ * (3) Preserve everything given after "|" for display (caption, image
+ * parameters, ...)
+ *
+ * (4) Use the (default) caption for display. When the value comes from
+ * user input, this includes the full value that one would also see in
+ * MediaWiki.
+ *
+ * @param $linked mixed generate links if not null or false
+ * @return string
+ */
+ public function getShortWikiText( $linked = null ) {
+
+ if ( is_null( $linked ) || $linked === false ||
+ $this->m_outformat == '-' || !$this->isValid() ||
+ $this->m_caption === '' ) {
+ return $this->m_caption !== false ? $this->m_caption : $this->getWikiValue();
+ }
+
+ if ( Image::isImage( $this->m_dataitem ) && $this->m_dataitem->getInterwiki() === '' ) {
+ $linkEscape = '';
+ $options = $this->m_outformat === false ? 'frameless|border|text-top|' : str_replace( ';', '|', \Sanitizer::removeHTMLtags( $this->m_outformat ) );
+ $defaultCaption = '|' . $this->getShortCaptionText() . '|' . $options;
+ } else {
+ $linkEscape = ':';
+ $defaultCaption = '|' . $this->getShortCaptionText();
+ }
+
+ if ( $this->m_caption === false ) {
+ $link = '[[' . $linkEscape . $this->getWikiLinkTarget() . $defaultCaption . ']]';
+ } else {
+ $link = '[[' . $linkEscape . $this->getWikiLinkTarget() . '|' . $this->m_caption . ']]';
+ }
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ if ( $this->linkAttributes !== [] ) {
+ $link = \Html::rawElement(
+ 'span',
+ $this->linkAttributes,
+ $link
+ );
+ }
+
+ return $link;
+ }
+
+ /**
+ * Display the value as in getShortWikiText() but create HTML.
+ * The only difference is that images are not embedded.
+ *
+ * @param Linker $linker mixed the Linker object to use or null if no linking is desired
+ * @return string
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ // init the Title object, may reveal hitherto unnoticed errors:
+ if ( !is_null( $linker ) && $linker !== false &&
+ $this->m_caption !== '' && $this->m_outformat != '-' ) {
+ $this->getTitle();
+ }
+
+ if ( is_null( $linker ) || $linker === false || !$this->isValid() ||
+ $this->m_outformat == '-' || $this->m_caption === '' ) {
+
+ $caption = $this->m_caption === false ? $this->getWikiValue() : $this->m_caption;
+ return \Sanitizer::removeHTMLtags( $caption );
+ }
+
+ $caption = $this->m_caption === false ? $this->getShortCaptionText() : $this->m_caption;
+ $caption = \Sanitizer::removeHTMLtags( $caption );
+
+ if ( $this->getNamespace() == NS_MEDIA ) { // this extra case *is* needed
+ return $linker->makeMediaLinkObj( $this->getTitle(), $caption );
+ }
+
+ return $linker->link(
+ $this->getTitle(),
+ $caption,
+ $this->linkAttributes,
+ $this->queryParameters
+ );
+ }
+
+ /**
+ * Display the "long" value on a wiki page. This behaves largely like
+ * getShortWikiText() but does not use the caption. Instead, it always
+ * takes the long display form (wiki value).
+ *
+ * @param $linked mixed if true the result will be linked
+ * @return string
+ */
+ public function getLongWikiText( $linked = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ if ( is_null( $linked ) || $linked === false || $this->m_outformat == '-' ) {
+ return $this->getWikiValue();
+ } elseif ( Image::isImage( $this->m_dataitem ) && $this->m_dataitem->getInterwiki() === '' ) {
+ // Embed images and other files
+ // Note that the embedded file links to the image, hence needs no additional link text.
+ // There should not be a linebreak after an impage, just like there is no linebreak after
+ // other values (whether formatted or not).
+ return '[[' . $this->getWikiLinkTarget() . '|' .
+ $this->getLongCaptionText() . '|frameless|border|text-top]]';
+ }
+
+ $link = '[[:' . $this->getWikiLinkTarget() . '|' . $this->getLongCaptionText() . ']]';
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ if ( $this->linkAttributes !== [] ) {
+ $link = \Html::rawElement(
+ 'span',
+ $this->linkAttributes,
+ $link
+ );
+ }
+
+ return $link;
+ }
+
+ /**
+ * Display the "long" value in HTML. This behaves largely like
+ * getLongWikiText() but does not embed images.
+ *
+ * @param $linker mixed if a Linker is given, the result will be linked
+ * @return string
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ // init the Title object, may reveal hitherto unnoticed errors:
+ if ( !is_null( $linker ) && ( $this->m_outformat != '-' ) ) {
+ $this->getTitle();
+ }
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ if ( $linker === null || $linker === false || $this->m_outformat == '-' ) {
+ return \Sanitizer::removeHTMLtags( $this->getWikiValue() );
+ } elseif ( $this->getNamespace() == NS_MEDIA ) { // this extra case is really needed
+ return $linker->makeMediaLinkObj(
+ $this->getTitle(),
+ \Sanitizer::removeHTMLtags( $this->getLongCaptionText() )
+ );
+ }
+
+ // all others use default linking, no embedding of images here
+ return $linker->link(
+ $this->getTitle(),
+ \Sanitizer::removeHTMLtags( $this->getLongCaptionText() ),
+ $this->linkAttributes,
+ $this->queryParameters
+ );
+ }
+
+ /**
+ * Return a string that could be used in an in-page property assignment
+ * for setting this value. This does not include initial ":" for
+ * escaping things like Category: links since the property value does
+ * not include such escapes either. Fragment information is included.
+ * Namespaces are omitted if a fixed namespace is used, since they are
+ * not needed in this case when making a property assignment.
+ *
+ * @return string
+ */
+ public function getWikiValue() {
+
+ if ( $this->getOption( self::SHORT_FORM, false ) ) {
+ $text = $this->getText();
+ } elseif ( $this->getTypeID() === '_wpp' || $this->m_fixNamespace == NS_MAIN ) {
+ $text = $this->getPrefixedText();
+ } else {
+ $text = $this->getText();
+ }
+
+ return $text . ( $this->m_fragment !== '' ? "#{$this->m_fragment}" : '' );
+ }
+
+ public function getHash() {
+ return $this->isValid() ? $this->getPrefixedText() : implode( "\t", $this->getErrors() );
+ }
+
+ /**
+ * Create links to mapping services based on a wiki-editable message.
+ * The parameters available to the message are:
+ * $1: urlencoded article name (no namespace)
+ *
+ * @return array
+ */
+ protected function getServiceLinkParams() {
+ if ( $this->isValid() ) {
+ return [ rawurlencode( str_replace( '_', ' ', $this->m_dataitem->getDBkey() ) ) ];
+ } else {
+ return [];
+ }
+ }
+
+///// special interface for wiki page values
+
+ /**
+ * Return according Title object or null if no valid value was set.
+ * null can be returned even if this object returns true for isValid(),
+ * since the latter function does not check whether MediaWiki can really
+ * make a Title out of the given data.
+ * However, isValid() will return false *after* this function failed in
+ * trying to create a title.
+ *
+ * @return Title
+ */
+ public function getTitle() {
+
+ if ( $this->m_title !== null ) {
+ return $this->m_title;
+ }
+
+ if ( $this->isValid() ) {
+
+ if ( ( $title = $this->m_dataitem->getTitle() ) !== null ) {
+ return $this->m_title = $title;
+ }
+
+ // #3278, Special handling of `>` in the user namespace, MW (1.31+)
+ // added a prefix to users that originate from imported content
+ if (
+ $this->m_dataitem->getNamespace() === NS_USER &&
+ strpos( $this->m_dataitem->getDBkey(), '>' ) !== false ) {
+
+ $this->setOption( self::OPT_DISABLE_INFOLINKS, true );
+
+ $this->m_title = Title::newFromText(
+ $this->m_dataitem->getDBkey()
+ );
+
+ return $this->m_title;
+ }
+ }
+
+ $errArg = $this->m_caption;
+
+ if ( $this->isValid() ) {
+ $ns = Localizer::getInstance()->getNamespaceTextById(
+ $this->m_dataitem->getNamespace()
+ );
+
+ $errArg = "$ns:" . $this->m_dataitem->getDBkey();
+ }
+
+ // Should not normally happen, but anyway ...
+ $this->addErrorMsg( [ 'smw_notitle', $errArg ] );
+ }
+
+ /**
+ * Get MediaWiki's ID for this value or 0 if not available.
+ *
+ * @return integer
+ */
+ public function getArticleID() {
+ if ( $this->m_id === false ) {
+ $this->m_id = !is_null( $this->getTitle() ) ? $this->m_title->getArticleID() : 0;
+ }
+
+ return $this->m_id;
+ }
+
+ /**
+ * Get namespace constant for this value.
+ *
+ * @return integer
+ */
+ public function getNamespace() {
+ return $this->m_dataitem->getNamespace();
+ }
+
+ /**
+ * Get DBKey for this value. Subclasses that allow for values that do not
+ * correspond to wiki pages may choose a DB key that is not a legal title
+ * DB key but rather another suitable internal ID. Thus it is not suitable
+ * to use this method in places where only MediaWiki Title keys are allowed.
+ *
+ * @return string
+ */
+ public function getDBkey() {
+ return $this->m_dataitem->getDBkey();
+ }
+
+ /**
+ * Get text label for this value, just like Title::getText().
+ *
+ * @return string
+ */
+ public function getText() {
+
+ if ( $this->getOption( self::NO_TEXT_TRANSFORMATION, false ) ) {
+ return $this->m_dataitem->getDBkey();
+ }
+
+ return str_replace( '_', ' ', $this->m_dataitem->getDBkey() );
+ }
+
+ /**
+ * Get the prefixed text for this value, including a localized namespace
+ * prefix.
+ *
+ * @return string
+ */
+ public function getPrefixedText() {
+
+ if ( $this->m_prefixedtext !== '' ) {
+ return $this->m_prefixedtext;
+ }
+
+ // In case something went wrong (invalid NS etc.), hint the ID to aid the
+ // investigation
+ if ( $this->m_dataitem->getId() > 0 ) {
+ $this->m_prefixedtext = 'NO_VALID_VALUE (ID: ' . $this->m_dataitem->getId() . ')';
+ } else {
+ $this->m_prefixedtext = 'NO_VALID_VALUE';
+ }
+
+ if ( $this->isValid() ) {
+ $nstext = Localizer::getInstance()->getNamespaceTextById( $this->m_dataitem->getNamespace() );
+ $this->m_prefixedtext =
+ ( $this->m_dataitem->getInterwiki() !== '' ? $this->m_dataitem->getInterwiki() . ':' : '' ) .
+ ( $nstext !== '' ? "$nstext:" : '' ) . $this->getText();
+ }
+
+ return $this->m_prefixedtext;
+ }
+
+ /**
+ * Get interwiki prefix or empty string.
+ *
+ * @return string
+ */
+ public function getInterwiki() {
+ return $this->m_dataitem->getInterwiki();
+ }
+
+ /**
+ * DataValue::getPreferredCaption
+ *
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getPreferredCaption() {
+
+ if ( ( $preferredCaption = parent::getPreferredCaption() ) !== '' && $preferredCaption !== false ) {
+ return $preferredCaption;
+ }
+
+ $preferredCaption = $this->getDisplayTitle();
+
+ if ( $preferredCaption === '' && $this->getOption( 'prefixed.preferred.caption' ) ) {
+ $preferredCaption = $this->getPrefixedText();
+ } elseif ( $preferredCaption === '' ) {
+ $preferredCaption = $this->getText();
+ }
+
+ return $preferredCaption;
+ }
+
+ /**
+ * Get a short caption used to label this value. In particular, this
+ * omits namespace and interwiki prefixes (similar to the MediaWiki
+ * "pipe trick"). Fragments are included unless they start with an
+ * underscore (used for generated fragment names that are not helpful
+ * for users and that might change easily).
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getShortCaptionText() {
+ if ( $this->m_fragment !== '' && $this->m_fragment[0] != '_' ) {
+ $fragmentText = '#' . str_replace( '_', ' ', $this->m_fragment );
+ } else {
+ $fragmentText = '';
+ }
+
+ if ( $this->m_caption && $this->m_caption !== '' ) {
+ return $this->m_caption;
+ }
+
+ $displayTitle = $this->getDisplayTitle();
+
+ if ( $displayTitle === '' ) {
+ $displayTitle = $this->getText();
+ }
+
+ return $displayTitle . $fragmentText;
+ }
+
+ /**
+ * Get a long caption used to label this value. In particular, this
+ * includes namespace and interwiki prefixes, while fragments are only
+ * included if they do not start with an underscore (used for generated
+ * fragment names that are not helpful for users and that might change
+ * easily).
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getLongCaptionText() {
+ if ( $this->m_fragment !== '' && $this->m_fragment[0] != '_' ) {
+ $fragmentText = '#' . str_replace( '_', ' ', $this->m_fragment );
+ } else {
+ $fragmentText = '';
+ }
+
+ if ( $this->m_caption && $this->m_caption !== '' ) {
+ return $this->m_caption;
+ }
+
+ $displayTitle = $this->getDisplayTitle();
+
+ if ( $displayTitle === '' ) {
+ $displayTitle = $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText() : $this->getText();
+ }
+
+ return $displayTitle . $fragmentText;
+ }
+
+ /**
+ * Compute a text that can be used in wiki text to link to this
+ * datavalue. Processing includes some escaping and adding the
+ * fragment.
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getWikiLinkTarget() {
+ return str_replace( "'", '&#x0027;', $this->getPrefixedText() ) .
+ ( $this->m_fragment !== '' ? "#{$this->m_fragment}" : '' );
+ }
+
+ /**
+ * Find the sortkey for this object.
+ *
+ * @deprecated Use SMWStore::getWikiPageSortKey(). Will vanish before SMW 1.7
+ *
+ * @return string sortkey
+ */
+ public function getSortKey() {
+ return ApplicationFactory::getInstance()->getStore()->getWikiPageSortKey( $this->m_dataitem );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getDisplayTitle() {
+
+ if ( $this->m_dataitem === null || !$this->isEnabledFeature( SMW_DV_WPV_DTITLE ) ) {
+ return '';
+ }
+
+ return $this->findDisplayTitleFor( $this->m_dataitem );
+ }
+
+ private function findDisplayTitleFor( $subject ) {
+
+ $displayTitle = '';
+
+ $dataItems = ApplicationFactory::getInstance()->getCachedPropertyValuesPrefetcher()->getPropertyValues(
+ $subject,
+ new DIProperty( '_DTITLE' )
+ );
+
+ if ( $dataItems !== null && $dataItems !== [] ) {
+ $displayTitle = end( $dataItems )->getString();
+ } elseif ( $subject->getSubobjectName() !== '' ) {
+ // Check whether the base subject has a DISPLAYTITLE
+ return $this->findDisplayTitleFor( $subject->asBase() );
+ }
+
+ return $displayTitle;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php
new file mode 100644
index 00000000..a4becb46
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php
@@ -0,0 +1,897 @@
+<?php
+
+/**
+ * This group contains all parts of SMW that relate to the processing of datavalues
+ * of various types.
+ *
+ * @defgroup SMWDataValues SMWDataValues
+ * @ingroup SMW
+ */
+
+use SMW\DataValues\InfoLinksProvider;
+use SMW\Deserializers\DVDescriptionDeserializerRegistry;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\Options;
+use SMW\Query\QueryComparator;
+use SMW\Services\DataValueServiceFactory;
+use SMW\Utils\CharArmor;
+
+/**
+ * Objects of this type represent all that is known about a certain user-provided
+ * data value, especially its various representations as strings, tooltips,
+ * numbers, etc. Objects can be created as "empty" containers of a certain type,
+ * but are then usually filled with data to present one particular data value.
+ *
+ * Data values have two chief representation forms: the user-facing syntax and the
+ * internal representation. In user syntax, every value is (necessarily) a single
+ * string, however complex the value is. For example, a string such as "Help:editing"
+ * may represent a wiki page called "Editing" in the namespace for "Help". The
+ * internal representation may be any numerical array of strings and numbers. In the
+ * example, it might be array("Editing",12), where 12 is the number used for identifying
+ * the namespace "Help:". Of course, the internal representation could also use a single
+ * string value, such as in array("Help:Editing"), but this might be less useful for
+ * certain operations (e.g. filterng by namespace). Moreover, all values that are
+ * restored from the database are given in the internal format, so it wise to choose a
+ * format that allows for very fast and easy processing without unnecessary parsing.
+ *
+ * The main functions of data value objects are:
+ * - setUserValue() which triggers parseUserValue() to process a user-level string.
+ *
+ * In addition, there are a number of get-functions that provide useful output versions
+ * for displaying and serializing the value.
+ *
+ * @ingroup SMWDataValues
+ *
+ * @author Markus Krötzsch
+ */
+abstract class SMWDataValue {
+
+ /**
+ * Contains the user language a user operates in.
+ */
+ const OPT_USER_LANGUAGE = 'user.language';
+
+ /**
+ * Contains either the global "site" content language or a specified page
+ * content language invoked by the context page.
+ */
+ const OPT_CONTENT_LANGUAGE = 'content.language';
+
+ /**
+ * Describes a state where a DataValue is part of a query condition and may
+ * (or not) require a different treatment.
+ */
+ const OPT_QUERY_CONTEXT = 'query.context';
+
+ /**
+ * Describes a state where a DataValue is part of a query condition and
+ * contains a comparator.
+ */
+ const OPT_QUERY_COMP_CONTEXT = 'query.comparator.context';
+
+ /**
+ * Option to disable related infolinks
+ */
+ const OPT_DISABLE_INFOLINKS = 'disable.infolinks';
+
+ /**
+ * Option to disable service links
+ */
+ const OPT_DISABLE_SERVICELINKS = 'disable.servicelinks';
+
+ /**
+ * Option to use compact infolinks
+ */
+ const OPT_COMPACT_INFOLINKS = 'compact.infolinks';
+
+ /**
+ * Associated data item. This is the reference to the immutable object
+ * that represents the current data content. All other data stored here
+ * is only about presentation and parsing, but is not relevant to the
+ * actual data that is represented (and stored later on).
+ *
+ * This variable must always be set to some data item, even if there
+ * have been errors in initialising the data.
+ * @var SMWDataItem
+ */
+ protected $m_dataitem;
+
+ /**
+ * The property for which this value is constructed or null if none
+ * given. Property pages are used to make settings that affect parsing
+ * and display, hence it is sometimes needed to know them.
+ *
+ * @var DIProperty
+ */
+ protected $m_property = null;
+
+ /**
+ * Wiki page in the context of which the value is to be interpreted, or
+ * null if not given (or not on a page). This information is used to
+ * parse user values such as "#subsection" which only make sense when
+ * used on a certain page.
+ *
+ * @var SMWDIWikiPage
+ */
+ protected $m_contextPage = null;
+
+ /**
+ * The text label to be used for output or false if none given.
+ * @var string
+ */
+ protected $m_caption;
+
+ /**
+ * The type id for this value object.
+ * @var string
+ */
+ protected $m_typeid;
+
+ /**
+ * Output formatting string, false when not set.
+ * @see setOutputFormat()
+ * @var mixed
+ */
+ protected $m_outformat = false;
+
+ /**
+ * Array of error text messages. Private to allow us to track error insertion
+ * (PHP's count() is too slow when called often) by using $mHasErrors.
+ * @var array
+ */
+ private $mErrors = [];
+
+ /**
+ * Boolean indicating if there where any errors.
+ * Should be modified accordingly when modifying $mErrors.
+ * @var boolean
+ */
+ private $mHasErrors = false;
+
+ /**
+ * @var false|array
+ */
+ protected $restrictionError = false;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var InfoLinksProvider
+ */
+ private $infoLinksProvider = null;
+
+ /**
+ * @var string
+ */
+ private $userValue = '';
+
+ /**
+ * @var DataValueServiceFactory
+ */
+ protected $dataValueServiceFactory;
+
+ /**
+ * Constructor.
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid ) {
+ $this->m_typeid = $typeid;
+ }
+
+ /**
+ * Return a short string that unambiguously specify the type of this
+ * value. This value will globally be used to identify the type of a
+ * value (in spite of the class it actually belongs to, which can still
+ * implement various types).
+ */
+ public function getTypeID() {
+ return $this->m_typeid;
+ }
+
+ /**
+ * Set the user value (and compute other representations if possible).
+ * The given value is a string as supplied by some user. An alternative
+ * label for printout might also be specified.
+ *
+ * @param string $value
+ * @param mixed $caption
+ */
+ public function setUserValue( $value, $caption = false ) {
+
+ $this->m_dataitem = null;
+ $this->mErrors = []; // clear errors
+ $this->mHasErrors = false;
+ $this->m_caption = is_string( $caption ) ? trim( $caption ) : false;
+ $this->userValue = $value;
+
+ // #2435
+ $value = CharArmor::removeControlChars(
+ CharArmor::removeSpecialChars( $value )
+ );
+
+ // Process may set a caption if not set yet, depending on datavalue
+ $this->parseUserValue( $value );
+
+ // The following checks for Strip markers generated by MediaWiki to handle special content,
+ // from parser and extension tags e.g. <pre>,<nowiki>,<math>,<source>.
+ // See https://en.wikipedia.org/wiki/Help:Strip_markers
+ // In general, we are not prepared to handle such content properly, and we
+ // also have no means of obtaining the user input at this point. Hence the assignment
+ // just fails, even if parseUserValue() above might not have noticed this issue.
+ // Note: \x07 was used in MediaWiki 1.11.0, \x7f is used now (backwards compatibility, b/c)
+ if ( is_string( $value ) && ( ( strpos( $value, "\x7f" ) !== false ) || ( strpos( $value, "\x07" ) !== false ) ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-stripmarker-parse-error', $value ] );
+ }
+
+ if ( $this->isValid() && !$this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+ $this->checkAllowedValues();
+ }
+ }
+
+ /**
+ * Set the actual data contained in this object. The method returns
+ * true if this was successful (requiring the type of the dataitem
+ * to match the data value). If false is returned, the data value is
+ * left unchanged (the data item was rejected).
+ *
+ * @note Even if this function returns true, the data value object
+ * might become invalid if the content of the data item caused errors
+ * in spite of it being of the right basic type. False is only returned
+ * if the data item is fundamentally incompatible with the data value.
+ *
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ public function setDataItem( SMWDataItem $dataItem ) {
+ $this->m_dataitem = null;
+ $this->mErrors = [];
+ $this->mHasErrors = $this->m_caption = false;
+ return $this->loadDataItem( $dataItem );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValueServiceFactory $dataValueServiceFactory
+ */
+ public function setDataValueServiceFactory( DataValueServiceFactory $dataValueServiceFactory ) {
+ $this->dataValueServiceFactory = $dataValueServiceFactory;
+ }
+
+ /**
+ * Specify the property to which this value refers. Property pages are
+ * used to make settings that affect parsing and display, hence it is
+ * sometimes needed to know them.
+ *
+ * @since 1.6
+ *
+ * @param DIProperty $property
+ */
+ public function setProperty( DIProperty $property ) {
+ $this->m_property = $property;
+ }
+
+ /**
+ * Returns the property to which this value refers.
+ *
+ * @since 1.8
+ *
+ * @return DIProperty|null
+ */
+ public function getProperty() {
+ return $this->m_property;
+ }
+
+ /**
+ * Specify the wiki page to which this value refers. This information is
+ * used to parse user values such as "#subsection" which only make sense
+ * when used on a certain page.
+ *
+ * @since 1.7
+ *
+ * @param SMWDIWikiPage|null $contextPage
+ */
+ public function setContextPage( SMWDIWikiPage $contextPage = null ) {
+ $this->m_contextPage = $contextPage;
+
+ $this->setOption(
+ self::OPT_CONTENT_LANGUAGE,
+ Localizer::getInstance()->getPreferredContentLanguage( $contextPage )->getCode()
+ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DIWikiPage|null
+ */
+ public function getContextPage() {
+ return $this->m_contextPage;
+ }
+
+ /**
+ * Change the caption (the text used for displaying this datavalue). The given
+ * value must be a string.
+ *
+ * @param string $caption
+ */
+ public function setCaption( $caption ) {
+ $this->m_caption = $caption;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $caption
+ */
+ public function getCaption() {
+ return $this->m_caption;
+ }
+
+ /**
+ * Returns a preferred caption and may deviate from the standard caption as
+ * a subclass is permitted to override this method and provide a more
+ * contextualized display representation (language or value context etc.).
+ *
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getPreferredCaption() {
+ return $this->m_caption;
+ }
+
+ /**
+ * Define a particular output format. Output formats are user-supplied strings
+ * that the datavalue may (or may not) use to customise its return value. For
+ * example, quantities with units of measurement may interpret the string as
+ * a desired output unit. In other cases, the output format might be built-in
+ * and subject to internationalisation (which the datavalue has to implement).
+ * In any case, an empty string resets the output format to the default.
+ *
+ * There is one predefined output format that all datavalues should respect: the
+ * format '-' indicates "plain" output that is most useful for further processing
+ * the value in a template. It should not use any wiki markup or beautification,
+ * and it should also avoid localization to the current language. When users
+ * explicitly specify an empty format string in a query, it is normalized to "-"
+ * to avoid confusion. Note that empty format strings are not interpreted in
+ * this way when directly passed to this function.
+ *
+ * @param string $formatString
+ */
+ public function setOutputFormat( $formatString ) {
+ $this->m_outformat = $formatString; // just store it, subclasses may or may not use this
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getOutputFormat() {
+ return $this->m_outformat;
+ }
+
+ /**
+ * Add a new error string or array of such strings to the error list.
+ *
+ * @note Errors should not be escaped here in any way, in contradiction to what
+ * the docs used to say here in 1.5 and before. Escaping should happen at the output.
+ *
+ * @param mixed $error A single string, or array of strings.
+ */
+ public function addError( $error ) {
+ if ( is_array( $error ) ) {
+ $this->mErrors = array_merge( $this->mErrors, $error );
+ $this->mHasErrors = $this->mHasErrors || ( count( $error ) > 0 );
+ } else {
+ $this->mErrors[] = $error;
+ $this->mHasErrors = true;
+ }
+ }
+
+ /**
+ * Messages are not resolved until the output and instead will be kept with the
+ * message and argument keys (e.g. `[2,"smw_baduri","~*0123*"]`). This allows to
+ * switch the a representation without requiring language context by the object
+ * that reports an error.
+ *
+ * @since 2.4
+ *
+ * @param $parameters
+ * @param integer|null $type
+ * @param integer|null $language
+ */
+ public function addErrorMsg( $parameters, $type = null ) {
+ $this->mErrors[Message::getHash( $parameters, $type )] = Message::encode( $parameters, $type );
+ $this->mHasErrors = true;
+ }
+
+ /**
+ * Return a string that displays all error messages as a tooltip, or
+ * an empty string if no errors happened.
+ *
+ * @return string
+ */
+ public function getErrorText() {
+ return smwfEncodeMessages( $this->mErrors );
+ }
+
+ /**
+ * Return an array of error messages, or an empty array
+ * if no errors occurred.
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->mErrors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array|false
+ */
+ public function getRestrictionError() {
+ return $this->restrictionError;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function clearErrors() {
+ $this->mErrors = [];
+ $this->mHasErrors = false;
+ }
+
+///// Query support /////
+
+ /**
+ * FIXME 3.0, allow NULL as value
+ *
+ * @see DataValueDescriptionDeserializer::deserialize
+ *
+ * @note Descriptions of values need to know their property to be able to
+ * create a parsable wikitext version of a query condition again. Thus it
+ * might be necessary to call setProperty() before using this method.
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function getQueryDescription( $value ) {
+
+ $descriptionDeserializer = DVDescriptionDeserializerRegistry::getInstance()->getDescriptionDeserializerBy( $this );
+ $description = $descriptionDeserializer->deserialize( $value );
+
+ foreach ( $descriptionDeserializer->getErrors() as $error ) {
+ $this->addError( $error );
+ }
+
+ return $description;
+ }
+
+ /**
+ * @deprecated since 2.3
+ *
+ * @see DescriptionDeserializer::prepareValue
+ *
+ * This method should no longer be used for direct public access, instead a
+ * DataValue is expected to register a DescriptionDeserializer with
+ * DVDescriptionDeserializerRegistry.
+ */
+ static public function prepareValue( &$value, &$comparator ) {
+ $comparator = QueryComparator::getInstance()->extractComparatorFromString( $value );
+ }
+
+///// Get methods /////
+
+ /**
+ * Get the actual data contained in this object or null if the data is
+ * not defined (due to errors or due to not being set at all).
+ * @note Most implementations ensure that a data item is always set,
+ * even if errors occurred, to avoid additional checks for not
+ * accessing null. Hence, one must not assume that a non-null return
+ * value here implies that isValid() returns true.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataItem|SMWDIError
+ */
+ public function getDataItem() {
+
+ if ( $this->isValid() ) {
+ return $this->m_dataitem;
+ }
+
+ return new SMWDIError( $this->mErrors, $this->userValue );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getDataItem()->getSerialization();
+ }
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in wiki text.
+ *
+ * The parameter $linked controls linking of values such as titles and should
+ * be non-NULL and non-false if this is desired.
+ */
+ abstract public function getShortWikiText( $linked = null );
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in HTML text.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (or NULL for no linking).
+ */
+ abstract public function getShortHTMLText( $linker = null );
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message
+ * The result always is a wiki-source string.
+ *
+ * The parameter $linked controls linking of values such as titles and should
+ * be non-NULL and non-false if this is desired.
+ */
+ abstract public function getLongWikiText( $linked = null );
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message
+ * The result always is an HTML string.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (or NULL for no linking).
+ */
+ abstract public function getLongHTMLText( $linker = null );
+
+ /**
+ * Return the plain wiki version of the value, or
+ * FALSE if no such version is available. The returned
+ * string suffices to reobtain the same DataValue
+ * when passing it as an input string to setUserValue().
+ */
+ abstract public function getWikiValue();
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in the specified format.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output), or NULL for no linking.
+ */
+ public function getShortText( $outputformat, $linker = null ) {
+ switch ( $outputformat ) {
+ case SMW_OUTPUT_WIKI:
+ return $this->getShortWikiText( $linker );
+ case SMW_OUTPUT_HTML:
+ case SMW_OUTPUT_FILE:
+ default:
+ return $this->getShortHTMLText( $linker );
+ }
+ }
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message.
+ * The output is in the specified format.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output), or NULL for no linking.
+ */
+ public function getLongText( $outputformat, $linker = null ) {
+ switch ( $outputformat ) {
+ case SMW_OUTPUT_WIKI:
+ return $this->getLongWikiText( $linker );
+ case SMW_OUTPUT_HTML:
+ case SMW_OUTPUT_FILE:
+ default:
+ return $this->getLongHTMLText( $linker );
+ }
+ }
+
+ /**
+ * Return text serialisation of info links. Ensures more uniform layout
+ * throughout wiki (Factbox, Property pages, ...).
+ *
+ * @param integer $outputformat Element of the SMW_OUTPUT_ enum
+ * @param $linker
+ *
+ * @return string
+ */
+ public function getInfolinkText( $outputformat, $linker = null ) {
+
+ if ( $this->getOption( self::OPT_DISABLE_INFOLINKS ) === true ) {
+ return '';
+ }
+
+ if ( $this->infoLinksProvider === null ) {
+ $this->infoLinksProvider = $this->dataValueServiceFactory->newInfoLinksProvider( $this );
+ }
+
+ if ( $this->getOption( self::OPT_DISABLE_SERVICELINKS ) === true ) {
+ $this->infoLinksProvider->disableServiceLinks();
+ }
+
+ $this->infoLinksProvider->setCompactLink(
+ $this->getOption( self::OPT_COMPACT_INFOLINKS, false )
+ );
+
+ return $this->infoLinksProvider->getInfolinkText( $outputformat, $linker );
+ }
+
+ /**
+ * Return an array of SMWLink objects that provide additional resources
+ * for the given value. Captions can contain some HTML markup which is
+ * admissible for wiki text, but no more. Result might have no entries
+ * but is always an array.
+ */
+ public function getInfolinks() {
+
+ if ( $this->infoLinksProvider === null ) {
+ $this->infoLinksProvider = $this->dataValueServiceFactory->newInfoLinksProvider( $this );
+ }
+
+ $this->infoLinksProvider->setServiceLinkParameters(
+ $this->getServiceLinkParams()
+ );
+
+ return $this->infoLinksProvider->createInfoLinks();
+ }
+
+ /**
+ * Return a string that identifies the value of the object, and that can
+ * be used to compare different value objects.
+ * Possibly overwritten by subclasses (e.g. to ensure that returned
+ * value is normalized first)
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->isValid() ? $this->m_dataitem->getHash() : implode( "\t", $this->mErrors );
+ }
+
+ /**
+ * Convenience method that checks if the value that is used to sort
+ * data of this type is numeric. This only works if the value is set.
+ *
+ * @return boolean
+ */
+ public function isNumeric() {
+ if ( isset( $this->m_dataitem ) ) {
+ return is_numeric( $this->m_dataitem->getSortKey() );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return true if a value was defined and understood by the given type,
+ * and false if parsing errors occurred or no value was given.
+ *
+ * @return boolean
+ */
+ public function isValid() {
+ return !$this->mHasErrors && isset( $this->m_dataitem );
+ }
+
+ /**
+ * Whether a datavalue can be used or not (can be made more restrictive then
+ * isValid).
+ *
+ * @note Validity defines a processable state without any technical restrictions
+ * while usability is determined by its accessibility to a context
+ * (permission, convention etc.)
+ *
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canUse() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isRestricted() {
+ return false;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ * @throws RuntimeException
+ */
+ public function getExtraneousFunctionFor( $name, array $parameters = [] ) {
+ return $this->dataValueServiceFactory->newExtraneousFunctionByName( $name, $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $data
+ */
+ public function setExtensionData( $key, $data ) {
+ $this->extenstionData[$key] = $data;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getExtensionData( $key ) {
+
+ if ( isset( $this->extenstionData[$key] ) ) {
+ return $this->extenstionData[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Options|null $options
+ */
+ public function copyOptions( Options $options = null ) {
+
+ if ( $options === null ) {
+ return;
+ }
+
+ foreach ( $options->getOptions() as $key => $value ) {
+ $this->setOption( $key, $value );
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string $key
+ * @param mxied $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return mixed|false
+ */
+ public function getOption( $key, $default = false ) {
+
+ if ( $this->options !== null && $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function hasFeature( $feature ) {
+
+ if ( $this->options !== null ) {
+ return $this->options->isFlagSet( 'smwgDVFeatures', (int)$feature );
+ }
+
+ return false;
+ }
+
+ /**
+ * @deprecated since 3.0, use DataValue::hasFeature
+ * @since 2.4
+ */
+ public function isEnabledFeature( $feature ) {
+ return $this->hasFeature( $feature );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return Options
+ */
+ protected function getOptions() {
+ return $this->options;
+ }
+
+ /**
+ * Initialise the datavalue from the given value string.
+ * The format of this strings might be any acceptable user input
+ * and especially includes the output of getWikiValue().
+ *
+ * @param string $value
+ */
+ abstract protected function parseUserValue( $value );
+
+ /**
+ * Set the actual data contained in this object. The method returns
+ * true if this was successful (requiring the type of the dataitem
+ * to match the data value). If false is returned, the data value is
+ * left unchanged (the data item was rejected).
+ *
+ * @note Even if this function returns true, the data value object
+ * might become invalid if the content of the data item caused errors
+ * in spite of it being of the right basic type. False is only returned
+ * if the data item is fundamentally incompatible with the data value.
+ *
+ * @since 1.6
+ *
+ * @param SMWDataItem $dataItem
+ *
+ * @return boolean
+ */
+ abstract protected function loadDataItem( SMWDataItem $dataItem );
+
+ /**
+ * Overwritten by callers to supply an array of parameters that can be used for
+ * creating servicelinks. The number and content of values in the parameter array
+ * may vary, depending on the concrete datatype.
+ */
+ protected function getServiceLinkParams() {
+ return false;
+ }
+
+ /**
+ * Check if property is range restricted and, if so, whether the current value is allowed.
+ * Creates an error if the value is illegal.
+ */
+ protected function checkAllowedValues() {
+
+ if ( $this->dataValueServiceFactory === null ) {
+ return;
+ }
+
+ $this->dataValueServiceFactory->getConstraintValueValidator()->validate( $this );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php
new file mode 100644
index 00000000..e4c81210
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php
@@ -0,0 +1,328 @@
+<?php
+
+use SMW\Exporter\Element;
+
+/**
+ * SMWExpData is a class representing semantic data that is ready for easy
+ * serialisation in OWL or RDF.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMW
+ */
+
+
+
+/**
+ * SMWExpData is a data container for export-ready semantic content. It is
+ * organised as a tree-shaped data structure with one root subject and zero
+ * or more children connected with labelled edges to the root. Children are
+ * again SMWExpData objects, and edges are annotated with SMWExpNsElements
+ * specifying properties.
+ * @note We do not allow property element without namespace abbreviation
+ * here. Property aabbreviations are mandatory for some serialisations.
+ *
+ * @ingroup SMW
+ */
+class SMWExpData implements Element {
+
+ /**
+ * @var DataItem|null
+ */
+ private $dataItem;
+
+ /**
+ * The subject of the data that we store.
+ * @var SMWExpResource
+ */
+ protected $m_subject;
+
+ /**
+ * Array mapping property URIs to arrays their values, given as
+ * SMWExpElement objects.
+ * @var array of array of SMWElement
+ */
+ protected $m_children = [];
+
+ /**
+ * Array mapping property URIs to arrays their SMWExpResource
+ * @var array of SMWExpResource
+ */
+ protected $m_edges = [];
+
+ /**
+ * @var string|null
+ */
+ private $hash = null;
+
+ /**
+ * Constructor. $subject is the SMWExpResource for the
+ * subject about which this SMWExpData is.
+ */
+ public function __construct( SMWExpResource $subject ) {
+ $this->dataItem = $subject->getDataItem();
+ $this->m_subject = $subject;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DataItem|null
+ */
+ public function getDataItem() {
+ return $this->dataItem;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ if ( $this->hash !== null ) {
+ return $this->hash;
+ }
+
+ $hashes = [];
+ $hashes[] = $this->m_subject->getHash();
+
+ foreach ( $this->getProperties() as $property ) {
+
+ $hashes[] = $property->getHash();
+
+ foreach ( $this->getValues( $property ) as $child ) {
+ $hashes[] = $child->getHash();
+ }
+ }
+
+ sort( $hashes );
+
+ $this->hash = md5( implode( '#', $hashes ) );
+ unset( $hashes );
+
+ return $this->hash;
+ }
+
+ /**
+ * Turn an array of SMWExpElements into an RDF collection.
+ *
+ * @param $elements array of SMWExpElement
+ * @return SMWExpData
+ */
+ public static function makeCollection( array $elements ) {
+
+ if ( count( $elements ) == 0 ) {
+ return new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'nil' ) );
+ }
+
+ $result = new SMWExpData( new SMWExpResource( '' ) ); // bnode
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
+ new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'List' ) )
+ );
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'first' ),
+ array_shift( $elements )
+ );
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'rest' ),
+ self::makeCollection( $elements )
+ );
+
+ return $result;
+ }
+
+ /**
+ * Return subject to which the stored semantic annotation refer to.
+ *
+ * @return SMWExpResource
+ */
+ public function getSubject() {
+ return $this->m_subject;
+ }
+
+ /**
+ * Store a value for a property identified by its title object. No
+ * duplicate elimination as this is usually done in SMWSemanticData
+ * already (which is typically used to generate this object).
+ *
+ * @param SMWExpNsResource $property
+ * @param Element $child
+ */
+ public function addPropertyObjectValue( SMWExpNsResource $property, Element $child ) {
+
+ $this->hash = null;
+
+ if ( !array_key_exists( $property->getUri(), $this->m_edges ) ) {
+ $this->m_children[$property->getUri()] = [];
+ $this->m_edges[$property->getUri()] = $property;
+ }
+
+ $this->m_children[$property->getUri()][] = $child;
+ }
+
+ /**
+ * Return the list of SMWExpResource objects for all properties for
+ * which some values have been given.
+ *
+ * @return array of SMWExpResource
+ */
+ public function getProperties() {
+ return $this->m_edges;
+ }
+
+ /**
+ * Return the list of SMWExpElement values associated to some property
+ * (element).
+ *
+ * @return array of SMWExpElement
+ */
+ public function getValues( SMWExpResource $property ) {
+
+ if ( array_key_exists( $property->getUri(), $this->m_children ) ) {
+ return $this->m_children[$property->getUri()];
+ }
+
+ return [];
+ }
+
+ /**
+ * Return the list of SMWExpData values associated to some property that is
+ * specified by a standard namespace id and local name.
+ *
+ * @param $namespaceId string idetifying a known special namespace (e.g. "rdf")
+ * @param $localName string of local name (e.g. "type")
+ * @return array of SMWExpData
+ */
+ public function getSpecialValues( $namespaceId, $localName ) {
+ $pe = SMWExporter::getInstance()->getSpecialNsResource( $namespaceId, $localName );
+ return $this->getValues( $pe );
+ }
+
+ /**
+ * This function finds the main type (class) element of the subject
+ * based on the current property assignments. It returns this type
+ * element (SMWExpElement) and removes the according type assignement
+ * from the data. If no type is assigned, the element for rdf:Resource
+ * is returned.
+ *
+ * @note Under all normal conditions, the result will be an
+ * SMWExpResource.
+ *
+ * @return SMWExpElement
+ */
+ public function extractMainType() {
+ $pe = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' );
+ if ( array_key_exists( $pe->getUri(), $this->m_children ) ) {
+ $result = array_shift( $this->m_children[$pe->getUri()] );
+ if ( count( $this->m_children[$pe->getUri()] ) == 0 ) {
+ unset( $this->m_edges[$pe->getUri()] );
+ unset( $this->m_children[$pe->getUri()] );
+ }
+ return ( $result instanceof SMWExpData ) ? $result->getSubject() : $result;
+ } else {
+ return SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'Resource' );
+ }
+ }
+
+ /**
+ * Check if this element encodes an RDF list, and if yes return an
+ * array of SMWExpElements corresponding to the collection elements in
+ * the specified order. Otherwise return false.
+ * The method only returns lists that can be encoded using
+ * parseType="Collection" in RDF/XML, i.e. only lists of non-literal
+ * resources.
+ *
+ * @return mixed array of SMWExpElement (but not SMWExpLiteral) or false
+ */
+ public function getCollection() {
+ $rdftypeUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' )->getUri();
+ $rdffirstUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'first' )->getUri();
+ $rdfrestUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'rest' )->getUri();
+ $rdfnilUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'nil' )->getUri();
+ // first check if we are basically an RDF List:
+ if ( ( $this->m_subject->isBlankNode() ) &&
+ ( count( $this->m_children ) == 3 ) &&
+ ( array_key_exists( $rdftypeUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdftypeUri] ) == 1 ) &&
+ ( array_key_exists( $rdffirstUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdffirstUri] ) == 1 ) &&
+ !( end( $this->m_children[$rdffirstUri] ) instanceof SMWExpLiteral ) &&
+ // (parseType collection in RDF not possible with literals :-/)
+ ( array_key_exists( $rdfrestUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdfrestUri] ) == 1 ) ) {
+ $typedata = end( $this->m_children[$rdftypeUri] );
+ $rdflistUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'List' )->getUri();
+ if ( $typedata->getSubject()->getUri() == $rdflistUri ) {
+ $first = end( $this->m_children[$rdffirstUri] );
+ $rest = end( $this->m_children[$rdfrestUri] );
+ if ( $rest instanceof SMWExpData ) {
+ $restlist = $rest->getCollection();
+ if ( $restlist === false ) {
+ return false;
+ } else {
+ array_unshift( $restlist, $first );
+ return $restlist;
+ }
+ } elseif ( ( $rest instanceof SMWExpResource ) &&
+ ( $rest->getUri() == $rdfnilUri ) ) {
+ return [ $first ];
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } elseif ( ( count( $this->m_children ) == 0 ) && ( $this->m_subject->getUri() == $rdfnilUri ) ) {
+ return [];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return an array of ternary arrays (subject predicate object) of
+ * SMWExpElements that represents the flattened version of this data.
+ *
+ * @return array of array of SMWExpElement
+ */
+ public function getTripleList( Element $subject = null ) {
+ global $smwgBnodeCount;
+ if ( !isset( $smwgBnodeCount ) ) {
+ $smwgBnodeCount = 0;
+ }
+
+ if ( $subject == null ) {
+ $subject = $this->m_subject;
+ }
+
+ $result = [];
+
+ foreach ( $this->m_edges as $key => $edge ) {
+ foreach ( $this->m_children[$key] as $childElement ) {
+ if ( $childElement instanceof SMWExpData ) {
+ $childSubject = $childElement->getSubject();
+ } else {
+ $childSubject = $childElement;
+ }
+
+ if ( ( $childSubject instanceof SMWExpResource ) &&
+ ( $childSubject->isBlankNode() ) ) { // bnode, rename ID to avoid unifying bnodes of different contexts
+ // TODO: should we really rename bnodes of the form "_id" here?
+ $childSubject = new SMWExpResource( '_' . $smwgBnodeCount++, $childSubject->getDataItem() );
+ }
+
+ $result[] = [ $subject, $edge, $childSubject ];
+ if ( $childElement instanceof SMWExpData ) { // recursively add child's triples
+ $result = array_merge( $result, $childElement->getTripleList( $childSubject ) );
+ }
+ }
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php
new file mode 100644
index 00000000..3e40c077
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php
@@ -0,0 +1,768 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\Escaper;
+use SMW\Query\PrintRequest;
+use SMW\SemanticData;
+use SMW\Site;
+
+/**
+ * File holding the SMWExportController class that provides basic functions for
+ * exporting pages to RDF and OWL.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for controlling the export of SMW page data, supporting high-level
+ * features such as recursive export and backlink inclusion. The class controls
+ * export independent of the serialisation syntax that is used.
+ *
+ * @ingroup SMW
+ */
+class SMWExportController {
+ const MAX_CACHE_SIZE = 5000; // do not let cache arrays get larger than this
+ const CACHE_BACKJUMP = 500; // kill this many cached entries if limit is reached,
+ // avoids too much array copying; <= MAX_CACHE_SIZE!
+ /**
+ * The object used for serialisation.
+ * @var SMWSerializer
+ */
+ protected $serializer;
+ /**
+ * An array that keeps track of the elements for which we still need to
+ * write auxiliary definitions/declarations.
+ */
+ protected $element_queue;
+ /**
+ * An array that keeps track of the recursion depth with which each object
+ * has been serialised.
+ */
+ protected $element_done;
+ /**
+ * Boolean to indicate whether all objects that are exported in full (with
+ * all data) should also lead to the inclusion of all "inlinks" that they
+ * receive from other objects. If yes, these other objects are also
+ * serialised with at least the relevant inlinking properties included.
+ * Adding such dependencies counts as "recursive serialisation" and whether
+ * or not inlinking objects are included in full depends on the setting for
+ * recursion depth. Setting this to true enables "browsable RDF".
+ */
+ protected $add_backlinks;
+ /**
+ * Controls how long to wait until flushing content to output. Flushing
+ * early may reduce the memory footprint of serialization functions.
+ * Flushing later has some advantages for export formats like RDF/XML where
+ * global namespace declarations are only possible by modifying the header,
+ * so that only local declarations are possible after the first flush.
+ */
+ protected $delay_flush;
+ /**
+ * File handle for a potential output file to write to, or null if printing
+ * to standard output.
+ */
+ protected $outputfile;
+
+ /**
+ * @var DeepRedirectTargetResolver
+ */
+ private $deepRedirectTargetResolver = null;
+
+ /**
+ * Constructor.
+ * @param SMWSerializer $serializer defining the object used for syntactic
+ * serialization.
+ * @param boolean $enable_backlinks defining if backlinks are included,
+ * see $add_backlinks for details.
+ */
+ public function __construct( SMWSerializer $serializer, $enable_backlinks = false ) {
+ $this->serializer = $serializer;
+ $this->outputfile = null;
+ $this->add_backlinks = $enable_backlinks;
+ }
+
+ /**
+ * Enable or disable inclusion of backlinks into the output.
+ * @param boolean $enable
+ */
+ public function enableBacklinks( $enable ) {
+ $this->add_backlinks = $enable;
+ }
+
+ /**
+ * Initialize all internal structures to begin with some serialization.
+ * Returns true if initialization was successful (this means that the
+ * optional output file is writable).
+ * @param string $outfilename URL of the file that output should be written
+ * to, or empty string for writing to the standard output.
+ *
+ * @return boolean
+ */
+ protected function prepareSerialization( $outfilename = '' ) {
+ $this->serializer->clear();
+ $this->element_queue = [];
+ $this->element_done = [];
+ if ( $outfilename !== '' ) {
+ $this->outputfile = fopen( $outfilename, 'w' );
+ if ( !$this->outputfile ) { // TODO Rather throw an exception here.
+ print "\nCannot open \"$outfilename\" for writing.\n";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Serialize data associated to a specific page. This method works on the
+ * level of pages, i.e. it serialises parts of SMW content and implements
+ * features like recursive export or backlinks that are available for this
+ * type of data.
+ *
+ * The recursion depth means the following. Depth of 1 or above means
+ * the object is serialised with all property values, and referenced
+ * objects are serialised with depth reduced by 1. Depth 0 means that only
+ * minimal declarations are serialised, so no dependencies are added. A
+ * depth of -1 encodes "infinite" depth, i.e. a complete recursive
+ * serialisation without limit.
+ *
+ * @param SMWDIWikiPage $diWikiPage specifying the page to be exported
+ * @param integer $recursiondepth specifying the depth of recursion
+ */
+ protected function serializePage( SMWDIWikiPage $diWikiPage, $recursiondepth = 1 ) {
+
+ if ( $this->isPageDone( $diWikiPage, $recursiondepth ) ) {
+ return; // do not export twice
+ }
+
+ $this->markPageAsDone( $diWikiPage, $recursiondepth );
+ $semData = $this->getSemanticData( $diWikiPage, ( $recursiondepth == 0 ) );
+
+ // Don't try to serialize an empty page that cause an incomplete exp-data set
+ // (e.g. _REDI as no property page hence DBKey is empty)
+ if ( $semData === null || $diWikiPage->getDBKey() === '' ) {
+ return null;
+ }
+
+ $expData = SMWExporter::getInstance()->makeExportData( $semData );
+ $this->serializer->serializeExpData( $expData, $recursiondepth );
+
+ foreach( $semData->getSubSemanticData() as $subSemanticData ) {
+
+ // Mark SubSemanticData subjects as well to ensure that backlinks to
+ // the same subject do not create duplicate XML export entities
+ $this->markPageAsDone(
+ $subSemanticData->getSubject(),
+ $recursiondepth
+ );
+
+ $expData = SMWExporter::getInstance()->makeExportData(
+ $subSemanticData
+ );
+
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ // let other extensions add additional RDF data for this page
+ $expDataList = [];
+
+ \Hooks::run(
+ 'SMW::Exporter::Controller::AddExpData',
+ [
+ $diWikiPage,
+ &$expDataList,
+ ( $recursiondepth != 0 ),
+ $this->add_backlinks
+ ]
+ );
+
+ foreach ( $expDataList as $data ) {
+
+ if ( !$data instanceof SMWExpData ) {
+ continue;
+ }
+
+ $this->serializer->serializeExpData( $data );
+ }
+
+ if ( $recursiondepth != 0 ) {
+ $subrecdepth = $recursiondepth > 0 ? ( $recursiondepth - 1 ) :
+ ( $recursiondepth == 0 ? 0 : -1 );
+
+ foreach ( $expData->getProperties() as $property ) {
+ if ( $property->getDataItem() instanceof SMWWikiPageValue ) {
+ $this->queuePage( $property->getDataItem(), 0 ); // no real recursion along properties
+ }
+ $wikipagevalues = false;
+ foreach ( $expData->getValues( $property ) as $valueExpElement ) {
+ $valueResource = $valueExpElement instanceof SMWExpData ? $valueExpElement->getSubject() : $valueExpElement;
+ if ( !$wikipagevalues && ( $valueResource->getDataItem() instanceof SMWWikiPageValue ) ) {
+ $wikipagevalues = true;
+ } elseif ( !$wikipagevalues ) {
+ break;
+ }
+ $this->queuePage( $valueResource->getDataItem(), $subrecdepth );
+ }
+ }
+
+ // Add backlinks:
+ // Note: Backlinks are different from recursive serialisations, since
+ // stub declarations (recdepth==0) still need to have the property that
+ // links back to the object. So objects that would be exported with
+ // recdepth 0 cannot be put into the main queue but must be done right
+ // away. They also might be required many times, if they link back to
+ // many different objects in many ways (we cannot consider them "Done"
+ // if they were serialised at recdepth 0 only).
+ if ( $this->add_backlinks ) {
+ $inprops = \SMW\StoreFactory::getStore()->getInProperties( $diWikiPage );
+
+ foreach ( $inprops as $inprop ) {
+ $propWikiPage = $inprop->getCanonicalDiWikiPage();
+
+ if ( !is_null( $propWikiPage ) ) {
+ $this->queuePage( $propWikiPage, 0 ); // no real recursion along properties
+ }
+
+ $inSubs = \SMW\StoreFactory::getStore()->getPropertySubjects( $inprop, $diWikiPage );
+
+ foreach ( $inSubs as $inSub ) {
+ if ( !$this->isPageDone( $inSub, $subrecdepth ) ) {
+ $semdata = $this->getSemanticData( $inSub, true );
+
+ if ( !$semdata instanceof SMWSemanticData ) {
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $inprop, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData, $subrecdepth );
+ }
+ }
+ }
+
+ if ( NS_CATEGORY === $diWikiPage->getNamespace() ) { // also print elements of categories
+ $options = new SMWRequestOptions();
+ $options->limit = 100; // Categories can be large, always use limit
+ $instances = \SMW\StoreFactory::getStore()->getPropertySubjects( new SMW\DIProperty( '_INST' ), $diWikiPage, $options );
+ $pinst = new SMW\DIProperty( '_INST' );
+
+ foreach ( $instances as $instance ) {
+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
+ $semdata = $this->getSemanticData( $instance, true );
+
+ if ( !$semdata instanceof SMWSemanticData ) {
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData, $subrecdepth );
+ }
+ }
+ } elseif ( SMW_NS_CONCEPT === $diWikiPage->getNamespace() ) { // print concept members (slightly different code)
+ $desc = new SMWConceptDescription( $diWikiPage );
+ $desc->addPrintRequest( new PrintRequest( PrintRequest::PRINT_THIS, '' ) );
+ $query = new SMWQuery( $desc );
+ $query->setLimit( 100 );
+
+ $res = \SMW\StoreFactory::getStore()->getQueryResult( $query );
+ $resarray = $res->getNext();
+ $pinst = new SMW\DIProperty( '_INST' );
+
+ while ( $resarray !== false ) {
+ $instance = end( $resarray )->getNextDataItem();
+
+ if ( !$instance instanceof \SMWDataItem ) {
+ $resarray = $res->getNext();
+ continue;
+ }
+
+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
+ $semdata = $this->getSemanticData( $instance, true );
+
+ if ( !$semdata instanceof \SMW\SemanticData ) {
+ $resarray = $res->getNext();
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ $resarray = $res->getNext();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a given SMWDIWikiPage to the export queue if needed.
+ */
+ protected function queuePage( SMWDIWikiPage $diWikiPage, $recursiondepth ) {
+ if ( !$this->isPageDone( $diWikiPage, $recursiondepth ) ) {
+ $diWikiPage->recdepth = $recursiondepth; // add a field
+ $this->element_queue[$diWikiPage->getHash()] = $diWikiPage;
+ }
+ }
+
+ /**
+ * Mark an article as done while making sure that the cache used for this
+ * stays reasonably small. Input is given as an SMWDIWikiPage object.
+ */
+ protected function markPageAsDone( SMWDIWikiPage $di, $recdepth ) {
+ $this->markHashAsDone( $di->getHash(), $recdepth );
+ }
+
+ /**
+ * Mark a task as done while making sure that the cache used for this
+ * stays reasonably small.
+ */
+ protected function markHashAsDone( $hash, $recdepth ) {
+ if ( count( $this->element_done ) >= self::MAX_CACHE_SIZE ) {
+ $this->element_done = array_slice( $this->element_done,
+ self::CACHE_BACKJUMP,
+ self::MAX_CACHE_SIZE - self::CACHE_BACKJUMP,
+ true );
+ }
+ if ( !$this->isHashDone( $hash, $recdepth ) ) {
+ $this->element_done[$hash] = $recdepth; // mark title as done, with given recursion
+ }
+ unset( $this->element_queue[$hash] ); // make sure it is not in the queue
+ }
+
+ /**
+ * Check if the given object has already been serialised at sufficient
+ * recursion depth.
+ * @param SMWDIWikiPage $st specifying the object to check
+ *
+ * @return boolean
+ */
+ protected function isPageDone( SMWDIWikiPage $di, $recdepth ) {
+ return $this->isHashDone( $di->getHash(), $recdepth );
+ }
+
+ /**
+ * Check if the given task has already been completed at sufficient
+ * recursion depth.
+ */
+ protected function isHashDone( $hash, $recdepth ) {
+ return ( ( array_key_exists( $hash, $this->element_done ) ) &&
+ ( ( $this->element_done[$hash] == -1 ) ||
+ ( ( $recdepth != -1 ) && ( $this->element_done[$hash] >= $recdepth ) ) ) );
+ }
+
+ /**
+ * Retrieve a copy of the semantic data for a wiki page, possibly filtering
+ * it so that only essential properties are included (in some cases, we only
+ * want to export stub information about a page).
+ * We make a copy of the object since we may want to add more data later on
+ * and we do not want to modify the store's result which may be used for
+ * caching purposes elsewhere.
+ */
+ protected function getSemanticData( SMWDIWikiPage $diWikiPage, $core_props_only ) {
+
+ // Issue 619
+ // Resolve the redirect target and return a container with information
+ // about the redirect
+ if ( $diWikiPage->getTitle() !== null && $diWikiPage->getTitle()->isRedirect() ) {
+
+ try {
+ $redirectTarget = $this->getDeepRedirectTargetResolver()->findRedirectTargetFor( $diWikiPage->getTitle() );
+ } catch ( \Exception $e ) {
+ $redirectTarget = null;
+ }
+
+ // Couldn't resolve the redirect which is most likely caused by a
+ // circular redirect therefore we give up
+ if ( $redirectTarget === null ) {
+ return null;
+ }
+
+ $semData = new SemanticData( $diWikiPage );
+
+ $semData->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ DIWikiPage::newFromTitle( $redirectTarget )
+ );
+
+ return $semData;
+ }
+
+ $semdata = \SMW\StoreFactory::getStore()->getSemanticData( $diWikiPage, $core_props_only ? [ '__spu', '__typ', '__imp' ] : false ); // advise store to retrieve only core things
+ if ( $core_props_only ) { // be sure to filter all non-relevant things that may still be present in the retrieved
+ $result = new SMWSemanticData( $diWikiPage );
+ foreach ( [ '_URI', '_TYPE', '_IMPO' ] as $propid ) {
+ $prop = new SMW\DIProperty( $propid );
+ $values = $semdata->getPropertyValues( $prop );
+ foreach ( $values as $dv ) {
+ $result->addPropertyObjectValue( $prop, $dv );
+ }
+ }
+ } else {
+ $result = clone $semdata;
+ }
+
+
+ return $result;
+ }
+
+ /**
+ * Send to the output what has been serialized so far. The flush might
+ * be deferred until later unless $force is true.
+ */
+ protected function flush( $force = false ) {
+ if ( !$force && ( $this->delay_flush > 0 ) ) {
+ $this->delay_flush -= 1;
+ } elseif ( !is_null( $this->outputfile ) ) {
+ fwrite( $this->outputfile, $this->serializer->flushContent() );
+ } else {
+ ob_start();
+ print $this->serializer->flushContent();
+ // Ship data in small chunks (even though browsers often do not display anything
+ // before the file is complete -- this might be due to syntax highlighting features
+ // for app/xml). You may want to sleep(1) here for debugging this.
+ ob_flush();
+ flush();
+ ob_get_clean();
+ }
+ }
+
+ /**
+ * This function prints all selected pages, specified as an array of page
+ * names (strings with namespace identifiers).
+ *
+ * @param array $pages list of page names to export
+ * @param integer $recursion determines how pages are exported recursively:
+ * "0" means that referenced resources are only declared briefly, "1" means
+ * that all referenced resources are also exported recursively (propbably
+ * retrieving the whole wiki).
+ * @param string $revisiondate filter page list by including only pages
+ * that have been changed since this date; format "YmdHis"
+ *
+ * @todo Consider dropping the $revisiondate filtering and all associated
+ * functionality. Is anybody using this?
+ */
+ public function printPages( $pages, $recursion = 1, $revisiondate = false ) {
+
+ $linkCache = LinkCache::singleton();
+ $this->prepareSerialization();
+ $this->delay_flush = 10; // flush only after (fully) printing 11 objects
+
+ // transform pages into queued short titles
+ foreach ( $pages as $page ) {
+ $title = Title::newFromText( $page );
+ if ( null === $title ) {
+ continue; // invalid title name given
+ }
+ if ( $revisiondate !== '' ) { // filter page list by revision date
+ $rev = Revision::getTimeStampFromID( $title, $title->getLatestRevID() );
+ if ( $rev < $revisiondate ) {
+ continue;
+ }
+ }
+
+ $diPage = SMWDIWikiPage::newFromTitle( $title );
+ $this->queuePage( $diPage, ( $recursion==1 ? -1 : 1 ) );
+ }
+
+ $this->serializer->startSerialization();
+
+ if ( count( $pages ) == 1 ) { // ensure that ontologies that are retrieved as linked data are not confused with their subject!
+ $ontologyuri = SMWExporter::getInstance()->expandURI( '&export;' ) . '/' . Escaper::encodeUri( end( $pages ) );
+ } else { // use empty URI, i.e. "location" as URI otherwise
+ $ontologyuri = '';
+ }
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( $ontologyuri ) );
+
+ while ( count( $this->element_queue ) > 0 ) {
+ $diPage = reset( $this->element_queue );
+ $this->serializePage( $diPage, $diPage->recdepth );
+ $this->flush();
+ $linkCache->clear(); // avoid potential memory leak
+ }
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+ /**
+ * Exports semantic data for all pages within the wiki and for all elements
+ * that are referred to a file resource
+ *
+ * @since 2.0
+ *
+ * @param string $outfile the output file URI, or false if printing to stdout
+ * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
+ * @param integer $delay number of microseconds for which to sleep during
+ * export to reduce server load in long-running operations
+ * @param integer $delayeach number of pages to process between two sleeps
+ */
+ public function printAllToFile( $outfile, $ns_restriction = false, $delay, $delayeach ) {
+
+ if ( !$this->prepareSerialization( $outfile ) ) {
+ return;
+ }
+
+ $this->printAll( $ns_restriction, $delay, $delayeach );
+ }
+
+ /**
+ * Exports semantic data for all pages within the wiki and for all elements
+ * that are referred to the stdout
+ *
+ * @since 2.0
+ *
+ * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
+ * @param integer $delay number of microseconds for which to sleep during
+ * export to reduce server load in long-running operations
+ * @param integer $delayeach number of pages to process between two sleeps
+ */
+ public function printAllToOutput( $ns_restriction = false, $delay, $delayeach ) {
+ $this->prepareSerialization();
+ $this->printAll( $ns_restriction, $delay, $delayeach );
+ }
+
+ /**
+ * @since 2.0 made protected; use printAllToFile or printAllToOutput
+ */
+ protected function printAll( $ns_restriction = false, $delay, $delayeach ) {
+ $linkCache = LinkCache::singleton();
+ $db = wfGetDB( DB_SLAVE );
+
+ $this->delay_flush = 10;
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ $end = $db->selectField( 'page', 'max(page_id)', false, __METHOD__ );
+ $a_count = 0; // DEBUG
+ $d_count = 0; // DEBUG
+ $delaycount = $delayeach;
+
+ for ( $id = 1; $id <= $end; $id += 1 ) {
+ $title = Title::newFromID( $id );
+ if ( is_null( $title ) || !\SMW\NamespaceExaminer::getInstance()->isSemanticEnabled( $title->getNamespace() ) ) {
+ continue;
+ }
+ if ( !self::fitsNsRestriction( $ns_restriction, $title->getNamespace() ) ) {
+ continue;
+ }
+ $a_count += 1; // DEBUG
+
+ $diPage = SMWDIWikiPage::newFromTitle( $title );
+ $this->queuePage( $diPage, 1 );
+
+ while ( count( $this->element_queue ) > 0 ) {
+ $diPage = reset( $this->element_queue );
+ $this->serializePage( $diPage, $diPage->recdepth );
+ // resolve dependencies that will otherwise not be printed
+ foreach ( $this->element_queue as $key => $diaux ) {
+ if ( !\SMW\NamespaceExaminer::getInstance()->isSemanticEnabled( $diaux->getNamespace() ) ||
+ !self::fitsNsRestriction( $ns_restriction, $diaux->getNamespace() ) ) {
+ // Note: we do not need to check the cache to guess if an element was already
+ // printed. If so, it would not be included in the queue in the first place.
+ $d_count += 1; // DEBUG
+ } else { // don't carry values that you do not want to export (yet)
+ unset( $this->element_queue[$key] );
+ }
+ }
+ // sleep each $delaycount for $delay µs to be nice to the server
+ if ( ( $delaycount-- < 0 ) && ( $delayeach != 0 ) ) {
+ usleep( $delay );
+ $delaycount = $delayeach;
+ }
+ }
+
+ $this->flush();
+ $linkCache->clear();
+ }
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+ }
+
+ /**
+ * Print basic definitions a list of pages ordered by their page id.
+ * Offset and limit refer to the count of existing pages, not to the
+ * page id.
+ * @param integer $offset the number of the first (existing) page to
+ * serialize a declaration for
+ * @param integer $limit the number of pages to serialize
+ */
+ public function printPageList( $offset = 0, $limit = 30 ) {
+ global $smwgNamespacesWithSemanticLinks;
+
+ $db = wfGetDB( DB_SLAVE );
+ $this->prepareSerialization();
+ $this->delay_flush = 35; // don't do intermediate flushes with default parameters
+ $linkCache = LinkCache::singleton();
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ $query = '';
+ foreach ( $smwgNamespacesWithSemanticLinks as $ns => $enabled ) {
+ if ( $enabled ) {
+ if ( $query !== '' ) {
+ $query .= ' OR ';
+ }
+ $query .= 'page_namespace = ' . $db->addQuotes( $ns );
+ }
+ }
+ $res = $db->select( $db->tableName( 'page' ),
+ 'page_id,page_title,page_namespace', $query,
+ 'SMW::RDF::PrintPageList', [ 'ORDER BY' => 'page_id ASC', 'OFFSET' => $offset, 'LIMIT' => $limit ] );
+ $foundpages = false;
+
+ foreach ( $res as $row ) {
+ $foundpages = true;
+ try {
+ $diPage = new SMWDIWikiPage( $row->page_title, $row->page_namespace, '' );
+ $this->serializePage( $diPage, 0 );
+ $this->flush();
+ $linkCache->clear();
+ } catch ( SMWDataItemException $e ) {
+ // strange data, who knows, not our DB table, keep calm and carry on
+ }
+ }
+
+ if ( $foundpages ) { // add link to next result page
+ if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=' ) . ( $offset + $limit );
+ } else {
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=' ) . ( $offset + $limit );
+ }
+
+ $expData = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+
+ /**
+ * Print basic information about this site.
+ */
+ public function printWikiInfo() {
+
+ $this->prepareSerialization();
+ $this->delay_flush = 35; // don't do intermediate flushes with default parameters
+
+ // assemble export data:
+ $expData = new SMWExpData( new SMWExpResource( '&wiki;#wiki' ) );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
+ new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'Wikisite' ) )
+ );
+
+ // basic wiki information
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'label' ),
+ new SMWExpLiteral( Site::name() )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'siteName' ),
+ new SMWExpLiteral( Site::name(), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'pagePrefix' ),
+ new SMWExpLiteral( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'smwVersion' ),
+ new SMWExpLiteral( SMW_VERSION, 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'langCode' ),
+ new SMWExpLiteral( Site::languageCode(), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $mainpage = Title::newMainPage();
+
+ if ( !is_null( $mainpage ) ) {
+ $ed = new SMWExpData( new SMWExpResource( $mainpage->getFullURL() ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'mainPage' ), $ed );
+ }
+
+ // statistical information
+ foreach ( Site::stats() as $key => $value ) {
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', $key ),
+ new SMWExpLiteral( (string)$value, 'http://www.w3.org/2001/XMLSchema#int' )
+ );
+ }
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+ $this->serializer->serializeExpData( $expData );
+
+ // link to list of existing pages:
+ if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=0' );
+ } else {
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=0' );
+ }
+ $expData = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $this->serializer->serializeExpData( $expData );
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+ /**
+ * This function checks whether some article fits into a given namespace
+ * restriction. Restrictions are encoded as follows: a non-negative number
+ * requires the namespace to be identical to the given number; "-1"
+ * requires the namespace to be different from Category, Property, and
+ * Type; "false" means "no restriction".
+ *
+ * @param $res mixed encoding the restriction as described above
+ * @param $ns integer the namespace constant to be checked
+ *
+ * @return boolean
+ */
+ static public function fitsNsRestriction( $res, $ns ) {
+ if ( $res === false ) {
+ return true;
+ }
+ if ( is_array( $res ) ) {
+ return in_array( $ns, $res );
+ }
+ if ( $res >= 0 ) {
+ return ( $res == $ns );
+ }
+ return ( ( $res != NS_CATEGORY ) && ( $res != SMW_NS_PROPERTY ) );
+ }
+
+ private function getDeepRedirectTargetResolver() {
+
+ if ( $this->deepRedirectTargetResolver === null ) {
+ $this->deepRedirectTargetResolver = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newDeepRedirectTargetResolver();
+ }
+
+ return $this->deepRedirectTargetResolver;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php
new file mode 100644
index 00000000..9f97d748
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php
@@ -0,0 +1,675 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Site;
+use SMW\Exporter\DataItemMatchFinder;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ElementFactory;
+use SMW\Exporter\Escaper;
+use SMW\Exporter\ExpResourceMapper;
+use SMW\Exporter\ResourceBuilders\DispatchingResourceBuilder;
+use SMW\Localizer;
+use SMW\NamespaceUriFinder;
+
+/**
+ * SMWExporter is a class for converting internal page-based data (SMWSemanticData) into
+ * a format for easy serialisation in OWL or RDF.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMW
+ */
+class SMWExporter {
+
+ const POOLCACHE_ID = 'exporter.shared';
+
+ /**
+ * @var SMWExporter
+ */
+ private static $instance = null;
+
+ /**
+ * @var ExpResourceMapper
+ */
+ private static $expResourceMapper = null;
+
+ /**
+ * @var ElementFactory
+ */
+ private static $elementFactory = null;
+
+ /**
+ * @var DataItemMatchFinder
+ */
+ private static $dataItemMatchFinder = null;
+
+ /**
+ * @var DispatchingResourceBuilder
+ */
+ private static $dispatchingResourceBuilder = null;
+
+ static protected $m_exporturl = false;
+ static protected $m_ent_wiki = false;
+ static protected $m_ent_property = false;
+ static protected $m_ent_category = false;
+ static protected $m_ent_wikiurl = false;
+
+ /**
+ * @since 2.0
+ *
+ * @return SMWExporter
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+
+ self::$instance = new self();
+ self::$instance->initBaseURIs();
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $poolCache = $applicationFactory->getInMemoryPoolCache();
+ $poolCache->resetPoolCacheById( self::POOLCACHE_ID );
+
+ // FIXME with 3.x
+ // There is no better way of getting around the static use without BC
+ self::$elementFactory = new ElementFactory();
+
+ self::$dispatchingResourceBuilder = new DispatchingResourceBuilder();
+
+ self::$expResourceMapper = new ExpResourceMapper(
+ $applicationFactory->getStore()
+ );
+
+ self::$expResourceMapper->reset();
+
+ self::$expResourceMapper->setBCAuxiliaryUse(
+ $applicationFactory->getSettings()->get( 'smwgExportBCAuxiliaryUse' )
+ );
+
+ self::$dataItemMatchFinder = new DataItemMatchFinder(
+ $applicationFactory->getStore(),
+ self::$m_ent_wiki
+ );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static function clear() {
+ self::$instance = null;
+ self::$m_exporturl = false;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function resetCacheBy( SMWDIWikiPage $diWikiPage ) {
+ self::$expResourceMapper->invalidateCache( $diWikiPage );
+ }
+
+ /**
+ * Make sure that necessary base URIs are initialised properly.
+ */
+ static public function initBaseURIs() {
+ if ( self::$m_exporturl !== false ) {
+ return;
+ }
+ global $wgContLang;
+
+ global $smwgNamespace; // complete namespace for URIs (with protocol, usually http://)
+
+ $resolver = Title::makeTitle( NS_SPECIAL, 'URIResolver' );
+
+ if ( '' == $smwgNamespace ) {
+ $smwgNamespace = $resolver->getFullURL() . '/';
+ } elseif ( $smwgNamespace[0] == '.' ) {
+ $smwgNamespace = "http://" . substr( $smwgNamespace, 1 ) . $resolver->getLocalURL() . '/';
+ }
+
+ // The article name must be the last part of wiki URLs for proper OWL/RDF export:
+ self::$m_ent_wikiurl = Site::wikiurl();
+ self::$m_ent_wiki = $smwgNamespace;
+
+ $property = $GLOBALS['smwgExportBCNonCanonicalFormUse'] ? urlencode( str_replace( ' ', '_', $wgContLang->getNsText( SMW_NS_PROPERTY ) ) ) : 'Property';
+ $category = $GLOBALS['smwgExportBCNonCanonicalFormUse'] ? urlencode( str_replace( ' ', '_', $wgContLang->getNsText( NS_CATEGORY ) ) ) : 'Category';
+
+ self::$m_ent_property = self::$m_ent_wiki . Escaper::encodeUri( $property . ':' );
+ self::$m_ent_category = self::$m_ent_wiki . Escaper::encodeUri( $category . ':' );
+
+ $title = Title::makeTitle( NS_SPECIAL, 'ExportRDF' );
+ self::$m_exporturl = self::$m_ent_wikiurl . $title->getPrefixedURL();
+
+ // Canonical form, the title object always contains a wgContLang reference
+ // therefore replace it
+ if ( !$GLOBALS['smwgExportBCNonCanonicalFormUse'] ) {
+ $localizer = Localizer::getInstance();
+
+ self::$m_ent_property = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_property );
+ self::$m_ent_category = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_category );
+ self::$m_ent_wiki = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_wiki );
+ self::$m_exporturl = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_exporturl );
+ }
+ }
+
+ /**
+ * Create exportable data from a given semantic data record.
+ *
+ * @param $semdata SMWSemanticData
+ * @return SMWExpData
+ */
+ static public function makeExportData( SMWSemanticData $semdata ) {
+ self::initBaseURIs();
+
+ $subject = $semdata->getSubject();
+
+ // Make sure to use the canonical form, a localized representation
+ // should not carry a reference to a subject (e.g invoked as incoming
+ // property caused by a different user language)
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY && $subject->getSubobjectName() === '' ) {
+ $subject = DIProperty::newFromUserLabel( $subject->getDBKey() )->getCanonicalDiWikiPage();
+ }
+
+ // #1690 Couldn't match a CanonicalDiWikiPage which is most likely caused
+ // by an outdated pre-defined property therefore use the original subject
+ if ( $subject->getDBKey() === '' ) {
+ $subject = $semdata->getSubject();
+ }
+
+ // #649 Alwways make sure to have a least one valid sortkey
+ if ( !$semdata->getPropertyValues( new DIProperty( '_SKEY' ) ) && $subject->getSortKey() !== '' ) {
+
+ // @see SMWSQLStore3Writers::getSortKey
+ if ( $semdata->getExtensionData( 'sort.extension' ) !== null ) {
+ $sortkey = $semdata->getExtensionData( 'sort.extension' );
+ } else {
+ $sortkey = $subject->getSortKey();
+ }
+
+ // Extend the subobject sortkey in case no @sortkey was given for an
+ // entity
+ if ( $subject->getSubobjectName() !== '' ) {
+
+ // Add sort data from some dedicated containers (of a record or
+ // reference type etc.) otherwise use the sobj name as extension
+ // to distinguish each entity
+ if ( $semdata->getExtensionData( 'sort.data' ) !== null ) {
+ $sortkey .= '#' . $semdata->getExtensionData( 'sort.data' );
+ } else {
+ $sortkey .= '#' . $subject->getSubobjectName();
+ }
+ }
+
+ // #649 Be consistent about how sortkeys are stored therefore always
+ // normalize even for usages like {{DEFAULTSORT: Foo_bar }}
+ $sortkey = str_replace( '_', ' ', $sortkey );
+
+ $semdata->addPropertyObjectValue(
+ new DIProperty( '_SKEY' ),
+ new SMWDIBlob( $sortkey )
+ );
+ }
+
+ $result = self::makeExportDataForSubject( $subject );
+
+ foreach ( $semdata->getProperties() as $property ) {
+ self::addPropertyValues( $property, $semdata->getPropertyValues( $property ), $result, $subject );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Make an SMWExpData object for the given page, and include the basic
+ * properties about this subject that are not directly represented by
+ * SMW property values. The optional parameter $typevalueforproperty
+ * can be used to pass a particular SMWTypesValue object that is used
+ * for determining the OWL type for property pages.
+ *
+ * @todo Take into account whether the wiki page belongs to a builtin property, and ensure URI alignment/type declaration in this case.
+ *
+ * @param $diWikiPage SMWDIWikiPage
+ * @param $addStubData boolean to indicate if additional data should be added to make a stub entry for this page
+ * @return SMWExpData
+ */
+ static public function makeExportDataForSubject( SMWDIWikiPage $diWikiPage, $addStubData = false ) {
+ global $wgContLang;
+
+ $wikiPageExpElement = self::getDataItemExpElement( $diWikiPage );
+ $result = new SMWExpData( $wikiPageExpElement );
+
+ if ( $diWikiPage->getSubobjectName() !== '' ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'rdf', 'type' ),
+ self::getSpecialNsResource( 'swivt', 'Subject' )
+ );
+
+ $masterPage = new SMWDIWikiPage(
+ $diWikiPage->getDBkey(),
+ $diWikiPage->getNamespace(),
+ $diWikiPage->getInterwiki()
+ );
+
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'masterPage' ),
+ self::getDataItemExpElement( $masterPage )
+ );
+
+ // #649
+ // Subobjects contain there individual sortkey's therefore
+ // no need to add them twice
+
+ // #520
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'wikiNamespace' ),
+ new ExpLiteral( strval( $diWikiPage->getNamespace() ), 'http://www.w3.org/2001/XMLSchema#integer' )
+ );
+
+ } else {
+
+ // #1410 (use the display title as label when available)
+ $displayTitle = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage )->getDisplayTitle();
+
+ $pageTitle = str_replace( '_', ' ', $diWikiPage->getDBkey() );
+ if ( $diWikiPage->getNamespace() !== 0 ) {
+ $prefixedSubjectTitle = $wgContLang->getNsText( $diWikiPage->getNamespace()) . ":" . $pageTitle;
+ } else {
+ $prefixedSubjectTitle = $pageTitle;
+ }
+
+ $prefixedSubjectUrl = Escaper::encodeUri( str_replace( ' ', '_', $prefixedSubjectTitle ) );
+
+ switch ( $diWikiPage->getNamespace() ) {
+ case NS_CATEGORY: case SMW_NS_CONCEPT:
+ $maintype_pe = self::getSpecialNsResource( 'owl', 'Class' );
+ $label = $pageTitle;
+ break;
+ case SMW_NS_PROPERTY:
+ $property = new DIProperty( $diWikiPage->getDBKey() );
+ $maintype_pe = self::getSpecialNsResource( 'owl', self::getOWLPropertyType( $property->findPropertyTypeID() ) );
+ $label = $pageTitle;
+ break;
+ default:
+ $label = $prefixedSubjectTitle;
+ $maintype_pe = self::getSpecialNsResource( 'swivt', 'Subject' );
+ }
+
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), $maintype_pe );
+
+ if ( !$wikiPageExpElement->isBlankNode() ) {
+ $ed = new ExpLiteral( $displayTitle !== '' ? $displayTitle : $label );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'label' ), $ed );
+ $ed = new SMWExpResource( self::$m_exporturl . '/' . $prefixedSubjectUrl );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $ed = new SMWExpResource( self::getNamespaceUri( 'wikiurl' ) . $prefixedSubjectUrl );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'page' ), $ed );
+ $ed = new ExpLiteral( strval( $diWikiPage->getNamespace() ), 'http://www.w3.org/2001/XMLSchema#integer' );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'wikiNamespace' ), $ed );
+
+ if ( $addStubData ) {
+ // Add a default sort key; for pages that exist in the wiki,
+ // this is set during parsing
+ $property = new DIProperty( '_SKEY' );
+ $resourceBuilder = self::$dispatchingResourceBuilder->findResourceBuilder( $property );
+ $resourceBuilder->addResourceValue( $result, $property, $diWikiPage );
+ }
+
+ if ( $diWikiPage->getPageLanguage() ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'wikiPageContentLanguage' ),
+ new ExpLiteral( strval( $diWikiPage->getPageLanguage() ), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+ }
+
+ if ( $diWikiPage->getNamespace() === NS_FILE ) {
+
+ $title = Title::makeTitle( $diWikiPage->getNamespace(), $diWikiPage->getDBkey() );
+ $file = wfFindFile( $title );
+
+ if ( $file !== false ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'file' ),
+ new SMWExpResource( $file->getFullURL() )
+ );
+ }
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Extend a given SMWExpData element by adding export data for the
+ * specified property data itme. This method is called when
+ * constructing export data structures from SMWSemanticData objects.
+ *
+ * @param $property SMWDIProperty
+ * @param $dataItems array of SMWDataItem objects for the given property
+ * @param $data SMWExpData to add the data to
+ */
+ static public function addPropertyValues( SMWDIProperty $property, array $dataItems, SMWExpData &$expData ) {
+
+ $resourceBuilder = self::$dispatchingResourceBuilder->findResourceBuilder( $property );
+
+ if ( $property->isUserDefined() ) {
+ foreach ( $dataItems as $dataItem ) {
+ $resourceBuilder->addResourceValue( $expData, $property, $dataItem );
+ }
+ } else { // pre-defined property, only exported if known
+ $diSubject = $expData->getSubject()->getDataItem();
+ // subject wikipage required for disambiguating special properties:
+ if ( is_null( $diSubject ) ||
+ $diSubject->getDIType() != SMWDataItem::TYPE_WIKIPAGE ) {
+ return;
+ }
+
+ $pe = self::getSpecialPropertyResource( $property->getKey(), $diSubject->getNamespace() );
+ if ( is_null( $pe ) ) {
+ return; // unknown special property, not exported
+ }
+ // have helper property ready before entering the for loop, even if not needed:
+ $peHelper = self::getResourceElementForProperty( $property, true );
+
+ $filterNamespace = ( $property->getKey() == '_REDI' || $property->getKey() == '_URI' );
+
+ foreach ( $dataItems as $dataItem ) {
+ // Basic namespace filtering to ensure that types match for redirects etc.
+ /// TODO: currently no full check for avoiding OWL DL illegal redirects is done (OWL property type ignored)
+ if ( $filterNamespace && !( $dataItem instanceof SMWDIUri ) &&
+ ( !( $dataItem instanceof SMWDIWikiPage ) ) ) {
+ continue;
+ }
+
+ $resourceBuilder->addResourceValue( $expData, $property, $dataItem );
+ }
+ }
+ }
+
+ /**
+ * @see ExpResourceMapper::mapPropertyToResourceElement
+ */
+ static public function getResourceElementForProperty( SMWDIProperty $diProperty, $helperProperty = false, $seekImportVocabulary = true ) {
+ return self::$expResourceMapper->mapPropertyToResourceElement( $diProperty, $helperProperty, $seekImportVocabulary );
+ }
+
+ /**
+ * @see ExpResourceMapper::mapWikiPageToResourceElement
+ */
+ static public function getResourceElementForWikiPage( SMWDIWikiPage $diWikiPage, $markForAuxiliaryUsage = false ) {
+ return self::$expResourceMapper->mapWikiPageToResourceElement( $diWikiPage, $markForAuxiliaryUsage );
+ }
+
+ /**
+ * Try to find an SMWDataItem that the given ExpElement might
+ * represent. Returns null if this attempt failed.
+ *
+ * @param ExpElement $expElement
+ * @return SMWDataItem or null
+ */
+ public function findDataItemForExpElement( ExpElement $expElement ) {
+ return self::$dataItemMatchFinder->matchExpElement( $expElement );
+ }
+
+ /**
+ * Determine what kind of OWL property some SMW property should be exported as.
+ * The input is an SMWTypesValue object, a typeid string, or empty (use default)
+ * @todo An improved mechanism for selecting property types here is needed.
+ */
+ static public function getOWLPropertyType( $type = '' ) {
+ if ( $type instanceof SMWDIWikiPage ) {
+ $type = DataTypeRegistry::getInstance()->findTypeByLabel( str_replace( '_', ' ', $type->getDBkey() ) );
+ } elseif ( $type == false ) {
+ $type = '';
+ }
+
+ switch ( $type ) {
+ case '_anu':
+ return 'AnnotationProperty';
+ case '': case '_wpg': case '_wpp': case '_wpc': case '_wpf':
+ case '_uri': case '_ema': case '_tel': case '_rec': case '__typ':
+ case '__red': case '__spf': case '__spu':
+ return 'ObjectProperty';
+ default:
+ return 'DatatypeProperty';
+ }
+ }
+
+ /**
+ * Get an ExpNsResource for a special property of SMW, or null if
+ * no resource is assigned to the given property key. The optional
+ * namespace is used to select the proper resource for properties that
+ * must take the type of the annotated object into account for some
+ * reason.
+ *
+ * @param $propertyKey string the Id of the special property
+ * @param $forNamespace integer the namespace of the page which has a value for this property
+ * @return ExpNsResource|null
+ */
+ static public function getSpecialPropertyResource( $propertyKey, $forNamespace = NS_MAIN ) {
+ switch ( $propertyKey ) {
+ case '_INST':
+ return self::getSpecialNsResource( 'rdf', 'type' );
+ case '_SUBC':
+ return self::getSpecialNsResource( 'rdfs', 'subClassOf' );
+ case '_CONC':
+ return self::getSpecialNsResource( 'owl', 'equivalentClass' );
+ case '_URI':
+ if ( $forNamespace == NS_CATEGORY || $forNamespace == SMW_NS_CONCEPT ) {
+ return self::getSpecialNsResource( 'owl', 'equivalentClass' );
+ } elseif ( $forNamespace == SMW_NS_PROPERTY ) {
+ return self::getSpecialNsResource( 'owl', 'equivalentProperty' );
+ } else {
+ return self::getSpecialNsResource( 'owl', 'sameAs' );
+ }
+ case '_REDI':
+ return self::getSpecialNsResource( 'swivt', 'redirectsTo' );
+ case '_SUBP':
+ if ( $forNamespace == SMW_NS_PROPERTY ) {
+ return self::getSpecialNsResource( 'rdfs', 'subPropertyOf' );
+ } else {
+ return null;
+ }
+ case '_MDAT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageModificationDate' );
+ case '_CDAT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageCreationDate' );
+ case '_LEDT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageLastEditor' );
+ case '_NEWP':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageIsNew' );
+ case '_SKEY':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageSortKey' );
+ case '_TYPE':
+ return self::getSpecialNsResource( 'swivt', 'type' );
+ case '_IMPO':
+ return self::getSpecialNsResource( 'swivt', 'specialImportedFrom' );
+ default:
+ return self::getSpecialNsResource( 'swivt', 'specialProperty' . $propertyKey );
+ }
+ }
+
+
+ /**
+ * Create an ExpNsResource for some special element that belongs to
+ * a known vocabulary. An exception is generated when given parameters
+ * that do not fit any known vocabulary.
+ *
+ * @param $namespaceId string (e.g. "rdf")
+ * @param $localName string (e.g. "type")
+ * @return ExpNsResource
+ */
+ static public function getSpecialNsResource( $namespaceId, $localName ) {
+ $namespace = self::getNamespaceUri( $namespaceId );
+ if ( $namespace !== '' ) {
+ return new ExpNsResource( $localName, $namespace, $namespaceId );
+ } else {
+ throw new InvalidArgumentException( "The vocabulary '$namespaceId' is not a known special vocabulary." );
+ }
+ }
+
+ /**
+ * This function expands standard XML entities used in some generated
+ * URIs. Given a string with such entities, it returns a string with
+ * all entities properly replaced.
+ *
+ * @note The function SMWExporter::getInstance()->getNamespaceUri() is often more
+ * suitable. This XML-specific method might become obsolete.
+ *
+ * @param $uri string of the URI to be expanded
+ * @return string of the expanded URI
+ */
+ static public function expandURI( $uri ) {
+ self::initBaseURIs();
+ $uri = str_replace( [ '&wiki;', '&wikiurl;', '&property;', '&category;', '&owl;', '&rdf;', '&rdfs;', '&swivt;', '&export;' ],
+ [ self::$m_ent_wiki, self::$m_ent_wikiurl, self::$m_ent_property, self::$m_ent_category, 'http://www.w3.org/2002/07/owl#', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'http://www.w3.org/2000/01/rdf-schema#', 'http://semantic-mediawiki.org/swivt/1.0#',
+ self::$m_exporturl ],
+ $uri );
+ return $uri;
+ }
+
+ /**
+ * @return string
+ */
+ public function decodeURI( $uri ) {
+ return Escaper::decodeUri( $uri );
+ }
+
+ /**
+ * Get the URI of a standard namespace prefix used in SMW, or the empty
+ * string if the prefix is not known.
+ *
+ * @param $shortName string id (prefix) of the namespace
+ * @return string of the expanded URI
+ */
+ static public function getNamespaceUri( $shortName ) {
+ self::initBaseURIs();
+
+ if ( ( $uri = NamespaceUriFinder::getUri( $shortName ) ) !== false ) {
+ return $uri;
+ }
+
+ switch ( $shortName ) {
+ case 'wiki':
+ return self::$m_ent_wiki;
+ case 'wikiurl':
+ return self::$m_ent_wikiurl;
+ case 'property':
+ return self::$m_ent_property;
+ case 'category':
+ return self::$m_ent_category;
+ case 'export':
+ return self::$m_exporturl;
+ case 'owl':
+ return 'http://www.w3.org/2002/07/owl#';
+ case 'rdf':
+ return 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+ case 'rdfs':
+ return 'http://www.w3.org/2000/01/rdf-schema#';
+ case 'swivt':
+ return 'http://semantic-mediawiki.org/swivt/1.0#';
+ case 'xsd':
+ return 'http://www.w3.org/2001/XMLSchema#';
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Create an SMWExpData container that encodes the ontology header for an
+ * SMW exported OWL file.
+ *
+ * @param string $ontologyuri specifying the URI of the ontology, possibly
+ * empty
+ *
+ * @return SMWExpData
+ */
+ static public function getOntologyExpData( $ontologyuri ) {
+ $data = new SMWExpData( new SMWExpResource( $ontologyuri ) );
+ $ed = self::getSpecialNsResource( 'owl', 'Ontology' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new ExpLiteral( date( DATE_W3C ), 'http://www.w3.org/2001/XMLSchema#dateTime' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'creationDate' ), $ed );
+ $ed = new SMWExpResource( 'http://semantic-mediawiki.org/swivt/1.0' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'owl', 'imports' ), $ed );
+ return $data;
+ }
+
+ /**
+ * @see ElementFactory::mapDataItemToElement
+ */
+ static public function getDataItemExpElement( SMWDataItem $dataItem ) {
+ return self::$elementFactory->newFromDataItem( $dataItem );
+ }
+
+ /**
+ * Create an SWMExpElement that encodes auxiliary data for representing
+ * values of the specified dataitem object in a simplified fashion.
+ * This is done for types of dataitems that are not supported very well
+ * in current systems, or that do not match a standard datatype in RDF.
+ * For example, time points (DITime) are encoded as numbers. The number
+ * can replace the actual time for all query and ordering purposes (the
+ * order in either case is linear and maps to the real number line).
+ * Only data retrieval should better use the real values to avoid that
+ * rounding errors lead to unfaithful recovery of data. Note that the
+ * helper values do not maintain any association with their original
+ * values -- they are a fully redundant alternative representation, not
+ * an additional piece of information for the main values. Even if
+ * decoding is difficult, they must be in one-to-one correspondence to
+ * the original value.
+ *
+ * For dataitems that do not have such a simplification, the method
+ * returns null.
+ *
+ * @note If a helper element is used, then it must be the same as
+ * getDataItemHelperExpElement( $dataItem->getSortKeyDataItem() ).
+ * Query conditions like ">" use sortkeys for values, and helper
+ * elements are always preferred in query answering.
+ *
+ * @param $dataItem SMWDataItem
+ * @return ExpElement|null
+ */
+ static public function getDataItemHelperExpElement( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_TIME ) {
+ return new ExpLiteral( (string)$dataItem->getSortKey(), 'http://www.w3.org/2001/XMLSchema#double', '', $dataItem );
+ }
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_GEO ) {
+ return new ExpLiteral( (string)$dataItem->getSortKey(), 'http://www.w3.org/2001/XMLSchema#string', '', $dataItem );
+ }
+
+ return null;
+ }
+
+ /**
+ * Check whether the values of a given type of dataitem have helper
+ * values in the sense of SMWExporter::getInstance()->getDataItemHelperExpElement().
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ static public function hasHelperExpElement( DIProperty $property ) {
+ return ( $property->findPropertyTypeID() === '_dat' || $property->findPropertyTypeID() === '_geo' ) || ( !$property->isUserDefined() && !self::hasSpecialPropertyResource( $property ) );
+ }
+
+ static protected function hasSpecialPropertyResource( DIProperty $property ) {
+ return $property->getKey() === '_SKEY' ||
+ $property->getKey() === '_INST' ||
+ $property->getKey() === '_MDAT' ||
+ $property->getKey() === '_SUBC' ||
+ $property->getKey() === '_SUBP' ||
+ $property->getKey() === '_TYPE' ||
+ $property->getKey() === '_IMPO' ||
+ $property->getKey() === '_URI';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php
new file mode 100644
index 00000000..775d1b42
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php
@@ -0,0 +1,333 @@
+<?php
+
+/**
+ * File holding the SMWSerializer class that provides basic functions for
+ * serialising data in OWL and RDF syntaxes.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+define( 'SMW_SERIALIZER_DECL_CLASS', 1 );
+define( 'SMW_SERIALIZER_DECL_OPROP', 2 );
+define( 'SMW_SERIALIZER_DECL_APROP', 4 );
+
+/**
+ * Abstract class for serializing exported data (encoded as SMWExpData object)
+ * in a concrete syntactic format such as Turtle or RDF/XML. The serializer
+ * adds object serialisations to an internal string that can be retrieved for
+ * pushing it to an output. This abstract class does not define this string as
+ * implementations may want to use their own scheme (e.g. using two buffers as
+ * in the case of SMWRDFXMLSerializer). The function flushContent() returns the
+ * string serialized so far so as to enable incremental serialization.
+ *
+ * RDF and OWL have two types of dependencies that are managed by this class:
+ * namespaces (and similar abbreviation schemes) and element declarations.
+ * The former need to be defined before being used, while the latter can occur
+ * at some later point in the serialization. Declarations are relevant to the
+ * OWL data model, being one of Class, DatatypeProperty, and ObjectProperty
+ * (only the latter two are mutually exclusive). This class determines the
+ * required declaration from the context in which an element is used.
+ *
+ * @ingroup SMW
+ */
+abstract class SMWSerializer {
+ /**
+ * The current working string is obtained by concatenating the strings
+ * $pre_ns_buffer and $post_ns_buffer. The split between the two is such
+ * that one can append additional namespace declarations to $pre_ns_buffer
+ * so that they affect all current elements. The buffers are flushed during
+ * output in order to achieve "streaming" RDF export for larger files.
+ * @var string
+ */
+ protected $pre_ns_buffer;
+ /**
+ * See documentation for $pre_ns_buffer.
+ * @var string
+ */
+ protected $post_ns_buffer;
+ /**
+ * Array for recording required declarations; format:
+ * resourcename => decl-flag, where decl-flag is a sum of flags
+ * SMW_SERIALIZER_DECL_CLASS, SMW_SERIALIZER_DECL_OPROP,
+ * SMW_SERIALIZER_DECL_APROP.
+ * @var array of integer
+ */
+ protected $decl_todo;
+ /**
+ * Array for recording previous declarations; format:
+ * resourcename => decl-flag, where decl-flag is a sum of flags
+ * SMW_SERIALIZER_DECL_CLASS, SMW_SERIALIZER_DECL_OPROP,
+ * SMW_SERIALIZER_DECL_APROP.
+ * @var array of integer
+ */
+ protected $decl_done;
+ /**
+ * Array of additional namespaces (abbreviation => URI), flushed on
+ * closing the current namespace tag. Since we export in a streamed
+ * way, it is not always possible to embed additional namespaces into
+ * a syntactic block (e.g. an RDF/XML tag) which might have been sent to
+ * the client already. But we wait with printing the current block so that
+ * extra namespaces from this array can still be printed (note that one
+ * never know which extra namespaces you encounter during export).
+ * @var array of string
+ */
+ protected $extra_namespaces;
+ /**
+ * Array of namespaces that have been declared globally already. Contains
+ * entries of format 'namespace abbreviation' => true, assuming that the
+ * same abbreviation always refers to the same URI.
+ * @var array of string
+ */
+ protected $global_namespaces;
+
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ $this->clear();
+ }
+
+ /**
+ * Clear internal states to start a new serialization.
+ */
+ public function clear() {
+ $this->pre_ns_buffer = '';
+ $this->post_ns_buffer = '';
+ $this->decl_todo = [];
+ $this->decl_done = [];
+ $this->global_namespaces = [];
+ $this->extra_namespaces = [];
+ }
+
+ /**
+ * Start a new serialization, resetting all internal data and serializing
+ * necessary header elements.
+ */
+ public function startSerialization() {
+ $this->clear();
+ $this->serializeHeader();
+ }
+
+ /**
+ * Complete the serialization so that calling flushContent() will return
+ * the final part of the output, leading to a complete serialization with
+ * all necessary declarations. No further serialization functions must be
+ * called after this.
+ */
+ public function finishSerialization() {
+ $this->serializeDeclarations();
+ $this->serializeFooter();
+ }
+
+ /**
+ * Serialize the header (i.e. write it to the internal buffer). May
+ * include standard syntax to start output but also declare some common
+ * namespaces globally.
+ */
+ abstract protected function serializeHeader();
+
+ /**
+ * Serialise the footer (i.e. write it to the internal buffer).
+ */
+ abstract protected function serializeFooter();
+
+ /**
+ * Serialize any declarations that have been found to be missing while
+ * serializing other elements.
+ */
+ public function serializeDeclarations() {
+ foreach ( $this->decl_todo as $name => $flag ) {
+ $types = [];
+ if ( $flag & SMW_SERIALIZER_DECL_CLASS ) {
+ $types[] = 'owl:Class';
+ }
+ if ( $flag & SMW_SERIALIZER_DECL_OPROP ) {
+ $types[] = 'owl:ObjectProperty';
+ }
+ if ( $flag & SMW_SERIALIZER_DECL_APROP ) {
+ $types[] = 'owl:DatatypeProperty';
+ }
+ foreach ( $types as $typename ) {
+ $this->serializeDeclaration( $name, $typename );
+ }
+ $curdone = array_key_exists( $name, $this->decl_done ) ? $this->decl_done[$name] : 0;
+ $this->decl_done[$name] = $curdone | $flag;
+ }
+ $this->decl_todo = []; // reset all
+ }
+
+ /**
+ * Serialize a single declaration for the given $uri (expanded) and type
+ * (given as a QName).
+ * @param $uri string URI of the thing to declare
+ * @param $typename string one of owl:Class, owl:ObjectProperty, and
+ * owl:datatypeProperty
+ */
+ abstract public function serializeDeclaration( $uri, $typename );
+
+ /**
+ * Serialise the given SMWExpData object. The method must not assume that
+ * the exported data refers to wiki pages or other SMW data, and it must
+ * ensure that all required auxiliary declarations for obtaining proper OWL
+ * are included in any case (this can be done using requireDeclaration()).
+ *
+ * @param $data SMWExpData containing the data to be serialised.
+ */
+ abstract public function serializeExpData( SMWExpData $data );
+
+ /**
+ * Get the string that has been serialized so far. This function also
+ * resets the internal buffers for serilized strings and namespaces
+ * (what is flushed is gone).
+ */
+ public function flushContent() {
+ if ( ( $this->pre_ns_buffer === '' ) && ( $this->post_ns_buffer === '' ) ) {
+ return '';
+ }
+ $this->serializeNamespaces();
+ $result = $this->pre_ns_buffer . $this->post_ns_buffer;
+ $this->pre_ns_buffer = '';
+ $this->post_ns_buffer = '';
+ return $result;
+ }
+
+ /**
+ * Include collected namespace information into the serialization.
+ */
+ protected function serializeNamespaces() {
+ foreach ( $this->extra_namespaces as $nsshort => $nsuri ) {
+ $this->serializeNamespace( $nsshort, $nsuri );
+ }
+ $this->extra_namespaces = [];
+ }
+
+ /**
+ * Serialize a single namespace.
+ * Namespaces that were serialized in such a way that they remain
+ * available for all following output should be added to
+ * $global_namespaces.
+ * @param $shortname string abbreviation/prefix to declare
+ * @param $uri string URI prefix that the namespace encodes
+ */
+ abstract protected function serializeNamespace( $shortname, $uri );
+
+ /**
+ * Require an additional namespace to be declared in the serialization.
+ * The function checks whether the required namespace is available globally
+ * and add it to the list of required namespaces otherwise.
+ */
+ protected function requireNamespace( $nsshort, $nsuri ) {
+ if ( !array_key_exists( $nsshort, $this->global_namespaces ) ) {
+ $this->extra_namespaces[$nsshort] = $nsuri;
+ }
+ }
+
+ /**
+ * State that a certain declaration is needed. The method checks if the
+ * declaration is already available, and records a todo otherwise.
+ */
+ protected function requireDeclaration( SMWExpResource $resource, $decltype ) {
+ // Do not declare predefined OWL language constructs:
+ if ( $resource instanceof SMWExpNsResource ) {
+ $nsId = $resource->getNamespaceId();
+ if ( ( $nsId == 'owl' ) || ( $nsId == 'rdf' ) || ( $nsId == 'rdfs' ) ) {
+ return;
+ }
+ }
+ // Do not declare blank nodes:
+ if ( $resource->isBlankNode() ) {
+ return;
+ }
+
+ $name = $resource->getUri();
+ if ( array_key_exists( $name, $this->decl_done ) && ( $this->decl_done[$name] & $decltype ) ) {
+ return;
+ }
+ if ( !array_key_exists( $name, $this->decl_todo ) ) {
+ $this->decl_todo[$name] = $decltype;
+ } else {
+ $this->decl_todo[$name] = $this->decl_todo[$name] | $decltype;
+ }
+ }
+
+ /**
+ * Update the declaration "todo" and "done" lists for the case that the
+ * given data has been serialized with the type information it provides.
+ *
+ * @param $expData specifying the type data upon which declarations are based
+ */
+ protected function recordDeclarationTypes( SMWExpData $expData ) {
+ foreach ( $expData->getSpecialValues( 'rdf', 'type') as $typeresource ) {
+ if ( $typeresource instanceof SMWExpNsResource ) {
+ switch ( $typeresource->getQName() ) {
+ case 'owl:Class': $typeflag = SMW_SERIALIZER_DECL_CLASS;
+ break;
+ case 'owl:ObjectProperty': $typeflag = SMW_SERIALIZER_DECL_OPROP;
+ break;
+ case 'owl:DatatypeProperty': $typeflag = SMW_SERIALIZER_DECL_APROP;
+ break;
+ default: $typeflag = 0;
+ }
+ if ( $typeflag != 0 ) {
+ $this->declarationDone( $expData->getSubject(), $typeflag );
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the declaration "todo" and "done" lists to reflect the fact that
+ * the given element has been declared to has the given type.
+ *
+ * @param $element SMWExpResource specifying the element to update
+ * @param $typeflag integer specifying the type (e.g. SMW_SERIALIZER_DECL_CLASS)
+ */
+ protected function declarationDone( SMWExpResource $element, $typeflag ) {
+ $name = $element->getUri();
+ $curdone = array_key_exists( $name, $this->decl_done ) ? $this->decl_done[$name] : 0;
+ $this->decl_done[$name] = $curdone | $typeflag;
+ if ( array_key_exists( $name, $this->decl_todo ) ) {
+ $this->decl_todo[$name] = $this->decl_todo[$name] & ( ~$typeflag );
+ if ( $this->decl_todo[$name] == 0 ) {
+ unset( $this->decl_todo[$name] );
+ }
+ }
+ }
+
+ /**
+ * Check if the given property is one of the special properties of the OWL
+ * language that require their values to be classes or RDF lists of
+ * classes. In these cases, it is necessary to declare this in the exported
+ * data.
+ *
+ * @note The list of properties checked here is not complete for the OWL
+ * language but covers what is used in SMW.
+ * @note OWL 2 allows URIs to refer to both classes and individual elements
+ * in different contexts. We only need declarations for classes that are
+ * used as such, hence it is enough to check the property. Moreover, we do
+ * not use OWL Datatypes in SMW, so rdf:type, rdfs:domain, etc. always
+ * refer to classes.
+ *
+ * @param SMWExpNsResource $property
+ *
+ * @return boolean
+ */
+ protected function isOWLClassTypeProperty( SMWExpNsResource $property ) {
+ $locname = $property->getLocalName();
+ if ( $property->getNamespaceID() == 'rdf' ) {
+ return ( $locname == 'type' );
+ } elseif ( $property->getNamespaceID() == 'owl' ) {
+ return ( $locname == 'intersectionOf' ) || ( $locname == 'unionOf' ) ||
+ ( $locname == 'equivalentClass' ) ||
+ ( $locname == 'complementOf' ) || ( $locname == 'someValuesFrom' ) ||
+ ( $locname == 'allValuesFrom' ) || ( $locname == 'onClass' );
+ } elseif ( $property->getNamespaceID() == 'rdfs' ) {
+ return ( $locname == 'subClassOf' ) || ( $locname == 'range' ) || ( $locname == 'domain' );
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php
new file mode 100644
index 00000000..4848dcf0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * File holding the SMWRDFXMLSerializer class that provides basic functions for
+ * serialising OWL data in RDF/XML syntax.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for serializing exported data (encoded as SMWExpData object) in
+ * RDF/XML.
+ *
+ * @ingroup SMW
+ */
+class SMWRDFXMLSerializer extends SMWSerializer {
+ /**
+ * True if the $pre_ns_buffer contains the beginning of a namespace
+ * declaration block to which further declarations for the current
+ * context can be appended.
+ */
+ protected $namespace_block_started;
+ /**
+ * True if the namespaces that are added at the current serialization stage
+ * become global, i.e. remain available for all later contexts. This is the
+ * case in RDF/XML only as long as the header has not been streamed to the
+ * client (reflected herein by calling flushContent()). Later, namespaces
+ * can only be added locally to individual elements, thus requiring them to
+ * be re-added multiple times if used in many elements.
+ */
+ protected $namespaces_are_global;
+
+ public function clear() {
+ parent::clear();
+ $this->namespaces_are_global = false;
+ $this->namespace_block_started = false;
+ }
+
+ protected function serializeHeader() {
+ $this->namespaces_are_global = true;
+ $this->namespace_block_started = true;
+ $this->pre_ns_buffer =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" .
+ "<!DOCTYPE rdf:RDF[\n" .
+ "\t<!ENTITY rdf " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdf;' ) ) . ">\n" .
+ "\t<!ENTITY rdfs " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdfs;' ) ) . ">\n" .
+ "\t<!ENTITY owl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&owl;' ) ) . ">\n" .
+ "\t<!ENTITY swivt " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&swivt;' ) ) . ">\n" .
+ // A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
+ // In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
+ "\t<!ENTITY wiki " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wiki;' ) ) . ">\n" .
+ "\t<!ENTITY category " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&category;' ) ) . ">\n" .
+ "\t<!ENTITY property " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&property;' ) ) . ">\n" .
+ "\t<!ENTITY wikiurl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wikiurl;' ) ) . ">\n" .
+ "]>\n\n" .
+ "<rdf:RDF\n" .
+ "\txmlns:rdf=\"&rdf;\"\n" .
+ "\txmlns:rdfs=\"&rdfs;\"\n" .
+ "\txmlns:owl =\"&owl;\"\n" .
+ "\txmlns:swivt=\"&swivt;\"\n" .
+ "\txmlns:wiki=\"&wiki;\"\n" .
+ "\txmlns:category=\"&category;\"\n" .
+ "\txmlns:property=\"&property;\"";
+ $this->global_namespaces = [ 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true ];
+ $this->post_ns_buffer .= ">\n\n";
+ }
+
+ protected function serializeFooter() {
+ $this->post_ns_buffer .= "\t<!-- Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/ -->\n";
+ $this->post_ns_buffer .= '</rdf:RDF>';
+ }
+
+ public function serializeDeclaration( $uri, $typename ) {
+ $this->post_ns_buffer .= "\t<$typename rdf:about=\"$uri\" />\n";
+ }
+
+ public function serializeExpData( SMWExpData $expData ) {
+ $this->serializeNestedExpData( $expData, '' );
+ $this->serializeNamespaces();
+ if ( !$this->namespaces_are_global ) {
+ $this->pre_ns_buffer .= $this->post_ns_buffer;
+ $this->post_ns_buffer = '';
+ $this->namespace_block_started = false;
+ }
+ }
+
+ public function flushContent() {
+ $result = parent::flushContent();
+ $this->namespaces_are_global = false; // must not be done before calling the parent method (which may declare namespaces)
+ $this->namespace_block_started = false;
+ return $result;
+ }
+
+ protected function serializeNamespace( $shortname, $uri ) {
+ if ( $this->namespaces_are_global ) {
+ $this->global_namespaces[$shortname] = true;
+ $this->pre_ns_buffer .= "\n\t";
+ } else {
+ $this->pre_ns_buffer .= ' ';
+ }
+ $this->pre_ns_buffer .= "xmlns:$shortname=\"$uri\"";
+ }
+
+ /**
+ * Serialize the given SMWExpData object, possibly recursively with
+ * increased indentation.
+ *
+ * @param $expData SMWExpData containing the data to be serialised.
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeNestedExpData( SMWExpData $expData, $indent ) {
+ $this->recordDeclarationTypes( $expData );
+
+ $type = $expData->extractMainType()->getQName();
+ if ( !$this->namespace_block_started ) { // start new ns block
+ $this->pre_ns_buffer .= "\t$indent<$type";
+ $this->namespace_block_started = true;
+ } else { // continue running block
+ $this->post_ns_buffer .= "\t$indent<$type";
+ }
+
+ if ( ( $expData->getSubject() instanceof SMWExpResource ) &&
+ !$expData->getSubject()->isBlankNode() ) {
+ $this->post_ns_buffer .= ' rdf:about="' . $expData->getSubject()->getUri() . '"';
+ } // else: blank node, no "rdf:about"
+
+ if ( count( $expData->getProperties() ) == 0 ) { // nothing else to export
+ $this->post_ns_buffer .= " />\n";
+ } else { // process data
+ $this->post_ns_buffer .= ">\n";
+
+ foreach ( $expData->getProperties() as $property ) {
+ $prop_decl_queued = false;
+ $isClassTypeProp = $this->isOWLClassTypeProperty( $property );
+
+ foreach ( $expData->getValues( $property ) as $valueElement ) {
+ $this->requireNamespace( $property->getNamespaceID(), $property->getNamespace() );
+
+ if ( $valueElement instanceof SMWExpLiteral ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_APROP;
+ $this->serializeExpLiteral( $property, $valueElement, "\t\t$indent" );
+ } elseif ( $valueElement instanceof SMWExpResource ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $this->serializeExpResource( $property, $valueElement, "\t\t$indent", $isClassTypeProp );
+ } elseif ( $valueElement instanceof SMWExpData ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+
+ $collection = $valueElement->getCollection();
+ if ( $collection !== false ) { // RDF-style collection (list)
+ $this->serializeExpCollection( $property, $collection, "\t\t$indent", $isClassTypeProp );
+ } elseif ( count( $valueElement->getProperties() ) > 0 ) { // resource with data
+ $this->post_ns_buffer .= "\t\t$indent<" . $property->getQName() . ">\n";
+ $this->serializeNestedExpData( $valueElement, "\t\t$indent" );
+ $this->post_ns_buffer .= "\t\t$indent</" . $property->getQName() . ">\n";
+ } else { // resource without data
+ $this->serializeExpResource( $property, $valueElement->getSubject(), "\t\t$indent", $isClassTypeProp );
+ }
+ } // else: no other types of export elements
+
+ if ( !$prop_decl_queued ) {
+ $this->requireDeclaration( $property, $prop_decl_type );
+ $prop_decl_queued = true;
+ }
+ }
+ }
+ $this->post_ns_buffer .= "\t$indent</" . $type . ">\n";
+ }
+ }
+
+ /**
+ * Add to the output a serialization of a property assignment where an
+ * SMWExpLiteral is the object. It is assumed that a suitable subject
+ * block has already been openend.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expLiteral SMWExpLiteral the data value to use
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeExpLiteral( SMWExpNsResource $expResourceProperty, SMWExpLiteral $expLiteral, $indent ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
+
+ // https://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-languages
+ // "... to indicate that the included content is in the given language.
+ // Typed literals which includes XML literals are not affected by this
+ // attribute. The most specific in-scope language present (if any) is
+ // applied to property element string literal ..."
+ if ( $expLiteral->getDatatype() !== '' && $expLiteral->getLang() !== '' ) {
+ $this->post_ns_buffer .= ' xml:lang="' . $expLiteral->getLang() . '"';
+ } elseif ( $expLiteral->getDatatype() !== '' ) {
+ $this->post_ns_buffer .= ' rdf:datatype="' . $expLiteral->getDatatype() . '"';
+ }
+
+ $this->post_ns_buffer .= '>' . $this->makeAttributeValueString( $expLiteral->getLexicalForm() );
+ $this->post_ns_buffer .= '</' . $expResourceProperty->getQName() . ">\n";
+ }
+
+ /**
+ * Add to the output a serialization of a property assignment where an
+ * SMWExpResource is the object. It is assumed that a suitable subject
+ * block has already been openend.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expResource SMWExpResource the data value to use
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ * @param $isClassTypeProp boolean whether the resource must be declared as a class
+ */
+ protected function serializeExpResource( SMWExpNsResource $expResourceProperty, SMWExpResource $expResource, $indent, $isClassTypeProp ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
+ if ( !$expResource->isBlankNode() ) {
+ if ( ( $expResource instanceof SMWExpNsResource ) && ( $expResource->getNamespaceID() == 'wiki' ) ) {
+ // very common case, reduce bandwidth
+ $this->post_ns_buffer .= ' rdf:resource="&wiki;' . $expResource->getLocalName() . '"';
+ } else {
+ $uriValue = $this->makeAttributeValueString( $expResource->getUri() );
+ $this->post_ns_buffer .= ' rdf:resource="' . $uriValue . '"';
+ }
+ }
+ $this->post_ns_buffer .= "/>\n";
+ if ( $isClassTypeProp ) {
+ $this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+
+ /**
+ * Add a serialization of the given SMWExpResource to the output,
+ * assuming that an opening property tag is alerady there.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expResource array of (SMWExpResource or SMWExpData)
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ * @param $isClassTypeProp boolean whether the resource must be declared as a class
+ *
+ * @bug The $isClassTypeProp parameter is not properly taken into account.
+ * @bug Individual resources are not serialised properly.
+ */
+ protected function serializeExpCollection( SMWExpNsResource $expResourceProperty, array $collection, $indent, $isClassTypeProp ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName() . " rdf:parseType=\"Collection\">\n";
+ foreach ( $collection as $expElement ) {
+ if ( $expElement instanceof SMWExpData ) {
+ $this->serializeNestedExpData( $expElement, $indent );
+ } else {
+ // FIXME: the below is not the right thing to do here
+ //$this->serializeExpResource( $expResourceProperty, $expElement, $indent );
+ }
+ if ( $isClassTypeProp ) {
+ // FIXME: $expResource is undefined
+ //$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+ $this->post_ns_buffer .= "$indent</" . $expResourceProperty->getQName() . ">\n";
+ }
+
+ /**
+ * Escape a string in the special form that is required for values in
+ * DTD entity declarations in XML. Namely, this require the percent sign
+ * to be replaced.
+ *
+ * @param $string string to be escaped
+ * @return string
+ */
+ protected function makeValueEntityString( $string ) {
+ return "'" . str_replace( '%', '&#37;', $string ) . "'";
+ }
+
+ /**
+ * Escape a string as required for using it in XML attribute values.
+ *
+ * @param $string string to be escaped
+ * @return string
+ */
+ protected function makeAttributeValueString( $string ) {
+ return str_replace( [ '&', '>', '<' ], [ '&amp;', '&gt;', '&lt;' ], $string );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php
new file mode 100644
index 00000000..d7a6ff2a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php
@@ -0,0 +1,294 @@
+<?php
+
+use SMW\InMemoryPoolCache;
+
+/**
+ * File holding the SMWTurtleSerializer class that provides basic functions for
+ * serialising OWL data in Turtle syntax.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for serializing exported data (encoded as SMWExpData object) in
+ * Turtle syntax.
+ *
+ * @ingroup SMW
+ */
+class SMWTurtleSerializer extends SMWSerializer {
+ /**
+ * Array of non-trivial sub-SMWExpData elements that cannot be nested while
+ * serializing some SMWExpData. The elements of the array are serialized
+ * later during the same serialization step (so this is not like another
+ * queue for declarations or the like; it just unfolds an SMWExpData
+ * object).
+ *
+ * @var array of SMWExpData
+ */
+ protected $subexpdata;
+
+ /**
+ * If true, do not serialize namespace declarations and record them in
+ * $sparql_namespaces instead for later retrieval.
+ * @var boolean
+ */
+ protected $sparqlmode;
+
+ /**
+ * Array of retrieved namespaces (abbreviation => URI) for later use.
+ * @var array of string
+ */
+ protected $sparql_namespaces;
+
+ public function __construct( $sparqlMode = false ) {
+ parent::__construct();
+ $this->sparqlmode = $sparqlMode;
+ }
+
+ public function clear() {
+ parent::clear();
+ $this->sparql_namespaces = [];
+ }
+
+ /**
+ * @since 2.3
+ */
+ public static function reset() {
+ InMemoryPoolCache::getInstance()->resetPoolCacheById( 'turtle.serializer' );
+ }
+
+ /**
+ * Get an array of namespace prefixes used in SPARQL mode.
+ * Namespaces are not serialized among triples in SPARQL mode but are
+ * collected separately. This method returns the prefixes and empties
+ * the collected list afterwards.
+ *
+ * @return array shortName => namespace URI
+ */
+ public function flushSparqlPrefixes() {
+ $result = $this->sparql_namespaces;
+ $this->sparql_namespaces = [];
+ return $result;
+ }
+
+ protected function serializeHeader() {
+ if ( $this->sparqlmode ) {
+ $this->pre_ns_buffer = '';
+ $this->sparql_namespaces = [
+ "rdf" => SMWExporter::getInstance()->expandURI( '&rdf;' ),
+ "rdfs" => SMWExporter::getInstance()->expandURI( '&rdfs;' ),
+ "owl" => SMWExporter::getInstance()->expandURI( '&owl;' ),
+ "swivt" => SMWExporter::getInstance()->expandURI( '&swivt;' ),
+ "wiki" => SMWExporter::getInstance()->expandURI( '&wiki;' ),
+ "category" => SMWExporter::getInstance()->expandURI( '&category;' ),
+ "property" => SMWExporter::getInstance()->expandURI( '&property;' ),
+ "xsd" => "http://www.w3.org/2001/XMLSchema#" ,
+ "wikiurl" => SMWExporter::getInstance()->expandURI( '&wikiurl;' )
+ ];
+ } else {
+ $this->pre_ns_buffer =
+ "@prefix rdf: <" . SMWExporter::getInstance()->expandURI( '&rdf;' ) . "> .\n" .
+ "@prefix rdfs: <" . SMWExporter::getInstance()->expandURI( '&rdfs;' ) . "> .\n" .
+ "@prefix owl: <" . SMWExporter::getInstance()->expandURI( '&owl;' ) . "> .\n" .
+ "@prefix swivt: <" . SMWExporter::getInstance()->expandURI( '&swivt;' ) . "> .\n" .
+ // A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
+ // In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
+ "@prefix wiki: <" . SMWExporter::getInstance()->expandURI( '&wiki;' ) . "> .\n" .
+ "@prefix category: <" . SMWExporter::getInstance()->expandURI( '&category;' ) . "> .\n" .
+ "@prefix property: <" . SMWExporter::getInstance()->expandURI( '&property;' ) . "> .\n" .
+ "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n" . // note that this XSD URI is hardcoded below (its unlikely to change, of course)
+ "@prefix wikiurl: <" . SMWExporter::getInstance()->expandURI( '&wikiurl;' ) . "> .\n";
+ }
+ $this->global_namespaces = [ 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true ];
+ $this->post_ns_buffer = "\n";
+ }
+
+ protected function serializeFooter() {
+ if ( !$this->sparqlmode ) {
+ $this->post_ns_buffer .= "\n# Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/\n";
+ }
+ }
+
+ public function serializeDeclaration( $uri, $typename ) {
+ $this->post_ns_buffer .= "<" . SMWExporter::getInstance()->expandURI( $uri ) . "> rdf:type $typename .\n";
+ }
+
+ public function serializeExpData( SMWExpData $expData ) {
+
+ $this->subExpData = [ $expData ];
+
+ while ( count( $this->subExpData ) > 0 ) {
+ $this->serializeNestedExpData( array_pop( $this->subExpData ), '' );
+ }
+
+ $this->serializeNamespaces();
+ }
+
+ protected function serializeNamespace( $shortname, $uri ) {
+ $this->global_namespaces[$shortname] = true;
+ if ( $this->sparqlmode ) {
+ $this->sparql_namespaces[$shortname] = $uri;
+ } else {
+ $this->pre_ns_buffer .= "@prefix $shortname: <$uri> .\n";
+ }
+ }
+
+ /**
+ * Serialize the given SMWExpData object, possibly recursively with
+ * increased indentation.
+ *
+ * @param $data SMWExpData containing the data to be serialised.
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeNestedExpData( SMWExpData $data, $indent ) {
+ if ( count( $data->getProperties() ) == 0 ) {
+ return; // nothing to export
+ }
+
+ // Avoid posting turtle property declarations already known for the
+ // subject more than once
+ if ( $data->getSubject()->getDataItem() !== null && $data->getSubject()->getDataItem()->getNamespace() === SMW_NS_PROPERTY ) {
+
+ $hash = $data->getHash();
+ $poolCache = InMemoryPoolCache::getInstance()->getPoolCacheById( 'turtle.serializer' );
+
+ if ( $poolCache->contains( $hash ) && $poolCache->fetch( $hash ) ) {
+ return;
+ }
+
+ $poolCache->save( $hash, true );
+ }
+
+ $this->recordDeclarationTypes( $data );
+
+ $bnode = false;
+ $this->post_ns_buffer .= $indent;
+ if ( !$data->getSubject()->isBlankNode() ) {
+ $this->serializeExpResource( $data->getSubject() );
+ } else { // blank node
+ $bnode = true;
+ $this->post_ns_buffer .= "[";
+ }
+
+ if ( ( $indent !== '' ) && ( !$bnode ) ) { // called to generate a nested descripion; but Turtle cannot nest non-bnode descriptions, do this later
+ $this->subexpdata[] = $data;
+ return;
+ } elseif ( !$bnode ) {
+ $this->post_ns_buffer .= "\n ";
+ }
+
+ $firstproperty = true;
+ foreach ( $data->getProperties() as $property ) {
+ $this->post_ns_buffer .= $firstproperty ? "\t" : " ;\n $indent\t";
+ $firstproperty = false;
+ $prop_decl_queued = false;
+ $class_type_prop = $this->isOWLClassTypeProperty( $property );
+ $this->serializeExpResource( $property );
+ $firstvalue = true;
+
+ foreach ( $data->getValues( $property ) as $value ) {
+ $this->post_ns_buffer .= $firstvalue ? ' ' : ' , ';
+ $firstvalue = false;
+
+ if ( $value instanceof SMWExpLiteral ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_APROP;
+ $this->serializeExpLiteral( $value );
+ } elseif ( $value instanceof SMWExpResource ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $this->serializeExpResource( $value );
+ } elseif ( $value instanceof SMWExpData ) { // resource (maybe blank node), could have subdescriptions
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $collection = $value->getCollection();
+ if ( $collection !== false ) { // RDF-style collection (list)
+ $this->post_ns_buffer .= "( ";
+ foreach ( $collection as $subvalue ) {
+ $this->serializeNestedExpData( $subvalue, $indent . "\t\t" );
+ if ( $class_type_prop ) {
+ $this->requireDeclaration( $subvalue->getSubject(), SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+ $this->post_ns_buffer .= " )";
+ } else {
+ if ( $class_type_prop ) {
+ $this->requireDeclaration( $value->getSubject(), SMW_SERIALIZER_DECL_CLASS );
+ }
+ if ( count( $value->getProperties() ) > 0 ) { // resource with data: serialise
+ $this->post_ns_buffer .= "\n";
+ $this->serializeNestedExpData( $value, $indent . "\t\t" );
+ } else { // resource without data: may need to be queued
+ $this->serializeExpResource( $value->getSubject() );
+ }
+ }
+ }
+
+ if ( !$prop_decl_queued ) {
+ $this->requireDeclaration( $property, $prop_decl_type );
+ $prop_decl_queued = true;
+ }
+ }
+ }
+ $this->post_ns_buffer .= ( $bnode ? " ]" : " ." ) . ( $indent === '' ? "\n\n" : '' );
+ }
+
+ protected function serializeExpLiteral( SMWExpLiteral $element ) {
+ $this->post_ns_buffer .= self::getTurtleNameForExpElement( $element );
+ }
+
+ protected function serializeExpResource( SMWExpResource $element ) {
+ if ( $element instanceof SMWExpNsResource ) {
+ $this->requireNamespace( $element->getNamespaceID(), $element->getNamespace() );
+ }
+ $this->post_ns_buffer .= self::getTurtleNameForExpElement( $element );
+ }
+
+ /**
+ * Get the Turtle serialization string for the given SMWExpElement. The
+ * method just computes a name, and does not serialize triples, so the
+ * parameter must be an SMWExpResource or SMWExpLiteral, no SMWExpData.
+ *
+ * @param $expElement SMWExpElement being SMWExpLiteral or SMWExpResource
+ * @return string
+ */
+ public static function getTurtleNameForExpElement( SMWExpElement $expElement ) {
+ if ( $expElement instanceof SMWExpResource ) {
+ if ( $expElement->isBlankNode() ) {
+ return '[]';
+ } elseif ( ( $expElement instanceof SMWExpNsResource ) && ( $expElement->hasAllowedLocalName() ) ) {
+ return $expElement->getQName();
+ } else {
+ return '<' . str_replace( '>', '\>', SMWExporter::getInstance()->expandURI( $expElement->getUri() ) ) . '>';
+ }
+ } elseif ( $expElement instanceof SMWExpLiteral ) {
+ $dataType = $expElement->getDatatype();
+ $lexicalForm = self::getCorrectLexicalForm( $expElement );
+
+ if ( ( $dataType !== '' ) && ( $dataType != 'http://www.w3.org/2001/XMLSchema#string' ) ) {
+ $count = 0;
+ $newdt = str_replace( 'http://www.w3.org/2001/XMLSchema#', 'xsd:', $dataType, $count );
+ return ( $count == 1 ) ? "$lexicalForm^^$newdt" : "$lexicalForm^^<$dataType>";
+ } else {
+ return $lexicalForm;
+ }
+ } else {
+ throw new InvalidArgumentException( 'The method can only serialize atomic elements of type SMWExpResource or SMWExpLiteral.' );
+ }
+ }
+
+ private static function getCorrectLexicalForm( $expElement ) {
+
+ $lexicalForm = str_replace( [ '\\', "\n", '"' ], [ '\\\\', "\\n", '\"' ], $expElement->getLexicalForm() );
+
+ if ( $expElement->getLang() !== '' && ( $expElement->getDatatype() === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString' ) ) {
+ $lexicalForm = '"' . $lexicalForm . '@' . $expElement->getLang() . '"';
+ } elseif ( $expElement->getLang() !== '' ) {
+ $lexicalForm = '"' . $lexicalForm . '"'. '@' . $expElement->getLang();
+ } else {
+ $lexicalForm = '"' . $lexicalForm . '"';
+ }
+
+ return $lexicalForm;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php b/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php
new file mode 100644
index 00000000..642ee9da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php
@@ -0,0 +1,295 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use Language;
+
+/**
+ * Class implementing message output formatting
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * This class is implementing message output formatting to avoid having
+ * classes to invoke a language object that is not a direct dependency (which
+ * means that context relevant information is mostly missing from the invoking
+ * class) therefore it is more appropriate to collect Message objects from the
+ * source and initiate an output formatting only when necessary and requested.
+ *
+ * @ingroup Formatter
+ */
+class MessageFormatter {
+
+ /** @var array */
+ protected $messages = [];
+
+ /** @var string */
+ protected $type = 'warning';
+
+ /** @var string */
+ protected $separator = ' <!--br-->';
+
+ /** @var boolean */
+ protected $escape = true;
+
+ /**
+ * @since 1.9
+ *
+ * @param Language $language
+ */
+ public function __construct( Language $language ) {
+ $this->language = $language;
+ }
+
+ /**
+ * Convenience factory method to invoke a message array together with
+ * a language object
+ *
+ * @par Example:
+ * @code
+ * MessageFormatter::newFromArray( $language, array( 'Foo' ) )->getHtml();
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param Language $language
+ * @param array|null $messages
+ *
+ * @return MessageFormatter
+ */
+ public static function newFromArray( Language $language, array $messages = [] ) {
+ $instance = new self( $language );
+ return $instance->addFromArray( $messages );
+ }
+
+ /**
+ * Creates a Message object from a key and adds it to an internal array
+ *
+ * @since 1.9
+ *
+ * @param string $key message key
+ *
+ * @return MessageFormatter
+ */
+ public function addFromKey( $key /*...*/ ) {
+ $params = func_get_args();
+ array_shift( $params );
+ $this->addFromArray( [ new \Message( $key, $params ) ] );
+ return $this;
+ }
+
+ /**
+ * Adds an arbitrary array of messages which can either contain text
+ * or/and Message objects
+ *
+ * @par Example:
+ * @code
+ * $msgFormatter = new MessageFormatter( $language );
+ * $msgFormatter->addFromArray( array( 'Foo', new Message( 'Bar' ) ) )->getHtml()
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ *
+ * @return MessageFormatter
+ */
+ public function addFromArray( array $messages ) {
+
+ $messages = ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $messages );
+
+ foreach ( $messages as $message ) {
+ if ( is_string( $message ) ) {
+ $this->messages[md5( $message )] = $message;
+ } else{
+ $this->messages[] = $message;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns unformatted invoked messages
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getMessages() {
+ return $this->messages;
+ }
+
+ /**
+ * Used in connection with the html output to invoke a specific display
+ * type
+ *
+ * @see Highlighter::getTypeId
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function setType( $type ) {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * Enables/disables escaping for the output representation
+ *
+ * @note Escaping is generally enabled but in cases of special pages or
+ * with messages already being escaped this option can be circumvent by
+ * invoking escape( false )
+ *
+ * @since 1.9
+ *
+ * @param boolean $escape
+ *
+ * @return MessageFormatter
+ */
+ public function escape( $escape ) {
+ $this->escape = (bool)$escape;
+ return $this;
+ }
+
+ /**
+ * Clears the internal message array
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function clear() {
+ $this->messages = [];
+ return $this;
+ }
+
+ /**
+ * Returns if the internal message array does contain messages
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function exists() {
+ return $this->messages !== [];
+ }
+
+ /**
+ * Overrides invoked language object
+ *
+ * @since 1.9
+ *
+ * @param Language $language
+ *
+ * @return MessageFormatter
+ */
+ public function setLanguage( Language $language ) {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * Formatting and normalization of an array
+ *
+ * @note The array is being recursively resolved in order to ensure that
+ * the returning representation is a 1-n array where duplicate entries
+ * have been eliminated already while Message objects being transformed
+ * into a simple text representation using the invoked language
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ *
+ * @return array
+ */
+ protected function doFormat( array $messages ) {
+ $newArray = [];
+
+ foreach ( $messages as $msg ) {
+
+ if ( $msg instanceof \Message ) {
+ $text = $msg->inLanguage( $this->language )->text();
+ $newArray[md5( $text )] = $text;
+ } elseif ( (array)$msg === $msg ) {
+ foreach ( $this->doFormat( $msg ) as $m ) {
+ $newArray[md5( $m )] = $m;
+ }
+ } elseif ( (string)$msg === $msg ) {
+ $newArray[md5( $msg )] = $msg;
+ }
+ }
+
+ return $newArray;
+ }
+
+ /**
+ * Converts the message array into a string representation
+ *
+ * @since 1.9
+ *
+ * @param boolean $escape
+ * @param boolean $html
+ *
+ * @return string
+ */
+ protected function getString( $html = true ) {
+
+ if ( $this->escape ) {
+ $messages = array_map( 'htmlspecialchars', array_values( $this->doFormat( $this->messages ) ) );
+ } else {
+ $messages = array_values( $this->doFormat( $this->messages ) );
+ }
+
+ if ( count( $messages ) == 1 ) {
+ $messageString = $messages[0];
+ } else {
+ foreach ( $messages as &$message ) {
+ $message = $html ? Html::rawElement( 'li', [], $message ) : $message;
+ }
+
+ $messageString = implode( $this->separator, $messages );
+ $messageString = $html ? Html::rawElement( 'ul', [], $messageString ) : $messageString;
+ }
+
+ return $messageString;
+ }
+
+ /**
+ * Returns html representation of the formatted messages
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ if ( $this->exists() ) {
+
+ $highlighter = Highlighter::factory( $this->type );
+ $highlighter->setContent( [ 'content' => $this->getString( true ) ] );
+
+ return $highlighter->getHtml();
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns plain text representation of the formatted messages
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getPlain() {
+ return $this->exists() ? $this->getString( false ) : '';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php b/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php
new file mode 100644
index 00000000..935fd4e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php
@@ -0,0 +1,171 @@
+<?php
+
+use ParamProcessor\Definition\StringParam;
+use ParamProcessor\IParam;
+use SMW\Query\PrintRequest;
+
+/**
+ * Definition for the format parameter.
+ *
+ * @since 1.6.2
+ * @deprecated since 1.9
+ *
+ * @ingroup SMW
+ * @ingroup ParamDefinition
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWParamFormat extends StringParam {
+
+ /**
+ * List of the queries print requests, used to determine the format
+ * when it's not provided. Set with setPrintRequests before passing
+ * to Validator.
+ *
+ * @since 1.6.2
+ *
+ * @var PrintRequest[]
+ */
+ protected $printRequests = [];
+
+ protected $showMode = false;
+
+ /**
+ * Takes a format name, which can be an alias and returns a format name
+ * which will be valid for sure. Aliases are resolved. If the given
+ * format name is invalid, the predefined default format will be returned.
+ *
+ * @since 1.6.2
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ protected function getValidFormatName( $value ) {
+ global $smwgResultFormats;
+
+ $value = strtolower( trim( $value ) );
+
+ if ( !array_key_exists( $value, $smwgResultFormats ) ) {
+ $isAlias = self::resolveFormatAliases( $value );
+
+ if ( !$isAlias ) {
+ $value = $this->getDefaultFormat();
+ self::resolveFormatAliases( $value );
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Turns format aliases into main formats.
+ *
+ * @since 1.6.2
+ *
+ * @param string $format
+ *
+ * @return boolean Indicates if the passed format was an alias, and thus was changed.
+ */
+ public static function resolveFormatAliases( &$format ) {
+ global $smwgResultAliases;
+
+ $isAlias = false;
+
+ foreach ( $smwgResultAliases as $mainFormat => $aliases ) {
+ if ( in_array( $format, $aliases ) ) {
+ $format = $mainFormat;
+ $isAlias = true;
+ break;
+ }
+ }
+
+ return $isAlias;
+ }
+
+ /**
+ * Determines and returns the default format, based on the queries print
+ * requests, if provided.
+ *
+ * @since 1.6.2
+ *
+ * @return string Array key in $smwgResultFormats
+ */
+ protected function getDefaultFormat() {
+
+ if ( empty( $this->printRequests ) ) {
+ return 'table';
+ }
+
+ $format = false;
+
+ /**
+ * This hook allows extensions to override SMWs implementation of default result
+ * format handling.
+ *
+ * @since 1.5.2
+ */
+ \Hooks::run( 'SMWResultFormat', [ &$format, $this->printRequests, [] ] );
+
+ if ( $format !== false ) {
+ return $format;
+ }
+
+ // If no default was set by an extension, use a table, plainlist or list, depending on showMode and column count.
+ if ( count( $this->printRequests ) > 1 ) {
+ return 'table';
+ }
+
+ return 'plainlist';
+ }
+
+ /**
+ * Sets the print requests of the query, used for determining
+ * the default format if none is provided.
+ *
+ * @since 1.6.2
+ *
+ * @param PrintRequest[] $printRequests
+ */
+ public function setPrintRequests( array $printRequests ) {
+ $this->printRequests = $printRequests;
+ }
+
+ /**
+ *
+ * @since 3.0
+ *
+ * @param bool $showMode
+ */
+ public function setShowMode( $showMode ) {
+ $this->showMode = $showMode;
+ }
+
+ /**
+ * Formats the parameter value to it's final result.
+ *
+ * @since 1.8
+ *
+ * @param mixed $value
+ * @param IParam $param
+ * @param IParamDefinition[] $definitions
+ * @param IParam[] $params
+ *
+ * @return mixed
+ */
+ protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
+ $value = parent::formatValue( $value, $param, $definitions, $params );
+
+ // Make sure the format value is valid.
+ $value = self::getValidFormatName( $value );
+
+ // Add the formats parameters to the parameter list.
+ $queryPrinter = SMWQueryProcessor::getResultPrinter( $value );
+
+ $definitions = $queryPrinter->getParamDefinitions( $definitions );
+
+ return $value;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php
new file mode 100644
index 00000000..eadf97b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php
@@ -0,0 +1,552 @@
+<?php
+
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\Query\Language\Description;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryContext;
+use SMW\Query\QueryStringifier;
+use SMW\Query\QueryToken;
+
+/**
+ * This file contains the class for representing queries in SMW, each
+ * consisting of a query description and possible query parameters.
+ * @ingroup SMWQuery
+ * @author Markus Krötzsch
+ */
+
+/**
+ * This group contains all parts of SMW that relate to processing semantic queries.
+ * SMW components that relate to plain storage access (for querying or otherwise)
+ * have their own group.
+ * @defgroup SMWQuery SMWQuery
+ * @ingroup SMW
+ */
+
+/**
+ * Representation of queries in SMW, each consisting of a query
+ * description and various parameters. Some settings might also lead to
+ * changes in the query description.
+ *
+ * Most additional query parameters (limit, sort, ascending, ...) are
+ * interpreted as in SMWRequestOptions (though the latter contains some
+ * additional settings).
+ * @ingroup SMWQuery
+ */
+class SMWQuery implements QueryContext {
+
+ const ID_PREFIX = '_QUERY';
+
+ /**
+ * The time the QueryEngine required to answer a query condition
+ */
+ const PROC_QUERY_TIME = 'proc.query.time';
+
+ /**
+ * The time a ResultPrinter required to build the final result including all
+ * PrintRequests
+ */
+ const PROC_PRINT_TIME = 'proc.print.time';
+
+ /**
+ * The processing context in which the query is being executed
+ */
+ const PROC_CONTEXT = 'proc.context';
+
+ /**
+ * Status code information
+ */
+ const PROC_STATUS_CODE = 'proc.status.code';
+
+ /**
+ * The processing parameters
+ */
+ const OPT_PARAMETERS = 'proc.parameters';
+
+ /**
+ * Suppress a possible cache request
+ */
+ const NO_CACHE = 'no.cache';
+
+ /**
+ * Indicates no dependency trace
+ */
+ const NO_DEPENDENCY_TRACE = 'no.dependency.trace';
+
+ /**
+ * Sort by score if the query engine supports it.
+ */
+ const SCORE_SORT = 'score.sort';
+
+ public $sort = false;
+ public $sortkeys = []; // format: "Property key" => "ASC" / "DESC" (note: order of entries also matters)
+ public $querymode = self::MODE_INSTANCES;
+
+ private $limit;
+ private $offset = 0;
+ private $description;
+ private $errors = []; // keep any errors that occurred so far
+ private $queryString = false; // string (inline query) version (if fixed and known)
+ private $isInline; // query used inline? (required for finding right default parameters)
+ private $isUsedInConcept; // query used in concept? (required for finding right default parameters)
+
+ /**
+ * @var PrintRequest[]
+ */
+ private $m_extraprintouts = []; // SMWPrintoutRequest objects supplied outside querystring
+ private $m_mainlabel = ''; // Since 1.6
+
+ /**
+ * @var DIWikiPage|null
+ */
+ private $contextPage;
+
+ /**
+ * Describes a non-local (remote) query source
+ *
+ * @var string|null
+ */
+ private $querySource = null;
+
+ /**
+ * @var QueryToken|null
+ */
+ private $queryToken;
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 1.6
+ *
+ * @param Description $description
+ * @param integer|boolean $context
+ */
+ public function __construct( Description $description = null, $context = false ) {
+ $inline = false;
+ $concept = false;
+
+ // stating whether this query runs in an inline context; used to
+ // determine proper default parameters (e.g. the default limit)
+ if ( $context === self::INLINE_QUERY || $context === self::DEFERRED_QUERY ) {
+ $inline = true;
+ }
+
+ // stating whether this query belongs to a concept; used to determine
+ // proper default parameters (concepts usually have less restrictions)
+ if ( $context === self::CONCEPT_DESC ) {
+ $concept = true;
+ }
+
+ $this->limit = $inline ? $GLOBALS['smwgQMaxInlineLimit'] : $GLOBALS['smwgQMaxLimit'];
+ $this->isInline = $inline;
+ $this->isUsedInConcept = $concept;
+ $this->description = $description;
+ $this->applyRestrictions();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function isEmbedded() {
+ return $this->isInline;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer
+ */
+ public function setQueryMode( $queryMode ) {
+ // FIXME 3.0; $this->querymode is a public property
+ // declare it private and rename it to $this->queryMode
+ $this->querymode = $queryMode;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer
+ */
+ public function getQueryMode() {
+ return $this->querymode;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIWikiPage|null $contextPage
+ */
+ public function setContextPage( DIWikiPage $contextPage = null ) {
+ $this->contextPage = $contextPage;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return DIWikiPage|null
+ */
+ public function getContextPage() {
+ return $this->contextPage;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string
+ */
+ public function setQuerySource( $querySource ) {
+ $this->querySource = $querySource;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getQuerySource() {
+ return $this->querySource;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryToken|null $queryToken
+ */
+ public function setQueryToken( QueryToken $queryToken = null ) {
+ $this->queryToken = $queryToken;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return QueryToken|null
+ */
+ public function getQueryToken() {
+ return $this->queryToken;
+ }
+
+ /**
+ * Sets the mainlabel.
+ *
+ * @since 1.6.
+ *
+ * @param string $mainlabel
+ */
+ public function setMainLabel( $mainlabel ) {
+ $this->m_mainlabel = $mainlabel;
+ }
+
+ /**
+ * Gets the mainlabel.
+ *
+ * @since 1.6.
+ *
+ * @return string
+ */
+ public function getMainLabel() {
+ return $this->m_mainlabel;
+ }
+
+ public function setDescription( SMWDescription $description ) {
+ $this->description = $description;
+ $this->queryString = false;
+
+ foreach ( $this->m_extraprintouts as $printout ) {
+ $this->description->addPrintRequest( $printout );
+ }
+ $this->applyRestrictions();
+ }
+
+ public function getDescription() {
+ return $this->description;
+ }
+
+ public function setExtraPrintouts( $extraprintouts ) {
+ $this->m_extraprintouts = $extraprintouts;
+
+ if ( !is_null( $this->description ) ) {
+ foreach ( $extraprintouts as $printout ) {
+ $this->description->addPrintRequest( $printout );
+ }
+ }
+ }
+
+ /**
+ * @return PrintRequest[]
+ */
+ public function getExtraPrintouts() {
+ return $this->m_extraprintouts;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clearErrors() {
+ $this->errors = [];
+ }
+
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ public function addErrors( $errors ) {
+ $this->errors = array_merge( $this->errors, $errors );
+ }
+
+ public function setQueryString( $querystring ) {
+ $this->queryString = $querystring;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key ) {
+ return isset( $this->options[$key] ) ? $this->options[$key] : false;
+ }
+
+ /**
+ * @since 1.7
+ *
+ * @param boolean $fresh
+ *
+ * @return string
+ */
+ public function getQueryString( $fresh = false ) {
+
+ // Mostly relevant on requesting a further results link to
+ // ensure that localized values are transformed into a canonical
+ // representation
+ if ( $fresh && $this->description !== null ) {
+ return $this->description->getQueryString();
+ }
+
+ if ( $this->queryString !== false ) {
+ return $this->queryString;
+ } elseif ( !is_null( $this->description ) ) {
+ return $this->description->getQueryString();
+ } else {
+ return '';
+ }
+ }
+
+ public function getOffset() {
+ return $this->offset;
+ }
+
+ /**
+ * Set an offset for the returned query results. No offset beyond the maximal query
+ * limit will be set, and the current query limit might be reduced in order to ensure
+ * that no results beyond the maximal limit are returned.
+ * The function returns the chosen offset.
+ * @todo The function should be extended to take into account whether or not we
+ * are in inline mode (not critical, since offsets are usually not applicable inline).
+ */
+ public function setOffset( $offset ) {
+ global $smwgQMaxLimit;
+ $this->offset = min( $smwgQMaxLimit, $offset ); // select integer between 0 and maximal limit;
+ $this->limit = min( $smwgQMaxLimit - $this->offset, $this->limit ); // note that limit might become 0 here
+ return $this->offset;
+ }
+
+ public function getLimit() {
+ return $this->limit;
+ }
+
+ /**
+ * Set a limit for number of query results. The set limit might be restricted by the
+ * current offset so as to ensure that the number of the last considered result does not
+ * exceed the maximum amount of supported results.
+ * The function returns the chosen limit.
+ * @note It makes sense to have limit==0, e.g. to only show a link to the search special
+ */
+ public function setLimit( $limit, $restrictinline = true ) {
+ global $smwgQMaxLimit, $smwgQMaxInlineLimit;
+ $maxlimit = ( $this->isInline && $restrictinline ) ? $smwgQMaxInlineLimit : $smwgQMaxLimit;
+ $this->limit = min( $smwgQMaxLimit - $this->offset, $limit, $maxlimit );
+ return $this->limit;
+ }
+
+ /**
+ * @note Sets an unbound limit that is independent from GLOBAL settings
+ *
+ * @since 2.0
+ *
+ * @param integer $limit
+ */
+ public function setUnboundLimit( $limit ) {
+ $this->limit = (int)$limit;
+ }
+
+ /**
+ * @note format: "Property key" => "ASC" / "DESC" (note: order of entries also matters)
+ *
+ * @since 2.2
+ *
+ * @param array $sortKeys
+ */
+ public function setSortKeys( array $sortKeys ) {
+ $this->sortkeys = $sortKeys;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSortKeys() {
+ return $this->sortkeys;
+ }
+
+ /**
+ * Apply structural restrictions to the current description.
+ */
+ public function applyRestrictions() {
+ global $smwgQMaxSize, $smwgQMaxDepth, $smwgQConceptMaxSize, $smwgQConceptMaxDepth;
+
+ if ( !is_null( $this->description ) ) {
+ if ( $this->isUsedInConcept ) {
+ $maxsize = $smwgQConceptMaxSize;
+ $maxdepth = $smwgQConceptMaxDepth;
+ } else {
+ $maxsize = $smwgQMaxSize;
+ $maxdepth = $smwgQMaxDepth;
+ }
+
+ $log = [];
+ $this->description = $this->description->prune( $maxsize, $maxdepth, $log );
+
+ if ( count( $log ) > 0 ) {
+ $this->errors[] = Message::encode( [
+ 'smw_querytoolarge',
+ str_replace( '[', '&#91;', implode( ', ', $log ) ),
+ count( $log )
+ ] );
+ }
+ }
+ }
+
+ /**
+ * Returns serialized query details
+ *
+ * The output is following the askargs api module convention
+ *
+ * conditions The query conditions (requirements for a subject to be included)
+ * printouts The query printouts (which properties to show per subject)
+ * parameters The query parameters (non-condition and non-printout arguments)
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function toArray() {
+ $serialized = [];
+
+ $serialized['conditions'] = $this->getQueryString();
+
+ // This can be extended but for the current use cases that is
+ // sufficient since most printer related parameters have to be sourced
+ // in the result printer class
+ $serialized['parameters'] = [
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ 'sortkeys' => $this->sortkeys,
+ 'mainlabel' => $this->m_mainlabel,
+ 'querymode' => $this->querymode
+ ];
+
+ // @2.4 Keep the queryID stable with previous versions unless
+ // a query source is selected. The "same" query executed on different
+ // remote systems requires a different queryID
+ if ( $this->querySource !== null && $this->querySource !== '' ) {
+ $serialized['parameters']['source'] = $this->querySource;
+ }
+
+ foreach ( $this->getExtraPrintouts() as $printout ) {
+ if ( ( $serialisation = $printout->getSerialisation() ) !== '' ) {
+ $serialized['printouts'][] = $serialisation;
+ }
+ }
+
+ return $serialized;
+ }
+
+ /**
+ * @note Before 2.5, toArray was used to generate the content, as of 2.5
+ * only parameters that influence the result of an query is included.
+ *
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ // Only use elements that directly influence the result list
+ $serialized = [];
+
+ // Don't use the QueryString, use the canonized fingerprint to ensure that
+ // [[Foo::123]][[Bar::abc]] returns the same ID as [[Bar::abc]][[Foo::123]]
+ // given that limit, offset, and sort/order are the same
+ if ( $this->description !== null ) {
+ $serialized['fingerprint'] = $this->description->getFingerprint();
+ } else {
+ $serialized['conditions'] = $this->getQueryString();
+ }
+
+ $serialized['parameters'] = [
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ 'sortkeys' => $this->sortkeys,
+
+ // COUNT, DEBUG ...
+ 'querymode' => $this->querymode
+ ];
+
+ // Make to sure to distinguish queries and results from a foreign repository
+ if ( $this->querySource !== null && $this->querySource !== '' ) {
+ $serialized['parameters']['source'] = $this->querySource;
+ }
+
+ // Printouts are avoided as part of the hash as they not influence the
+ // list of entities and are only resolved after the query result has
+ // been retrieved
+ return md5( json_encode( $serialized ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function toString() {
+ return QueryStringifier::toString( $this );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getQueryId() {
+ return self::ID_PREFIX . $this->getHash();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php
new file mode 100644
index 00000000..ed851db5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php
@@ -0,0 +1,530 @@
+<?php
+
+use ParamProcessor\Options;
+use ParamProcessor\Param;
+use ParamProcessor\ParamDefinition;
+use ParamProcessor\Processor;
+use SMW\ApplicationFactory;
+use SMW\Message;
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\Query\Deferred;
+use SMW\Query\PrintRequest;
+use SMW\Query\Processor\ParamListProcessor;
+use SMW\Query\Processor\DefaultParamDefinition;
+use SMW\Query\QueryContext;
+use SMW\Query\Exception\ResultFormatNotFoundException;
+
+/**
+ * This file contains a static class for accessing functions to generate and execute
+ * semantic queries and to serialise their results.
+ *
+ * @ingroup SMWQuery
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Static class for accessing functions to generate and execute semantic queries
+ * and to serialise their results.
+ * @ingroup SMWQuery
+ */
+class SMWQueryProcessor implements QueryContext {
+
+ /**
+ * @var RecursiveTextProcessor
+ */
+ private static $recursiveTextProcessor;
+
+ /**
+ * @since 3.0
+ *
+ * @param RecursiveTextProcessor|null $recursiveTextProcessor
+ */
+ public static function setRecursiveTextProcessor( RecursiveTextProcessor $recursiveTextProcessor = null ) {
+ self::$recursiveTextProcessor = $recursiveTextProcessor;
+ }
+
+ /**
+ * Takes an array of unprocessed parameters, processes them using
+ * Validator, and returns them.
+ *
+ * Both input and output arrays are
+ * param name (string) => param value (mixed)
+ *
+ * @since 1.6.2
+ * The return value changed in SMW 1.8 from an array with result values
+ * to an array with Param objects.
+ *
+ * @param array $params
+ * @param array $printRequests
+ * @param boolean $unknownInvalid
+ *
+ * @return Param[]
+ */
+ public static function getProcessedParams( array $params, array $printRequests = [], $unknownInvalid = true, $context = null, $showMode = false ) {
+ $validator = self::getValidatorForParams( $params, $printRequests, $unknownInvalid, $context, $showMode );
+ $validator->processParameters();
+ $parameters = $validator->getParameters();
+
+ // Negates some weird behaviour of ParamDefinition::setDefault used in
+ // an individual printer.
+ // Applying $smwgQMaxLimit, $smwgQMaxInlineLimit will happen at a later
+ // stage.
+ if ( isset( $params['limit'] ) && isset( $parameters['limit'] ) ) {
+ $parameters['limit']->setValue( (int)$params['limit'] );
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Parse a query string given in SMW's query language to create
+ * an SMWQuery. Parameters are given as key-value-pairs in the
+ * given array. The parameter $context defines in what context the
+ * query is used, which affects ceretain general settings.
+ * An object of type SMWQuery is returned.
+ *
+ * The format string is used to specify the output format if already
+ * known. Otherwise it will be determined from the parameters when
+ * needed. This parameter is just for optimisation in a common case.
+ *
+ * @param string $queryString
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param $context
+ * @param string $format
+ * @param array $extraPrintouts
+ *
+ * @return SMWQuery
+ */
+ static public function createQuery( $queryString, array $params, $context = self::INLINE_QUERY, $format = '', array $extraPrintouts = [], $contextPage = null ) {
+
+ if ( $format === '' || is_null( $format ) ) {
+ $format = $params['format']->getValue();
+ }
+
+ $defaultSort = 'ASC';
+
+ if ( $format == 'count' ) {
+ $queryMode = SMWQuery::MODE_COUNT;
+ } elseif ( $format == 'debug' ) {
+ $queryMode = SMWQuery::MODE_DEBUG;
+ } else {
+ $printer = self::getResultPrinter( $format, $context );
+ $queryMode = $printer->getQueryMode( $context );
+ $defaultSort = $printer->getDefaultSort();
+ }
+
+ // set mode, limit, and offset:
+ $offset = 0;
+ $limit = $GLOBALS['smwgQDefaultLimit'];
+
+ if ( ( array_key_exists( 'offset', $params ) ) && ( is_int( $params['offset']->getValue() + 0 ) ) ) {
+ $offset = $params['offset']->getValue();
+ }
+
+ if ( ( array_key_exists( 'limit', $params ) ) && ( is_int( trim( $params['limit']->getValue() ) + 0 ) ) ) {
+ $limit = $params['limit']->getValue();
+
+ // limit < 0: always show further results link only
+ if ( ( trim( $params['limit']->getValue() ) + 0 ) < 0 ) {
+ $queryMode = SMWQuery::MODE_NONE;
+ }
+ }
+
+ // largest possible limit for "count", even inline
+ if ( $queryMode == SMWQuery::MODE_COUNT ) {
+ $offset = 0;
+ $limit = $GLOBALS['smwgQMaxLimit'];
+ }
+
+ $queryCreator = ApplicationFactory::getInstance()->singleton( 'QueryCreator' );
+
+ $params = [
+ 'extraPrintouts' => $extraPrintouts,
+ 'queryMode' => $queryMode,
+ 'context' => $context,
+ 'contextPage' => $contextPage,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'source' => $params['source']->getValue(),
+ 'mainLabel' => $params['mainlabel']->getValue(),
+ 'sort' => $params['sort']->getValue(),
+ 'order' => $params['order']->getValue(),
+ 'defaultSort' => $defaultSort
+ ];
+
+ return $queryCreator->create( $queryString, $params );
+ }
+
+ /**
+ * Add the subject print request, unless mainlabel is set to "-".
+ *
+ * @since 1.7
+ *
+ * @param array $printRequests
+ * @param array $rawParams
+ */
+ public static function addThisPrintout( array &$printRequests, array $rawParams ) {
+
+ if ( $printRequests === null ) {
+ return;
+ }
+
+ // If THIS is already registered, bail-out!
+ foreach ( $printRequests as $printRequest ) {
+ if ( $printRequest->isMode( PrintRequest::PRINT_THIS ) ) {
+ return;
+ }
+ }
+
+ $hasMainlabel = array_key_exists( 'mainlabel', $rawParams );
+
+ if ( !$hasMainlabel || trim( $rawParams['mainlabel'] ) !== '-' ) {
+ $printRequest = new PrintRequest(
+ PrintRequest::PRINT_THIS,
+ $hasMainlabel ? $rawParams['mainlabel'] : ''
+ );
+
+ // Signal to any post-processing that THIS was added outside of
+ // the normal processing chain
+ $printRequest->isDisconnected( true );
+
+ array_unshift( $printRequests, $printRequest );
+ }
+ }
+
+ /**
+ * Preprocess a query as given by an array of parameters as is typically
+ * produced by the #ask parser function. The parsing results in a querystring,
+ * an array of additional parameters, and an array of additional SMWPrintRequest
+ * objects, which are filled into call-by-ref parameters.
+ * $showMode is true if the input should be treated as if given by #show
+ *
+ * @param array $rawParams
+ * @param string $querystring
+ * @param array $params
+ * @param array $printouts array of SMWPrintRequest
+ * @param boolean $showMode
+ * @deprecated Will vanish after SMW 1.8 is released.
+ * Use getComponentsFromFunctionParams which has a cleaner interface.
+ */
+ static public function processFunctionParams( array $rawParams, &$querystring, &$params, &$printouts, $showMode = false ) {
+ list( $querystring, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+ }
+
+
+ /**
+ * Preprocess a query as given by an array of parameters as is
+ * typically produced by the #ask parser function or by Special:Ask.
+ * The parsing results in a querystring, an array of additional
+ * parameters, and an array of additional SMWPrintRequest objects,
+ * which are returned in an array with three components. If
+ * $showMode is true then the input will be processed as if for #show.
+ * This uses a slightly different way to get the query, and different
+ * default labels (empty) for additional print requests.
+ *
+ * @param array $rawParams
+ * @param boolean $showMode
+ * @return array( string, array( string => string ), array( SMWPrintRequest ) )
+ */
+ static public function getComponentsFromFunctionParams( array $rawParams, $showMode ) {
+
+ $paramListProcessor = ApplicationFactory::getInstance()->singleton( 'ParamListProcessor' );
+
+ return $paramListProcessor->format(
+ $paramListProcessor->preprocess( $rawParams, $showMode ),
+ ParamListProcessor::FORMAT_LEGACY
+ );
+ }
+
+ /**
+ * Process and answer a query as given by an array of parameters as is
+ * typically produced by the #ask parser function. The parameter
+ * $context defines in what context the query is used, which affects
+ * certain general settings.
+ *
+ * The main task of this function is to preprocess the raw parameters
+ * to obtain actual parameters, printout requests, and the query string
+ * for further processing.
+ *
+ * @since 1.8
+ * @param array $rawParams user-provided list of unparsed parameters
+ * @param integer $outputMode SMW_OUTPUT_WIKI, SMW_OUTPUT_HTML, ...
+ * @param integer $context INLINE_QUERY, SPECIAL_PAGE, CONCEPT_DESC
+ * @param boolean $showMode process like #show parser function?
+ * @return array( SMWQuery, array of IParam )
+ */
+ static public function getQueryAndParamsFromFunctionParams( array $rawParams, $outputMode, $context, $showMode, $contextPage = null ) {
+ list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+
+ if ( !$showMode ) {
+ self::addThisPrintout( $printouts, $params );
+ }
+
+ $params = self::getProcessedParams( $params, $printouts, true, $context, $showMode );
+
+ $query = self::createQuery( $queryString, $params, $context, '', $printouts, $contextPage );
+
+ // For convenience keep parameters and options to be available for immediate
+ // processing
+ if ( $context === self::DEFERRED_QUERY ) {
+ $query->setOption( Deferred::QUERY_PARAMETERS, implode( '|', $rawParams ) );
+ $query->setOption( Deferred::SHOW_MODE, $showMode );
+ $query->setOption( Deferred::CONTROL_ELEMENT, isset( $params['@control'] ) ? $params['@control']->getValue() : '' );
+ }
+
+ return [ $query, $params ];
+ }
+
+ /**
+ * Process and answer a query as given by an array of parameters as is
+ * typically produced by the #ask parser function. The result is formatted
+ * according to the specified $outputformat. The parameter $context defines
+ * in what context the query is used, which affects ceretain general settings.
+ *
+ * The main task of this function is to preprocess the raw parameters to
+ * obtain actual parameters, printout requests, and the query string for
+ * further processing.
+ *
+ * @note Consider using getQueryAndParamsFromFunctionParams() and
+ * getResultFromQuery() instead.
+ * @deprecated Will vanish after release of SMW 1.8.
+ * See SMW_Ask.php for example code on how to get query results from
+ * #ask function parameters.
+ */
+ static public function getResultFromFunctionParams( array $rawParams, $outputMode, $context = self::INLINE_QUERY, $showMode = false ) {
+ list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+
+ if ( !$showMode ) {
+ self::addThisPrintout( $printouts, $params );
+ }
+
+ $params = self::getProcessedParams( $params, $printouts );
+
+ return self::getResultFromQueryString( $queryString, $params, $printouts, SMW_OUTPUT_WIKI, $context );
+ }
+
+ /**
+ * Process a query string in SMW's query language and return a formatted
+ * result set as specified by $outputmode. A parameter array of key-value-pairs
+ * constrains the query and determines the serialisation mode for results. The
+ * parameter $context defines in what context the query is used, which affects
+ * certain general settings. Finally, $extraprintouts supplies additional
+ * printout requests for the query results.
+ *
+ * @param string $queryString
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param $extraPrintouts
+ * @param $outputMode
+ * @param $context
+ *
+ * @return string
+ * @deprecated Will vanish after release of SMW 1.8.
+ * See SMW_Ask.php for example code on how to get query results from
+ * #ask function parameters.
+ */
+ static public function getResultFromQueryString( $queryString, array $params, $extraPrintouts, $outputMode, $context = self::INLINE_QUERY ) {
+
+ $query = self::createQuery( $queryString, $params, $context, '', $extraPrintouts );
+ $result = self::getResultFromQuery( $query, $params, $outputMode, $context );
+
+
+ return $result;
+ }
+
+ /**
+ * Create a fully formatted result string from a query and its
+ * parameters. The method takes care of processing various types of
+ * query result. Most cases are handled by printers, but counting and
+ * debugging uses special code.
+ *
+ * @param SMWQuery $query
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param integer $outputMode
+ * @param integer $context
+ * @since before 1.7, but public only since 1.8
+ *
+ * @return string
+ */
+ public static function getResultFromQuery( SMWQuery $query, array $params, $outputMode, $context ) {
+
+ $printer = self::getResultPrinter(
+ $params['format']->getValue(),
+ $context
+ );
+
+ if ( $printer->isDeferrable() && $context === self::DEFERRED_QUERY && $query->getLimit() > 0 ) {
+
+ // Halt processing that is not `DEFERRED_DATA` as it is expected the
+ // process is picked-up by the `deferred.js` loader that will
+ // initiate an API request to finalize the query after MW has build
+ // the page.
+ if ( $printer->isDeferrable() !== $printer::DEFERRED_DATA ) {
+ return Deferred::buildHTML( $query );
+ }
+
+ // `DEFERRED_DATA` is interpret as "execute the query with limit=0" (i.e.
+ // no query execution) but allow the printer to setup the HTML so that
+ // the data can be loaded after MW has finished the page build including
+ // the pre-rendered query HTML representation. This mode deferrers the
+ // actual query execution and data load to after the page build.
+ //
+ // Each printer that uses this mode has to handle the required parameters
+ // and data load accordingly.
+ $query->querymode = SMWQuery::MODE_INSTANCES;
+ $query->setOption( 'deferred.limit', $query->getLimit() );
+ $query->setLimit( 0 );
+ }
+
+ $res = self::getStoreFromParams( $params )->getQueryResult( $query );
+ $start = microtime( true );
+
+ if ( $res instanceof SMWQueryResult && $query->getOption( 'calc.result_hash' ) ) {
+ $query->setOption( 'result_hash', $res->getHash( 'quick' ) );
+ }
+
+ if ( ( $query->querymode == SMWQuery::MODE_INSTANCES ) ||
+ ( $query->querymode == SMWQuery::MODE_NONE ) ) {
+
+ $result = $printer->getResult( $res, $params, $outputMode );
+
+ $query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
+ return $result;
+ } else { // result for counting or debugging is just a string or number
+
+ if ( $res instanceof SMWQueryResult ) {
+ $res = $res->getCountValue();
+ }
+
+ if ( is_numeric( $res ) ) {
+ $res = strval( $res );
+ }
+
+ if ( is_string( $res ) ) {
+ $result = str_replace( '_', ' ', $params['intro']->getValue() )
+ . $res
+ . str_replace( '_', ' ', $params['outro']->getValue() )
+ . smwfEncodeMessages( $query->getErrors() );
+ } else {
+ // When no valid result was obtained, $res will be a SMWQueryResult.
+ $result = smwfEncodeMessages( $query->getErrors() );
+ }
+
+ $query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
+
+ return $result;
+ }
+ }
+
+ private static function getStoreFromParams( array $params ) {
+ return ApplicationFactory::getInstance()->getQuerySourceFactory()->get( $params['source']->getValue() );
+ }
+
+ /**
+ * Find suitable SMWResultPrinter for the given format. The context in
+ * which the query is to be used determines some basic settings of the
+ * returned printer object. Possible contexts are
+ * SMWQueryProcessor::SPECIAL_PAGE, SMWQueryProcessor::INLINE_QUERY,
+ * SMWQueryProcessor::CONCEPT_DESC.
+ *
+ * @param string $format
+ * @param $context
+ *
+ * @return SMWResultPrinter
+ * @throws MissingResultFormatException
+ */
+ static public function getResultPrinter( $format, $context = self::SPECIAL_PAGE ) {
+ global $smwgResultFormats;
+
+ SMWParamFormat::resolveFormatAliases( $format );
+
+ if ( !array_key_exists( $format, $smwgResultFormats ) ) {
+ throw new ResultFormatNotFoundException( "There is no result format for '$format'." );
+ }
+
+ $formatClass = $smwgResultFormats[$format];
+
+ $printer = new $formatClass( $format, ( $context != self::SPECIAL_PAGE ) );
+
+ if ( self::$recursiveTextProcessor === null ) {
+ self::$recursiveTextProcessor = new RecursiveTextProcessor();
+ }
+
+ $printer->setRecursiveTextProcessor(
+ self::$recursiveTextProcessor
+ );
+
+ return $printer;
+ }
+
+ /**
+ * Produces a list of default allowed parameters for a result printer. Most
+ * query printers should override this function.
+ *
+ * @since 1.6.2, return element type changed in 1.8
+ *
+ * @param integer|null $context
+ * @param ResultPrinter|null $resultPrinter
+ *
+ * @return IParamDefinition[]
+ */
+ public static function getParameters( $context = null, $resultPrinter = null ) {
+ return DefaultParamDefinition::getParamDefinitions( $context, $resultPrinter );
+ }
+
+ /**
+ * Returns the definitions of all parameters supported by the specified format.
+ *
+ * @since 1.8
+ *
+ * @param string $format
+ *
+ * @return ParamDefinition[]
+ */
+ public static function getFormatParameters( $format ) {
+ SMWParamFormat::resolveFormatAliases( $format );
+
+ if ( !array_key_exists( $format, $GLOBALS['smwgResultFormats'] ) ) {
+ return [];
+ }
+
+ $resultPrinter = self::getResultPrinter( $format );
+
+ if ( $resultPrinter instanceof \SMW\Query\ResultPrinters\NullResultPrinter ) {
+ return [];
+ }
+
+ return ParamDefinition::getCleanDefinitions(
+ $resultPrinter->getParamDefinitions( self::getParameters( null, $resultPrinter ) )
+ );
+ }
+
+ /**
+ * Takes an array of unprocessed parameters,
+ * and sets them on a new Validator object,
+ * which is returned and ready to process the parameters.
+ *
+ * @since 1.8
+ *
+ * @param array $params
+ * @param array $printRequests
+ * @param boolean $unknownInvalid
+ *
+ * @return Processor
+ */
+ private static function getValidatorForParams( array $params, array $printRequests = [], $unknownInvalid = true, $context = null, $showMode = false ) {
+ $paramDefinitions = self::getParameters( $context );
+
+ $paramDefinitions['format']->setPrintRequests( $printRequests );
+ $paramDefinitions['format']->setShowMode( $showMode );
+
+ $processorOptions = new Options();
+ $processorOptions->setUnknownInvalid( $unknownInvalid );
+
+ $validator = Processor::newFromOptions( $processorOptions );
+
+ $validator->setParameters( $params, $paramDefinitions, false );
+
+ return $validator;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php
new file mode 100644
index 00000000..737fce97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Exception\PropertyNotFoundException;
+use SMWDIError;
+use SMWTypesValue;
+
+/**
+ * Query class that provides content for the Special:Properties page
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class PropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @return string
+ */
+ function getPageHeader() {
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-sp-properties-docu' ],
+ $this->msg( 'smw-sp-properties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo() ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return 'Properties';
+ }
+
+ /**
+ * Format a result in the list of results as a string. We expect the
+ * result to be an array with one object of type SMWDIProperty
+ * (normally) or maybe SMWDIError (if something went wrong), followed
+ * by a number (how often the property is used).
+ *
+ * @param Skin $skin provided by MediaWiki, not needed here
+ * @param mixed $result
+ * @return String
+ * @throws PropertyNotFoundException if the result was not of a supported type
+ */
+ function formatResult( $skin, $result ) {
+
+ list ( $dataItem, $useCount ) = $result;
+
+ if ( $dataItem instanceof DIProperty ) {
+ return $this->formatPropertyItem( $dataItem, $useCount );
+ } elseif ( $dataItem instanceof SMWDIError ) {
+ return $this->getMessageFormatter()->clear()
+ ->setType( 'warning' )
+ ->addFromArray( [ $dataItem->getErrors(), 'ID: ' . ( isset( $dataItem->id ) ? $dataItem->id : 'N/A' ) ] )
+ ->getHtml();
+ }
+
+ throw new PropertyNotFoundException( 'PropertiesQueryPage expects results that are properties or errors.' );
+ }
+
+ /**
+ * Produce a formatted string representation for showing a property and
+ * its usage count in the list of used properties.
+ *
+ * @since 1.8
+ *
+ * @param DIProperty $property
+ * @param integer $useCount
+ * @return string
+ */
+ protected function formatPropertyItem( DIProperty $property, $useCount ) {
+
+ // Clear formatter before invoking messages
+ $this->getMessageFormatter()->clear();
+
+ $diWikiPage = $property->getDiWikiPage();
+ $title = $diWikiPage !== null ? $diWikiPage->getTitle() : null;
+
+ if ( $useCount == 0 && !$this->settings->get( 'smwgPropertyZeroCountDisplay' ) ) {
+ return '';
+ }
+
+ if ( $property->isUserDefined() ) {
+
+ if ( $title === null ) {
+ // Show even messed up property names.
+ $typestring = '';
+ $proplink = $property->getLabel();
+ $this->getMessageFormatter()
+ ->addFromArray( [ 'ID: ' . ( isset( $property->id ) ? $property->id : 'N/A' ) ] )
+ ->addFromKey( 'smw_notitle', $proplink );
+ } else {
+ list( $typestring, $proplink ) = $this->getUserDefinedPropertyInfo( $title, $property, $useCount );
+ }
+
+ $infoLink = '';
+
+ // Add a link to SearchByProperty to hopefully identify the
+ // "hidden" reference
+ if ( $useCount < 1 ) {
+ $infoLink = '&#160;' . \SMWInfolink::newPropertySearchLink( '+', $property->getLabel(), '' )->getHTML( $this->getLinker() );
+ }
+
+ $proplink .= $infoLink;
+
+ } else {
+ list( $typestring, $proplink ) = $this->getPredefinedPropertyInfo( $property );
+ }
+
+ if ( $typestring === '' ) { // Built-ins have no type
+
+ // @todo Should use numParams for $useCount?
+ return $this->msg( 'smw_property_template_notype' )
+ ->rawParams( $proplink )->numParams( $useCount )->text() . ' ' .
+ $this->getMessageFormatter()
+ ->setType( 'warning' )
+ ->escape( false )->getHtml();
+
+ } else {
+
+ // @todo Should use numParams for $useCount?
+ return $this->msg( 'smw_property_template' )
+ ->rawParams( $proplink, $typestring )->numParams( $useCount )->escaped() . ' ' .
+ $this->getMessageFormatter()
+ ->setType( 'warning' )
+ ->escape( false )->getHtml();
+
+ }
+ }
+
+ /**
+ * Returns information related to user-defined properties
+ *
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param DIProperty $property
+ * @param integer $useCount
+ *
+ * @return array
+ */
+ private function getUserDefinedPropertyInfo( $title, $property, $useCount ) {
+
+ if ( $useCount <= $this->settings->get( 'smwgPropertyLowUsageThreshold' ) ) {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertyhardlyused' );
+ }
+
+ // User defined types default to Page
+ $typestring = SMWTypesValue::newFromTypeId( $this->settings->get( 'smwgPDefaultType' ) )->getLongHTMLText( $this->getLinker() );
+
+ $label = htmlspecialchars( $property->getLabel() );
+ $linkAttributes = [];
+
+ if ( isset( $property->id ) ) {
+ $linkAttributes['title'] = 'ID: ' . $property->id;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $property );
+ $dataValue->setLinkAttributes( $linkAttributes );
+
+ $proplink = $dataValue->getFormattedLabel(
+ DataValueFormatter::HTML_SHORT,
+ $this->getLinker()
+ );
+
+ if ( !$title->exists() ) {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackspage' );
+ }
+
+ $typeProperty = new DIProperty( '_TYPE' );
+ $types = $this->store->getPropertyValues( $property->getDiWikiPage(), $typeProperty );
+
+ if ( is_array( $types ) && count( $types ) >= 1 ) {
+ $typeDataValue = DataValueFactory::getInstance()->newDataValueByItem( current( $types ), $typeProperty );
+ $typestring = $typeDataValue->getLongHTMLText( $this->getLinker() );
+ } else {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackstype', $typestring );
+ }
+
+ return [ $typestring, $proplink ];
+ }
+
+ /**
+ * Returns information related to predefined properties
+ *
+ * @since 1.9
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ private function getPredefinedPropertyInfo( DIProperty $property ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $property, null );
+
+ $dataValue->setLinkAttributes( [
+ 'title' => 'ID: ' . ( isset( $property->id ) ? $property->id : 'N/A' ) . ' (' . $property->getKey() . ')'
+ ] );
+
+ $label = $dataValue->getFormattedLabel(
+ DataValueFormatter::HTML_SHORT,
+ $this->getLinker()
+ );
+
+ return [
+ SMWTypesValue::newFromTypeId( $property->findPropertyTypeID() )->getLongHTMLText( $this->getLinker() ),
+ $label
+ ];
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of array( SMWDIProperty|SMWDIError, integer )
+ */
+ function getResults( $requestOptions ) {
+ $this->listLookup = $this->store->getPropertiesSpecial( $requestOptions );
+ return $this->listLookup->fetchList();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php
new file mode 100644
index 00000000..79cecae8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMWRequestOptions;
+use SMWStringCondition;
+use Xml;
+
+/**
+ * An abstract query page base class that supports array-based
+ * data retrieval instead of the SQL-based access used by MW.
+ *
+ *
+ * @license GNU GPL v2+
+ * @since ??
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Abstract base class for SMW's variant of the MW QueryPage.
+ * Subclasses must implement getResults() and formatResult(), as
+ * well as some other standard functions of QueryPage.
+ *
+ * @ingroup SMW
+ * @ingroup QueryPage
+ */
+abstract class QueryPage extends \QueryPage {
+
+ /** @var MessageFormatter */
+ protected $msgFormatter;
+
+ /** @var Linker */
+ protected $linker = null;
+
+ /** @var array */
+ protected $selectOptions = [];
+
+ /** @var array */
+ protected $useSerchForm = false;
+
+ /**
+ * Implemented by subclasses to provide concrete functions.
+ */
+ abstract function getResults( $requestoptions );
+
+ /**
+ * Clear the cache and save new results
+ * @todo Implement caching for SMW query pages
+ */
+ function recache( $limit, $ignoreErrors = true ) {
+ /// TODO
+ }
+
+ function isExpensive() {
+ return false; // Disables caching for now
+ }
+
+ function isSyndicated() {
+ return false; // TODO: why not?
+ }
+
+ /**
+ * @see QueryPage::linkParameters
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function linkParameters() {
+
+ $parameters = [];
+ $property = $this->getRequest()->getVal( 'property' );
+
+ if ( $property !== null && $property !== '' ) {
+ $parameters['property'] = $property;
+ }
+
+ $filter = $this->getRequest()->getVal( 'filter' );
+
+ if ( $filter !== null && $filter !== '' ) {
+ $parameters['filter'] = $filter;
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Returns a MessageFormatter object
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function getMessageFormatter() {
+
+ if ( !isset( $this->msgFormatter ) ) {
+ $this->msgFormatter = new MessageFormatter( $this->getLanguage() );
+ }
+
+ return $this->msgFormatter;
+ }
+
+ /**
+ * Returns a Linker object
+ *
+ * @since 1.9
+ *
+ * @return Linker
+ */
+ public function getLinker() {
+
+ if ( $this->linker === null ) {
+ $this->linker = smwfGetLinker();
+ }
+
+ return $this->linker;
+ }
+
+ /**
+ * Generates a search form
+ *
+ * @since 1.9
+ *
+ * @param string $property
+ *
+ * @return string
+ */
+ public function getSearchForm( $property = '', $cacheDate = '', $propertySearch = true, $filter = '' ) {
+
+ $this->useSerchForm = true;
+ $this->getOutput()->addModules( 'ext.smw.autocomplete.property' );
+
+ // No need to verify $this->selectOptions because its values are set
+ // during doQuery() which is processed before this form is generated
+ $resultCount = wfShowingResults( $this->selectOptions['offset'], $this->selectOptions['count'] );
+
+ $selection = $this->getLanguage()->viewPrevNext(
+ $this->getContext()->getTitle(),
+ $this->selectOptions['offset'],
+ $this->selectOptions['limit'],
+ $this->linkParameters(),
+ $this->selectOptions['end']
+ );
+
+ if ( $cacheDate !== '' ) {
+ $cacheDate = Xml::tags( 'p', [], $cacheDate );
+ }
+
+ if ( $propertySearch ) {
+ $propertySearch = Xml::tags( 'hr', [ 'style' => 'margin-bottom:10px;' ], '' ) .
+ Xml::inputLabel( $this->msg( 'smw-special-property-searchform' )->text(), 'property', 'smw-property-input', 20, $property ) . ' ' .
+ Xml::submitButton( $this->msg( 'allpagessubmit' )->text() );
+ }
+
+ if ( $filter !== '' ) {
+ $filter = Xml::tags( 'hr', [ 'style' => 'margin-bottom:10px;' ], '' ) . $filter;
+ }
+
+ return Xml::tags( 'form', [
+ 'method' => 'get',
+ 'action' => htmlspecialchars( $GLOBALS['wgScript'] ),
+ 'class' => 'plainlinks'
+ ], Html::hidden( 'title', $this->getContext()->getTitle()->getPrefixedText() ) .
+ Xml::fieldset( $this->msg( 'smw-special-property-searchform-options' )->text(),
+ Xml::tags( 'p', [], $resultCount ) .
+ Xml::tags( 'p', [], $selection ) .
+ $cacheDate .
+ $propertySearch .
+ $filter
+ )
+ );
+ }
+
+ /**
+ * This is the actual workhorse. It does everything needed to make a
+ * real, honest-to-gosh query page.
+ * Alas, we need to overwrite the whole beast since we do not assume
+ * an SQL-based storage backend.
+ *
+ * @param $offset database query offset
+ * @param $limit database query limit
+ * @param $property database string query
+ */
+ function doQuery( $offset = false, $limit = false, $property = false ) {
+ $out = $this->getOutput();
+ $sk = $this->getSkin();
+
+ $options = new SMWRequestOptions();
+ $options->limit = $limit;
+ $options->offset = $offset;
+ $options->sort = true;
+
+ if ( $property ) {
+ $options->addStringCondition( $property, SMWStringCondition::STRCOND_MID );
+ }
+
+ if ( ( $filter = $this->getRequest()->getVal( 'filter' ) ) === 'unapprove' ) {
+ $options->addExtraCondition( [ 'filter.unapprove' => true ] );
+ }
+
+ $res = $this->getResults( $options );
+ $num = count( $res );
+
+ // often disable 'next' link when we reach the end
+ $atend = $num < $limit;
+
+ $this->selectOptions = [
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'end' => $atend,
+ 'count' => $num
+ ];
+
+ $out->addHTML( $this->getPageHeader() );
+
+ // if list is empty, show it
+ if ( $num == 0 ) {
+ $out->addHTML( '<p>' . $this->msg( 'specialpage-empty' )->escaped() . '</p>' );
+ return;
+ }
+
+ if ( $num > 0 ) {
+ $s = [];
+ if ( ! $this->listoutput ) {
+ $s[] = $this->openList( $offset );
+ }
+
+ foreach ( $res as $r ) {
+ $format = $this->formatResult( $sk, $r );
+ if ( $format ) {
+ $s[] = $this->listoutput ? $format : "<li>{$format}</li>\n";
+ }
+ }
+
+ if ( ! $this->listoutput ) {
+ $s[] = $this->closeList();
+ }
+ $str = $this->listoutput ? $this->getLanguage()->listToText( $s ) : implode( '', $s );
+ $out->addHTML( $str );
+ }
+
+ if ( !$this->useSerchForm ) {
+ $out->addHTML( "<p>{$sl}</p>\n" );
+ }
+
+ return $num;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php
new file mode 100644
index 00000000..878acf98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php
@@ -0,0 +1,186 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\Exception\PropertyNotFoundException;
+use SMWDIError;
+use SMWTypesValue;
+
+/**
+ * Query page that provides content to Special:UnusedProperties
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class UnusedPropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return "UnusedProperties";
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isExpensive() {
+ return false; // Disables caching for now
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isSyndicated() {
+ return false; // TODO: why not?
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getPageHeader() {
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-unusedproperties-docu' ],
+ $this->msg( 'smw-unusedproperties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo() ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * Format a result in the list of results as a string. We expect the
+ * result to be an object of type SMWDIProperty (normally) or maybe
+ * SMWDIError (if something went wrong).
+ *
+ * @param Skin $skin provided by MediaWiki, not needed here
+ * @param mixed $result
+ *
+ * @return String
+ * @throws InvalidResultException if the result was not of a supported type
+ */
+ function formatResult( $skin, $result ) {
+
+ if ( $result instanceof DIProperty ) {
+ return $this->formatPropertyItem( $result );
+ } elseif ( $result instanceof SMWDIError ) {
+ return $this->getMessageFormatter()->clear()
+ ->setType( 'warning' )
+ ->addFromArray( [ $result->getErrors() ] )
+ ->getHtml();
+ }
+
+ throw new PropertyNotFoundException( 'UnusedPropertiesQueryPage expects results that are properties or errors.' );
+ }
+
+ /**
+ * Produce a formatted string representation for showing a property in
+ * the list of unused properties.
+ *
+ * @since 1.8
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ protected function formatPropertyItem( DIProperty $property ) {
+
+ // Clear formatter before invoking messages and
+ // avoid having previous data to be present
+ $this->getMessageFormatter()->clear();
+
+ if ( $property->isUserDefined() ) {
+
+ $title = $property->getDiWikiPage()->getTitle();
+
+ if ( !$title instanceof \Title ) {
+ return '';
+ }
+
+ $propertyLink = $this->getLinker()->link(
+ $title,
+ $property->getLabel()
+ );
+
+ $types = $this->store->getPropertyValues( $property->getDiWikiPage(), new DIProperty( '_TYPE' ) );
+
+ if ( is_array( $types ) && count( $types ) >= 1 ) {
+ $typeDataValue = DataValueFactory::getInstance()->newDataValueByItem( current( $types ), new DIProperty( '_TYPE' ) );
+ } else {
+ $typeDataValue = SMWTypesValue::newFromTypeId( '_wpg' );
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackstype', $typeDataValue->getLongHTMLText() );
+ }
+
+ } else {
+ $typeDataValue = SMWTypesValue::newFromTypeId( $property->findPropertyTypeID() );
+ $propertyLink = DataValueFactory::getInstance()->newDataValueByItem( $property, null )->getShortHtmlText( $this->getLinker() );
+ }
+
+ return $this->msg( 'smw-unusedproperty-template', $propertyLink, $typeDataValue->getLongHTMLText( $this->getLinker() ) )->text() . ' ' .
+ $this->getMessageFormatter()->getHtml();
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of SMWDIProperty|SMWDIError
+ */
+ function getResults( $requestOptions ) {
+ $this->listLookup = $this->store->getUnusedPropertiesSpecial( $requestOptions );
+ return $this->listLookup->fetchList();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php
new file mode 100644
index 00000000..0daf7608
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace SMW;
+
+use Html;
+
+/**
+ * Query class that provides content for the Special:WantedProperties page
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class WantedPropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ public function setTitle( $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return "WantedProperties";
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isExpensive() {
+ return false; /// disables caching for now
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isSyndicated() {
+ return false; ///TODO: why not?
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getPageHeader() {
+
+ $filer = $this->getRequest()->getVal( 'filter', '' );
+
+ if ( $filer !== 'unapprove' ) {
+ $label = $this->msg( 'smw-special-wantedproperties-filter-unapproved' )->text();
+ $title = $this->msg( 'smw-special-wantedproperties-filter-unapproved-desc' )->text();
+ } else {
+ $label = $this->msg( 'smw-special-wantedproperties-filter-none' )->text();
+ $title = '';
+ }
+
+ $filter = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-special-filter'
+ ],
+ $this->msg( 'smw-special-wantedproperties-filter-label' )->text() .
+ '&nbsp;' .
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-special-filter-button',
+ 'title' => $title
+ ],
+ Html::element(
+ 'a',
+ [
+ 'href' => $this->title->getLocalURL( [ 'filter' => $filer !== '' ? '' : 'unapprove' ] ),
+ 'rel' => 'nofollow'
+ ],
+ $label
+ )
+ )
+ );
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-wantedproperties-docu plainlinks' ],
+ $this->msg( 'smw-special-wantedproperties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo(), false, $filter ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * @param $skin
+ * @param array $result First item is SMWDIProperty, second item is int
+ *
+ * @return string
+ */
+ function formatResult( $skin, $result ) {
+ // Only display user-defined properties because it can happen that
+ // custom predefined (fixed) properties are mixed within the result
+ // (did not use their own fixedProperty table and therefore were
+ // selected as well e.g _SF_PDF etc.)
+ if ( !$result[0] instanceof DIProperty || !$result[0]->isUserDefined() ) {
+ return '';
+ }
+
+ $title = $result[0]->getDiWikiPage()->getTitle();
+
+ if ( !$title instanceof \Title ) {
+ return '';
+ }
+
+ $proplink = $this->getLinker()->link(
+ $title,
+ htmlspecialchars( $result[0]->getLabel() ),
+ [],
+ [ 'action' => 'view' ]
+ );
+
+ return $this->msg( 'smw-special-wantedproperties-template' )
+ ->rawParams( $proplink )
+ ->params( $result[1] )
+ ->escaped();
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of SMWDIProperty|SMWDIError
+ */
+ function getResults( $requestoptions ) {
+ $this->listLookup = $this->store->getWantedPropertiesSpecial( $requestoptions );
+ return $this->listLookup->fetchList();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php
new file mode 100644
index 00000000..7439d2f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php
@@ -0,0 +1,280 @@
+<?php
+
+namespace SMW;
+
+use SMWDataItem;
+use SMWQueryResult;
+
+/**
+ * Abstract class that supports the aggregation and distributive calculation
+ * of numerical data.
+ *
+ * @since 1.9
+ *
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+/**
+ * Abstract class that supports the aggregation and distributive calculation
+ * of numerical data. Supports the distribution parameter, and related
+ * parameters that allows the user to choose between regular behavior or
+ * generating a distribution of values.
+ *
+ * For example, this result set: foo bar baz foo bar bar ohi
+ * Will be turned into
+ * * bar (3)
+ * * foo (2)
+ * * baz (1)
+ * * ohi (1)
+ *
+ * @ingroup QueryPrinter
+ */
+abstract class AggregatablePrinter extends ResultPrinter {
+
+ /**
+ * Create the formats output given the result data and return it.
+ *
+ * @since 1.7
+ *
+ * @param array $data label => value
+ */
+ protected abstract function getFormatOutput( array $data );
+
+ /**
+ * Method gets called right before the result is returned
+ * in case there are values to display. It is meant for
+ * adding resources such as JS and CSS only needed for this
+ * format when it has actual values.
+ *
+ * @since 1.7
+ */
+ protected function addResources() {
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWResultPrinter::getResultText()
+ */
+ protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
+ $data = $this->getResults( $queryResult, $outputMode );
+
+ if ( $data === [] ) {
+ $queryResult->addErrors( [
+ $this->msg( 'smw-qp-empty-data' )->inContentLanguage()->text()
+ ] );
+ return '';
+ } else {
+ $this->applyDistributionParams( $data );
+ $this->addResources();
+ return $this->getFormatOutput( $data );
+ }
+ }
+
+ /**
+ * Apply the distribution specific parameters.
+ *
+ * @since 1.7
+ *
+ * @param array $data
+ */
+ protected function applyDistributionParams( array &$data ) {
+ if ( $this->params['distributionsort'] == 'asc' ) {
+ asort( $data, SORT_NUMERIC );
+ }
+ elseif ( $this->params['distributionsort'] == 'desc' ) {
+ arsort( $data, SORT_NUMERIC );
+ }
+
+ if ( $this->params['distributionlimit'] !== false ) {
+ $data = array_slice( $data, 0, $this->params['distributionlimit'], true );
+ }
+ }
+
+ /**
+ * Gets and processes the results so they can be fed directly to the
+ * getFormatOutput method. They are returned as an array with the keys
+ * being the labels and the values being their corresponding (numeric) values.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $result
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getResults( SMWQueryResult $result, $outputMode ) {
+ if ( $this->params['distribution'] ) {
+ return $this->getDistributionResults( $result, $outputMode );
+ }
+ else {
+ return $this->getNumericResults( $result, $outputMode );
+ }
+ }
+
+ /**
+ * Counts all the occurrences of all values in the query result,
+ * and returns an array with as key the value and as value the count.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $result
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getDistributionResults( SMWQueryResult $result, $outputMode ) {
+ $values = [];
+
+ while ( /* array of SMWResultArray */ $row = $result->getNext() ) { // Objects (pages)
+ for ( $i = 0, $n = count( $row ); $i < $n; $i++ ) { // SMWResultArray for a sinlge property
+ while ( ( /* SMWDataValue */ $dataValue = $row[$i]->getNextDataValue() ) !== false ) { // Data values
+
+ // Get the HTML for the tag content. Pages are linked, other stuff is just plaintext.
+ if ( $dataValue->getTypeID() == '_wpg' ) {
+ $value = $dataValue->getTitle()->getText();
+ }
+ else {
+ $value = $dataValue->getShortText( $outputMode, $this->getLinker( false ) );
+ }
+
+ if ( !array_key_exists( $value, $values ) ) {
+ $values[$value] = 0;
+ }
+
+ $values[$value]++;
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns an array with the numerical data in the query result.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getNumericResults( SMWQueryResult $res, $outputMode ) {
+ $values = [];
+
+ // print all result rows
+ while ( $subject = $res->getNext() ) {
+ $dataValue = $subject[0]->getNextDataValue();
+
+ if ( $dataValue !== false ) {
+ $name = $dataValue->getShortWikiText();
+
+ foreach ( $subject as $field ) {
+
+ // Use the aggregation parameter to determine the source of
+ // the number composition
+ if ( $this->params['aggregation'] === 'property' ) {
+ $name = $field->getPrintRequest()->getLabel();
+ }
+
+ // Aggregated array key depends on the mainlabel which is
+ // either the subject or a printout property
+ if ( $this->params['mainlabel'] === '-' ) {
+
+ // In case of '-', getNextDataValue() already shifted the
+ // array forward which means the first column
+ // ( $subject[0] == $field ) contains a possible value
+ // and has to be collected as well
+ if ( ( $subject[0] == $field ) && $dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_NUMBER ) {
+ $value = $dataValue->getDataItem( )->getNumber();
+ $values[$name] = isset( $values[$name] ) ? $values[$name] + $value : $value;
+ }
+ }
+
+ while ( ( /* SMWDataItem */ $dataItem = $field->getNextDataItem() ) !== false ) {
+ $this->addNumbersForDataItem( $dataItem, $values, $name );
+ }
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Adds all numbers contained in a dataitem to the list.
+ *
+ * @since 1.7
+ *
+ * @param SMWDataItem $dataItem
+ * @param array $values
+ * @param string $name
+ */
+ protected function addNumbersForDataItem( SMWDataItem $dataItem, array &$values, $name ) {
+ switch ( $dataItem->getDIType() ) {
+ case SMWDataItem::TYPE_NUMBER:
+ // Collect and aggregate values for the same array key
+ $value = $dataItem->getNumber();
+ if ( !isset( $values[$name] ) ) {
+ $values[$name] = 0;
+ }
+ $values[$name] += $value;
+ break;
+ case SMWDataItem::TYPE_CONTAINER:
+ foreach ( $dataItem->getDataItems() as $di ) {
+ $this->addNumbersForDataItem( $di, $values, $name );
+ }
+ break;
+ default:
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions['distribution'] = [
+ 'name' => 'distribution',
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-distribution',
+ ];
+
+ $definitions['distributionsort'] = [
+ 'name' => 'distributionsort',
+ 'type' => 'string',
+ 'default' => 'none',
+ 'message' => 'smw-paramdesc-distributionsort',
+ 'values' => [ 'asc', 'desc', 'none' ],
+ ];
+
+ $definitions['distributionlimit'] = [
+ 'name' => 'distributionlimit',
+ 'type' => 'integer',
+ 'default' => false,
+ 'manipulatedefault' => false,
+ 'message' => 'smw-paramdesc-distributionlimit',
+ 'lowerbound' => 1,
+ ];
+
+ $definitions['aggregation'] = [
+ 'message' => 'smw-paramdesc-aggregation',
+ 'default' => 'subject',
+ 'values' => [ 'property', 'subject' ],
+ ];
+
+ return $definitions;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php
new file mode 100644
index 00000000..f472132e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace SMW;
+
+use Sanitizer;
+use SMWQueryResult;
+
+/**
+ * Result printer to print results in UNIX-style DSV (deliminter separated value) format.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DsvResultPrinter extends FileExportPrinter {
+
+ protected $separator = ':';
+ protected $fileName = 'result.dsv';
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.6
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+
+ // Do not allow backspaces as delimiter, as they'll break stuff.
+ if ( trim( $params['separator'] ) != '\\' ) {
+ $this->separator = trim( $params['separator'] );
+ }
+
+ $this->fileName = str_replace( ' ', '_', $params['filename'] );
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return 'text/dsv';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return $this->fileName;
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_dsv' )->text();
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $outputMode == SMW_OUTPUT_FILE ) { // Make the DSV file.
+ return $this->getResultFileContents( $res );
+ }
+ else { // Create a link pointing to the DSV file.
+ return $this->getLinkToFile( $res, $outputMode );
+ }
+ }
+
+ /**
+ * Returns the query result in DSV.
+ *
+ * @since 1.6
+ *
+ * @param SMWQueryResult $res
+ *
+ * @return string
+ */
+ protected function getResultFileContents( SMWQueryResult $queryResult ) {
+ $lines = [];
+
+ if ( $this->mShowHeaders ) {
+ $headerItems = [];
+
+ foreach ( $queryResult->getPrintRequests() as $printRequest ) {
+ $headerItems[] = $printRequest->getLabel();
+ }
+
+ $lines[] = $this->getDSVLine( $headerItems );
+ }
+
+ // Loop over the result objects (pages).
+ while ( $row = $queryResult->getNext() ) {
+ $rowItems = [];
+
+ /**
+ * Loop over their fields (properties).
+ * @var SMWResultArray $field
+ */
+ foreach ( $row as $field ) {
+ $itemSegments = [];
+
+ // Loop over all values for the property.
+ while ( ( $object = $field->getNextDataValue() ) !== false ) {
+ $itemSegments[] = Sanitizer::decodeCharReferences( $object->getWikiValue() );
+ }
+
+ // Join all values into a single string, separating them with comma's.
+ $rowItems[] = implode( ',', $itemSegments );
+ }
+
+ $lines[] = $this->getDSVLine( $rowItems );
+ }
+
+ return implode( "\n", $lines );
+ }
+
+ /**
+ * Returns a single DSV line.
+ *
+ * @since 1.6
+ *
+ * @param array $fields
+ *
+ * @return string
+ */
+ protected function getDSVLine( array $fields ) {
+ return implode( $this->separator, array_map( [ $this, 'encodeDSV' ], $fields ) );
+ }
+
+ /**
+ * Encodes a single DSV.
+ *
+ * @since 1.6
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ protected function encodeDSV( $value ) {
+ // TODO
+ // \nnn or \onnn or \0nnn for the character with octal value nnn
+ // \xnn for the character with hexadecimal value nn
+ // \dnnn for the character with decimal value nnn
+ // \unnnn for a hexadecimal Unicode literal.
+ return str_replace(
+ [ '\n', '\r', '\t', '\b', '\f', '\\', $this->separator ],
+ [ "\n", "\r", "\t", "\b", "\f", '\\\\', "\\$this->separator" ],
+ $value
+ );
+ }
+
+ /**
+ * Returns html for a link to a query that returns the DSV file.
+ *
+ * @since 1.6
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode
+ *
+ * @return string
+ */
+ protected function getLinkToFile( SMWQueryResult $res, $outputMode ) {
+ // yes, our code can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = ( $outputMode == SMW_OUTPUT_HTML );
+ return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( wfMessage( 'smw_dsv_link' )->text() );
+
+ $params['limit']->setDefault( 100 );
+
+ $params[] = [
+ 'name' => 'separator',
+ 'message' => 'smw-paramdesc-dsv-separator',
+ 'default' => $this->separator,
+ 'aliases' => 'sep',
+ ];
+
+ $params[] = [
+ 'name' => 'filename',
+ 'message' => 'smw-paramdesc-dsv-filename',
+ 'default' => $this->fileName,
+ ];
+
+ return $params;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php
new file mode 100644
index 00000000..d067a255
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW;
+
+use SMWQueryResult;
+use Title;
+
+/**
+ * Printer for embedded data.
+ *
+ * Embeds in the page output the contents of the pages in the query result set.
+ * Printouts are ignored: it only matters which pages were returned by the query.
+ * The optional "titlestyle" formatting parameter can be used to apply a format to
+ * the headings for the page titles. If "titlestyle" is not specified, a <h1> tag is
+ * used.
+ *
+ * @license GNU GPL v2+
+ * @since 1.7
+ *
+ * @author Fernando Correia
+ * @author Markus Krötzsch
+ */
+class EmbeddedResultPrinter extends ResultPrinter {
+
+ protected $m_showhead;
+ protected $m_embedformat;
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.7
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+
+ $this->m_showhead = !$params['embedonly'];
+ $this->m_embedformat = $params['embedformat'];
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_embedded' )->text();
+ }
+
+ /**
+ * @see ResultPrinter::isDeferrable
+ *
+ * {@inheritDoc}
+ */
+ public function isDeferrable() {
+ return true;
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+
+ // Ensure that there is an annotation block in place before starting the
+ // parse and transclution process. Unfortunately we are unable to block
+ // the inclusion of categories which are attached to a MediaWiki
+ // object we have no immediate access or control.
+ $this->transcludeAnnotation = false;
+
+ global $wgParser;
+ // No page should embed itself, find out who we are:
+ if ( $wgParser->getTitle() instanceof Title ) {
+ $title = $wgParser->getTitle()->getPrefixedText();
+ } else { // this is likely to be in vain -- this case is typical if we run on special pages
+ global $wgTitle;
+ $title = $wgTitle->getPrefixedText();
+ }
+
+ // print header
+ $result = '';
+ $footer = '';
+ $embstart = '';
+ $embend = '';
+ $headstart = '';
+ $headend = '';
+ $this->hasTemplates = true;
+
+ switch ( $this->m_embedformat ) {
+ case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
+ $headstart = '<' . $this->m_embedformat . '>';
+ $headend = '</' . $this->m_embedformat . ">\n";
+ break;
+ case 'ul': case 'ol':
+ $result .= '<' . $this->m_embedformat . '>';
+ $footer = '</' . $this->m_embedformat . '>';
+ $embstart = '<li>';
+ $headend = "<br />\n";
+ $embend = "</li>\n";
+ break;
+ }
+
+ // Print all result rows:
+ foreach ( $res->getResults() as $diWikiPage ) {
+ if ( $diWikiPage instanceof DIWikiPage ) { // ensure that we deal with title-likes
+ $dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null );
+ $result .= $embstart;
+
+ if ( $this->m_showhead ) {
+ $result .= $headstart . $dvWikiPage->getLongWikiText( $this->mLinker ) . $headend;
+ }
+
+ if ( $dvWikiPage->getLongWikiText() != $title ) {
+ if ( $diWikiPage->getNamespace() == NS_MAIN ) {
+ $result .= '{{:' . $diWikiPage->getDBkey() . '}}';
+ } else {
+ $result .= '{{' . $dvWikiPage->getLongWikiText() . '}}';
+ }
+ } else { // block recursion
+ $result .= '<b>' . $dvWikiPage->getLongWikiText() . '</b>';
+ }
+
+ $result .= $embend;
+ }
+ }
+
+ // show link to more results
+ if ( $this->linkFurtherResults( $res ) ) {
+ $result .= $embstart
+ . $this->getFurtherResultsLink( $res, $outputMode )->getText( SMW_OUTPUT_WIKI, $this->mLinker )
+ . $embend;
+ }
+
+ $result .= $footer;
+
+ return $result;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions[] = [
+ 'name' => 'embedformat',
+ 'message' => 'smw-paramdesc-embedformat',
+ 'default' => 'h1',
+ 'values' => [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul' ],
+ ];
+
+ $definitions[] = [
+ 'name' => 'embedonly',
+ 'type' => 'boolean',
+ 'message' => 'smw-paramdesc-embedonly',
+ 'default' => false,
+ ];
+
+
+ return $definitions;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php
new file mode 100644
index 00000000..990e11d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace SMW;
+
+use FormatJson;
+use SMWQueryResult;
+
+/**
+ * Print links to JSON files representing query results.
+ *
+ * @see http://www.semantic-mediawiki.org/wiki/Help:JSON_format
+ *
+ * @since 1.5.3
+ *
+ *
+ * @license GNU GPL v2 or later
+ * @author mwjames
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Fabian Howahl
+ */
+
+/**
+ * Print links to JSON files representing query results.
+ *
+ * @ingroup QueryPrinter
+ */
+class JsonResultPrinter extends FileExportPrinter {
+
+ /**
+ * Returns human readable label for this printer
+ * @codeCoverageIgnore
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->msg( 'smw_printername_json' )->text();
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return 'application/json';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+
+ if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) !== '' ) {
+ return str_replace( ' ', '_', $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) . '.json';
+ }
+
+ return 'result.json';
+ }
+
+ /**
+ * Returns a filename that is to be sent to the caller
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode integer
+ *
+ * @return string
+ */
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+
+ if ( $outputMode == SMW_OUTPUT_FILE ) {
+
+ // No results, just bailout
+ if ( $res->getCount() == 0 ){
+ return $this->params['default'] !== '' ? $this->params['default'] : '';
+ }
+
+ $flags = $this->params['prettyprint'] ? JSON_PRETTY_PRINT : 0;
+ $flags = $flags | ( $this->params['unescape'] ? JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES : 0 );
+
+ // Serialize queryResult
+ if ( isset( $this->params['type'] ) && $this->params['type'] === 'simple' ) {
+ $result = $this->serializeAsSimpleList( $res );
+ } else {
+ $result = array_merge(
+ $res->serializeToArray(),
+ [ 'rows' => $res->getCount() ]
+ );
+ }
+
+ $result = json_encode(
+ $result,
+ $flags
+ );
+
+ } else {
+ // Create a link that points to the JSON file
+ $result = $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+
+ // Code can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = $outputMode == SMW_OUTPUT_HTML;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ * @codeCoverageIgnore
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( $this->msg( 'smw_json_link' )->inContentLanguage()->text() );
+
+ $params['limit']->setDefault( 100 );
+
+ $params['type'] = [
+ 'values' => [ 'simple', 'full' ],
+ 'default' => 'full',
+ 'message' => 'smw-paramdesc-json-type',
+ ];
+
+ $params['prettyprint'] = [
+ 'type' => 'boolean',
+ 'default' => '',
+ 'message' => 'smw-paramdesc-prettyprint',
+ ];
+
+ $params['unescape'] = [
+ 'type' => 'boolean',
+ 'default' => '',
+ 'message' => 'smw-paramdesc-json-unescape',
+ ];
+
+ return $params;
+ }
+
+ private function serializeAsSimpleList( $res ) {
+
+ $result = [];
+
+ while ( $row = $res->getNext() ) {
+ $item = [];
+ $subject = '';
+
+ foreach ( $row as /* SMWResultArray */ $field ) {
+ $label = $field->getPrintRequest()->getLabel();
+
+ if ( $label === '' ) {
+ continue;
+ }
+
+ $values = [];
+ $subject = $field->getResultSubject()->getHash();
+
+ while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
+ $values[] = $dataValue->getWikiValue();
+ }
+
+ $item[$label] = $values;
+ }
+
+ if ( $this->params['mainlabel'] === '-' || $subject === '' ) {
+ $result[] = $item;
+ } else {
+ $result[$subject] = $item;
+ }
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php
new file mode 100644
index 00000000..1e975162
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW;
+
+use FormatJson;
+use Html;
+use SMWOutputs;
+use SMWQueryResult;
+
+/**
+ * Base class for result printers that use the serialized results
+ *
+ * @since 1.9
+ *
+ * @license GNU GPL v2 or later
+ * @author mwjames
+ */
+abstract class RawResultPrinter extends ResultPrinter {
+
+ /**
+ * Returns html output.
+ *
+ * @since 1.9
+ */
+ abstract protected function getHtml( array $data );
+
+ /**
+ * Convenience method to register resources
+ *
+ * @since 1.9
+ *
+ * @param string $resource
+ */
+ protected function addResources( $resource ) {
+ SMWOutputs::requireResource( $resource );
+ }
+
+ /**
+ * Convenience method to create a unique id
+ *
+ * @since 1.9
+ */
+ protected function getId( ) {
+ return 'smw-' . uniqid();
+ }
+
+ /**
+ * Convenience method generating a visual placeholder before any
+ * JS is registered to indicate that resources (JavaScript, CSS)
+ * are being loaded and once ready ensure to set
+ * ( '.smw-spinner' ).hide()
+ *
+ * @since 1.9
+ */
+ protected function createLoadingHtmlPlaceholder() {
+ $this->addResources( 'ext.smw.style' );
+
+ return Html::rawElement(
+ 'div',
+ [ 'class' => 'smw-spinner left mw-small-spinner' ],
+ Html::element(
+ 'p',
+ [ 'class' => 'text' ],
+ $this->msg( 'smw-livepreview-loading' )->inContentLanguage()->text()
+ )
+ );
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ protected function loading() {
+ return $this->createLoadingHtmlPlaceholder();
+ }
+
+ /**
+ * Convenience method to encode output data
+ *
+ * @since 1.9
+ *
+ * @param string $id
+ * @param array $data
+ */
+ protected function encodeToJsonForId( $id, $data ) {
+ SMWOutputs::requireHeadItem(
+ $id,
+ $this->getSkin()->makeVariablesScript( [ $id => FormatJson::encode( $data ) ]
+ ) );
+
+ return $this;
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ protected function encode( $id, $data ) {
+ return $this->encodeToJsonForId( $id, $data );
+ }
+
+ /**
+ * Returns serialised content
+ *
+ * @see SMWResultPrinter::getResultText()
+ *
+ * @param SMWQueryResult $queryResult
+ * @param $outputMode
+ *
+ * @return string
+ */
+ protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
+
+ // Add parameters that are only known to the specific printer
+ $ask = $queryResult->getQuery()->toArray();
+ foreach ( $this->params as $key => $value ) {
+ if ( is_string( $value ) || is_integer( $value ) || is_bool( $value ) ) {
+ $ask['parameters'][$key] = $value;
+ }
+ }
+
+ // Combine all data into one object
+ $data = [
+ 'query' => [
+ 'result' => $queryResult->toArray(),
+ 'ask' => $ask
+ ]
+ ];
+
+ return $this->getHtml( $data );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php
new file mode 100644
index 00000000..24006dbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW;
+
+use SMW\Query\PrintRequest;
+use SMWExporter;
+use SMWQueryResult;
+use SMWRDFXMLSerializer;
+use SMWTurtleSerializer;
+
+/**
+ * Printer class for generating RDF output
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class RdfResultPrinter extends FileExportPrinter {
+
+ /**
+ * The syntax to be used for export. May be 'rdfxml' or 'turtle'.
+ */
+ protected $syntax;
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.7
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+ $this->syntax = $params['syntax'];
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return $this->syntax == 'turtle' ? 'application/x-turtle' : 'application/xml';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return $this->syntax == 'turtle' ? 'result.ttl' : 'result.rdf';
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_rdf' )->text();
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $outputMode == SMW_OUTPUT_FILE ) { // make RDF file
+ $serializer = $this->syntax == 'turtle' ? new SMWTurtleSerializer() : new SMWRDFXMLSerializer();
+ $serializer->startSerialization();
+ $serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ while ( $row = $res->getNext() ) {
+ $subjectDi = reset( $row )->getResultSubject();
+ $data = SMWExporter::getInstance()->makeExportDataForSubject( $subjectDi );
+
+ foreach ( $row as $resultarray ) {
+ $printreq = $resultarray->getPrintRequest();
+ $property = null;
+
+ switch ( $printreq->getMode() ) {
+ case PrintRequest::PRINT_PROP:
+ $property = $printreq->getData()->getDataItem();
+ break;
+ case PrintRequest::PRINT_CATS:
+ $property = new DIProperty( '_TYPE' );
+ break;
+ case PrintRequest::PRINT_CCAT:
+ // not serialised right now
+ break;
+ case PrintRequest::PRINT_THIS:
+ // ignored here (object is always included in export)
+ break;
+ }
+
+ if ( !is_null( $property ) ) {
+ SMWExporter::getInstance()->addPropertyValues( $property, $resultarray->getContent(), $data, $subjectDi );
+ }
+ }
+ $serializer->serializeExpData( $data );
+ }
+
+ $serializer->finishSerialization();
+
+ return $serializer->flushContent();
+ } else { // just make link to feed
+ $this->isHTML = ( $outputMode == SMW_OUTPUT_HTML ); // yes, our code can be viewed as HTML if requested, no more parsing needed
+
+ return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+ }
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions['limit']->setDefault( 100 );
+
+ $definitions['searchlabel']->setDefault( wfMessage( 'smw_rdf_link' )->inContentLanguage()->text() );
+
+ $definitions[] = [
+ 'name' => 'syntax',
+ 'message' => 'smw-paramdesc-rdfsyntax',
+ 'values' => [ 'rdfxml', 'turtle' ],
+ 'default' => 'rdfxml',
+ ];
+
+ return $definitions;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php
new file mode 100644
index 00000000..06884ffd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * This special page (Special:ExportRDF) for MediaWiki implements an OWL-export of semantic data,
+ * gathered both from the annotations in articles, and from metadata already
+ * present in the database.
+ *
+ * @ingroup SMWSpecialPage
+ * @ingroup SpecialPage
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SMWSpecialOWLExport extends SpecialPage {
+
+ /// Export controller object to be used for serializing data
+ protected $export_controller;
+
+ public function __construct() {
+ parent::__construct( 'ExportRDF' );
+ }
+
+ public function execute( $page ) {
+ $this->setHeaders();
+ global $wgOut, $wgRequest;
+
+ $wgOut->setPageTitle( wfMessage( 'exportrdf' )->text() );
+
+ // see if we can find something to export:
+ $page = is_null( $page ) ? $wgRequest->getVal( 'page' ) : rawurldecode( $page );
+ $pages = false;
+
+ if ( !is_null( $page ) || $wgRequest->getCheck( 'page' ) ) {
+ $page = is_null( $page ) ? $wgRequest->getCheck( 'text' ) : $page;
+
+ if ( $page !== '' ) {
+ $pages = [ $page ];
+ }
+ }
+
+ if ( $pages === false && $wgRequest->getCheck( 'pages' ) ) {
+ $pageBlob = $wgRequest->getText( 'pages' );
+
+ if ( $pageBlob !== '' ) {
+ $pages = explode( "\n", $wgRequest->getText( 'pages' ) );
+ }
+ }
+
+ if ( $pages !== false ) {
+ $this->exportPages( $pages );
+ return;
+ } else {
+ $offset = $wgRequest->getVal( 'offset' );
+
+ if ( isset( $offset ) ) {
+ $this->startRDFExport();
+ $this->export_controller->printPageList( $offset );
+ return;
+ } else {
+ $stats = $wgRequest->getVal( 'stats' );
+
+ if ( isset( $stats ) ) {
+ $this->startRDFExport();
+ $this->export_controller->printWikiInfo();
+ return;
+ }
+ }
+ }
+
+ // Nothing exported yet; show user interface:
+ $this->showForm();
+ }
+
+ /**
+ * Create the HTML user interface for this special page.
+ */
+ protected function showForm() {
+ global $wgOut, $wgUser, $smwgAllowRecursiveExport, $smwgExportBacklinks, $smwgExportAll;
+
+ $html = '<form name="tripleSearch" action="" method="POST">' . "\n" .
+ '<p>' . wfMessage( 'smw_exportrdf_docu' )->text() . "</p>\n" .
+ '<input type="hidden" name="postform" value="1"/>' . "\n" .
+ '<textarea name="pages" cols="40" rows="10"></textarea><br />' . "\n";
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgAllowRecursiveExport ) {
+ $html .= '<input type="checkbox" name="recursive" value="1" id="rec">&#160;<label for="rec">' . wfMessage( 'smw_exportrdf_recursive' )->text() . '</label></input><br />' . "\n";
+ }
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgExportBacklinks ) {
+ $html .= '<input type="checkbox" name="backlinks" value="1" default="true" id="bl">&#160;<label for="bl">' . wfMessage( 'smw_exportrdf_backlinks' )->text() . '</label></input><br />' . "\n";
+ }
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgExportAll ) {
+ $html .= '<br />';
+ $html .= '<input type="text" name="date" value="' . date( DATE_W3C, mktime( 0, 0, 0, 1, 1, 2000 ) ) . '" id="date">&#160;<label for="ea">' . wfMessage( 'smw_exportrdf_lastdate' )->text() . '</label></input><br />' . "\n";
+ }
+
+ $html .= '<br /><input type="submit" value="' . wfMessage( 'smw_exportrdf_submit' )->text() . "\"/>\n</form>";
+
+ $wgOut->addHTML( $html );
+ }
+
+ /**
+ * Prepare $wgOut for printing non-HTML data.
+ */
+ protected function startRDFExport() {
+ global $wgOut, $wgRequest;
+
+ $syntax = $wgRequest->getText( 'syntax' );
+
+ if ( $syntax === '' ) {
+ $syntax = $wgRequest->getVal( 'syntax' );
+ }
+
+ $wgOut->disable();
+ ob_start();
+
+ if ( $syntax == 'turtle' ) {
+ $mimetype = 'application/x-turtle'; // may change to 'text/turtle' at some time, watch Turtle development
+ $serializer = new SMWTurtleSerializer();
+ } else { // rdfxml as default
+ // Only use rdf+xml mimetype if explicitly requested (browsers do
+ // not support it by default).
+ // We do not add this parameter to RDF links within the export
+ // though; it is only meant to help some tools to see that HTML
+ // included resources are RDF (from there on they should be fine).
+ $mimetype = ( $wgRequest->getVal( 'xmlmime' ) == 'rdf' ) ? 'application/rdf+xml' : 'application/xml';
+ $serializer = new SMWRDFXMLSerializer();
+ }
+
+ header( "Content-type: $mimetype; charset=UTF-8" );
+
+ $this->export_controller = new SMWExportController( $serializer );
+ }
+
+ /**
+ * Export the given pages to RDF.
+ * @param array $pages containing the string names of pages to be exported
+ */
+ protected function exportPages( $pages ) {
+ global $wgRequest, $smwgExportBacklinks, $wgUser, $smwgAllowRecursiveExport;
+
+ // Effect: assume "no" from missing parameters generated by checkboxes.
+ $postform = $wgRequest->getText( 'postform' ) == 1;
+
+ $recursive = 0; // default, no recursion
+ $rec = $wgRequest->getText( 'recursive' );
+
+ if ( $rec === '' ) {
+ $rec = $wgRequest->getVal( 'recursive' );
+ }
+
+ if ( ( $rec == '1' ) && ( $smwgAllowRecursiveExport || $wgUser->isAllowed( 'delete' ) ) ) {
+ $recursive = 1; // users may be allowed to switch it on
+ }
+
+ $backlinks = $smwgExportBacklinks; // default
+ $bl = $wgRequest->getText( 'backlinks' );
+
+ if ( $bl === '' ) {
+ // TODO: wtf? this does not make a lot of sense...
+ $bl = $wgRequest->getVal( 'backlinks' );
+ }
+
+ if ( ( $bl == '1' ) && ( $wgUser->isAllowed( 'delete' ) ) ) {
+ $backlinks = true; // admins can always switch on backlinks
+ } elseif ( ( $bl == '0' ) || ( '' == $bl && $postform ) ) {
+ $backlinks = false; // everybody can explicitly switch off backlinks
+ }
+
+ $date = $wgRequest->getText( 'date' );
+ if ( $date === '' ) {
+ $date = $wgRequest->getVal( 'date' );
+ }
+
+ if ( $date !== '' ) {
+ $timeint = strtotime( $date );
+ $stamp = date( "YmdHis", $timeint );
+ $date = $stamp;
+ }
+
+ // If it is a redirect then we don't want to generate triples other than
+ // the redirect target information
+ if ( isset( $pages[0] ) && ( $title = Title::newFromText( $pages[0] ) ) !== null && $title->isRedirect() ) {
+ $backlinks = false;
+ }
+
+ $this->startRDFExport();
+ $this->export_controller->enableBacklinks( $backlinks );
+ $this->export_controller->printPages( $pages, $recursive, $date );
+ }
+
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php
new file mode 100644
index 00000000..1fe7ad8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php
@@ -0,0 +1,387 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\Utils\HtmlTabs;
+use SMW\Page\ListPager;
+use SMW\Utils\HtmlColumns;
+use SMWDataItem as DataItem;
+use SMW\MediaWiki\Collator;
+use SMW\TypesRegistry;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMWInfolink as Infolink;
+use SMW\DataValues\TypesValue;
+use SMWErrorValue as ErrorValue;
+use SMW\Page\ListBuilder;
+
+/**
+ * This special page for MediaWiki provides information about available types
+ * and those related properties.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class SMWSpecialTypes extends SpecialPage {
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function __construct() {
+ parent::__construct( 'Types' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+ $out = $this->getOutput();
+
+ $out->addModuleStyles( 'ext.smw.page.styles' );
+ $out->addModules( 'smw.property.page' );
+
+ $params = Infolink::decodeParameters( $param, false );
+ $typeLabel = reset( $params );
+
+ if ( $typeLabel == false ) {
+ $out->setPageTitle( wfMessage( 'types' )->text() );
+ $html = $this->getTypesList();
+ } else {
+ $typeLabel = str_replace( '%', '-', $typeLabel );
+ $typeName = str_replace( '_', ' ', $typeLabel );
+ $out->setPageTitle( wfMessage( 'smw-types-title', $typeName )->text() );
+ $out->prependHTML( $this->getBreadcrumbLink() );
+ $html = $this->getPropertiesByType( $typeLabel );
+ }
+
+ $out->addHTML( $html );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataValue $dataValue
+ * @param Link $linker
+ *
+ * @return string
+ */
+ public function formatItem( $dataValue, $linker ) {
+
+ // Outdated property? Predefined property definition no longer exists?
+ if ( $dataValue->getDataItem()->getInterwiki() === SMW_SQL3_SMWIW_OUTDATED ) {
+ $dataItem = $dataValue->getDataItem();
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIWikiPage( $dataItem->getDBKey(), SMW_NS_PROPERTY ),
+ null
+ );
+
+ $dataValue->setOption( $dataValue::NO_TEXT_TRANSFORMATION, true );
+ $dataValue->setOption( $dataValue::SHORT_FORM, true );
+
+ return $dataValue->getWikiValue();
+ }
+
+ $searchlink = Infolink::newBrowsingLink(
+ '+', $dataValue->getWikiValue()
+ );
+
+ return $dataValue->getLongHTMLText( $linker ) . '&#160;' . $searchlink->getHTML( $linker );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'pages';
+ }
+
+ private function getTypesList() {
+
+ $this->addHelpLink( wfMessage( 'smw-specials-types-helplink' )->escaped(), true );
+
+ $typeLabels = DataTypeRegistry::getInstance()->getKnownTypeLabels();
+ $linker = smwfGetLinker();
+ asort( $typeLabels, SORT_STRING );
+
+ $primitive = TypesRegistry::getTypesByGroup( 'primitive' );
+ $compound = TypesRegistry::getTypesByGroup( 'compound' );
+
+ $pr_text = wfMessage( 'smw-type-primitive' )->text();
+ $cx_text = wfMessage( 'smw-type-contextual' )->text();
+ $cp_text = wfMessage( 'smw-type-compound' )->text();
+
+ $contents = [
+ $pr_text => [],
+ $cx_text => [],
+ $cp_text => []
+ ];
+
+ foreach ( $typeLabels as $typeId => $label ) {
+ $typeValue = TypesValue::newFromTypeId( $typeId );
+ $msgKey = 'smw-type' . str_replace( '_', '-', strtolower( $typeId ) );
+
+ $text = $typeValue->getLongHTMLText( $linker );
+
+ if ( wfMessage( $msgKey )->exists() ) {
+ $msg = wfMessage( $msgKey, '' )->parse();
+ $text .= Html::rawElement(
+ 'span' ,
+ [
+ 'class' => 'plainlinks',
+ 'style' => 'font-size:85%'
+ ],
+
+ // Remove the first two chars which are a localized
+ // diacritical, quotation mark
+ str_replace( mb_substr( $msg, 0, 2 ), '', $msg )
+ );
+ }
+
+ if ( isset( $primitive[$typeId] ) ) {
+ $contents[$pr_text][] = $typeValue->getLongHTMLText( $linker );
+ } elseif ( isset( $compound[$typeId] ) ) {
+ $contents[$cp_text][] = $text;
+ } else {
+ $contents[$cx_text][] = $text;
+ }
+ }
+
+ $htmlColumns = new HtmlColumns();
+ $htmlColumns->setColumnClass( 'smw-column-responsive' );
+
+ $htmlColumns->setContinueAbbrev(
+ wfMessage( 'listingcontinuesabbrev' )->text()
+ );
+
+ $htmlColumns->setColumns( 2 );
+ $htmlColumns->addContents( $contents, HtmlColumns::INDX_CONTENT );
+ $html = $htmlColumns->getHtml();
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'types' );
+
+ $htmlTabs->tab( 'smw-type-list', $this->msg( 'smw-type-tab-types' ) );
+ $htmlTabs->content( 'smw-type-list', "<div class='smw-page-navigation'>$html</div>" );
+
+ $html = $htmlTabs->buildHTML(
+ [
+ 'id' => 'smw-types',
+ 'class' => 'smw-types'
+ ]
+ );
+
+ return Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks smw-types-intro'
+ ],
+ wfMessage( 'smw_types_docu' )->parse()
+ ) . $html;
+ }
+
+ private function getPropertiesByType( $typeLabel ) {
+
+ $typeValue = DataValueFactory::getInstance()->newTypeIDValue(
+ TypesValue::TYPE_ID,
+ $typeLabel
+ );
+
+ if ( !$typeValue->isValid() ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'plainlinks smw-type-unknown'
+ ],
+ $this->msg( 'smw-special-types-no-such-type', $typeLabel )->escaped()
+ );
+ }
+
+ $this->addHelpLink( wfMessage( 'smw-specials-bytype-helplink', $typeLabel )->escaped(), true );
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $pagingLimit = $applicationFactory->getSettings()->dotGet( 'smwgPagingLimit.type' );
+
+ // not too useful, but we comply to this request
+ if ( $pagingLimit <= 0 ) {
+ return '';
+ }
+
+ $limit = $this->getRequest()->getVal( 'limit', $pagingLimit );
+ $offset = $this->getRequest()->getVal( 'offset', 0 );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->setLimit( $limit + 1 );
+ $requestOptions->setOffset( $offset );
+
+ $dataItems = $applicationFactory->getStore()->getPropertySubjects(
+ new DIProperty( '_TYPE' ),
+ $typeValue->getDataItem(),
+ $requestOptions
+ );
+
+ if ( $dataItems instanceof \Iterator ) {
+ $dataItems = iterator_to_array( $dataItems );
+ }
+
+ if ( !$requestOptions->ascending ) {
+ $dataItems = array_reverse( $dataItems );
+ }
+
+ $typeId = $typeValue->getDataItem()->getFragment();
+
+ $dataValue = DataValueFactory::getInstance()->newTypeIDValue(
+ $typeId
+ );
+
+ $label = htmlspecialchars( $typeValue->getWikiValue() );
+ $typeKey = 'smw-type' . str_replace( '_', '-', strtolower( $typeId ) );
+ $msgKey = wfMessage( $typeKey )->exists() ? $typeKey : 'smw-types-default';
+
+ $result = \Html::rawElement(
+ 'div',
+ [
+ 'class' => 'plainlinks smw-types-intro '. $typeKey
+ ],
+ wfMessage( $msgKey, str_replace( '_', ' ', $label ) )->parse() .
+ $this->find_extras( $dataValue, $typeId, $label )
+ );
+
+ $count = count( $dataItems );
+
+ if ( $count == 0 ) {
+ return $result;
+ }
+
+ $html = Html::rawElement(
+ 'div' ,
+ [
+ 'class' => 'smw-page-navigation'
+ ],
+ ListPager::pagination(
+ $this->getTitleFor( 'Types', $typeLabel ),
+ $limit,
+ $offset,
+ $count,
+ [ '_target' => '#smw-list' ]
+ ) . Html::rawElement(
+ 'div' ,
+ [
+ 'class' => 'smw-page-nav-note'
+ ],
+ wfMessage( 'smw_typearticlecount' )->numParams( min( $limit, $count ) )->text()
+ )
+ );
+
+ $listBuilder = new ListBuilder(
+ ApplicationFactory::getInstance()->getStore()
+ );
+
+ $listBuilder->setItemFormatter( [ $this, 'formatItem' ] );
+
+ $html .= $listBuilder->getColumnList(
+ $dataItems
+ );
+
+ $errors = $this->find_errors( $dataValue, $typeId, $label );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'type' );
+
+ $htmlTabs->setActiveTab(
+ $errors !== null ? 'smw-type-errors' : 'smw-type-list'
+ );
+
+ $htmlTabs->tab( 'smw-type-list', $this->msg( 'smw-type-tab-properties' ) );
+ $htmlTabs->content( 'smw-type-list', "<div>$html</div>" );
+
+ $htmlTabs->tab(
+ 'smw-type-errors',
+ $this->msg( 'smw-type-tab-errors' ),
+ [
+ 'hide' => $errors === null,
+ 'class' => 'smw-tab-warning'
+ ]
+ );
+
+ $htmlTabs->content( 'smw-type-errors', "<div>$errors</div>" );
+
+ return $result . $htmlTabs->buildHTML(
+ [
+ 'id' => 'smw-list',
+ 'class' => 'smw-types'
+ ]
+ );
+ }
+
+ private function find_errors( $dataValue, $typeId, $label ) {
+
+ $errors = [];
+
+ if ( $typeId === '_geo' && $dataValue instanceof ErrorValue ) {
+ $errors[] = Html::rawElement(
+ 'li',
+ [],
+ wfMessage( 'smw-types-extra-geo-not-available', $label )->parse()
+ );
+ }
+
+ if ( $errors !== [] ) {
+ return Html::rawElement(
+ 'ul',
+ [
+ 'class' => 'smw-page-content plainlinks'
+ ],
+ implode( '', $errors)
+ );
+ }
+ }
+
+ private function find_extras( $dataValue, $typeId, $label ) {
+
+ $html = '';
+
+ if ( $typeId === '_mlt_rec' ) {
+ $option = $dataValue->hasFeature( SMW_DV_MLTV_LCODE ) ? 1 : 2;
+ $html = '&nbsp;' . wfMessage( 'smw-types-extra-mlt-lcode', $label, $option )->parse();
+ }
+
+ $extra = 'smw-type-extra' . str_replace( '_', '-', $typeId );
+
+ if ( wfMessage( $extra )->exists() ) {
+ $html = '&nbsp;' . wfMessage( $extra )->parse();
+ }
+
+ return $html;
+ }
+
+ private function getBreadcrumbLink() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-breadcrumb-link'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-breadcrumb-arrow-right'
+ ]
+ ) .
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => SpecialPage::getTitleFor( 'Types')->getFullURL()
+ ],
+ $this->msg( 'types' )->escaped()
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php
new file mode 100644
index 00000000..9e5bea44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\Page\ListPager;
+use SMW\Page\ListBuilder;
+use SMW\SQLStore\SQLStore;
+use SMW\Utils\HtmlTabs;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Collator;
+
+/**
+ * Special page that lists available concepts
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SpecialConcepts extends \SpecialPage {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @see SpecialPage::__construct
+ */
+ public function __construct() {
+ parent::__construct( 'Concepts' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+
+ $this->setHeaders();
+ $out = $this->getOutput();
+ $out->addModuleStyles( 'ext.smw.page.styles' );
+
+ $limit = $this->getRequest()->getVal( 'limit', 50 );
+ $offset = $this->getRequest()->getVal( 'offset', 0 );
+
+ $this->store = ApplicationFactory::getInstance()->getStore();
+
+ $diWikiPages = $this->fetchFromTable( $limit, $offset );
+ $html = $this->getHtml( $diWikiPages, $limit, $offset );
+
+ $this->addHelpLink( wfMessage( 'smw-helplink-concepts' )->escaped(), true );
+
+ $out->setPageTitle( $this->msg( 'concepts' )->text() );
+ $out->addHTML( $html );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return DIWikiPage[]
+ */
+ public function fetchFromTable( $limit, $offset ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $results = [];
+
+ $fields = [
+ 'smw_id',
+ 'smw_title'
+ ];
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_CONCEPT,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_proptable_hash IS NOT NULL',
+ 'concept_features > 0'
+ ];
+
+ $options = [
+ 'LIMIT' => $limit + 1,
+ 'OFFSET' => $offset,
+ ];
+
+ $res = $connection->select(
+ [
+ $connection->tableName( SQLStore::ID_TABLE ),
+ $connection->tableName( SQLStore::CONCEPT_TABLE )
+ ],
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options,
+ [
+ $connection->tableName( SQLStore::ID_TABLE ) => [ 'INNER JOIN', [ 'smw_id=s_id' ] ]
+ ]
+ );
+
+ foreach ( $res as $row ) {
+ $results[] = new DIWikiPage( $row->smw_title, SMW_NS_CONCEPT );
+ }
+
+ return $results;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param DIWikiPage[] $dataItems
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return string
+ */
+ public function getHtml( $dataItems, $limit, $offset ) {
+
+ if ( $this->store === null ) {
+ $this->store = ApplicationFactory::getInstance()->getStore();
+ }
+
+ $count = count( $dataItems );
+ $resultNumber = min( $limit, $count );
+
+ if ( $resultNumber == 0 ) {
+ $key = 'smw-special-concept-empty';
+ } else {
+ $key = 'smw-special-concept-count';
+ }
+
+ $listBuilder = new ListBuilder(
+ $this->store,
+ Collator::singleton()
+ );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'concept' );
+
+ $html = Html::rawElement(
+ 'div',
+ [ 'id' => 'mw-pages'],
+ Html::rawElement(
+ 'div',
+ [ 'class' => 'smw-page-navigation' ],
+ ListPager::pagination( $this->getPageTitle(), $limit, $offset, $count )
+ ) . Html::element(
+ 'div',
+ [ 'class' => $key, 'style' => 'margin-top:10px;margin-bottom:10px;' ],
+ $this->msg( $key, $resultNumber )->parse()
+ ) . $listBuilder->getColumnList( $dataItems )
+ );
+
+ $htmlTabs->tab( 'smw-concept-list', $this->msg( 'smw-concept-tab-list' ) );
+ $htmlTabs->content( 'smw-concept-list', $html );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-concept clearfix' ]
+ );
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-special-concept-docu plainlinks' ],
+ $this->msg( 'smw-special-concept-docu' )->parse()
+ ) . $html;
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'pages';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php
new file mode 100644
index 00000000..5bc84de0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Semantic MediaWiki SpecialPage base class
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Semantic MediaWiki SpecialPage base class
+ *
+ * @ingroup SpecialPage
+ * @codeCoverageIgnore
+ */
+class SpecialPage extends \SpecialPage {
+
+ /** @var Store */
+ protected $store = null;
+
+ /** @var Settings */
+ protected $settings = null;
+
+ /**
+ * @see SpecialPage::__construct
+ *
+ * @since 1.9
+ *
+ * @param $name
+ * @param $restriction
+ */
+ public function __construct( $name = '', $restriction = '' ) {
+ parent::__construct( $name, $restriction );
+ $this->store = StoreFactory::getStore();
+ }
+
+ /**
+ * Sets store instance
+ *
+ * @since 1.9
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ return $this;
+ }
+
+ /**
+ * Returns store object
+ *
+ * @since 1.9
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * Sets Settings object
+ *
+ * @since 1.9
+ *
+ * @param Settings $settings
+ */
+ public function setSettings( Settings $settings ) {
+ $this->settings = $settings;
+ return $this;
+ }
+
+ /**
+ * Returns Settings object
+ *
+ * @since 1.9
+ *
+ * @return Store
+ */
+ public function getSettings() {
+
+ if ( $this->settings === null ) {
+ $this->settings = Settings::newFromGlobals();
+ }
+
+ return $this->settings;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php
new file mode 100644
index 00000000..8a4ab4c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:Properties) for MediaWiki shows all
+ * used properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page for MediaWiki shows all used properties.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'Properties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+ $out = $this->getOutput();
+
+ $out->setPageTitle( $this->msg( 'properties' )->text() );
+
+ $page = new PropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit, $this->getRequest()->getVal( 'property' ) );
+
+ // Ensure locally collected output data is pushed to the output!
+ SMWOutputs::commitToOutputPage( $out );
+
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'pages';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php
new file mode 100644
index 00000000..23987442
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:UnusedProperties) for MediaWiki shows all
+ * unused properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page (Special:UnusedProperties) for MediaWiki shows all unused
+ * properties.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialUnusedProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'UnusedProperties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+
+ $out = $this->getOutput();
+
+ $out->setPageTitle( $this->msg( 'unusedproperties' )->text() );
+
+ $page = new UnusedPropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit, $this->getRequest()->getVal( 'property' ) );
+
+ // Ensure locally collected output data is pushed to the output!
+ SMWOutputs::commitToOutputPage( $out );
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php
new file mode 100644
index 00000000..79a9f681
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:WantedProperties) for MediaWiki shows all
+ * wanted properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page (Special:WantedProperties) for MediaWiki shows all wanted
+ * properties (used but not having a page).
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialWantedProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'WantedProperties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+
+ $out = $this->getOutput();
+
+ $out->addModuleStyles( [
+ 'ext.smw.special.style'
+ ] );
+
+ $out->setPageTitle( $this->msg( 'wantedproperties' )->text() );
+
+ $page = new WantedPropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+ $page->setTitle( $this->getPageTitle() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit );
+
+ // Ensure locally collected output data is pushed to the output!
+ // ?? still needed !!
+ SMWOutputs::commitToOutputPage( $out );
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php
new file mode 100644
index 00000000..17f760d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php
@@ -0,0 +1,462 @@
+<?php
+
+use SMW\Query\Excerpts;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryLinker;
+use SMW\Query\Result\ResolverJournal;
+use SMW\Query\ScoreSet;
+use SMW\SerializerFactory;
+
+/**
+ * Objects of this class encapsulate the result of a query in SMW. They
+ * provide access to the query result and printed data, and to some
+ * relevant query parameters that were used.
+ *
+ * Standard access is provided through the iterator function getNext(),
+ * which returns an array ("table row") of SMWResultArray objects ("table cells").
+ * It is also possible to access the set of result pages directly using
+ * getResults(). This is useful for printers that disregard printouts and
+ * only are interested in the actual list of pages.
+ *
+ *
+ * @ingroup SMWQuery
+ *
+ * @licence GNU GPL v2 or later
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWQueryResult {
+
+ /**
+ * Array of SMWDIWikiPage objects that are the basis for this result
+ * @var SMWDIWikiPage[]
+ */
+ protected $mResults;
+
+ /**
+ * Array of SMWPrintRequest objects, indexed by their natural hash keys
+ *
+ * @var PrintRequest[]
+ */
+ protected $mPrintRequests;
+
+ /**
+ * Are there more results than the ones given?
+ * @var boolean
+ */
+ protected $mFurtherResults;
+
+ /**
+ * The query object for which this is a result, must be set on create and is the source of
+ * data needed to create further result links.
+ * @var SMWQuery
+ */
+ protected $mQuery;
+
+ /**
+ * The SMWStore object used to retrieve further data on demand.
+ * @var SMWStore
+ */
+ protected $mStore;
+
+ /**
+ * Holds a value that belongs to a count query result
+ * @var integer|null
+ */
+ private $countValue;
+
+ /**
+ * Indicates whether results have been retrieved from cache or not
+ *
+ * @var boolean
+ */
+ private $isFromCache = false;
+
+ /**
+ * @var ResolverJournal
+ */
+ private $resolverJournal;
+
+ /**
+ * @var integer
+ */
+ private $serializer_version = 2;
+
+ /**
+ * @var ScoreSet
+ */
+ private $scoreSet;
+
+ /**
+ * @var Excerpts
+ */
+ private $excerpts;
+
+ /**
+ * Initialise the object with an array of SMWPrintRequest objects, which
+ * define the structure of the result "table" (one for each column).
+ *
+ * TODO: Update documentation
+ *
+ * @param PrintRequest[] $printRequests
+ * @param SMWQuery $query
+ * @param SMWDIWikiPage[] $results
+ * @param SMWStore $store
+ * @param boolean $furtherRes
+ */
+ public function __construct( array $printRequests, SMWQuery $query, array $results, SMWStore $store, $furtherRes = false ) {
+ $this->mResults = $results;
+ reset( $this->mResults );
+ $this->mPrintRequests = $printRequests;
+ $this->mFurtherResults = $furtherRes;
+ $this->mQuery = $query;
+ $this->mStore = $store;
+ $this->resolverJournal = new ResolverJournal();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ResolverJournal $resolverJournal
+ */
+ public function setResolverJournal( ResolverJournal $ResolverJournal ) {
+ $this->resolverJournal = $ResolverJournal;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return ResolverJournal
+ */
+ public function getResolverJournal() {
+ return $this->resolverJournal;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param boolean $isFromCache
+ */
+ public function setFromCache( $isFromCache ) {
+ $this->isFromCache = (bool)$isFromCache;
+ }
+
+ /**
+ * Only available by some stores that support the computation of scores.
+ *
+ * @since 3.0
+ *
+ * @param ScoreSet $scoreSet
+ */
+ public function setScoreSet( ScoreSet $scoreSet ) {
+ $this->scoreSet = $scoreSet;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ScoreSet|null
+ */
+ public function getScoreSet() {
+ return $this->scoreSet;
+ }
+
+ /**
+ * Only available by some stores that support the retrieval of excerpts.
+ *
+ * @since 3.0
+ *
+ * @param Excerpts $excerpts
+ */
+ public function setExcerpts( Excerpts $excerpts ) {
+ $this->excerpts = $excerpts;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Excerpts|null
+ */
+ public function getExcerpts() {
+ return $this->excerpts;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return $this->isFromCache;
+ }
+
+ /**
+ * Get the SMWStore object that this result is based on.
+ *
+ * @return SMWStore
+ */
+ public function getStore() {
+ return $this->mStore;
+ }
+
+ /**
+ * Return the next result row as an array of SMWResultArray objects, and
+ * advance the internal pointer.
+ *
+ * @return SMWResultArray[]|false
+ */
+ public function getNext() {
+ $page = current( $this->mResults );
+ next( $this->mResults );
+
+ if ( $page === false ) {
+ return false;
+ }
+
+ $row = [];
+
+ foreach ( $this->mPrintRequests as $p ) {
+ $resultArray = new SMWResultArray( $page, $p, $this->mStore );
+ $resultArray->setResolverJournal( $this->resolverJournal );
+ $resultArray->setQueryToken( $this->mQuery->getQueryToken() );
+ $row[] = $resultArray;
+ }
+
+ return $row;
+ }
+
+ /**
+ * Return number of available results.
+ *
+ * @return integer
+ */
+ public function getCount() {
+ return count( $this->mResults );
+ }
+
+ /**
+ * Return an array of SMWDIWikiPage objects that make up the
+ * results stored in this object.
+ *
+ * @return SMWDIWikiPage[]
+ */
+ public function getResults() {
+ return $this->mResults;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function reset() {
+ return reset( $this->mResults );
+ }
+
+ /**
+ * Returns the query object of the current result set
+ *
+ * @since 1.8
+ *
+ * @return SMWQuery
+ */
+ public function getQuery() {
+ return $this->mQuery;
+ }
+
+ /**
+ * Return the number of columns of result values that each row
+ * in this result set contains.
+ *
+ * @return integer
+ */
+ public function getColumnCount() {
+ return count( $this->mPrintRequests );
+ }
+
+ /**
+ * Return array of print requests (needed for printout since they contain
+ * property labels).
+ *
+ * @return PrintRequest[]
+ */
+ public function getPrintRequests() {
+ return $this->mPrintRequests;
+ }
+
+ /**
+ * Returns the query string defining the conditions for the entities to be
+ * returned.
+ *
+ * @return string
+ */
+ public function getQueryString() {
+ return $this->mQuery->getQueryString();
+ }
+
+ /**
+ * Would there be more query results that were not shown due to a limit?
+ *
+ * @return boolean
+ */
+ public function hasFurtherResults() {
+ return $this->mFurtherResults;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param integer $countValue
+ */
+ public function setCountValue( $countValue ) {
+ $this->countValue = (int)$countValue;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return integer|null
+ */
+ public function getCountValue() {
+ return $this->countValue;
+ }
+
+ /**
+ * Return error array, possibly empty.
+ *
+ * @return array
+ */
+ public function getErrors() {
+ // Just use query errors, as no errors generated in this class at the moment.
+ return $this->mQuery->getErrors();
+ }
+
+ /**
+ * Adds an array of erros.
+ *
+ * @param array $errors
+ */
+ public function addErrors( array $errors ) {
+ $this->mQuery->addErrors( $errors );
+ }
+
+ /**
+ * Create an SMWInfolink object representing a link to further query results.
+ * This link can then be serialised or extended by further params first.
+ * The optional $caption can be used to set the caption of the link (though this
+ * can also be changed afterwards with SMWInfolink::setCaption()). If empty, the
+ * message 'smw_iq_moreresults' is used as a caption.
+ *
+ * @param string|false $caption
+ *
+ * @return SMWInfolink
+ */
+ public function getQueryLink( $caption = false ) {
+
+ $link = QueryLinker::get( $this->mQuery );
+
+ $link->setCaption( $caption );
+ $link->setParameter( $this->mQuery->getOffset() + count( $this->mResults ), 'offset' );
+
+ return $link;
+ }
+
+ /**
+ * @deprecated since 2.5, use QueryResult::getQueryLink
+ *
+ * Returns an SMWInfolink object with the QueryResults print requests as parameters.
+ *
+ * @since 1.8
+ *
+ * @return SMWInfolink
+ */
+ public function getLink() {
+ return $this->getQueryLink();
+ }
+
+ /**
+ * @private
+ *
+ * @since 3.0
+ */
+ public function setSerializerVersion( $version ) {
+ $this->serializer_version = $version;
+ }
+
+ /**
+ * @see DISerializer::getSerializedQueryResult
+ * @since 1.7
+ * @return array
+ */
+ public function serializeToArray() {
+
+ $serializerFactory = new SerializerFactory();
+ $serializer = $serializerFactory->newQueryResultSerializer();
+ $serializer->version( $this->serializer_version );
+
+ $serialized = $serializer->serialize( $this );
+ reset( $this->mResults );
+
+ return $serialized;
+ }
+
+ /**
+ * Returns a serialized SMWQueryResult object with additional meta data
+ *
+ * This methods extends the serializeToArray() for additional meta
+ * that are useful when handling data via the api
+ *
+ * @note should be used instead of SMWQueryResult::serializeToArray()
+ * as this method contains additional informaion
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function toArray() {
+
+ $time = microtime( true );
+
+ // @note micro optimization: We call getSerializedQueryResult()
+ // only once and create the hash here instead of calling getHash()
+ // to avoid getSerializedQueryResult() being called again
+ // @note count + offset equals total therefore we deploy both values
+ $serializeArray = $this->serializeToArray();
+
+ return array_merge( $serializeArray, [
+ 'meta'=> [
+ 'hash' => md5( json_encode( $serializeArray ) ),
+ 'count' => $this->getCount(),
+ 'offset' => $this->mQuery->getOffset(),
+ 'source' => $this->mQuery->getQuerySource(),
+ 'time' => number_format( ( microtime( true ) - $time ), 6, '.', '' )
+ ]
+ ]
+ );
+ }
+
+ /**
+ * Returns result hash value
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHash( $type = null ) {
+
+ // Just iterate over available subjects to create a "quick" hash given
+ // that resolving the entire object tree is costly due to recursive
+ // processing of all data items including its printouts
+ if ( $type === 'quick' ) {
+ $hash = [];
+
+ foreach ( $this->mResults as $dataItem ) {
+ $hash[] = $dataItem->getHash();
+ }
+
+ reset( $this->mResults );
+ return 'q:' . md5( json_encode( $hash ) );
+ }
+
+ return md5( json_encode( $this->serializeToArray() ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php
new file mode 100644
index 00000000..438edf5a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php
@@ -0,0 +1,282 @@
+<?php
+
+use SMW\DataValueFactory;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryToken;
+use SMW\Query\Result\ResolverJournal;
+use SMW\Query\Result\ResultFieldMatchFinder;
+use SMWDataItem as DataItem;
+
+/**
+ * Container for the contents of a single result field of a query result,
+ * i.e. basically an array of SMWDataItems with some additional parameters.
+ * The content of the array is fetched on demand only.
+ *
+ * @ingroup SMWQuery
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWResultArray {
+
+ /**
+ * @var PrintRequest
+ */
+ private $mPrintRequest;
+
+ /**
+ * @var SMWDIWikiPage
+ */
+ private $mResult;
+
+ /**
+ * @var SMWStore
+ */
+ private $mStore;
+
+ /**
+ * @var SMWDataItem[]|false
+ */
+ private $mContent;
+
+ /**
+ * @var ResolverJournal
+ */
+ private $resolverJournal;
+
+ /**
+ * @var ResultFieldMatchFinder
+ */
+ private $resultFieldMatchFinder;
+
+ /**
+ * @var QueryToken
+ */
+ private $queryToken;
+
+ /**
+ * Constructor.
+ *
+ * @param SMWDIWikiPage $resultPage
+ * @param PrintRequest $printRequest
+ * @param SMWStore $store
+ */
+ public function __construct( SMWDIWikiPage $resultPage, PrintRequest $printRequest, SMWStore $store ) {
+ $this->mResult = $resultPage;
+ $this->mPrintRequest = $printRequest;
+ $this->mStore = $store;
+ $this->mContent = false;
+
+ // FIXME 3.0; Inject the object
+ $this->resultFieldMatchFinder = new ResultFieldMatchFinder( $store, $printRequest );
+ }
+
+ /**
+ * Get the SMWStore object that this result is based on.
+ *
+ * @return SMWStore
+ */
+ public function getStore() {
+ return $this->mStore;
+ }
+
+ /**
+ * Returns the SMWDIWikiPage object to which this SMWResultArray refers.
+ * If you only care for those objects, consider using SMWQueryResult::getResults()
+ * directly.
+ *
+ * @return SMWDIWikiPage
+ */
+ public function getResultSubject() {
+ return $this->mResult;
+ }
+
+ /**
+ * Temporary track what entities are used while being instantiated, so an external
+ * service can have access to the list without requiring to resolve the objects
+ * independently.
+ *
+ * @since 2.4
+ *
+ * @param ResolverJournal $resolverJournal
+ */
+ public function setResolverJournal( ResolverJournal $resolverJournal ) {
+ $this->resolverJournal = $resolverJournal;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryToken|null $queryToken
+ */
+ public function setQueryToken( QueryToken $queryToken = null ) {
+ $this->queryToken = $queryToken;
+ }
+
+ /**
+ * Returns an array of SMWDataItem objects that contain the results of
+ * the given print request for the given result object.
+ *
+ * @return SMWDataItem[]|false
+ */
+ public function getContent() {
+ $this->loadContent();
+ return $this->mContent;
+ }
+
+ /**
+ * Return a PrintRequest object describing what is contained in this
+ * result set.
+ *
+ * @return PrintRequest
+ */
+ public function getPrintRequest() {
+ return $this->mPrintRequest;
+ }
+
+ /**
+ * Return the next SMWDataItem object or false if no further object exists.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataItem|false
+ */
+ public function getNextDataItem() {
+ $this->loadContent();
+ $result = current( $this->mContent );
+
+ if ( $this->resolverJournal !== null && $result instanceof DataItem ) {
+ $this->resolverJournal->recordItem( $result );
+ }
+
+ next( $this->mContent );
+ return $result;
+ }
+
+ /**
+ * Set the internal pointer of the array of SMWDataItem objects to its first
+ * element. Return the first SMWDataItem object or false if the array is
+ * empty.
+ *
+ * @since 1.7.1
+ *
+ * @return SMWDataItem|false
+ */
+ public function reset() {
+ $this->loadContent();
+ return reset( $this->mContent );
+ }
+
+ /**
+ * Return an SMWDataValue object for the next SMWDataItem object or
+ * false if no further object exists.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataValue|false
+ */
+ public function getNextDataValue() {
+ $dataItem = $this->getNextDataItem();
+
+ if ( $dataItem === false ) {
+ return false;
+ }
+
+ if ( $this->mPrintRequest->getMode() == PrintRequest::PRINT_PROP &&
+ strpos( $this->mPrintRequest->getTypeID(), '_rec' ) !== false &&
+ $this->mPrintRequest->getParameter( 'index' ) !== false ) {
+
+ $recordValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $this->mPrintRequest->getData()->getDataItem()
+ );
+
+ $diProperty = $recordValue->getPropertyDataItemByIndex(
+ $this->mPrintRequest->getParameter( 'index' )
+ );
+ } elseif ( $this->mPrintRequest->isMode( PrintRequest::PRINT_PROP ) ) {
+ $diProperty = $this->mPrintRequest->getData()->getDataItem();
+ } elseif ( $this->mPrintRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ $diProperty = $this->mPrintRequest->getData()->getLastPropertyChainValue()->getDataItem();
+ } else {
+ $diProperty = null;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $diProperty
+ );
+
+ $dataValue->setContextPage(
+ $this->mResult
+ );
+
+ if ( $this->mPrintRequest->getOutputFormat() ) {
+ $dataValue->setOutputFormat( $this->mPrintRequest->getOutputFormat() );
+ }
+
+ if ( $this->resolverJournal !== null && $dataItem instanceof DataItem ) {
+ $this->resolverJournal->recordItem( $dataItem );
+ $this->resolverJournal->recordProperty( $diProperty );
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * Return the main text representation of the next SMWDataItem object
+ * in the specified format, or false if no further object exists.
+ *
+ * The parameter $linker controls linking of title values and should
+ * be some Linker object (or NULL for no linking).
+ *
+ * @param integer $outputMode
+ * @param mixed $linker
+ *
+ * @return string|false
+ */
+ public function getNextText( $outputMode, $linker = null ) {
+ $dataValue = $this->getNextDataValue();
+ if ( $dataValue !== false ) { // Print data values.
+ return $dataValue->getShortText( $outputMode, $linker );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Load results of the given print request and result subject. This is only
+ * done when needed.
+ */
+ protected function loadContent() {
+
+ if ( $this->mContent !== false ) {
+ return;
+ }
+
+ $this->resultFieldMatchFinder->setQueryToken(
+ $this->queryToken
+ );
+
+ $this->mContent = $this->resultFieldMatchFinder->findAndMatch(
+ $this->mResult
+ );
+
+ return reset( $this->mContent );
+ }
+
+ /**
+ * Make a request option object based on the given parameters, and
+ * return NULL if no such object is required. The parameter defines
+ * if the limit should be taken into account, which is not always desired
+ * (especially if results are to be cached for future use).
+ *
+ * @param boolean $useLimit
+ *
+ * @return SMWRequestOptions|null
+ */
+ protected function getRequestOptions( $useLimit = true ) {
+ return $this->resultFieldMatchFinder->getRequestOptions( $useLimit );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php
new file mode 100644
index 00000000..cfc21271
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php
@@ -0,0 +1,653 @@
+<?php
+
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableInfoFetcher;
+use SMW\SQLStore\RequestOptionsProc;
+use SMW\SQLStore\SQLStoreFactory;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\SQLStore\TableDefinition;
+
+/**
+ * SQL-based implementation of SMW's storage abstraction layer.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ *
+ * @ingroup SMWStore
+ */
+
+// The use of the following constants is explained in SMWSQLStore3::setup():
+define( 'SMW_SQL3_SMWIW_OUTDATED', ':smw' ); // virtual "interwiki prefix" for old-style special SMW objects (no longer used)
+define( 'SMW_SQL3_SMWREDIIW', ':smw-redi' ); // virtual "interwiki prefix" for SMW objects that are redirected
+define( 'SMW_SQL3_SMWBORDERIW', ':smw-border' ); // virtual "interwiki prefix" separating very important pre-defined properties from the rest
+define( 'SMW_SQL3_SMWINTDEFIW', ':smw-intprop' ); // virtual "interwiki prefix" marking internal (invisible) predefined properties
+define( 'SMW_SQL3_SMWDELETEIW', ':smw-delete' ); // virtual "interwiki prefix" marking a deleted subject, see #1100
+
+/**
+ * Storage access class for using the standard MediaWiki SQL database for
+ * keeping semantic data.
+ *
+ * @note Regarding the use of interwiki links in the store, there is currently
+ * no support for storing semantic data about interwiki objects, and hence
+ * queries that involve interwiki objects really make sense only for them
+ * occurring in object positions. Most methods still use the given input
+ * interwiki text as a simple way to filter out results that may be found if an
+ * interwiki object is given but a local object of the same name exists. It is
+ * currently not planned to support things like interwiki reuse of properties.
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3 extends SMWStore {
+
+ /**
+ * Specifies the border limit (upper bound) for pre-defined properties used
+ * in the ID_TABLE
+ *
+ * When changing the upper bound, please make sure to copy the current upper
+ * bound as legcy to the TableIntegrityExaminer::checkPredefinedPropertyUpperbound
+ */
+ const FIXED_PROPERTY_ID_UPPERBOUND = 500;
+
+ /**
+ * Name of the table to store the concept cache in.
+ *
+ * @note This should never change. If it is changed, the concept caches
+ * will appear empty until they are recomputed.
+ */
+ const CONCEPT_CACHE_TABLE = 'smw_concept_cache';
+ const CONCEPT_TABLE = 'smw_fpt_conc';
+
+ /**
+ * Name of the table to store the concept cache in.
+ *
+ * @note This should never change, but if it does then its contents can
+ * simply be rebuilt by running the setup.
+ */
+ const PROPERTY_STATISTICS_TABLE = 'smw_prop_stats';
+
+ /**
+ * Name of the table that manages the query dependency links
+ */
+ const QUERY_LINKS_TABLE = 'smw_query_links';
+
+ /**
+ * Name of the table that manages the fulltext index
+ */
+ const FT_SEARCH_TABLE = 'smw_ft_search';
+
+ /**
+ * Name of the table that manages the Store IDs
+ */
+ const ID_TABLE = 'smw_object_ids';
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var PropertyTableInfoFetcher|null
+ */
+ private $propertyTableInfoFetcher = null;
+
+ /**
+ * @var PropertyTableIdReferenceFinder
+ */
+ private $propertyTableIdReferenceFinder;
+
+ /**
+ * @var DataItemHandlerDispatcher
+ */
+ private $dataItemHandlerDispatcher;
+
+ /**
+ * @var EntityLookup
+ */
+ private $entityLookup;
+
+ /**
+ * @var ServicesContainer
+ */
+ protected $servicesContainer;
+
+ /**
+ * Object to access the SMW IDs table.
+ *
+ * @since 1.8
+ * @var SMWSql3SmwIds
+ */
+ public $smwIds;
+
+ /**
+ * The reader object used by this store. Initialized by getReader()
+ * Always access using getReader()
+ *
+ * @since 1.8
+ * @var SMWSQLStore3Readers
+ */
+ protected $reader = false;
+
+ /**
+ * The writer object used by this store. Initialized by getWriter(),
+ * which is the only way in which it should be accessed.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3Writers
+ */
+ protected $writer = false;
+
+ /**
+ * @since 1.8
+ */
+ public function __construct() {
+ $this->factory = new SQLStoreFactory( $this, $this->messageReporter );
+ $this->smwIds = $this->factory->newEntityTable();
+ }
+
+ /**
+ * Get an object of the dataitem handler from the dataitem provided.
+ *
+ * @since 1.8
+ * @param integer $diType
+ *
+ * @return SMWDIHandler
+ * @throws RuntimeException if no handler exists for the given type
+ */
+ public function getDataItemHandlerForDIType( $diType ) {
+
+ if ( $this->dataItemHandlerDispatcher === null ) {
+ $this->dataItemHandlerDispatcher = $this->factory->newDataItemHandlerDispatcher( $this );
+ }
+
+ return $this->dataItemHandlerDispatcher->getHandlerByType( $diType );
+ }
+
+///// Reading methods /////
+
+ public function getReader() {
+ if( $this->reader == false ) {
+ $this->reader = new SMWSQLStore3Readers( $this, $this->factory );//Initialize if not done already
+ }
+
+ return $this->reader;
+ }
+
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+ return $this->getEntityLookup()->getSemanticData( $subject, $filter );
+ }
+
+ /**
+ * @param mixed $subject
+ * @param DIProperty $property
+ * @param null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( $subject, DIProperty $property, $requestOptions = null ) {
+ return $this->getEntityLookup()->getPropertyValues( $subject, $property, $requestOptions );
+ }
+
+ public function getProperties( DIWikiPage $subject, $requestOptions = null ) {
+ return $this->getEntityLookup()->getProperties( $subject, $requestOptions );
+ }
+
+ public function getPropertySubjects( DIProperty $property, $dataItem, $requestOptions = null ) {
+ return $this->getEntityLookup()->getPropertySubjects( $property, $dataItem, $requestOptions );
+ }
+
+ public function getAllPropertySubjects( DIProperty $property, $requestoptions = null ) {
+ return $this->getEntityLookup()->getAllPropertySubjects( $property, $requestoptions );
+ }
+
+ public function getInProperties( SMWDataItem $value, $requestoptions = null ) {
+ return $this->getEntityLookup()->getInProperties( $value, $requestoptions );
+ }
+
+
+///// Writing methods /////
+
+
+ public function getWriter() {
+ if( $this->writer == false ) {
+ $this->writer = new SMWSQLStore3Writers( $this, $this->factory );
+ }
+
+ return $this->writer;
+ }
+
+ public function deleteSubject( Title $title ) {
+
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $this->getEntityLookup()->invalidateCache(
+ $subject
+ );
+
+ $this->getWriter()->deleteSubject( $title );
+
+ $this->doDeferredCachedListLookupUpdate(
+ $subject
+ );
+ }
+
+ protected function doDataUpdate( SemanticData $semanticData ) {
+
+ $this->getEntityLookup()->invalidateCache(
+ $semanticData->getSubject()
+ );
+
+ $this->getWriter()->doDataUpdate( $semanticData );
+
+ $this->doDeferredCachedListLookupUpdate(
+ $semanticData->getSubject()
+ );
+ }
+
+ public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) {
+
+ $this->getEntityLookup()->invalidateCache(
+ DIWikiPage::newFromTitle( $oldtitle )
+ );
+
+ $this->getEntityLookup()->invalidateCache(
+ DIWikiPage::newFromTitle( $newtitle )
+ );
+
+ $this->getWriter()->changeTitle( $oldtitle, $newtitle, $pageid, $redirid );
+
+ $this->doDeferredCachedListLookupUpdate(
+ DIWikiPage::newFromTitle( $oldtitle )
+ );
+ }
+
+ private function doDeferredCachedListLookupUpdate( DIWikiPage $subject ) {
+
+ if ( $subject->getNamespace() !== SMW_NS_PROPERTY ) {
+ return null;
+ }
+
+ $deferredCallableUpdate = $this->factory->newDeferredCallableCachedListLookupUpdate();
+ $deferredCallableUpdate->setOrigin( __METHOD__ );
+ $deferredCallableUpdate->waitOnTransactionIdle();
+ $deferredCallableUpdate->pushUpdate();
+ }
+
+///// Query answering /////
+
+ /**
+ * @note Move hooks to the base class in 3.*
+ *
+ * @see SMWStore::fetchQueryResult
+ *
+ * @since 1.8
+ * @param SMWQuery $query
+ * @return SMWQueryResult|string|integer depends on $query->querymode
+ */
+ public function getQueryResult( SMWQuery $query ) {
+
+ $result = null;
+ $start = microtime( true );
+
+ if ( \Hooks::run( 'SMW::Store::BeforeQueryResultLookupComplete', [ $this, $query, &$result, $this->factory->newSlaveQueryEngine() ] ) ) {
+ $result = $this->fetchQueryResult( $query );
+ }
+
+ \Hooks::run( 'SMW::SQLStore::AfterQueryResultLookupComplete', [ $this, &$result ] );
+ \Hooks::run( 'SMW::Store::AfterQueryResultLookupComplete', [ $this, &$result ] );
+
+ $query->setOption( SMWQuery::PROC_QUERY_TIME, microtime( true ) - $start );
+
+ return $result;
+ }
+
+ protected function fetchQueryResult( SMWQuery $query ) {
+ return $this->factory->newSlaveQueryEngine()->getQueryResult( $query );
+ }
+
+///// Special page functions /////
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newPropertyUsageCachedListLookup( $requestOptions );
+ }
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getUnusedPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newUnusedPropertyCachedListLookup( $requestOptions );
+ }
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getWantedPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newUndeclaredPropertyCachedListLookup( $requestOptions );
+ }
+
+ public function getStatistics() {
+ return $this->factory->newUsageStatisticsCachedListLookup()->fetchList();
+ }
+
+
+///// Setup store /////
+
+ /**
+ * @see Store::service
+ *
+ * {@inheritDoc}
+ */
+ public function service( $service, ...$args ) {
+
+ if ( $this->servicesContainer === null ) {
+ $this->servicesContainer = $this->newServicesContainer();
+ }
+
+ return $this->servicesContainer->get( $service, ...$args );
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function setup( $verbose = true ) {
+
+ $installer = $this->factory->newInstaller();
+ $installer->setMessageReporter( $this->messageReporter );
+
+ return $installer->install( $verbose );
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function drop( $verbose = true ) {
+
+ $installer = $this->factory->newInstaller();
+ $installer->setMessageReporter( $this->messageReporter );
+
+ return $installer->uninstall( $verbose );
+ }
+
+ public function refreshData( &$id, $count, $namespaces = false, $usejobs = true ) {
+
+ $entityRebuildDispatcher = $this->factory->newEntityRebuildDispatcher();
+
+ $entityRebuildDispatcher->setDispatchRangeLimit( $count );
+ $entityRebuildDispatcher->setRestrictionToNamespaces( $namespaces );
+
+ $entityRebuildDispatcher->setOptions(
+ [
+ 'use-job' => $usejobs
+ ]
+ );
+
+ return $entityRebuildDispatcher;
+ }
+
+
+///// Concept caching /////
+
+ /**
+ * Refresh the concept cache for the given concept.
+ *
+ * @since 1.8
+ * @param Title $concept
+ * @return array of error strings (empty if no errors occurred)
+ */
+ public function refreshConceptCache( Title $concept ) {
+ return $this->factory->newMasterConceptCache()->refreshConceptCache( $concept );
+ }
+
+ /**
+ * Delete the concept cache for the given concept.
+ *
+ * @since 1.8
+ * @param Title $concept
+ */
+ public function deleteConceptCache( $concept ) {
+ $this->factory->newMasterConceptCache()->deleteConceptCache( $concept );
+ }
+
+ /**
+ * Return status of the concept cache for the given concept as an array
+ * with key 'status' ('empty': not cached, 'full': cached, 'no': not
+ * cacheable). If status is not 'no', the array also contains keys 'size'
+ * (query size), 'depth' (query depth), 'features' (query features). If
+ * status is 'full', the array also contains keys 'date' (timestamp of
+ * cache), 'count' (number of results in cache).
+ *
+ * @since 1.8
+ * @param Title|SMWWikiPageValue $concept
+ *
+ * @return DIConcept|null
+ */
+ public function getConceptCacheStatus( $concept ) {
+ return $this->factory->newSlaveConceptCache()->getStatus( $concept );
+ }
+
+
+///// Helper methods, mostly protected /////
+
+ /**
+ * @see RequestOptionsProc::getSQLOptions
+ *
+ * @since 1.8
+ *
+ * @param SMWRequestOptions|null $requestOptions
+ * @param string $valuecol
+ *
+ * @return array
+ */
+ public function getSQLOptions( SMWRequestOptions $requestOptions = null, $valueCol = '' ) {
+ return RequestOptionsProc::getSQLOptions( $requestOptions, $valueCol );
+ }
+
+ /**
+ * @see RequestOptionsProc::getSQLConditions
+ *
+ * @since 1.8
+ *
+ * @param SMWRequestOptions|null $requestOptions
+ * @param string $valueCol name of SQL column to which conditions apply
+ * @param string $labelCol name of SQL column to which string conditions apply, if any
+ * @param boolean $addAnd indicate whether the string should begin with " AND " if non-empty
+ *
+ * @return string
+ */
+ public function getSQLConditions( SMWRequestOptions $requestOptions = null, $valueCol = '', $labelCol = '', $addAnd = true ) {
+ return RequestOptionsProc::getSQLConditions( $this, $requestOptions, $valueCol, $labelCol, $addAnd );
+ }
+
+ /**
+ * @see RequestOptionsProc::applyRequestOptions
+ *
+ * @since 1.8
+ *
+ * @param array $data array of SMWDataItem objects
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function applyRequestOptions( array $data, SMWRequestOptions $requestOptions = null ) {
+ return RequestOptionsProc::applyRequestOptions( $this, $data, $requestOptions );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForDataTypeTypeId
+ *
+ * @param string $typeid
+ *
+ * @return string
+ */
+ public function findTypeTableId( $typeid ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForDataTypeTypeId( $typeid );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForDataItemTypeId
+ *
+ * @param integer $dataItemId
+ *
+ * @return string
+ */
+ public function findDiTypeTableId( $dataItemId ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForDataItemTypeId( $dataItemId );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForProperty
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public function findPropertyTableID( DIProperty $property ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForProperty( $property );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::getPropertyTableDefinitions
+ *
+ * @return TableDefinition[]
+ */
+ public function getPropertyTables() {
+ return $this->getPropertyTableInfoFetcher()->getPropertyTableDefinitions();
+ }
+
+ /**
+ * Returns SMW Id object
+ *
+ * @since 1.9
+ *
+ * @return SMWSql3SmwIds
+ */
+ public function getObjectIds() {
+ return $this->smwIds;
+ }
+
+ /**
+ * Returns the statics table
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getStatisticsTable() {
+ return self::PROPERTY_STATISTICS_TABLE;
+ }
+
+ /**
+ * Resets internal objects
+ *
+ * @since 1.9.1.1
+ */
+ public function clear() {
+ parent::clear();
+ $this->factory->newSemanticDataLookup()->clear();
+ $this->propertyTableInfoFetcher = null;
+ $this->servicesContainer = null;
+ $this->getObjectIds()->initCache();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getInfo( $type = null ) {
+
+ if ( $type === 'store' ) {
+ return 'SMWSQLStore';
+ }
+
+ $connection = $this->getConnection( 'mw.db' );
+
+ if ( $type === 'db' ) {
+ return $connection->getInfo();
+ }
+
+ return [
+ 'SMWSQLStore' => $connection->getInfo()
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ *
+ * @return Database
+ */
+ public function getConnection( $type = 'mw.db' ) {
+ return parent::getConnection( $type );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return PropertyTableInfoFetcher
+ */
+ public function getPropertyTableInfoFetcher() {
+
+ if ( $this->propertyTableInfoFetcher === null ) {
+ $this->propertyTableInfoFetcher = $this->factory->newPropertyTableInfoFetcher();
+ }
+
+ return $this->propertyTableInfoFetcher;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return PropertyTableIdReferenceFinder
+ */
+ public function getPropertyTableIdReferenceFinder() {
+
+ if ( $this->propertyTableIdReferenceFinder === null ) {
+ $this->propertyTableIdReferenceFinder = $this->factory->newPropertyTableIdReferenceFinder();
+ }
+
+ return $this->propertyTableIdReferenceFinder;
+ }
+
+ /**
+ * @return ServicesContainer
+ */
+ protected function newServicesContainer() {
+ return $this->factory->newServicesContainer();
+ }
+
+ /**
+ * @return EntityLookup
+ */
+ private function getEntityLookup() {
+
+ if ( $this->entityLookup === null ) {
+ $this->entityLookup = $this->factory->newEntityLookup();
+ }
+
+ return $this->entityLookup;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
new file mode 100644
index 00000000..f2968edb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
@@ -0,0 +1,501 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\Enum;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableDefinition;
+
+/**
+ * Class to provide all basic read methods for SMWSQLStore3.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3Readers {
+
+ /**
+ * The store used by this store reader
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var TraversalPropertyLookup
+ */
+ private $traversalPropertyLookup;
+
+ /**
+ * @var PropertySubjectsLookup
+ */
+ private $propertySubjectsLookup;
+
+ /**
+ * @var PropertiesLookup
+ */
+ private $propertiesLookup;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ public function __construct( SMWSQLStore3 $parentStore, $factory ) {
+ $this->store = $parentStore;
+ $this->factory = $factory;
+ $this->traversalPropertyLookup = $this->factory->newTraversalPropertyLookup();
+ $this->propertySubjectsLookup = $this->factory->newPropertySubjectsLookup();
+ $this->propertiesLookup = $this->factory->newPropertiesLookup();
+ $this->semanticDataLookup = $this->factory->newSemanticDataLookup();
+ }
+
+ /**
+ * @see SMWStore::getSemanticData()
+ * @since 1.8
+ *
+ * @param DIWikiPage $subject
+ * @param string[]|bool $filter
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+
+ // *** Find out if this subject exists ***//
+ $sortKey = '';
+
+ $sid = $this->store->smwIds->getSMWPageIDandSort(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ $sortKey,
+ true,
+ true
+ );
+
+ // Ensures that a cached item to contain an expected sortKey when
+ // for example the ID was just created and the sortKey from the DB
+ // is empty otherwise the DB wins over the invoked sortKey
+ if ( !$sortKey ) {
+ $sortKey = $subject->getSortKey();
+ }
+
+ $subject->setSortKey( $sortKey );
+
+ if ( $sid == 0 ) {
+ // We consider redirects for getting $sid,
+ // so $sid == 0 also means "no redirects".
+ return new SMWSemanticData( $subject );
+ }
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+ $opts = null;
+ $semanticData = null;
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $semanticData = $this->semanticDataLookup->newStubSemanticData( $subject );
+ }
+
+ foreach ( $this->store->getPropertyTables() as $tid => $proptable ) {
+ if ( !array_key_exists( $proptable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $opts = $filter;
+ } elseif ( $filter !== false ) {
+ $relevant = false;
+ foreach ( $filter as $typeId ) {
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $typeId );
+ $relevant = $relevant || ( $proptable->getDiType() == $diType );
+ if ( $relevant ) {
+ break;
+ }
+ }
+ if ( !$relevant ) {
+ continue;
+ }
+ }
+
+ $data = $this->semanticDataLookup->getSemanticDataFromTable( $sid, $subject, $proptable, $opts );
+
+ if ( $semanticData !== null ) {
+ $semanticData->importDataFrom( $data );
+ }
+ }
+
+ if ( $semanticData === null ) {
+ // Note: the sortkey is always set but belongs to no property table,
+ // hence no entry in $this->store->m_sdstate[$sid] is made.
+ $this->semanticDataLookup->lockCache();
+ $this->semanticDataLookup->initLookupCache( $sid, $subject );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataById(
+ $sid
+ );
+
+ $this->semanticDataLookup->unlockCache();
+ }
+
+ // Avoid adding a sortkey for an already extended stub
+ if ( !$semanticData->hasProperty( new DIProperty( '_SKEY' ) ) ) {
+ $semanticData->addPropertyStubValue( '_SKEY', [ '', $sortKey ] );
+ }
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getProperties()
+ );
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getPropertyValues( new DIProperty( '_INST' ) )
+ );
+
+ return $semanticData;
+ }
+
+ /**
+ * @see SMWStore::getPropertyValues
+ *
+ * @todo Retrieving all sortkeys (values for _SKEY with $subject null)
+ * is not supported. An empty array will be given.
+ *
+ * @since 1.8
+ *
+ * @param $subject mixed DIWikiPage or null
+ * @param $property SMWDIProperty
+ * @param $requestOptions SMWRequestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( $subject, SMWDIProperty $property, $requestOptions = null ) {
+
+ if ( $property->isInverse() ) { // inverses are working differently
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertySubjects( $noninverse, $subject, $requestOptions );
+ } elseif ( !is_null( $subject ) ) { // subject given, use semantic data cache
+ $sid = $this->store->smwIds->getSMWPageID( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), true );
+ if ( $sid == 0 ) {
+ $result = [];
+ } elseif ( $property->getKey() == '_SKEY' ) {
+ $this->store->smwIds->getSMWPageIDandSort( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), $sortKey, true );
+ $sortKeyDi = new SMWDIBlob( $sortKey );
+ $result = $this->store->applyRequestOptions( [ $sortKeyDi ], $requestOptions );
+ } else {
+ $propTableId = $this->store->findPropertyTableID( $property );
+ $proptables = $this->store->getPropertyTables();
+
+ if ( !isset( $proptables[$propTableId] ) ) {
+ return [];
+ }
+
+ $propertyTableDef = $proptables[$propTableId];
+
+ $opts = $this->semanticDataLookup->newRequestOptions(
+ $propertyTableDef,
+ $property,
+ $requestOptions
+ );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataFromTable(
+ $sid,
+ $subject,
+ $propertyTableDef,
+ $opts
+ );
+
+ $pv = $semanticData->getPropertyValues( $property );
+ $this->store->smwIds->warmUpCache( $pv );
+
+ $result = $this->store->applyRequestOptions(
+ $pv,
+ $requestOptions
+ );
+ }
+ } else { // no subject given, get all values for the given property
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $data = $this->semanticDataLookup->fetchSemanticData(
+ $pid,
+ $property,
+ $proptables[$tableid],
+ $requestOptions
+ );
+
+ $result = [];
+ $propertyTypeId = $property->findPropertyTypeID();
+ $propertyDiId = DataTypeRegistry::getInstance()->getDataItemId( $propertyTypeId );
+
+ foreach ( $data as $dbkeys ) {
+ try {
+ $diHandler = $this->store->getDataItemHandlerForDIType( $propertyDiId );
+ $result[] = $diHandler->dataItemFromDBKeys( $dbkeys );
+ } catch ( SMWDataItemException $e ) {
+ // maybe type assignment changed since data was stored;
+ // don't worry, but we can only drop the data here
+ }
+ }
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * @see SMWStore::getPropertySubjects
+ *
+ * @todo This method cannot retrieve subjects for sortkeys, i.e., for
+ * property _SKEY. Only empty arrays will be returned there.
+ *
+ * @param SMWDIProperty $property
+ * @param SMWDataItem|null $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getPropertySubjects( SMWDIProperty $property, SMWDataItem $dataItem = null, SMWRequestOptions $requestOptions = null ) {
+
+ // inverses are working differently
+ if ( $property->isInverse() ) {
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertyValues( $dataItem, $noninverse, $requestOptions );
+ return $result;
+ }
+
+ $type = DataTypeRegistry::getInstance()->getDataItemByType(
+ $property->findPropertyTypeID()
+ );
+
+ // #1222, Filter those where types don't match (e.g property = _txt
+ // and value = _wpg)
+ if ( $dataItem !== null && $type !== $dataItem->getDIType() ) {
+ return [];
+ }
+
+ // First build $select, $from, and $where for the DB query
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $proptable = $proptables[$tableid];
+
+ $result = $this->propertySubjectsLookup->fetchFromTable(
+ $pid,
+ $proptable,
+ $dataItem,
+ $requestOptions
+ );
+
+ // Keep the result as iterator which is normally advised when the result
+ // size is expected to be larger than 1000 or results are retrieved through
+ // a job which may process them in batches.
+ if ( $requestOptions !== null && $requestOptions->getOption( Enum::SUSPEND_CACHE_WARMUP ) ) {
+ return $result;
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+
+ /**
+ * Helper function to compute from and where strings for a DB query so that
+ * only rows of the given value object match. The parameter $tableindex
+ * counts that tables used in the query to avoid duplicate table names. The
+ * parameter $proptable provides the SMWSQLStore3Table object that is
+ * queried.
+ *
+ * @todo Maybe do something about redirects. The old code was
+ * $oid = $this->store->smwIds->getSMWPageID($value->getDBkey(),$value->getNamespace(),$value->getInterwiki(),false);
+ *
+ * @note This method cannot handle DIContainer objects with sortkey
+ * properties correctly. This should never occur, but it would be good
+ * to fail in a more controlled way if it ever does.
+ *
+ * @param string $from
+ * @param string $where
+ * @param TableDefinition $propTable
+ * @param SMWDataItem $value
+ * @param integer $tableIndex
+ */
+ private function prepareValueQuery( &$from, &$where, TableDefinition $propTable, $value, $tableIndex = 1 ) {
+ $db = $this->store->getConnection();
+
+ if ( $value instanceof SMWDIContainer ) { // recursive handling of containers
+ $keys = array_keys( $propTable->getFields( $this->store ) );
+ $joinfield = "t$tableIndex." . reset( $keys ); // this must be a type 'p' object
+ $proptables = $this->store->getPropertyTables();
+ $semanticData = $value->getSemanticData();
+
+ foreach ( $semanticData->getProperties() as $subproperty ) {
+ $tableid = $this->store->findPropertyTableID( $subproperty );
+ $subproptable = $proptables[$tableid];
+
+ foreach ( $semanticData->getPropertyValues( $subproperty ) as $subvalue ) {
+ $tableIndex++;
+
+ if ( $subproptable->usesIdSubject() ) { // simply add property table to check values
+ $from .= " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON t$tableIndex.s_id=$joinfield";
+ } else { // exotic case with table that uses subject title+namespace in container object (should never happen in SMW core)
+ $from .= " INNER JOIN " . $db->tableName( SMWSql3SmwIds::TABLE_NAME ) . " AS ids$tableIndex ON ids$tableIndex.smw_id=$joinfield" .
+ " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON " .
+ "t$tableIndex.s_title=ids$tableIndex.smw_title AND t$tableIndex.s_namespace=ids$tableIndex.smw_namespace";
+ }
+
+ if ( !$subproptable->isFixedPropertyTable() ) { // the ID we get should be !=0, so no point in filtering the converse
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.p_id=" . $db->addQuotes( $this->store->smwIds->getSMWPropertyID( $subproperty ) );
+ }
+
+ $this->prepareValueQuery( $from, $where, $subproptable, $subvalue, $tableIndex );
+ }
+ }
+ } elseif ( !is_null( $value ) ) { // add conditions for given value
+ $diHandler = $this->store->getDataItemHandlerForDIType( $value->getDIType() );
+ foreach ( $diHandler->getWhereConds( $value ) as $fieldname => $value ) {
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.$fieldname=" . $db->addQuotes( $value );
+ }
+ }
+ }
+
+ /**
+ * @see SMWStore::getAllPropertySubjects
+ *
+ * @param SMWDIProperty $property
+ * @param SMWRequestOptions $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getAllPropertySubjects( SMWDIProperty $property, SMWRequestOptions $requestOptions = null ) {
+ return $this->getPropertySubjects( $property, null, $requestOptions );
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @param DIWikiPage $subject
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getProperties( DIWikiPage $subject, SMWRequestOptions $requestOptions = null ) {
+
+ $sid = $this->store->smwIds->getSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName()
+ );
+
+ // no id, no page, no properties
+ if ( $sid == 0 ) {
+ return [];
+ }
+
+ $subject->setId( $sid );
+ $result = [];
+
+ // Potentially need to get more results, since options apply to union
+ $lookupOptions = $this->propertiesLookup->newRequestOptions(
+ $requestOptions
+ );
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+
+ foreach ( $this->store->getPropertyTables() as $tid => $propertyTable ) {
+
+ if ( !array_key_exists( $propertyTable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ $res = $this->propertiesLookup->fetchFromTable(
+ $subject,
+ $propertyTable,
+ $lookupOptions
+ );
+
+ foreach ( $res as $row ) {
+ $result[] = new DIProperty(
+ isset( $row->smw_title ) ? $row->smw_title : $row
+ );
+ }
+ }
+
+ // apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * Implementation of SMWStore::getInProperties(). This function is meant to
+ * be used for finding properties that link to wiki pages.
+ *
+ * @since 1.8
+ * @see SMWStore::getInProperties
+ *
+ * @param SMWDataItem $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return DIProperty[]
+ */
+ public function getInProperties( SMWDataItem $value, SMWRequestOptions $requestOptions = null ) {
+
+ $result = [];
+ $diType = $value->getDIType();
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+
+ if ( $diType != $proptable->getDiType() ) {
+ continue;
+ }
+
+ $res = $this->traversalPropertyLookup->fetchFromTable(
+ $proptable,
+ $value,
+ $requestOptions
+ );
+
+ foreach ( $res as $row ) {
+ try {
+ $result[] = new SMW\DIProperty( $row->smw_title );
+ } catch (SMWDataItemException $e) {
+ // has been observed to happen (empty property title); cause unclear; ignore this data
+ }
+ }
+ }
+
+ // Apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php
new file mode 100644
index 00000000..bc8ef57e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php
@@ -0,0 +1,825 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\ChangePropListener;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\Deferred\ChangeTitleUpdate;
+use SMW\SemanticData;
+use SMW\Parameters;
+use SMW\SQLStore\PropertyStatisticsTable;
+use SMW\SQLStore\PropertyTableRowDiffer;
+
+/**
+ * Class Handling all the write and update methods for SMWSQLStore3.
+ *
+ * @note Writing may also require some reading operations. Operations that are
+ * only needed in helper methods of this class should be implemented here, not
+ * in SMWSQLStore3Readers.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3Writers {
+
+ /**
+ * The store used by this store writer.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ protected $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var PropertyTableRowDiffer
+ */
+ private $propertyTableRowDiffer;
+
+ /**
+ * @var PropertyTableUpdater
+ */
+ private $propertyTableUpdater;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ /**
+ * @var IdChanger
+ */
+ private $idChanger;
+
+ /**
+ * @since 1.8
+ *
+ * @param SMWSQLStore3 $parentStore
+ * @param SQLStoreFactory $factory
+ */
+ public function __construct( SMWSQLStore3 $parentStore, $factory ) {
+ $this->store = $parentStore;
+ $this->factory = $factory;
+ $this->propertyTableRowDiffer = $this->factory->newPropertyTableRowDiffer();
+ $this->propertyTableUpdater = $this->factory->newPropertyTableUpdater();
+ $this->semanticDataLookup = $this->factory->newSemanticDataLookup();
+ $this->idChanger = $this->factory->newIdChanger();
+ }
+
+ /**
+ * @see SMWStore::deleteSubject
+ *
+ * @since 1.8
+ * @param Title $title
+ */
+ public function deleteSubject( Title $title ) {
+
+ // @deprecated since 2.1, use 'SMW::SQLStore::BeforeDeleteSubjectComplete'
+ \Hooks::run( 'SMWSQLStore3::deleteSubjectBefore', [ $this->store, $title ] );
+
+ \Hooks::run( 'SMW::SQLStore::BeforeDeleteSubjectComplete', [ $this->store, $title ] );
+
+ // Fetch all possible matches (including any duplicates created by
+ // incomplete rollback or DB deadlock)
+ $idList = $this->store->getObjectIds()->findAllEntitiesThatMatch(
+ $title->getDBkey(),
+ $title->getNamespace()
+ );
+
+ $extensionList = array_flip( $idList );
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $emptySemanticData = new SemanticData( $subject );
+ $emptySemanticData->setOption( SemanticData::PROC_DELETE, true );
+
+ $subobjectListFinder = $this->factory->newSubobjectListFinder();
+
+ foreach ( $idList as $id ) {
+ $this->doDelete( $id, $subject, $subobjectListFinder, $extensionList );
+ $this->doDataUpdate( $emptySemanticData );
+
+ if ( $this->store->service( 'PropertyTableIdReferenceFinder' )->hasResidualPropertyTableReference( $id ) === false ) {
+ // Mark subject/subobjects with a special IW, the final removal is being
+ // triggered by the `EntityRebuildDispatcher`
+ $this->store->getObjectIds()->updateInterwikiField(
+ $id,
+ $subject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ } else {
+ // Convert the subject into a simple object instance
+ $this->store->getObjectIds()->setPropertyTableHashes(
+ $id,
+ null
+ );
+ }
+ }
+
+ $extensionList = array_keys( $extensionList );
+
+ $this->store->extensionData['delete.list'] = $extensionList;
+
+ // @deprecated since 2.1, use 'SMW::SQLStore::AfterDeleteSubjectComplete'
+ \Hooks::run( 'SMWSQLStore3::deleteSubjectAfter', [ $this->store, $title ] );
+
+ \Hooks::run( 'SMW::SQLStore::AfterDeleteSubjectComplete', [ $this->store, $title ] );
+ }
+
+ private function doDelete( $id, $subject, $subobjectListFinder, &$extensionList ) {
+
+ $this->semanticDataLookup->invalidateCache( $id );
+
+ if ( $subject->getNamespace() === SMW_NS_CONCEPT ) { // make sure to clear caches
+ $db = $this->store->getConnection();
+
+ $db->delete(
+ SMWSQLStore3::CONCEPT_TABLE,
+ [ 's_id' => $id ],
+ 'SMW::deleteSubject::Conc'
+ );
+
+ $db->delete(
+ SMWSQLStore3::CONCEPT_CACHE_TABLE,
+ [ 'o_id' => $id ],
+ 'SMW::deleteSubject::Conccache'
+ );
+ }
+
+ $subject->setId( $id );
+
+ foreach( $subobjectListFinder->find( $subject ) as $subobject ) {
+ $extensionList[$subobject->getId()] = true;
+
+ $this->store->getObjectIds()->updateInterwikiField(
+ $subobject->getId(),
+ $subobject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ /**
+ * @see SMWStore::doDataUpdate
+ *
+ * @since 1.8
+ * @param SMWSemanticData $data
+ */
+ public function doDataUpdate( SMWSemanticData $semanticData ) {
+ \Hooks::run( 'SMWSQLStore3::updateDataBefore', [ $this->store, $semanticData ] );
+
+ $subject = $semanticData->getSubject();
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $subobjectListFinder = $this->factory->newSubobjectListFinder();
+
+ $changeOp = $this->factory->newChangeOp(
+ $subject
+ );
+
+ $this->propertyTableRowDiffer->setChangeOp(
+ $changeOp
+ );
+
+ $changePropListener = $this->factory->newChangePropListener();
+ $hierarchyLookup = $this->factory->newHierarchyLookup();
+
+ // #2698
+ $hierarchyLookup->addListenersTo(
+ $changePropListener
+ );
+
+ $changePropListener->loadListeners(
+ $this->store
+ );
+
+ // Update data about our main subject
+ $this->doFlatDataUpdate( $semanticData );
+ $sid = $subject->getId();
+
+ // Update data about our subobjects
+ $subSemanticData = $semanticData->getSubSemanticData();
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ foreach( $subSemanticData as $subobjectData ) {
+ $this->doFlatDataUpdate( $subobjectData );
+ }
+
+ $deleteList = [];
+
+ // Mark subobjects without reference to be deleted
+ foreach( $subobjectListFinder->find( $subject ) as $subobject ) {
+ if( !$semanticData->hasSubSemanticData( $subobject->getSubobjectName() ) ) {
+
+ $this->doFlatDataUpdate( new SemanticData( $subobject ) );
+ $deleteList[] = $subobject->getId();
+
+ $this->store->getObjectIds()->updateInterwikiField(
+ $subobject->getId(),
+ $subobject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ if ( ( $rev_id = $semanticData->getExtensionData( 'revision_id' ) ) !== null ) {
+ $this->store->getObjectIds()->updateRevField( $sid, $rev_id );
+ }
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ // Store the diff in cache so any post processing has a chance to find
+ // what entities and values were changed
+ $changeDiff = $changeOp->newChangeDiff();
+ $changeDiff->save( ApplicationFactory::getInstance()->getCache() );
+
+ $changePropListener->callListeners();
+
+ $this->store->extensionData['delete.list'] = $deleteList;
+ $this->store->extensionData['change.diff'] = $changeDiff;
+
+ // Deprecated since 2.3, use SMW::SQLStore::AfterDataUpdateComplete
+ \Hooks::run( 'SMWSQLStore3::updateDataAfter', [ $this->store, $semanticData ] );
+
+ \Hooks::run( 'SMW::SQLStore::AfterDataUpdateComplete', [
+ $this->store,
+ $semanticData,
+ $changeOp
+ ] );
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ /**
+ * Update the store to contain the given data, without taking any
+ * subobject data into account.
+ *
+ * @since 1.8
+ * @param SMWSemanticData $data
+ */
+ protected function doFlatDataUpdate( SMWSemanticData $data ) {
+ $subject = $data->getSubject();
+
+ // Take care of redirects
+ $redirects = $data->getPropertyValues( new SMW\DIProperty( '_REDI' ) );
+
+ if ( count( $redirects ) > 0 ) {
+ $redirect = end( $redirects ); // at most one redirect per page
+ $this->updateRedirects(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $redirect->getDBkey(),
+ $redirect->getNameSpace()
+ );
+ // Stop here:
+ // * no support for annotations on redirect pages
+ // * updateRedirects takes care of deleting any previous data
+ return;
+ } else {
+ $this->updateRedirects(
+ $subject->getDBkey(),
+ $subject->getNamespace()
+ );
+ }
+
+ // Find an approriate sortkey, the field is influenced by various
+ // elements incl. DEFAULTSORT and can be altered without modifying
+ // any other annotation.
+ $sortKey = $this->makeSortKey( $subject, $data );
+
+ // Always fetch an ID which is either recalled from cache or is created.
+ // Doing so ensures that the sortkey and namespace data are updated
+ // to both the DB and the cache.
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ true,
+ $sortKey,
+ true
+ );
+
+ $subject->setSortKey( $sortKey );
+ $subject->setId( $sid );
+
+ // Find any potential duplicate entries for the current subject and
+ // if matched, mark them as to be deleted
+ $idList = $this->store->getObjectIds()->findAllEntitiesThatMatch(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName()
+ );
+
+ foreach ( $idList as $id ) {
+ if ( $id != $sid ) {
+ $this->store->getObjectIds()->updateInterwikiField(
+ $id,
+ $subject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ // Take care of all remaining property table data
+ list( $insertRows, $deleteRows, $newHashes ) = $this->propertyTableRowDiffer->computeTableRowDiff(
+ $sid,
+ $data
+ );
+
+ $params = new Parameters(
+ [
+ 'insert_rows' => $insertRows,
+ 'delete_rows' => $deleteRows,
+ 'new_hashes' => $newHashes
+ ]
+ );
+
+ $this->propertyTableUpdater->update( $sid, $params );
+
+ if ( $redirects === [] && $subject->getSubobjectName() === '' ) {
+
+ $dataItemFromId = $this->store->getObjectIds()->getDataItemById( $sid );
+
+ // If for some reason the internal redirect marker is still set but no
+ // redirect annotations are known then do update the interwiki field
+ if ( $dataItemFromId !== null && $dataItemFromId->getInterwiki() === SMW_SQL3_SMWREDIIW ) {
+ $this->store->getObjectIds()->updateInterwikiField( $sid, $subject );
+ }
+ }
+
+ // Update caches (may be important if jobs are directly following this call)
+ $this->semanticDataLookup->setLookupCache( $sid, $data );
+ }
+
+ private function makeSortKey( $subject, $data ) {
+
+ // Don't mind the delete process
+ if ( $data->getOption( SemanticData::PROC_DELETE ) ) {
+ return '';
+ }
+
+ $property = new DIProperty( '_SKEY' );
+
+ // Take care of the sortkey
+ $pv = $data->getPropertyValues( $property );
+ $dataItem = end( $pv );
+
+ if ( $dataItem instanceof SMWDIBlob ) {
+ $sortkey = $dataItem->getString();
+ } elseif ( $data->getExtensionData( 'sort.extension' ) !== null ) {
+ $sortkey = $data->getExtensionData( 'sort.extension' );
+ } else {
+ $sortkey = $subject->getSortKey();
+ }
+
+ // Extend the subobject sortkey in case no @sortkey was given for an
+ // entity
+ if ( $subject->getSubobjectName() !== '' && !$dataItem instanceof SMWDIBlob ) {
+
+ // Add sort data from some dedicated containers (of a record or
+ // reference type etc.) otherwise use the sobj name as extension
+ // to distinguish each entity
+ if ( $data->getExtensionData( 'sort.data' ) !== null ) {
+ $sortkey .= '#' . $data->getExtensionData( 'sort.data' );
+ } else {
+ $sortkey .= '#' . $subject->getSubobjectName();
+ }
+ }
+
+ // #649 Be consistent about how sortkeys are stored therefore always
+ // normalize even for usages like {{DEFAULTSORT: Foo_bar }}
+ $sortkey = str_replace( '_', ' ', $sortkey );
+
+ return $sortkey;
+ }
+
+ /**
+ * Implementation of SMWStore::changeTitle(). In contrast to
+ * updateRedirects(), this function does not simply write a redirect
+ * from the old page to the new one, but also deletes all data that may
+ * already be stored for the new title (normally the new title should
+ * belong to an empty page that has no data but at least it could have a
+ * redirect to the old page), and moves all data that exists for the old
+ * title to the new location. Thus, the function executes three steps:
+ * delete data at newtitle, move data from oldtitle to newtitle, and set
+ * redirect from oldtitle to newtitle. In some cases, the goal can be
+ * achieved more efficiently, e.g. if the new title does not occur in SMW
+ * yet: then we can just change the ID records for the titles instead of
+ * changing all data tables
+ *
+ * Note that the implementation ignores the MediaWiki IDs since this
+ * store has its own ID management. Also, the function requires that both
+ * titles are local, i.e. have empty interwiki prefix.
+ *
+ * @todo Currently the sortkey is not moved with the remaining data. It is
+ * not possible to move it reliably in all cases: we cannot distinguish an
+ * unset sortkey from one that was set to the name of oldtitle. Maybe use
+ * update jobs right away?
+ *
+ * @since 1.8
+ *
+ * @param Title $oldTitle
+ * @param Title $newTitle
+ * @param integer $pageId
+ * @param integer $redirectId
+ */
+ public function changeTitle( Title $oldTitle, Title $newTitle, $pageId, $redirectId = 0 ) {
+ global $smwgQEqualitySupport;
+
+ \Hooks::run(
+ 'SMW::SQLStore::BeforeChangeTitleComplete',
+ [ $this->store, $oldTitle, $newTitle, $pageId, $redirectId ]
+ );
+
+ $db = $this->store->getConnection();
+
+ // get IDs but do not resolve redirects:
+ $sid = $this->store->getObjectIds()->getSMWPageID(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ $tid = $this->store->getObjectIds()->getSMWPageID(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ // Easy case: target not used anywhere yet, just hijack its title for our current id
+ if ( ( $tid == 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) {
+ // This condition may not hold even if $newtitle is
+ // currently unused/non-existing since we keep old IDs.
+ // If equality support is off, then this simple move
+ // does too much; fall back to general case below.
+ if ( $sid != 0 ) { // change id entry to refer to the new title
+ // Note that this also changes the reference for internal objects (subobjects)
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [
+ 'smw_title' => $newTitle->getDBkey(),
+ 'smw_namespace' => $newTitle->getNamespace(),
+ 'smw_iw' => ''
+ ],
+ [
+ 'smw_title' => $oldTitle->getDBkey(),
+ 'smw_namespace' => $oldTitle->getNamespace(),
+ 'smw_iw' => ''
+ ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->moveSubobjects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ '',
+ '',
+ 0,
+ ''
+ );
+
+ // We do not know the new sortkey, so just clear the cache:
+ $this->store->getObjectIds()->deleteCache(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ ''
+ );
+
+ } else { // make new (target) id for use in redirect table
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ ''
+ );
+ } // at this point, $sid is the id of the target page (according to the IDs table)
+
+ // make redirect id for oldtitle:
+ $this->store->getObjectIds()->makeSMWPageID(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ SMW_SQL3_SMWREDIIW,
+ ''
+ );
+
+ $this->store->getObjectIds()->addRedirect(
+ $sid,
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace()
+ );
+
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore();
+
+ $propertyStatisticsStore->addToUsageCount(
+ $this->store->getObjectIds()->getSMWPropertyID( new SMW\DIProperty( '_REDI' ) ),
+ 1
+ );
+
+ /// NOTE: there is the (bad) case that the moved page is a redirect. As chains of
+ /// redirects are not supported by MW or SMW, the above is maximally correct in this case too.
+ /// NOTE: this temporarily leaves existing redirects to oldtitle point to newtitle as well, which
+ /// will be lost after the next update. Since double redirects are an error anyway, this is not
+ /// a bad behavior: everything will continue to work until the existing redirects are updated,
+ /// which will hopefully be done to fix the double redirect.
+ } else { // General move method: should always be correct
+ // (equality support respected when updating redirects)
+
+ // Delete any existing data (including redirects) from old title
+ $emptyNewSemanticData = new SMWSemanticData( SMWDIWikiPage::newFromTitle( $oldTitle ) );
+ $this->doDataUpdate( $emptyNewSemanticData );
+
+ // Move all data of old title to new position:
+ if ( $sid != 0 ) {
+ $this->idChanger->change(
+ $sid,
+ $tid,
+ $oldTitle->getNamespace(),
+ $newTitle->getNamespace(),
+ true,
+ false
+ );
+ }
+
+ // Associate internal objects (subobjects) with the new title:
+ $table = $db->tableName( SMWSql3SmwIds::TABLE_NAME );
+
+ $values = [
+ 'smw_title' => $newTitle->getDBkey(),
+ 'smw_namespace' => $newTitle->getNamespace(),
+ 'smw_iw' => ''
+ ];
+
+ $sql = "UPDATE $table SET " . $db->makeList( $values, LIST_SET ) .
+ ' WHERE smw_title = ' . $db->addQuotes( $oldTitle->getDBkey() ) . ' AND ' .
+ 'smw_namespace = ' . $db->addQuotes( $oldTitle->getNamespace() ) . ' AND ' .
+ 'smw_iw = ' . $db->addQuotes( '' ) . ' AND ' .
+ 'smw_subobject != ' . $db->addQuotes( '' ); // The "!=" is why we cannot use MW array syntax here
+
+ $db->query( $sql, __METHOD__ );
+
+ $this->store->getObjectIds()->moveSubobjects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ // $redirid == 0 means that the oldTitle was not supposed to be a redirect
+ // (oldTitle is delete from the db) but instead of deleting all
+ // references we will still copy data from old to new during updateRedirects()
+ // and clear the semantic data container for the oldTitle instance
+ // to ensure that no ghost references exists for an deleted oldTitle
+ // @see Title::moveTo(), createRedirect
+ if ( $redirectId == 0 ) {
+
+ // Delete any existing data (including redirects) from old title
+ $this->updateRedirects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace()
+ );
+
+ } else {
+
+ // Write a redirect from old title to new one:
+ // (this also updates references in other tables as needed.)
+ // TODO: may not be optimal for the standard case that newtitle
+ // existed and redirected to oldtitle (PERFORMANCE)
+ $this->updateRedirects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ }
+
+ }
+
+ if ( $redirectId == 0 ) {
+ $oldTitle = null;
+ }
+
+ ChangeTitleUpdate::addUpdate( $oldTitle, $newTitle );
+ }
+
+ /**
+ * Helper method to write information about some redirect. Various updates
+ * can be necessary if redirects are resolved as identities in SMW. The
+ * title and namespace of the affected page and of its updated redirect
+ * target are given. The target can be empty ('') to delete any redirect.
+ * Returns the canonical ID that is now to be used for the subject.
+ *
+ * This method does not change the ids of the affected pages, and thus it
+ * is not concerned with updates of the data that is currently stored for
+ * the subject. Normally, a subject that is a redirect will not have other
+ * data, but this method does not depend on this.
+ *
+ * @note Please make sure you fully understand this code before making any
+ * changes here. Keeping the redirect structure consistent is important,
+ * and errors in this code can go unnoticed for quite some time.
+ *
+ * @note This method merely handles the addition or deletion of a redirect
+ * statement in the wiki. It does not assume that any page contents has
+ * been changed (e.g. moved). See changeTitle() for additional handling in
+ * this case.
+ *
+ * @todo Clean up this code.
+ *
+ * @since 1.8
+ * @param string $subject_t
+ * @param integer $subject_ns
+ * @param string $curtarget_t
+ * @param integer $curtarget_ns
+ * @return integer the new canonical ID of the subject
+ */
+ protected function updateRedirects( $subject_t, $subject_ns, $curtarget_t = '', $curtarget_ns = -1 ) {
+ global $smwgQEqualitySupport;
+
+ $count = 0; //track count changes for redi property
+ $db = $this->store->getConnection();
+
+ // *** First get id of subject, old redirect target, and current (new) redirect target ***//
+
+ $sid_sort = '';
+
+ // find real id of subject, if any
+ $sid = $this->store->getObjectIds()->getSMWPageIDandSort(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ $sid_sort,
+ false
+ );
+
+ /// NOTE: $sid can be 0 here; this is useful to know since it means that fewer table updates are needed
+ $new_tid = $curtarget_t ? ( $this->store->getObjectIds()->makeSMWPageID( $curtarget_t, $curtarget_ns, '', '', false ) ) : 0; // real id of new target, if given
+
+ $old_tid = $this->store->getObjectIds()->findRedirect(
+ $subject_t,
+ $subject_ns
+ );
+
+ /// NOTE: $old_tid and $new_tid both (intentionally) ignore further redirects: no redirect chains
+
+ if ( $old_tid == $new_tid ) { // no change, all happy
+ return ( $new_tid == 0 ) ? $sid : $new_tid;
+ } // note that this means $old_tid != $new_tid in all cases below
+
+ // *** Make relevant changes in property tables (don't write the new redirect yet) ***//
+ $jobs = [];
+
+ if ( ( $old_tid == 0 ) && ( $sid != 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) { // new redirect
+ // $smwgQEqualitySupport requires us to change all tables' page references from $sid to $new_tid.
+ // Since references must not be 0, we don't have to do this is $sid == 0.
+ $this->idChanger->change(
+ $sid,
+ $new_tid,
+ $subject_ns,
+ $curtarget_ns,
+ false,
+ true
+ );
+
+ } elseif ( $old_tid != 0 ) { // existing redirect is changed or deleted
+
+ $count--;
+
+ $this->store->getObjectIds()->updateRedirect(
+ $old_tid,
+ $subject_t,
+ $subject_ns
+ );
+ }
+
+ // *** Finally, write the new redirect data ***//
+
+ if ( $new_tid != 0 ) { // record a new redirect
+ // Redirecting done right:
+ // (1) make a new ID with iw SMW_SQL3_SMWREDIIW or
+ // change iw field of current ID in this way,
+ // (2) write smw_fpt_redi table,
+ // (3) update canonical cache.
+ // This order must be obeyed unless you really understand what you are doing!
+
+ if ( ( $old_tid == 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) {
+ // mark subject as redirect (if it was no redirect before)
+ if ( $sid == 0 ) { // every redirect page must have an ID
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ false
+ );
+ } else {
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [ 'smw_iw' => SMW_SQL3_SMWREDIIW ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ 0,
+ ''
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ $sid,
+ $sid_sort
+ );
+ }
+ }
+
+ $this->store->getObjectIds()->addRedirect(
+ $new_tid,
+ $subject_t,
+ $subject_ns
+ );
+
+ $count++;
+
+ } else { // delete old redirect
+ // This case implies $old_tid != 0 (or we would have new_tid == old_tid above).
+ // Therefore $subject had a redirect, and it must also have an ID.
+ // This shows that $sid != 0 here.
+ if ( $smwgQEqualitySupport != SMW_EQ_NONE ) { // mark subject as non-redirect
+
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [ 'smw_iw' => '' ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ 0,
+ ''
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ $sid,
+ $sid_sort
+ );
+ }
+ }
+
+ // *** Flush some caches to be safe, though they are not essential in runs with redirect updates ***//
+ $this->semanticDataLookup->invalidateCache( $sid );
+ $this->semanticDataLookup->invalidateCache( $new_tid );
+ $this->semanticDataLookup->invalidateCache( $old_tid );
+
+ // *** Update reference count for _REDI property ***//
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore();
+
+ $propertyStatisticsStore->addToUsageCount(
+ $this->store->getObjectIds()->getSMWPropertyID( new SMW\DIProperty( '_REDI' ) ),
+ $count
+ );
+
+ return ( $new_tid == 0 ) ? $sid : $new_tid;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php
new file mode 100644
index 00000000..b8c402b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php
@@ -0,0 +1,1296 @@
+<?php
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Collator;
+use SMW\PropertyRegistry;
+use SMW\RequestOptions;
+use SMW\SQLStore\EntityStore\IdCacheManager;
+use SMW\SQLStore\IdToDataItemMatchFinder;
+use SMW\SQLStore\RedirectStore;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\SQLStoreFactory;
+use SMW\SQLStore\TableFieldUpdater;
+use SMWDataItem as DataItem;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\Connection\Sequence;
+use SMW\TypesRegistry;
+
+/**
+ * @ingroup SMWStore
+ * @since 1.8
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class to access the SMW IDs table in SQLStore3.
+ * Provides transparent in-memory caching facilities.
+ *
+ * Documentation for the SMW IDs table: This table is a dictionary that
+ * assigns integer IDs to pages, properties, and other objects used by SMW.
+ * All tables that refer to such objects store these IDs instead. If the ID
+ * information is lost (e.g., table gets deleted), then the data stored in SMW
+ * is no longer meaningful: all tables need to be dropped, recreated, and
+ * refreshed to get back to a working database.
+ *
+ * The table has a column for storing interwiki prefixes, used to refer to
+ * pages on external sites (like in MediaWiki). This column is also used to
+ * mark some special objects in the table, using "interwiki prefixes" that
+ * cannot occur in MediaWiki:
+ *
+ * - Rows with iw SMW_SQL3_SMWREDIIW are similar to normal entries for
+ * (internal) wiki pages, but the iw indicates that the page is a redirect, the
+ * (target of which should be sought using the smw_fpt_redi table.
+ *
+ * - The (unique) row with iw SMW_SQL3_SMWBORDERIW just marks the border
+ * between predefined ids (rows that are reserved for hardcoded ids built into
+ * SMW) and normal entries. It is no object, but makes sure that SQL's auto
+ * increment counter is high enough to not add any objects before that marked
+ * "border".
+ *
+ * @note Do not call the constructor of SMWDIWikiPage using data from the SMW
+ * IDs table; use SMWDIHandlerWikiPage::dataItemFromDBKeys() instead. The table
+ * does not always contain data as required wiki pages. Especially predefined
+ * properties are represented by language-independent keys rather than proper
+ * titles. SMWDIHandlerWikiPage takes care of this.
+ *
+ * @since 1.8
+ *
+ * @ingroup SMWStore
+ */
+class SMWSql3SmwIds {
+
+ /**
+ * Specifies the border limit for pre-defined properties declared
+ * in SMWSql3SmwIds::special_ids
+ */
+ const FXD_PROP_BORDER_ID = SMWSQLStore3::FIXED_PROPERTY_ID_UPPERBOUND;
+
+ /**
+ * Name of the table to store IDs in.
+ *
+ * @note This should never change. Existing wikis will have to drop and
+ * rebuild their SMW tables completely to recover from any change here.
+ */
+ const TABLE_NAME = SMWSQLStore3::ID_TABLE;
+
+ const MAX_CACHE_SIZE = 1000;
+ const POOLCACHE_ID = 'smw.sqlstore';
+
+ /**
+ * Parent SMWSQLStore3.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ public $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var IdToDataItemMatchFinder
+ */
+ private $idMatchFinder;
+
+ /**
+ * @var RedirectStore
+ */
+ private $redirectStore;
+
+ /**
+ * @var TableFieldUpdater
+ */
+ private $tableFieldUpdater;
+
+ /**
+ * @var array
+ */
+ public static $special_ids = [];
+
+ /**
+ * @var IdCacheManager
+ */
+ private $idCacheManager;
+
+ /**
+ * @var IdEntityFinder
+ */
+ private $idEntityFinder;
+
+ /**
+ * @var IdChanger
+ */
+ private $idChanger;
+
+ /**
+ * @var UniquenessLookup
+ */
+ private $uniquenessLookup;
+
+ /**
+ * @since 1.8
+ * @param SMWSQLStore3 $store
+ */
+ public function __construct( SMWSQLStore3 $store, SQLStoreFactory $factory ) {
+ $this->store = $store;
+ $this->factory = $factory;
+ $this->initCache();
+
+ $this->idEntityFinder = $this->factory->newIdEntityFinder(
+ $this->idCacheManager
+ );
+
+ $this->redirectStore = $this->factory->newRedirectStore();
+ $this->uniquenessLookup = $this->factory->newUniquenessLookup();
+
+ $this->tableFieldUpdater = $this->factory->newTableFieldUpdater();
+ $this->idChanger = $this->factory->newIdChanger();
+
+ self::$special_ids = TypesRegistry::getFixedPropertyIdList();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return boolean
+ */
+ public function isRedirect( DIWikiPage $subject ) {
+ return $this->redirectStore->isRedirect( $subject->getDBKey(), $subject->getNamespace() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ public function isUnique( DataItem $dataItem ) {
+ return $this->uniquenessLookup->isUnique( $dataItem );
+ }
+
+ /**
+ * @see RedirectStore::findRedirect
+ *
+ * @since 2.1
+ *
+ * @param string $title DB key
+ * @param integer $namespace
+ *
+ * @return integer
+ */
+ public function findRedirect( $title, $namespace ) {
+ return $this->redirectStore->findRedirect( $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::addRedirect
+ *
+ * @since 2.1
+ *
+ * @param integer $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function addRedirect( $id, $title, $namespace ) {
+ $this->redirectStore->addRedirect( $id, $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::updateRedirect
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function updateRedirect( $id, $title, $namespace ) {
+ $this->redirectStore->updateRedirect( $id, $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::deleteRedirect
+ *
+ * @since 2.1
+ *
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function deleteRedirect( $title, $namespace ) {
+ $this->redirectStore->deleteRedirect( $title, $namespace );
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given title,
+ * namespace, interwiki, and subobject. If $canonical is set to true,
+ * redirects are taken into account to find the canonical alias ID for
+ * the given page. If no such ID exists, 0 is returned. The Call-By-Ref
+ * parameter $sortkey is set to the current sortkey, or to '' if no ID
+ * exists.
+ *
+ * If $fetchhashes is true, the property table hash blob will be
+ * retrieved in passing if the opportunity arises, and cached
+ * internally. This will speed up a subsequent call to
+ * getPropertyTableHashes() for this id. This should only be done
+ * if such a call is intended, both to safe the previous cache and
+ * to avoid extra work (even if only a little) to fill it.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes = false ) {
+ $id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
+ if ( $id != 0 ) {
+ return (int)$id;
+ } else {
+ return (int)$this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $sortkey, $canonical, $fetchHashes );
+ }
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given normalized title,
+ * namespace, interwiki, and subobjectName. Predefined IDs are not
+ * taken into account (however, they would still be found correctly by
+ * an avoidable database read if they are stored correctly in the
+ * database; this should always be the case). In all other aspects, the
+ * method works just like getSMWPageIDandSort().
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ protected function getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes ) {
+ global $smwgQEqualitySupport;
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // Integration test "query-04-02-subproperty-dc-import-marc21.json"
+ // showed a deterministic failure (due to a wrong cache id during querying
+ // for redirects) hence we force to read directly from the RedirectStore
+ // for objects marked as redirect
+ if ( $iw === SMW_SQL3_SMWREDIIW && $canonical &&
+ $smwgQEqualitySupport !== SMW_EQ_NONE && $subobjectName === '' ) {
+ $id = $this->findRedirect( $title, $namespace );
+ } else {
+ $id = $this->idCacheManager->getId( [ $title, (int)$namespace, $iw, $subobjectName ] );
+ }
+
+ if ( $id !== false && $id != 0 ) { // cache hit
+ $sortkey = $this->idCacheManager->getSort( [ $title, (int)$namespace, $iw, $subobjectName ] );
+ } elseif ( $iw == SMW_SQL3_SMWREDIIW && $canonical &&
+ $smwgQEqualitySupport != SMW_EQ_NONE && $subobjectName === '' ) {
+ $id = $this->findRedirect( $title, $namespace );
+ if ( $id != 0 ) {
+
+ if ( $fetchHashes ) {
+ $select = [ 'smw_sortkey', 'smw_sort', 'smw_proptable_hash' ];
+ } else {
+ $select = [ 'smw_sortkey', 'smw_sort' ];
+ }
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ $select,
+ [ 'smw_id' => $id ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ // Make sure that smw_sort is being re-computed in case it is null
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash );
+ }
+ } else { // inconsistent DB; just recover somehow
+ $sortkey = str_replace( '_', ' ', $title );
+ }
+ } else {
+ $sortkey = '';
+ }
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+ } else {
+
+ if ( $fetchHashes ) {
+ $select = [ 'smw_id', 'smw_sortkey', 'smw_sort', 'smw_proptable_hash' ];
+ } else {
+ $select = [ 'smw_id', 'smw_sortkey', 'smw_sort' ];
+ }
+
+ // #2001
+ // In cases where title components are excessively long (beyond the
+ // field limit) it has been observed that at least on MySQL/MariaDB no
+ // appropriate matches are found even though a row with a truncated
+ // representation exists in the table.
+ //
+ // `postgres` has no field limit and a divergent behaviour has not
+ // been observed
+ if ( $subobjectName !== '' && !$db->isType( 'postgres' ) ) {
+ $subobjectName = mb_substr( $subobjectName, 0, 255 );
+ }
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ $select,
+ [
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName
+ ],
+ __METHOD__
+ );
+
+ //$this->selectrow_sort_debug++;
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+ // Make sure that smw_sort is being re-computed in case it is null
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash);
+ }
+ } else {
+ $id = 0;
+ $sortkey = '';
+ }
+
+ $this->setCache(
+ $title,
+ $namespace,
+ $iw,
+ $subobjectName,
+ $id,
+ $sortkey
+ );
+ }
+
+ if ( $id == 0 && $subobjectName === '' && $iw === '' ) { // could be a redirect; check
+ $id = $this->getSMWPageIDandSort(
+ $title,
+ $namespace,
+ SMW_SQL3_SMWREDIIW,
+ $subobjectName,
+ $sortkey,
+ $canonical,
+ $fetchHashes
+ );
+ }
+
+ return $id;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function findDuplicates() {
+ return $this->uniquenessLookup->findDuplicates();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string|null $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ *
+ * @param array
+ */
+ public function findAllEntitiesThatMatch( $title, $namespace, $iw = null, $subobjectName = '' ) {
+
+ $matches = [];
+ $query = [];
+
+ $query['fields'] = ['smw_id'];
+
+ $query['conditions'] = [
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName
+ ];
+
+ // Null means select all (incl. those marked delete, redi etc.)
+ if ( $iw === null ) {
+ unset( $query['conditions']['smw_iw'] );
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $rows = $connection->select(
+ $connection->tableName( self::TABLE_NAME ),
+ $query['fields'],
+ $query['conditions'],
+ __METHOD__
+ );
+
+ if ( $rows === false ) {
+ return $matches;
+ }
+
+ foreach ( $rows as $row ) {
+ $matches[] = (int)$row->smw_id;
+ }
+
+ return $matches;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ *
+ * @param boolean
+ */
+ public function exists( DIWikiPage $subject ) {
+ return $this->getId( $subject ) > 0;
+ }
+
+ /**
+ * @note SMWSql3SmwIds::getSMWPageID has some issues with the cache as it returned
+ * 0 even though an object was matchable, using this method is safer then trying
+ * to encipher getSMWPageID related methods.
+ *
+ * It uses the PoolCache which means Lru is in place to avoid memory leakage.
+ *
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ *
+ * @param integer
+ */
+ public function getId( DIWikiPage $subject ) {
+
+ // Try to match a predefined property
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY && $subject->getInterWiki() === '' ) {
+ $property = DIProperty::newFromUserLabel( $subject->getDBKey() );
+ $key = $property->getKey();
+
+ // Has a fixed ID?
+ if ( isset( self::$special_ids[$key] ) && $subject->getSubobjectName() === '' ) {
+ return self::$special_ids[$key];
+ }
+
+ // Switch title for fixed properties without a fixed ID (e.g. _MIME is the smw_title)
+ if ( !$property->isUserDefined() ) {
+ $subject = new DIWikiPage(
+ $key,
+ SMW_NS_PROPERTY,
+ $subject->getInterWiki(),
+ $subject->getSubobjectName()
+ );
+ }
+ }
+
+ if ( ( $id = $this->idCacheManager->getId( $subject ) ) !== false ) {
+ return $id;
+ }
+
+ $id = 0;
+
+ $row = $this->store->getConnection( 'mw.db' )->selectRow(
+ self::TABLE_NAME,
+ [ 'smw_id' ],
+ [
+ 'smw_title' => $subject->getDBKey(),
+ 'smw_namespace' => $subject->getNamespace(),
+ 'smw_iw' => $subject->getInterWiki(),
+ 'smw_subobject' => $subject->getSubobjectName()
+ ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+
+ // Legacy
+ $this->setCache(
+ $subject->getDBKey(),
+ $subject->getNamespace(),
+ $subject->getInterWiki(),
+ $subject->getSubobjectName(),
+ $id,
+ $subject->getSortKey()
+ );
+ }
+
+ return $id;
+ }
+
+ /**
+ * Convenience method for calling getSMWPageIDandSort without
+ * specifying a sortkey (if not asked for).
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function getSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $fetchHashes = false ) {
+ $sort = '';
+ return $this->getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, $sort, $canonical, $fetchHashes );
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given title, namespace,
+ * interwiki, and subobjectName. If $canonical is set to true,
+ * redirects are taken into account to find the canonical alias ID for
+ * the given page. If no such ID exists, a new ID is created and
+ * returned. In any case, the current sortkey is set to the given one
+ * unless $sortkey is empty.
+ *
+ * @note Using this with $canonical==false can make sense, especially when
+ * the title is a redirect target (we do not want chains of redirects).
+ * But it is of no relevance if the title does not have an id yet.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function makeSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $sortkey = '', $fetchHashes = false ) {
+ $id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
+ if ( $id != 0 ) {
+ return (int)$id;
+ } else {
+ return (int)$this->makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes );
+ }
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given normalized title,
+ * namespace, interwiki, and subobjectName. Predefined IDs are not
+ * taken into account (however, they would still be found correctly by
+ * an avoidable database read if they are stored correctly in the
+ * database; this should always be the case). In all other aspects, the
+ * method works just like makeSMWPageID(). Especially, if no ID exists,
+ * a new ID is created and returned.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ protected function makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes ) {
+
+ $oldsort = '';
+ $id = $this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $oldsort, $canonical, $fetchHashes );
+ $db = $this->store->getConnection( 'mw.db' );
+ $collator = Collator::singleton();
+
+ // Safeguard to ensure that no duplicate IDs are created
+ if ( $id == 0 ) {
+ $id = $this->getId( new DIWikiPage( $title, $namespace, $iw, $subobjectName ) );
+ }
+
+ $db->beginAtomicTransaction( __METHOD__ );
+
+ if ( $id == 0 ) {
+ $sortkey = $sortkey ? $sortkey : ( str_replace( '_', ' ', $title ) );
+
+ // Bug 42659
+ $sequenceValue = $db->nextSequenceValue(
+ Sequence::makeSequence( SQLStore::ID_TABLE, 'smw_id' )
+ );
+
+ // #2089 (MySQL 5.7 complained with "Data too long for column")
+ $sortkey = mb_substr( $sortkey, 0, 254 );
+
+ $db->insert(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $sequenceValue,
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName,
+ 'smw_sortkey' => $sortkey,
+ 'smw_sort' => $collator->getSortKey( $sortkey ),
+ 'smw_hash' => $this->computeSha1( [ $title, (int)$namespace, $iw, $subobjectName ] )
+ ],
+ __METHOD__
+ );
+
+ $id = (int)$db->insertId();
+
+ // Properties also need to be in the property statistics table
+ if( $namespace === SMW_NS_PROPERTY ) {
+
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore(
+ $db
+ );
+
+ $propertyStatisticsStore->insertUsageCount( $id, 0 );
+ }
+
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, null );
+ }
+
+ } elseif ( $sortkey !== '' && ( $sortkey != $oldsort || !$collator->isIdentical( $oldsort, $sortkey ) ) ) {
+ $this->tableFieldUpdater->updateSortField( $id, $sortkey );
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+ }
+
+ $db->endAtomicTransaction( __METHOD__ );
+
+ return $id;
+ }
+
+ /**
+ * Properties have a mechanisms for being predefined (i.e. in PHP instead
+ * of in wiki). Special "interwiki" prefixes separate the ids of such
+ * predefined properties from the ids for the current pages (which may,
+ * e.g., be moved, while the predefined object is not movable).
+ *
+ * @todo This documentation is out of date. Right now, the special
+ * interwiki is used only for special properties without a label, i.e.,
+ * which cannot be shown to a user. This allows us to filter such cases
+ * from all queries that retrieve lists of properties. It should be
+ * checked that this is really the only use that this has throughout
+ * the code.
+ *
+ * @since 1.8
+ * @param SMWDIProperty $property
+ * @return string
+ */
+ public function getPropertyInterwiki( SMWDIProperty $property ) {
+ return ( $property->getLabel() !== '' ) ? '' : SMW_SQL3_SMWINTDEFIW;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $sid
+ * @param DIWikiPage $subject
+ * @param integer|string|null $interwiki
+ */
+ public function updateInterwikiField( $sid, DIWikiPage $subject, $interwiki = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( $interwiki === null ) {
+ $interwiki = $subject->getInterWiki();
+ }
+
+ $hash = [
+ $subject->getDBKey(),
+ (int)$subject->getNamespace(),
+ $interwiki,
+ $subject->getSubobjectName()
+ ];
+
+ $connection->update(
+ self::TABLE_NAME,
+ [
+ 'smw_iw' => $interwiki,
+ 'smw_hash' => $this->computeSha1( $hash )
+ ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->setCache(
+ $subject->getDBKey(),
+ $subject->getNamespace(),
+ $subject->getInterWiki(),
+ $subject->getSubobjectName(),
+ $sid,
+ $subject->getSortKey()
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $title
+ * @param integer $namespace
+ * @param string $iw
+ */
+ public function findAssociatedRev( $title, $namespace, $iw = '' ) {
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ self::TABLE_NAME,
+ 'smw_rev',
+ [
+ "smw_title =" . $connection->addQuotes( $title ),
+ "smw_namespace =" . $connection->addQuotes( $namespace ),
+ "smw_iw =" . $connection->addQuotes( $iw ),
+ "smw_subobject =''"
+ ],
+ __METHOD__
+ );
+
+ return $row === false ? 0 : $row->smw_rev;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $sid
+ * @param integer $sid
+ */
+ public function updateRevField( $sid, $rev_id ) {
+ $this->tableFieldUpdater->updateRevField( $sid, $rev_id );
+ }
+
+ /**
+ * Fetch the ID for an SMWDIProperty object. This method achieves the
+ * same as getSMWPageID(), but avoids additional normalization steps
+ * that have already been performed when creating an SMWDIProperty
+ * object.
+ *
+ * @note There is no distinction between properties and inverse
+ * properties here. A property and its inverse have the same ID in SMW.
+ *
+ * @param SMWDIProperty $property
+ * @return integer
+ */
+ public function getSMWPropertyID( SMWDIProperty $property ) {
+ if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
+ return self::$special_ids[$property->getKey()];
+ } else {
+ $sortkey = '';
+ return $this->getDatabaseIdAndSort( $property->getKey(), SMW_NS_PROPERTY, $this->getPropertyInterwiki( $property ), '', $sortkey, true, false );
+ }
+ }
+
+ /**
+ * Fetch and possibly create the ID for an SMWDIProperty object. The
+ * method achieves the same as getSMWPageID() but avoids additional
+ * normalization steps that have already been performed when creating
+ * an SMWDIProperty object.
+ *
+ * @see getSMWPropertyID
+ * @param SMWDIProperty $property
+ * @return integer
+ */
+ public function makeSMWPropertyID( SMWDIProperty $property ) {
+ if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
+ return (int)self::$special_ids[$property->getKey()];
+ } else {
+ return (int)$this->makeDatabaseId(
+ $property->getKey(),
+ SMW_NS_PROPERTY,
+ $this->getPropertyInterwiki( $property ),
+ '',
+ true,
+ $property->getLabel(),
+ false
+ );
+ }
+ }
+
+ /**
+ * Normalize the information for an SMW object (page etc.) and return
+ * the predefined ID if any. All parameters are call-by-reference and
+ * will be changed to perform any kind of built-in normalization that
+ * SMW requires. This mainly applies to predefined properties that
+ * should always use their property key as a title, have fixed
+ * sortkeys, etc. Some very special properties also have fixed IDs that
+ * do not require any DB lookups. In such cases, the method returns
+ * this ID; otherwise it returns 0.
+ *
+ * @note This function could be extended to account for further kinds
+ * of normalization and predefined ID. However, both getSMWPropertyID
+ * and makeSMWPropertyID must then also be adjusted to do the same.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName
+ * @param string $sortkey
+ * @return integer predefined id or 0 if none
+ */
+ protected function getPredefinedData( &$title, &$namespace, &$iw, &$subobjectName, &$sortkey ) {
+ if ( $namespace == SMW_NS_PROPERTY &&
+ ( $iw === '' || $iw == SMW_SQL3_SMWINTDEFIW ) && $title != '' ) {
+
+ // Check if this is a predefined property:
+ if ( $title{0} != '_' ) {
+ // This normalization also applies to
+ // subobjects of predefined properties.
+ $newTitle = PropertyRegistry::getInstance()->findPropertyIdByLabel( str_replace( '_', ' ', $title ) );
+ if ( $newTitle ) {
+ $title = $newTitle;
+ $sortkey = PropertyRegistry::getInstance()->findPropertyLabelById( $title );
+ if ( $sortkey === '' ) {
+ $iw = SMW_SQL3_SMWINTDEFIW;
+ }
+ }
+ }
+
+ // Check if this is a property with a fixed SMW ID:
+ if ( $subobjectName === '' && array_key_exists( $title, self::$special_ids ) ) {
+ return self::$special_ids[$title];
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Change an internal id to another value. If no target value is given, the
+ * value is changed to become the last id entry (based on the automatic id
+ * increment of the database). Whatever currently occupies this id will be
+ * moved consistently in all relevant tables. Whatever currently occupies
+ * the target id will be ignored (it should be ensured that nothing is
+ * moved to an id that is still in use somewhere).
+ *
+ * @since 1.8
+ * @param integer $curid
+ * @param integer $targetid
+ */
+ public function moveSMWPageID( $curid, $targetid = 0 ) {
+ $db = $this->store->getConnection();
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ '*',
+ [ 'smw_id' => $curid ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ return; // no id at current position, ignore
+ }
+
+ $db->beginAtomicTransaction( __METHOD__ );
+
+ if ( $targetid == 0 ) { // append new id
+
+ // Bug 42659
+ $sequenceValue = $db->nextSequenceValue(
+ Sequence::makeSequence( SQLStore::ID_TABLE, 'smw_id' )
+ );
+
+ $db->insert(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $sequenceValue,
+ 'smw_title' => $row->smw_title,
+ 'smw_namespace' => $row->smw_namespace,
+ 'smw_iw' => $row->smw_iw,
+ 'smw_subobject' => $row->smw_subobject,
+ 'smw_sortkey' => $row->smw_sortkey,
+ 'smw_sort' => $row->smw_sort
+ ],
+ __METHOD__
+ );
+
+ $targetid = $db->insertId();
+ } else { // change to given id
+ $db->insert(
+ self::TABLE_NAME,
+ [ 'smw_id' => $targetid,
+ 'smw_title' => $row->smw_title,
+ 'smw_namespace' => $row->smw_namespace,
+ 'smw_iw' => $row->smw_iw,
+ 'smw_subobject' => $row->smw_subobject,
+ 'smw_sortkey' => $row->smw_sortkey,
+ 'smw_sort' => $row->smw_sort
+ ],
+ __METHOD__
+ );
+ }
+
+ $db->delete(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $curid
+ ],
+ __METHOD__
+ );
+
+ $this->setCache(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject,
+ $targetid,
+ $row->smw_sortkey
+ );
+
+ $this->idChanger->change(
+ $curid,
+ $targetid,
+ $row->smw_namespace,
+ $row->smw_namespace
+ );
+
+ $db->endAtomicTransaction( __METHOD__ );
+
+ if ( ( $title = \Title::newFromText( $row->smw_title, $row->smw_namespace ) ) !== null ) {
+ $updateJob = new UpdateJob(
+ $title
+ );
+
+ $updateJob->insert();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|array $args
+ *
+ * @return string
+ */
+ public function computeSha1( $args = '' ) {
+ return IdCacheManager::computeSha1( $args );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $list
+ */
+ public function warmUpCache( $list = [] ) {
+
+ $hashList = [];
+
+ if ( $list instanceof \SMWQueryResult ) {
+ $list = $list->getResults();
+ }
+
+ if ( !$list instanceof \Iterator && !is_array( $list ) ) {
+ return;
+ }
+
+ foreach ( $list as $item ) {
+
+ $hash = null;
+
+ if ( $item instanceof DIWikiPage ) {
+ $hash = [
+ $item->getDBKey(),
+ (int)$item->getNamespace(),
+ $item->getInterwiki(),
+ $item->getSubobjectName()
+ ];
+ }
+
+ if ( $item instanceof DIProperty ) {
+
+ // Avoid _SKEY as it is not used during an entity lookup to
+ // match an ID
+ if ( $item->getKey() === '_SKEY' ) {
+ continue;
+ }
+
+ $hash = [ $item->getKey(), SMW_NS_PROPERTY, '', '' ];
+ }
+
+ if ( $hash === null ) {
+ continue;
+ }
+
+ $hash = IdCacheManager::computeSha1( $hash );
+
+ if ( !$this->idCacheManager->hasCache( $hash ) ) {
+ $hashList[] = $hash;
+ }
+ }
+
+ if ( $hashList === [] ) {
+ return;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $rows = $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey',
+ 'smw_sort'
+ ],
+ [
+ 'smw_hash' => $hashList
+ ],
+ __METHOD__
+ );
+
+ foreach ( $rows as $row ) {
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+
+ $this->idCacheManager->setCache(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject,
+ $row->smw_id,
+ $sortkey
+ );
+ }
+ }
+
+ /**
+ * Add or modify a cache entry. The key consists of the
+ * parameters $title, $namespace, $interwiki, and $subobject. The
+ * cached data is $id and $sortkey.
+ *
+ * @since 1.8
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ * @param integer $id
+ * @param string $sortkey
+ */
+ public function setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey ) {
+ $this->idCacheManager->setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $id
+ *
+ * @return DIWikiPage|null
+ */
+ public function getDataItemById( $id ) {
+ return $this->idEntityFinder->getDataItemById( $id );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $id
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return string[]
+ */
+ public function getDataItemsFromList( array $idlist, RequestOptions $requestOptions = null ) {
+ return $this->idEntityFinder->getDataItemsFromList( $idlist, $requestOptions );
+ }
+
+ /**
+ * @deprecated since 3.0, use SMWSql3SmwIds::getDataItemsFromList
+ */
+ public function getDataItemPoolHashListFor( array $idlist, RequestOptions $requestOptions = null ) {
+ return $this->idEntityFinder->getDataItemsFromList( $idlist, $requestOptions );
+ }
+
+ /**
+ * Remove any cache entry for the given data. The key consists of the
+ * parameters $title, $namespace, $interwiki, and $subobject. The
+ * cached data is $id and $sortkey.
+ *
+ * @since 1.8
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ */
+ public function deleteCache( $title, $namespace, $interwiki, $subobject ) {
+ $this->idCacheManager->deleteCache( $title, $namespace, $interwiki, $subobject );
+ }
+
+ /**
+ * Move all cached information about subobjects.
+ *
+ * @todo This method is neither efficient nor very convincing
+ * architecturally; it should be redesigned.
+ *
+ * @since 1.8
+ * @param string $oldtitle
+ * @param integer $oldnamespace
+ * @param string $newtitle
+ * @param integer $newnamespace
+ */
+ public function moveSubobjects( $oldtitle, $oldnamespace, $newtitle, $newnamespace ) {
+ // Currently we have no way to change title and namespace across all entries.
+ // Best we can do is clear the cache to avoid wrong hits:
+ if ( $oldnamespace != SMW_NS_PROPERTY || $newnamespace != SMW_NS_PROPERTY ) {
+ $this->idCacheManager->deleteCache( $oldtitle, $oldnamespace, '', '' );
+ $this->idCacheManager->deleteCache( $newtitle, $newnamespace, '', '' );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function initCache() {
+
+ // Tests indicate that it is more memory efficient to have two
+ // arrays (IDs and sortkeys) than to have one array that stores both
+ // values in some data structure (other than a single string).
+ $this->idCacheManager = $this->factory->newIdCacheManager(
+ self::POOLCACHE_ID,
+ [
+ 'entity.id' => self::MAX_CACHE_SIZE,
+ 'entity.sort' => self::MAX_CACHE_SIZE,
+ 'entity.lookup' => 2000,
+ 'table.hash' => self::MAX_CACHE_SIZE,
+ ]
+ );
+ }
+
+ /**
+ * Return an array of hashes with table names as keys. These
+ * hashes are used to compare new data with old data for each
+ * property-value table when updating data
+ *
+ * @since 1.8
+ *
+ * @param integer $subjectId ID of the page as stored in the SMW IDs table
+ *
+ * @return array
+ */
+ public function getPropertyTableHashes( $sid ) {
+
+ if ( $sid == 0 ) {
+ return [];
+ }
+
+ $hash = null;
+ $cache = $this->idCacheManager->get( 'table.hash' );
+
+ if ( ( $hash = $cache->fetch( $sid ) ) !== false ) {
+ return $hash;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ self::TABLE_NAME,
+ [ 'smw_proptable_hash' ],
+ 'smw_id=' . $sid,
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $hash = $row->smw_proptable_hash;
+ }
+
+ if ( $hash !== null && $hash !== false && $connection->isType( 'postgres' ) ) {
+ $hash = pg_unescape_bytea( $hash );
+ }
+
+ $hash = $hash === null || $hash === false ? [] : unserialize( $hash );
+ $cache->save( $sid, $hash );
+
+ return $hash;
+ }
+
+ /**
+ * Update the proptable_hash for a given page.
+ *
+ * @since 1.8
+ * @param integer $sid ID of the page as stored in SMW IDs table
+ * @param string[] of hash values with table names as keys
+ */
+ public function setPropertyTableHashes( $sid, $hash = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $update = [];
+
+ if ( $hash === null ) {
+ $update = [ 'smw_proptable_hash' => $hash, 'smw_rev' => null ];
+ } elseif ( is_array( $hash ) ) {
+ $update = [ 'smw_proptable_hash' => serialize( $hash ) ];
+ } else {
+ throw new RuntimeException( "Expected a null or an array as value!");
+ }
+
+ $connection->update(
+ self::TABLE_NAME,
+ $update,
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->setPropertyTableHashesCache( $sid, $hash );
+
+ if ( $hash === null ) {
+ $this->idCacheManager->deleteCacheById( $sid );
+ }
+ }
+
+ /**
+ * Temporarily cache a property tablehash that has been retrieved for
+ * the given SMW ID.
+ *
+ * @since 1.8
+ * @param $id integer
+ * @param $propertyTableHash string
+ */
+ /**
+ * Temporarily cache a property tablehash that has been retrieved for
+ * the given SMW ID.
+ *
+ * @since 1.8
+ * @param $id integer
+ * @param $propertyTableHash string
+ */
+ protected function setPropertyTableHashesCache( $sid, $hash ) {
+
+ // never cache 0
+ if ( $sid == 0 ) {
+ return;
+ }
+
+ if ( $hash === null ) {
+ $hash = [];
+ } elseif ( is_string( $hash ) ) {
+ $hash = unserialize( $hash );
+ }
+
+ $cache = $this->idCacheManager->get( 'table.hash' );
+ $cache->save( $sid, $hash );
+ }
+
+ /**
+ * Returns store Id table name
+ *
+ * @return string
+ */
+ public function getIdTable() {
+ return self::TABLE_NAME;
+ }
+
+}