Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 64 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
BaseModule | |
0.00% |
0 / 64 |
|
0.00% |
0 / 14 |
420 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
setPageURL | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPageURL | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
shouldWrapModuleWithLink | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getJsData | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
renderMobileDetailsForOverlay | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getGrowthWikiConfig | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getModuleStyles | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getState | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getActionData | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
buildModuleWrapper | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
20 | |||
outputDependencies | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
getModuleRoute | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUserVariant | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\HomepageModules; |
4 | |
5 | use GrowthExperiments\DashboardModule\DashboardModule; |
6 | use GrowthExperiments\ExperimentUserManager; |
7 | use MediaWiki\Config\Config; |
8 | use MediaWiki\Context\IContextSource; |
9 | use MediaWiki\Html\Html; |
10 | |
11 | /** |
12 | * BaseModule is a base class for homepage modules. |
13 | * It provides utilities and a default structure (header, subheader, body, footer). |
14 | */ |
15 | abstract class BaseModule extends DashboardModule { |
16 | |
17 | protected const BASE_CSS_CLASS = 'growthexperiments-homepage-module'; |
18 | protected const MODULE_STATE_COMPLETE = 'complete'; |
19 | protected const MODULE_STATE_INCOMPLETE = 'incomplete'; |
20 | protected const MODULE_STATE_ACTIVATED = 'activated'; |
21 | protected const MODULE_STATE_UNACTIVATED = 'unactivated'; |
22 | protected const MODULE_STATE_NOEMAIL = 'noemail'; |
23 | protected const MODULE_STATE_UNCONFIRMED = 'unconfirmed'; |
24 | protected const MODULE_STATE_CONFIRMED = 'confirmed'; |
25 | protected const MODULE_STATE_NOTRENDERED = 'notrendered'; |
26 | |
27 | /** |
28 | * @var ExperimentUserManager |
29 | */ |
30 | private $experimentUserManager; |
31 | |
32 | /** @var Config */ |
33 | private $wikiConfig; |
34 | |
35 | /** |
36 | * @var bool |
37 | */ |
38 | private $shouldWrapModuleWithLink; |
39 | |
40 | /** |
41 | * @var string |
42 | */ |
43 | private $pageURL = null; |
44 | |
45 | /** |
46 | * @param string $name Name of the module |
47 | * @param IContextSource $ctx |
48 | * @param Config $wikiConfig |
49 | * @param ExperimentUserManager $experimentUserManager |
50 | * @param bool $shouldWrapModuleWithLink |
51 | */ |
52 | public function __construct( |
53 | $name, |
54 | IContextSource $ctx, |
55 | Config $wikiConfig, |
56 | ExperimentUserManager $experimentUserManager, |
57 | bool $shouldWrapModuleWithLink = true |
58 | ) { |
59 | parent::__construct( $name, $ctx ); |
60 | |
61 | $this->wikiConfig = $wikiConfig; |
62 | $this->experimentUserManager = $experimentUserManager; |
63 | $this->shouldWrapModuleWithLink = $shouldWrapModuleWithLink; |
64 | } |
65 | |
66 | /** |
67 | * Sets the page base URL where the module is being rendered. |
68 | * Can be later used for generating links from inside the module. |
69 | * @param string $url |
70 | */ |
71 | public function setPageURL( string $url ): void { |
72 | $this->pageURL = $url; |
73 | } |
74 | |
75 | /** |
76 | * Gets the base page URL where the module is being rendered. |
77 | * @return string|null |
78 | */ |
79 | public function getPageURL(): ?string { |
80 | return $this->pageURL; |
81 | } |
82 | |
83 | /** |
84 | * Gets whether the module will be wrapped in a link to its |
85 | * full screen view or not |
86 | * @return bool |
87 | */ |
88 | public function shouldWrapModuleWithLink(): bool { |
89 | return $this->shouldWrapModuleWithLink; |
90 | } |
91 | |
92 | /** |
93 | * Get an array of data needed by the Javascript code related to this module. |
94 | * The data will be available in the 'homepagemodules' JS configuration field, keyed by module name. |
95 | * Keys currently in use: |
96 | * - html: module HTML |
97 | * - overlay: mobile overlay HTML |
98 | * - rlModules: ResourceLoader modules this module depends on |
99 | * - heading: module header text |
100 | * 'html' is only present when the module supports dynamic loading, 'overlay' and 'heading' |
101 | * in mobile summary/overlay mode, and 'rlModules' in both cases. |
102 | * |
103 | * @param string $mode One of RENDER_DESKTOP, RENDER_MOBILE_SUMMARY, RENDER_MOBILE_DETAILS |
104 | * @return array |
105 | */ |
106 | public function getJsData( $mode ) { |
107 | if ( !$this->supports( $mode ) ) { |
108 | return []; |
109 | } |
110 | |
111 | $data = []; |
112 | if ( $this->canRender() |
113 | && $mode == self::RENDER_MOBILE_SUMMARY |
114 | ) { |
115 | $this->setMode( self::RENDER_MOBILE_DETAILS_OVERLAY ); |
116 | $data = [ |
117 | 'overlay' => $this->renderMobileDetailsForOverlay(), |
118 | 'rlModules' => $this->getModules(), |
119 | 'heading' => $this->getHeaderText(), |
120 | ]; |
121 | } |
122 | $this->setMode( $mode ); |
123 | $data[ 'renderMode' ] = $mode; |
124 | return $data; |
125 | } |
126 | |
127 | /** |
128 | * @return string HTML rendering for overlay. Same as mobile details but without header. |
129 | */ |
130 | protected function renderMobileDetailsForOverlay() { |
131 | return $this->buildModuleWrapper( |
132 | $this->buildSection( 'subheader', $this->getSubheader(), $this->getSubheaderTag() ), |
133 | $this->buildSection( 'body', $this->getBody() ), |
134 | $this->buildSection( 'footer', $this->getFooter() ) |
135 | ); |
136 | } |
137 | |
138 | /** |
139 | * @return Config |
140 | */ |
141 | final protected function getGrowthWikiConfig(): Config { |
142 | return $this->wikiConfig; |
143 | } |
144 | |
145 | /** |
146 | * @inheritDoc |
147 | */ |
148 | protected function getModuleStyles() { |
149 | return [ 'oojs-ui.styles.icons-movement' ]; |
150 | } |
151 | |
152 | /** |
153 | * Override this function to provide the state of this module. It will |
154 | * be included in 'state' for all HomepageModule events. |
155 | * |
156 | * @return string |
157 | */ |
158 | public function getState() { |
159 | return ''; |
160 | } |
161 | |
162 | /** |
163 | * Override this function to provide the action data of this module. It will |
164 | * be included in 'action_data' for HomepageModule events. |
165 | * |
166 | * @return array |
167 | */ |
168 | protected function getActionData() { |
169 | return []; |
170 | } |
171 | |
172 | /** |
173 | * @inheritDoc |
174 | */ |
175 | protected function buildModuleWrapper( ...$sections ) { |
176 | $moduleContent = Html::rawElement( |
177 | 'div', |
178 | [ |
179 | 'class' => array_merge( [ |
180 | self::BASE_CSS_CLASS, |
181 | self::BASE_CSS_CLASS . '-' . $this->name, |
182 | self::BASE_CSS_CLASS . '-' . $this->getMode(), |
183 | self::BASE_CSS_CLASS . '-user-variant-' . $this->getUserVariant() |
184 | ], $this->getCssClasses() ), |
185 | 'data-module-name' => $this->name, |
186 | 'data-mode' => $this->getMode(), |
187 | ], |
188 | implode( "\n", $sections ) |
189 | ); |
190 | |
191 | if ( |
192 | $this->getMode() === self::RENDER_MOBILE_SUMMARY && |
193 | $this->supports( self::RENDER_MOBILE_DETAILS ) && |
194 | $this->shouldWrapModuleWithLink() |
195 | ) { |
196 | return Html::rawElement( 'a', [ |
197 | 'href' => $this->getPageURL() . '/' . $this->getName(), |
198 | 'data-overlay-route' => $this->getModuleRoute() |
199 | ], $moduleContent ); |
200 | } |
201 | |
202 | return $moduleContent; |
203 | } |
204 | |
205 | protected function outputDependencies() { |
206 | parent::outputDependencies(); |
207 | |
208 | $out = $this->getContext()->getOutput(); |
209 | $out->addModuleStyles( [ |
210 | 'ext.growthExperiments.Homepage.styles', |
211 | 'ext.growthExperiments.icons' |
212 | ] ); |
213 | $out->addJsConfigVars( [ |
214 | 'wgGEHomepageModuleState-' . $this->getName() => $this->getState(), |
215 | 'wgGEHomepageModuleActionData-' . $this->getName() => $this->getActionData(), |
216 | ] ); |
217 | } |
218 | |
219 | /** |
220 | * The component for mw.router to use when routing clicks from mobile |
221 | * summary HTML. If this is an empty string, no routing occurs. |
222 | * |
223 | * @return string |
224 | */ |
225 | protected function getModuleRoute(): string { |
226 | return '#/homepage/' . $this->name; |
227 | } |
228 | |
229 | /** |
230 | * @return string |
231 | */ |
232 | private function getUserVariant(): string { |
233 | return $this->experimentUserManager->getVariant( $this->getContext()->getUser() ); |
234 | } |
235 | |
236 | } |