public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) { $oldWikiPage = SMWDIWikiPage::newFromTitle( $oldtitle ); $newWikiPage = SMWDIWikiPage::newFromTitle( $newtitle ); $oldExpResource = SMWExporter::getDataItemExpElement( $oldWikiPage ); $newExpResource = SMWExporter::getDataItemExpElement( $newWikiPage ); $namespaces = array( $oldExpResource->getNamespaceId() => $oldExpResource->getNamespace() ); $namespaces[$newExpResource->getNamespaceId()] = $newExpResource->getNamespace(); $oldUri = SMWTurtleSerializer::getTurtleNameForExpElement( $oldExpResource ); $newUri = SMWTurtleSerializer::getTurtleNameForExpElement( $newExpResource ); parent::changeTitle( $oldtitle, $newtitle, $pageid, $redirid ); // do this only here, so Imported from is not moved too early $sparqlDatabase = smwfGetSparqlDatabase(); $sparqlDatabase->insertDelete( "?s ?p $newUri", "?s ?p $oldUri", "?s ?p $oldUri", $namespaces ); if ( $oldtitle->getNamespace() == SMW_NS_PROPERTY ) { $sparqlDatabase->insertDelete( "?s $newUri ?o", "?s $oldUri ?o", "?s $oldUri ?o", $namespaces ); } // Note that we cannot change oldUri to newUri in triple subjects, // since some triples change due to the move. Use SMWUpdateJob. $newUpdate = new SMWUpdateJob( $newtitle ); $newUpdate->run(); if ( $redirid != 0 ) { // update/create redirect page data $oldUpdate = new SMWUpdateJob( $oldtitle ); $oldUpdate->run(); } }
/** * Gets the properties pointing from the current page to this one. */ static function getPagePropertiesOfPage( $title ) { if ( self::$mLinkedPagesRetrieved ) { return; } $store = smwfGetStore(); if ( class_exists( 'SMWDataItem' ) ) { $value = SMWDIWikiPage::newFromTitle( $title ); } else { $value = $title; } $data = $store->getSemanticData( $value ); foreach ( $data->getProperties() as $property ) { $propertyValues = $data->getPropertyValues( $property ); foreach ( $propertyValues as $propertyValue ) { $linkedPageName = null; if ( $propertyValue instanceof SMWDIWikiPage ) { $propertyName = $property->getKey(); $linkedPageName = $propertyValue->getDBkey(); } elseif ( $propertyValue instanceof SMWWikiPageValue ) { $propertyName = $property->getWikiValue(); $linkedPageName = $propertyValue->getWikiValue(); } if ( !is_null( $linkedPageName ) ) { if ( array_key_exists( $linkedPageName, self::$mLinkedPages ) ) { self::$mLinkedPages[$linkedPageName][] = $propertyName; } else { self::$mLinkedPages[$linkedPageName] = array( $propertyName ); } } } } self::$mLinkedPagesRetrieved = true; }
/** * This method will be called before an article is displayed or previewed. * For display and preview we strip out the semantic properties and append them * at the end of the article. * * @param Parser $parser * @param string $text */ static public function onInternalParseBeforeLinks( &$parser, &$text ) { global $smwgStoreAnnotations, $smwgLinksInValues; SMWParseData::stripMagicWords( $text, $parser ); // Store the results if enabled (we have to parse them in any case, // in order to clean the wiki source for further processing). $smwgStoreAnnotations = smwfIsSemanticsProcessed( $parser->getTitle()->getNamespace() ); SMWParserExtensions::$mTempStoreAnnotations = true; // used for [[SMW::on]] and [[SMW:off]] // Process redirects, if any (it seems that there is indeed no more direct way of getting this info from MW) if ( $smwgStoreAnnotations ) { $rt = Title::newFromRedirect( $text ); if ( !is_null( $rt ) ) { $p = new SMWDIProperty( '_REDI' ); $di = SMWDIWikiPage::newFromTitle( $rt, '__red' ); SMWParseData::getSMWData( $parser )->addPropertyObjectValue( $p, $di ); } } // only used in subsequent callbacks, forgotten afterwards SMWParserExtensions::$mTempParser = $parser; // In the regexp matches below, leading ':' escapes the markup, as known for Categories. // Parse links to extract semantic properties. if ( $smwgLinksInValues ) { // More complex regexp -- lib PCRE may cause segfaults if text is long :-( $semanticLinkPattern = '/\[\[ # Beginning of the link (?:([^:][^]]*):[=:])+ # Property name (or a list of those) ( # After that: (?:[^|\[\]] # either normal text (without |, [ or ]) |\[\[[^]]*\]\] # or a [[link]] |\[[^]]*\] # or an [external link] )*) # all this zero or more times (?:\|([^]]*))? # Display text (like "text" in [[link|text]]), optional \]\] # End of link /xu'; $text = preg_replace_callback( $semanticLinkPattern, array( 'SMWParserExtensions', 'parsePropertiesCallback' ), $text ); } else { // Simpler regexps -- no segfaults found for those, but no links in values. $semanticLinkPattern = '/\[\[ # Beginning of the link (?:([^:][^]]*):[=:])+ # Property name (or a list of those) ([^\[\]]*) # content: anything but [, |, ] \]\] # End of link /xu'; $text = preg_replace_callback( $semanticLinkPattern, array( 'SMWParserExtensions', 'simpleParsePropertiesCallback' ), $text ); } // Add link to RDF to HTML header. // TODO: do escaping via Html or Xml class. SMWOutputs::requireHeadItem( 'smw_rdf', '<link rel="alternate" type="application/rdf+xml" title="' . htmlspecialchars( $parser->getTitle()->getPrefixedText() ) . '" href="' . htmlspecialchars( SpecialPage::getTitleFor( 'ExportRDF', $parser->getTitle()->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' ) ) . "\" />" ); SMWOutputs::commitToParser( $parser ); return true; // always return true, in order not to stop MW's hook processing! }
function saclGetPermissionErrors($title, $user, $action, &$result) { // Failsafe: Some users are exempt from Semantic ACLs if ($user->isAllowed('sacl-exempt')) { return true; } $store = smwfGetStore(); $subject = SMWDIWikiPage::newFromTitle($title); // The prefix for the whitelisted group and user properties // Either ___VISIBLE or ___EDITABLE $prefix = ''; if ($action == 'read') { $prefix = '___VISIBLE'; } else { $type_property = 'Editable by'; $prefix = '___EDITABLE'; } $property = new SMWDIProperty($prefix); $aclTypes = $store->getPropertyValues($subject, $property); foreach ($aclTypes as $valueObj) { $value = strtolower($valueObj->getString()); if ($value == 'users') { if ($user->isAnon()) { $result = false; return false; } } elseif ($value == 'whitelist') { $isWhitelisted = false; $groupProperty = new SMWDIProperty("{$prefix}_WL_GROUP"); $userProperty = new SMWDIProperty("{$prefix}_WL_USER"); $whitelistValues = $store->getPropertyValues($subject, $groupProperty); foreach ($whitelistValues as $whitelistValue) { $group = strtolower($whitelistValue->getString()); if (in_array($group, $user->getEffectiveGroups())) { $isWhitelisted = true; break; } } $whitelistValues = $store->getPropertyValues($subject, $userProperty); foreach ($whitelistValues as $whitelistValue) { $title = $whitelistValue->getTitle(); if ($title->equals($user->getUserPage())) { $isWhitelisted = true; } } if (!$isWhitelisted) { $result = false; return false; } } elseif ($value == 'public') { return true; } } return true; }
/** * * Load all semantic properties of a page as a hash * @param String $pagename * @param Integer $namespace * @param Boolean $normalizeTitle * @return an associative array with the property name as key and property value as value. */ public static function &loadSemanticProperties($pagename, $namespace = NS_MAIN, $normalizeTitle = true) { //-----normalize title $data = null; if ($normalizeTitle) { $title = MWUtil::normalizePageTitle($pagename, false); $di = SMWDIWikiPage::newFromTitle($title); $data = smwfGetStore()->getSemanticData($di); } else { $di = new SMWDIWikiPage($pagename, $namespace, ''); $data = smwfGetStore()->getSemanticData($di); } $valuehash = array(); $diProperties = $data->getProperties(); foreach ($diProperties as $diProperty) { $dvProperty = SMWDataValueFactory::newDataItemValue($diProperty, null); $name = null; if ($dvProperty->isVisible()) { $name = $diProperty->getLabel(); } elseif ($diProperty->getKey() == '_INST') { $name = 'Categories'; } else { continue; // skip this line } if (!$name) { continue; } $values = $data->getPropertyValues($diProperty); $vs = array(); foreach ($values as $di) { $dv = SMWDataValueFactory::newDataItemValue($di, $diProperty); $vs[] = $dv->getWikiValue(); } if (count($vs) == 1) { $valuehash[$name] = $vs[0]; } else { $valuehash[$name] = $vs; } } #error_log(print_r($valuehash, true)); return $valuehash; }
public static function getAllPagesForConcept($conceptName, $substring = null) { global $sfgMaxAutocompleteValues, $sfgAutocompleteOnAllChars; $store = smwfGetStore(); $conceptTitle = Title::makeTitleSafe(SMW_NS_CONCEPT, $conceptName); if (!is_null($substring)) { $substring = strtolower($substring); } // Escape if there's no such concept. if ($conceptTitle == null || !$conceptTitle->exists()) { return "Could not find concept: {$conceptName}"; } $conceptDI = SMWDIWikiPage::newFromTitle($conceptTitle); $desc = new SMWConceptDescription($conceptDI); $printout = new SMWPrintRequest(SMWPrintRequest::PRINT_THIS, ""); $desc->addPrintRequest($printout); $query = new SMWQuery($desc); $query->setLimit($sfgMaxAutocompleteValues); $query_result = $store->getQueryResult($query); $pages = array(); while ($res = $query_result->getNext()) { $pageName = $res[0]->getNextText(SMW_OUTPUT_WIKI); if (is_null($substring)) { $pages[] = $pageName; } else { // Filter on the substring manually. It would // be better to do this filtering in the // original SMW query, but that doesn't seem // possible yet. $lowercasePageName = strtolower($pageName); if ($sfgAutocompleteOnAllChars) { if (strpos($lowercasePageName, $substring) >= 0) { $pages[] = $pageName; } } else { if (strpos($lowercasePageName, $substring) === 0 || strpos($lowercasePageName, ' ' . $substring) > 0) { $pages[] = $pageName; } } } } sort($pages); return $pages; }
/** * Fetch additional information that is related to the saving that has just happened, * e.g. regarding the last edit date. In runs where this hook is not triggered, the * last DB entry (of MW) will be used to fill such properties. * * @note This method directly accesses a member of Article that is informally declared to * be private. However, there is no way to otherwise access an article's parseroutput for * the purpose of adding information there. If the private access ever becomes a problem, * a global/static variable appears to be the only way to get more article data to * LinksUpdate. * * @param WikiPage|Article $article WikiPage on 1.19 and later * @param Revision $rev * @param integer $baseID * @param User $user * * @return true */ public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user) { global $smwgPageSpecialProperties; if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) { $output = $article->mPreparedEdit->output; $title = $article->getTitle(); if (!isset($title)) { return true; // nothing we can do } if (!isset($output->mSMWData)) { // no data container yet, make one $output->mSMWData = new SMWSemanticData(new SMWDIWikiPage($title->getDBkey(), $title->getNamespace(), $title->getInterwiki())); } $semdata = $output->mSMWData; } else { // give up, just keep the old data return true; } if (in_array('_MDAT', $smwgPageSpecialProperties)) { $timestamp = $article->getTimestamp(); $di = self::getDataItemFromMWTimestamp($timestamp); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di); } } if (in_array('_LEDT', $smwgPageSpecialProperties)) { $di = SMWDIWikiPage::newFromTitle($user->getUserPage()); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di); } } if (in_array('_NEWP', $smwgPageSpecialProperties)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId()))); } return true; }
/** * 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 $redirid */ public function changeTitle(Title $oldtitle, Title $newtitle, $pageid, $redirid = 0) { global $smwgQEqualitySupport; wfRunHooks('SMW::SQLStore::BeforeChangeTitleComplete', array($this->store, $oldtitle, $newtitle, $pageid, $redirid)); $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::tableName, array('smw_title' => $newtitle->getDBkey(), 'smw_namespace' => $newtitle->getNamespace(), 'smw_iw' => ''), array('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()->addRedirectForId($sid, $oldtitle->getDBkey(), $oldtitle->getNamespace()); $statsTable = new PropertyStatisticsTable($db, SMWSQLStore3::PROPERTY_STATISTICS_TABLE); $statsTable->addToUsageCount($this->store->getObjectIds()->getSMWPropertyID(new SMWDIProperty('_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 new title // ($newtitle should not have data, but let's be sure) $emptyNewSemanticData = new SMWSemanticData(SMWDIWikiPage::newFromTitle($newtitle)); $this->doDataUpdate($emptyNewSemanticData); // Move all data of old title to new position: if ($sid != 0) { $this->store->changeSMWPageID($sid, $tid, $oldtitle->getNamespace(), $newtitle->getNamespace(), true, false); } // Associate internal objects (subobjects) with the new title: $table = $db->tableName(SMWSql3SmwIds::tableName); $values = array('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 ($redirid == 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()); } } }
/** * Having a title of a page, what is the URI that is described by that page? * The result still requires expandURI() * @param string $title * @return string $uri */ private function getURI($title) { $uri = ""; if ($title instanceof Title) { $wikiPageDI = SMWDIWikiPage::newFromTitle($title); $exp = SMWExporter::getInstance()->makeExportDataForSubject($wikiPageDI); $uri = $exp->getSubject()->getUri(); } else { // There could be other types as well that we do NOT handle here } return $uri; // still requires expandURI() }
/** * @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) || !smwfIsSemanticsProcessed($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 (!smwfIsSemanticsProcessed($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); }
/** * Static function for creating a new wikipage object from a * MediaWiki Title object. * * @deprecated This method will vanish before SMW 1.7. If you really need this, simply copy its code. * * @return SMWWikiPageValue */ public static function makePageFromTitle(Title $title) { $dvWikiPage = new SMWWikiPageValue('_wpg'); $diWikiPage = SMWDIWikiPage::newFromTitle($title); $dvWikiPage->setDataItem($diWikiPage); $dvWikiPage->m_title = $title; // optional, just for efficiency return $dvWikiPage; }
/** * Returns whether the concepts of the group cover the specified page. * * @since 0.1 * * @param Title $title * * @return boolean */ public function conceptsCoverPage(Title $title) { if (count($this->concepts) == 0) { return true; } $foundMatch = false; foreach ($this->concepts as $groupConcept) { $queryDescription = new SMWConjunction(); $conceptTitle = Title::newFromText($groupConcept, SMW_NS_CONCEPT); if (!$conceptTitle->exists()) { continue; } $queryDescription->addDescription(new SMWConceptDescription(SMWDIWikiPage::newFromTitle($conceptTitle))); $queryDescription->addDescription(new SMWValueDescription(SMWDIWikiPage::newFromTitle($title))); $query = new SMWQuery($queryDescription); $query->querymode = SMWQuery::MODE_COUNT; /* SMWQueryResult */ $result = smwfGetStore()->getQueryResult($query); $foundMatch = $result instanceof SMWQueryResult ? $result->getCount() > 0 : $result > 0; if ($foundMatch) { break; } } return $foundMatch; }
public function execute($par) { global $wgRequest, $wgScript; $this->setHeaders(); $output = $this->getOutput(); if ($par != '') { $parts = explode('/', urldecode($par)); } else { $parts = array(); } $type = isset($parts[0]) ? $parts[0] : $wgRequest->getText('type'); if ($type == '') { $type = 'ancestors'; } $pageName = isset($parts[1]) ? $parts[1] : $wgRequest->getText('page'); if ($type == 'link') { $pageName2 = isset($parts[2]) ? $parts[2] : $wgRequest->getText('page2'); $numOfGenerations = 0; } else { $numOfGenerations = isset($parts[2]) ? intval($parts[2]) : $wgRequest->getInt('gen'); if ($numOfGenerations <= 0) { $numOfGenerations = 5; } $pageName2 = ''; } if (!$this->mIncluding) { $output->addModules('ext.smg.specialfamilytree'); $typeSelect = new XmlSelect('type', 'type', $type); $typeSelect->addOption(wfMsg('semanticgenealogy-specialfamilytree-type-ancestors'), 'ancestors'); $typeSelect->addOption(wfMsg('semanticgenealogy-specialfamilytree-type-descendant'), 'descendant'); $typeSelect->addOption(wfMsg('semanticgenealogy-specialfamilytree-type-link'), 'link'); $output->addHTML(Xml::openElement('form', array('action' => $wgScript)) . Html::hidden('title', $this->getPageTitle()->getPrefixedText()) . Xml::openElement('fieldset') . Xml::openElement('table', array('id' => 'smg-familyTree-form')) . Xml::openElement('tr', array('id' => 'smg-form-entry-page')) . Xml::openElement('th', array('class' => 'mw-label')) . Xml::label(wfMsg('semanticgenealogy-specialfamilytree-label-page'), 'page') . Xml::closeElement('th') . Xml::openElement('td', array('class' => 'mw-input')) . Xml::input('page', 30, $pageName, array('class' => 'smg-input-page')) . Xml::closeElement('td') . Xml::closeElement('tr') . Xml::openElement('tr', array('id' => 'smg-form-entry-type')) . Xml::openElement('th', array('class' => 'mw-label')) . Xml::label(wfMsg('semanticgenealogy-specialfamilytree-label-type'), 'type') . Xml::closeElement('th') . Xml::openElement('td', array('class' => 'mw-input')) . $typeSelect->getHtml() . Xml::closeElement('td') . Xml::closeElement('tr') . Xml::openElement('tr', array('id' => 'smg-form-entry-gen')) . Xml::openElement('th', array('class' => 'mw-label')) . Xml::label(wfMsg('semanticgenealogy-specialfamilytree-label-gen'), 'gen') . Xml::closeElement('th') . Xml::openElement('td', array('class' => 'mw-input')) . Xml::input('gen', 2, $numOfGenerations) . Xml::closeElement('td') . Xml::closeElement('tr') . Xml::openElement('tr', array('id' => 'smg-form-entry-page2')) . Xml::openElement('th', array('class' => 'mw-label')) . Xml::label(wfMsg('semanticgenealogy-specialfamilytree-label-page2'), 'page2') . Xml::closeElement('th') . Xml::openElement('td', array('class' => 'mw-input')) . Xml::input('page2', 30, $pageName2, array('class' => 'smg-input-page')) . Xml::closeElement('td') . Xml::closeElement('tr') . Xml::closeElement('table') . Xml::submitButton(wfMsg('semanticgenealogy-specialfamilytree-button-submit')) . Xml::closeElement('fieldset') . Xml::closeElement('form')); } if ($pageName == '') { return; } $pageTitle = Title::newFromText($pageName); $page = SMWDIWikiPage::newFromTitle($pageTitle); if ($type == '') { $type = 'ancestors'; } switch ($type) { case 'ancestors': $tree = $this->getAncestors($page, $numOfGenerations); $this->outputAncestorsTree($tree, $numOfGenerations); break; case 'descendant': $this->outputDescendantList($page, $numOfGenerations); break; case 'link': if ($pageName2 == '') { $output->addWikiText('<span class="error">' . wfMsg('semanticgenealogy-specialfamilytree-error-nosecondpagename') . '</span>'); return; } $pageTitle2 = Title::newFromText($pageName2); $page2 = SMWDIWikiPage::newFromTitle($pageTitle2); $tree = $this->getRelation($page, $page2); if ($tree !== null) { $this->outputRelationTree($tree); } else { $output->addWikiText('<span class="error">' . wfMsg('semanticgenealogy-specialfamilytree-error-nolinkfound', $pageName, $pageName2) . '</span>'); } break; default: $output->addWikiText('<span class="error">' . wfMsg('semanticgenealogy-specialfamilytree-error-unknowntype', $type) . '</span>'); } return Status::newGood(); }
/** * @brief Adds the properties, hooks into SMWStore::updateDataBefore. * * @param SMWStore $store, SMWSemanticData $newData * * @return true * */ public function sespUpdateDataBefore($store, $data) { global $sespSpecialProperties, $wgDisableCounters; // just some compat mode global $smwgPageSpecialProperties2; if (isset($smwgPageSpecialProperties2) && !isset($sespSpecialProperties)) { $sespSpecialProperties = $smwgPageSpecialProperties2; } /* Get array of properties to set */ if (!isset($sespSpecialProperties)) { wfDebug(__METHOD__ . ": SESP array is not specified, please add the following\n"); wfDebug("variables to your LocalSettings.php:\n"); wfDebug("\$sespSpecialProperties\n"); return true; } /* Get current title and article */ $title = $data->getSubject()->getTitle(); $article = Article::newFromTitle($title, RequestContext::getMain()); // return if $title or $article is null if (is_null($title) || is_null($article)) { return true; } /**************************/ /* CUSER (First author) */ /**************************/ if (in_array('_CUSER', $sespSpecialProperties)) { $firstRevision = $title->getFirstRevision(); if ($firstRevision !== null) { $firstAuthor = User::newFromId($firstRevision->getRawUser()); if ($firstAuthor) { $property = new SMWDIProperty('___CUSER'); $dataItem = SMWDIWikiPage::newFromTitle($firstAuthor->getUserPage()); $data->addPropertyObjectValue($property, $dataItem); } } } // end if _CUSER /**************************/ /* REVID (Revision ID) */ /**************************/ if (in_array('_REVID', $sespSpecialProperties)) { $property = new SMWDIProperty('___REVID'); $dataItem = new SMWDINumber($article->getRevIdFetched()); $data->addPropertyObjectValue($property, $dataItem); } /********************************/ /* VIEWS (Number of page views) */ /********************************/ if (in_array('_VIEWS', $sespSpecialProperties) && !$wgDisableCounters) { $property = new SMWDIProperty('___VIEWS'); $dataItem = new SMWDINumber($article->getCount()); $data->addPropertyObjectValue($property, $dataItem); } /*****************************/ /* EUSER (Page contributors) */ /*****************************/ if (in_array('_EUSER', $sespSpecialProperties)) { /* Create property */ $property = new SMWDIProperty('___EUSER'); /* Get options */ global $wgSESPExcludeBots; if (!isset($wgSESPExcludeBots)) { $wgSESPExcludeBots = false; } /* Get author from current revision */ $u = User::newFromId($article->getUser()); /* Get authors from earlier revisions */ $authors = $article->getContributors(); while ($u) { if (!$u->isHidden() && !(in_array('bot', $u->getRights()) && $wgSESPExcludeBots) && !$u->isAnon()) { //no anonymous users /* Add values*/ $dataItem = SMWDIWikiPage::newFromTitle($u->getUserPage()); $data->addPropertyObjectValue($property, $dataItem); } $u = $authors->current(); $authors->next(); } } /******************************/ /* NREV (Number of revisions) */ /******************************/ if (in_array('_NREV', $sespSpecialProperties)) { /* Create property */ $property = new SMWDIProperty('___NREV'); /* Get number of revisions */ $dbr =& wfGetDB(DB_SLAVE); $num = $dbr->estimateRowCount("revision", "*", array("rev_page" => $title->getArticleID())); /* Add values */ $dataItem = new SMWDINumber($num); $data->addPropertyObjectValue($property, $dataItem); } /*****************************************/ /* NTREV (Number of talk page revisions) */ /*****************************************/ if (in_array('_NTREV', $sespSpecialProperties)) { /* Create property */ $property = new SMWDIProperty('___NTREV'); /* Get number of revisions */ if (!isset($dbr)) { $dbr =& wfGetDB(DB_SLAVE); } $talkPage = $title->getTalkPage(); $num = $dbr->estimateRowCount("revision", "*", array("rev_page" => $talkPage->getArticleID())); /* Add values */ $dataItem = new SMWDINumber($num); $data->addPropertyObjectValue($property, $dataItem); } /************************/ /* SUBP (Get sub pages) */ /************************/ if (in_array('_SUBP', $sespSpecialProperties)) { /* Create property */ $property = new SMWDIProperty('___SUBP'); $subpages = $title->getSubpages(-1); //-1 = no limit. Returns TitleArray object /* Add values*/ foreach ($subpages as $t) { $dataItem = SMWDIWikiPage::newFromTitle($t); $data->addPropertyObjectValue($property, $dataItem); } // end foreach } // end _SUBP /************************/ /* MIMETYPE */ /************************/ if ($title->inNamespace(NS_FILE) && in_array('_MIMETYPE', $sespSpecialProperties)) { // Build image page instance $imagePage = new ImagePage($title); $file = $imagePage->getFile(); $mimetype = $file->getMimeType(); $mediaType = MimeMagic::singleton()->findMediaType($mimetype); list($mimetypemajor, $mimetypeminor) = $file->splitMime($mimetype); // MIMETYPE $property = new SMWDIProperty('___MIMETYPE'); $dataItem = new SMWDIString($mimetypeminor); $data->addPropertyObjectValue($property, $dataItem); // MEDIATYPE $property = new SMWDIProperty('___MEDIATYPE'); $dataItem = new SMWDIString($mediaType); $data->addPropertyObjectValue($property, $dataItem); } // end if MIMETYPE /************************/ /* IMAGEMETA */ /************************/ function convertexifdate($exifString) { $exifPieces = explode(":", $exifString); if ($exifPieces[0] && $exifPieces[1] && $exifPieces[2]) { $res = new DateTime($exifPieces[0] . "-" . $exifPieces[1] . "-" . $exifPieces[2] . ":" . $exifPieces[3] . ":" . $exifPieces[4]); return $res; } else { return false; } } if ($title->inNamespace(NS_FILE) && in_array('_METADATA', $sespSpecialProperties)) { $imagePage = new ImagePage($title); $file = $imagePage->getFile(); $metadata = $file->getMetadata(); if ($metadata === ExifBitmapHandler::OLD_BROKEN_FILE || $metadata === ExifBitmapHandler::BROKEN_FILE) { // So we don't try and display metadata from PagedTiffHandler // for example when using InstantCommons. return true; } $exif = unserialize($metadata); if ($exif) { if (count($exif)) { // EXIFDATETIME if (array_key_exists('DateTimeOriginal', $exif) || array_key_exists('DateTime', $exif)) { $property = new SMWDIProperty('___EXIFDATETIME'); if (array_key_exists('DateTimeOriginal', $exif)) { $exifstr = $exif['DateTimeOriginal']; } else { $exifstr = $exif['DateTime']; } $datetime = convertexifdate($exifstr); if ($datetime) { $dataItem = new SMWDITime(SMWDITime::CM_GREGORIAN, $datetime->format('Y'), $datetime->format('n'), $datetime->format('j'), $datetime->format('G'), $datetime->format('i')); $data->addPropertyObjectValue($property, $dataItem); } } // EXIFSOFTWARE if (array_key_exists('Software', $exif) || array_key_exists('metadata', $exif) && array_key_exists('Software', $exif['metadata'])) { $str = array_key_exists('Software', $exif) ? $exif['Software'] : $exif['metadata']['Software']; if (is_array($str)) { $str = array_key_exists('x-default', $str) ? $str['x-default'] : $str[0]; } if (!$str) { return true; } $property = new SMWDIProperty('___EXIFSOFTWARE'); $dataItem = new SMWDIString($str); $data->addPropertyObjectValue($property, $dataItem); } // EXIFLATLON /* //TODO if ( array_key_exists( 'GPSLatitudeRef', $exif ) || array_key_exists( 'GPSLongitudeRef', $exif ) ) { } */ //EXIFLATLON } } } //IMAGEMETA /************************/ /* SHORTURL */ /************************/ //FIXME handle internal and external links if (in_array('_SHORTURL', $sespSpecialProperties) && class_exists('ShortUrlUtils')) { global $wgShortUrlPrefix; if (!is_string($wgShortUrlPrefix)) { $urlPrefix = SpecialPage::getTitleFor('ShortUrl')->getFullUrl() . '/'; } else { $urlPrefix = $wgShortUrlPrefix; } if (ShortUrlUtils::needsShortUrl($title)) { $shortId = ShortUrlUtils::encodeTitle($title); $shortURL = $urlPrefix . $shortId; $property = new SMWDIProperty('___SHORTURL'); $dataItem = new SMWDIUri('http', $shortURL, '', ''); $data->addPropertyObjectValue($property, $dataItem); } else { } } // end if SHORTURL return true; }
/** * Fetch additional information that is related to the saving that has just happened, * e.g. regarding the last edit date. In runs where this hook is not triggered, the * last DB entry (of MW) will be used to fill such properties. * * @note This method directly accesses a member of Article that is informally declared to * be private. However, there is no way to otherwise access an article's parseroutput for * the purpose of adding information there. If the private access ever becomes a problem, * a global/static variable appears to be the only way to get more article data to * LinksUpdate. * * @param WikiPage|Article $article WikiPage on 1.19 and later * @param Revision $rev * @param integer $baseID * @param User $user * * @return true */ public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user) { global $smwgPageSpecialProperties; if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) { $semdata = self::getSMWDataFromParserOutput($article->mPreparedEdit->output, $article->getTitle()); } else { // give up, just keep the old data return true; } if (in_array('_MDAT', $smwgPageSpecialProperties)) { $timestamp = $article->getTimestamp(); $di = self::getDataItemFromMWTimestamp($timestamp); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di); } } if (in_array('_LEDT', $smwgPageSpecialProperties)) { $di = SMWDIWikiPage::newFromTitle($user->getUserPage()); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di); } } if (in_array('_NEWP', $smwgPageSpecialProperties)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId()))); } return true; }
/** * Refresh the concept cache for the given concept. * * @since 1.8 * @param $concept Title * @return array of error strings (empty if no errors occurred) */ public function refreshConceptCache(Title $concept) { global $smwgQMaxLimit, $smwgQConceptFeatures, $wgDBtype; $fname = 'SMW::refreshConceptCache'; $cid = $this->m_store->smwIds->getSMWPageID($concept->getDBkey(), SMW_NS_CONCEPT, '', ''); $cid_c = $this->m_store->smwIds->getSMWPageID($concept->getDBkey(), SMW_NS_CONCEPT, '', '', false); if ($cid != $cid_c) { $this->m_errors[] = "Skipping redirect concept."; return $this->m_errors; } $values = $this->m_store->getPropertyValues(SMWDIWikiPage::newFromTitle($concept), new SMWDIProperty('_CONC')); $di = end($values); $desctxt = $di !== false ? $di->getConceptQuery() : false; $this->m_errors = array(); if ($desctxt) { // concept found $this->m_qmode = SMWQuery::MODE_INSTANCES; $this->m_queries = array(); $this->m_hierarchies = array(); $this->m_querylog = array(); $this->m_sortkeys = array(); SMWSQLStore3Query::$qnum = 0; // Pre-process query: $qp = new SMWQueryParser($smwgQConceptFeatures); $desc = $qp->getQueryDescription($desctxt); $qid = $this->compileQueries($desc); $this->executeQueries($this->m_queries[$qid]); // execute query tree, resolve all dependencies $qobj = $this->m_queries[$qid]; if ($qobj->joinfield === '') { return; } // Update database: $this->m_dbs->delete(SMWSQLStore3::CONCEPT_CACHE_TABLE, array('o_id' => $cid), $fname); $smw_conccache = $this->m_dbs->tablename(SMWSQLStore3::CONCEPT_CACHE_TABLE); if ($wgDBtype == 'postgres') { // PostgresQL: no INSERT IGNORE, check for duplicates explicitly $where = $qobj->where . ($qobj->where ? ' AND ' : '') . "NOT EXISTS (SELECT NULL FROM {$smw_conccache}" . " WHERE {$smw_conccache}.s_id = {$qobj->alias}.s_id " . " AND {$smw_conccache}.o_id = {$qobj->alias}.o_id )"; } else { // MySQL just uses INSERT IGNORE, no extra conditions $where = $qobj->where; } $this->m_dbs->query("INSERT " . ($wgDBtype == 'postgres' ? '' : 'IGNORE ') . "INTO {$smw_conccache}" . " SELECT DISTINCT {$qobj->joinfield} AS s_id, {$cid} AS o_id FROM " . $this->m_dbs->tableName($qobj->jointable) . " AS {$qobj->alias}" . $qobj->from . ($where ? ' WHERE ' : '') . $where . " LIMIT {$smwgQMaxLimit}", $fname); $this->m_dbs->update('smw_fpt_conc', array('cache_date' => strtotime("now"), 'cache_count' => $this->m_dbs->affectedRows()), array('s_id' => $cid), $fname); } else { // no concept found; just delete old data if there is any $this->m_dbs->delete(SMWSQLStore3::CONCEPT_CACHE_TABLE, array('o_id' => $cid), $fname); $this->m_dbs->update('smw_fpt_conc', array('cache_date' => null, 'cache_count' => null), array('s_id' => $cid), $fname); $this->m_errors[] = "No concept description found."; } $this->cleanUp(); return $this->m_errors; }
/** * Like Article's getTitle(), but returning a suitable SMWDIWikiPage. * * @since 1.6 * * @return SMWDIWikiPage */ protected function getDataItem() { return SMWDIWikiPage::newFromTitle($this->getTitle()); }
/** * 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? * * @param Title $oldtitle * @param Title $newtitle * @param integer $pageid * @param integer $redirid */ public function changeTitle(Title $oldtitle, Title $newtitle, $pageid, $redirid = 0) { global $smwgQEqualitySupport; wfProfileIn("SMWSQLStore2::changeTitle (SMW)"); // get IDs but do not resolve redirects: $sid = $this->getSMWPageID($oldtitle->getDBkey(), $oldtitle->getNamespace(), '', '', false); $tid = $this->getSMWPageID($newtitle->getDBkey(), $newtitle->getNamespace(), '', '', false); $db = wfGetDB(DB_MASTER); // 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('smw_ids', array('smw_title' => $newtitle->getDBkey(), 'smw_namespace' => $newtitle->getNamespace(), 'smw_iw' => ''), array('smw_title' => $oldtitle->getDBkey(), 'smw_namespace' => $oldtitle->getNamespace(), 'smw_iw' => ''), __METHOD__); $this->m_idCache->moveSubobjects($oldtitle->getDBkey(), $oldtitle->getNamespace(), $newtitle->getDBkey(), $newtitle->getNamespace()); $this->m_idCache->setId($oldtitle->getDBkey(), $oldtitle->getNamespace(), '', '', 0); $this->m_idCache->setId($newtitle->getDBkey(), $newtitle->getNamespace(), '', '', $sid); } else { // make new (target) id for use in redirect table $sid = $this->makeSMWPageID($newtitle->getDBkey(), $newtitle->getNamespace(), '', ''); } // at this point, $sid is the id of the target page (according to smw_ids) // make redirect id for oldtitle: $this->makeSMWPageID($oldtitle->getDBkey(), $oldtitle->getNamespace(), SMW_SQL2_SMWREDIIW, ''); $db->insert('smw_redi2', array('s_title' => $oldtitle->getDBkey(), 's_namespace' => $oldtitle->getNamespace(), 'o_id' => $sid), __METHOD__); /// 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 behaviour: 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 from new title: // $newtitle should not have data, but let's be sure $this->deleteSemanticData(SMWDIWikiPage::newFromTitle($newtitle)); // Update (i.e. delete) redirects (may trigger update jobs): $this->updateRedirects($newtitle->getDBkey(), $newtitle->getNamespace()); // Move all data of old title to new position: if ($sid != 0) { $this->changeSMWPageID($sid, $tid, $oldtitle->getNamespace(), $newtitle->getNamespace(), true, false); } // Associate internal objects (subobjects) with the new title: $table = $db->tableName('smw_ids'); $values = array('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(''); $db->query($sql, __METHOD__); // The below code can be used instead when moving to MW 1.17 (support for '!' in Database::makeList()): // $db->update( 'smw_ids', // array( 'smw_title' => $newtitle->getDBkey(), 'smw_namespace' => $newtitle->getNamespace(), 'smw_iw' => '' ), // array( 'smw_title' => $oldtitle->getDBkey(), 'smw_namespace' => $oldtitle->getNamespace(), 'smw_iw' => '', 'smw_subobject!' => array( '' ) ), // array() needed for ! to work // __METHOD__ ); $this->m_idCache->moveSubobjects($oldtitle->getDBkey(), $oldtitle->getNamespace(), $newtitle->getDBkey(), $newtitle->getNamespace()); // 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()); } wfProfileOut("SMWSQLStore2::changeTitle (SMW)"); }
/** * This function creates wiki text suitable for rendering a Factbox based on the * information found in a given ParserOutput object. If the required custom data * is not found in the given ParserOutput, then semantic data for the provided Title * object is retreived from the store. * * @param ParserOutput $parseroutput * @param Title $title * * @return string */ public static function getFactboxTextFromOutput(ParserOutput $parseroutput, Title $title) { global $wgRequest, $smwgShowFactboxEdit, $smwgShowFactbox; $mws = isset($parseroutput->mSMWMagicWords) ? $parseroutput->mSMWMagicWords : array(); if (in_array('SMW_SHOWFACTBOX', $mws)) { $showfactbox = SMW_FACTBOX_NONEMPTY; } elseif (in_array('SMW_NOFACTBOX', $mws)) { $showfactbox = SMW_FACTBOX_HIDDEN; } elseif ($wgRequest->getCheck('wpPreview')) { $showfactbox = $smwgShowFactboxEdit; } else { $showfactbox = $smwgShowFactbox; } if ($showfactbox == SMW_FACTBOX_HIDDEN) { // use shortcut return ''; } // Deal with complete dataset only if needed: $smwData = SMWParseData::getSMWDataFromParserOutput($parseroutput); if ($smwData === null || $smwData->stubObject) { $smwData = smwfGetStore()->getSemanticData(SMWDIWikiPage::newFromTitle($title)); } return SMWFactbox::getFactboxText($smwData, $showfactbox); }
/** * Creates and returns a new SWLChangeSet instance from a database result * obtained by doing a select on swl_sets. * * @since 0.1 * * @param array $changeSetArray * * @return SWLChangeSet */ public static function newFromArray(array $changeSetArray) { $changeSet = new SWLChangeSet(SMWDIWikiPage::newFromTitle(Title::newFromID($changeSetArray['page_id'])), null, null, null, $changeSetArray['id'], new SWLEdit($changeSetArray['page_id'], $changeSetArray['user_name'], $changeSetArray['time'], $changeSetArray['editid'])); foreach ($changeSetArray['changes'] as $propName => $changes) { $property = SMWDIProperty::doUnserialize($propName, '__pro'); foreach ($changes as $change) { $changeSet->addChange($property, SWLPropertyChange::newFromSerialization($property, array_key_exists('old', $change) ? $change['old'] : null, array_key_exists('new', $change) ? $change['new'] : null)); } } return $changeSet; }
/** * Return serialised results in specified format. */ protected function getResultText(SMWQueryResult $res, $outputmode) { if ($this->mTreeProp === '') { $res->addErrors(array(wfMessage('srf-noparentprop')->inContentLanguage()->text())); return ''; } $store = $res->getStore(); // put everything in a list and set parent hashes // elements appearing more than once will be inserted more than once and // elements with more than one parent will be cloned for each parent, // but only one instance will ever be inserted with the hash and // only this instance will later be considered as a parent element in the tree $tree = array(); while ($row = $res->getNext()) { $element = new SRFTreeElement($row); $hash = $row[0]->getResultSubject()->getSerialization(); if (array_key_exists($hash, $tree)) { $hash = null; } $parents = $store->getPropertyValues($element->mRow[0]->getResultSubject(), SMWDIProperty::newFromUserLabel($this->mTreeProp)); if (empty($parents)) { // no parents: copy into tree as root level item if ($hash !== null) { $tree[$hash] = $element; } else { $tree[] = $element; } } else { // one or more parents: copy one copy per parent into tree foreach ($parents as $parent) { if ($hash !== null) { $tree[$hash] = $element; $hash = null; } else { $element = clone $element; $tree[] = $element; } $element->mParent = $parent->getSerialization(); } } } $rootElements = array(); // build pointers from parents to children and remove pointers to parents that don't exist in the tree foreach ($tree as $hash => $element) { if ($element->mParent !== null) { if (array_key_exists($element->mParent, $tree)) { $tree[$element->mParent]->mChildren[] = $element; } else { $element->mParent = null; $rootElements[$hash] = $element; } } else { $rootElements[$hash] = $element; } } $result = ''; $rownum = 0; // if a specific page was specified as root element of the tree if ($this->mRoot !== '') { // get the title object of the root page $rootTitle = Title::newFromText($this->mRoot); if ($rootTitle === null) { $res->addErrors(array(wfMessage('srf-rootinvalid')->params($this->mRoot)->inContentLanguage()->text())); return ''; } $rootSerialization = SMWDIWikiPage::newFromTitle($rootTitle)->getSerialization(); // find the root page in the tree and print it and its subtree if (array_key_exists($rootSerialization, $tree)) { $this->printElement($result, $tree[$rootSerialization], $rownum, $this->mStartLevel); } } else { // iterate through all tree elements foreach ($rootElements as $hash => $element) { // print current root element and its subtree $this->printElement($result, $element, $rownum, $this->mStartLevel); } } return $result; }