r69916 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r69915‎ | r69916 | r69917 >
Date:21:26, 25 July 2010
Author:demon
Status:ok
Tags:
Comment:
Remove useless ext installation script. All this stuff is being redone as part of GSoC and nothing here is useful at all
Modified paths:
  • /trunk/phase3/maintenance/installExtension.php (deleted) (history)

Diff [purge]

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 -

Status & tagging log