xref: /plugin/sqlite/admin.php (revision 16e91cc05dc8f57f43feda69c1196c720afee918)
193d995e0SAndreas Gohr<?php
293d995e0SAndreas Gohr
3a7a40fb2SAnna Dabrowskause dokuwiki\Extension\AdminPlugin;
4af0e7691SSzymon Olewniczakuse dokuwiki\Form\Form;
5ecc45fdcSSzymon Olewniczakuse dokuwiki\Form\InputElement;
63a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\QuerySaver;
73a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
83a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
9af0e7691SSzymon Olewniczak
109038f707SAndreas Gohr/**
119038f707SAndreas Gohr * DokuWiki Plugin sqlite (Admin Component)
129038f707SAndreas Gohr *
139038f707SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
149038f707SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
159038f707SAndreas Gohr */
16a7a40fb2SAnna Dabrowskaclass admin_plugin_sqlite extends AdminPlugin
173a56750bSAndreas Gohr{
183a56750bSAndreas Gohr    /** @var SQLiteDB */
19a7a40fb2SAnna Dabrowska    protected $db;
2093d995e0SAndreas Gohr
213a56750bSAndreas Gohr    /** @var QuerySaver */
22a7a40fb2SAnna Dabrowska    protected $querySaver;
233a56750bSAndreas Gohr
243a56750bSAndreas Gohr    /** @inheritdoc */
259038f707SAndreas Gohr    public function getMenuSort()
263a56750bSAndreas Gohr    {
270542fc8eSKlap-in        return 500;
280542fc8eSKlap-in    }
290542fc8eSKlap-in
303a56750bSAndreas Gohr    /** @inheritdoc */
319038f707SAndreas Gohr    public function forAdminOnly()
323a56750bSAndreas Gohr    {
330542fc8eSKlap-in        return true;
340542fc8eSKlap-in    }
3593d995e0SAndreas Gohr
363a56750bSAndreas Gohr    /** @inheritdoc */
379038f707SAndreas Gohr    public 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':
5604a1c67aSAnna Dabrowska                $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;
9004a1c67aSAnna Dabrowska            case 'download':
9104a1c67aSAnna Dabrowska                $file = $this->db->getDbFile();
9204a1c67aSAnna Dabrowska                header('Content-Type: application/vnd.sqlite3');
9314fe8e27SAnna Dabrowska                header(
9414fe8e27SAnna Dabrowska                    'Content-Disposition: attachment; filename="'
9514fe8e27SAnna Dabrowska                    . $this->db->getDbName() . SQLiteDB::FILE_EXTENSION . '"'
9614fe8e27SAnna Dabrowska                );
9704a1c67aSAnna Dabrowska                readfile($file);
9804a1c67aSAnna Dabrowska                exit(0);
99a34ef333SKlap-in        }
10093d995e0SAndreas Gohr    }
10193d995e0SAndreas Gohr
1023a56750bSAndreas Gohr    /** @inheritdoc */
1039038f707SAndreas Gohr    public function html()
1043a56750bSAndreas Gohr    {
105271f9caeSSzymon Olewniczak        global $INPUT;
10693d995e0SAndreas Gohr
1079038f707SAndreas Gohr        echo '<div class="plugin_sqlite_admin">';
10893d995e0SAndreas Gohr        echo $this->locale_xhtml('intro');
10993d995e0SAndreas Gohr
1109038f707SAndreas Gohr        if ($this->db) {
1113a56750bSAndreas Gohr            echo '<h2>' . $this->getLang('db') . ' "' . hsc($this->db->getDbName()) . '"</h2>';
11293d995e0SAndreas Gohr
1133a56750bSAndreas Gohr            echo '<div class="commands">';
1143a56750bSAndreas Gohr            $this->showCommands();
1153a56750bSAndreas Gohr            $this->showSavedQueries();
1163a56750bSAndreas Gohr            echo '</div>';
117a34ef333SKlap-in
1183a56750bSAndreas Gohr            // query form
1193a56750bSAndreas Gohr            $form = new Form(['action' => $this->selfLink()]);
1203a56750bSAndreas Gohr            $form->addClass('sqliteplugin');
121ecc45fdcSSzymon Olewniczak            $form->addFieldsetOpen('SQL Command');
1223a56750bSAndreas Gohr            $form->addTextarea('sql')->addClass('edit');
1233a56750bSAndreas Gohr            $form->addButton('submit', $this->getLang('btn_execute'))->attr('type', 'submit');
124ecc45fdcSSzymon Olewniczak            $form->addTextInput('name', $this->getLang('query_name'));
1253a56750bSAndreas Gohr            $form->addButton('cmd[save_query]', $this->getLang('save_query'))->attr('type', 'submit');
126ecc45fdcSSzymon Olewniczak            $form->addFieldsetClose();
1273a56750bSAndreas Gohr            echo $form->toHTML();
12893d995e0SAndreas Gohr
1293a56750bSAndreas Gohr            // results
1303a56750bSAndreas Gohr            if ($INPUT->has('sql')) $this->showQueryResults($INPUT->str('sql'));
1319038f707SAndreas Gohr        }
132271f9caeSSzymon Olewniczak        echo '</div>';
133271f9caeSSzymon Olewniczak    }
134271f9caeSSzymon Olewniczak
1355a212f8eSAndreas Gohr    /**
1365a212f8eSAndreas Gohr     * List all available databases in the TOC
1375a212f8eSAndreas Gohr     *
1385a212f8eSAndreas Gohr     * @inheritdoc
1395a212f8eSAndreas Gohr     */
1405a212f8eSAndreas Gohr    public function getTOC()
1413a56750bSAndreas Gohr    {
14293d995e0SAndreas Gohr        global $conf;
14393d995e0SAndreas Gohr        global $ID;
14493d995e0SAndreas Gohr
1455a212f8eSAndreas Gohr        $toc = [];
1465a212f8eSAndreas Gohr        $toc[] = [
1475a212f8eSAndreas Gohr            'link' => wl($ID, ['do' => 'admin', 'page' => 'sqlite']),
1485a212f8eSAndreas Gohr            'title' => $this->getLang('db') . ':',
14993d995e0SAndreas Gohr            'level' => 1,
15093d995e0SAndreas Gohr            'type' => 'ul',
1515a212f8eSAndreas Gohr        ];
1525a212f8eSAndreas Gohr        $dbfiles = glob($conf['metadir'] . '/*.sqlite3');
15340698f67SKlap-in        if (is_array($dbfiles)) foreach ($dbfiles as $file) {
1545a212f8eSAndreas Gohr            $db = basename($file, '.sqlite3');
15514fe8e27SAnna Dabrowska            $toc[] = [
15614fe8e27SAnna Dabrowska                'link' => wl($ID, ['do' => 'admin', 'page' => 'sqlite', 'db' => $db, 'sectok' => getSecurityToken()]),
15714fe8e27SAnna Dabrowska                'title' => $db,
15814fe8e27SAnna Dabrowska                'level' => 2,
15914fe8e27SAnna Dabrowska                'type' => 'ul',
16014fe8e27SAnna Dabrowska            ];
16140698f67SKlap-in        }
16293d995e0SAndreas Gohr
16393d995e0SAndreas Gohr        return $toc;
16493d995e0SAndreas Gohr    }
1653a56750bSAndreas Gohr
1663a56750bSAndreas Gohr    /**
1673a56750bSAndreas Gohr     * Execute and display the results of the given SQL query
1683a56750bSAndreas Gohr     *
1693a56750bSAndreas Gohr     * multiple queries can be given separated by semicolons
1703a56750bSAndreas Gohr     *
1713a56750bSAndreas Gohr     * @param string $sql
1723a56750bSAndreas Gohr     */
1733a56750bSAndreas Gohr    protected function showQueryResults($sql)
1743a56750bSAndreas Gohr    {
1753a56750bSAndreas Gohr        echo '<h3 id="scroll__here">Query results</h3>';
1763a56750bSAndreas Gohr
1773a56750bSAndreas Gohr        $sql = Tools::SQLstring2array($sql);
1783a56750bSAndreas Gohr        foreach ($sql as $s) {
1793a56750bSAndreas Gohr            try {
1803a56750bSAndreas Gohr                $time_start = microtime(true);
1813a56750bSAndreas Gohr                $result = $this->db->queryAll($s);
1823a56750bSAndreas Gohr                $time_end = microtime(true);
1833a56750bSAndreas Gohr            } catch (Exception $e) {
1843a56750bSAndreas Gohr                msg($e->getMessage(), -1);
1853a56750bSAndreas Gohr                continue;
18693d995e0SAndreas Gohr            }
18793d995e0SAndreas Gohr
1883a56750bSAndreas Gohr            $time = $time_end - $time_start;
1893a56750bSAndreas Gohr            $cnt = count($result);
1903a56750bSAndreas Gohr            msg($cnt . ' affected rows in ' . $this->microtimeToSeconds($time) . ' seconds', 1);
1913a56750bSAndreas Gohr            if (!$cnt) continue;
1923a56750bSAndreas Gohr
1933a56750bSAndreas Gohr            echo '<div>';
1943a56750bSAndreas Gohr            $ths = array_keys($result[0]);
1953a56750bSAndreas Gohr            echo '<table class="inline">';
1963a56750bSAndreas Gohr            echo '<tr>';
1973a56750bSAndreas Gohr            foreach ($ths as $th) {
1983a56750bSAndreas Gohr                echo '<th>' . hsc($th) . '</th>';
1993a56750bSAndreas Gohr            }
2003a56750bSAndreas Gohr            echo '</tr>';
2013a56750bSAndreas Gohr            foreach ($result as $row) {
2023a56750bSAndreas Gohr                echo '<tr>';
2033a56750bSAndreas Gohr                $tds = array_values($row);
2043a56750bSAndreas Gohr                foreach ($tds as $td) {
2053a56750bSAndreas Gohr                    if ($td === null) $td = '␀';
2063a56750bSAndreas Gohr                    echo '<td>' . hsc($td) . '</td>';
2073a56750bSAndreas Gohr                }
2083a56750bSAndreas Gohr                echo '</tr>';
2093a56750bSAndreas Gohr            }
2103a56750bSAndreas Gohr            echo '</table>';
2113a56750bSAndreas Gohr            echo '</div>';
2123a56750bSAndreas Gohr        }
2133a56750bSAndreas Gohr    }
2143a56750bSAndreas Gohr
2153a56750bSAndreas Gohr
2163a56750bSAndreas Gohr    /**
2173a56750bSAndreas Gohr     * Convert a microtime() value to a string in seconds
2183a56750bSAndreas Gohr     *
2193a56750bSAndreas Gohr     * @param float $time
2203a56750bSAndreas Gohr     * @return string
2213a56750bSAndreas Gohr     */
2223a56750bSAndreas Gohr    protected function microtimeToSeconds($time)
2233a56750bSAndreas Gohr    {
2243a56750bSAndreas Gohr        return ($time < 0.0001 ? substr($time, 0, 5) . substr($time, -3) : substr($time, 0, 7));
2253a56750bSAndreas Gohr    }
2263a56750bSAndreas Gohr
2273a56750bSAndreas Gohr    /**
2283a56750bSAndreas Gohr     * Construct a link to the sqlite admin page with the given additional parameters
2293a56750bSAndreas Gohr     *
2303a56750bSAndreas Gohr     * Basically a wrapper around wl() with some defaults
2313a56750bSAndreas Gohr     *
2323a56750bSAndreas Gohr     * @param string[] $params
2333a56750bSAndreas Gohr     * @param bool $form for use in form action?
2343a56750bSAndreas Gohr     * @return string
2353a56750bSAndreas Gohr     */
2363a56750bSAndreas Gohr    protected function selfLink($form = true, $params = [])
2373a56750bSAndreas Gohr    {
2383a56750bSAndreas Gohr        global $ID;
239*16e91cc0SAndreas Gohr        if(isset($params['info'])) unset($params['info']);
240*16e91cc0SAndreas Gohr
2413a56750bSAndreas Gohr        $params = array_merge(
2423a56750bSAndreas Gohr            [
2433a56750bSAndreas Gohr                'do' => 'admin',
2443a56750bSAndreas Gohr                'page' => 'sqlite',
2453a56750bSAndreas Gohr                'db' => $this->db ? $this->db->getDBName() : '',
2463a56750bSAndreas Gohr                'sectok' => getSecurityToken(),
247a7a40fb2SAnna Dabrowska            ],
248a7a40fb2SAnna Dabrowska            $params
2493a56750bSAndreas Gohr        );
2503a56750bSAndreas Gohr
2513a56750bSAndreas Gohr        return wl($ID, $params, false, $form ? '&' : '&amp;');
2523a56750bSAndreas Gohr    }
2533a56750bSAndreas Gohr
2543a56750bSAndreas Gohr    /**
2553a56750bSAndreas Gohr     * Display the standard actions for a database
2563a56750bSAndreas Gohr     */
2573a56750bSAndreas Gohr    protected function showCommands()
2583a56750bSAndreas Gohr    {
2593a56750bSAndreas Gohr        $commands = [
2603a56750bSAndreas Gohr            'dbversion' => [
2613a56750bSAndreas Gohr                'sql' => 'SELECT val FROM opts WHERE opt=\'dbversion\'',
2623a56750bSAndreas Gohr            ],
2633a56750bSAndreas Gohr            'table' => [
2643a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'table\' ORDER BY name',
2653a56750bSAndreas Gohr            ],
2663a56750bSAndreas Gohr            'index' => [
2673a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'index\' ORDER BY name',
2683a56750bSAndreas Gohr            ],
2693a56750bSAndreas Gohr            'export' => [
2703a56750bSAndreas Gohr                'cmd' => 'export'
2713a56750bSAndreas Gohr            ],
27204a1c67aSAnna Dabrowska            'download' => [
273*16e91cc0SAndreas Gohr                'cmd' => 'download',
274*16e91cc0SAndreas Gohr                'info' => filesize_h(filesize($this->db->getDbFile()))
27504a1c67aSAnna Dabrowska            ],
2763a56750bSAndreas Gohr        ];
2773a56750bSAndreas Gohr
2783a56750bSAndreas Gohr        // import form
2793a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink(), 'enctype' => 'multipart/form-data', 'method' => 'post']);
2809038f707SAndreas Gohr        $form->addElement(
2819038f707SAndreas Gohr            (new InputElement('file', 'importfile'))
2829038f707SAndreas Gohr                ->attr('required', 'required')
2839038f707SAndreas Gohr                ->attr('accept', '.sql')
2849038f707SAndreas Gohr        );
2853a56750bSAndreas Gohr        $form->addButton('cmd[import]', $this->getLang('import'));
2863a56750bSAndreas Gohr
2873a56750bSAndreas Gohr        // output as a list
2883a56750bSAndreas Gohr        echo '<ul>';
2893a56750bSAndreas Gohr        foreach ($commands as $label => $command) {
2903a56750bSAndreas Gohr            echo '<li><div class="li">';
2913a56750bSAndreas Gohr            echo '<a href="' . $this->selfLink(false, $command) . '">' . $this->getLang($label) . '</a>';
292*16e91cc0SAndreas Gohr            if(isset($command['info'])) {
293*16e91cc0SAndreas Gohr                echo ' (' . $command['info'] . ')';
294*16e91cc0SAndreas Gohr            }
2953a56750bSAndreas Gohr            echo '</div></li>';
2963a56750bSAndreas Gohr        }
2973a56750bSAndreas Gohr        echo '<li><div class="li">';
2983a56750bSAndreas Gohr        echo $form->toHTML();
2993a56750bSAndreas Gohr        echo '</div></li>';
3003a56750bSAndreas Gohr        echo '</ul>';
3013a56750bSAndreas Gohr    }
3023a56750bSAndreas Gohr
3033a56750bSAndreas Gohr    /**
3049038f707SAndreas Gohr     * Display the saved queries for this database
3053a56750bSAndreas Gohr     */
3063a56750bSAndreas Gohr    public function showSavedQueries()
3073a56750bSAndreas Gohr    {
3083a56750bSAndreas Gohr        $queries = $this->querySaver->getQueries();
3093a56750bSAndreas Gohr        if (!$queries) return;
3103a56750bSAndreas Gohr
3113a56750bSAndreas Gohr        echo '<ul>';
3123a56750bSAndreas Gohr        foreach ($queries as $query) {
3133a56750bSAndreas Gohr            $link = $this->selfLink(false, ['sql' => $query['sql']]);
3143a56750bSAndreas Gohr            $del = $this->selfLink(false, ['cmd' => 'delete_query', 'name' => $query['name']]);
3153a56750bSAndreas Gohr
3163a56750bSAndreas Gohr            echo '<li><div class="li">';
3173a56750bSAndreas Gohr            echo '<a href="' . $link . '">' . hsc($query['name']) . '</a>';
3183a56750bSAndreas Gohr            echo ' [<a href="' . $del . '">' . $this->getLang('delete_query') . '</a>]';
3193a56750bSAndreas Gohr            echo '</div></li>';
3203a56750bSAndreas Gohr        }
3213a56750bSAndreas Gohr        echo '</ul>';
3223a56750bSAndreas Gohr    }
3233a56750bSAndreas Gohr}
324