Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.50% covered (warning)
87.50%
42 / 48
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpamlistLookup
87.50% covered (warning)
87.50%
42 / 48
50.00% covered (danger)
50.00%
2 / 4
17.56
0.00% covered (danger)
0.00%
0 / 1
 fetchTargets
n/a
0 / 0
n/a
0 / 0
0
 getTargets
95.83% covered (success)
95.83%
23 / 24
0.00% covered (danger)
0.00%
0 / 1
6
 factory
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 isCachable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 normalizeTargets
68.75% covered (warning)
68.75%
11 / 16
0.00% covered (danger)
0.00%
0 / 1
7.10
1<?php
2
3namespace MediaWiki\MassMessage\Lookup;
4
5use MediaWiki\MassMessage\UrlHelper;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Title\Title;
8use MediaWiki\WikiMap\WikiMap;
9use WANObjectCache;
10
11/**
12 * Functions related to target processing
13 */
14
15abstract 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}