Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
31.82% covered (danger)
31.82%
14 / 44
25.00% covered (danger)
25.00%
2 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiCrossWiki
31.82% covered (danger)
31.82%
14 / 44
25.00% covered (danger)
25.00%
2 / 8
76.12
0.00% covered (danger)
0.00%
0 / 1
 getFromForeign
25.00% covered (danger)
25.00%
3 / 12
0.00% covered (danger)
0.00%
0 / 1
6.80
 getForeignQueryParams
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 allowCrossWikiNotifications
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRequestedWikis
60.00% covered (warning)
60.00%
6 / 10
0.00% covered (danger)
0.00%
0 / 1
3.58
 getRequestedForeignWikis
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getForeignNotifications
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getForeignWikisWithUnreadNotifications
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCrossWikiParams
20.00% covered (danger)
20.00%
3 / 15
0.00% covered (danger)
0.00%
0 / 1
4.05
1<?php
2
3namespace MediaWiki\Extension\Notifications\Api;
4
5// @phan-file-suppress PhanUndeclaredMethod This is a trait, and phan is confused by $this
6use Exception;
7use MediaWiki\Extension\Notifications\ForeignNotifications;
8use MediaWiki\Extension\Notifications\ForeignWikiRequest;
9use MediaWiki\WikiMap\WikiMap;
10use Wikimedia\ParamValidator\ParamValidator;
11
12/**
13 * Trait that adds cross-wiki functionality to an API module. For mixing into ApiBase subclasses.
14 *
15 * In addition to mixing in this trait, you have to do the following in your API module:
16 * - In your getAllowedParams() method, merge in the return value of getCrossWikiParams()
17 * - In your execute() method, call getFromForeign() somewhere and do something with the result
18 * - Optionally, override getForeignQueryParams() to customize what is sent to the foreign wikis
19 */
20trait ApiCrossWiki {
21    /**
22     * @var ForeignNotifications
23     */
24    protected $foreignNotifications;
25
26    /**
27     * This will take the current API call (with all of its params) and execute
28     * it on all foreign wikis, returning an array of results per wiki.
29     *
30     * @param array|null $wikis List of wikis to query. Defaults to the result of getRequestedForeignWikis().
31     * @param array $paramOverrides Request parameter overrides
32     * @return array[]
33     * @throws Exception
34     */
35    protected function getFromForeign( array $wikis = null, array $paramOverrides = [] ) {
36        $wikis ??= $this->getRequestedForeignWikis();
37        if ( $wikis === [] ) {
38            return [];
39        }
40        $tokenType = $this->needsToken();
41        $foreignReq = new ForeignWikiRequest(
42            $this->getUser(),
43            $paramOverrides + $this->getForeignQueryParams(),
44            $wikis,
45            $this->getModulePrefix() . 'wikis',
46            $tokenType !== false ? $tokenType : null
47        );
48        return $foreignReq->execute( $this->getRequest() );
49    }
50
51    /**
52     * Get the query parameters to use for the foreign API requests.
53     * Implementing classes should override this if they need to customize
54     * the parameters.
55     * @return array Query parameters
56     */
57    protected function getForeignQueryParams() {
58        return $this->getRequest()->getValues();
59    }
60
61    /**
62     * @return bool
63     */
64    protected function allowCrossWikiNotifications() {
65        global $wgEchoCrossWikiNotifications;
66        return $wgEchoCrossWikiNotifications;
67    }
68
69    /**
70     * This is basically equivalent to $params['wikis'], but some added checks:
71     * - `*` will expand to "all wikis with unread notifications"
72     * - if `$wgEchoCrossWikiNotifications` is off, foreign wikis will be excluded
73     *
74     * @return string[]
75     */
76    protected function getRequestedWikis() {
77        $params = $this->extractRequestParams();
78
79        // if wiki is omitted from params, that's because crosswiki is/was not
80        // available, and it'll default to current wiki
81        $wikis = $params['wikis'] ?? [ WikiMap::getCurrentWikiId() ];
82
83        if ( in_array( '*', $wikis ) ) {
84            // expand `*` to all foreign wikis with unread notifications + local
85            $wikis = array_merge(
86                [ WikiMap::getCurrentWikiId() ],
87                $this->getForeignWikisWithUnreadNotifications()
88            );
89        }
90
91        if ( !$this->allowCrossWikiNotifications() ) {
92            // exclude foreign wikis if x-wiki is not enabled
93            $wikis = array_intersect_key( [ WikiMap::getCurrentWikiId() ], $wikis );
94        }
95
96        return $wikis;
97    }
98
99    /**
100     * @return string[] Wiki names
101     */
102    protected function getRequestedForeignWikis() {
103        return array_diff( $this->getRequestedWikis(), [ WikiMap::getCurrentWikiId() ] );
104    }
105
106    /**
107     * @return ForeignNotifications
108     */
109    protected function getForeignNotifications() {
110        if ( $this->foreignNotifications === null ) {
111            $this->foreignNotifications = new ForeignNotifications( $this->getUser() );
112        }
113        return $this->foreignNotifications;
114    }
115
116    /**
117     * @return string[] Wiki names
118     */
119    protected function getForeignWikisWithUnreadNotifications() {
120        return $this->getForeignNotifications()->getWikis();
121    }
122
123    /**
124     * @return array[]
125     */
126    public function getCrossWikiParams() {
127        global $wgConf;
128
129        $params = [];
130
131        if ( $this->allowCrossWikiNotifications() ) {
132            $params += [
133                // fetch notifications from multiple wikis
134                'wikis' => [
135                    ParamValidator::PARAM_ISMULTI => true,
136                    ParamValidator::PARAM_DEFAULT => WikiMap::getCurrentWikiId(),
137                    // `*` will let you immediately fetch from all wikis that have
138                    // unread notifications, without having to look them up first
139                    ParamValidator::PARAM_TYPE => array_unique(
140                        array_merge(
141                            $wgConf->wikis,
142                            [ WikiMap::getCurrentWikiId(), '*' ]
143                        )
144                    ),
145                ],
146            ];
147        }
148
149        return $params;
150    }
151}