[ ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', ], ]; } public function execute() { $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'cite' ); if ( !$config->get( 'CiteStoreReferencesData' ) ) { $this->dieWithError( 'apierror-citestoragedisabled' ); } $params = $this->extractRequestParams(); $titles = $this->getPageSet()->getGoodTitles(); ksort( $titles ); if ( !is_null( $params['continue'] ) ) { $startId = (int)$params['continue']; // check it is definitely an int $this->dieContinueUsageIf( strval( $startId ) !== $params['continue'] ); } else { $startId = false; } foreach ( $titles as $pageId => $title ) { // Skip until you have the correct starting point if ( $startId !== false && $startId !== $pageId ) { continue; } else { $startId = false; } $storedRefs = $this->getStoredReferences( $title ); $allReferences = []; // some pages may not have references stored if ( $storedRefs !== false ) { // a page can have multiple tags but they all have unique keys foreach ( $storedRefs['refs'] as $index => $grouping ) { foreach ( $grouping as $group => $members ) { foreach ( $members as $name => $ref ) { $ref['name'] = $name; $key = $ref['key']; if ( is_string( $name ) ) { $id = Cite::getReferencesKey( $name . '-' . $key ); } else { $id = Cite::getReferencesKey( $key ); } $ref['group'] = $group; $ref['reflist'] = $index; $allReferences[$id] = $ref; } } } } // set some metadata since its an assoc data structure ApiResult::setArrayType( $allReferences, 'kvp', 'id' ); // Ship a data representation of the combined references. $fit = $this->addPageSubItems( $pageId, $allReferences ); if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $pageId ); break; } } } /** * Fetch references stored for the given title in page_props * For performance, results are cached * * @param Title $title * @return array|false */ private function getStoredReferences( Title $title ) { global $wgCiteStoreReferencesData; if ( !$wgCiteStoreReferencesData ) { return false; } $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); $key = $cache->makeKey( Cite::EXT_DATA_KEY, $title->getArticleID() ); return $cache->getWithSetCallback( $key, self::CACHE_DURATION_ONFETCH, function ( $oldValue, &$ttl, array &$setOpts ) use ( $title ) { $dbr = wfGetDB( DB_REPLICA ); $setOpts += Database::getCacheSetOptions( $dbr ); return $this->recursiveFetchRefsFromDB( $title, $dbr ); }, [ 'checkKeys' => [ $key ], 'lockTSE' => 30, ] ); } /** * Reconstructs compressed json by successively retrieving the properties references-1, -2, etc * It attempts the next step when a decoding error occurs. * Returns json_decoded uncompressed string, with validation of json * * @param Title $title * @param IDatabase $dbr * @param string $string * @param int $i * @return array|false */ private function recursiveFetchRefsFromDB( Title $title, IDatabase $dbr, $string = '', $i = 1 ) { $id = $title->getArticleID(); $result = $dbr->selectField( 'page_props', 'pp_value', [ 'pp_page' => $id, 'pp_propname' => 'references-' . $i ], __METHOD__ ); if ( $result === false ) { // no refs stored in page_props at this index if ( $i > 1 ) { // shouldn't happen wfDebug( "Failed to retrieve stored references for title id $id" ); } return false; } $string .= $result; $decodedString = gzdecode( $string ); if ( $decodedString !== false ) { $json = json_decode( $decodedString, true ); if ( json_last_error() === JSON_ERROR_NONE ) { return $json; } // corrupted json ? // shouldn't happen since when string is truncated, gzdecode should fail wfDebug( "Corrupted json detected when retrieving stored references for title id $id" ); } // if gzdecode fails, try to fetch next references- property value return $this->recursiveFetchRefsFromDB( $title, $dbr, $string, ++$i ); } /** * Get the cache mode for the data generated by this module. * * @param array $params * @return string */ public function getCacheMode( $params ) { return 'public'; } /** * @inheritDoc */ protected function getExamplesMessages() { return [ 'action=query&prop=references&titles=Albert%20Einstein' => 'apihelp-query+references-example-1', ]; } }