Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
87.50% |
42 / 48 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
SpamlistLookup | |
87.50% |
42 / 48 |
|
50.00% |
2 / 4 |
17.56 | |
0.00% |
0 / 1 |
fetchTargets | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getTargets | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
6 | |||
factory | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
isCachable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
normalizeTargets | |
68.75% |
11 / 16 |
|
0.00% |
0 / 1 |
7.10 |
1 | <?php |
2 | |
3 | namespace MediaWiki\MassMessage\Lookup; |
4 | |
5 | use MediaWiki\MassMessage\UrlHelper; |
6 | use MediaWiki\MediaWikiServices; |
7 | use MediaWiki\Title\Title; |
8 | use MediaWiki\WikiMap\WikiMap; |
9 | use Wikimedia\ObjectCache\WANObjectCache; |
10 | |
11 | /** |
12 | * Functions related to target processing |
13 | */ |
14 | |
15 | abstract class SpamlistLookup { |
16 | |
17 | /** |
18 | * Get an array of targets via the getTarget function. |
19 | * |
20 | * @return array[] |
21 | */ |
22 | abstract public function fetchTargets(); |
23 | |
24 | /** |
25 | * Get an array of targets given a title; returns null if invalid. |
26 | * |
27 | * Each target is an associative array with the following keys: |
28 | * title: The title of the target |
29 | * wiki: The ID of the wiki |
30 | * site: The hostname and port (if exists) of the wiki |
31 | * |
32 | * Normalized targets are briefly cached because it can be expensive to parse PF targets on both |
33 | * preview and save in SpecialMassMessage. |
34 | * |
35 | * @param Title $spamlist |
36 | * @param bool $normalize Whether to normalize and deduplicate the targets |
37 | * @return array[]|null |
38 | */ |
39 | public static function getTargets( Title $spamlist, $normalize = true ) { |
40 | if ( !$spamlist->exists() && !$spamlist->inNamespace( NS_CATEGORY ) ) { |
41 | return null; |
42 | } |
43 | |
44 | $lookup = self::factory( $spamlist ); |
45 | $callback = function ( $old = null, &$ttl = null ) use ( $lookup, $spamlist, $normalize ) { |
46 | $targets = $lookup->fetchTargets(); |
47 | if ( $targets && $normalize ) { |
48 | $value = self::normalizeTargets( $targets ); |
49 | } else { |
50 | $value = $targets; |
51 | // Do not cache negatives (null or []) nor non-normalized lists |
52 | $ttl = WANObjectCache::TTL_UNCACHEABLE; |
53 | } |
54 | return $value; |
55 | }; |
56 | |
57 | $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); |
58 | |
59 | return $lookup->isCachable() |
60 | ? $cache->getWithSetCallback( |
61 | $cache->makeKey( |
62 | 'massmessage', |
63 | 'targets', |
64 | $spamlist->getLatestRevId(), |
65 | $spamlist->getTouched() |
66 | ), |
67 | $cache::TTL_HOUR, |
68 | $callback |
69 | ) |
70 | : $callback(); |
71 | } |
72 | |
73 | /** |
74 | * Gets a Title and returns an object depending on the content of $title. |
75 | * Returns null if invalid. |
76 | * |
77 | * @param Title $title |
78 | * @return SpamlistLookup|null |
79 | */ |
80 | public static function factory( Title $title ) { |
81 | if ( $title->inNamespace( NS_CATEGORY ) ) { |
82 | return new CategorySpamlistLookup( $title ); |
83 | } elseif ( $title->hasContentModel( 'MassMessageListContent' ) ) { |
84 | return new ListContentSpamlistLookup( $title ); |
85 | } elseif ( $title->hasContentModel( CONTENT_MODEL_WIKITEXT ) ) { |
86 | return new ParserFunctionSpamlistLookup( $title ); |
87 | } else { |
88 | return null; |
89 | } |
90 | } |
91 | |
92 | /** |
93 | * Returns true |
94 | * |
95 | * @return bool |
96 | */ |
97 | public function isCachable() { |
98 | return true; |
99 | } |
100 | |
101 | /** |
102 | * Get array of normalized targets with duplicates removed. |
103 | * |
104 | * @param array[] $data |
105 | * @return array[] |
106 | */ |
107 | private static function normalizeTargets( array $data ) { |
108 | $conversionNamespaces = MediaWikiServices::getInstance()->getMainConfig()->get( 'NamespacesToConvert' ); |
109 | |
110 | $currentWikiId = WikiMap::getCurrentWikiId(); |
111 | foreach ( $data as &$target ) { |
112 | if ( $target['wiki'] === $currentWikiId ) { |
113 | $title = Title::newFromText( $target['title'] ); |
114 | if ( $title === null ) { |
115 | continue; |
116 | } |
117 | if ( isset( $conversionNamespaces[$title->getNamespace()] ) ) { |
118 | $title = Title::makeTitle( |
119 | $conversionNamespaces[$title->getNamespace()], |
120 | $title->getText() ); |
121 | } |
122 | $title = UrlHelper::followRedirect( $title ); |
123 | if ( $title === null ) { |
124 | // Interwiki redirect |
125 | continue; |
126 | } |
127 | $target['title'] = $title->getPrefixedText(); |
128 | } |
129 | } |
130 | // Return $data with duplicates removed |
131 | return array_unique( $data, SORT_REGULAR ); |
132 | } |
133 | |
134 | } |