Index: trunk/phase3/maintenance/installExtension.php |
— | — | @@ -1,687 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * Copyright (C) 2006 Daniel Kinzler, brightbyte.de |
5 | | - * |
6 | | - * This program is free software; you can redistribute it and/or modify |
7 | | - * it under the terms of the GNU General Public License as published by |
8 | | - * the Free Software Foundation; either version 2 of the License, or |
9 | | - * (at your option) any later version. |
10 | | - * |
11 | | - * This program is distributed in the hope that it will be useful, |
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | - * GNU General Public License for more details. |
15 | | - * |
16 | | - * You should have received a copy of the GNU General Public License along |
17 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
18 | | - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | | - * http://www.gnu.org/copyleft/gpl.html |
20 | | - * |
21 | | - * @file |
22 | | - * @ingroup Maintenance |
23 | | - */ |
24 | | - |
25 | | -$optionsWithArgs = array( 'target', 'repository', 'repos' ); |
26 | | - |
27 | | -require_once( dirname( __FILE__ ) . '/commandLine.inc' ); |
28 | | - |
29 | | -define( 'EXTINST_NOPATCH', 0 ); |
30 | | -define( 'EXTINST_WRITEPATCH', 6 ); |
31 | | -define( 'EXTINST_HOTPATCH', 10 ); |
32 | | - |
33 | | -/** |
34 | | - * @ingroup Maintenance |
35 | | - */ |
36 | | -class InstallerRepository { |
37 | | - var $path; |
38 | | - |
39 | | - function InstallerRepository( $path ) { |
40 | | - $this->path = $path; |
41 | | - } |
42 | | - |
43 | | - function printListing( ) { |
44 | | - trigger_error( 'override InstallerRepository::printListing()', E_USER_ERROR ); |
45 | | - } |
46 | | - |
47 | | - function getResource( $name ) { |
48 | | - trigger_error( 'override InstallerRepository::getResource()', E_USER_ERROR ); |
49 | | - } |
50 | | - |
51 | | - static function makeRepository( $path, $type = NULL ) { |
52 | | - if ( !$type ) { |
53 | | - $m = array(); |
54 | | - preg_match( '!(([-+\w]+)://)?.*?(\.[-\w\d.]+)?$!', $path, $m ); |
55 | | - $proto = @$m[2]; |
56 | | - |
57 | | - if ( !$proto ) { |
58 | | - $type = 'dir'; |
59 | | - } else if ( ( $proto == 'http' || $proto == 'https' ) && preg_match( '!([^\w]svn|svn[^\w])!i', $path ) ) { |
60 | | - $type = 'svn'; # HACK! |
61 | | - } else { |
62 | | - $type = $proto; |
63 | | - } |
64 | | - } |
65 | | - |
66 | | - if ( $type == 'dir' || $type == 'file' ) { return new LocalInstallerRepository( $path ); } |
67 | | - else if ( $type == 'http' || $type == 'http' ) { return new WebInstallerRepository( $path ); } |
68 | | - else { return new SVNInstallerRepository( $path ); } |
69 | | - } |
70 | | -} |
71 | | - |
72 | | -/** |
73 | | - * @ingroup Maintenance |
74 | | - */ |
75 | | -class LocalInstallerRepository extends InstallerRepository { |
76 | | - |
77 | | - function LocalInstallerRepository ( $path ) { |
78 | | - parent::InstallerRepository( $path ); |
79 | | - } |
80 | | - |
81 | | - function printListing( ) { |
82 | | - $ff = glob( "{$this->path}/*" ); |
83 | | - if ( $ff === false || $ff === NULL ) { |
84 | | - ExtensionInstaller::error( "listing directory {$this->path} failed!" ); |
85 | | - return false; |
86 | | - } |
87 | | - |
88 | | - foreach ( $ff as $f ) { |
89 | | - $n = basename( $f ); |
90 | | - |
91 | | - if ( !is_dir( $f ) ) { |
92 | | - $m = array(); |
93 | | - if ( !preg_match( '/(.*)\.(tgz|tar\.gz|zip)/', $n, $m ) ) continue; |
94 | | - $n = $m[1]; |
95 | | - } |
96 | | - |
97 | | - print "\t$n\n"; |
98 | | - } |
99 | | - } |
100 | | - |
101 | | - function getResource( $name ) { |
102 | | - $path = $this->path . '/' . $name; |
103 | | - |
104 | | - if ( !file_exists( $path ) || !is_dir( $path ) ) $path = $this->path . '/' . $name . '.tgz'; |
105 | | - if ( !file_exists( $path ) ) $path = $this->path . '/' . $name . '.tar.gz'; |
106 | | - if ( !file_exists( $path ) ) $path = $this->path . '/' . $name . '.zip'; |
107 | | - |
108 | | - return new LocalInstallerResource( $path ); |
109 | | - } |
110 | | -} |
111 | | - |
112 | | -/** |
113 | | - * @ingroup Maintenance |
114 | | - */ |
115 | | -class WebInstallerRepository extends InstallerRepository { |
116 | | - |
117 | | - function WebInstallerRepository ( $path ) { |
118 | | - parent::InstallerRepository( $path ); |
119 | | - } |
120 | | - |
121 | | - function printListing( ) { |
122 | | - ExtensionInstaller::note( "listing index from {$this->path}..." ); |
123 | | - |
124 | | - $txt = @file_get_contents( $this->path . '/index.txt' ); |
125 | | - if ( $txt ) { |
126 | | - print $txt; |
127 | | - print "\n"; |
128 | | - } |
129 | | - else { |
130 | | - $txt = file_get_contents( $this->path ); |
131 | | - if ( !$txt ) { |
132 | | - ExtensionInstaller::error( "listing index from {$this->path} failed!" ); |
133 | | - print ( $txt ); |
134 | | - return false; |
135 | | - } |
136 | | - |
137 | | - $m = array(); |
138 | | - $ok = preg_match_all( '!<a\s[^>]*href\s*=\s*[' . "'" . '"]([^/' . "'" . '"]+)\.tgz[' . "'" . '"][^>]*>.*?</a>!si', $txt, $m, PREG_SET_ORDER ); |
139 | | - if ( !$ok ) { |
140 | | - ExtensionInstaller::error( "listing index from {$this->path} does not match!" ); |
141 | | - print ( $txt ); |
142 | | - return false; |
143 | | - } |
144 | | - |
145 | | - foreach ( $m as $l ) { |
146 | | - $n = $l[1]; |
147 | | - print "\t$n\n"; |
148 | | - } |
149 | | - } |
150 | | - } |
151 | | - |
152 | | - function getResource( $name ) { |
153 | | - $path = $this->path . '/' . $name . '.tgz'; |
154 | | - return new WebInstallerResource( $path ); |
155 | | - } |
156 | | -} |
157 | | - |
158 | | -/** |
159 | | - * @ingroup Maintenance |
160 | | - */ |
161 | | -class SVNInstallerRepository extends InstallerRepository { |
162 | | - |
163 | | - function SVNInstallerRepository ( $path ) { |
164 | | - parent::InstallerRepository( $path ); |
165 | | - } |
166 | | - |
167 | | - function printListing( ) { |
168 | | - ExtensionInstaller::note( "SVN list {$this->path}..." ); |
169 | | - $code = null; // Shell Exec return value. |
170 | | - $txt = wfShellExec( 'svn ls ' . escapeshellarg( $this->path ), $code ); |
171 | | - if ( $code !== 0 ) { |
172 | | - ExtensionInstaller::error( "svn list for {$this->path} failed!" ); |
173 | | - return false; |
174 | | - } |
175 | | - |
176 | | - $ll = preg_split( '/(\s*[\r\n]\s*)+/', $txt ); |
177 | | - |
178 | | - foreach ( $ll as $line ) { |
179 | | - $m = array(); |
180 | | - if ( !preg_match( '!^(.*)/$!', $line, $m ) ) continue; |
181 | | - $n = $m[1]; |
182 | | - |
183 | | - print "\t$n\n"; |
184 | | - } |
185 | | - } |
186 | | - |
187 | | - function getResource( $name ) { |
188 | | - $path = $this->path . '/' . $name; |
189 | | - return new SVNInstallerResource( $path ); |
190 | | - } |
191 | | -} |
192 | | - |
193 | | -/** |
194 | | - * @ingroup Maintenance |
195 | | - */ |
196 | | -class InstallerResource { |
197 | | - var $path; |
198 | | - var $isdir; |
199 | | - var $islocal; |
200 | | - |
201 | | - function InstallerResource( $path, $isdir, $islocal ) { |
202 | | - $this->path = $path; |
203 | | - |
204 | | - $this->isdir = $isdir; |
205 | | - $this->islocal = $islocal; |
206 | | - |
207 | | - $m = array(); |
208 | | - preg_match( '!([-+\w]+://)?.*?(\.[-\w\d.]+)?$!', $path, $m ); |
209 | | - |
210 | | - $this->protocol = @$m[1]; |
211 | | - $this->extensions = @$m[2]; |
212 | | - |
213 | | - if ( $this->extensions ) $this->extensions = strtolower( $this->extensions ); |
214 | | - } |
215 | | - |
216 | | - function fetch( $target ) { |
217 | | - trigger_error( 'override InstallerResource::fetch()', E_USER_ERROR ); |
218 | | - } |
219 | | - |
220 | | - function extract( $file, $target ) { |
221 | | - |
222 | | - if ( $this->extensions == '.tgz' || $this->extensions == '.tar.gz' ) { # tgz file |
223 | | - ExtensionInstaller::note( "extracting $file..." ); |
224 | | - $code = null; // shell Exec return value. |
225 | | - wfShellExec( 'tar zxvf ' . escapeshellarg( $file ) . ' -C ' . escapeshellarg( $target ), $code ); |
226 | | - |
227 | | - if ( $code !== 0 ) { |
228 | | - ExtensionInstaller::error( "failed to extract $file!" ); |
229 | | - return false; |
230 | | - } |
231 | | - } |
232 | | - else if ( $this->extensions == '.zip' ) { # zip file |
233 | | - ExtensionInstaller::note( "extracting $file..." ); |
234 | | - $code = null; // shell Exec return value. |
235 | | - wfShellExec( 'unzip ' . escapeshellarg( $file ) . ' -d ' . escapeshellarg( $target ) , $code ); |
236 | | - |
237 | | - if ( $code !== 0 ) { |
238 | | - ExtensionInstaller::error( "failed to extract $file!" ); |
239 | | - return false; |
240 | | - } |
241 | | - } |
242 | | - else { |
243 | | - ExtensionInstaller::error( "unknown extension {$this->extensions}!" ); |
244 | | - return false; |
245 | | - } |
246 | | - |
247 | | - return true; |
248 | | - } |
249 | | - |
250 | | - /*static*/ function makeResource( $url ) { |
251 | | - $m = array(); |
252 | | - preg_match( '!(([-+\w]+)://)?.*?(\.[-\w\d.]+)?$!', $url, $m ); |
253 | | - $proto = @$m[2]; |
254 | | - $ext = @$m[3]; |
255 | | - if ( $ext ) $ext = strtolower( $ext ); |
256 | | - |
257 | | - if ( !$proto ) { return new LocalInstallerResource( $url, $ext ? false : true ); } |
258 | | - else if ( $ext && ( $proto == 'http' || $proto == 'http' || $proto == 'ftp' ) ) { return new WebInstallerResource( $url ); } |
259 | | - else { return new SVNInstallerResource( $url ); } |
260 | | - } |
261 | | -} |
262 | | - |
263 | | -/** |
264 | | - * @ingroup Maintenance |
265 | | - */ |
266 | | -class LocalInstallerResource extends InstallerResource { |
267 | | - function LocalInstallerResource( $path ) { |
268 | | - parent::InstallerResource( $path, is_dir( $path ), true ); |
269 | | - } |
270 | | - |
271 | | - function fetch( $target ) { |
272 | | - if ( $this->isdir ) { |
273 | | - return ExtensionInstaller::copyDir( $this->path, dirname( $target ) ); |
274 | | - } |
275 | | - else return $this->extract( $this->path, dirname( $target ) ); |
276 | | - } |
277 | | - |
278 | | -} |
279 | | - |
280 | | -/** |
281 | | - * @ingroup Maintenance |
282 | | - */ |
283 | | -class WebInstallerResource extends InstallerResource { |
284 | | - function WebInstallerResource( $path ) { |
285 | | - parent::InstallerResource( $path, false, false ); |
286 | | - } |
287 | | - |
288 | | - function fetch( $target ) { |
289 | | - $tmp = wfTempDir() . '/' . basename( $this->path ); |
290 | | - |
291 | | - ExtensionInstaller::note( "downloading {$this->path}..." ); |
292 | | - $ok = copy( $this->path, $tmp ); |
293 | | - |
294 | | - if ( !$ok ) { |
295 | | - ExtensionInstaller::error( "failed to download {$this->path}" ); |
296 | | - return false; |
297 | | - } |
298 | | - |
299 | | - $this->extract( $tmp, dirname( $target ) ); |
300 | | - unlink( $tmp ); |
301 | | - |
302 | | - return true; |
303 | | - } |
304 | | -} |
305 | | - |
306 | | -/** |
307 | | - * @ingroup Maintenance |
308 | | - */ |
309 | | -class SVNInstallerResource extends InstallerResource { |
310 | | - function SVNInstallerResource( $path ) { |
311 | | - parent::InstallerResource( $path, true, false ); |
312 | | - } |
313 | | - |
314 | | - function fetch( $target ) { |
315 | | - ExtensionInstaller::note( "SVN checkout of {$this->path}..." ); |
316 | | - $code = null; // shell exec return val. |
317 | | - wfShellExec( 'svn co ' . escapeshellarg( $this->path ) . ' ' . escapeshellarg( $target ), $code ); |
318 | | - |
319 | | - if ( $code !== 0 ) { |
320 | | - ExtensionInstaller::error( "checkout failed for {$this->path}!" ); |
321 | | - return false; |
322 | | - } |
323 | | - |
324 | | - return true; |
325 | | - } |
326 | | -} |
327 | | - |
328 | | -/** |
329 | | - * @ingroup Maintenance |
330 | | - */ |
331 | | -class ExtensionInstaller { |
332 | | - var $source; |
333 | | - var $target; |
334 | | - var $name; |
335 | | - var $dir; |
336 | | - var $tasks; |
337 | | - |
338 | | - function ExtensionInstaller( $name, $source, $target ) { |
339 | | - if ( !is_object( $source ) ) { |
340 | | - $source = parent::makeResource( $source ); |
341 | | - } |
342 | | - |
343 | | - $this->name = $name; |
344 | | - $this->source = $source; |
345 | | - $this->target = realpath( $target ); |
346 | | - $this->extdir = "$target/extensions"; |
347 | | - $this->dir = "{$this->extdir}/$name"; |
348 | | - $this->incpath = "extensions/$name"; |
349 | | - $this->tasks = array(); |
350 | | - |
351 | | - # TODO: allow a subdir different from "extensions" |
352 | | - # TODO: allow a config file different from "LocalSettings.php" |
353 | | - } |
354 | | - |
355 | | - static function note( $msg ) { |
356 | | - print "$msg\n"; |
357 | | - } |
358 | | - |
359 | | - static function warn( $msg ) { |
360 | | - print "WARNING: $msg\n"; |
361 | | - } |
362 | | - |
363 | | - static function error( $msg ) { |
364 | | - print "ERROR: $msg\n"; |
365 | | - } |
366 | | - |
367 | | - function prompt( $msg ) { |
368 | | - if ( function_exists( 'readline' ) ) { |
369 | | - $s = readline( $msg ); |
370 | | - } |
371 | | - else { |
372 | | - if ( !@$this->stdin ) $this->stdin = fopen( 'php://stdin', 'r' ); |
373 | | - if ( !$this->stdin ) die( "Failed to open stdin for user interaction!\n" ); |
374 | | - |
375 | | - print $msg; |
376 | | - flush(); |
377 | | - |
378 | | - $s = fgets( $this->stdin ); |
379 | | - } |
380 | | - |
381 | | - $s = trim( $s ); |
382 | | - return $s; |
383 | | - } |
384 | | - |
385 | | - function confirm( $msg ) { |
386 | | - while ( true ) { |
387 | | - $s = $this->prompt( $msg . " [yes/no]: " ); |
388 | | - $s = strtolower( trim( $s ) ); |
389 | | - |
390 | | - if ( $s == 'yes' || $s == 'y' ) { return true; } |
391 | | - else if ( $s == 'no' || $s == 'n' ) { return false; } |
392 | | - else { print "bad response: $s\n"; } |
393 | | - } |
394 | | - } |
395 | | - |
396 | | - function deleteContents( $dir ) { |
397 | | - $ff = glob( $dir . "/*" ); |
398 | | - if ( !$ff ) return; |
399 | | - |
400 | | - foreach ( $ff as $f ) { |
401 | | - if ( is_dir( $f ) && !is_link( $f ) ) $this->deleteContents( $f ); |
402 | | - unlink( $f ); |
403 | | - } |
404 | | - } |
405 | | - |
406 | | - function copyDir( $dir, $tgt ) { |
407 | | - $d = $tgt . '/' . basename( $dir ); |
408 | | - |
409 | | - if ( !file_exists( $d ) ) { |
410 | | - $ok = mkdir( $d ); |
411 | | - if ( !$ok ) { |
412 | | - ExtensionInstaller::error( "failed to create director $d" ); |
413 | | - return false; |
414 | | - } |
415 | | - } |
416 | | - |
417 | | - $ff = glob( $dir . "/*" ); |
418 | | - if ( $ff === false || $ff === NULL ) return false; |
419 | | - |
420 | | - foreach ( $ff as $f ) { |
421 | | - if ( is_dir( $f ) && !is_link( $f ) ) { |
422 | | - $ok = $this->copyDir( $f, $d ); |
423 | | - if ( !$ok ) return false; |
424 | | - } |
425 | | - else { |
426 | | - $t = $d . '/' . basename( $f ); |
427 | | - $ok = copy( $f, $t ); |
428 | | - |
429 | | - if ( !$ok ) { |
430 | | - ExtensionInstaller::error( "failed to copy $f to $t" ); |
431 | | - return false; |
432 | | - } |
433 | | - } |
434 | | - } |
435 | | - |
436 | | - return true; |
437 | | - } |
438 | | - |
439 | | - function setPermissions( $dir, $dirbits, $filebits ) { |
440 | | - if ( !chmod( $dir, $dirbits ) ) ExtensionInstaller::warn( "faield to set permissions for $dir" ); |
441 | | - |
442 | | - $ff = glob( $dir . "/*" ); |
443 | | - if ( $ff === false || $ff === NULL ) return false; |
444 | | - |
445 | | - foreach ( $ff as $f ) { |
446 | | - $n = basename( $f ); |
447 | | - if ( $n { 0 } == '.' ) continue; # HACK: skip dot files |
448 | | - |
449 | | - if ( is_link( $f ) ) continue; # skip link |
450 | | - |
451 | | - if ( is_dir( $f ) ) { |
452 | | - $this->setPermissions( $f, $dirbits, $filebits ); |
453 | | - } |
454 | | - else { |
455 | | - if ( !chmod( $f, $filebits ) ) ExtensionInstaller::warn( "faield to set permissions for $f" ); |
456 | | - } |
457 | | - } |
458 | | - |
459 | | - return true; |
460 | | - } |
461 | | - |
462 | | - function fetchExtension( ) { |
463 | | - if ( $this->source->islocal && $this->source->isdir && realpath( $this->source->path ) === $this->dir ) { |
464 | | - $this->note( "files are already in the extension dir" ); |
465 | | - return true; |
466 | | - } |
467 | | - |
468 | | - if ( file_exists( $this->dir ) && glob( $this->dir . "/*" ) ) { |
469 | | - if ( $this->confirm( "{$this->dir} exists and is not empty.\nDelete all files in that directory?" ) ) { |
470 | | - $this->deleteContents( $this->dir ); |
471 | | - } |
472 | | - else { |
473 | | - return false; |
474 | | - } |
475 | | - } |
476 | | - |
477 | | - $ok = $this->source->fetch( $this->dir ); |
478 | | - if ( !$ok ) return false; |
479 | | - |
480 | | - if ( !file_exists( $this->dir ) && glob( $this->dir . "/*" ) ) { |
481 | | - $this->error( "{$this->dir} does not exist or is empty. Something went wrong, sorry." ); |
482 | | - return false; |
483 | | - } |
484 | | - |
485 | | - if ( file_exists( $this->dir . '/README' ) ) $this->tasks[] = "read the README file in {$this->dir}"; |
486 | | - if ( file_exists( $this->dir . '/INSTALL' ) ) $this->tasks[] = "read the INSTALL file in {$this->dir}"; |
487 | | - if ( file_exists( $this->dir . '/RELEASE-NOTES' ) ) $this->tasks[] = "read the RELEASE-NOTES file in {$this->dir}"; |
488 | | - |
489 | | - # TODO: configure this smartly...? |
490 | | - $this->setPermissions( $this->dir, 0755, 0644 ); |
491 | | - |
492 | | - $this->note( "fetched extension to {$this->dir}" ); |
493 | | - return true; |
494 | | - } |
495 | | - |
496 | | - function patchLocalSettings( $mode ) { |
497 | | - # NOTE: if we get a better way to hook up extensions, that should be used instead. |
498 | | - |
499 | | - $f = $this->dir . '/install.settings'; |
500 | | - $t = $this->target . '/LocalSettings.php'; |
501 | | - |
502 | | - # TODO: assert version ?! |
503 | | - # TODO: allow custom installer scripts + sql patches |
504 | | - |
505 | | - if ( !file_exists( $f ) ) { |
506 | | - self::warn( "No install.settings file provided!" ); |
507 | | - $this->tasks[] = "Please read the instructions and edit LocalSettings.php manually to activate the extension."; |
508 | | - return '?'; |
509 | | - } |
510 | | - else { |
511 | | - self::note( "applying settings patch..." ); |
512 | | - } |
513 | | - |
514 | | - $settings = file_get_contents( $f ); |
515 | | - |
516 | | - if ( !$settings ) { |
517 | | - self::error( "failed to read settings from $f!" ); |
518 | | - return false; |
519 | | - } |
520 | | - |
521 | | - $settings = str_replace( '{{path}}', $this->incpath, $settings ); |
522 | | - |
523 | | - if ( $mode == EXTINST_NOPATCH ) { |
524 | | - $this->tasks[] = "Please put the following into your LocalSettings.php:" . "\n$settings\n"; |
525 | | - self::note( "Skipping patch phase, automatic patching is off." ); |
526 | | - return true; |
527 | | - } |
528 | | - |
529 | | - if ( $mode == EXTINST_HOTPATCH ) { |
530 | | - # NOTE: keep php extension for backup file! |
531 | | - $bak = $this->target . '/LocalSettings.install-' . $this->name . '-' . wfTimestamp( TS_MW ) . '.bak.php'; |
532 | | - |
533 | | - $ok = copy( $t, $bak ); |
534 | | - |
535 | | - if ( !$ok ) { |
536 | | - self::warn( "failed to create backup of LocalSettings.php!" ); |
537 | | - return false; |
538 | | - } |
539 | | - else { |
540 | | - self::note( "created backup of LocalSettings.php at $bak" ); |
541 | | - } |
542 | | - } |
543 | | - |
544 | | - $localsettings = file_get_contents( $t ); |
545 | | - |
546 | | - if ( !$settings ) { |
547 | | - self::error( "failed to read $t for patching!" ); |
548 | | - return false; |
549 | | - } |
550 | | - |
551 | | - $marker = "<@< extension {$this->name} >@>"; |
552 | | - $blockpattern = "/\n\s*#\s*BEGIN\s*$marker.*END\s*$marker\s*/smi"; |
553 | | - |
554 | | - if ( preg_match( $blockpattern, $localsettings ) ) { |
555 | | - $localsettings = preg_replace( $blockpattern, "\n", $localsettings ); |
556 | | - $this->warn( "removed old configuration block for extension {$this->name}!" ); |
557 | | - } |
558 | | - |
559 | | - $newblock = "\n# BEGIN $marker\n$settings\n# END $marker\n"; |
560 | | - |
561 | | - $localsettings = preg_replace( "/\?>\s*$/si", "$newblock?>", $localsettings ); |
562 | | - |
563 | | - if ( $mode != EXTINST_HOTPATCH ) { |
564 | | - $t = $this->target . '/LocalSettings.install-' . $this->name . '-' . wfTimestamp( TS_MW ) . '.php'; |
565 | | - } |
566 | | - |
567 | | - $ok = file_put_contents( $t, $localsettings ); |
568 | | - |
569 | | - if ( !$ok ) { |
570 | | - self::error( "failed to patch $t!" ); |
571 | | - return false; |
572 | | - } |
573 | | - else if ( $mode == EXTINST_HOTPATCH ) { |
574 | | - self::note( "successfully patched $t" ); |
575 | | - } |
576 | | - else { |
577 | | - self::note( "created patched settings file $t" ); |
578 | | - $this->tasks[] = "Replace your current LocalSettings.php with " . basename( $t ); |
579 | | - } |
580 | | - |
581 | | - return true; |
582 | | - } |
583 | | - |
584 | | - function printNotices( ) { |
585 | | - if ( !$this->tasks ) { |
586 | | - $this->note( "Installation is complete, no pending tasks" ); |
587 | | - } |
588 | | - else { |
589 | | - $this->note( "" ); |
590 | | - $this->note( "PENDING TASKS:" ); |
591 | | - $this->note( "" ); |
592 | | - |
593 | | - foreach ( $this->tasks as $t ) { |
594 | | - $this->note ( "* " . $t ); |
595 | | - } |
596 | | - |
597 | | - $this->note( "" ); |
598 | | - } |
599 | | - |
600 | | - return true; |
601 | | - } |
602 | | - |
603 | | -} |
604 | | - |
605 | | -$tgt = isset ( $options['target'] ) ? $options['target'] : $IP; |
606 | | - |
607 | | -$repos = @$options['repository']; |
608 | | -if ( !$repos ) $repos = @$options['repos']; |
609 | | -if ( !$repos ) $repos = @$wgExtensionInstallerRepository; |
610 | | - |
611 | | -if ( !$repos && file_exists( "$tgt/.svn" ) && is_dir( "$tgt/.svn" ) ) { |
612 | | - $svn = file_get_contents( "$tgt/.svn/entries" ); |
613 | | - |
614 | | - $m = array(); |
615 | | - if ( preg_match( '!url="(.*?)"!', $svn, $m ) ) { |
616 | | - $repos = dirname( $m[1] ) . '/extensions'; |
617 | | - } |
618 | | -} |
619 | | - |
620 | | -if ( !$repos ) $repos = 'http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions'; |
621 | | - |
622 | | -if ( !isset( $args[0] ) && !@$options['list'] ) { |
623 | | - die( "USAGE: installExtension.php [options] <name> [source]\n" . |
624 | | - "OPTIONS: \n" . |
625 | | - " --list list available extensions. <name> is ignored / may be omitted.\n" . |
626 | | - " --repository <n> repository to fetch extensions from. May be a local directoy,\n" . |
627 | | - " an SVN repository or a HTTP directory\n" . |
628 | | - " --target <dir> mediawiki installation directory to use\n" . |
629 | | - " --nopatch don't create a patched LocalSettings.php\n" . |
630 | | - " --hotpatch patched LocalSettings.php directly (creates a backup)\n" . |
631 | | - "SOURCE: specifies the package source directly. If given, the repository is ignored.\n" . |
632 | | - " The source my be a local file (tgz or zip) or directory, the URL of a\n" . |
633 | | - " remote file (tgz or zip), or a SVN path.\n" |
634 | | - ); |
635 | | -} |
636 | | - |
637 | | -$repository = InstallerRepository::makeRepository( $repos ); |
638 | | - |
639 | | -if ( isset( $options['list'] ) ) { |
640 | | - $repository->printListing(); |
641 | | - exit( 0 ); |
642 | | -} |
643 | | - |
644 | | -$name = $args[0]; |
645 | | - |
646 | | -$src = isset( $args[1] ) ? $args[1] : $repository->getResource( $name ); |
647 | | - |
648 | | -# TODO: detect $source mismatching $name !! |
649 | | - |
650 | | -$mode = EXTINST_WRITEPATCH; |
651 | | -if ( isset( $options['nopatch'] ) || @$wgExtensionInstallerNoPatch ) { $mode = EXTINST_NOPATCH; } |
652 | | -else if ( isset( $options['hotpatch'] ) || @$wgExtensionInstallerHotPatch ) { $mode = EXTINST_HOTPATCH; } |
653 | | - |
654 | | -if ( !file_exists( "$tgt/LocalSettings.php" ) ) { |
655 | | - die( "can't find $tgt/LocalSettings.php\n" ); |
656 | | -} |
657 | | - |
658 | | -if ( $mode == EXTINST_HOTPATCH && !is_writable( "$tgt/LocalSettings.php" ) ) { |
659 | | - die( "can't write to $tgt/LocalSettings.php\n" ); |
660 | | -} |
661 | | - |
662 | | -if ( !file_exists( "$tgt/extensions" ) ) { |
663 | | - die( "can't find $tgt/extensions\n" ); |
664 | | -} |
665 | | - |
666 | | -if ( !is_writable( "$tgt/extensions" ) ) { |
667 | | - die( "can't write to $tgt/extensions\n" ); |
668 | | -} |
669 | | - |
670 | | -$installer = new ExtensionInstaller( $name, $src, $tgt ); |
671 | | - |
672 | | -$installer->note( "Installing extension {$installer->name} from {$installer->source->path} to {$installer->dir}" ); |
673 | | - |
674 | | -print "\n"; |
675 | | -print "\tTHIS TOOL IS EXPERIMENTAL!\n"; |
676 | | -print "\tEXPECT THE UNEXPECTED!\n"; |
677 | | -print "\n"; |
678 | | - |
679 | | -if ( !$installer->confirm( "continue" ) ) die( "aborted\n" ); |
680 | | - |
681 | | -$ok = $installer->fetchExtension(); |
682 | | - |
683 | | -if ( $ok ) $ok = $installer->patchLocalSettings( $mode ); |
684 | | - |
685 | | -if ( $ok ) $ok = $installer->printNotices(); |
686 | | - |
687 | | -if ( $ok ) $installer->note( "$name extension installed." ); |
688 | | - |