/** * Handle the onDataChanged hook of SMW >1.6, which gets called * every time the value of a propery changes somewhere. * * @since 0.1 * * @param SMWStore $store * @param SMWChangeSet $changes * * @return true */ public static function onDataUpdate( SMWStore $store, SMWSemanticData $newData ) { $subject = $newData->getSubject(); $oldData = $store->getSemanticData( $subject ); $title = Title::makeTitle( $subject->getNamespace(), $subject->getDBkey() ); $groups = SWLGroups::getMatchingWatchGroups( $title ); $edit = false; foreach ( $groups as /* SWLGroup */ $group ) { $changeSet = SWLChangeSet::newFromSemanticData( $oldData, $newData, $group->getProperties() ); if ( $changeSet->hasUserDefinedProperties() ) { if ( $edit === false ) { $edit = new SWLEdit( $title->getArticleID(), $GLOBALS['wgUser']->getName(), wfTimestampNow() ); $edit->writeToDB(); } $changeSet->setEdit( $edit ); $setId = $changeSet->writeToStore( $groups, $edit->getId() ); if ( $setId != 0 ) { $group->notifyWatchingUsers( $changeSet ); } } } return true; }
/** * Create a new SMWSqlStubSemanticData object that holds the data of a * given SMWSemanticData object. Array assignments create copies in PHP * so the arrays are distinct in input and output object. The object * references are copied as references in a shallow way. This is * sufficient as the data items used there are immutable. * * @param $semanticData SMWSemanticData * @return SMWSqlStubSemanticData */ public static function newFromSemanticData(SMWSemanticData $semanticData) { $result = new SMWSqlStubSemanticData($semanticData->getSubject()); $result->mPropVals = $semanticData->mPropVals; $result->mProperties = $semanticData->mProperties; $result->mHasVisibleProps = $semanticData->mHasVisibleProps; $result->mHasVisibleSpecs = $semanticData->mHasVisibleSpecs; $result->stubObject = $semanticData->stubObject; return $result; }
/** * 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(); if ( $subject->getNamespace() == SMW_NS_PROPERTY ) { $types = $semdata->getPropertyValues( new SMWDIProperty( '_TYPE' ) ); } else { $types = array(); } $result = self::makeExportDataForSubject( $subject, end( $types ) ); foreach ( $semdata->getProperties() as $property ) { self::addPropertyValues( $property, $semdata->getPropertyValues( $property ), $result, $subject ); } return $result; }
/** * Update the semantic data stored for some individual. The data is * given as a SMWSemanticData object, which contains all semantic data * for one particular subject. * * @param $data SMWSemanticData */ public function updateData(SMWSemanticData $data) { /** * @since 1.6 */ wfRunHooks('SMWStore::updateDataBefore', array($this, $data)); // Invalidate the page, so data stored on it gets displayed immediately in queries. global $smwgAutoRefreshSubject; if ($smwgAutoRefreshSubject && !wfReadOnly()) { $title = Title::makeTitle($data->getSubject()->getNamespace(), $data->getSubject()->getDBkey()); $dbw = wfGetDB(DB_MASTER); $dbw->update('page', array('page_touched' => $dbw->timestamp(time() + 4)), $title->pageCond(), __METHOD__); HTMLFileCache::clearFileCache($title); } $this->doDataUpdate($data); /** * @since 1.6 */ wfRunHooks('SMWStore::updateDataAfter', array($this, $data)); }
/** * This function creates wiki text suitable for rendering a Factbox for a given * SMWSemanticData object that holds all relevant data. It also checks whether the * given setting of $showfactbox requires displaying the given data at all. * * @param SMWSemanticData $semdata * @param boolean $showfactbox * * @return string */ public static function getFactboxText(SMWSemanticData $semdata, $showfactbox = SMW_FACTBOX_NONEMPTY) { global $wgContLang; wfProfileIn('SMWFactbox::printFactbox (SMW)'); switch ($showfactbox) { case SMW_FACTBOX_HIDDEN: // never show wfProfileOut('SMWFactbox::printFactbox (SMW)'); return ''; case SMW_FACTBOX_SPECIAL: // show only if there are special properties if (!$semdata->hasVisibleSpecialProperties()) { wfProfileOut('SMWFactbox::printFactbox (SMW)'); return ''; } break; case SMW_FACTBOX_NONEMPTY: // show only if non-empty if (!$semdata->hasVisibleProperties()) { wfProfileOut('SMWFactbox::printFactbox (SMW)'); return ''; } break; // case SMW_FACTBOX_SHOWN: // just show ... } // actually build the Factbox text: $text = ''; if (wfRunHooks('smwShowFactbox', array(&$text, $semdata))) { $subjectDv = SMWDataValueFactory::newDataItemValue($semdata->getSubject(), null); SMWOutputs::requireResource('ext.smw.style'); $rdflink = SMWInfolink::newInternalLink(wfMessage('smw_viewasrdf')->inContentLanguage()->text(), $wgContLang->getNsText(NS_SPECIAL) . ':ExportRDF/' . $subjectDv->getWikiValue(), 'rdflink'); $browselink = SMWInfolink::newBrowsingLink($subjectDv->getText(), $subjectDv->getWikiValue(), 'swmfactboxheadbrowse'); $text .= '<div class="smwfact">' . '<span class="smwfactboxhead">' . wfMessage('smw_factbox_head', $browselink->getWikiText())->inContentLanguage()->text() . '</span>' . '<span class="smwrdflink">' . $rdflink->getWikiText() . '</span>' . '<table class="smwfacttable">' . "\n"; foreach ($semdata->getProperties() as $propertyDi) { $propertyDv = SMWDataValueFactory::newDataItemValue($propertyDi, null); if (!$propertyDi->isShown()) { // showing this is not desired, hide continue; } elseif ($propertyDi->isUserDefined()) { // user defined property $propertyDv->setCaption(preg_replace('/[ ]/u', ' ', $propertyDv->getWikiValue(), 2)); /// NOTE: the preg_replace is a slight hack to ensure that the left column does not get too narrow $text .= '<tr><td class="smwpropname">' . $propertyDv->getShortWikiText(true) . '</td><td class="smwprops">'; } elseif ($propertyDv->isVisible()) { // predefined property $text .= '<tr><td class="smwspecname">' . $propertyDv->getShortWikiText(true) . '</td><td class="smwspecs">'; } else { // predefined, internal property continue; } $propvalues = $semdata->getPropertyValues($propertyDi); $valuesHtml = array(); foreach ($propvalues as $dataItem) { $dataValue = SMWDataValueFactory::newDataItemValue($dataItem, $propertyDi); if ($dataValue->isValid()) { $valuesHtml[] = $dataValue->getLongWikiText(true) . $dataValue->getInfolinkText(SMW_OUTPUT_WIKI); } } $text .= $GLOBALS['wgLang']->listToText($valuesHtml); $text .= '</td></tr>'; } $text .= '</table></div>'; } wfProfileOut('SMWFactbox::printFactbox (SMW)'); return $text; }
/** * Remove data about a subobject. * If the removed data is not about a subobject of this object, * it will silently be ignored (nothing to remove). Likewise, * removing data that is not present does not change anything. * * @since 1.8 * @param SMWSemanticData */ public function removeSubSemanticData(SMWSemanticData $semanticData) { if ($semanticData->getSubject()->getDBkey() !== $this->getSubject()->getDBkey()) { return; } $subobjectName = $container->getSubject()->getSubobjectName(); if (array_key_exists($subobjectName, $this->subSemanticData)) { $this->subSemanticData[$subobjectName]->removeDataFrom($semanticData); if ($this->subSemanticData[$subobjectName]->isEmpty()) { unset($this->subSemanticData[$subobjectName]); } } }
/** * @see SMWStore::doDataUpdate * * @param SMWSemanticData $data */ public function doDataUpdate(SMWSemanticData $data) { wfProfileIn("SMWSQLStore2::updateData (SMW)"); wfRunHooks('SMWSQLStore2::updateDataBefore', array($this, $data)); $subject = $data->getSubject(); $this->deleteSemanticData($subject); $redirects = $data->getPropertyValues(new SMWDIProperty('_REDI')); if (count($redirects) > 0) { $redirect = end($redirects); // at most one redirect per page $this->updateRedirects($subject->getDBkey(), $subject->getNamespace(), $redirect->getDBkey(), $redirect->getNameSpace()); wfProfileOut("SMWSQLStore2::updateData (SMW)"); return; // Stop here -- no support for annotations on redirect pages! } else { $this->updateRedirects($subject->getDBkey(), $subject->getNamespace()); } $sortkeyDataItems = $data->getPropertyValues(new SMWDIProperty('_SKEY')); $sortkeyDataItem = end($sortkeyDataItems); if ($sortkeyDataItem instanceof SMWDIString) { $sortkey = $sortkeyDataItem->getString(); } else { // default sortkey $sortkey = str_replace('_', ' ', $subject->getDBkey()); } // Always make an ID (pages without ID cannot be in query results, not even in fixed value queries!): $sid = $this->makeSMWPageID($subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), $subject->getSubobjectName(), true, $sortkey); $updates = array(); // collect data for bulk updates; format: tableid => updatearray $this->prepareDBUpdates($updates, $data, $sid, $subject); $db = wfGetDB(DB_MASTER); foreach ($updates as $tablename => $uvals) { if ($tablename != 'smw_conc2') { $db->insert($tablename, $uvals, "SMW::updateData{$tablename}"); } } // Concepts are not just written but carefully updated, // preserving existing metadata (cache ...) for a concept: if ($subject->getNamespace() == SMW_NS_CONCEPT) { if (array_key_exists('smw_conc2', $updates) && count($updates['smw_conc2']) != 0) { $up_conc2 = end($updates['smw_conc2']); unset($up_conc2['cache_date']); unset($up_conc2['cache_count']); } else { $up_conc2 = array('concept_txt' => '', 'concept_docu' => '', 'concept_features' => 0, 'concept_size' => -1, 'concept_depth' => -1); } $row = $db->selectRow('smw_conc2', array('cache_date', 'cache_count'), array('s_id' => $sid), 'SMWSQLStore2Queries::updateConst2Data'); if ($row === false && $up_conc2['concept_txt'] !== '') { // insert newly given data $up_conc2['s_id'] = $sid; $db->insert('smw_conc2', $up_conc2, 'SMW::updateConc2Data'); } elseif ($row !== false) { // update data, preserve existing entries $db->update('smw_conc2', $up_conc2, array('s_id' => $sid), 'SMW::updateConc2Data'); } } // Finally update caches (may be important if jobs are directly following this call) $this->m_semdata[$sid] = SMWSqlStubSemanticData::newFromSemanticData($data); // Everything that one can know. $this->m_sdstate[$sid] = array(); foreach (self::getPropertyTables() as $tableId => $tableDeclaration) { $this->m_sdstate[$sid][$tableId] = true; } wfRunHooks('SMWSQLStore2::updateDataAfter', array($this, $data)); wfProfileOut("SMWSQLStore2::updateData (SMW)"); }
/** * Create an array of rows to insert into property tables in order to * store the given SMWSemanticData. The given $sid (subject page id) is * used directly and must belong to the subject of the data container. * Sortkeys are ignored since they are not stored in a property table * but in the ID table. * * The returned array uses property table names as keys and arrays of * table rows as values. Each table row is an array mapping column * names to values. * * @note Property tables that do not use ids as subjects are ignored. * This just excludes redirects that are handled differently anyway; * it would not make a difference to include them here. * * @since 1.8 * @param integer $sid * @param SMWSemanticData $data * @param DatabaseBase $dbr used for reading only * @return array */ protected function preparePropertyTableInserts($sid, SMWSemanticData $data, DatabaseBase $dbr) { $updates = array(); $subject = $data->getSubject(); $propertyTables = SMWSQLStore3::getPropertyTables(); foreach ($data->getProperties() as $property) { $tableId = SMWSQLStore3::findPropertyTableID($property); if (is_null($tableId)) { // not stored in a property table, e.g., sortkeys continue; } $propertyTable = $propertyTables[$tableId]; if (!$propertyTable->usesIdSubject()) { // not using subject ids, e.g., redirects continue; } $insertValues = array('s_id' => $sid); if (!$propertyTable->isFixedPropertyTable()) { $insertValues['p_id'] = $this->store->smwIds->makeSMWPropertyID($property); } foreach ($data->getPropertyValues($property) as $di) { if ($di instanceof SMWDIError) { // ignore error values continue; } if (!array_key_exists($propertyTable->getName(), $updates)) { $updates[$propertyTable->getName()] = array(); } $diHandler = $this->store->getDataItemHandlerForDIType($di->getDIType()); // Note that array_merge creates a new array; not overwriting past entries here $insertValues = array_merge($insertValues, $diHandler->getInsertValues($di)); $insertValueKey = self::makeDatabaseRowKey($insertValues); $updates[$propertyTable->getName()][$insertValueKey] = $insertValues; } } // Special handling of Concepts if ($subject->getNamespace() == SMW_NS_CONCEPT && $subject->getSubobjectName() == '') { $this->prepareConceptTableInserts($sid, $updates, $dbr); } return $updates; }
private function process( SMWSemanticData $answer ) { $this->getResult()->addValue( array( 'smwwriteable' ), 'title', $answer->getSubject()->getWikiValue() ); $properties = $answer->getProperties(); foreach ( $properties as $property ) { $values = $answer->getPropertyValues( $property ); $valuestrings = array(); foreach ( $values as $value ) $valuestrings[] = $value->getWikiValue(); $this->getResult()->setIndexedTagName( $valuestrings, 'value' ); $this->getResult()->addValue( array( 'smwwriteable', 'properties' ), $property->getWikiValue(), $valuestrings ); } }
function smwAutoRefresh( SMWStore $store, SMWSemanticData $data ) { $data->getSubject()->getTitle()->invalidateCache(); return true; }
/** * @since 2.5 * * @param string $sortKey */ public function addCompositeSortKey($sortKey) { $this->m_semanticData->addPropertyObjectValue(new DIProperty('_SKEY'), new DIBlob($this->m_semanticData->getSubject()->getSortKey() . '#' . $sortKey)); }
/** * This function is used for storing a SMWSemanticData Item in the Solr * Index * * @param SMWSemanticData $data */ public function parseSemanticData(SMWSemanticData $data) { $solritem = new SolrDoc(); $solritem->addField('pagetitle', $data->getSubject()->getTitle()->getText()); $solritem->addField('namespace', $data->getSubject()->getNamespace()); $solritem->addField('dbkey', $data->getSubject()->getDBkey()); $solritem->addField('interwiki', $data->getSubject()->getInterwiki()); $solritem->addField('subobjectname', $data->getSubject()->getSubobjectName()); foreach ($data->getProperties() as $property) { if ($property->getKey() == '_SKEY' || $property->getKey() == '_REDI') { continue; // skip these here, we store them differently } $propertyName = $property->getLabel(); foreach ($data->getPropertyValues($property) as $di) { if ($di instanceof SMWDIError) { // error values, ignore continue; } switch ($di->getDIType()) { case 0: // /// Data item ID that can be used to indicate that no data item class is appropriate // const TYPE_NOTYPE = 0; break; case 1: // /// Data item ID for SMWDINumber // const TYPE_NUMBER = 1; $solritem->addField($propertyName . '_i', $di->getNumber()); $solritem->addSortField($propertyName . '_i', $di->getNumber()); break; case 2: // /// Data item ID for SMWDIString // const TYPE_STRING = 2; $solritem->addField($propertyName . '_t', $di->getString()); $solritem->addSortField($propertyName . '_t', $di->getString()); break; case 3: // /// Data item ID for SMWDIBlob // const TYPE_BLOB = 3; $solritem->addField($propertyName . '_t', $di->getString()); $solritem->addSortField($propertyName . '_t', $di->getString()); break; case 4: // /// Data item ID for SMWDIBoolean // const TYPE_BOOLEAN = 4; $solritem->addField($propertyName . '_b', $di->getBoolean()); $solritem->addSortField($propertyName . '_b', $di->getBoolean()); break; case 5: // /// Data item ID for SMWDIUri // const TYPE_URI = 5; $solritem->addField($propertyName . '_t', $di->getURI()); $solritem->addSortField($propertyName . '_t', $di->getURI()); break; case 6: // /// Data item ID for SMWDITimePoint // const TYPE_TIME = 6; $date = $di->getYear() . '-' . $di->getMonth() . '-' . $di->getDay() . 'T' . $di->getHour() . ':' . $di->getMinute() . ':' . $di->getSecond() . 'Z'; $solritem->addField($propertyName . '_dt', $date); $solritem->addSortField($propertyName . '_dt', $date); break; case 7: // /// Data item ID for SMWDIGeoCoord // const TYPE_GEO = 7; // TODO: Implement range Search in SOLR $solritem->addField($propertyName . '_lat', $di->getLatitude()); $solritem->addField($propertyName . '_lng', $di->getLongitude()); break; case 8: // /// Data item ID for SMWDIContainer // const TYPE_CONTAINER = 8 // TODO: What the hell is this used for? $data->getSubject()->getTitle()->getText() . ' : '; break; case 9: // /// Data item ID for SMWDIWikiPage // const TYPE_WIKIPAGE = 9; $ns = $di->getNamespace(); if ($ns == 0) { $solritem->addField($propertyName . '_s', $di->getTitle()); } elseif ($ns == 14) { $title = $di->getTitle(); $solritem->addField('category', substr($title, stripos($title, ':') + 1)); } break; case 10: // /// Data item ID for SMWDIConcept // const TYPE_CONCEPT = 10; $data->getSubject()->getTitle()->getText() . ' : '; break; case 11: // /// Data item ID for SMWDIProperty // const TYPE_PROPERTY = 11; $data->getSubject()->getTitle()->getText() . ' : '; break; case 12: // /// Data item ID for SMWDIError // const TYPE_ERROR = 12; $data->getSubject()->getTitle()->getText() . ' : '; break; default: break; } } } $this->addDoc($solritem); }
/** * Create exportable data from a given semantic data record. * * @param $semdata SMWSemanticData * @return SMWExpData */ public static 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() !== '') { $semdata->addPropertyObjectValue(new DIProperty('_SKEY'), new SMWDIBlob($subject->getSortKey())); } $result = self::makeExportDataForSubject($subject); foreach ($semdata->getProperties() as $property) { self::addPropertyValues($property, $semdata->getPropertyValues($property), $result, $subject); } return $result; }
/** * Replaces the data in remove with the data in add. * * @param SMWSemanticData $remove Facts to be removed. If $remove is on a * null title, no data will be removed. If it is a SMWSemanticData object * with only the subject set but no properties given, all data of the * subject will be removed and replaced with the newly added facts. If the * object has properties, but the properties have a null value, all * properties with a null value will loose all their values before the data * in add will be added. * @param SMWSemanticData $add Facts to be added. * @param String $editsummary Text to be saved for edit summary * @param Integer bitfield $flags 0 means no special flags, * @return String if empty, the system thinks that everything went well. * Otherwise a String explaining what the system thinks just happened. * If a non-empty string is returned, the state of the wiki was not changed. */ public function update( SMWSemanticData $remove, SMWSemanticData $add, $editsummary, $flags = 0 ) { if ( !empty( $this->error ) ) return; if ( empty( $editsummary ) ) $editsummary = "Changed by calling SMWWriter::Update"; // TODO make this more intelligent $this->editsummary = $editsummary; $this->flags = $flags; if ( !$this->checkSubject( $remove->getSubject()->getTitle(), $add->getSubject()->getTitle() ) ) { $this->addError( "Not fitting subjects in add or remove." ); return; } $this->normalizeRequest( $remove, $add ); if ( empty( $this->error ) ) $this->doUpdate(); }
/** * This function takes care of storing the collected semantic data and takes * care of clearing out any outdated entries for the processed page. It assume that * parsing has happened and that all relevant data is contained in the provided parser * output. * * Optionally, this function also takes care of triggering indirect updates that might be * needed for overall database consistency. If the saved page describes a property or data type, * the method checks whether the property type, the data type, the allowed values, or the * conversion factors have changed. If so, it triggers SMWUpdateJobs for the relevant articles, * which then asynchronously update the semantic data in the database. * * @param $parseroutput ParserOutput object that contains the results of parsing which will * be stored. * @param $title Title object specifying the page that should be saved. * @param $makejobs Bool stating whether jobs should be created to trigger further updates if * this appears to be necessary after this update. * * @todo FIXME: Some job generations here might create too many jobs at once on a large wiki. Use incremental jobs instead. */ public static function storeData($parseroutput, Title $title, $makejobs = true) { global $smwgEnableUpdateJobs, $smwgDeclarationProperties, $smwgPageSpecialProperties; $semdata = $parseroutput->mSMWData; $namespace = $title->getNamespace(); $processSemantics = smwfIsSemanticsProcessed($namespace); if (!isset($semdata)) { // no data at all? $semdata = new SMWSemanticData(SMWDIWikiPage::newFromTitle($title)); } if ($processSemantics) { $props = array(); foreach ($smwgPageSpecialProperties as $propId) { // Do not calculate the same property again. if (array_key_exists($propId, $props)) { continue; } // Remember the property is processed. $props[$propId] = true; $prop = new SMWDIProperty($propId); if (count($semdata->getPropertyValues($prop)) > 0) { continue; } // Calculate property value. $value = null; switch ($propId) { case '_MDAT': $timestamp = Revision::getTimeStampFromID($title, $title->getLatestRevID()); $value = self::getDataItemFromMWTimestamp($timestamp); break; case '_CDAT': $timestamp = $title->getFirstRevision()->getTimestamp(); $value = self::getDataItemFromMWTimestamp($timestamp); break; case '_NEWP': $value = new SMWDIBoolean($title->isNewPage()); break; case '_LEDT': $revision = Revision::newFromId($title->getLatestRevID()); $user = User::newFromId($revision->getUser()); $value = SMWDIWikiPage::newFromTitle($user->getUserPage()); break; } if (!is_null($value)) { $semdata->addPropertyObjectValue($prop, $value); } // Issue error or warning? } // foreach } else { // data found, but do all operations as if it was empty $semdata = new SMWSemanticData($semdata->getSubject()); } // Check if the semantic data has been changed. // Sets the updateflag to true if so. // Careful: storage access must happen *before* the storage update; // even finding uses of a property fails after its type was changed. $updatejobflag = false; $jobs = array(); if ($makejobs && $smwgEnableUpdateJobs && $namespace == SMW_NS_PROPERTY) { // If it is a property, then we need to check if the type or the allowed values have been changed. $ptype = new SMWDIProperty('_TYPE'); $oldtype = smwfGetStore()->getPropertyValues($semdata->getSubject(), $ptype); $newtype = $semdata->getPropertyValues($ptype); if (!self::equalDatavalues($oldtype, $newtype)) { $updatejobflag = true; } else { foreach ($smwgDeclarationProperties as $prop) { $pv = new SMWDIProperty($prop); $oldvalues = smwfGetStore()->getPropertyValues($semdata->getSubject(), $pv); $newvalues = $semdata->getPropertyValues($pv); $updatejobflag = !self::equalDatavalues($oldvalues, $newvalues); } } if ($updatejobflag) { $prop = new SMWDIProperty($title->getDBkey()); $subjects = smwfGetStore()->getAllPropertySubjects($prop); foreach ($subjects as $subject) { $subjectTitle = $subject->getTitle(); if (!is_null($subjectTitle)) { // wikia change start - jobqueue migration $task = new \Wikia\Tasks\Tasks\JobWrapperTask(); $task->call('SMWUpdateJob', $subjectTitle); $jobs[] = $task; // wikia change end } } wfRunHooks('smwUpdatePropertySubjects', array(&$jobs)); $subjects = smwfGetStore()->getPropertySubjects(new SMWDIProperty('_ERRP'), $semdata->getSubject()); foreach ($subjects as $subject) { $subjectTitle = $subject->getTitle(); if (!is_null($subjectTitle)) { // wikia change start - jobqueue migration $task = new \Wikia\Tasks\Tasks\JobWrapperTask(); $task->call('SMWUpdateJob', $subjectTitle); $jobs[] = $task; // wikia change end } } } } elseif ($makejobs && $smwgEnableUpdateJobs && $namespace == SMW_NS_TYPE) { // if it is a type we need to check if the conversion factors have been changed $pconv = new SMWDIProperty('_CONV'); $ptype = new SMWDIProperty('_TYPE'); $oldfactors = smwfGetStore()->getPropertyValues($semdata->getSubject(), $pconv); $newfactors = $semdata->getPropertyValues($pconv); $updatejobflag = !self::equalDatavalues($oldfactors, $newfactors); if ($updatejobflag) { $store = smwfGetStore(); /// FIXME: this will kill large wikis! Use incremental updates! $dv = SMWDataValueFactory::newTypeIdValue('__typ', $title->getDBkey()); $proppages = $store->getPropertySubjects($ptype, $dv); foreach ($proppages as $proppage) { $propertyTitle = $proppage->getTitle(); if (!is_null($propertyTitle)) { // wikia change start - jobqueue migration $task = new \Wikia\Tasks\Tasks\JobWrapperTask(); $task->call('SMWUpdateJob', $propertyTitle); $jobs[] = $task; // wikia change end } $prop = new SMWDIProperty($proppage->getDBkey()); $subjects = $store->getAllPropertySubjects($prop); foreach ($subjects as $subject) { $subjectTitle = $subject->getTitle(); if (!is_null($subjectTitle)) { // wikia change start - jobqueue migration $task = new \Wikia\Tasks\Tasks\JobWrapperTask(); $task->call('SMWUpdateJob', $subjectTitle); $jobs[] = $task; // wikia change end } } $subjects = smwfGetStore()->getPropertySubjects(new SMWDIProperty('_ERRP'), $prop->getWikiPageValue()); foreach ($subjects as $subject) { $subjectTitle = $subject->getTitle(); if (!is_null($subjectTitle)) { // wikia change start - jobqueue migration $task = new \Wikia\Tasks\Tasks\JobWrapperTask(); $task->call('SMWUpdateJob', $subjectTitle); $jobs[] = $task; // wikia change end } } } } } // Actually store semantic data, or at least clear it if needed if ($processSemantics) { smwfGetStore()->updateData($semdata); } else { smwfGetStore()->clearData($semdata->getSubject()); } // Finally trigger relevant Updatejobs if necessary if ($updatejobflag) { // wikia change start - jobqueue migration \Wikia\Tasks\Tasks\BaseTask::batch($jobs); // wikia change end } return true; }
/** * Change the object to become an exact copy of the given * SMWSemanticData object. This is used to make other types of * SMWSemanticData into an SMWContainerSemanticData. To copy objects of * the same type, PHP clone() should be used. * * @since 1.7 * * @param $semanticData SMWSemanticData object to copy from */ public function copyDataFrom(SMWSemanticData $semanticData) { $this->mSubject = $semanticData->getSubject(); $this->mProperties = $semanticData->getProperties(); $this->mPropVals = array(); foreach ($this->mProperties as $property) { $this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues($property); } $this->mHasVisibleProps = $semanticData->hasVisibleProperties(); $this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties(); $this->mNoDuplicates = $semanticData->mNoDuplicates; }
/** * @see SMWStore::doDataUpdate * * @param SMWSemanticData $data */ public function doDataUpdate(SMWSemanticData $data) { wfProfileIn("SMWSQLStore3::updateData (SMW)"); wfRunHooks('SMWSQLStore3::updateDataBefore', array($this->store, $data)); $subject = $data->getSubject(); $redirects = $data->getPropertyValues(new SMWDIProperty('_REDI')); if (count($redirects) > 0) { $redirect = end($redirects); // at most one redirect per page $this->updateRedirects($subject->getDBkey(), $subject->getNamespace(), $redirect->getDBkey(), $redirect->getNameSpace()); wfProfileOut("SMWSQLStore3::updateData (SMW)"); return; // Stop here -- no support for annotations on redirect pages! } else { $this->updateRedirects($subject->getDBkey(), $subject->getNamespace()); } $sortkeyDataItems = $data->getPropertyValues(new SMWDIProperty('_SKEY')); $sortkeyDataItem = end($sortkeyDataItems); if ($sortkeyDataItem instanceof SMWDIString) { $sortkey = $sortkeyDataItem->getString(); } else { // default sortkey $sortkey = str_replace('_', ' ', $subject->getDBkey()); } // Always make an ID (pages without ID cannot be in query results, not even in fixed value queries!): $sid = $this->store->smwIds->makeSMWPageID($subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), $subject->getSubobjectName(), true, $sortkey, true); if ($subject->getSubobjectName() == '') { $this->updateSubSemanticData($data); } $updates = array(); // collect data for bulk updates; format: tableid => updatearray $this->prepareDBUpdates($updates, $data, $sid, $subject); $db = wfGetDB(DB_MASTER); $oldHashes = $this->store->smwIds->getPropertyTableHashes($sid); $hashIsChanged = false; //old SemanticData container for this subject (This will only hold Semantic data that will be deleted) $oldData = new SMWSql3StubSemanticData($subject, $this->store, false); //new SemanticData container for this subject (This will only hold Semantic data that will be newly added) $newData = new SMWSql3StubSemanticData($subject, $this->store, false); //tables into which data has been changed or added (not considering the ones where data is only deleted) $modifiedTables = array(); foreach (SMWSQLStore3::getPropertyTables() as $tableId => $tableDeclaration) { $tableName = $tableDeclaration->name; if ($tableName == 'smw_fpt_redi') { // TODO - handle these for updating property counts continue; //smw_fpt_redi are not considered here. } if (array_key_exists($tableName, $updates)) { $newHash = md5(serialize($updates[$tableName])); if (array_key_exists($tableName, $oldHashes) && $newHash == $oldHashes[$tableName]) { //table was used before and value didn't change, nothing to do here continue; } else { //data didn't exist before or has changed $this->store->getReader()->addTableSemanticData($sid, $oldData, $tableDeclaration); // Add data for this table // Concepts are not just written but carefully updated, // preserving existing metadata (cache ...) for a concept: if ($tableName == 'smw_fpt_conc') { $row = $db->selectRow('smw_fpt_conc', array('cache_date', 'cache_count'), array('s_id' => $sid), 'SMWSQLStoreQueries::updateConcData'); if ($row === false && $updates['smw_fpt_conc']['concept_txt'] !== '') { // insert newly given data $db->insert('smw_fpt_conc', $updates['smw_fpt_conc'], 'SMW::updateConcData'); } elseif ($row !== false) { // update data, preserve existing entries $db->update('smw_fpt_conc', $updates['smw_fpt_conc'], array('s_id' => $sid), 'SMW::updateConcData'); } } else { $this->deleteTableSemanticData($sid, $tableDeclaration); $db->insert($tableName, $updates[$tableName], "SMW::updateData{$tableName}"); } $oldHashes[$tableName] = $newHash; $hashIsChanged = true; $modifiedTables[$tableId] = $tableDeclaration; } } elseif (array_key_exists($tableName, $oldHashes)) { //data existed before but not now (Concepts data is not deleted here) $this->store->getReader()->addTableSemanticData($sid, $oldData, $tableDeclaration); // Add data for this table $this->deleteTableSemanticData($sid, $tableDeclaration); unset($oldHashes[$tableName]); $hashIsChanged = true; } } if ($hashIsChanged) { $this->store->smwIds->setPropertyTableHashes($sid, $oldHashes); } // Finally update caches (may be important if jobs are directly following this call) $this->store->m_semdata[$sid] = SMWSql3StubSemanticData::newFromSemanticData($data, $this->store); // Everything that one can know. $this->store->m_sdstate[$sid] = array(); foreach (SMWSQLStore3::getPropertyTables() as $tableId => $tableDeclaration) { $this->store->m_sdstate[$sid][$tableId] = true; } //Add newly added property-values to $newData //and remove property-values that hasn't been modified from $oldData foreach ($data->getProperties() as $propKey => $diProp) { $propTable = $this->store->findPropertyTableID($diProp); if (!array_key_exists($propTable, $modifiedTables)) { continue; //Properties in these table have been taken care of already } $dataPropVals = $data->getPropertyValues($diProp); //remove common property-values from $oldData and add new ones to $newData $oldPropVals = $oldData->getPropertyValues($diProp); foreach ($dataPropVals as $di) { if (in_array($di, $oldPropVals)) { $oldData->removePropertyObjectValue($diProp, $di); } else { $newData->addPropertyObjectValue($diProp, $di); } } } $this->doDiffandUpdateCount($oldData, $newData); wfRunHooks('SMWSQLStore3::updateDataAfter', array($this->store, $data)); wfProfileOut("SMWSQLStore3::updateData (SMW)"); }
/** * Create exportable data from a given semantic data record. * * @param $semdata SMWSemanticData * @return SMWExpData */ public static function makeExportData(SMWSemanticData $semdata) { self::initBaseURIs(); $subject = $semdata->getSubject(); // #649 Alwways make sure to have a least one valid sortkey if (!$semdata->getPropertyValues(new DIProperty('_SKEY')) && $subject->getSortKey() !== '') { $semdata->addPropertyObjectValue(new DIProperty('_SKEY'), new SMWDIBlob($subject->getSortKey())); } $result = self::makeExportDataForSubject($subject); foreach ($semdata->getProperties() as $property) { self::addPropertyValues($property, $semdata->getPropertyValues($property), $result, $subject); } return $result; }
/** * 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(); if ($this->store->canUseUpdateFeature(SMW_TRX_UPDATE)) { $this->store->getConnection()->beginTransaction(__METHOD__); } // Take care of redirects $redirects = $data->getPropertyValues(new SMWDIProperty('_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 $this->store->getConnection()->commitTransaction(__METHOD__); return; } else { $this->updateRedirects($subject->getDBkey(), $subject->getNamespace()); } // Take care of the sortkey $sortkeyDataItems = $data->getPropertyValues(new SMWDIProperty('_SKEY')); $sortkeyDataItem = end($sortkeyDataItems); if ($sortkeyDataItem instanceof SMWDIBlob) { $sortkey = $sortkeyDataItem->getString(); } else { // default sortkey $sortkey = $subject->getSortKey(); } // #649 Be consistent about how sortkeys are stored therefore always // normalize even for usages like {{DEFAULTSORT: Foo_bar }} $sortkey = str_replace('_', ' ', $sortkey); // Always make an ID; this also writes sortkey and namespace data $sid = $this->store->getObjectIds()->makeSMWPageID($subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), $subject->getSubobjectName(), true, $sortkey, true); // Take care of all remaining property table data list($deleteRows, $insertRows, $newHashes) = $this->propertyTableRowDiffer->computeTableRowDiffFor($sid, $data); $this->writePropertyTableUpdates($sid, $deleteRows, $insertRows, $newHashes); if ($redirects === array() && $subject->getSubobjectName() === '') { $dataItemFromId = $this->store->getObjectIds()->getDataItemForId($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->setSemanticDataCache($sid, $data); $this->store->getConnection()->commitTransaction(__METHOD__); // TODO Make overall diff SMWSemanticData containers and return them. // This can only be done here, since the $deleteRows/$insertRows // alone do not have enough information to compute this later (sortkey // and redirects may also change). }
/** * Get derived properties. * @param SMWSemanticData $semData * Annotated facts of an article * @return SMWSemanticData * Derived facts of the article */ public static function getDerivedProperties(SMWSemanticData $semData) { global $smwgIP, $smwgHaloIP, $smwgTripleStoreGraph; require_once $smwgIP . '/includes/SMW_QueryProcessor.php'; require_once $smwgHaloIP . '/includes/storage/SMW_TripleStore.php'; $derivedProperties = new SMWSemanticData($semData->getSubject()); $subject = $semData->getSubject()->getDBkey(); global $wgContLang; $subject = $semData->getSubject(); $ns = strtolower($wgContLang->getNSText($subject->getNamespace())); if (empty($ns)) { $ns = 'a'; } $localName = $subject->getDBkey(); $inst = $smwgTripleStoreGraph . TSNamespaces::$INST_NS_SUFFIX; // $queryText = "PREFIX a:<$inst> SELECT ?pred ?obj WHERE { a:$subject ?pred ?obj . }"; // $queryText = "SELECT ?pred ?obj WHERE { a:$subject ?pred ?obj . }"; $queryText = "SELECT ?pred ?obj WHERE { <" . $smwgTripleStoreGraph . "/{$ns}#{$localName}> ?pred ?obj . }"; // echo $queryText; wfRunHooks('BeforeDerivedPropertyQuery', array(&$queryText)); // Ask for all properties of the subject (derived and ground facts) $q = SMWSPARQLQueryProcessor::createQuery($queryText, array()); $res = smwfGetStore()->getQueryResult($q); // SMWQueryResult wfRunHooks('AfterDerivedPropertyQuery', array()); wfRunHooks('FilterQueryResults', array(&$res, array('pred'))); $propVal = array(); while ($row = $res->getNext()) { //$row: SMWResultArray[] $i = 0; $valuesForProperty = array(); $key = false; if (count($row) == 2) { $properties = array(); $values = array(); // There may be several properties with the same values $p = $row[0]; while (($object = $p->getNextObject()) !== false) { if ($object instanceof SMWURIValue) { $keys = $object->getDBkeys(); $properties[] = $keys[0]; } else { $properties[] = $object->getDBkey(); } } // Retrieve the values of the properties $v = $row[1]; while (($object = $v->getNextObject()) !== false) { $values[] = $object; } } foreach ($properties as $p) { if (array_key_exists($p, $propVal)) { // The same property may appear several times $propVal[$p] = array_merge($values, $propVal[$p]); } else { $propVal[$p] = $values; } } } // Check is a property is derived or directly annotated foreach ($propVal as $propName => $derivedValues) { // does the property already exist? $prop = SMWPropertyValue::makeUserProperty($propName); $values = $semData->getPropertyValues($prop); foreach ($derivedValues as $dv) { $isDerived = true; $val = null; foreach ($values as $v) { if ($dv->getTypeID() == '_wpg' && $v->getTypeID() == '_wpg') { $vt1 = $dv->getTitle(); $vt2 = $v->getTitle(); if (isset($vt1) && isset($vt2) && $vt1->getText() == $vt2->getText()) { $isDerived = false; break; } } else { if ($dv->getTypeID() == '_wpg' && $v->getTypeID() != '_wpg') { // how can this happen? $isDerived = false; break; } else { if ($dv->isNumeric()) { if ($dv->getWikiValue() == $v->getWikiValue()) { $isDerived = false; break; } } else { if (array_shift($dv->getDBkeys()) == array_shift($v->getDBkeys())) { $isDerived = false; break; } } } } } if ($isDerived) { $property = SMWPropertyValue::makeUserProperty($propName); $derivedProperties->addPropertyObjectValue($property, $dv); } } } return $derivedProperties; }
public function doDataUpdate(SMWSemanticData $data) { wfProfileIn("SMWSQLStoreLight::updateData (SMW)"); wfRunHooks('SMWSQLStoreLight::updateDataBefore', array($this, $data)); $subject = $data->getSubject(); $this->deleteSemanticData($subject); $sid = $subject->getTitle()->getArticleID(); $updates = array(); // collect data for bulk updates; format: tableid => updatearray foreach ($data->getProperties() as $property) { $tablename = SMWSQLStoreLight::findPropertyTableName($property); if ($tablename === '') { continue; } foreach ($data->getPropertyValues($property) as $dv) { if (!$dv->isValid()) { continue; } if ($dv instanceof SMWContainerValue) { continue; // subobjects not supported in this store right now; maybe could simply be PHP serialized } else { $uvals = array('pageid' => $sid, 'propname' => $property->getDBkey(), 'value' => $tablename == 'smwsimple_special' ? reset($dv->getDBkeys()) : serialize($dv->getDBkeys())); } if (!array_key_exists($tablename, $updates)) { $updates[$tablename] = array(); } $updates[$tablename][] = $uvals; } } $db = wfGetDB(DB_MASTER); foreach ($updates as $tablename => $uvals) { $db->insert($tablename, $uvals, "SMW::updateData{$tablename}"); } // Finally update caches (may be important if jobs are directly following this call) $this->m_semdata[$sid] = clone $data; $this->m_sdstate[$sid] = array('smwsimple_data' => true, 'smwsimple_special' => true); // everything that one can know wfRunHooks('SMWSQLStoreLight::updateDataAfter', array($this, $data)); wfProfileOut("SMWSQLStoreLight::updateData (SMW)"); }
/** * Creates and returns a new SMWChangeSet from 2 SMWSemanticData objects. * * @param SMWSemanticData $old * @param SMWSemanticData $new * @param array $filterProperties Optional list of properties (string serializations) to filter on. Null for no filtering. * * @return SMWChangeSet */ public static function newFromSemanticData(SMWSemanticData $old, SMWSemanticData $new, array $filterProperties = null) { $subject = $old->getSubject(); if ($subject != $new->getSubject()) { return new self($subject); } $changes = new SWLPropertyChanges(); $insertions = new SMWSemanticData($subject); $deletions = new SMWSemanticData($subject); $oldProperties = array(); $newProperties = array(); foreach ($old->getProperties() as $property) { if (is_null($filterProperties) || in_array($property->getLabel(), $filterProperties)) { $oldProperties[] = $property; } } foreach ($new->getProperties() as $property) { if (is_null($filterProperties) || in_array($property->getLabel(), $filterProperties)) { $newProperties[] = $property; } } // Find the deletions. self::findSingleDirectionChanges($deletions, $oldProperties, $old, $newProperties, $filterProperties); // Find the insertions. self::findSingleDirectionChanges($insertions, $newProperties, $new, $oldProperties, $filterProperties); foreach ($oldProperties as $propertyKey => $diProperty) { $oldDataItems = array(); $newDataItems = array(); // Populate the data item arrays using keys that are their hash, so matches can be found. // Note: this code assumes there are no duplicates. foreach ($old->getPropertyValues($diProperty) as $dataItem) { $oldDataItems[$dataItem->getHash()] = $dataItem; } foreach ($new->getPropertyValues($diProperty) as $dataItem) { $newDataItems[$dataItem->getHash()] = $dataItem; } $foundMatches = array(); // Find values that are both in the old and new version. foreach (array_keys($oldDataItems) as $hash) { if (array_key_exists($hash, $newDataItems)) { $foundMatches[] = $hash; } } // Remove the values occuring in both sets, so only changes remain. foreach ($foundMatches as $foundMatch) { unset($oldDataItems[$foundMatch]); unset($newDataItems[$foundMatch]); } // Find which group is biggest, so it's easy to loop over all values of the smallest. $oldIsBigger = count($oldDataItems) > count($newDataItems); $bigGroup = $oldIsBigger ? $oldDataItems : $newDataItems; $smallGroup = $oldIsBigger ? $newDataItems : $oldDataItems; // Add all one-to-one changes. while ($dataItem = array_shift($smallGroup)) { $changes->addPropertyObjectChange($diProperty, new SWLPropertyChange($dataItem, array_shift($bigGroup))); } // If the bigger group is not-equal to the smaller one, items will be left, // that are either insertions or deletions, depending on the group. if (count($bigGroup > 0)) { $semanticData = $oldIsBigger ? $deletions : $insertions; foreach ($bigGroup as $dataItem) { $semanticData->addPropertyObjectValue($diProperty, $dataItem); } } } return new self($subject, $changes, $insertions, $deletions); }
/** * Callback function for the hook 'smwShowFactbox'. It is called when SMW creates * the factbox for an article. * This method replaces the whole factbox with a tabbed version that contains * the original factbox in one tab and the derived facts in another. * * @param string $text * The HTML for the tabbed factbox is returned in this parameter * @param SMWSemanticData $semdata * All static facts for the article * @return bool * <false> : This means that SMW's factbox is completely replaced. */ function smwfAddDerivedFacts(&$text, $semdata) { global $smwgHaloScriptPath, $wgContLang; wfLoadExtensionMessages('SemanticMediaWiki'); SMWOutputs::requireHeadItem(SMW_HEADER_STYLE); $rdflink = SMWInfolink::newInternalLink(wfMsgForContent('smw_viewasrdf'), $wgContLang->getNsText(NS_SPECIAL) . ':ExportRDF/' . $semdata->getSubject()->getWikiValue(), 'rdflink'); $browselink = SMWInfolink::newBrowsingLink($semdata->getSubject()->getText(), $semdata->getSubject()->getWikiValue(), 'swmfactboxheadbrowse'); $fbText = '<div class="smwfact">' . '<span class="smwfactboxhead">' . wfMsgForContent('smw_factbox_head', $browselink->getWikiText()) . '</span>' . '<span class="smwrdflink">' . $rdflink->getWikiText() . '</span>' . '<table class="smwfacttable">' . "\n"; foreach ($semdata->getProperties() as $property) { if (!$property->isShown()) { // showing this is not desired, hide continue; } elseif ($property->isUserDefined()) { // user defined property $property->setCaption(preg_replace('/[ ]/u', ' ', $property->getWikiValue(), 2)); /// NOTE: the preg_replace is a slight hack to ensure that the left column does not get too narrow $fbText .= '<tr><td class="smwpropname">' . $property->getLongWikiText(true) . '</td><td class="smwprops">'; } elseif ($property->isVisible()) { // predefined property $fbText .= '<tr><td class="smwspecname">' . $property->getLongWikiText(true) . '</td><td class="smwspecs">'; } else { // predefined, internal property continue; } $propvalues = $semdata->getPropertyValues($property); $l = count($propvalues); $i = 0; foreach ($propvalues as $propvalue) { if ($i != 0) { if ($i > $l - 2) { $fbText .= wfMsgForContent('smw_finallistconjunct') . ' '; } else { $fbText .= ', '; } } $i += 1; $fbText .= $propvalue->getLongWikiText(true) . $propvalue->getInfolinkText(SMW_OUTPUT_WIKI); } $fbText .= '</td></tr>'; } $fbText .= '</table></div>'; $text = '<div id="smw_dft_rendered_boxcontent"> <br />' . '<table>' . '<tr>' . '<td id="dftTab1" class="dftTabActive">' . str_replace(' ', ' ', wfMsg('smw_df_static_tab')) . '</td>' . '<td class="dftTabSpacer"> </td>' . '<td id="dftTab2" class="dftTabInactive">' . str_replace(' ', ' ', wfMsg('smw_df_derived_tab')) . '</td>' . '<td class="dftTabSpacer" width="100%"></td>' . '</tr>' . '<tr>' . '<td colspan="4" class="dftTabCont">' . '<div id="dftTab1Content" >' . $fbText . '</div>' . '<div id="dftTab2Content" style="display:none">' . '<div id="dftTab2ContentInnerDiv">' . wfMsg('smw_df_loading_df') . '</div>' . '</div>' . '</td>' . '</tr>' . '</table>' . '</div>'; return false; }