xref: /plugin/sqlite/admin.php (revision 5a212f8e61b602e1eb44e12787f0613caaee1414)
193d995e0SAndreas Gohr<?php
293d995e0SAndreas Gohr/**
393d995e0SAndreas Gohr * DokuWiki Plugin sqlite (Admin Component)
493d995e0SAndreas Gohr *
593d995e0SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
693d995e0SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
793d995e0SAndreas Gohr */
893d995e0SAndreas Gohr
9af0e7691SSzymon Olewniczakuse dokuwiki\Form\Form;
10ecc45fdcSSzymon Olewniczakuse dokuwiki\Form\InputElement;
113a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\QuerySaver;
123a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
133a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
14af0e7691SSzymon Olewniczak
1593d995e0SAndreas Gohr
163a56750bSAndreas Gohrclass admin_plugin_sqlite extends DokuWiki_Admin_Plugin
173a56750bSAndreas Gohr{
183a56750bSAndreas Gohr    /** @var SQLiteDB */
193a56750bSAndreas Gohr    protected $db = null;
2093d995e0SAndreas Gohr
213a56750bSAndreas Gohr    /** @var QuerySaver */
223a56750bSAndreas Gohr    protected $querySaver = null;
233a56750bSAndreas Gohr
243a56750bSAndreas Gohr    /** @inheritdoc */
253a56750bSAndreas Gohr    function getMenuSort()
263a56750bSAndreas Gohr    {
270542fc8eSKlap-in        return 500;
280542fc8eSKlap-in    }
290542fc8eSKlap-in
303a56750bSAndreas Gohr    /** @inheritdoc */
313a56750bSAndreas Gohr    function forAdminOnly()
323a56750bSAndreas Gohr    {
330542fc8eSKlap-in        return true;
340542fc8eSKlap-in    }
3593d995e0SAndreas Gohr
363a56750bSAndreas Gohr    /** @inheritdoc */
373a56750bSAndreas Gohr    function handle()
383a56750bSAndreas Gohr    {
39a34ef333SKlap-in        global $conf;
400dbd1d4cSSzymon Olewniczak        global $INPUT;
410dbd1d4cSSzymon Olewniczak
423a56750bSAndreas Gohr        // load database if given and security token is valid
433a56750bSAndreas Gohr        if ($INPUT->str('db') && checkSecurityToken()) {
443a56750bSAndreas Gohr            try {
453a56750bSAndreas Gohr                $this->db = new SQLiteDB($INPUT->str('db'), '');
463a56750bSAndreas Gohr                $this->querySaver = new QuerySaver($this->db->getDBName());
473a56750bSAndreas Gohr            } catch (Exception $e) {
483a56750bSAndreas Gohr                msg($e->getMessage(), -1);
493a56750bSAndreas Gohr                return;
50a34ef333SKlap-in            }
513a56750bSAndreas Gohr        }
520dbd1d4cSSzymon Olewniczak
533a56750bSAndreas Gohr        $cmd = $INPUT->extract('cmd')->str('cmd');
543a56750bSAndreas Gohr        switch ($cmd) {
553a56750bSAndreas Gohr            case 'export':
563a56750bSAndreas Gohr                $exportfile = $conf['tmpdir'] . '/' . $this->db->getDBName() . '.sql';
573a56750bSAndreas Gohr                $this->db->dumpToFile($exportfile);
580dbd1d4cSSzymon Olewniczak                header('Content-Type: text/sql');
593a56750bSAndreas Gohr                header('Content-Disposition: attachment; filename="' . $this->db->getDbName() . '.sql";');
603a56750bSAndreas Gohr                readfile($exportfile);
613a56750bSAndreas Gohr                unlink($exportfile);
620dbd1d4cSSzymon Olewniczak                exit(0);
633a56750bSAndreas Gohr            case 'import':
643a56750bSAndreas Gohr                $importfile = $_FILES['importfile']['tmp_name'];
650dbd1d4cSSzymon Olewniczak
663a56750bSAndreas Gohr                if (empty($importfile)) {
670dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_no_file'), -1);
680dbd1d4cSSzymon Olewniczak                    return;
690dbd1d4cSSzymon Olewniczak                }
700dbd1d4cSSzymon Olewniczak
717ddaad11SAndreas Gohr                $sql = Tools::SQLstring2array(file_get_contents($importfile));
723a56750bSAndreas Gohr                try {
7314e7409bSAndreas Gohr                    $this->db->getPdo()->beginTransaction();
747ddaad11SAndreas Gohr                    foreach ($sql as $s) {
757ddaad11SAndreas Gohr                        $this->db->exec($s);
767ddaad11SAndreas Gohr                    }
7714e7409bSAndreas Gohr                    $this->db->getPdo()->commit();
780dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_success'), 1);
793a56750bSAndreas Gohr                } catch (Exception $e) {
8014e7409bSAndreas Gohr                    $this->db->getPdo()->rollBack();
817ddaad11SAndreas Gohr                    msg(hsc($e->getMessage()), -1);
820dbd1d4cSSzymon Olewniczak                }
833a56750bSAndreas Gohr                break;
843a56750bSAndreas Gohr            case 'save_query':
853a56750bSAndreas Gohr                $this->querySaver->saveQuery($INPUT->str('name'), $INPUT->str('sql'));
863a56750bSAndreas Gohr                break;
873a56750bSAndreas Gohr            case 'delete_query':
883a56750bSAndreas Gohr                $this->querySaver->deleteQuery($INPUT->str('name'));
893a56750bSAndreas Gohr                break;
90a34ef333SKlap-in        }
9193d995e0SAndreas Gohr    }
9293d995e0SAndreas Gohr
933a56750bSAndreas Gohr    /** @inheritdoc */
943a56750bSAndreas Gohr    function html()
953a56750bSAndreas Gohr    {
96271f9caeSSzymon Olewniczak        global $INPUT;
9793d995e0SAndreas Gohr
9893d995e0SAndreas Gohr        echo $this->locale_xhtml('intro');
993a56750bSAndreas Gohr        if (!$this->db) return;
10093d995e0SAndreas Gohr
10193d995e0SAndreas Gohr
1023a56750bSAndreas Gohr        echo '<h2>' . $this->getLang('db') . ' "' . hsc($this->db->getDbName()) . '"</h2>';
10393d995e0SAndreas Gohr        echo '<div class="level2">';
10493d995e0SAndreas Gohr
1053a56750bSAndreas Gohr        echo '<div class="commands">';
1063a56750bSAndreas Gohr        $this->showCommands();
1073a56750bSAndreas Gohr        $this->showSavedQueries();
1083a56750bSAndreas Gohr        echo '</div>';
109a34ef333SKlap-in
1103a56750bSAndreas Gohr        // query form
1113a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink()]);
1123a56750bSAndreas Gohr        $form->addClass('sqliteplugin');
113ecc45fdcSSzymon Olewniczak        $form->addFieldsetOpen('SQL Command');
1143a56750bSAndreas Gohr        $form->addTextarea('sql')->addClass('edit');
1153a56750bSAndreas Gohr        $form->addButton('submit', $this->getLang('btn_execute'))->attr('type', 'submit');
116ecc45fdcSSzymon Olewniczak        $form->addTextInput('name', $this->getLang('query_name'));
1173a56750bSAndreas Gohr        $form->addButton('cmd[save_query]', $this->getLang('save_query'))->attr('type', 'submit');
118ecc45fdcSSzymon Olewniczak        $form->addFieldsetClose();
1193a56750bSAndreas Gohr        echo $form->toHTML();
12093d995e0SAndreas Gohr
1213a56750bSAndreas Gohr        // results
1223a56750bSAndreas Gohr        if ($INPUT->has('sql')) $this->showQueryResults($INPUT->str('sql'));
123271f9caeSSzymon Olewniczak        echo '</div>';
124271f9caeSSzymon Olewniczak    }
125271f9caeSSzymon Olewniczak
126*5a212f8eSAndreas Gohr    /**
127*5a212f8eSAndreas Gohr     * List all available databases in the TOC
128*5a212f8eSAndreas Gohr     *
129*5a212f8eSAndreas Gohr     * @inheritdoc
130*5a212f8eSAndreas Gohr     */
131*5a212f8eSAndreas Gohr    public function getTOC()
1323a56750bSAndreas Gohr    {
13393d995e0SAndreas Gohr        global $conf;
13493d995e0SAndreas Gohr        global $ID;
13593d995e0SAndreas Gohr
136*5a212f8eSAndreas Gohr        $toc = [];
137*5a212f8eSAndreas Gohr        $toc[] = [
138*5a212f8eSAndreas Gohr            'link' => wl($ID, ['do' => 'admin', 'page' => 'sqlite']),
139*5a212f8eSAndreas Gohr            'title' => $this->getLang('db') . ':',
14093d995e0SAndreas Gohr            'level' => 1,
14193d995e0SAndreas Gohr            'type' => 'ul',
142*5a212f8eSAndreas Gohr        ];
143*5a212f8eSAndreas Gohr        $dbfiles = glob($conf['metadir'] . '/*.sqlite3');
14440698f67SKlap-in        if (is_array($dbfiles)) foreach ($dbfiles as $file) {
145*5a212f8eSAndreas Gohr            $db = basename($file, '.sqlite3');
14640698f67SKlap-in            $toc[] = array(
147*5a212f8eSAndreas Gohr                'link' => wl($ID, array('do' => 'admin', 'page' => 'sqlite', 'db' => $db, 'sectok' => getSecurityToken())),
148*5a212f8eSAndreas Gohr                'title' => $db,
14940698f67SKlap-in                'level' => 2,
15040698f67SKlap-in                'type' => 'ul',
15140698f67SKlap-in            );
15240698f67SKlap-in        }
15393d995e0SAndreas Gohr
15493d995e0SAndreas Gohr        return $toc;
15593d995e0SAndreas Gohr    }
1563a56750bSAndreas Gohr
1573a56750bSAndreas Gohr    /**
1583a56750bSAndreas Gohr     * Execute and display the results of the given SQL query
1593a56750bSAndreas Gohr     *
1603a56750bSAndreas Gohr     * multiple queries can be given separated by semicolons
1613a56750bSAndreas Gohr     *
1623a56750bSAndreas Gohr     * @param string $sql
1633a56750bSAndreas Gohr     */
1643a56750bSAndreas Gohr    protected function showQueryResults($sql)
1653a56750bSAndreas Gohr    {
1663a56750bSAndreas Gohr        echo '<h3 id="scroll__here">Query results</h3>';
1673a56750bSAndreas Gohr
1683a56750bSAndreas Gohr        $sql = Tools::SQLstring2array($sql);
1693a56750bSAndreas Gohr        foreach ($sql as $s) {
1703a56750bSAndreas Gohr            $s = preg_replace('!^\s*--.*$!m', '', $s);
1713a56750bSAndreas Gohr            $s = trim($s);
1723a56750bSAndreas Gohr            if (!$s) continue;
1733a56750bSAndreas Gohr
1743a56750bSAndreas Gohr            try {
1753a56750bSAndreas Gohr                $time_start = microtime(true);
1763a56750bSAndreas Gohr                $result = $this->db->queryAll($s);
1773a56750bSAndreas Gohr                $time_end = microtime(true);
1783a56750bSAndreas Gohr            } catch (Exception $e) {
1793a56750bSAndreas Gohr                msg($e->getMessage(), -1);
1803a56750bSAndreas Gohr                continue;
18193d995e0SAndreas Gohr            }
18293d995e0SAndreas Gohr
1833a56750bSAndreas Gohr            $time = $time_end - $time_start;
1843a56750bSAndreas Gohr            $cnt = count($result);
1853a56750bSAndreas Gohr            msg($cnt . ' affected rows in ' . $this->microtimeToSeconds($time) . ' seconds', 1);
1863a56750bSAndreas Gohr            if (!$cnt) continue;
1873a56750bSAndreas Gohr
1883a56750bSAndreas Gohr            echo '<div>';
1893a56750bSAndreas Gohr            $ths = array_keys($result[0]);
1903a56750bSAndreas Gohr            echo '<table class="inline">';
1913a56750bSAndreas Gohr            echo '<tr>';
1923a56750bSAndreas Gohr            foreach ($ths as $th) {
1933a56750bSAndreas Gohr                echo '<th>' . hsc($th) . '</th>';
1943a56750bSAndreas Gohr            }
1953a56750bSAndreas Gohr            echo '</tr>';
1963a56750bSAndreas Gohr            foreach ($result as $row) {
1973a56750bSAndreas Gohr                echo '<tr>';
1983a56750bSAndreas Gohr                $tds = array_values($row);
1993a56750bSAndreas Gohr                foreach ($tds as $td) {
2003a56750bSAndreas Gohr                    if ($td === null) $td = '␀';
2013a56750bSAndreas Gohr                    echo '<td>' . hsc($td) . '</td>';
2023a56750bSAndreas Gohr                }
2033a56750bSAndreas Gohr                echo '</tr>';
2043a56750bSAndreas Gohr            }
2053a56750bSAndreas Gohr            echo '</table>';
2063a56750bSAndreas Gohr            echo '</div>';
2073a56750bSAndreas Gohr        }
2083a56750bSAndreas Gohr    }
2093a56750bSAndreas Gohr
2103a56750bSAndreas Gohr
2113a56750bSAndreas Gohr    /**
2123a56750bSAndreas Gohr     * Convert a microtime() value to a string in seconds
2133a56750bSAndreas Gohr     *
2143a56750bSAndreas Gohr     * @param float $time
2153a56750bSAndreas Gohr     * @return string
2163a56750bSAndreas Gohr     */
2173a56750bSAndreas Gohr    protected function microtimeToSeconds($time)
2183a56750bSAndreas Gohr    {
2193a56750bSAndreas Gohr        return ($time < 0.0001 ? substr($time, 0, 5) . substr($time, -3) : substr($time, 0, 7));
2203a56750bSAndreas Gohr    }
2213a56750bSAndreas Gohr
2223a56750bSAndreas Gohr    /**
2233a56750bSAndreas Gohr     * Construct a link to the sqlite admin page with the given additional parameters
2243a56750bSAndreas Gohr     *
2253a56750bSAndreas Gohr     * Basically a wrapper around wl() with some defaults
2263a56750bSAndreas Gohr     *
2273a56750bSAndreas Gohr     * @param string[] $params
2283a56750bSAndreas Gohr     * @param bool $form for use in form action?
2293a56750bSAndreas Gohr     * @return string
2303a56750bSAndreas Gohr     */
2313a56750bSAndreas Gohr    protected function selfLink($form = true, $params = [])
2323a56750bSAndreas Gohr    {
2333a56750bSAndreas Gohr        global $ID;
2343a56750bSAndreas Gohr        $params = array_merge(
2353a56750bSAndreas Gohr            [
2363a56750bSAndreas Gohr                'do' => 'admin',
2373a56750bSAndreas Gohr                'page' => 'sqlite',
2383a56750bSAndreas Gohr                'db' => $this->db ? $this->db->getDBName() : '',
2393a56750bSAndreas Gohr                'sectok' => getSecurityToken(),
2403a56750bSAndreas Gohr            ], $params
2413a56750bSAndreas Gohr        );
2423a56750bSAndreas Gohr
2433a56750bSAndreas Gohr        return wl($ID, $params, false, $form ? '&' : '&amp;');
2443a56750bSAndreas Gohr    }
2453a56750bSAndreas Gohr
2463a56750bSAndreas Gohr    /**
2473a56750bSAndreas Gohr     * Display the standard actions for a database
2483a56750bSAndreas Gohr     */
2493a56750bSAndreas Gohr    protected function showCommands()
2503a56750bSAndreas Gohr    {
2513a56750bSAndreas Gohr        $commands = [
2523a56750bSAndreas Gohr            'dbversion' => [
2533a56750bSAndreas Gohr                'sql' => 'SELECT val FROM opts WHERE opt=\'dbversion\'',
2543a56750bSAndreas Gohr            ],
2553a56750bSAndreas Gohr            'table' => [
2563a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'table\' ORDER BY name',
2573a56750bSAndreas Gohr            ],
2583a56750bSAndreas Gohr            'index' => [
2593a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'index\' ORDER BY name',
2603a56750bSAndreas Gohr            ],
2613a56750bSAndreas Gohr            'export' => [
2623a56750bSAndreas Gohr                'cmd' => 'export'
2633a56750bSAndreas Gohr            ],
2643a56750bSAndreas Gohr        ];
2653a56750bSAndreas Gohr
2663a56750bSAndreas Gohr        // import form
2673a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink(), 'enctype' => 'multipart/form-data', 'method' => 'post']);
2683a56750bSAndreas Gohr        $form->addElement(new InputElement('file', 'importfile'));
2693a56750bSAndreas Gohr        $form->addButton('cmd[import]', $this->getLang('import'));
2703a56750bSAndreas Gohr
2713a56750bSAndreas Gohr        // output as a list
2723a56750bSAndreas Gohr        echo '<ul>';
2733a56750bSAndreas Gohr        foreach ($commands as $label => $command) {
2743a56750bSAndreas Gohr            echo '<li><div class="li">';
2753a56750bSAndreas Gohr            echo '<a href="' . $this->selfLink(false, $command) . '">' . $this->getLang($label) . '</a>';
2763a56750bSAndreas Gohr            echo '</div></li>';
2773a56750bSAndreas Gohr        }
2783a56750bSAndreas Gohr        echo '<li><div class="li">';
2793a56750bSAndreas Gohr        echo $form->toHTML();
2803a56750bSAndreas Gohr        echo '</div></li>';
2813a56750bSAndreas Gohr        echo '</ul>';
2823a56750bSAndreas Gohr    }
2833a56750bSAndreas Gohr
2843a56750bSAndreas Gohr    /**
2853a56750bSAndreas Gohr     * FIXME needs to be cleaned up
2863a56750bSAndreas Gohr     */
2873a56750bSAndreas Gohr    public function showSavedQueries()
2883a56750bSAndreas Gohr    {
2893a56750bSAndreas Gohr        $queries = $this->querySaver->getQueries();
2903a56750bSAndreas Gohr        if (!$queries) return;
2913a56750bSAndreas Gohr
2923a56750bSAndreas Gohr        echo '<ul>';
2933a56750bSAndreas Gohr        foreach ($queries as $query) {
2943a56750bSAndreas Gohr            $link = $this->selfLink(false, ['sql' => $query['sql']]);
2953a56750bSAndreas Gohr            $del = $this->selfLink(false, ['cmd' => 'delete_query', 'name' => $query['name']]);
2963a56750bSAndreas Gohr
2973a56750bSAndreas Gohr            echo '<li><div class="li">';
2983a56750bSAndreas Gohr            echo '<a href="' . $link . '">' . hsc($query['name']) . '</a>';
2993a56750bSAndreas Gohr            echo ' [<a href="' . $del . '">' . $this->getLang('delete_query') . '</a>]';
3003a56750bSAndreas Gohr            echo '</div></li>';
3013a56750bSAndreas Gohr        }
3023a56750bSAndreas Gohr        echo '</ul>';
3033a56750bSAndreas Gohr    }
3043a56750bSAndreas Gohr}
305