getPropertyValues( $dataItem, new DIProperty( '_SKEY' ) ); if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) { return end( $dataItems )->getString(); } return str_replace( '_', ' ', $dataItem->getDBkey() ); } /** * Convenience method to find the redirect target of a DIWikiPage * or DIProperty object. Returns a dataitem of the same type that * the input redirects to, or the input itself if there is no redirect. * * @param DataItem $dataItem * * @return DataItem */ public function getRedirectTarget( DataItem $dataItem ) { $type = $dataItem->getDIType(); if ( $type !== DataItem::TYPE_WIKIPAGE && $type !== DataItem::TYPE_PROPERTY ) { throw new InvalidArgumentException( 'Store::getRedirectTarget expects a DIProperty or DIWikiPage object.' ); } if ( $type === DataItem::TYPE_PROPERTY ) { if ( !$dataItem->isUserDefined() ) { return $dataItem; } $wikipage = $dataItem->getDiWikiPage(); } elseif ( $type === DataItem::TYPE_WIKIPAGE ) { $wikipage = $dataItem; } $dataItems = $this->getPropertyValues( $wikipage, new DIProperty( '_REDI' ) ); if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) { $redirectDataItem = end( $dataItems ); if ( $type == DataItem::TYPE_PROPERTY && $redirectDataItem instanceof DIWikiPage ) { $dataItem = DIProperty::newFromUserLabel( $redirectDataItem->getDBkey() ); } else { $dataItem = $redirectDataItem; } } return $dataItem; } ///// Writing methods ///// /** * Delete all semantic properties that the given subject has. This * includes relations, attributes, and special properties. This does * not delete the respective text from the wiki, but only clears the * stored data. * * @param Title $subject */ public abstract function deleteSubject( Title $subject ); /** * Update the semantic data stored for some individual. The data is * given as a SemanticData object, which contains all semantic data * for one particular subject. * * @param SemanticData $data */ protected abstract function doDataUpdate( SemanticData $data ); /** * Update the semantic data stored for some individual. The data is * given as a SemanticData object, which contains all semantic data * for one particular subject. * * @param SemanticData $semanticData */ public function updateData( SemanticData $semanticData ) { if ( !$this->getOption( 'smwgSemanticsEnabled' ) ) { return; } Timer::start( __METHOD__ ); $applicationFactory = ApplicationFactory::getInstance(); $subject = $semanticData->getSubject(); $hash = $subject->getHash(); /** * @since 1.6 */ \Hooks::run( 'SMWStore::updateDataBefore', [ $this, $semanticData ] ); $this->doDataUpdate( $semanticData ); /** * @since 1.6 */ \Hooks::run( 'SMWStore::updateDataAfter', [ $this, $semanticData ] ); $context = [ 'method' => __METHOD__, 'role' => 'production', 'origin' => $hash, 'procTime' => Timer::getElapsedTime( __METHOD__, 5 ), ]; $this->logger->info( '[Store] Update completed: {origin} (procTime in sec: {procTime})', $context ); if ( !$this->getOption( 'smwgAutoRefreshSubject' ) || $semanticData->getOption( Enum::OPT_SUSPEND_PURGE ) ) { return $this->logger->info( '[Store] Skipping html, parser cache purge', [ 'role' => 'user' ] ); } $pageUpdater = $applicationFactory->newPageUpdater(); $pageUpdater->addPage( $subject->getTitle() ); $pageUpdater->waitOnTransactionIdle(); $pageUpdater->markAsPending(); $pageUpdater->setOrigin( __METHOD__ ); $pageUpdater->doPurgeParserCache(); $pageUpdater->doPurgeHtmlCache(); $pageUpdater->pushUpdate(); } /** * Clear all semantic data specified for some page. * * @param DIWikiPage $di */ public function clearData( DIWikiPage $di ) { $this->updateData( new SMWSemanticData( $di ) ); } /** * Update the store to reflect a renaming of some article. Normally * this happens when moving pages in the wiki, and in this case there * is also a new redirect page generated at the old position. The title * objects given are only used to specify the name of the title before * and after the move -- do not use their IDs for anything! The ID of * the moved page is given in $pageid, and the ID of the newly created * redirect, if any, is given by $redirid. If no new page was created, * $redirid will be 0. */ public abstract function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ); ///// Query answering ///// /** * @note Change the signature in 3.* to avoid for subclasses to manage the * hooks; keep the current signature to adhere semver for the 2.* branch * * Execute the provided query and return the result as an * SMWQueryResult if the query was a usual instance retrieval query. In * the case that the query asked for a plain string (querymode * MODE_COUNT or MODE_DEBUG) a plain wiki and HTML-compatible string is * returned. * * @param SMWQuery $query * * @return SMWQueryResult */ public abstract function getQueryResult( SMWQuery $query ); /** * @note Change the signature to abstract for the 3.* branch * * @since 2.1 * * @param SMWQuery $query * * @return SMWQueryResult */ protected function fetchQueryResult( SMWQuery $query ) { } ///// Special page functions ///// /** * Return all properties that have been used on pages in the wiki. The * result is an array of arrays, each containing a property data item * and a count. The expected order is alphabetical w.r.t. to property * names. * * If there is an error on creating some property object, then a * suitable SMWDIError object might be returned in its place. Even if * there are errors, the function should always return the number of * results requested (otherwise callers might assume that there are no * further results to ask for). * * @param SMWRequestOptions $requestoptions * * @return array of array( DIProperty|SMWDIError, integer ) */ public abstract function getPropertiesSpecial( $requestoptions = null ); /** * Return all properties that have been declared in the wiki but that * are not used on any page. Stores might restrict here to those * properties that have been given a type if they have no efficient * means of accessing the set of all pages in the property namespace. * * If there is an error on creating some property object, then a * suitable SMWDIError object might be returned in its place. Even if * there are errors, the function should always return the number of * results requested (otherwise callers might assume that there are no * further results to ask for). * * @param SMWRequestOptions $requestoptions * * @return array of DIProperty|SMWDIError */ public abstract function getUnusedPropertiesSpecial( $requestoptions = null ); /** * Return all properties that are used on some page but that do not * have any page describing them. Stores that have no efficient way of * accessing the set of all existing pages can extend this list to all * properties that are used but do not have a type assigned to them. * * @param SMWRequestOptions $requestoptions * * @return array of array( DIProperty, int ) */ public abstract function getWantedPropertiesSpecial( $requestoptions = null ); /** * Return statistical information as an associative array with the * following keys: * - 'PROPUSES': Number of property instances (value assignments) in the datatbase * - 'USEDPROPS': Number of properties that are used with at least one value * - 'DECLPROPS': Number of properties that have been declared (i.e. assigned a type) * - 'OWNPAGE': Number of properties with their own page * - 'QUERY': Number of inline queries * - 'QUERYSIZE': Represents collective query size * - 'CONCEPTS': Number of declared concepts * - 'SUBOBJECTS': Number of declared subobjects * * @return array */ public abstract function getStatistics(); /** * Store administration */ /** * @private * * Returns store specific services. Services are registered with the store * implementation and may provide different services that are only available * for a particular store. * * @since 3.0 * * @param string $service * * @return mixed * @throws ServiceNotFoundException */ public function service( $service, ...$args ) { throw new ServiceNotFoundException( $service ); } /** * Setup all storage structures properly for using the store. This * function performs tasks like creation of database tables. It is * called upon installation as well as on upgrade: hence it must be * able to upgrade existing storage structures if needed. It should * return "true" if successful and return a meaningful string error * message otherwise. * * The parameter $verbose determines whether the procedure is allowed * to report on its progress. This is doen by just using print and * possibly ob_flush/flush. This is also relevant for preventing * timeouts during long operations. All output must be valid in an HTML * context, but should preferably be plain text, possibly with some * linebreaks and weak markup. * * @param boolean $verbose * * @return boolean Success indicator */ public abstract function setup( $verbose = true ); /** * Drop (delete) all storage structures created by setup(). This will * delete all semantic data and possibly leave the wiki uninitialised. * * @param boolean $verbose */ public abstract function drop( $verbose = true ); /** * Refresh some objects in the store, addressed by numerical ids. The * meaning of the ids is private to the store, and does not need to * reflect the use of IDs elsewhere (e.g. page ids). The store is to * refresh $count objects starting from the given $index. Typically, * updates are achieved by generating update jobs. After the operation, * $index is set to the next index that should be used for continuing * refreshing, or to -1 for signaling that no objects of higher index * require refresh. The method returns a decimal number between 0 and 1 * to indicate the overall progress of the refreshing (e.g. 0.7 if 70% * of all objects were refreshed). * * The optional parameter $namespaces may contain an array of namespace * constants. If given, only objects from those namespaces will be * refreshed. The default value FALSE disables this feature. * * The optional parameter $usejobs indicates whether updates should be * processed later using MediaWiki jobs, instead of doing all updates * immediately. The default is TRUE. * * @param $index integer * @param $count integer * @param $namespaces mixed array or false * @param $usejobs boolean * * @return float between 0 and 1 to indicate the overall progress of the refreshing */ public abstract function refreshData( &$index, $count, $namespaces = false, $usejobs = true ); /** * Setup the store. * * @since 1.8 * * @param bool $verbose * @param Options|null $options * * @return boolean Success indicator */ public static function setupStore( $verbose = true, $options = null ) { // See notes in ExtensionSchemaUpdates if ( is_bool( $verbose ) ) { $verbose = $verbose; } if ( isset( $options['verbose'] ) ) { $verbose = $options['verbose']; } if ( isset( $options['options'] ) ) { $options = $options['options']; } $store = StoreFactory::getStore(); if ( $options instanceof Options ) { foreach ( $options->getOptions() as $key => $value ) { $store->getOptions()->set( $key, $value ); } } return $store->setup( $verbose ); } /** * @since 2.5 * * @return Options */ public function getOptions() { if ( $this->options === null ) { $this->options = new Options(); } return $this->options; } /** * @since 3.0 * * @param string $key * @param mixed $value */ public function setOption( $key, $value ) { if ( $this->options === null ) { $this->options = new Options(); } return $this->options->set( $key, $value ); } /** * @since 3.0 * * @param string $key * @param mixed $default * * @return mixed */ public function getOption( $key, $default = null ) { if ( $this->options === null ) { $this->options = new Options(); } return $this->options->safeGet( $key, $default ); } /** * @since 2.0 */ public function clear() { if ( $this->connectionManager !== null ) { $this->connectionManager->releaseConnections(); } } /** * @since 3.0 * * @param string|null $type * * @return array */ public function getInfo( $type = null ) { return []; } /** * @since 2.1 * * @param ConnectionManager $connectionManager */ public function setConnectionManager( ConnectionManager $connectionManager ) { $this->connectionManager = $connectionManager; } /** * @since 2.1 * * @param string $type * * @return mixed */ public function getConnection( $type ) { if ( $this->connectionManager === null ) { $this->connectionManager = ApplicationFactory::getInstance()->getConnectionManager(); } return $this->connectionManager->getConnection( $type ); } }