Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.17% covered (warning)
56.17%
91 / 162
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
HomepageModuleRegistry
56.17% covered (warning)
56.17%
91 / 162
0.00% covered (danger)
0.00%
0 / 4
24.12
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
4.25
 getModuleIds
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWiring
55.92% covered (warning)
55.92%
85 / 152
0.00% covered (danger)
0.00%
0 / 1
9.08
1<?php
2
3namespace GrowthExperiments\Homepage;
4
5use ExtensionRegistry;
6use GrowthExperiments\DashboardModule\IDashboardModule;
7use GrowthExperiments\GrowthExperimentsServices;
8use GrowthExperiments\HomepageModules\Banner;
9use GrowthExperiments\HomepageModules\Help;
10use GrowthExperiments\HomepageModules\Impact;
11use GrowthExperiments\HomepageModules\Mentorship;
12use GrowthExperiments\HomepageModules\MentorshipOptIn;
13use GrowthExperiments\HomepageModules\NewImpact;
14use GrowthExperiments\HomepageModules\StartEditing;
15use GrowthExperiments\HomepageModules\StartEmail;
16use GrowthExperiments\HomepageModules\SuggestedEdits;
17use GrowthExperiments\HomepageModules\WelcomeSurveyReminder;
18use GrowthExperiments\VariantHooks;
19use IContextSource;
20use MediaWiki\MediaWikiServices;
21use OutOfBoundsException;
22
23/**
24 * Container class for handling dependency injection of homepage modules.
25 */
26class HomepageModuleRegistry {
27
28    /** @var MediaWikiServices */
29    private $services;
30
31    /** @var callable[] id => factory method */
32    private $wiring;
33
34    /** @var IDashboardModule[] id => module */
35    private $modules = [];
36
37    /**
38     * @param MediaWikiServices $services
39     */
40    public function __construct( MediaWikiServices $services ) {
41        $this->services = $services;
42    }
43
44    /**
45     * @param string $id
46     * @param IContextSource $contextSource
47     * @return IDashboardModule
48     */
49    public function get( string $id, IContextSource $contextSource ): IDashboardModule {
50        if ( $this->modules[$id] ?? null ) {
51            return $this->modules[$id];
52        }
53        if ( $this->wiring === null ) {
54            $this->wiring = self::getWiring();
55        }
56        if ( !array_key_exists( $id, $this->wiring ) ) {
57            throw new OutOfBoundsException( 'Module not found: ' . $id );
58        }
59        $this->modules[$id] = $this->wiring[$id]( $this->services, $contextSource );
60        return $this->modules[$id];
61    }
62
63    /**
64     * @internal for testing only
65     * @return string[]
66     */
67    public static function getModuleIds(): array {
68        return array_keys( self::getWiring() );
69    }
70
71    /**
72     * Returns wiring callbacks for each module.
73     * The callback receives the service container and the request context,
74     * and must return a homepage module.
75     * @return callable[] module id => callback
76     */
77    private static function getWiring() {
78        return [
79            'banner' => static function (
80                MediaWikiServices $services,
81                IContextSource $context
82            ) {
83                $growthServices = GrowthExperimentsServices::wrap( $services );
84                return new Banner(
85                    $context,
86                    $growthServices->getGrowthWikiConfig(),
87                    $growthServices->getExperimentUserManager()
88                );
89            },
90
91            'welcomesurveyreminder' => static function (
92                MediaWikiServices $services,
93                IContextSource $context
94            ) {
95                $growthServices = GrowthExperimentsServices::wrap( $services );
96                return new WelcomeSurveyReminder(
97                    $context,
98                    $growthServices->getGrowthWikiConfig(),
99                    $growthServices->getExperimentUserManager(),
100                    $services->getSpecialPageFactory(),
101                    $growthServices->getWelcomeSurveyFactory()
102                );
103            },
104
105            'startemail' => static function (
106                MediaWikiServices $services,
107                IContextSource $context
108            ) {
109                $growthServices = GrowthExperimentsServices::wrap( $services );
110                return new StartEmail(
111                    $context,
112                    $growthServices->getGrowthWikiConfig(),
113                    $growthServices->getExperimentUserManager()
114                );
115            },
116
117            'suggested-edits' => static function (
118                MediaWikiServices $services,
119                IContextSource $context
120            ) {
121                $growthServices = GrowthExperimentsServices::wrap( $services );
122                $pageViewInfoEnabled = ExtensionRegistry::getInstance()->isLoaded( 'PageViewInfo' );
123                return new SuggestedEdits(
124                    $context,
125                    $growthServices->getGrowthWikiConfig(),
126                    $growthServices->getGrowthExperimentsCampaignConfig(),
127                    $growthServices->getEditInfoService(),
128                    $growthServices->getExperimentUserManager(),
129                    $pageViewInfoEnabled ? $services->get( 'PageViewService' ) : null,
130                    $growthServices->getNewcomerTasksConfigurationLoader(),
131                    $growthServices->getNewcomerTasksUserOptionsLookup(),
132                    $growthServices->getTaskSuggesterFactory()->create(),
133                    $services->getTitleFactory(),
134                    $growthServices->getProtectionFilter(),
135                    $services->getUserOptionsLookup(),
136                    $growthServices->getLinkRecommendationFilter(),
137                    $growthServices->getImageRecommendationFilter()
138                );
139            },
140
141            'impact' => static function (
142                MediaWikiServices $services,
143                IContextSource $context
144            ) {
145                $growthServices = GrowthExperimentsServices::wrap( $services );
146                $experimentUserManager = $growthServices->getExperimentUserManager();
147                $userOptionsLookup = $services->getUserOptionsLookup();
148                $config = $context->getConfig();
149
150                // A/B test: use new impact module if explicitly requested,
151                // or the feature flag is on and user is in the test group.
152                if ( $context->getRequest()->getRawVal( 'new-impact' ) !== null ) {
153                    $useNewImpactModule = $context->getRequest()->getBool( 'new-impact' );
154                } else {
155                    $useNewImpactModule = $config->get( 'GEUseNewImpactModule' )
156                        && !$experimentUserManager->isUserInVariant(
157                            $context->getUser(), VariantHooks::VARIANT_OLDIMPACT
158                        );
159                }
160
161                if ( $useNewImpactModule ) {
162                    return new NewImpact(
163                        $context,
164                        $growthServices->getGrowthWikiConfig(),
165                        $growthServices->getExperimentUserManager(),
166                        $context->getUser(),
167                        $growthServices->getUserImpactStore(),
168                        $growthServices->getUserImpactFormatter(),
169                        $growthServices->getUserDatabaseHelper(),
170                        SuggestedEdits::isEnabled( $context->getConfig() ),
171                        SuggestedEdits::isActivated( $context->getUser(), $userOptionsLookup )
172                    );
173                }
174                $pageViewInfoEnabled = ExtensionRegistry::getInstance()->isLoaded( 'PageViewInfo' );
175                return new Impact(
176                    $context,
177                    $growthServices->getGrowthWikiConfig(),
178                    $services->getDBLoadBalancerFactory(),
179                    $growthServices->getExperimentUserManager(),
180                    [
181                        'isSuggestedEditsEnabled' => SuggestedEdits::isEnabled( $context->getConfig() ),
182                        'isSuggestedEditsActivated' => SuggestedEdits::isActivated(
183                            $context->getUser(),
184                            $userOptionsLookup
185                        ),
186                    ],
187                    $services->getTitleFactory(),
188                    $pageViewInfoEnabled ? $services->get( 'PageViewService' ) : null
189                );
190            },
191
192            'mentorship' => static function (
193                MediaWikiServices $services,
194                IContextSource $context
195            ) {
196                $growthServices = GrowthExperimentsServices::wrap( $services );
197                return new Mentorship(
198                    $context,
199                    $growthServices->getGrowthWikiConfig(),
200                    $growthServices->getExperimentUserManager(),
201                    $growthServices->getMentorManager(),
202                    $growthServices->getMentorStatusManager(),
203                    $services->getGenderCache()
204                );
205            },
206
207            'mentorship-optin' => static function (
208                MediaWikiServices $services,
209                IContextSource $context
210            ) {
211                $growthServices = GrowthExperimentsServices::wrap( $services );
212                return new MentorshipOptIn(
213                    $context,
214                    $growthServices->getGrowthWikiConfig(),
215                    $growthServices->getExperimentUserManager(),
216                    $growthServices->getMentorManager()
217                );
218            },
219
220            'help' => static function (
221                MediaWikiServices $services,
222                IContextSource $context
223            ) {
224                $growthServices = GrowthExperimentsServices::wrap( $services );
225                return new Help(
226                    $context,
227                    $growthServices->getGrowthWikiConfig(),
228                    $growthServices->getExperimentUserManager()
229                );
230            },
231
232            'start-startediting' => static function (
233                MediaWikiServices $services,
234                IContextSource $context
235            ) {
236                $growthServices = GrowthExperimentsServices::wrap( $services );
237                return new StartEditing(
238                    $context,
239                    $growthServices->getGrowthWikiConfig(),
240                    $growthServices->getExperimentUserManager(),
241                    $services->getUserOptionsLookup()
242                );
243            }
244
245        ];
246    }
247
248}