xref: /plugin/upgrade/admin.php (revision 75e9d1640bef394d89b378cdf37d8e9c5c0f8a62)
134aae6dbSAndreas Gohr<?php
234aae6dbSAndreas Gohr/**
334aae6dbSAndreas Gohr * DokuWiki Plugin update (Admin Component)
434aae6dbSAndreas Gohr *
534aae6dbSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
634aae6dbSAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
734aae6dbSAndreas Gohr */
834aae6dbSAndreas Gohr
934aae6dbSAndreas Gohr// must be run within Dokuwiki
1034aae6dbSAndreas Gohrif (!defined('DOKU_INC')) die();
1134aae6dbSAndreas Gohr
1234aae6dbSAndreas Gohrif (!defined('DOKU_LF')) define('DOKU_LF', "\n");
1334aae6dbSAndreas Gohrif (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
1434aae6dbSAndreas Gohrif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
1534aae6dbSAndreas Gohr
1634aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'admin.php';
1734aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'update/VerboseTarLib.class.php';
1834aae6dbSAndreas Gohr
1934aae6dbSAndreas Gohrclass admin_plugin_update extends DokuWiki_Admin_Plugin {
2034aae6dbSAndreas Gohr    private $tgzurl;
2134aae6dbSAndreas Gohr    private $tgzfile;
2234aae6dbSAndreas Gohr    private $tgzdir;
2334aae6dbSAndreas Gohr
2434aae6dbSAndreas Gohr    public function __construct(){
2534aae6dbSAndreas Gohr        global $conf;
2634aae6dbSAndreas Gohr
2734aae6dbSAndreas Gohr        $branch = 'stable';
2834aae6dbSAndreas Gohr
2934aae6dbSAndreas Gohr        $this->tgzurl  = 'http://github.com/splitbrain/dokuwiki/tarball/'.$branch;
3034aae6dbSAndreas Gohr        $this->tgzfile = $conf['tmpdir'].'/dokuwiki-update.tgz';
3134aae6dbSAndreas Gohr        $this->tgzdir  = $conf['tmpdir'].'/dokuwiki-update/';
3234aae6dbSAndreas Gohr    }
3334aae6dbSAndreas Gohr
3434aae6dbSAndreas Gohr    function getMenuSort() { return 555; }
3534aae6dbSAndreas Gohr
3634aae6dbSAndreas Gohr    function handle() {
3734aae6dbSAndreas Gohr    }
3834aae6dbSAndreas Gohr
3934aae6dbSAndreas Gohr    public function html() {
4034aae6dbSAndreas Gohr        $abrt = false;
4134aae6dbSAndreas Gohr        $next = false;
4234aae6dbSAndreas Gohr
4334aae6dbSAndreas Gohr        echo '<h1>' . $this->getLang('menu') . '</h1>';
4434aae6dbSAndreas Gohr#FIXME check and abort on safemode
4534aae6dbSAndreas Gohr
46*75e9d164SAndreas Gohr        $this->_say('<div id="plugin__update">');
47*75e9d164SAndreas Gohr        // enable auto scroll
48*75e9d164SAndreas Gohr        ?>
49*75e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
50*75e9d164SAndreas Gohr            var plugin_update = window.setInterval(function(){
51*75e9d164SAndreas Gohr                var obj = $('plugin__update');
52*75e9d164SAndreas Gohr                if(obj) obj.scrollTop = obj.scrollHeight;
53*75e9d164SAndreas Gohr            },25);
54*75e9d164SAndreas Gohr        </script>
55*75e9d164SAndreas Gohr        <?php
56*75e9d164SAndreas Gohr
57*75e9d164SAndreas Gohr        // handle current step
5834aae6dbSAndreas Gohr        $this->_stepit(&$abrt, &$next);
5934aae6dbSAndreas Gohr
60*75e9d164SAndreas Gohr        // disable auto scroll
61*75e9d164SAndreas Gohr        ?>
62*75e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
63*75e9d164SAndreas Gohr            window.setTimeout(function(){
64*75e9d164SAndreas Gohr                window.clearInterval(plugin_update);
65*75e9d164SAndreas Gohr            },50);
66*75e9d164SAndreas Gohr        </script>
67*75e9d164SAndreas Gohr        <?php
68*75e9d164SAndreas Gohr        $this->_say('</div>');
69*75e9d164SAndreas Gohr
7034aae6dbSAndreas Gohr#FIXME add security check
71*75e9d164SAndreas Gohr        echo '<form action="" method="get" id="plugin__update_form">';
7234aae6dbSAndreas Gohr        echo '<input type="hidden" name="do" value="admin" />';
7334aae6dbSAndreas Gohr        echo '<input type="hidden" name="page" value="update" />';
74*75e9d164SAndreas Gohr        if($next) echo '<input type="submit" name="step['.$next.']" value="Continue" class="button continue" />';
75*75e9d164SAndreas Gohr        if($abrt) echo '<input type="submit" name="step[cancel]" value="Abort" class="button abort" />';
7634aae6dbSAndreas Gohr        echo '</form>';
7734aae6dbSAndreas Gohr    }
7834aae6dbSAndreas Gohr
7934aae6dbSAndreas Gohr    private function _stepit(&$abrt, &$next){
8034aae6dbSAndreas Gohr        if(isset($_REQUEST['step']) && is_array($_REQUEST['step'])){
8134aae6dbSAndreas Gohr            $step = array_shift(array_keys($_REQUEST['step']));
8234aae6dbSAndreas Gohr        }else{
8334aae6dbSAndreas Gohr            $step = '';
8434aae6dbSAndreas Gohr        }
8534aae6dbSAndreas Gohr
8634aae6dbSAndreas Gohr        if($step == 'cancel'){
8734aae6dbSAndreas Gohr            # cleanup
8834aae6dbSAndreas Gohr            @unlink($this->tgzfile);
896b2d1b30SAndreas Gohr            $this->_rdel($this->tgzdir);
9034aae6dbSAndreas Gohr            $step = '';
9134aae6dbSAndreas Gohr        }
9234aae6dbSAndreas Gohr
9334aae6dbSAndreas Gohr        if($step){
9434aae6dbSAndreas Gohr            $abrt = true;
9534aae6dbSAndreas Gohr            $next = false;
9634aae6dbSAndreas Gohr            if(!file_exists($this->tgzfile)){
9734aae6dbSAndreas Gohr                if($this->_step_download()) $next = 'unpack';
9834aae6dbSAndreas Gohr            }elseif(!is_dir($this->tgzdir)){
9934aae6dbSAndreas Gohr                if($this->_step_unpack()) $next = 'check';
10034aae6dbSAndreas Gohr            }elseif($step != 'upgrade'){
10134aae6dbSAndreas Gohr                if($this->_step_copy(true)) $next = 'upgrade';
10234aae6dbSAndreas Gohr            }elseif($step == 'upgrade'){
10334aae6dbSAndreas Gohr                if($this->_step_copy(false)) $next = 'cancel';
10434aae6dbSAndreas Gohr            }else{
105*75e9d164SAndreas Gohr                echo 'uhm. what happened? where am I? This should not happen';
10634aae6dbSAndreas Gohr            }
10734aae6dbSAndreas Gohr        }else{
10834aae6dbSAndreas Gohr            # first time run, show intro
10934aae6dbSAndreas Gohr            echo $this->locale_xhtml('step0');
11034aae6dbSAndreas Gohr            $abrt = false;
11134aae6dbSAndreas Gohr            $next = 'download';
11234aae6dbSAndreas Gohr        }
11334aae6dbSAndreas Gohr    }
11434aae6dbSAndreas Gohr
11534aae6dbSAndreas Gohr    private function _say(){
11634aae6dbSAndreas Gohr        $args = func_get_args();
11734aae6dbSAndreas Gohr        echo vsprintf(array_shift($args)."<br />\n",$args);
11834aae6dbSAndreas Gohr        flush();
11934aae6dbSAndreas Gohr        ob_flush();
12034aae6dbSAndreas Gohr    }
12134aae6dbSAndreas Gohr
1226b2d1b30SAndreas Gohr    /**
1236b2d1b30SAndreas Gohr     * Recursive delete
1246b2d1b30SAndreas Gohr     *
1259285faa5SAndreas Gohr     * @author Jon Hassall
1269285faa5SAndreas Gohr     * @link http://de.php.net/manual/en/function.unlink.php#87045
1276b2d1b30SAndreas Gohr     */
1289285faa5SAndreas Gohr    private function _rdel($dir) {
1299285faa5SAndreas Gohr        if(!$dh = @opendir($dir)) {
1309285faa5SAndreas Gohr            return;
1319285faa5SAndreas Gohr        }
1329285faa5SAndreas Gohr        while (false !== ($obj = readdir($dh))) {
1339285faa5SAndreas Gohr            if($obj == '.' || $obj == '..') continue;
1349285faa5SAndreas Gohr
1359285faa5SAndreas Gohr            if (!@unlink($dir . '/' . $obj)) {
1369285faa5SAndreas Gohr                $this->_rdel($dir.'/'.$obj);
1379285faa5SAndreas Gohr            }
1389285faa5SAndreas Gohr        }
1399285faa5SAndreas Gohr        closedir($dh);
1409285faa5SAndreas Gohr        @rmdir($dir);
1416b2d1b30SAndreas Gohr    }
1426b2d1b30SAndreas Gohr
14334aae6dbSAndreas Gohr    private function _step_download(){
14434aae6dbSAndreas Gohr        $this->_say('Downloading from %s',$this->tgzurl);
14534aae6dbSAndreas Gohr
14634aae6dbSAndreas Gohr        @set_time_limit(120);
14734aae6dbSAndreas Gohr        @ignore_user_abort();
14834aae6dbSAndreas Gohr
14934aae6dbSAndreas Gohr        $http = new DokuHTTPClient();
15034aae6dbSAndreas Gohr        $http->timeout = 120;
15134aae6dbSAndreas Gohr        $data = $http->get($this->tgzurl);
15234aae6dbSAndreas Gohr
15334aae6dbSAndreas Gohr        if(!$data){
15434aae6dbSAndreas Gohr            $this->_say($http->error);
15534aae6dbSAndreas Gohr            $this->_say("Download failed.");
15634aae6dbSAndreas Gohr            return false;
15734aae6dbSAndreas Gohr        }
15834aae6dbSAndreas Gohr
15934aae6dbSAndreas Gohr        $this->_say('Received %d bytes',strlen($data));
16034aae6dbSAndreas Gohr
16134aae6dbSAndreas Gohr        if(!io_saveFile($this->tgzfile,$data)){
16234aae6dbSAndreas Gohr            $this->_say("Failed to save download.");
16334aae6dbSAndreas Gohr            return false;
16434aae6dbSAndreas Gohr        }
16534aae6dbSAndreas Gohr
16634aae6dbSAndreas Gohr        return true;
16734aae6dbSAndreas Gohr    }
16834aae6dbSAndreas Gohr
16934aae6dbSAndreas Gohr    private function _step_unpack(){
17034aae6dbSAndreas Gohr        global $conf;
171*75e9d164SAndreas Gohr        $this->_say('<b>Extracting the archive...</b>');
17234aae6dbSAndreas Gohr
17334aae6dbSAndreas Gohr        @set_time_limit(120);
17434aae6dbSAndreas Gohr        @ignore_user_abort();
17534aae6dbSAndreas Gohr
17634aae6dbSAndreas Gohr        $tar = new VerboseTarLib($this->tgzfile);
17734aae6dbSAndreas Gohr        if($tar->_initerror < 0){
17834aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($tar->_initerror));
17934aae6dbSAndreas Gohr            $this->_say('Extraction failed on init');
18034aae6dbSAndreas Gohr            return false;
18134aae6dbSAndreas Gohr        }
18234aae6dbSAndreas Gohr
18334aae6dbSAndreas Gohr        $ok = $tar->Extract(VerboseTarLib::FULL_ARCHIVE,$this->tgzdir,1,$conf['fmode'],'/^(_cs|_test|\.gitignore)/');
18434aae6dbSAndreas Gohr        if($ok < 1){
18534aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($ok));
18634aae6dbSAndreas Gohr            $this->_say('Extraction failed');
18734aae6dbSAndreas Gohr            return false;
18834aae6dbSAndreas Gohr        }
18934aae6dbSAndreas Gohr
19034aae6dbSAndreas Gohr        $this->_say('Extraction done.');
191738c0102SAndreas Gohr
192738c0102SAndreas Gohr        $this->_say('Version <b>%s</b> ready to install. Your current version is <b>%s</b>.',
193738c0102SAndreas Gohr                    hsc(file_get_contents($this->tgzdir.'/VERSION')),
194738c0102SAndreas Gohr                    getVersion());
19534aae6dbSAndreas Gohr        return true;
19634aae6dbSAndreas Gohr    }
19734aae6dbSAndreas Gohr
19834aae6dbSAndreas Gohr    private function _step_copy($dryrun=true){
19934aae6dbSAndreas Gohr        $ok = $this->_traverse('',$dryrun);
20034aae6dbSAndreas Gohr        if($dryrun){
20134aae6dbSAndreas Gohr            if($ok){
20234aae6dbSAndreas Gohr                $this->_say('<b>All files are writable, ready to upgrade</b>');
20334aae6dbSAndreas Gohr            }else{
20434aae6dbSAndreas Gohr                $this->_say('<b>Some files aren\'t writable. Uprade not possible.</b>');
20534aae6dbSAndreas Gohr            }
20634aae6dbSAndreas Gohr        }else{
20734aae6dbSAndreas Gohr            if($ok){
20834aae6dbSAndreas Gohr                $this->_say('<b>All files upgraded successfully</b>');
20934aae6dbSAndreas Gohr
21034aae6dbSAndreas Gohr                #FIXME delete unused files
21134aae6dbSAndreas Gohr            }else{
21234aae6dbSAndreas Gohr                $this->_say('<b>Some files couldn\'t be upgraded. Uh-oh. Better check manually.</b>');
21334aae6dbSAndreas Gohr            }
21434aae6dbSAndreas Gohr        }
21534aae6dbSAndreas Gohr        return $ok;
21634aae6dbSAndreas Gohr    }
21734aae6dbSAndreas Gohr
21834aae6dbSAndreas Gohr    private function _traverse($dir,$dryrun){
21934aae6dbSAndreas Gohr        $base = $this->tgzdir;
22034aae6dbSAndreas Gohr        $ok = true;
22134aae6dbSAndreas Gohr
22234aae6dbSAndreas Gohr        $dh = @opendir($base.'/'.$dir);
22334aae6dbSAndreas Gohr        if(!$dh) return;
22434aae6dbSAndreas Gohr        while(($file = readdir($dh)) !== false){
22534aae6dbSAndreas Gohr            if($file == '.' || $file == '..') continue;
22634aae6dbSAndreas Gohr            $from = "$base/$dir/$file";
22734aae6dbSAndreas Gohr            $to   = DOKU_INC."$dir/$file";
22834aae6dbSAndreas Gohr
22934aae6dbSAndreas Gohr            if(is_dir($from)){
23034aae6dbSAndreas Gohr                if($dryrun){
23134aae6dbSAndreas Gohr                    // just check for writability
23234aae6dbSAndreas Gohr                    if(!is_dir($to)){
23334aae6dbSAndreas Gohr                        if(is_dir(dirname($to)) && !is_writable(dirname($to))){
23434aae6dbSAndreas Gohr                            $this->_say("<b>%s is not writable</b>",hsc("$dir/$file"));
23534aae6dbSAndreas Gohr                            $ok = false;
23634aae6dbSAndreas Gohr                        }
23734aae6dbSAndreas Gohr                    }
23834aae6dbSAndreas Gohr                }
23934aae6dbSAndreas Gohr
24034aae6dbSAndreas Gohr                // recursion
24134aae6dbSAndreas Gohr                if(!$this->_traverse("$dir/$file",$dryrun)){
24234aae6dbSAndreas Gohr                    $ok = false;
24334aae6dbSAndreas Gohr                }
24434aae6dbSAndreas Gohr            }else{
245f2fa6d10SAndreas Gohr                $fmd5 = md5(@file_get_contents($from));
246f2fa6d10SAndreas Gohr                $tmd5 = md5(@file_get_contents($to));
24734aae6dbSAndreas Gohr                if($fmd5 != $tmd5){
24834aae6dbSAndreas Gohr                    if($dryrun){
24934aae6dbSAndreas Gohr                        // just check for writability
25034aae6dbSAndreas Gohr                        if( (file_exists($to) && !is_writable($to)) ||
25134aae6dbSAndreas Gohr                            (!file_exists($to) && is_dir(dirname($to)) && !is_writable(dirname($to))) ){
25234aae6dbSAndreas Gohr
25334aae6dbSAndreas Gohr                            $this->_say("<b>%s is not writable</b>",hsc("$dir/$file"));
25434aae6dbSAndreas Gohr                            $ok = false;
25534aae6dbSAndreas Gohr                        }else{
25634aae6dbSAndreas Gohr                            $this->_say("%s needs update",hsc("$dir/$file"));
25734aae6dbSAndreas Gohr                        }
25834aae6dbSAndreas Gohr                    }else{
25934aae6dbSAndreas Gohr                        // check dir
26034aae6dbSAndreas Gohr                        if(io_mkdir_p(dirname($to))){
26134aae6dbSAndreas Gohr                            // copy
26234aae6dbSAndreas Gohr                            if(!copy($from,$to)){
26334aae6dbSAndreas Gohr                                $this->_say("<b>%s couldn't be copied</b>",hsc("$dir/$file"));
26434aae6dbSAndreas Gohr                                $ok = false;
26534aae6dbSAndreas Gohr                            }else{
26634aae6dbSAndreas Gohr                                $this->_say("%s updated",hsc("$dir/$file"));
26734aae6dbSAndreas Gohr                            }
26834aae6dbSAndreas Gohr                        }else{
26934aae6dbSAndreas Gohr                            $this->_say("<b>failed to create %s</b>",hsc("$dir"));
27034aae6dbSAndreas Gohr                            $ok = false;
27134aae6dbSAndreas Gohr                        }
27234aae6dbSAndreas Gohr                    }
27334aae6dbSAndreas Gohr                }
27434aae6dbSAndreas Gohr            }
27534aae6dbSAndreas Gohr        }
27634aae6dbSAndreas Gohr        closedir($dh);
27734aae6dbSAndreas Gohr        return $ok;
27834aae6dbSAndreas Gohr    }
27934aae6dbSAndreas Gohr}
28034aae6dbSAndreas Gohr
28134aae6dbSAndreas Gohr// vim:ts=4:sw=4:et:enc=utf-8:
282