xref: /plugin/sqlite/admin.php (revision 3a56750b42e05745f84fd97217f7133805e622eb)
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;
11*3a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\QuerySaver;
12*3a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
13*3a56750bSAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
14af0e7691SSzymon Olewniczak
1593d995e0SAndreas Gohr
16*3a56750bSAndreas Gohrclass admin_plugin_sqlite extends DokuWiki_Admin_Plugin
17*3a56750bSAndreas Gohr{
18*3a56750bSAndreas Gohr    /** @var SQLiteDB */
19*3a56750bSAndreas Gohr    protected $db = null;
2093d995e0SAndreas Gohr
21*3a56750bSAndreas Gohr    /** @var QuerySaver */
22*3a56750bSAndreas Gohr    protected $querySaver = null;
23*3a56750bSAndreas Gohr
24*3a56750bSAndreas Gohr    /** @inheritdoc */
25*3a56750bSAndreas Gohr    function getMenuSort()
26*3a56750bSAndreas Gohr    {
270542fc8eSKlap-in        return 500;
280542fc8eSKlap-in    }
290542fc8eSKlap-in
30*3a56750bSAndreas Gohr    /** @inheritdoc */
31*3a56750bSAndreas Gohr    function forAdminOnly()
32*3a56750bSAndreas Gohr    {
330542fc8eSKlap-in        return true;
340542fc8eSKlap-in    }
3593d995e0SAndreas Gohr
36*3a56750bSAndreas Gohr    /** @inheritdoc */
37*3a56750bSAndreas Gohr    function handle()
38*3a56750bSAndreas Gohr    {
39a34ef333SKlap-in        global $conf;
400dbd1d4cSSzymon Olewniczak        global $INPUT;
410dbd1d4cSSzymon Olewniczak
42*3a56750bSAndreas Gohr        // load database if given and security token is valid
43*3a56750bSAndreas Gohr        if ($INPUT->str('db') && checkSecurityToken()) {
44*3a56750bSAndreas Gohr            try {
45*3a56750bSAndreas Gohr                $this->db = new SQLiteDB($INPUT->str('db'), '');
46*3a56750bSAndreas Gohr                $this->querySaver = new QuerySaver($this->db->getDBName());
47*3a56750bSAndreas Gohr            } catch (Exception $e) {
48*3a56750bSAndreas Gohr                msg($e->getMessage(), -1);
49*3a56750bSAndreas Gohr                return;
50a34ef333SKlap-in            }
51*3a56750bSAndreas Gohr        }
520dbd1d4cSSzymon Olewniczak
53*3a56750bSAndreas Gohr        $cmd = $INPUT->extract('cmd')->str('cmd');
54*3a56750bSAndreas Gohr        switch ($cmd) {
55*3a56750bSAndreas Gohr            case 'export':
56*3a56750bSAndreas Gohr                $exportfile = $conf['tmpdir'] . '/' . $this->db->getDBName() . '.sql';
57*3a56750bSAndreas Gohr                $this->db->dumpToFile($exportfile);
580dbd1d4cSSzymon Olewniczak                header('Content-Type: text/sql');
59*3a56750bSAndreas Gohr                header('Content-Disposition: attachment; filename="' . $this->db->getDbName() . '.sql";');
60*3a56750bSAndreas Gohr                readfile($exportfile);
61*3a56750bSAndreas Gohr                unlink($exportfile);
620dbd1d4cSSzymon Olewniczak                exit(0);
63*3a56750bSAndreas Gohr            case 'import':
64*3a56750bSAndreas Gohr                $importfile = $_FILES['importfile']['tmp_name'];
650dbd1d4cSSzymon Olewniczak
66*3a56750bSAndreas Gohr                if (empty($importfile)) {
670dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_no_file'), -1);
680dbd1d4cSSzymon Olewniczak                    return;
690dbd1d4cSSzymon Olewniczak                }
700dbd1d4cSSzymon Olewniczak
71*3a56750bSAndreas Gohr                $sql = file_get_contents($importfile);
72*3a56750bSAndreas Gohr                try {
73*3a56750bSAndreas Gohr                    $this->db->exec($sql);
740dbd1d4cSSzymon Olewniczak                    msg($this->getLang('import_success'), 1);
75*3a56750bSAndreas Gohr                } catch (Exception $e) {
76*3a56750bSAndreas Gohr                    msg($e->getMessage(), -1);
770dbd1d4cSSzymon Olewniczak                }
78*3a56750bSAndreas Gohr                break;
79*3a56750bSAndreas Gohr            case 'save_query':
80*3a56750bSAndreas Gohr                $this->querySaver->saveQuery($INPUT->str('name'), $INPUT->str('sql'));
81*3a56750bSAndreas Gohr                break;
82*3a56750bSAndreas Gohr            case 'delete_query':
83*3a56750bSAndreas Gohr                $this->querySaver->deleteQuery($INPUT->str('name'));
84*3a56750bSAndreas Gohr                break;
85a34ef333SKlap-in        }
8693d995e0SAndreas Gohr    }
8793d995e0SAndreas Gohr
88*3a56750bSAndreas Gohr    /** @inheritdoc */
89*3a56750bSAndreas Gohr    function html()
90*3a56750bSAndreas Gohr    {
91271f9caeSSzymon Olewniczak        global $INPUT;
9293d995e0SAndreas Gohr
9393d995e0SAndreas Gohr        echo $this->locale_xhtml('intro');
94*3a56750bSAndreas Gohr        if (!$this->db) return;
9593d995e0SAndreas Gohr
9693d995e0SAndreas Gohr
97*3a56750bSAndreas Gohr        echo '<h2>' . $this->getLang('db') . ' "' . hsc($this->db->getDbName()) . '"</h2>';
9893d995e0SAndreas Gohr        echo '<div class="level2">';
9993d995e0SAndreas Gohr
100*3a56750bSAndreas Gohr        echo '<div class="commands">';
101*3a56750bSAndreas Gohr        $this->showCommands();
102*3a56750bSAndreas Gohr        $this->showSavedQueries();
103*3a56750bSAndreas Gohr        echo '</div>';
104a34ef333SKlap-in
105*3a56750bSAndreas Gohr        // query form
106*3a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink()]);
107*3a56750bSAndreas Gohr        $form->addClass('sqliteplugin');
108ecc45fdcSSzymon Olewniczak        $form->addFieldsetOpen('SQL Command');
109*3a56750bSAndreas Gohr        $form->addTextarea('sql')->addClass('edit');
110*3a56750bSAndreas Gohr        $form->addButton('submit', $this->getLang('btn_execute'))->attr('type', 'submit');
111ecc45fdcSSzymon Olewniczak        $form->addTextInput('name', $this->getLang('query_name'));
112*3a56750bSAndreas Gohr        $form->addButton('cmd[save_query]', $this->getLang('save_query'))->attr('type', 'submit');
113ecc45fdcSSzymon Olewniczak        $form->addFieldsetClose();
114*3a56750bSAndreas Gohr        echo $form->toHTML();
11593d995e0SAndreas Gohr
116*3a56750bSAndreas Gohr        // results
117*3a56750bSAndreas Gohr        if ($INPUT->has('sql')) $this->showQueryResults($INPUT->str('sql'));
118271f9caeSSzymon Olewniczak        echo '</div>';
119271f9caeSSzymon Olewniczak    }
120271f9caeSSzymon Olewniczak
121*3a56750bSAndreas Gohr    function getTOC()
122*3a56750bSAndreas Gohr    {
12393d995e0SAndreas Gohr        global $conf;
12493d995e0SAndreas Gohr        global $ID;
12593d995e0SAndreas Gohr
12693d995e0SAndreas Gohr        $toc = array();
12740698f67SKlap-in        $fileextensions = array('sqlite2' => '.sqlite', 'sqlite3' => '.sqlite3');
12893d995e0SAndreas Gohr
12940698f67SKlap-in        foreach ($fileextensions as $dbformat => $fileextension) {
13093d995e0SAndreas Gohr            $toc[] = array(
1310542fc8eSKlap-in                'link' => wl($ID, array('do' => 'admin', 'page' => 'sqlite')),
13240698f67SKlap-in                'title' => $dbformat . ':',
13393d995e0SAndreas Gohr                'level' => 1,
13493d995e0SAndreas Gohr                'type' => 'ul',
13593d995e0SAndreas Gohr            );
13640698f67SKlap-in
13740698f67SKlap-in            $dbfiles = glob($conf['metadir'] . '/*' . $fileextension);
13840698f67SKlap-in
13940698f67SKlap-in            if (is_array($dbfiles)) foreach ($dbfiles as $file) {
14040698f67SKlap-in                $db = basename($file, $fileextension);
14140698f67SKlap-in                $toc[] = array(
14284041991SKlap-in                    'link' => wl($ID, array('do' => 'admin', 'page' => 'sqlite', 'db' => $db, 'version' => $dbformat, 'sectok' => getSecurityToken())),
14340698f67SKlap-in                    'title' => $this->getLang('db') . ' ' . $db,
14440698f67SKlap-in                    'level' => 2,
14540698f67SKlap-in                    'type' => 'ul',
14640698f67SKlap-in                );
14740698f67SKlap-in            }
14893d995e0SAndreas Gohr        }
14993d995e0SAndreas Gohr
15093d995e0SAndreas Gohr        return $toc;
15193d995e0SAndreas Gohr    }
152*3a56750bSAndreas Gohr
153*3a56750bSAndreas Gohr    /**
154*3a56750bSAndreas Gohr     * Execute and display the results of the given SQL query
155*3a56750bSAndreas Gohr     *
156*3a56750bSAndreas Gohr     * multiple queries can be given separated by semicolons
157*3a56750bSAndreas Gohr     *
158*3a56750bSAndreas Gohr     * @param string $sql
159*3a56750bSAndreas Gohr     */
160*3a56750bSAndreas Gohr    protected function showQueryResults($sql)
161*3a56750bSAndreas Gohr    {
162*3a56750bSAndreas Gohr        echo '<h3 id="scroll__here">Query results</h3>';
163*3a56750bSAndreas Gohr
164*3a56750bSAndreas Gohr        $sql = Tools::SQLstring2array($sql);
165*3a56750bSAndreas Gohr        foreach ($sql as $s) {
166*3a56750bSAndreas Gohr            $s = preg_replace('!^\s*--.*$!m', '', $s);
167*3a56750bSAndreas Gohr            $s = trim($s);
168*3a56750bSAndreas Gohr            if (!$s) continue;
169*3a56750bSAndreas Gohr
170*3a56750bSAndreas Gohr            try {
171*3a56750bSAndreas Gohr                $time_start = microtime(true);
172*3a56750bSAndreas Gohr                $result = $this->db->queryAll($s);
173*3a56750bSAndreas Gohr                $time_end = microtime(true);
174*3a56750bSAndreas Gohr            } catch (Exception $e) {
175*3a56750bSAndreas Gohr                msg($e->getMessage(), -1);
176*3a56750bSAndreas Gohr                continue;
17793d995e0SAndreas Gohr            }
17893d995e0SAndreas Gohr
179*3a56750bSAndreas Gohr            $time = $time_end - $time_start;
180*3a56750bSAndreas Gohr            $cnt = count($result);
181*3a56750bSAndreas Gohr            msg($cnt . ' affected rows in ' . $this->microtimeToSeconds($time) . ' seconds', 1);
182*3a56750bSAndreas Gohr            if (!$cnt) continue;
183*3a56750bSAndreas Gohr
184*3a56750bSAndreas Gohr            echo '<div>';
185*3a56750bSAndreas Gohr            $ths = array_keys($result[0]);
186*3a56750bSAndreas Gohr            echo '<table class="inline">';
187*3a56750bSAndreas Gohr            echo '<tr>';
188*3a56750bSAndreas Gohr            foreach ($ths as $th) {
189*3a56750bSAndreas Gohr                echo '<th>' . hsc($th) . '</th>';
190*3a56750bSAndreas Gohr            }
191*3a56750bSAndreas Gohr            echo '</tr>';
192*3a56750bSAndreas Gohr            foreach ($result as $row) {
193*3a56750bSAndreas Gohr                echo '<tr>';
194*3a56750bSAndreas Gohr                $tds = array_values($row);
195*3a56750bSAndreas Gohr                foreach ($tds as $td) {
196*3a56750bSAndreas Gohr                    if ($td === null) $td = '␀';
197*3a56750bSAndreas Gohr                    echo '<td>' . hsc($td) . '</td>';
198*3a56750bSAndreas Gohr                }
199*3a56750bSAndreas Gohr                echo '</tr>';
200*3a56750bSAndreas Gohr            }
201*3a56750bSAndreas Gohr            echo '</table>';
202*3a56750bSAndreas Gohr            echo '</div>';
203*3a56750bSAndreas Gohr        }
204*3a56750bSAndreas Gohr    }
205*3a56750bSAndreas Gohr
206*3a56750bSAndreas Gohr
207*3a56750bSAndreas Gohr    /**
208*3a56750bSAndreas Gohr     * Convert a microtime() value to a string in seconds
209*3a56750bSAndreas Gohr     *
210*3a56750bSAndreas Gohr     * @param float $time
211*3a56750bSAndreas Gohr     * @return string
212*3a56750bSAndreas Gohr     */
213*3a56750bSAndreas Gohr    protected function microtimeToSeconds($time)
214*3a56750bSAndreas Gohr    {
215*3a56750bSAndreas Gohr        return ($time < 0.0001 ? substr($time, 0, 5) . substr($time, -3) : substr($time, 0, 7));
216*3a56750bSAndreas Gohr    }
217*3a56750bSAndreas Gohr
218*3a56750bSAndreas Gohr    /**
219*3a56750bSAndreas Gohr     * Construct a link to the sqlite admin page with the given additional parameters
220*3a56750bSAndreas Gohr     *
221*3a56750bSAndreas Gohr     * Basically a wrapper around wl() with some defaults
222*3a56750bSAndreas Gohr     *
223*3a56750bSAndreas Gohr     * @param string[] $params
224*3a56750bSAndreas Gohr     * @param bool $form for use in form action?
225*3a56750bSAndreas Gohr     * @return string
226*3a56750bSAndreas Gohr     */
227*3a56750bSAndreas Gohr    protected function selfLink($form = true, $params = [])
228*3a56750bSAndreas Gohr    {
229*3a56750bSAndreas Gohr        global $ID;
230*3a56750bSAndreas Gohr        $params = array_merge(
231*3a56750bSAndreas Gohr            [
232*3a56750bSAndreas Gohr                'do' => 'admin',
233*3a56750bSAndreas Gohr                'page' => 'sqlite',
234*3a56750bSAndreas Gohr                'db' => $this->db ? $this->db->getDBName() : '',
235*3a56750bSAndreas Gohr                'sectok' => getSecurityToken(),
236*3a56750bSAndreas Gohr            ], $params
237*3a56750bSAndreas Gohr        );
238*3a56750bSAndreas Gohr
239*3a56750bSAndreas Gohr        return wl($ID, $params, false, $form ? '&' : '&amp;');
240*3a56750bSAndreas Gohr    }
241*3a56750bSAndreas Gohr
242*3a56750bSAndreas Gohr    /**
243*3a56750bSAndreas Gohr     * Display the standard actions for a database
244*3a56750bSAndreas Gohr     */
245*3a56750bSAndreas Gohr    protected function showCommands()
246*3a56750bSAndreas Gohr    {
247*3a56750bSAndreas Gohr        $commands = [
248*3a56750bSAndreas Gohr            'dbversion' => [
249*3a56750bSAndreas Gohr                'sql' => 'SELECT val FROM opts WHERE opt=\'dbversion\'',
250*3a56750bSAndreas Gohr            ],
251*3a56750bSAndreas Gohr            'table' => [
252*3a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'table\' ORDER BY name',
253*3a56750bSAndreas Gohr            ],
254*3a56750bSAndreas Gohr            'index' => [
255*3a56750bSAndreas Gohr                'sql' => 'SELECT name,sql FROM sqlite_master WHERE type=\'index\' ORDER BY name',
256*3a56750bSAndreas Gohr            ],
257*3a56750bSAndreas Gohr            'export' => [
258*3a56750bSAndreas Gohr                'cmd' => 'export'
259*3a56750bSAndreas Gohr            ],
260*3a56750bSAndreas Gohr        ];
261*3a56750bSAndreas Gohr
262*3a56750bSAndreas Gohr        // import form
263*3a56750bSAndreas Gohr        $form = new Form(['action' => $this->selfLink(), 'enctype' => 'multipart/form-data', 'method' => 'post']);
264*3a56750bSAndreas Gohr        $form->addElement(new InputElement('file', 'importfile'));
265*3a56750bSAndreas Gohr        $form->addButton('cmd[import]', $this->getLang('import'));
266*3a56750bSAndreas Gohr
267*3a56750bSAndreas Gohr        // output as a list
268*3a56750bSAndreas Gohr        echo '<ul>';
269*3a56750bSAndreas Gohr        foreach ($commands as $label => $command) {
270*3a56750bSAndreas Gohr            echo '<li><div class="li">';
271*3a56750bSAndreas Gohr            echo '<a href="' . $this->selfLink(false, $command) . '">' . $this->getLang($label) . '</a>';
272*3a56750bSAndreas Gohr            echo '</div></li>';
273*3a56750bSAndreas Gohr        }
274*3a56750bSAndreas Gohr        echo '<li><div class="li">';
275*3a56750bSAndreas Gohr        echo $form->toHTML();
276*3a56750bSAndreas Gohr        echo '</div></li>';
277*3a56750bSAndreas Gohr        echo '</ul>';
278*3a56750bSAndreas Gohr    }
279*3a56750bSAndreas Gohr
280*3a56750bSAndreas Gohr    /**
281*3a56750bSAndreas Gohr     * FIXME needs to be cleaned up
282*3a56750bSAndreas Gohr     */
283*3a56750bSAndreas Gohr    public function showSavedQueries()
284*3a56750bSAndreas Gohr    {
285*3a56750bSAndreas Gohr        $queries = $this->querySaver->getQueries();
286*3a56750bSAndreas Gohr        if (!$queries) return;
287*3a56750bSAndreas Gohr
288*3a56750bSAndreas Gohr        echo '<ul>';
289*3a56750bSAndreas Gohr        foreach ($queries as $query) {
290*3a56750bSAndreas Gohr            $link = $this->selfLink(false, ['sql' => $query['sql']]);
291*3a56750bSAndreas Gohr            $del = $this->selfLink(false, ['cmd' => 'delete_query', 'name' => $query['name']]);
292*3a56750bSAndreas Gohr
293*3a56750bSAndreas Gohr            echo '<li><div class="li">';
294*3a56750bSAndreas Gohr            echo '<a href="' . $link . '">' . hsc($query['name']) . '</a>';
295*3a56750bSAndreas Gohr            echo ' [<a href="' . $del . '">' . $this->getLang('delete_query') . '</a>]';
296*3a56750bSAndreas Gohr            echo '</div></li>';
297*3a56750bSAndreas Gohr        }
298*3a56750bSAndreas Gohr        echo '</ul>';
299*3a56750bSAndreas Gohr    }
300*3a56750bSAndreas Gohr}
301