xref: /plugin/sqlite/admin.php (revision 9038f70750bfbfe3c491d5027f066b50214e269d)
193d995e0SAndreas Gohr<?php
293d995e0SAndreas Gohr
3af0e7691SSzymon Olewniczakuse dokuwiki\Form\Form;
4ecc45fdcSSzymon Olewniczakuse dokuwiki\Form\InputElement;
53a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\QuerySaver;
63a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
73a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
8af0e7691SSzymon Olewniczak
9*9038f707SAndreas Gohr/**
10*9038f707SAndreas Gohr * DokuWiki Plugin sqlite (Admin Component)
11*9038f707SAndreas Gohr *
12*9038f707SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
13*9038f707SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
14*9038f707SAndreas Gohr */
153a56750bSAndreas Gohrclass admin_plugin_sqlite extends DokuWiki_Admin_Plugin
163a56750bSAndreas Gohr{
173a56750bSAndreas Gohr    /** @var SQLiteDB */
183a56750bSAndreas Gohr    protected $db = null;
1993d995e0SAndreas Gohr
203a56750bSAndreas Gohr    /** @var QuerySaver */
213a56750bSAndreas Gohr    protected $querySaver = null;
223a56750bSAndreas Gohr
233a56750bSAndreas Gohr    /** @inheritdoc */
24*9038f707SAndreas Gohr    public function getMenuSort()
253a56750bSAndreas Gohr    {
260542fc8eSKlap-in        return 500;
270542fc8eSKlap-in    }
280542fc8eSKlap-in
293a56750bSAndreas Gohr    /** @inheritdoc */
30*9038f707SAndreas Gohr    public function forAdminOnly()
313a56750bSAndreas Gohr    {
320542fc8eSKlap-in        return true;
330542fc8eSKlap-in    }
3493d995e0SAndreas Gohr
353a56750bSAndreas Gohr    /** @inheritdoc */
36*9038f707SAndreas Gohr    public function handle()
373a56750bSAndreas Gohr    {
38a34ef333SKlap-in        global $conf;
390dbd1d4cSSzymon Olewniczak        global $INPUT;
400dbd1d4cSSzymon Olewniczak
413a56750bSAndreas Gohr        // load database if given and security token is valid
423a56750bSAndreas Gohr        if ($INPUT->str('db') && checkSecurityToken()) {
433a56750bSAndreas Gohr            try {
443a56750bSAndreas Gohr                $this->db = new SQLiteDB($INPUT->str('db'), '');
453a56750bSAndreas Gohr                $this->querySaver = new QuerySaver($this->db->getDBName());
463a56750bSAndreas Gohr            } catch (Exception $e) {
473a56750bSAndreas Gohr                msg($e->getMessage(), -1);
483a56750bSAndreas Gohr                return;
49a34ef333SKlap-in            }
503a56750bSAndreas Gohr        }
510dbd1d4cSSzymon Olewniczak
523a56750bSAndreas Gohr        $cmd = $INPUT->extract('cmd')->str('cmd');
533a56750bSAndreas Gohr        switch ($cmd) {
543a56750bSAndreas Gohr            case 'export':
553a56750bSAndreas Gohr                $exportfile = $conf['tmpdir'] . '/' . $this->db->getDBName() . '.sql';
563a56750bSAndreas Gohr                $this->db->dumpToFile($exportfile);
570dbd1d4cSSzymon Olewniczak                header('Content-Type: text/sql');
583a56750bSAndreas Gohr                header('Content-Disposition: attachment; filename="' . $this->db->getDbName() . '.sql";');
593a56750bSAndreas Gohr                readfile($exportfile);
603a56750bSAndreas Gohr                unlink($exportfile);
610dbd1d4cSSzymon Olewniczak                exit(0);
623a56750bSAndreas Gohr            case 'import':
633a56750bSAndreas Gohr                $importfile = $_FILES['importfile']['tmp_name'];
640dbd1d4cSSzymon Olewniczak
653a56750bSAndreas Gohr                if (empty($importfile)) {
660dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_no_file'), -1);
670dbd1d4cSSzymon Olewniczak                    return;
680dbd1d4cSSzymon Olewniczak                }
690dbd1d4cSSzymon Olewniczak
707ddaad11SAndreas Gohr                $sql = Tools::SQLstring2array(file_get_contents($importfile));
713a56750bSAndreas Gohr                try {
7214e7409bSAndreas Gohr                    $this->db->getPdo()->beginTransaction();
737ddaad11SAndreas Gohr                    foreach ($sql as $s) {
747ddaad11SAndreas Gohr                        $this->db->exec($s);
757ddaad11SAndreas Gohr                    }
7614e7409bSAndreas Gohr                    $this->db->getPdo()->commit();
770dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_success'), 1);
783a56750bSAndreas Gohr                } catch (Exception $e) {
7914e7409bSAndreas Gohr                    $this->db->getPdo()->rollBack();
807ddaad11SAndreas Gohr                    msg(hsc($e->getMessage()), -1);
810dbd1d4cSSzymon Olewniczak                }
823a56750bSAndreas Gohr                break;
833a56750bSAndreas Gohr            case 'save_query':
843a56750bSAndreas Gohr                $this->querySaver->saveQuery($INPUT->str('name'), $INPUT->str('sql'));
853a56750bSAndreas Gohr                break;
863a56750bSAndreas Gohr            case 'delete_query':
873a56750bSAndreas Gohr                $this->querySaver->deleteQuery($INPUT->str('name'));
883a56750bSAndreas Gohr                break;
89a34ef333SKlap-in        }
9093d995e0SAndreas Gohr    }
9193d995e0SAndreas Gohr
923a56750bSAndreas Gohr    /** @inheritdoc */
93*9038f707SAndreas Gohr    public function html()
943a56750bSAndreas Gohr    {
95271f9caeSSzymon Olewniczak        global $INPUT;
9693d995e0SAndreas Gohr
97*9038f707SAndreas Gohr        echo '<div class="plugin_sqlite_admin">';
9893d995e0SAndreas Gohr        echo $this->locale_xhtml('intro');
9993d995e0SAndreas Gohr
100*9038f707SAndreas Gohr        if ($this->db) {
1013a56750bSAndreas Gohr            echo '<h2>' . $this->getLang('db') . ' "' . hsc($this->db->getDbName()) . '"</h2>';
10293d995e0SAndreas Gohr
1033a56750bSAndreas Gohr            echo '<div class="commands">';
1043a56750bSAndreas Gohr            $this->showCommands();
1053a56750bSAndreas Gohr            $this->showSavedQueries();
1063a56750bSAndreas Gohr            echo '</div>';
107a34ef333SKlap-in
1083a56750bSAndreas Gohr            // query form
1093a56750bSAndreas Gohr            $form = new Form(['action' => $this->selfLink()]);
1103a56750bSAndreas Gohr            $form->addClass('sqliteplugin');
111ecc45fdcSSzymon Olewniczak            $form->addFieldsetOpen('SQL Command');
1123a56750bSAndreas Gohr            $form->addTextarea('sql')->addClass('edit');
1133a56750bSAndreas Gohr            $form->addButton('submit', $this->getLang('btn_execute'))->attr('type', 'submit');
114ecc45fdcSSzymon Olewniczak            $form->addTextInput('name', $this->getLang('query_name'));
1153a56750bSAndreas Gohr            $form->addButton('cmd[save_query]', $this->getLang('save_query'))->attr('type', 'submit');
116ecc45fdcSSzymon Olewniczak            $form->addFieldsetClose();
1173a56750bSAndreas Gohr            echo $form->toHTML();
11893d995e0SAndreas Gohr
1193a56750bSAndreas Gohr            // results
1203a56750bSAndreas Gohr            if ($INPUT->has('sql')) $this->showQueryResults($INPUT->str('sql'));
121*9038f707SAndreas Gohr        }
122271f9caeSSzymon Olewniczak        echo '</div>';
123271f9caeSSzymon Olewniczak    }
124271f9caeSSzymon Olewniczak
1255a212f8eSAndreas Gohr    /**
1265a212f8eSAndreas Gohr     * List all available databases in the TOC
1275a212f8eSAndreas Gohr     *
1285a212f8eSAndreas Gohr     * @inheritdoc
1295a212f8eSAndreas Gohr     */
1305a212f8eSAndreas Gohr    public function getTOC()
1313a56750bSAndreas Gohr    {
13293d995e0SAndreas Gohr        global $conf;
13393d995e0SAndreas Gohr        global $ID;
13493d995e0SAndreas Gohr
1355a212f8eSAndreas Gohr        $toc = [];
1365a212f8eSAndreas Gohr        $toc[] = [
1375a212f8eSAndreas Gohr            'link' => wl($ID, ['do' => 'admin', 'page' => 'sqlite']),
1385a212f8eSAndreas Gohr            'title' => $this->getLang('db') . ':',
13993d995e0SAndreas Gohr            'level' => 1,
14093d995e0SAndreas Gohr            'type' => 'ul',
1415a212f8eSAndreas Gohr        ];
1425a212f8eSAndreas Gohr        $dbfiles = glob($conf['metadir'] . '/*.sqlite3');
14340698f67SKlap-in        if (is_array($dbfiles)) foreach ($dbfiles as $file) {
1445a212f8eSAndreas Gohr            $db = basename($file, '.sqlite3');
14540698f67SKlap-in            $toc[] = array(
1465a212f8eSAndreas Gohr                'link' => wl($ID, array('do' => 'admin', 'page' => 'sqlite', 'db' => $db, 'sectok' => getSecurityToken())),
1475a212f8eSAndreas Gohr                'title' => $db,
14840698f67SKlap-in                'level' => 2,
14940698f67SKlap-in                'type' => 'ul',
15040698f67SKlap-in            );
15140698f67SKlap-in        }
15293d995e0SAndreas Gohr
15393d995e0SAndreas Gohr        return $toc;
15493d995e0SAndreas Gohr    }
1553a56750bSAndreas Gohr
1563a56750bSAndreas Gohr    /**
1573a56750bSAndreas Gohr     * Execute and display the results of the given SQL query
1583a56750bSAndreas Gohr     *
1593a56750bSAndreas Gohr     * multiple queries can be given separated by semicolons
1603a56750bSAndreas Gohr     *
1613a56750bSAndreas Gohr     * @param string $sql
1623a56750bSAndreas Gohr     */
1633a56750bSAndreas Gohr    protected function showQueryResults($sql)
1643a56750bSAndreas Gohr    {
1653a56750bSAndreas Gohr        echo '<h3 id="scroll__here">Query results</h3>';
1663a56750bSAndreas Gohr
1673a56750bSAndreas Gohr        $sql = Tools::SQLstring2array($sql);
1683a56750bSAndreas Gohr        foreach ($sql as $s) {
1693a56750bSAndreas Gohr            try {
1703a56750bSAndreas Gohr                $time_start = microtime(true);
1713a56750bSAndreas Gohr                $result = $this->db->queryAll($s);
1723a56750bSAndreas Gohr                $time_end = microtime(true);
1733a56750bSAndreas Gohr            } catch (Exception $e) {
1743a56750bSAndreas Gohr                msg($e->getMessage(), -1);
1753a56750bSAndreas Gohr                continue;
17693d995e0SAndreas Gohr            }
17793d995e0SAndreas Gohr
1783a56750bSAndreas Gohr            $time = $time_end - $time_start;
1793a56750bSAndreas Gohr            $cnt = count($result);
1803a56750bSAndreas Gohr            msg($cnt . ' affected rows in ' . $this->microtimeToSeconds($time) . ' seconds', 1);
1813a56750bSAndreas Gohr            if (!$cnt) continue;
1823a56750bSAndreas Gohr
1833a56750bSAndreas Gohr            echo '<div>';
1843a56750bSAndreas Gohr            $ths = array_keys($result[0]);
1853a56750bSAndreas Gohr            echo '<table class="inline">';
1863a56750bSAndreas Gohr            echo '<tr>';
1873a56750bSAndreas Gohr            foreach ($ths as $th) {
1883a56750bSAndreas Gohr                echo '<th>' . hsc($th) . '</th>';
1893a56750bSAndreas Gohr            }
1903a56750bSAndreas Gohr            echo '</tr>';
1913a56750bSAndreas Gohr            foreach ($result as $row) {
1923a56750bSAndreas Gohr                echo '<tr>';
1933a56750bSAndreas Gohr                $tds = array_values($row);
1943a56750bSAndreas Gohr                foreach ($tds as $td) {
1953a56750bSAndreas Gohr                    if ($td === null) $td = '␀';
1963a56750bSAndreas Gohr                    echo '<td>' . hsc($td) . '</td>';
1973a56750bSAndreas Gohr                }
1983a56750bSAndreas Gohr                echo '</tr>';
1993a56750bSAndreas Gohr            }
2003a56750bSAndreas Gohr            echo '</table>';
2013a56750bSAndreas Gohr            echo '</div>';
2023a56750bSAndreas Gohr        }
2033a56750bSAndreas Gohr    }
2043a56750bSAndreas Gohr
2053a56750bSAndreas Gohr
2063a56750bSAndreas Gohr    /**
2073a56750bSAndreas Gohr     * Convert a microtime() value to a string in seconds
2083a56750bSAndreas Gohr     *
2093a56750bSAndreas Gohr     * @param float $time
2103a56750bSAndreas Gohr     * @return string
2113a56750bSAndreas Gohr     */
2123a56750bSAndreas Gohr    protected function microtimeToSeconds($time)
2133a56750bSAndreas Gohr    {
2143a56750bSAndreas Gohr        return ($time < 0.0001 ? substr($time, 0, 5) . substr($time, -3) : substr($time, 0, 7));
2153a56750bSAndreas Gohr    }
2163a56750bSAndreas Gohr
2173a56750bSAndreas Gohr    /**
2183a56750bSAndreas Gohr     * Construct a link to the sqlite admin page with the given additional parameters
2193a56750bSAndreas Gohr     *
2203a56750bSAndreas Gohr     * Basically a wrapper around wl() with some defaults
2213a56750bSAndreas Gohr     *
2223a56750bSAndreas Gohr     * @param string[] $params
2233a56750bSAndreas Gohr     * @param bool $form for use in form action?
2243a56750bSAndreas Gohr     * @return string
2253a56750bSAndreas Gohr     */
2263a56750bSAndreas Gohr    protected function selfLink($form = true, $params = [])
2273a56750bSAndreas Gohr    {
2283a56750bSAndreas Gohr        global $ID;
2293a56750bSAndreas Gohr        $params = array_merge(
2303a56750bSAndreas Gohr            [
2313a56750bSAndreas Gohr                'do' => 'admin',
2323a56750bSAndreas Gohr                'page' => 'sqlite',
2333a56750bSAndreas Gohr                'db' => $this->db ? $this->db->getDBName() : '',
2343a56750bSAndreas Gohr                'sectok' => getSecurityToken(),
2353a56750bSAndreas Gohr            ], $params
2363a56750bSAndreas Gohr        );
2373a56750bSAndreas Gohr
2383a56750bSAndreas Gohr        return wl($ID, $params, false, $form ? '&' : '&amp;');
2393a56750bSAndreas Gohr    }
2403a56750bSAndreas Gohr
2413a56750bSAndreas Gohr    /**
2423a56750bSAndreas Gohr     * Display the standard actions for a database
2433a56750bSAndreas Gohr     */
2443a56750bSAndreas Gohr    protected function showCommands()
2453a56750bSAndreas Gohr    {
2463a56750bSAndreas Gohr        $commands = [
2473a56750bSAndreas Gohr            'dbversion' => [
2483a56750bSAndreas Gohr                'sql' => 'SELECT val FROM opts WHERE opt=\'dbversion\'',
2493a56750bSAndreas Gohr            ],
2503a56750bSAndreas Gohr            'table' => [
2513a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'table\' ORDER BY name',
2523a56750bSAndreas Gohr            ],
2533a56750bSAndreas Gohr            'index' => [
2543a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'index\' ORDER BY name',
2553a56750bSAndreas Gohr            ],
2563a56750bSAndreas Gohr            'export' => [
2573a56750bSAndreas Gohr                'cmd' => 'export'
2583a56750bSAndreas Gohr            ],
2593a56750bSAndreas Gohr        ];
2603a56750bSAndreas Gohr
2613a56750bSAndreas Gohr        // import form
2623a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink(), 'enctype' => 'multipart/form-data', 'method' => 'post']);
263*9038f707SAndreas Gohr        $form->addElement(
264*9038f707SAndreas Gohr            (new InputElement('file', 'importfile'))
265*9038f707SAndreas Gohr                ->attr('required', 'required')
266*9038f707SAndreas Gohr                ->attr('accept', '.sql')
267*9038f707SAndreas Gohr        );
2683a56750bSAndreas Gohr        $form->addButton('cmd[import]', $this->getLang('import'));
2693a56750bSAndreas Gohr
2703a56750bSAndreas Gohr        // output as a list
2713a56750bSAndreas Gohr        echo '<ul>';
2723a56750bSAndreas Gohr        foreach ($commands as $label => $command) {
2733a56750bSAndreas Gohr            echo '<li><div class="li">';
2743a56750bSAndreas Gohr            echo '<a href="' . $this->selfLink(false, $command) . '">' . $this->getLang($label) . '</a>';
2753a56750bSAndreas Gohr            echo '</div></li>';
2763a56750bSAndreas Gohr        }
2773a56750bSAndreas Gohr        echo '<li><div class="li">';
2783a56750bSAndreas Gohr        echo $form->toHTML();
2793a56750bSAndreas Gohr        echo '</div></li>';
2803a56750bSAndreas Gohr        echo '</ul>';
2813a56750bSAndreas Gohr    }
2823a56750bSAndreas Gohr
2833a56750bSAndreas Gohr    /**
284*9038f707SAndreas Gohr     * Display the saved queries for this database
2853a56750bSAndreas Gohr     */
2863a56750bSAndreas Gohr    public function showSavedQueries()
2873a56750bSAndreas Gohr    {
2883a56750bSAndreas Gohr        $queries = $this->querySaver->getQueries();
2893a56750bSAndreas Gohr        if (!$queries) return;
2903a56750bSAndreas Gohr
2913a56750bSAndreas Gohr        echo '<ul>';
2923a56750bSAndreas Gohr        foreach ($queries as $query) {
2933a56750bSAndreas Gohr            $link = $this->selfLink(false, ['sql' => $query['sql']]);
2943a56750bSAndreas Gohr            $del = $this->selfLink(false, ['cmd' => 'delete_query', 'name' => $query['name']]);
2953a56750bSAndreas Gohr
2963a56750bSAndreas Gohr            echo '<li><div class="li">';
2973a56750bSAndreas Gohr            echo '<a href="' . $link . '">' . hsc($query['name']) . '</a>';
2983a56750bSAndreas Gohr            echo ' [<a href="' . $del . '">' . $this->getLang('delete_query') . '</a>]';
2993a56750bSAndreas Gohr            echo '</div></li>';
3003a56750bSAndreas Gohr        }
3013a56750bSAndreas Gohr        echo '</ul>';
3023a56750bSAndreas Gohr    }
3033a56750bSAndreas Gohr}
304