2015-08-03 06:37:32 +00:00
|
|
|
<?php
|
|
|
|
|
2022-02-06 18:54:47 +00:00
|
|
|
namespace MediaWiki\Extension\Gadgets;
|
|
|
|
|
|
|
|
use InvalidArgumentException;
|
|
|
|
use MediaWiki\Extension\Gadgets\Content\GadgetDefinitionContent;
|
2017-11-07 19:36:44 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
2020-04-02 00:38:28 +00:00
|
|
|
use MediaWiki\Revision\RevisionLookup;
|
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2023-08-19 04:15:51 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2022-02-06 18:54:47 +00:00
|
|
|
use WANObjectCache;
|
2018-02-25 02:41:50 +00:00
|
|
|
use Wikimedia\Rdbms\Database;
|
2017-03-03 01:41:25 +00:00
|
|
|
|
2015-08-03 06:37:32 +00:00
|
|
|
/**
|
|
|
|
* GadgetRepo implementation where each gadget has a page in
|
|
|
|
* the Gadget definition namespace, and scripts and styles are
|
|
|
|
* located in the Gadget namespace.
|
|
|
|
*/
|
|
|
|
class GadgetDefinitionNamespaceRepo extends GadgetRepo {
|
|
|
|
/**
|
|
|
|
* How long in seconds the list of gadget ids and
|
|
|
|
* individual gadgets should be cached for (1 day)
|
|
|
|
*/
|
2020-05-20 00:10:09 +00:00
|
|
|
private const CACHE_TTL = 86400;
|
2015-08-03 06:37:32 +00:00
|
|
|
|
2021-10-17 13:05:15 +00:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $titlePrefix = 'Gadget:';
|
|
|
|
|
2015-08-03 06:37:32 +00:00
|
|
|
/**
|
|
|
|
* @var WANObjectCache
|
|
|
|
*/
|
|
|
|
private $wanCache;
|
|
|
|
|
2020-04-02 00:38:28 +00:00
|
|
|
/**
|
|
|
|
* @var RevisionLookup
|
|
|
|
*/
|
|
|
|
private $revLookup;
|
|
|
|
|
2022-08-16 15:24:01 +00:00
|
|
|
public function __construct( WANObjectCache $wanCache, RevisionLookup $revLookup ) {
|
|
|
|
$this->wanCache = $wanCache;
|
|
|
|
$this->revLookup = $revLookup;
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of gadget ids from cache/database
|
|
|
|
*
|
|
|
|
* @return string[]
|
|
|
|
*/
|
2022-04-05 17:47:47 +00:00
|
|
|
public function getGadgetIds(): array {
|
2017-03-03 01:41:25 +00:00
|
|
|
$key = $this->getGadgetIdsKey();
|
|
|
|
|
2018-09-30 11:38:44 +00:00
|
|
|
$fname = __METHOD__;
|
2015-08-03 06:37:32 +00:00
|
|
|
return $this->wanCache->getWithSetCallback(
|
2017-03-03 01:41:25 +00:00
|
|
|
$key,
|
2015-08-03 06:37:32 +00:00
|
|
|
self::CACHE_TTL,
|
2021-05-04 07:24:31 +00:00
|
|
|
static function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
|
2017-09-24 05:26:03 +00:00
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
2015-08-03 06:37:32 +00:00
|
|
|
$setOpts += Database::getCacheSetOptions( $dbr );
|
2017-03-03 01:41:25 +00:00
|
|
|
|
2022-08-27 11:47:27 +00:00
|
|
|
return $dbr->newSelectQueryBuilder()
|
|
|
|
->select( 'page_title' )
|
|
|
|
->from( 'page' )
|
|
|
|
->where( [ 'page_namespace' => NS_GADGET_DEFINITION ] )
|
|
|
|
->caller( $fname )
|
|
|
|
->fetchFieldValues();
|
2015-08-03 06:37:32 +00:00
|
|
|
},
|
2016-12-28 10:25:47 +00:00
|
|
|
[
|
2017-03-03 01:41:25 +00:00
|
|
|
'checkKeys' => [ $key ],
|
|
|
|
'pcTTL' => WANObjectCache::TTL_PROC_SHORT,
|
|
|
|
'lockTSE' => 30
|
2016-12-28 10:25:47 +00:00
|
|
|
]
|
2015-08-03 06:37:32 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-11-07 19:36:44 +00:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
2022-04-05 17:47:47 +00:00
|
|
|
public function handlePageUpdate( LinkTarget $target ): void {
|
2017-11-07 19:36:44 +00:00
|
|
|
if ( $target->inNamespace( NS_GADGET_DEFINITION ) ) {
|
|
|
|
$this->purgeGadgetIdsList();
|
|
|
|
$this->purgeGadgetEntry( $target->getText() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-03 06:37:32 +00:00
|
|
|
/**
|
2017-03-03 01:41:25 +00:00
|
|
|
* Purge the list of gadget ids when a page is deleted or if a new page is created
|
2015-08-03 06:37:32 +00:00
|
|
|
*/
|
2022-04-05 17:47:47 +00:00
|
|
|
public function purgeGadgetIdsList(): void {
|
2017-03-03 01:41:25 +00:00
|
|
|
$this->wanCache->touchCheckKey( $this->getGadgetIdsKey() );
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|
|
|
|
|
2021-12-26 21:23:06 +00:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function getGadgetDefinitionTitle( string $id ): ?Title {
|
|
|
|
return Title::makeTitleSafe( NS_GADGET_DEFINITION, $id );
|
|
|
|
}
|
|
|
|
|
2015-08-03 06:37:32 +00:00
|
|
|
/**
|
|
|
|
* @param string $id
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
* @return Gadget
|
|
|
|
*/
|
2022-04-05 17:47:47 +00:00
|
|
|
public function getGadget( string $id ): Gadget {
|
2015-08-03 06:37:32 +00:00
|
|
|
$key = $this->getGadgetCacheKey( $id );
|
|
|
|
$gadget = $this->wanCache->getWithSetCallback(
|
|
|
|
$key,
|
|
|
|
self::CACHE_TTL,
|
2017-03-03 01:41:25 +00:00
|
|
|
function ( $old, &$ttl, array &$setOpts ) use ( $id ) {
|
2017-09-24 05:26:03 +00:00
|
|
|
$setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
|
2015-08-03 06:37:32 +00:00
|
|
|
$title = Title::makeTitleSafe( NS_GADGET_DEFINITION, $id );
|
|
|
|
if ( !$title ) {
|
|
|
|
$ttl = WANObjectCache::TTL_UNCACHEABLE;
|
|
|
|
return null;
|
|
|
|
}
|
2017-03-03 01:41:25 +00:00
|
|
|
|
2020-04-02 00:38:28 +00:00
|
|
|
$revRecord = $this->revLookup->getRevisionByTitle( $title );
|
|
|
|
if ( !$revRecord ) {
|
2015-08-03 06:37:32 +00:00
|
|
|
$ttl = WANObjectCache::TTL_UNCACHEABLE;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-04-02 00:38:28 +00:00
|
|
|
$content = $revRecord->getContent( SlotRecord::MAIN );
|
2015-08-03 06:37:32 +00:00
|
|
|
if ( !$content instanceof GadgetDefinitionContent ) {
|
|
|
|
// Uhm...
|
|
|
|
$ttl = WANObjectCache::TTL_UNCACHEABLE;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-12-13 10:08:16 +00:00
|
|
|
$handler = $content->getContentHandler();
|
|
|
|
'@phan-var \MediaWiki\Extension\Gadgets\Content\GadgetDefinitionContentHandler $handler';
|
|
|
|
$data = wfArrayPlus2d( $content->getAssocArray(), $handler->getDefaultMetadata() );
|
|
|
|
return Gadget::serializeDefinition( $id, $data );
|
2015-08-03 06:37:32 +00:00
|
|
|
},
|
2016-12-28 10:25:47 +00:00
|
|
|
[
|
|
|
|
'checkKeys' => [ $key ],
|
2017-03-03 01:41:25 +00:00
|
|
|
'pcTTL' => WANObjectCache::TTL_PROC_SHORT,
|
2022-08-27 10:54:28 +00:00
|
|
|
'lockTSE' => 30,
|
|
|
|
'version' => 2,
|
2016-12-28 10:25:47 +00:00
|
|
|
]
|
2015-08-03 06:37:32 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if ( $gadget === null ) {
|
|
|
|
throw new InvalidArgumentException( "No gadget registered for '$id'" );
|
|
|
|
}
|
|
|
|
|
2022-08-27 10:54:28 +00:00
|
|
|
return new Gadget( $gadget );
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|
2017-03-03 01:41:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the cache for a specific Gadget whenever it is updated
|
|
|
|
*
|
|
|
|
* @param string $id
|
|
|
|
*/
|
|
|
|
public function purgeGadgetEntry( $id ) {
|
|
|
|
$this->wanCache->touchCheckKey( $this->getGadgetCacheKey( $id ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function getGadgetIdsKey() {
|
|
|
|
return $this->wanCache->makeKey( 'gadgets', 'namespace', 'ids' );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-25 02:41:50 +00:00
|
|
|
* @param string $id
|
2017-03-03 01:41:25 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function getGadgetCacheKey( $id ) {
|
|
|
|
return $this->wanCache->makeKey(
|
|
|
|
'gadgets', 'object', md5( $id ), Gadget::GADGET_CLASS_VERSION );
|
|
|
|
}
|
2015-08-03 06:37:32 +00:00
|
|
|
}
|