diff --git a/extension.json b/extension.json index 3928781d8..088b76410 100644 --- a/extension.json +++ b/extension.json @@ -921,18 +921,18 @@ "EchoAbstractMapper": "includes/mapper/AbstractMapper.php", "EchoAbstractMapperStub": "tests/phpunit/mapper/AbstractMapperTest.php", "EchoAbstractMapperTest": "tests/phpunit/mapper/AbstractMapperTest.php", - "EchoArrayList": "includes/ContainmentSet.php", + "EchoArrayList": "includes/EchoArrayList.php", "EchoAttributeManager": "includes/AttributeManager.php", "EchoAttributeManagerTest": "tests/phpunit/AttributeManagerTest.php", - "EchoCachedList": "includes/ContainmentSet.php", + "EchoCachedList": "includes/EchoCachedList.php", "EchoCallbackIterator": "includes/iterator/CallbackIterator.php", "EchoCatchableFatalErrorException": "includes/exception/CatchableFatalErrorException.php", - "EchoContainmentList": "includes/ContainmentSet.php", - "EchoContainmentSet": "includes/ContainmentSet.php", + "EchoContainmentList": "includes/EchoContainmentList.php", + "EchoContainmentSet": "includes/EchoContainmentSet.php", "EchoDataOutputFormatter": "includes/DataOutputFormatter.php", "EchoDeferredMarkAsDeletedUpdate": "includes/DeferredMarkAsDeletedUpdate.php", - "EchoDiffGroup": "includes/DiffParser.php", - "EchoDiffParser": "includes/DiffParser.php", + "EchoDiffGroup": "includes/EchoDiffGroup.php", + "EchoDiffParser": "includes/EchoDiffParser.php", "EchoDiffParserTest": "tests/phpunit/DiffParserTest.php", "EchoDiscussionParser": "includes/DiscussionParser.php", "EchoDiscussionParserTest": "tests/phpunit/DiscussionParserTest.php", @@ -975,7 +975,7 @@ "EchoNotificationTest": "tests/phpunit/model/NotificationTest.php", "EchoNotifier": "includes/Notifier.php", "EchoOOUI\\LabelIconWidget": "includes/ooui/LabelIconWidget.php", - "EchoOnWikiList": "includes/ContainmentSet.php", + "EchoOnWikiList": "includes/EchoOnWikiList.php", "EchoPageLinkedPresentationModel": "includes/formatters/PageLinkedPresentationModel.php", "EchoPlainTextDigestEmailFormatter": "includes/formatters/EchoPlainTextDigestEmailFormatter.php", "EchoPlainTextEmailFormatter": "includes/formatters/EchoPlainTextEmailFormatter.php", diff --git a/includes/ContainmentSet.php b/includes/ContainmentSet.php deleted file mode 100644 index 5b779d0bb..000000000 --- a/includes/ContainmentSet.php +++ /dev/null @@ -1,271 +0,0 @@ -addArray( $wgSomeGlobalParameter ); - * $set->addOnWiki( NS_USER, 'Foo/bar-baz', $cache, 'some_user_specific_cache_key' ); - * - * Usage: - * if ( $set->contains( 'SomeUser' ) ) { - * ... - * } - */ -class EchoContainmentSet { - /** - * @var EchoContainmentList[] - */ - protected $lists = []; - - /** - * @var User - */ - protected $recipient; - - public function __construct( User $recipient ) { - $this->recipient = $recipient; - } - - /** - * Add an EchoContainmentList to the set of lists checked by self::contains() - * - * @param EchoContainmentList $list - */ - public function add( EchoContainmentList $list ) { - $this->lists[] = $list; - } - - /** - * Add a php array to the set of lists checked by self::contains() - * - * @param array $list - */ - public function addArray( array $list ) { - $this->add( new EchoArrayList( $list ) ); - } - - /** - * Add a list from a user preference to the set of lists checked by self::contains(). - * - * @param string $preferenceName - */ - public function addFromUserOption( $preferenceName ) { - $preference = $this->recipient->getOption( $preferenceName, [] ); - - if ( $preference ) { - $lookup = CentralIdLookup::factory(); - $names = $lookup->namesFromCentralIds( $preference, $this->recipient ); - $this->addArray( $names ); - } - } - - /** - * Add a list from a wiki page to the set of lists checked by self::contains(). Data - * from wiki pages is cached via the BagOStuff. Caching is disabled when passing a null - * $cache object. - * - * @param int $namespace An NS_* constant representing the mediawiki namespace of the page containing the list. - * @param string $title The title of the page containing the list. - * @param BagOStuff $cache An object to cache the page with or null for no cache. - * @param string $cacheKeyPrefix A prefix to be combined with the pages latest revision id and used as a cache key. - * - * @throws MWException - */ - public function addOnWiki( $namespace, $title, BagOStuff $cache = null, $cacheKeyPrefix = '' ) { - $list = new EchoOnWikiList( $namespace, $title ); - if ( $cache ) { - if ( $cacheKeyPrefix === '' ) { - throw new MWException( 'Cache requires providing a cache key prefix.' ); - } - $list = new EchoCachedList( $cache, $cacheKeyPrefix, $list ); - } - $this->add( $list ); - } - - /** - * Test the wrapped lists for existence of $value - * - * @param mixed $value The value to look for - * @return bool True when the set contains the provided value - */ - public function contains( $value ) { - foreach ( $this->lists as $list ) { - // Use strict comparison to prevent the number 0 from matching all strings (T177825) - if ( array_search( $value, $list->getValues(), true ) !== false ) { - return true; - } - } - - return false; - } -} - -/** - * Implements the EchoContainmentList interface for php arrays. Possible source - * of arrays includes $wg* global variables initialized from extensions or global - * wiki config. - */ -class EchoArrayList implements EchoContainmentList { - /** - * @var array - */ - protected $list; - - /** - * @param array $list - */ - public function __construct( array $list ) { - $this->list = $list; - } - - /** - * @inheritDoc - */ - public function getValues() { - return $this->list; - } - - /** - * @inheritDoc - */ - public function getCacheKey() { - return ''; - } -} - -/** - * Implements EchoContainmentList interface for sourcing a list of items from a wiki - * page. Uses the pages latest revision ID as cache key. - */ -class EchoOnWikiList implements EchoContainmentList { - /** - * @var Title|null A title object representing the page to source the list from, - * or null if the page does not exist. - */ - protected $title; - - /** - * @param int $titleNs An NS_* constant representing the mediawiki namespace of the page - * @param string $titleString String portion of the wiki page title - */ - public function __construct( $titleNs, $titleString ) { - $title = Title::newFromText( $titleString, $titleNs ); - if ( $title !== null && $title->getArticleId() ) { - $this->title = $title; - } - } - - /** - * @inheritDoc - */ - public function getValues() { - if ( !$this->title ) { - return []; - } - - $article = WikiPage::newFromID( $this->title->getArticleId() ); - if ( $article === null || !$article->exists() ) { - return []; - } - $text = ContentHandler::getContentText( $article->getContent() ); - if ( $text === null ) { - return []; - } - return array_filter( array_map( 'trim', explode( "\n", $text ) ) ); - } - - /** - * @inheritDoc - */ - public function getCacheKey() { - if ( !$this->title ) { - return ''; - } - - return $this->title->getLatestRevID(); - } -} - -/** - * Caches an EchoContainmentList within a BagOStuff(memcache, etc) to prevent needing - * to load the nested list from a potentially slow source (mysql, etc). - */ -class EchoCachedList implements EchoContainmentList { - const ONE_WEEK = 4233600; - const ONE_DAY = 86400; - - protected $cache; - protected $partialCacheKey; - protected $nestedList; - protected $timeout; - private $result; - - /** - * @param BagOStuff $cache Bag to stored cached data in. - * @param string $partialCacheKey Partial cache key, $nestedList->getCacheKey() will be appended - * to this to construct the cache key used. - * @param EchoContainmentList $nestedList The nested EchoContainmentList to cache the result of. - * @param int $timeout How long in seconds to cache the nested list, defaults to 1 week. - */ - public function __construct( BagOStuff $cache, $partialCacheKey, EchoContainmentList $nestedList, $timeout = self::ONE_WEEK ) { - $this->cache = $cache; - $this->partialCacheKey = $partialCacheKey; - $this->nestedList = $nestedList; - $this->timeout = $timeout; - } - - /** - * @inheritDoc - */ - public function getValues() { - if ( $this->result ) { - return $this->result; - } - - $cacheKey = $this->getCacheKey(); - $fetched = $this->cache->get( $cacheKey ); - if ( is_array( $fetched ) ) { - return $this->result = $fetched; - } - - $result = $this->nestedList->getValues(); - if ( !is_array( $result ) ) { - throw new MWException( sprintf( - "Expected array but received '%s' from '%s::getValues'", - is_object( $result ) ? get_class( $result ) : gettype( $result ), - get_class( $this->nestedList ) - ) ); - } - $this->cache->set( $cacheKey, $result, $this->timeout ); - - return $this->result = $result; - } - - /** - * @inheritDoc - */ - public function getCacheKey() { - return $this->partialCacheKey . '_' . $this->nestedList->getCacheKey(); - } -} diff --git a/includes/EchoArrayList.php b/includes/EchoArrayList.php new file mode 100644 index 000000000..7c2202108 --- /dev/null +++ b/includes/EchoArrayList.php @@ -0,0 +1,34 @@ +list = $list; + } + + /** + * @inheritDoc + */ + public function getValues() { + return $this->list; + } + + /** + * @inheritDoc + */ + public function getCacheKey() { + return ''; + } +} diff --git a/includes/EchoCachedList.php b/includes/EchoCachedList.php new file mode 100644 index 000000000..6e0a209fe --- /dev/null +++ b/includes/EchoCachedList.php @@ -0,0 +1,64 @@ +getCacheKey() will be appended + * to this to construct the cache key used. + * @param EchoContainmentList $nestedList The nested EchoContainmentList to cache the result of. + * @param int $timeout How long in seconds to cache the nested list, defaults to 1 week. + */ + public function __construct( BagOStuff $cache, $partialCacheKey, EchoContainmentList $nestedList, $timeout = self::ONE_WEEK ) { + $this->cache = $cache; + $this->partialCacheKey = $partialCacheKey; + $this->nestedList = $nestedList; + $this->timeout = $timeout; + } + + /** + * @inheritDoc + */ + public function getValues() { + if ( $this->result ) { + return $this->result; + } + + $cacheKey = $this->getCacheKey(); + $fetched = $this->cache->get( $cacheKey ); + if ( is_array( $fetched ) ) { + return $this->result = $fetched; + } + + $result = $this->nestedList->getValues(); + if ( !is_array( $result ) ) { + throw new MWException( sprintf( + "Expected array but received '%s' from '%s::getValues'", + is_object( $result ) ? get_class( $result ) : gettype( $result ), + get_class( $this->nestedList ) + ) ); + } + $this->cache->set( $cacheKey, $result, $this->timeout ); + + return $this->result = $result; + } + + /** + * @inheritDoc + */ + public function getCacheKey() { + return $this->partialCacheKey . '_' . $this->nestedList->getCacheKey(); + } +} diff --git a/includes/EchoContainmentList.php b/includes/EchoContainmentList.php new file mode 100644 index 000000000..56c5b298f --- /dev/null +++ b/includes/EchoContainmentList.php @@ -0,0 +1,18 @@ +addArray( $wgSomeGlobalParameter ); + * $set->addOnWiki( NS_USER, 'Foo/bar-baz', $cache, 'some_user_specific_cache_key' ); + * + * Usage: + * if ( $set->contains( 'SomeUser' ) ) { + * ... + * } + */ +class EchoContainmentSet { + /** + * @var EchoContainmentList[] + */ + protected $lists = []; + + /** + * @var User + */ + protected $recipient; + + public function __construct( User $recipient ) { + $this->recipient = $recipient; + } + + /** + * Add an EchoContainmentList to the set of lists checked by self::contains() + * + * @param EchoContainmentList $list + */ + public function add( EchoContainmentList $list ) { + $this->lists[] = $list; + } + + /** + * Add a php array to the set of lists checked by self::contains() + * + * @param array $list + */ + public function addArray( array $list ) { + $this->add( new EchoArrayList( $list ) ); + } + + /** + * Add a list from a user preference to the set of lists checked by self::contains(). + * + * @param string $preferenceName + */ + public function addFromUserOption( $preferenceName ) { + $preference = $this->recipient->getOption( $preferenceName, [] ); + + if ( $preference ) { + $lookup = CentralIdLookup::factory(); + $names = $lookup->namesFromCentralIds( $preference, $this->recipient ); + $this->addArray( $names ); + } + } + + /** + * Add a list from a wiki page to the set of lists checked by self::contains(). Data + * from wiki pages is cached via the BagOStuff. Caching is disabled when passing a null + * $cache object. + * + * @param int $namespace An NS_* constant representing the mediawiki namespace of the page containing the list. + * @param string $title The title of the page containing the list. + * @param BagOStuff $cache An object to cache the page with or null for no cache. + * @param string $cacheKeyPrefix A prefix to be combined with the pages latest revision id and used as a cache key. + * + * @throws MWException + */ + public function addOnWiki( $namespace, $title, BagOStuff $cache = null, $cacheKeyPrefix = '' ) { + $list = new EchoOnWikiList( $namespace, $title ); + if ( $cache ) { + if ( $cacheKeyPrefix === '' ) { + throw new MWException( 'Cache requires providing a cache key prefix.' ); + } + $list = new EchoCachedList( $cache, $cacheKeyPrefix, $list ); + } + $this->add( $list ); + } + + /** + * Test the wrapped lists for existence of $value + * + * @param mixed $value The value to look for + * @return bool True when the set contains the provided value + */ + public function contains( $value ) { + foreach ( $this->lists as $list ) { + // Use strict comparison to prevent the number 0 from matching all strings (T177825) + if ( array_search( $value, $list->getValues(), true ) !== false ) { + return true; + } + } + + return false; + } +} diff --git a/includes/EchoDiffGroup.php b/includes/EchoDiffGroup.php new file mode 100644 index 000000000..d541e46ee --- /dev/null +++ b/includes/EchoDiffGroup.php @@ -0,0 +1,115 @@ +position = [ + 'right-pos' => $rightPos + 1, + 'left-pos' => $leftPos + 1, + ]; + } + + /** + * @param string $line Line in the right text but not in the left + */ + public function add( $line ) { + $this->new[] = $line; + } + + /** + * @param string $line Line in the left text but not in the right + */ + public function subtract( $line ) { + $this->old[] = $line; + } + + /** + * @return array[] set of changes + * Each change consists of: + * An 'action', one of: + * - add + * - subtract + * - change + * 'content' that was added or removed, or in the case + * of a change, 'old_content' and 'new_content' + * 'left_pos' and 'right_pos' (in 1-indexed lines) of the change. + */ + public function getChangeSet() { + $old = implode( "\n", $this->old ); + $new = implode( "\n", $this->new ); + $position = $this->position; + $changeSet = []; + + // The implodes must come first because we consider array( '' ) to also be false + // meaning a blank link replaced with content is an addition + if ( $old && $new ) { + $min = min( count( $this->old ), count( $this->new ) ); + $changeSet[] = $position + [ + 'action' => 'change', + 'old_content' => implode( "\n", array_slice( $this->old, 0, $min ) ), + 'new_content' => implode( "\n", array_slice( $this->new, 0, $min ) ), + ]; + $position['left-pos'] += $min; + $position['right-pos'] += $min; + $old = implode( "\n", array_slice( $this->old, $min ) ); + $new = implode( "\n", array_slice( $this->new, $min ) ); + } + + if ( $new ) { + $changeSet[] = $position + [ + 'action' => 'add', + 'content' => $new, + ]; + } elseif ( $old ) { + $changeSet[] = $position + [ + 'action' => 'subtract', + 'content' => $old, + ]; + } + + return $changeSet; + } +} diff --git a/includes/DiffParser.php b/includes/EchoDiffParser.php similarity index 73% rename from includes/DiffParser.php rename to includes/EchoDiffParser.php index 379945fa2..f8b97af7f 100644 --- a/includes/DiffParser.php +++ b/includes/EchoDiffParser.php @@ -238,96 +238,3 @@ class EchoDiffParser { return $change; } } - -/** - * Represents a single set of changes all effecting neighboring lines - */ -class EchoDiffGroup { - /** - * @var array The left and right position this change starts at - */ - protected $position; - - /** - * @var array The lines that have been added - */ - protected $new = []; - - /** - * @var array The lines that have been removed - */ - protected $old = []; - - /** - * @param int $leftPos The starting line number in the left text - * @param int $rightPos The starting line number in the right text - */ - public function __construct( $leftPos, $rightPos ) { - // +1 due to the origional code use 1 indexing for this result - $this->position = [ - 'right-pos' => $rightPos + 1, - 'left-pos' => $leftPos + 1, - ]; - } - - /** - * @param string $line Line in the right text but not in the left - */ - public function add( $line ) { - $this->new[] = $line; - } - - /** - * @param string $line Line in the left text but not in the right - */ - public function subtract( $line ) { - $this->old[] = $line; - } - - /** - * @return array[] set of changes - * Each change consists of: - * An 'action', one of: - * - add - * - subtract - * - change - * 'content' that was added or removed, or in the case - * of a change, 'old_content' and 'new_content' - * 'left_pos' and 'right_pos' (in 1-indexed lines) of the change. - */ - public function getChangeSet() { - $old = implode( "\n", $this->old ); - $new = implode( "\n", $this->new ); - $position = $this->position; - $changeSet = []; - - // The implodes must come first because we consider array( '' ) to also be false - // meaning a blank link replaced with content is an addition - if ( $old && $new ) { - $min = min( count( $this->old ), count( $this->new ) ); - $changeSet[] = $position + [ - 'action' => 'change', - 'old_content' => implode( "\n", array_slice( $this->old, 0, $min ) ), - 'new_content' => implode( "\n", array_slice( $this->new, 0, $min ) ), - ]; - $position['left-pos'] += $min; - $position['right-pos'] += $min; - $old = implode( "\n", array_slice( $this->old, $min ) ); - $new = implode( "\n", array_slice( $this->new, $min ) ); - } - - if ( $new ) { - $changeSet[] = $position + [ - 'action' => 'add', - 'content' => $new, - ]; - } elseif ( $old ) { - $changeSet[] = $position + [ - 'action' => 'subtract', - 'content' => $old, - ]; - } - - return $changeSet; - } -} diff --git a/includes/EchoOnWikiList.php b/includes/EchoOnWikiList.php new file mode 100644 index 000000000..5bdc4dd68 --- /dev/null +++ b/includes/EchoOnWikiList.php @@ -0,0 +1,54 @@ +getArticleId() ) { + $this->title = $title; + } + } + + /** + * @inheritDoc + */ + public function getValues() { + if ( !$this->title ) { + return []; + } + + $article = WikiPage::newFromID( $this->title->getArticleId() ); + if ( $article === null || !$article->exists() ) { + return []; + } + $text = ContentHandler::getContentText( $article->getContent() ); + if ( $text === null ) { + return []; + } + return array_filter( array_map( 'trim', explode( "\n", $text ) ) ); + } + + /** + * @inheritDoc + */ + public function getCacheKey() { + if ( !$this->title ) { + return ''; + } + + return $this->title->getLatestRevID(); + } +}