Fix typo of Minimum in variable name
[lhc/web/wiklou.git] / includes / installer / WebInstallerOutput.php
1 <?php
2 /**
3 * Output handler for the web installer.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Deployment
22 */
23
24 use MediaWiki\MediaWikiServices;
25
26 /**
27 * Output class modelled on OutputPage.
28 *
29 * I've opted to use a distinct class rather than derive from OutputPage here in
30 * the interests of separation of concerns: if we used a subclass, there would be
31 * quite a lot of things you could do in OutputPage that would break the installer,
32 * that wouldn't be immediately obvious.
33 *
34 * @ingroup Deployment
35 * @since 1.17
36 * @private
37 */
38 class WebInstallerOutput {
39
40 /**
41 * The WebInstaller object this WebInstallerOutput is used by.
42 *
43 * @var WebInstaller
44 */
45 public $parent;
46
47 /**
48 * Buffered contents that haven't been output yet
49 * @var string
50 */
51 private $contents = '';
52
53 /**
54 * Has the header (or short header) been output?
55 * @var bool
56 */
57 private $headerDone = false;
58
59 /**
60 * @var string
61 */
62 public $redirectTarget;
63
64 /**
65 * Does the current page need to allow being used as a frame?
66 * If not, X-Frame-Options will be output to forbid it.
67 *
68 * @var bool
69 */
70 public $allowFrames = false;
71
72 /**
73 * Whether to use the limited header (used during CC license callbacks)
74 * @var bool
75 */
76 private $useShortHeader = false;
77
78 /**
79 * @param WebInstaller $parent
80 */
81 public function __construct( WebInstaller $parent ) {
82 $this->parent = $parent;
83 }
84
85 /**
86 * @param string $html
87 */
88 public function addHTML( $html ) {
89 $this->contents .= $html;
90 $this->flush();
91 }
92
93 /**
94 * @param string $text
95 * @deprecated since 1.32; use addWikiTextAsInterface instead
96 */
97 public function addWikiText( $text ) {
98 wfDeprecated( __METHOD__, '1.32' );
99 $this->addWikiTextAsInterface( $text );
100 }
101
102 /**
103 * @param string $text
104 * @since 1.32
105 */
106 public function addWikiTextAsInterface( $text ) {
107 $this->addHTML( $this->parent->parse( $text ) );
108 }
109
110 /**
111 * @param string $html
112 */
113 public function addHTMLNoFlush( $html ) {
114 $this->contents .= $html;
115 }
116
117 /**
118 * @param string $url
119 *
120 * @throws MWException
121 */
122 public function redirect( $url ) {
123 if ( $this->headerDone ) {
124 throw new MWException( __METHOD__ . ' called after sending headers' );
125 }
126 $this->redirectTarget = $url;
127 }
128
129 public function output() {
130 $this->flush();
131
132 if ( !$this->redirectTarget ) {
133 $this->outputFooter();
134 }
135 }
136
137 /**
138 * Get the stylesheet of the MediaWiki skin.
139 *
140 * @return string
141 */
142 public function getCSS() {
143 global $wgStyleDirectory;
144
145 $moduleNames = [
146 // Based on Skin::getDefaultModules
147 'mediawiki.legacy.shared',
148 // Based on Vector::setupSkinUserCss
149 'mediawiki.skinning.interface',
150 ];
151
152 $resourceLoader = MediaWikiServices::getInstance()->getResourceLoader();
153
154 if ( file_exists( "$wgStyleDirectory/Vector/skin.json" ) ) {
155 // Force loading Vector skin if available as a fallback skin
156 // for whatever ResourceLoader wants to have as the default.
157 $registry = new ExtensionRegistry();
158 $data = $registry->readFromQueue( [
159 "$wgStyleDirectory/Vector/skin.json" => 1,
160 ] );
161 if ( isset( $data['globals']['wgResourceModules'] ) ) {
162 $resourceLoader->register( $data['globals']['wgResourceModules'] );
163 }
164
165 $moduleNames[] = 'skins.vector.styles';
166 }
167
168 $moduleNames[] = 'mediawiki.legacy.config';
169
170 $rlContext = new ResourceLoaderContext( $resourceLoader, new FauxRequest( [
171 'debug' => 'true',
172 'lang' => $this->getLanguage()->getCode(),
173 'only' => 'styles',
174 ] ) );
175
176 $styles = [];
177 foreach ( $moduleNames as $moduleName ) {
178 /** @var ResourceLoaderFileModule $module */
179 $module = $resourceLoader->getModule( $moduleName );
180 if ( !$module ) {
181 // T98043: Don't fatal, but it won't look as pretty.
182 continue;
183 }
184
185 // Based on: ResourceLoaderFileModule::getStyles (without the DB query)
186 $styles = array_merge( $styles, ResourceLoader::makeCombinedStyles(
187 $module->readStyleFiles(
188 $module->getStyleFiles( $rlContext ),
189 $module->getFlip( $rlContext ),
190 $rlContext
191 ) ) );
192 }
193
194 return implode( "\n", $styles );
195 }
196
197 /**
198 * "<link>" to index.php?css=1 for the "<head>"
199 *
200 * @return string
201 */
202 private function getCssUrl() {
203 return Html::linkedStyle( $this->parent->getUrl( [ 'css' => 1 ] ) );
204 }
205
206 public function useShortHeader( $use = true ) {
207 $this->useShortHeader = $use;
208 }
209
210 public function allowFrames( $allow = true ) {
211 $this->allowFrames = $allow;
212 }
213
214 public function flush() {
215 if ( !$this->headerDone ) {
216 $this->outputHeader();
217 }
218 if ( !$this->redirectTarget && strlen( $this->contents ) ) {
219 echo $this->contents;
220 flush();
221 $this->contents = '';
222 }
223 }
224
225 /**
226 * @since 1.33
227 * @return Language
228 */
229 private function getLanguage() {
230 global $wgLang;
231
232 return is_object( $wgLang ) ? $wgLang : Language::factory( 'en' );
233 }
234
235 /**
236 * @return string[]
237 */
238 public function getHeadAttribs() {
239 return [
240 'dir' => $this->getLanguage()->getDir(),
241 'lang' => $this->getLanguage()->getHtmlCode(),
242 ];
243 }
244
245 /**
246 * Get whether the header has been output
247 *
248 * @return bool
249 */
250 public function headerDone() {
251 return $this->headerDone;
252 }
253
254 public function outputHeader() {
255 $this->headerDone = true;
256 $this->parent->request->response()->header( 'Content-Type: text/html; charset=utf-8' );
257
258 if ( !$this->allowFrames ) {
259 $this->parent->request->response()->header( 'X-Frame-Options: DENY' );
260 }
261
262 if ( $this->redirectTarget ) {
263 $this->parent->request->response()->header( 'Location: ' . $this->redirectTarget );
264
265 return;
266 }
267
268 if ( $this->useShortHeader ) {
269 $this->outputShortHeader();
270
271 return;
272 }
273 ?>
274 <?php echo Html::htmlHeader( $this->getHeadAttribs() ); ?>
275
276 <head>
277 <meta name="robots" content="noindex, nofollow" />
278 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
279 <title><?php $this->outputTitle(); ?></title>
280 <?php echo $this->getCssUrl() . "\n"; ?>
281 <?php echo $this->getJQuery() . "\n"; ?>
282 <?php echo Html::linkedScript( 'config.js' ) . "\n"; ?>
283 </head>
284
285 <?php echo Html::openElement( 'body', [ 'class' => $this->getLanguage()->getDir() ] ) . "\n"; ?>
286 <div id="mw-page-base"></div>
287 <div id="mw-head-base"></div>
288 <div id="content" class="mw-body">
289 <div id="bodyContent" class="mw-body-content">
290
291 <h1><?php $this->outputTitle(); ?></h1>
292 <?php
293 }
294
295 public function outputFooter() {
296 if ( $this->useShortHeader ) {
297 echo Html::closeElement( 'body' ) . Html::closeElement( 'html' );
298
299 return;
300 }
301 ?>
302
303 </div></div>
304
305 <div id="mw-panel">
306 <div class="portal" id="p-logo">
307 <a style="background-image: url(images/installer-logo.png);"
308 href="https://www.mediawiki.org/"
309 title="Main Page"></a>
310 </div>
311 <?php
312 $message = wfMessage( 'config-sidebar' )->plain();
313 foreach ( explode( '----', $message ) as $section ) {
314 echo '<div class="portal"><div class="body">';
315 echo $this->parent->parse( $section, true );
316 echo '</div></div>';
317 }
318 ?>
319 </div>
320
321 <?php
322 echo Html::closeElement( 'body' ) . Html::closeElement( 'html' );
323 }
324
325 public function outputShortHeader() {
326 ?>
327 <?php echo Html::htmlHeader( $this->getHeadAttribs() ); ?>
328 <head>
329 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
330 <meta name="robots" content="noindex, nofollow" />
331 <title><?php $this->outputTitle(); ?></title>
332 <?php echo $this->getCssUrl() . "\n"; ?>
333 <?php echo $this->getJQuery(); ?>
334 <?php echo Html::linkedScript( 'config.js' ); ?>
335 </head>
336
337 <body style="background-image: none">
338 <?php
339 }
340
341 public function outputTitle() {
342 global $wgVersion;
343 echo wfMessage( 'config-title', $wgVersion )->escaped();
344 }
345
346 /**
347 * @return string
348 */
349 public function getJQuery() {
350 return Html::linkedScript( "../resources/lib/jquery/jquery.js" );
351 }
352
353 }