- <?php 
- /* 
-  * Copyright (C) 2017 Karmabunny Pty Ltd. 
-  * 
-  * This file is a part of SproutCMS. 
-  * 
-  * SproutCMS is free software: you can redistribute it and/or modify it under the terms 
-  * of the GNU General Public License as published by the Free Software Foundation, either 
-  * version 2 of the License, or (at your option) any later version. 
-  * 
-  * For more information, visit <http://getsproutcms.com>. 
-  */ 
-   
- namespace Sprout\Controllers; 
-   
- use DOMDocument; 
- use Exception; 
- use InvalidArgumentException; 
- use PDO; 
- use RecursiveDirectoryIterator; 
- use RecursiveIteratorIterator; 
- use ReflectionMethod; 
- use ZipArchive; 
-   
- use Kohana; 
- use Kohana_404_Exception; 
- use Kohana_Exception; 
- use karmabunny\pdb\Exceptions\QueryException; 
- use karmabunny\pdb\Exceptions\RowMissingException; 
- use Sprout\Exceptions\ValidationException; 
- use Sprout\Helpers\Admin; 
- use Sprout\Helpers\AdminAuth; 
- use Sprout\Helpers\Archive; 
- use Sprout\Helpers\Auth; 
- use Sprout\Helpers\Constants; 
- use Sprout\Helpers\Csrf; 
- use Sprout\Helpers\DatabaseSync; 
- use Sprout\Helpers\Email; 
- use Sprout\Helpers\Enc; 
- use Sprout\Helpers\Export; 
- use Sprout\Helpers\ExportTableSQL; 
- use Sprout\Helpers\Fb; 
- use Sprout\Helpers\FileIndexing; 
- use Sprout\Helpers\FileUpload; 
- use Sprout\Helpers\Form; 
- use Sprout\Helpers\Html; 
- use Sprout\Helpers\ImportCMS; 
- use Sprout\Helpers\Inflector; 
- use Sprout\Helpers\Itemlist; 
- use Sprout\Helpers\Json; 
- use Sprout\Helpers\LaunchChecks; 
- use Sprout\Helpers\Navigation; 
- use Sprout\Helpers\Needs; 
- use Sprout\Helpers\Notification; 
- use Sprout\Helpers\Pdb; 
- use Sprout\Helpers\QrCode; 
- use Sprout\Helpers\QueryTo; 
- use Sprout\Helpers\Register; 
- use Sprout\Helpers\Request; 
- use Sprout\Helpers\Router; 
- use Sprout\Helpers\Session; 
- use Sprout\Helpers\Sprout; 
- use Sprout\Helpers\SubsiteSelector; 
- use Sprout\Helpers\Subsites; 
- use Sprout\Helpers\Text; 
- use Sprout\Helpers\TreenodeValueMatcher; 
- use Sprout\Helpers\Url; 
- use Sprout\Helpers\Validator; 
- use Sprout\Helpers\Validity; 
- use Sprout\Helpers\View; 
-   
-   
- /** 
- * Provides tools for dealing with the database 
- * Tools include a sync tool and a structure viewer 
- **/ 
- class DbToolsController extends Controller 
- { 
-     private $template_enabled = true; 
-   
-   
-   
-     /** 
-      * List of tools to show in the sidebar and index view 
-      */ 
-     public static $tools = [ 
-         'Development' => [ 
-             [ 'url' => 'dbtools/sql', 'name' => 'SQL', 'desc' => 'Allows the user to execute SQL queries' ], 
-             [ 'url' => 'dbtools/sync', 'name' => 'Database sync', 'desc' => 'Syncs the db structure to match db_struct.xml' ], 
-             [ 'url' => 'dbtools/struct', 'name' => 'View db structure', 'desc' => 'Shows table and column definitions' ], 
-             [ 'url' => 'dbtools/testSkinTemplates', 'name' => 'Test skin templates', 'desc' => 'Simple tool for testing skin templates' ], 
-             [ 'url' => 'dbtools/sessionEditor', 'name' => 'Session editor', 'desc' => 'Edit the $_SESSION' ], 
-             [ 'url' => 'dbtools/listRoutes', 'name' => 'Routes inspector', 'desc' => 'View a list of routes' ], 
-             [ 'url' => 'admin/extra/worker_job/manual_run', 'name' => 'Manual run worker job', 'desc' => 'Manually run a worker job in a browser' ], 
-             [ 'url' => 'admin/extra/cron_job/manual_run', 'name' => 'Manual run cron job', 'desc' => 'Manually run a cron job in a browser' ], 
-             [ 'url' => 'admin/style_guide', 'name' => 'Admin style guide', 'desc' => 'View styles of various admin features - form fields, etc' ], 
-         ], 
-         'Code generation' => [ 
-             [ 'url' => 'dbtools/moduleBuilder', 'name' => 'Module builder', 'desc' => 'Generates blank modules' ], 
-             [ 'url' => 'dbtools/moduleBuilderDb', 'name' => 'Database struct generator', 'desc' => 'Generates db_struct.xml content for a module' ], 
-             [ 'url' => 'dbtools/moduleBuilderExisting', 'name' => 'Module builder from existing', 'desc' => 'Generates modules from an existing db_struct.xml file' ], 
-             [ 'url' => 'dbtools/multimake', 'name' => 'Generate multiedit', 'desc' => 'Generate multiedit code' ], 
-         ], 
-         'Environment' => [ 
-             [ 'url' => 'dbtools/info', 'name' => 'Env and PHP info', 'desc' => 'Sprout information + phpinfo()' ], 
-             [ 'url' => 'dbtools/varDump', 'name' => 'Var dump', 'desc' => 'View session, cookie & server data'], 
-             [ 'url' => 'dbtools/email', 'name' => 'Test email', 'desc' => 'Renders form to send emails' ], 
-             [ 'url' => 'dbtools/launchChecks', 'name' => 'Launch checks', 'desc' => 'Run a series of self-tests to ensure everything is configured correctly' ], 
-             [ 'url' => 'admin/user-agent', 'name' => 'User agent tool', 'desc' => 'Show browser information<br><span>(this link doesn\'t require auth)</span>' ], 
-             [ 'url' => 'dbtools/generatePasswordHash', 'name' => 'Generate password hash', 'desc' => 'Generate a password hash to store in a config file' ], 
-             [ 'url' => 'dbtools/bcryptSpeed', 'name' => 'Test hashing speed', 'desc' => 'Test hasing speed of bcrypt' ], 
-             [ 'url' => 'dbtools/fileTypesIndexingSupport', 'name' => 'File indexing support', 'desc' => 'List of file types which can be indexed for full-text search' ], 
-         ], 
-         'Logs' => [ 
-             [ 'url' => 'dbtools/exceptionLog', 'name' => 'Exception log', 'desc' => 'Browse and search exceptions' ], 
-             [ 'url' => 'admin/intro/cron_job', 'name' => 'Cron job log', 'desc' => 'Cron (scheduled task) log' ], 
-             [ 'url' => 'admin/intro/worker_job', 'name' => 'Worker job log', 'desc' => 'Log of worker (background) processes' ], 
-         ], 
-         'Migration' => [ 
-             [ 'url' => 'dbtools/exportFiles', 'name' => 'Export files', 'desc' => 'Exports all files' ], 
-             [ 'url' => 'dbtools/exportTables', 'name' => 'Export database', 'desc' => 'Export tables to an SQL file' ], 
-             [ 'url' => 'dbtools/importFiles', 'name' => 'Import files', 'desc' => 'Imports a files into the cms' ], 
-             [ 'url' => 'dbtools/importTables', 'name' => 'Import database', 'desc' => 'Import database tables from a .sql file' ], 
-             [ 'url' => 'dbtools/importXML', 'name' => 'Import XML', 'desc' => 'Import Sprout2 CMS export' ], 
-         ], 
-     ]; 
-   
-   
-     /** 
-     * Constructor 
-     **/ 
-     public function __construct() 
-     { 
-         parent::__construct(); 
-   
-         // Command-line access does not require auth OR output buffering 
-         if (PHP_SAPI === 'cli') return; 
-   
-         // Require remote (super) auth 
-         AdminAuth::checkLogin(); 
-         if (AdminAuth::isSuper() !== true) { 
-             Notification::error('Access denied'); 
-             Url::redirect('admin'); 
-         } 
-   
-         // Don't start output buffering for some methods 
-         if (strpos(- Router ::$method, 'json_') === 0) return;
 
-         if (Router::$method == 'gettempfile') return; 
-         if (Router::$method == 'sqlcsv') return; 
-   
-         // Execute some code for each module 
-         // This usually just loads some menu items 
-         $module_paths = Register::getModuleDirs(); 
-         foreach ($module_paths as $path) { 
-             $path .= '/admin_load.php'; 
-         } 
-   
-         // Load registered API test controllers 
-         $apis = Register::getDbtoolsApi(); 
-         if (count($apis) > 0) self::$tools['APIs'] = []; 
-   
-         foreach ($apis as $api) { 
-             // Validate 
-             if (empty($api['class']) || empty($api['method'])) continue; 
-   
-             // Populate dbtools list 
-             self::$tools['APIs'][] = array( 
-                 'url' => sprintf('dbtools/api/%s/%s',-  Enc ::url($api['class']),-  Enc ::url($api['method'])),
 
-                 'name' => !empty($api['title'])-  ?  $api['title'] : $api['class'],
 
-                 'desc' => !empty($api['desc'])-  ?  $api['desc'] : 'API test form',
 
-             ); 
-         } 
-   
-         // Output buffering allows the methods to "echo" directly, and then the output 
-         // is captured and wrapped up in a template by the ->template() method 
-     } 
-   
-   
-     /** 
-      * Render dbtools template 
-      * 
-      * @param string HTML 
-      * @return void Echos HTML directly 
-      */ 
-     private function template($main_title, $html = null) 
-     { 
-         if (!$this->template_enabled) return; 
-   
-   
-   
-         $nav = new View('sprout/dbtools/navigation'); 
-   
-         $view = new View('sprout/admin/main_layout'); 
-         $view->main_title = $main_title; 
-         $view->browser_title = $main_title; 
-         $view->controller_name = 'dbtools'; 
-         $view->controller_navigation_name = 'Dev tools'; 
-         $view->live_url = ''; 
-         $view->nav = $nav->render(); 
-         $view->nav_tools = ''; 
-         $view->main_content = $main_content . $html; 
-   
-         echo $view->render(); 
-     } 
-   
-   
-     /** 
-     * Shows a list of database tools 
-     **/ 
-     public function index() 
-     { 
-         $view = new View('sprout/dbtools/overview'); 
-         $view->sections = self::$tools; 
-         $view->base_class = 'dbtools-box white-box column column-3'; 
-   
-         $this->template('Database and system tools', $view->render()); 
-     } 
-   
-   
-     /** 
-     * Output some sprout and platform info 
-     **/ 
-     public function info() 
-     { 
-             'Server software' => @$_SERVER['SERVER_SOFTWARE'], 
-             'Server OS' => PHP_OS, 
-             'IN_PRODUCTION' => IN_PRODUCTION ? 'true' : 'false', 
-             'DOCROOT' => DOCROOT, 
-             'KOHANA' => KOHANA, 
-             'APPPATH' => APPPATH, 
-             'HTTP_X_FORWARDED_FOR' => @$_SERVER['HTTP_X_FORWARDED_FOR'], 
-             'REMOTE_ADDR' => @$_SERVER['REMOTE_ADDR'], 
-             'Request::userIp' => Request::userIp(), 
-             'Request::method' => Request::method(), 
-             'Request::isAjax' => (Request::isAjax() ? 'true' : 'false'), 
-             'Request::protocol' => Request::protocol(), 
-             'PHP date' => date('Y-m-d H:i:s'), 
-         ); 
-   
-         try { 
-             $row = Pdb::q("SELECT NOW() AS now, @@session.time_zone AS tz", [], 'row'); 
-             $vals['MySQL date'] = $row['now']; 
-             $vals['MySQL TZ'] = $row['tz']; 
-         } catch (Exception $ex) { 
-             $vals['MySQL date'] = 'Lookup failure'; 
-             $vals['MySQL TZ'] = 'Lookup failure'; 
-         } 
-   
-         $vals['Sprout::absRoot'] = Sprout::absRoot(); 
-         $vals['Subsite ID'] = SubsiteSelector::$subsite_id; 
-   
-         $q = "SELECT id, name, code, active FROM ~subsites ORDER BY id"; 
-         $subsites = Pdb::query($q, [], 'arr'); 
-   
-         $view = new View('sprout/dbtools/php_info'); 
-         $view->vals = $vals; 
-         $view->subsites = $subsites; 
-   
-         $this->template('Env and PHP info', $view->render()); 
-     } 
-   
-   
-     /** 
-      * Benchmark the server to find appropriate cost parameter 
-      */ 
-     public function bcryptSpeed() 
-     { 
-         $results = []; 
-         $cost = 8; 
-         $thresh_secs = 0.5; 
-         do { 
-             password_hash("test", PASSWORD_BCRYPT, ['cost' => $cost]); 
-             $results[$cost] = $time_secs; 
-             ++$cost; 
-         } while ($time_secs < $thresh_secs); 
-   
-         echo '<p>This tool reports the time required to hash a password using bcrypt and varying levels of difficulty cost.</p>'; 
-         echo '<pre>'; 
-         foreach ($results as $cost => $time_secs) { 
-             echo 'Cost ', $cost, ' took ', number_format($time_secs * 1000, 2), ' ms',-  PHP_EOL ;
 
-         } 
-         echo '</pre>'; 
-   
-         $this->template('Test hashing speed'); 
-     } 
-   
-   
-     /** 
-      * Renders SQL result set into a table 
-      * 
-      * @param PDOStatement $results Query result 
-      * @param mixed 
-      * @return int Number of rows 
-      */ 
-     private function outputSqlResultset($results, $headings = null) 
-     { 
-         if ($results->columnCount() == 0) return; 
-         if ($results->rowCount() == 0) return; 
-   
-         $results->setFetchMode(PDO::FETCH_NUM); 
-         $columns = []; 
-   
-         for ($i = 0; $i < $results->columnCount(); ++$i) { 
-             $col = $results->getColumnMeta($i); 
-             $columns[] = $col['name']; 
-         } 
-   
-         $view = new View('sprout/dbtools/sql_result'); 
-         $view->results = $results; 
-         $view->columns = $columns; 
-         $view->render(true); 
-   
-         return $results->rowCount(); 
-     } 
-   
-     # 
-     # Tools are below. 
-     # 
-   
-   
-     /** 
-     * Syncs the db structure to match db_struct.xml 
-     **/ 
-     public function sync() 
-     { 
-         $act = false; 
-         if (isset($_POST['act'])-  and  $_POST['act'] === 'yes') $act = true;
 
-         if (PHP_SAPI === 'cli') $act = true; 
-   
-         // If there is no tables, act straight away without asking the user 
-         $num = 0; 
-         try { 
-             $num += (int) Pdb::query("SELECT COUNT(*) FROM ~operators", [], 'val'); 
-             $num += (int) Pdb::query("SELECT COUNT(*) FROM ~pages", [], 'val'); 
-             $num += (int) Pdb::query("SELECT COUNT(*) FROM ~subsites", [], 'val'); 
-         } catch (Exception $ex) {} 
-         if ($num == 0) { 
-             $act = true; 
-         } 
-   
-         $sync = new DatabaseSync($act); 
-   
-         $chk = $sync->checkConnPermissions(); 
-         if ($chk !== true) { 
-             echo '<p>Insufficent database permissions for this tool.</p>'; 
-             echo '<p>Additional permissions required: ', implode(', ', $chk), '.</p>'; 
-             return; 
-         } 
-   
-         $sync->loadStandardXmlFiles(); 
-         $sync->sanityCheck(); 
-   
-         if ($sync->hasLoadErrors()) { 
-             echo $sync->getLoadErrorsHtml(); 
-             $this->template('Database sync'); 
-             return; 
-         } 
-   
-         $out = $sync->updateDatabase(); 
-         if (PHP_SAPI === 'cli') { 
-             if ($out) { 
-                 $out = str_replace('<pre class="query">', ' --> ', $out); 
-                 echo PHP_EOL; 
-             } else { 
-                 echo 'Everything is up to date.', PHP_EOL; 
-             } 
-             return; 
-         } 
-   
-         if ($out == '') { 
-             echo '<p>Everything is up to date.</p>'; 
-         } else { 
-             echo '<style>'; 
-             echo '.update-log .query { color: blue; border: none; padding: 0 0 0 100px; }'; 
-             echo '.update-log b { display: inline-block; width: 100px; }'; 
-             echo '.update-log p.heading { margin: 20px 0 5px; }'; 
-             echo '.update-log pre { margin-bottom: 0; }'; 
-             echo '</style>'; 
-             if (!$act) { 
-                 echo '<form action="dbtools/sync" method="post">'; 
-                 echo '<input type="hidden" name="act" value="yes">'; 
-                 echo '<div class="action-bar"><button type="submit" class="button button-orange icon-after icon-loop">Run this sync</button></div>'; 
-                 echo '</form>'; 
-             } 
-             echo '<div class="update-log">'; 
-             echo $out; 
-             echo '</div>'; 
-             if (!$act) { 
-                 echo '<form action="dbtools/sync" method="post">'; 
-                 echo '<input type="hidden" name="act" value="yes">'; 
-                 echo '<div class="action-bar"><button type="submit" class="button button-orange icon-after icon-loop">Run this sync</button></div>'; 
-                 echo '</form>'; 
-             } 
-         } 
-   
-         // Clear the Kohana caches too 
-         if (file_exists(- APPPATH  . 'cache/kohana_configuration')) {
 
-             unlink(- APPPATH  . 'cache/kohana_configuration');
 
-         } 
-         if (file_exists(- APPPATH  . 'cache/kohana_find_file_paths')) {
 
-             unlink(- APPPATH  . 'cache/kohana_find_file_paths');
 
-         } 
-             unlink(- APPPATH  . 'cache/kohana_language');
 
-         } 
-   
-         $this->template('Database sync'); 
-     } 
-   
-   
-     /** 
-     * Shows table and column definitions 
-     **/ 
-     public function struct($arg = '') 
-     { 
-         // Show columns if a table was specified 
-             echo "<h3>Columns from {$arg}</h3>"; 
-             $q = "SHOW COLUMNS FROM `{$arg}`"; 
-             $res = Pdb::query($q, [], 'pdo'); 
-             $this->outputSqlResultset($res); 
-             $res->closeCursor(); 
-   
-             echo "<h3>Example data from {$arg}</h3>"; 
-             $q = "SELECT * FROM `{$arg}` LIMIT 3"; 
-             $res = Pdb::query($q, [], 'pdo'); 
-             $count = $this->outputSqlResultset($res); 
-             $res->closeCursor(); 
-             if ($count == 0) echo '<p><i>No data is in this table at this time</i></p>'; 
-         } 
-   
-   
-         // Show a list of tables 
-         $params = []; 
-         $q = "SHOW TABLE STATUS"; 
-         if (!empty($_GET['search'])) { 
-             $q .= " WHERE NAME LIKE CONCAT('%', ?, '%')"; 
-             $params[] = $_GET['search']; 
-         } 
-         $res = Pdb::query($q, $params, 'pdo'); 
-   
-         $ignore_cols = ['Row_format', 'Max_data_length', 'Auto_increment', 'Comment', 'Version', 'Create_time', 
-             'Update_time', 'Check_time', 'Collation', 'Checksum', 'Create_options']; 
-         for ($i = 0; $i < $res->columnCount(); ++$i) { 
-             $meta = $res->getColumnMeta($i); 
-             $val = $meta['name']; 
-             if (in_array($val, $ignore_cols)) continue; 
-             $headings[$val] = $val; 
-         } 
-   
-         // Remove ignored columns 
-         $results = []; 
-         foreach ($res as $row) { 
-             foreach ($ignore_cols as $ignore) { 
-             } 
-   
-             $columns = []; 
-             foreach ($row as $name => $val) { 
-                 $columns[$name] = $val; 
-             } 
-             $results[] = $columns; 
-         } 
-   
-         $res->closeCursor(); 
-   
-         $view = new View('sprout/dbtools/db_struct'); 
-         $view->headings = $headings; 
-         $view->results = $results; 
-   
-         $this->template('Database structure', $view->render()); 
-     } 
-   
-   
-     /** 
-     * Splits a set of SQL queries into individual queries 
-     **/ 
-     private function splitSql($all) 
-     { 
-   
-   
-         $offset = 0; 
-         $query = ''; 
-         $quote = ''; 
-   
-         while ($offset < $length) { 
-             // Search for end-of-statement 
-             if ($quote == '' and $all[$offset] == ';') { 
-                 $queries[] = $query; 
-                 $query = ''; 
-                 $offset++; 
-                 continue; 
-             } 
-   
-             if ($quote != '' and $all[$offset] == '\\') { 
-                 $query .= '\\'; 
-                 $offset++; 
-                 $query .= $all[$offset]; 
-                 $offset++; 
-                 continue; 
-             } 
-   
-             if ($all[$offset] == "'") { 
-                 if ($quote == "'") { 
-                     $quote = ''; 
-                 } else if ($quote == '') { 
-                     $quote = "'"; 
-                 } 
-             } 
-   
-             if ($all[$offset] == '"') { 
-                 if ($quote == '"') { 
-                     $quote = ''; 
-                 } else if ($quote == '') { 
-                     $quote = '"'; 
-                 } 
-             } 
-   
-             $query .= $all[$offset]; 
-             $offset++; 
-         } 
-   
-         if ($query) $queries[] = $query; 
-   
-         return $queries; 
-     } 
-   
-   
-     /** 
-     * Allows the user to execute SQL queries 
-     **/ 
-     public function sql() 
-     { 
-         $out = ''; 
-   
-         $vars = [0 => []]; 
-         $binds = []; 
-             $idx = 0; 
-             foreach ($_POST['vars'] as $var) { 
-                 if (empty($var['key'])-  and  (!isset($var['val'])-  or  $var['val'] === '')) continue;
 
-   
-                 $key = (string) @$var['key']; 
-                 $val = (string) @$var['val']; 
-                 if ($key) { 
-                     $binds[$key] = $val; 
-                 } else { 
-                     $binds[] = $val; 
-                 } 
-                 $vars[$idx]['key'] = $key; 
-                 $vars[$idx]['val'] = $val; 
-                 ++$idx; 
-             } 
-         } 
-   
-   
-         // Split up the queries 
-         if (isset($_POST['sql'])) { 
-             Csrf::checkOrDie(); 
-   
-             $successes = 0; 
-             $failures = 0; 
-   
-             $queries = $this->splitSql($_POST['sql']); 
-   
-             // Execute the queries 
-             foreach ($queries as $q) { 
-                 $res = false; 
-   
-                 if ($q == '') continue; 
-   
-                 $out .= "<div class=\"sql-block\">\n"; 
-   
-                 $out .= '<pre class="sql">' . Enc::html($q) . '</pre>'; 
-   
-                 if (!empty($_POST['profile'])) { 
-                     Pdb::query("SET profiling=1", [], 'count'); 
-                 } 
-   
-                 $bind_subset = Pdb::getBindSubset($q, $binds); 
-                 try { 
-                     $res = Pdb::query($q, $bind_subset, 'pdo'); 
-                     $successes ++; 
-                 } catch (QueryException $ex) { 
-                     $out .= '<ul class="messages all-type-error"><li class="error">'; 
-                     $out .= nl2br(- Enc ::html($ex->getMessage()));
 
-   
-                     $failures ++; 
-   
-                     // If a DROP TABLE query fails due to a foreign key constraint, list the constraining columns 
-                     if ($ex->state == 23000) { 
-                         $matches = []; 
-                         if (preg_match('/^DROP\s+TABLE\s+~([a-z0-9_]+)$/i', $q, $matches)) { 
-                             $deps = Pdb::getDependentKeys($matches[1]); 
-                                 $out .= '<p style="margin-bottom: 0;">The following columns link to the specified table:</p>'; 
-                                 $out .= '<p style="padding-left: 30px; margin: 0;">'; 
-                                 $out = ''; 
-                                 foreach ($deps as $dep) { 
-                                     $out .= Enc::html("{$dep['table']}.{$dep['column']}") . '<br>'; 
-                                 } 
-                                 $out .= '</p>'; 
-                             } 
-                         } 
-                     } 
-                     $out .= '</li></ul>'; 
-                 } 
-   
-                 if (!empty($_POST['profile'])) { 
-                     Pdb::query("SET profiling=0", [], 'count'); 
-                 } 
-   
-                 if (! $res) continue; 
-   
-                 $out .= sprintf('<ul class="messages"><li class="neutral neutral-grey">%u %s</li></ul>', $res->rowCount(),-  Inflector ::singular('rows', $res->rowCount()));
 
-   
-                 $this->outputSqlResultset($res); 
-   
-                 $res->closeCursor(); 
-   
-                 if (!empty($_POST['explain'])) { 
-                     $q = "EXPLAIN {$q}"; 
-                     $res = Pdb::query($q, [], 'pdo'); 
-   
-                     $this->outputSqlResultset($res); 
-   
-                     $res->closeCursor(); 
-                 } 
-                 $out .= "</div>\n"; 
-             } 
-   
-             if ((!empty($queries)-  and  count($queries) > 0)-  and  $failures > 0) {
 
-                 Notification ::error(sprintf('Failed to execute %u %s. Scroll down for results', $failures,-  Inflector ::singular('queries', $failures)));
-             } 
-   
-             if ((!empty($queries)-  and  count($queries) > 0)-  and  $successes > 0) {
 
-                 Notification ::confirm(sprintf('Executed %u %s. Scroll down for results', $successes,-  Inflector ::singular('queries', $successes)));
-             } 
-   
-             // Show profiling info 
-             if (!empty($_POST['profile'])) { 
-   
-                 $q = "SHOW PROFILES"; 
-                 $res = Pdb::query($q, [], 'arr'); 
-   
-                 foreach ($res as $row) { 
-                     $q = "SHOW PROFILE FOR QUERY {$row['Query_ID']}"; 
-                     $res2 = Pdb::query($q, [], 'pdo'); 
-   
-                     $out .= "<h3>Query #{$row['Query_ID']}; total duration: {$row['Duration']}</h3>"; 
-                     $out .= '<pre class="sql">' . Enc::html($row['Query']) . '</pre>'; 
-   
-                     $this->outputSqlResultset($res2); 
-   
-                     $res2->closeCursor(); 
-                 } 
-             } 
-         } 
-   
-         $out .= '</div>'; 
-   
-         $res = Pdb::query('SHOW TABLES', [], 'col'); 
-         $tables = []; 
-         foreach ($res as $row) { 
-         } 
-   
-         $view = new View('sprout/dbtools/sql'); 
-         $view->vars = $vars; 
-         $view->tables = $tables; 
-         $view->results = $out; 
-   
-         echo $view->render(); 
-         $this->template('SQL query'); 
-     } 
-   
-   
-     public function sqlcsv() 
-     { 
-         AdminAuth::checkLogin(); 
-         Csrf::checkOrDie(); 
-   
-         if (empty($_POST['sql'])) { 
-             Url::redirect('dbtools/sql'); 
-         } 
-   
-         $rows = Pdb::q($_POST['sql'], [], 'pdo'); 
-         $csv = QueryTo::csv($rows); 
-         $rows->closeCursor(); 
-   
-         if (!$csv) { 
-             echo "CSV generation failed"; 
-             return; 
-         } 
-   
-         header('Content-type: text/csv'); 
-         header('Content-disposition: attachment; filename="sql.csv"'); 
-         echo $csv; 
-     } 
-   
-   
-     public function ajaxTableDefn($table) 
-     { 
-         $table = trim($table, '~'); 
-         try { 
-             Pdb::validateIdentifier($table); 
-         } catch (InvalidArgumentException $ex) { 
-             return; 
-         } 
-   
-         // Get FKs and group them by columns 
-         $fks = []; 
-         $fk_defs = Pdb::getForeignKeys($table); 
-         foreach ($fk_defs as $fk) { 
-             $fks[$fk['source_column']][] = "{$fk['target_table']}.{$fk['target_column']}"; 
-         } 
-   
-         $allowed_keys = ['Field', 'Type', 'Null', 'Key', 'Default', 'Collation']; 
-         $raw_res = Pdb::q("SHOW FULL COLUMNS FROM ~{$table}", [], 'pdo'); 
-         $res = []; 
-         foreach ($raw_res as $raw_row) { 
-             $row = []; 
-             foreach ($allowed_keys as $key) { 
-                 $row[$key] = $raw_row[$key]; 
-             } 
-             if (isset($fks[$row['Field']])) { 
-                 $row['FK'] = implode(', ', $fks[$row['Field']]); 
-             } else { 
-                 $row['FK'] = ''; 
-             } 
-             $res[] = $row; 
-         } 
-         $raw_res->closeCursor(); 
-   
-         echo Json::out($res); 
-     } 
-   
-   
-     /** 
-     * Import database tables from a .sql file 
-     **/ 
-     public function importTables() 
-     { 
-             echo '<p><em>Accepts raw SQL files, and zip archives.</em></p>'; 
-         } else { 
-             echo '<p><em>Accepts raw SQL files.</em></p>'; 
-         } 
-   
-         echo '<form action="SITE/dbtools/importSave" method="post" enctype="multipart/form-data">'; 
-         echo Csrf::token(); 
-         Form::nextFieldDetails('File', true); 
-         echo Form::upload('filename'); 
-         echo '<div class="action-bar"><button type="submit" class="button icon-after icon-file_upload">Upload file</button></div>'; 
-         echo '</form>'; 
-   
-         $this->template('Import tables'); 
-     } 
-   
-   
-     public function importSave() 
-     { 
-         Csrf::checkOrDie(); 
-   
-   
-         $valid_exts = ['sql']; 
-             $valid_exts[] = 'zip'; 
-         } 
-   
-             echo "Invalid file type, suported types: " . implode(', ', $valid_exts); 
-             return; 
-         } 
-   
-         // Determine temp filename 
-         $tempname = "dbtools_import_{$timestamp}.{$ext}"; 
-   
-         $res = @copy($_FILES['filename']['tmp_name'],-  APPPATH  . 'temp/' . $tempname);
 
-         if (! $res) { 
-             echo 'Unable to copy file to temporary directory'; 
-             return; 
-         } 
-   
-         Url::redirect('dbtools/importOptions?tempname=' . Enc::url($tempname)); 
-     } 
-   
-   
-   
-     public function importOptions() 
-     { 
-         echo '<p>Uploaded file: <code>', Enc::html($_GET['tempname']), '</code></p>'; 
-   
-         $tempname = APPPATH . 'temp/' . $_GET['tempname']; 
-         if (File::getExt($tempname) == 'zip') { 
-             $za = new ZipArchive(); 
-             $za->open($tempname); 
-             $sql = 0; 
-             for ($i = 0; $i < $za->numFiles; $i++) { 
-                 $info = $za->statIndex($i); 
-                     $sql++; 
-                 } 
-             } 
-             $za->close(); 
-   
-             echo "<p>It's a zip file containing {$sql} SQL files.</p>"; 
-   
-         } else if (File::getExt($tempname) == 'sql') { 
-             echo "<p>It's a single SQL file.</p>"; 
-         } 
-   
-   
-         echo '<form action="SITE/dbtools/importAction" method="post" target="process" onsubmit="$(\'iframe\').show(); $(this).find(\'.action-bar\').remove();">'; 
-         echo Csrf::token(); 
-         echo '    <input type="hidden" name="tempname" value="', Enc::html($_GET['tempname']), '">'; 
-         echo '    <div class="action-bar"><button type="submit" class="button icon-before icon-check">Process file</button></div>'; 
-         echo '</form>'; 
-   
-         echo '<iframe name="process" style="border: 1px #999 dashed; margin: 30px 0; width: 700px; height: 300px; display: none;"></iframe>'; 
-   
-         $this->template('Import tables'); 
-     } 
-   
-     /** 
-     * Action for importing tables 
-     **/ 
-     public function importAction() 
-     { 
-         Csrf::checkOrDie(); 
-         Kohana::closeBuffers(); 
-         echo '<style>body { font-size: 11px; font-family: sans-serif; } p { margin: 0; }</style>'; 
-   
-   
-         $tempname = APPPATH . 'temp/' . $_POST['tempname']; 
-         echo "<p>Processing: '{$tempname}'.</p>"; 
-   
-   
-         // Rip SQL files out of ZIP file 
-         if (File::getExt($tempname) == 'zip') { 
-             $za = new ZipArchive(); 
-             $za->open($tempname); 
-   
-             for ($i = 0; $i < $za->numFiles; $i++) { 
-                 $info = $za->statIndex($i); 
-   
-                     echo "<p>Found sql file in zip: '{$info['name']}'.</p>"; 
-                     $sql_files[] = $za->getFromIndex($i); 
-                 } 
-             } 
-   
-             $za->close(); 
-   
-         } else if (File::getExt($tempname) == 'sql') { 
-             echo "<p>Found sql file: '{$tempname}'.</p>"; 
-         } 
-   
-   
-   
-         echo '<p>Processing ' . count($sql_files) . ' sql file(s).</p>'; 
-   
-         // Process files 
-         $idx = 0; 
-         foreach ($sql_files as $file) { 
-             $idx++; 
-             echo '<p>Processing file # ', $idx, '.</p>'; 
-   
-             $queries = $this->splitSql($file); 
-   
-             echo '<p>File # ', $idx, ' contains ', count($queries), ' queries.</p>'; 
-   
-             Pdb::q("SET FOREIGN_KEY_CHECKS=0", [], 'null'); 
-   
-             $qidx = 0; 
-             foreach ($queries as $q) { 
-                 if ($q == '') continue; 
-   
-                 try { 
-                     Pdb::q($q, [], 'count'); 
-                 } catch (Exception $ex) { 
-                     echo '<p>Failed query: <code>' . Enc::html($q) . '</code>, exception: ', 
-                         Enc::html($ex->getMessage()), '</p>'; 
-                 } 
-   
-   
-                 $qidx++; 
-                 if ($qidx % 1000 == 0) { 
-                     echo '<p>Done ', $qidx, ' queries.</p>'; 
-                 } 
-             } 
-   
-   
-             Pdb::q("SET FOREIGN_KEY_CHECKS=1", [], 'null'); 
-   
-             echo '<p>File # ', $idx, ' finished.</p>'; 
-         } 
-   
-         echo '<p>Import complete, running sync...</p>'; 
-   
-         $sync = new DatabaseSync(true); 
-         $sync->loadStandardXmlFiles(); 
-         $sync->sanityCheck(); 
-   
-         if ($sync->hasLoadErrors()) { 
-             echo $sync->getLoadErrorsHtml(); 
-         } 
-   
-         echo $sync->updateDatabase(); 
-   
-         echo '<p>Sync complete</p>'; 
-   
-     } 
-   
-   
-     /** 
-     * Run a series of self-tests to ensure everything is configured correctly 
-     **/ 
-     public function launchChecks() 
-     { 
-         $results = LaunchChecks::runTests(); 
-   
-         echo '<style>'; 
-         echo '.status--okay { color: #005306; }'; 
-         echo '.status--warning { color: #C26600; }'; 
-         echo '.status--error { color: #B20000; }'; 
-         echo '</style>'; 
-   
-         $itemlist = new Itemlist(); 
-         $itemlist->items = $results; 
-         $itemlist->main_columns = [ 
-             'Check' => 'check', 
-             'Skin' => 'skin', 
-             'Result' => 'result', 
-             'Message' => 'message', 
-         ]; 
-         $itemlist->setRowClassesFunc(function($row){ 
-             return 'status--' . $row['result']; 
-         }); 
-         echo $itemlist->render(); 
-   
-         $this->template('Launch checks'); 
-     } 
-   
-   
-     /** 
-     * Returns a list of file types which can be indexed for fulltext search 
-     **/ 
-     public function fileTypesIndexingSupport() 
-     { 
-             'txt' => '', 
-             'csv' => '', 
-             'pdf' => 'pdftotext', 
-             'doc' => 'antiword', 
-             'docx' => 'perl', 
-             'odt' => 'odt2txt', 
-             'xls' => 'xls2csv' 
-         ); 
-   
-         $out = '<table class="main-list">'; 
-         $out .= '<thead><tr><th>Format</th><th>Supported?</th></thead>'; 
-         $out .= '<tbody>'; 
-         foreach ($exts as $e => $pkg) { 
-             $out .= '<tr>'; 
-             $out .= '<td>' . $e . '</td>'; 
-   
-             if (FileIndexing::isExtSupported($e)) { 
-                 $out .= '<td>Yes</td>'; 
-             } else { 
-                 $out .= '<td><span style="color: #900;">No.</span> Please install "' . $pkg . '".</td>'; 
-             } 
-   
-             $out .= '</tr>'; 
-         } 
-         $out .= '</tbody>'; 
-         $out .= '</table>'; 
-   
-         echo $out; 
-         $this->template('File types with indexing support'); 
-     } 
-   
-   
-     /** 
-     * Export tables to an SQL file 
-     **/ 
-     public function exportTables() 
-     { 
-         ?> 
-         <script type="text/javascript"> 
-         $(document).ready(function() { 
-             $('a.next-toggle').click(function() { 
-                 $(this).next().toggle(); 
-                 return false; 
-             }); 
-   
-             $('a.next-toggle').next().hide(); 
-         }); 
-         </script> 
-         <?php 
-   
-   
-         // Show a list of tables 
-         $q = "SHOW TABLE STATUS"; 
-         $db_tables = Pdb::q($q, [], 'arr'); 
-   
-         ?> 
-         <p><a class="preview" id="select-all-none" href="#">Select all/none</a></p> 
-         <script type="text/javascript"> 
-         $('#select-all-none').click(function(){ 
-             var all_checked = true; 
-             $("input[name*='tables[']").each(function() { 
-                 if (!$(this).attr('checked')) all_checked = false; 
-             }); 
-             if (all_checked) { 
-                 $("input[name*='tables[']").each(function() { 
-                     $(this).removeAttr('checked'); 
-                 }); 
-             } else { 
-                 $("input[name*='tables[']").each(function() { 
-                     $(this).attr('checked', 'checked'); 
-                 }); 
-             } 
-             return false; 
-         }); 
-         </script> 
-         <?php 
-   
-         echo '<form action="SITE/dbtools/exportAction" method="post">'; 
-         echo Csrf::token(); 
-         echo '<table class="main-list main-list-no-js">'; 
-         echo '<col width="1"><col width="230"><col width="40"><col width="40"><col width="200"><col width="200"><col width="1"><col width="1"><col width="75">'; 
-         echo '<thead><tr><th style="width: 1px;"> </th>'; 
-         echo '<th>Name</th><th>Rows</th><th>Size</th><th colspan="2">Options</th><th>Drop</th><th>Structure</th><th>Data</th>'; 
-         echo '</tr></thead>'; 
-         echo '<tbody>'; 
-         $idx = 0; 
-         $prefix = Pdb::prefix(); 
-         foreach ($db_tables as $table) { 
-             $column_options = '<p>No options yet.</p>'; 
-   
-             $where_options = '<p><b>Where clause:</b>'; 
-             $where_options .= "<br><input type=\"text\" name=\"where[{$idx}]\"></p>"; 
-   
-             $type_options = "<select name=\"data[{$idx}]\">"; 
-             $type_options .= '<option value="' . ExportTableSQL::DATA_INSERT . '">Insert</option>'; 
-             $type_options .= '<option value="' . ExportTableSQL::DATA_BOTH . '">Insert...update</option>'; 
-             $type_options .= '<option value="' . ExportTableSQL::DATA_UPDATE .'">Update</option>'; 
-             $type_options .= '<option value="' . ExportTableSQL::DATA_CSV . '">CSV file</option>'; 
-             $type_options .= '<option value="' . ExportTableSQL::DATA_NONE . '">None</option>'; 
-             $type_options .= '</select>'; 
-   
-             $checked = ''; 
-             if (strpos($table['Name'], $prefix) === 0) $checked = ' checked'; 
-   
-             echo '<tr>'; 
-             echo "<td><input type=\"checkbox\" name=\"tables[{$idx}]\" value=\"{$table['Name']}\"{$checked}></td>"; 
-             echo "<td>{$table['Name']}</td>"; 
-             echo "<td>{$table['Rows']}</td>"; 
-             echo "<td>" . $this->sizeToHuman($table['Data_length']) . "</td>"; 
-             echo '<td><a href="#" class="next-toggle">Columns</a><div>' . $column_options . '</div></td>'; 
-             echo '<td><a href="#" class="next-toggle">Where clause</a><div>' . $where_options . '</div></td>'; 
-             echo "<td><input type=\"checkbox\" name=\"drop[{$idx}]\" value=\"1\" checked></td>"; 
-             echo "<td><input type=\"checkbox\" name=\"structure[{$idx}]\" value=\"1\" checked></td>"; 
-             echo '<td>' . $type_options . '</td>'; 
-             echo '</tr>'; 
-   
-             $idx++; 
-         } 
-         echo '</tbody>'; 
-         echo '</table>'; 
-   
-         echo '<p><input type="checkbox" name="split_table" value="1"> Split the export per table.</p>'; 
-         echo '<p><input type="checkbox" name="split_size" value="1" checked> Split the export into chunks no bigger than <input type="text" name="split_amount" value="8m">. <small>(prefixes: k, m, g)</small></p>'; 
-   
-         echo '<p><input type="checkbox" name="compress" value="1" checked> Compress the file into a zip archive.</p>'; 
-   
-         echo '<p><b>DBMS:</b>   <select name="dbms" style="width: 100px;"><option>MySQL<option>SQLite</select></p>'; 
-   
-         echo '<div class="action-bar"><button type="submit" class="button icon-after icon-save">Export</button></div>'; 
-         echo '</form>'; 
-   
-   
-         $this->template('Export tables'); 
-     } 
-   
-   
-     /** 
-      * Render table data size in human readable form 
-      * @param int $size 
-      * @return string HTML 
-      */ 
-     private function sizeToHuman($size) 
-     { 
-         static $types = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 
-         $size = (int) $size; 
-   
-         $type = 0; 
-         while ($size > 1024) { 
-             $size /= 1024; 
-             $type++; 
-             if ($type > 5) break; 
-         } 
-   
-         return sprintf('%s %s', round($size, 1), $types[$type]); 
-     } 
-   
-   
-     /** 
-     * Does the actual export 
-     * 
-     * @post tables Array of tables 
-     * @post dbms DBMS - MySQL or SQLite 
-     * @post split_tables Split the export per table 
-     * @post split_size Split the export by size 
-     * @post split_amount Split the export by size - size in kb, mb or gb 
-     * @post compress Should the data be compressed 
-     * @return void Outputs HTML directly 
-     **/ 
-     public function exportAction() 
-     { 
-         Csrf::checkOrDie(); 
-   
-         if (empty($_POST['tables'])) { 
-             throw new InvalidArgumentException('No tables specified'); 
-         } 
-   
-         $temp_filename = 'temp/export-' . time() . '-'; 
-   
-         $class = 'Sprout\Helpers\ExportDBMS_' . $_POST['dbms']; 
-         $dbms = Sprout::instance($class); 
-   
-         $export = new Export(); 
-         $export->setFilenamePrefix(APPPATH . $temp_filename); 
-         $export->setDbms($dbms); 
-   
-         if (!empty($_POST['split_table'])) { 
-             $export->split_table = true; 
-         } 
-   
-         if ( 
-             isset($_POST['split_size']) 
-             and  isset($_POST['split_amount'])
-             and  preg_match('/([0-9]+)\s?([kmg])/', $_POST['split_amount'], $matches)
-         ) { 
-             if ($matches[2] == 'k') $amt = $matches[1] * 1000; 
-             if ($matches[2] == 'm') $amt = $matches[1] * 1000 * 1000; 
-             if ($matches[2] == 'g') $amt = $matches[1] * 1000 * 1000 * 1000; 
-             $export->split_size = $amt; 
-         } 
-   
-         foreach ($_POST['tables'] as $idx => $table_name) { 
-             $table = new ExportTableSQL(); 
-             $table->name = $table_name; 
-             $table->drop = (bool)@$_POST['drop'][$idx]; 
-             $table->structure = (bool)@$_POST['structure'][$idx]; 
-             $table->data = $_POST['data'][$idx]; 
-             $table->where = $_POST['where'][$idx]; 
-   
-             $export->addTable($table); 
-         } 
-   
-         $export->exportSql(); 
-   
-         if (!empty($_POST['compress'])) { 
-             $name = Kohana::config('sprout.site_title'); 
-   
-             $name = 'sql_' . date('Y-m-d') . '_' . $name . '.zip'; 
-             $export->buildArchive($name); 
-         } 
-   
-         $files = $export->getGeneratedFiles(); 
-         foreach ($files as $f) { 
-             echo "<p><a href=\"SITE/dbtools/gettempfile/export-database/{$f_url}/{$f}\">{$f}</a></p>"; 
-         } 
-   
-         $this->template('Export tables'); 
-     } 
-   
-   
-     /** 
-     * Imports a files into the cms 
-     **/ 
-     public function importFiles() 
-     { 
-             echo '<p><em>ARGH! No ZIP support!</em></p>'; 
-             return; 
-         } 
-   
-         echo '<form action="SITE/dbtools/importFileAction" method="post" enctype="multipart/form-data">'; 
-         echo Csrf::token(); 
-         Form::nextFieldDetails('File', true); 
-         echo Form::upload('filename'); 
-         echo '<div class="action-bar"><button type="submit" class="button icon-after icon-file_upload">Import files</button></div>'; 
-         echo '</form>'; 
-   
-         $this->template('Import files'); 
-     } 
-   
-     /** 
-     * Action for importing files 
-     **/ 
-     public function importFileAction() 
-     { 
-         Csrf::checkOrDie(); 
-   
-             echo '<p><em>ARGH! No ZIP support!</em></p>'; 
-             return; 
-         } 
-   
-         if (empty($_FILES['filename'])-  or  $_FILES['filename']['error'] !==-  UPLOAD_ERR_OK ) {
 
-             Notification::error('There was an error uploading your file, please try again.'); 
-             Url::redirect('dbtools/importFiles'); 
-         } 
-   
-         if (File::getExt($_FILES['filename']['name']) !== 'zip') { 
-             Notification::error('Invalid file type; only .zip files are supported.'); 
-             Url::redirect('dbtools/importFiles'); 
-         } 
-   
-         copy($_FILES['filename']['tmp_name'],-  APPPATH  . 'temp/import.zip');
 
-   
-         $za = new ZipArchive(); 
-         $za->open(APPPATH . 'temp/import.zip'); 
-   
-         // Check for disallowed file types 
-         $invalid = []; 
-         for ($i = 0; $i < $za->numFiles; $i++) { 
-             $filename = $za->getNameIndex($i); 
-             if (!FileUpload::checkFilename($filename)) { 
-                 $invalid[] = $filename; 
-             } 
-         } 
-   
-         // If there are any disallowed files in the ZIP, then stop 
-             $za->close(); 
-             unlink(- APPPATH  . 'temp/import.zip');
 
-             header('Content-type: text/plain'); 
-             echo "DISALLOWED FILES FOUND:\n - ", implode("\n - ", $invalid); 
-         } 
-   
-         for ($i = 0; $i < $za->numFiles; $i++) { 
-             @File::putString($za->getNameIndex($i), $za->getFromIndex($i)); 
-         } 
-   
-         $za->close(); 
-   
-         unlink(- APPPATH  . 'temp/import.zip');
 
-   
-   
-         echo '<p>Done.</p>'; 
-   
-         $this->template('Import files'); 
-     } 
-   
-   
-     /** 
-     * Allows files to be downloaded from the temporary directory 
-     * 
-     * Only certain files from the temp directory can be downloaded. 
-     * Each 'source' has a regex which is used to restrict downloads to only that particular type of file 
-     * 
-     * @param string $source The source of the file, one of 'export-database', 'export-files', 'module-builder' 
-     * @param string $tempfile File name in the temporary directory 
-     * @param string $orig Alternate name to use when providing file to browser 
-     **/ 
-     public function gettempfile($source, $tempfile, $orig = null) 
-     { 
-         AdminAuth::checkLogin(); 
-   
-         $validation_regexes = [ 
-             'export-database' => '/^export-[0-9]+-(?:sql_[-0-9]+_.+\.zip|.+\.sql)$/', 
-             'export-files' => '/^files_[-0-9]+_.+\.zip$/', 
-             'module-builder' => '/^mt_[0-9]+\.tar\.bz2$/', 
-         ]; 
-   
-         if (!isset($validation_regexes[$source])) { 
-             throw new InvalidArgumentException('Invalid source specified'); 
-         } 
-         if (!preg_match($validation_regexes[$source], $tempfile)) { 
-             throw new InvalidArgumentException('Invalid tempfile specified'); 
-         } 
-   
-         $disk_filename = APPPATH . 'temp/' . $tempfile; 
-             throw new Kohana_404_Exception($tempfile); 
-         } 
-   
-         // If no original name, use the disk filename 
-         if (! $orig) { 
-             $orig = $tempfile; 
-         } 
-   
-         // Determine mimetype 
-             'txt' => 'text/plain', 
-             'sql' => 'text/plain', 
-             'zip' => 'application/zip', 
-             'bz2' => 'application/bzip2', 
-             'gz' => 'application/gzip', 
-         ); 
-         if (! $mime = $mimetypes[$ext]) { 
-             $mime = 'application/octet-stream'; 
-         } 
-   
-         // Make sure there's no buffering, or large files will exhaust PHP's memory allocation 
-         } 
-   
-   
-         header ("Content-type: {$mime}"); 
-         header ("Content-disposition: attachment; filename=\"{$orig}\""); 
-     } 
-   
-   
-     /** 
-     * UI to export all files 
-     **/ 
-     public function exportFiles() 
-     { 
-   
-         echo "<p>Found " . count($files) . " files.\n"; 
-   
-         echo '<p>NOTE: Exports of many files may take a long time and/or fail.</p>'; 
-   
-         echo '<form action="dbtools/exportFilesAction" method="post">'; 
-         echo Csrf::token(); 
-         echo '<div class="action-bar"><button type="submit" class="button">Export files</button></div>'; 
-         echo '</form>'; 
-   
-         $this->template('Export files'); 
-     } 
-   
-   
-     /** 
-     * Action to export all files 
-     **/ 
-     public function exportFilesAction() 
-     { 
-         Csrf::checkOrDie(); 
-   
-   
-         echo "<p>Found " . count($files) . " files.\n"; 
-   
-         // Prep archive 
-         $arch = new Archive('zip'); 
-         foreach ($files as $filename) { 
-             $temp = File::createLocalCopy($filename); 
-             if (!$temp) continue; 
-             $arch->add($temp, $filename); 
-             $temp_names[] = $temp; 
-         } 
-   
-         // Build file name 
-         $name = Kohana::config('sprout.site_title'); 
-         $name = 'files_' . date('Y-m-d') . '_' . $name . '.zip'; 
-   
-         // Save archive 
-         echo "<p>Saving archive.\n"; 
-         $arch->save(APPPATH . 'temp/' . $name); 
-   
-         // Nuke temps 
-         foreach ($temp_names as $temp) { 
-             File::cleanupLocalCopy($temp); 
-         } 
-   
-         // Offer download link 
-         echo "<div class=\"action-bar\"><a href=\"SITE/dbtools/gettempfile/export-files/{$name}\" class=\"button icon-after icon-save\">Download: {$name}</a></div>"; 
-   
-         $this->template('Export files'); 
-     } 
-   
-   
-     /** 
-     * Edit the $_SESSION 
-     **/ 
-     public function sessionEditor() 
-     { 
-         Session::instance(); 
-   
-         echo '<style>'; 
-         echo 'div.val { margin: 0 20px 15px; padding: 5px; clear: right; background: rgba(0,0,0,0.1); border-radius: 3px; }'; 
-         echo 'div.val div.val:last-child { margin-bottom: 6px; }'; 
-         echo 'div.val p { margin: 0; padding: 0; cursor: default; line-height: 32px; }'; 
-         echo 'div.val h3 { margin: 0 0 5px 0; padding: 0; }'; 
-         echo 'div.val form { margin: 0; padding: 0; }'; 
-         echo 'div.val:hover { background: rgba(0,0,0,0.3); }'; 
-         echo 'div.val:hover p { color: #fff; }'; 
-         echo 'div.val:hover h3 { color: #fff; }'; 
-         echo 'div.val button.right { margin: 0; }'; 
-         echo '</style>'; 
-   
-         echo '<h3>$_SESSION</h3>'; 
-         $this->sessionLoop($_SESSION, 0, []); 
-   
-         $this->template('Session editor'); 
-     } 
-   
-     /** 
-     * List items in an array, along with the tools buttons 
-     **/ 
-     private function sessionLoop(&$a, $depth, $keys) 
-     { 
-         if ($depth > 50) { 
-             echo '<p>TOO DEEP!</p>'; 
-             return; 
-         } 
-   
-         foreach ($a as $key => $val) { 
-             $this_keys = $keys; 
-             $this_keys[] = $key; 
-   
-             // Can't delete protected/private members 
-             $can_delete = true; 
-             foreach ($this_keys as $k) { 
-                 if (strpos($k, '->') !== false) { 
-                     $can_delete = false; 
-                     break; 
-                 }; 
-             } 
-   
-             echo '<div class="val -clearfix">'; 
-             if ($can_delete) { 
-                 echo '<form action="SITE/dbtools/sessionEditorAction" method="post">'; 
-                 foreach ($this_keys as $k) { 
-                     echo '<input type="hidden" name="key[]" value="' . Enc::url($k) . '">'; 
-                 } 
-                 echo '<button type="submit" value="delete" name="do" class="button right button-orange button-small icon-after icon-close">Delete</button>'; 
-                 echo '</form>'; 
-             } 
-   
-                 echo '<h3>' . Enc::html($key) . '</h3>'; 
-                     echo '<div class="val"><p><i>(empty array)</i></p></div>'; 
-                 } else { 
-                     $this->sessionLoop($val, $depth + 1, $this_keys); 
-                 } 
-   
-                 echo '<h3>' .-  Enc ::html($key) . ' <i>' . get_class($val);
 
-                 $parent = $val; 
-                     echo " ex $parent"; 
-                 } 
-                 $i = 0; 
-                 foreach ($implements as $interface) { 
-                     if (++$i == 1) { 
-                         echo ' impl '; 
-                     } else { 
-                         echo ', '; 
-                     } 
-                     echo $interface; 
-                 } 
-                 echo '</i></h3>'; 
-                 $obj_all_members = (array) $val; 
-   
-                 foreach ($obj_all_members as $mem_key => $mem_val) { 
-                     // N.B. This doesn't work (it returns an array): 
-                     // $new_key = preg_replace('/[^\0]*\0+/', '', $mem_key); 
-                     // Undocumented PHP behaviour; assume \0 is interpreted 
-                     // in $pattern as it is in $replacement 
-                     $null_pos = strrpos($mem_key, "\0"); 
-                     if ($null_pos !== false) { 
-                         $new_key = substr($mem_key, $null_pos + 1); 
-                     } else { 
-                         $new_key = $mem_key; 
-                     } 
-   
-                     if (in_array($new_key, $obj_pub_members)) { 
-                         $members["->{$new_key}"] = $mem_val; 
-                         continue; 
-                     } 
-   
-                     $getter = false; 
-                     $getters = array('get_' . $new_key, 'get' . $new_key); 
-                     foreach ($getters as $fn) { 
-                             $getter = $fn; 
-                             break; 
-                         } 
-                     } 
-                     if (!$getter) continue; 
-   
-                     $members["->{$getter}()"] = $mem_val; 
-                 } 
-                 $this->sessionLoop($members, $depth + 1, $this_keys); 
-   
-             } else if (gettype($val) == 'object') { 
-                 // e.g. __PHP_Incomplete_Class objects 
-                 echo '<p>' . $key . ' = <i>OBJECT OF UNKNOWN CLASS</i></p>'; 
-             } else { 
-                 echo '<p>' . $key . ' = ' . $val . '</p>'; 
-             } 
-   
-             echo '</div>'; 
-   
-             unset ($this_keys, $key, $val); 
-         } 
-     } 
-   
-     /** 
-     * Session Editor Action 
-     **/ 
-     public function sessionEditorAction() 
-     { 
-         Session::instance(); 
-   
-   
-         // Find the array & key which will do the action 
-         $final_array = &$_SESSION; 
-         foreach ($_POST['key'] as $key) { 
-             $final_array = &$final_array[$key]; 
-         } 
-   
-         // Do the action 
-         switch ($_POST['do']) { 
-             case 'delete': 
-                 unset ($final_array[$final_key]); 
-                 break; 
-         } 
-   
-         Url::redirect('dbtools/sessionEditor'); 
-     } 
-   
-   
-     /** 
-      * 
-      * @return void 
-      */ 
-     public function listRoutes() 
-     { 
-         $routes = Router::getRoutes(); 
-   
-         echo "<table>"; 
-         echo "<thead><tr>"; 
-         echo "<th>Rule</th>"; 
-         echo "<th>Target</th>"; 
-         echo "</thead></tr>\n"; 
-         echo "<tbody>"; 
-   
-         foreach ($routes as $rule => $target) { 
-             $rule = Enc::html($rule); 
-             $target =-  Enc ::html(json_encode($target,-  JSON_UNESCAPED_SLASHES ));
 
-   
-             echo "<tr>"; 
-             echo "<td>{$rule}</td>"; 
-             echo "<td>{$target}</td>"; 
-             echo "</tr>\n"; 
-         } 
-   
-         echo "</tbody>"; 
-         echo "</table>"; 
-   
-         $this->template('Route Inspector'); 
-     } 
-   
-   
-     /** 
-     * For a given field name, a type to use which is better than VARCHAR(200) 
-     **/ 
-     private-  static  $module_builder_type_guess = array(
 
-         'active' => 'TINYINT UNSIGNED', 
-         'amount' => 'DECIMAL(6,2)', 
-         'data' => 'BLOB', 
-         'description' => 'TEXT', 
-         'email' => 'VARCHAR(150)', 
-         'fax' => 'VARCHAR(20)', 
-         'filename' => 'VARCHAR(255)', 
-         'first_name' => 'VARCHAR(100)', 
-         'image' => 'VARCHAR(255)', 
-         'last_name' => 'VARCHAR(100)', 
-         'mobile' => 'VARCHAR(20)', 
-         'notes' => 'TEXT', 
-         'phone' => 'VARCHAR(20)', 
-         'photo' => 'VARCHAR(255)', 
-         'postcode' => 'VARCHAR(10)', 
-         'price' => 'DECIMAL(6,2)', 
-         'state' => 'VARCHAR(50)', 
-         'suburb' => 'VARCHAR(50)', 
-         'text' => 'TEXT', 
-         'visible' => 'TINYINT UNSIGNED', 
-     ); 
-   
-   
-     /** 
-     * Generates blank modules 
-     **/ 
-     public function moduleBuilder() 
-     { 
-         $temp = APPPATH . 'temp'; 
-   
-         // Generate list of modules 
-         $mod_dir = DOCROOT . '/modules/'; 
-         foreach ($modules as $key => $mod) { 
-             if ($mod[0] == '.'-  or  !is_dir($mod_dir . $mod)) unset($modules[$key]);
 
-         } 
-   
-         // Prep array for form data 
-         $modules_list = []; 
-         foreach($modules as $mod) { 
-             $modules_list[$mod] = $mod; 
-         } 
-   
-         $view = new View('sprout/dbtools/module_builder'); 
-         $view->bad_fields = array('id', 'name', 'active', 'date_added', 'date_modified', 'record_order'); 
-         $view->modules = $modules_list; 
-         echo $view->render(); 
-   
-         $this->template('Module builder'); 
-     } 
-   
-   
-     public function moduleBuilderAction() 
-     { 
-         if (empty($_POST['module_author'])) { 
-             throw new InvalidArgumentException('Module author not specified'); 
-         } 
-         if (empty($_POST['module_name'])) { 
-             throw new InvalidArgumentException('Module name not specified'); 
-         } else { 
-             $module_name = trim($_POST['module_name']); 
-             if (!preg_match('/^([A-Z][a-z0-9]+)+$/', $module_name)) { 
-                 throw new InvalidArgumentException('Invalid module name'); 
-             } 
-         } 
-   
-         // Name => type 
-         $inbuilt_fields = [ 
-             'id' => 'INT UNSIGNED', 
-             'name' => 'VARCHAR(60)', 
-             'active' => 'TINYINT UNSIGNED', 
-             'date_added' => 'DATETIME', 
-             'date_modified' => 'DATETIME', 
-         ]; 
-         if (in_array($_POST['module_type'], ['list', 'tree'])) { 
-             $inbuilt_fields['record_order'] = 'INT UNSIGNED'; 
-         } 
-   
-         if ($_POST['module_type'] == 'tree') $fields[] = 'parent_id'; 
-         foreach (explode("\n", $_POST['fields']) as $field) { 
-             if ($field == '') continue; 
-             if (in_array($field, $fields)) continue; 
-             $fields[] = $field; 
-         } 
-   
-         $fields_manual = array(); 
-   
-         $t = "    "; 
-         foreach ($fields as $f) { 
-             if (!in_array($f, ['name'])-  and  isset($inbuilt_fields[$f])) continue;
 
-   
-   
-             // Try to guess a name using a basic algorithm 
-             if (isset($inbuilt_fields[$f])) { 
-                 $type = $inbuilt_fields[$f]; 
-                 $type = 'INT UNSIGNED'; 
-             } else { 
-                 $type = @self::$module_builder_type_guess[$f]; 
-             } 
-   
-             // If it all fails, fall back to something generic 
-             if (! $type) { 
-                 $type = 'VARCHAR(100)'; 
-             } 
-   
-             $attrs = 'allownull="0" '; 
-   
-             if ($f == 'parent_id') { 
-                 $input_method = 'Fb::dropdown'; 
-                 $items = "{\"query\": \"SELECT id, name FROM ~{$_POST['pname']} WHERE parent_id = 0 ORDER BY record_order\"}"; 
-             } else { 
-                 $input_method = 'Fb::text'; 
-                 $items = "{}"; 
-             } 
-   
-             $json = "{$t}{$t}{\n" . 
-                 "{$t}{$t}{$t}\"field\": {\n" . 
-                 "{$t}{$t}{$t}{$t}\"name\": \"{$f}\",\n" . 
-                 "{$t}{$t}{$t}{$t}\"label\": \"{$l}\",\n" . 
-                 "{$t}{$t}{$t}{$t}\"display\": \"{$input_method}\",\n" . 
-                 "{$t}{$t}{$t}{$t}\"items\": {$items},\n" . 
-                 "{$t}{$t}{$t}{$t}\"required\": false,\n" . 
-                 "{$t}{$t}{$t}{$t}\"validate\": [\n"; 
-   
-             // Use length as basic validation where possible, allowing an extra char for a decimal point if relevant 
-             $matches = []; 
-             if (preg_match('/\([0-9]+(\s*,)?/', $type, $matches)) { 
-                 $field_len = (- int ) substr($matches[0], 1);
 
-                 if (!empty($matches[1])) ++$field_len; 
-                 $json .= "{$t}{$t}{$t}{$t}{$t}{\"func\": \"Validity::length\", \"args\": [0, {$field_len}]}\n"; 
-             } 
-             $json .= "{$t}{$t}{$t}{$t}]\n" . 
-                 "{$t}{$t}{$t}}\n" . 
-                 "{$t}{$t}}"; 
-             $fields_json[] = $json; 
-   
-             if (isset($inbuilt_fields[$f])) continue; 
-   
-             $fields_xml[] = "{$t}{$t}<column name=\"{$f}\" type=\"{$type}\" {$attrs}/>"; 
-             $fields_manual[] = "<p><b>{$l}</b>\n<br><!-- description goes here --></p>\n"; 
-         } 
-   
-         $possible_main_fields = [ 
-             'name', 
-             'email', 
-             'first_name', 
-             'last_name', 
-             'price', 
-             'suburb', 
-             'state', 
-             'visible', 
-         ]; 
-   
-         $fields_main = []; 
-         foreach ($possible_main_fields as $ind) { 
-                     $field = "[new ColModifierBinary(), '{$ind}']"; 
-                 } else { 
-                     $field = "'{$ind}'"; 
-                 } 
-                 $fields_main[] = "'{$label}' => {$field},"; 
-             } 
-         } 
-   
-         $_POST['_fields_json'] = implode(",\n", $fields_json); 
-         $_POST['_fields_manual'] = rtrim(implode("\n", $fields_manual)); 
-         $_POST['_fields_main'] = implode("\n{$t}{$t}{$t}", $fields_main); 
-   
-   
-         $temp = APPPATH . 'temp'; 
-         $template_dir = APPPATH . 'module_template/' . $_POST['module_type']; 
-   
-         if (!file_exists("{$temp}/{$module_name}")-  and  !@mkdir("{$temp}/{$module_name}")) {
 
-             echo "<ul class=\"messages\"><li class=\"error\">Failed to create temp directory {$module_name}</li></ul>"; 
-             $this->template('Module builder'); 
-             return; 
-         } 
-   
-         $dir_iterator = new RecursiveDirectoryIterator($template_dir); 
-         $iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST); 
-   
-         echo '<pre>'; 
-         echo "Ready.\n"; 
-   
-         foreach ($iterator as $file) { 
-             if ($basename[0] == '.') continue; 
-             if (strpos($file, '.svn') !== false) continue; 
-             if (strpos($file, '~') !== false) continue; 
-   
-   
-             echo "Processing: '/{$_POST['module_type']}{$relative_name}'"; 
-   
-             if ($file->isDir()) { 
-                 // directories 
-                 $new_name = self::mtTransform($relative_name); 
-                 @mkdir ("{$temp}/{$module_name}" . $new_name); 
-   
-             } else if ($file->isFile()) { 
-                 // files 
-                 $text = self::mtTransform($text); 
-   
-                 $new_name = "{$temp}/{$module_name}" . self::mtTransform($relative_name); 
-             } 
-   
-             echo " => '{$new_name}'.\n"; 
-         } 
-   
-         echo "Done building, now compressing.\n"; 
-   
-         echo shell_exec("cd {$temp}; tar -cjvf mt_{$rand}.tar.bz2 {$module_name}"); 
-   
-         echo "Done.\n"; 
-         echo '</pre>'; 
-   
-         echo "<p><a href=\"SITE/dbtools/gettempfile/module-builder/mt_{$rand}.tar.bz2/sprout_module_{$_POST['sname']}_{$_POST['module_type']}.tar.bz2\">Download module</a></p>"; 
-   
-         $this->template('Module builder'); 
-     } 
-   
-   
-     /** 
-      * Generates db_struct.xml content for a module 
-      */ 
-     public function moduleBuilderDb() 
-     { 
-         if (!empty($_GET['table'])-  and  in_array(@$_GET['type'], ['has_categories', 'list', 'tree'])) {
 
-             $template_path = APPPATH . 'module_template/' . $_GET['type'] . '/db_struct.xml'; 
-             $content = str_replace('PNAME', $_GET['table'], $content); 
-             $content = str_replace('SNAME',-  Inflector ::singular($_GET['table']), $content);
 
-             $_GET['xml'] = $content; 
-         } 
-   
-         $view = new View('sprout/dbtools/module_builder_db'); 
-         $view->data = $_GET; 
-         echo $view->render(); 
-   
-         $this->template('Module builder - DB'); 
-     } 
-   
-   
-     /** 
-     * Generates modules from an existing db_struct.xml file 
-     **/ 
-     public function moduleBuilderExisting() 
-     { 
-         $temp = APPPATH . 'temp'; 
-   
-         $existing_files = []; 
-         $modules = glob(- DOCROOT  . 'modules/*');
 
-         foreach ($modules as $mod) { 
-                 $existing_files[$mod] = $mod; 
-             } 
-         } 
-   
-         $view = new View('sprout/dbtools/module_builder_existing_upload'); 
-         $view->temp_writeable = $temp_writeable; 
-         $view->existing_files = $existing_files; 
-         echo $view->render(); 
-   
-         $this->template('Module builder existing'); 
-     } 
-   
-   
-     /** 
-     * Generates modules from an existing db_struct.xml file 
-     **/ 
-     public function moduleBuilderExistingUploadAction() 
-     { 
-         $filename = 'mbe' . time() . '.xml'; 
-   
-         if (isset($_FILES['file']['tmp_name'])) { 
-             // Upload a new file 
-             copy($_FILES['file']['tmp_name'],-  APPPATH  . 'temp/' . $filename);
 
-   
-         } else if (isset($_POST['existing'])) { 
-             // Process an existing file 
-             if (!preg_match('!^[a-zA-Z0-9]+$!', $_POST['existing'])) { 
-             } 
-             copy(- DOCROOT  . 'modules/' . $_POST['existing'] . '/db_struct.xml',-  APPPATH  . 'temp/' . $filename);
 
-             $_SESSION['module_builder_existing']['field_values']['module_name'] = $_POST['existing']; 
-             $_SESSION['module_builder_existing']['field_values']['module_author'] = 'Karmabunny'; 
-   
-         } else if (isset($_POST['content'])) { 
-             // Copy and paste XML content 
-             if (strpos($_POST['content'], '<database>') === false) { 
-                 $_POST['content'] = '<database>' . $_POST['content'] . '</database>'; 
-             } 
-   
-         } else { 
-         } 
-   
-         Url::redirect('dbtools/moduleBuilderExistingForm/' . $filename); 
-     } 
-   
-     /** 
-     * Generates modules from an existing db_struct.xml file 
-     **/ 
-     public function moduleBuilderExistingForm($input_xml) 
-     { 
-         if (!preg_match('/^mbe[0-9]+\.xml$/', $input_xml)) die('Invalid filename'); 
-   
-         $sync = new DatabaseSync(false); 
-         $sync->loadXml(APPPATH . 'temp/' . $input_xml); 
-   
-         $tables = $sync->tables; 
-   
-         if (!empty($_SESSION['module_builder_existing']['field_values'])) { 
-             $data = $_SESSION['module_builder_existing']['field_values']; 
-         } else { 
-             $data = []; 
-         } 
-   
-         if (!isset($data['tables_cname'])) { 
-             $data = ['tables_cname' => [], 'tables_sname' => [], 'tables_snice' => [], 'tables_pnice' => []]; 
-             foreach ($tables as $name => $defn) { 
-                 $data['tables_cname'][$name] = Text::lc2camelCaps(Inflector::singular($name)); 
-                 $data['tables_sname'][$name] = Inflector::singular($name); 
-                 $data['tables_snice'][$name] = ucfirst(str_replace('_', ' ',-  Inflector ::singular($name)));
 
-             } 
-             if (empty($data['module_author'])) $data['module_author'] = 'Karmabunny'; 
-         } 
-   
-         $view = new View('sprout/dbtools/module_builder_existing_form'); 
-         $view->tables = $tables; 
-         $view->templates = [ 
-             'has_categories' => 'Categories', 
-             'tree' => 'Tree', 
-             'list' => 'List', 
-         ]; 
-         $view->data = $data; 
-         $view->input_xml = $input_xml; 
-         if (!empty($_SESSION['module_builder_existing']['field_errors'])) { 
-             $view->errors = $_SESSION['module_builder_existing']['field_errors']; 
-         } else { 
-             $view->errors = []; 
-         } 
-         echo $view->render(); 
-   
-         $this->template('Module builder existing'); 
-     } 
-   
-   
-     /** 
-     * Generates modules from an existing db_struct.xml file 
-     **/ 
-     public function moduleBuilderExistingAction($input_xml) 
-     { 
-         static $tab = "    "; 
-   
-         $errs = []; 
-         if (!preg_match('/^mbe[0-9]+\.xml$/', $input_xml)) $errs[] = 'Invalid filename'; 
-   
-         if (empty($_POST['module_author'])) { 
-             $errs['module_author'] = 'Required'; 
-         } 
-         if (empty($_POST['module_name'])) { 
-             $errs['module_name'] = 'Required'; 
-         } else { 
-             $module_name = trim($_POST['module_name']); 
-             if (!preg_match('/^([A-Z][a-z0-9]+)+$/', $module_name)) { 
-                 $errs['module_name'] = 'Invalid value'; 
-             } 
-         } 
-         if ($errs) { 
-             $_SESSION['module_builder_existing']['field_values'] =-  Validator ::trim($_POST);
 
-             $_SESSION['module_builder_existing']['field_errors'] = $errs; 
-             Url::redirect('/dbtools/moduleBuilderExistingForm/' . $input_xml); 
-         } 
-   
-         $temp = APPPATH . 'temp'; 
-         if (!file_exists("{$temp}/{$module_name}")-  and  !@mkdir("{$temp}/{$module_name}")) {
 
-             echo "<ul class=\"messages\"><li class=\"error\">Failed to create temp directory {$module_name}</li></ul>"; 
-             $this->template('Module builder'); 
-             return; 
-         } 
-   
-         $sync = new DatabaseSync(false); 
-         $sync->loadXml(APPPATH . 'temp/' . $input_xml); 
-         $tables = $sync->tables; 
-   
-         foreach ($tables as $t => $defn) { 
-             if (empty($_POST['tables'][$t])) continue; 
-   
-             $template_dir = APPPATH . 'module_template/' . $_POST['tables'][$t]; 
-             $dir_iterator = new \RecursiveDirectoryIterator($template_dir); 
-             $iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::SELF_FIRST); 
-   
-             $fields_manual = array(); 
-   
-             echo '<h3>', Enc::html($t), '</h3>'; 
-             echo "<pre>\n"; 
-             foreach ($defn['columns'] as $f => $col) { 
-                 if (in_array($f, ['id', 'date_added', 'date_modified'])) continue; 
-   
-                 $items = "{}"; 
-   
-                 // Determine HTML input field based on field name and type 
-                 // Active field (every table should have one of these) 
-                 if ($f == 'active') { 
-                     $input_method = 'Fb::multiradio'; 
-                     $items = "{\"0\": \"No\", \"1\": \"Yes\"}"; 
-   
-                 // Numeric fields 
-                     $input_method = 'Fb::number'; 
-   
-                 // ENUM and SET fields 
-                 } else if (preg_match('/^ENUM\s*\(/', $col['type'])) { 
-                     $input_method = 'Fb::dropdown'; 
-                     $items = '{"func": "Pdb::extractEnumArr", "args": ["' . Enc::js($t) . '", "' . Enc::js($f) . '"]}'; 
-                 } else if (preg_match('/^SET\s*\(/', $col['type'])) { 
-                     $input_method = 'Fb::checkboxSet'; 
-                     $items = '{"func": "Pdb::extractEnumArr", "args": ["' . Enc::js($t) . '", "' . Enc::js($f) . '"]}'; 
-   
-                 // Foreign keys (ending in _id) 
-                     $target_table =-  Inflector ::plural(substr($f, 0, -3));
 
-                     $input_method = 'Fb::dropdown'; 
-                     $items = '{"func": "Pdb::lookup", "args": ["' . Enc::js($target_table) . '"]}'; 
-   
-                 // Other columns: determine field type 
-                 } else { 
-                     $col_def_parts = preg_split('/\s+/', $col['type']); 
-                     $type = strtoupper(- Sprout ::iterableFirstValue($col_def_parts));
 
-   
-                     switch ($type) { 
-                     case 'DATETIME': 
-                         $input_method = 'Fb::datetimepicker'; 
-                         break; 
-   
-                     case 'DATE': 
-                         $input_method = 'Fb::datepicker'; 
-                         break; 
-   
-                     case 'TEXT': 
-                         $input_method = 'Fb::multiline'; 
-                         break; 
-   
-                     case 'TINYINT': 
-                         $input_method = 'Fb::multiradio'; 
-                         $items = '{"0": "No", "1": "Yes"}'; 
-                         break; 
-   
-                     default: 
-                         $input_method = 'Fb::text'; 
-                     } 
-                 } 
-   
-   
-                 // Handle common acronyms 
-                 foreach ($l_parts as &$l_part) { 
-                     } 
-                 } 
-   
-                 $json = "{$tab}{$tab}{\n" . 
-                     "{$tab}{$tab}{$tab}\"field\": {\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"name\": \"{$f}\",\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"label\": \"{$l}\",\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"display\": \"{$input_method}\",\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"items\": {$items},\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"required\": false,\n" . 
-                     "{$tab}{$tab}{$tab}{$tab}\"validate\": [\n"; 
-   
-                 // Use length as basic validation where possible, allowing an extra char for a decimal point if relevant 
-                 $matches = []; 
-                 if (preg_match('/\([0-9]+(\s*,)?/', $col['type'], $matches)) { 
-                     $field_len = (- int ) substr($matches[0], 1);
 
-                     if (!empty($matches[1])) ++$field_len; 
-                     $json .= "{$tab}{$tab}{$tab}{$tab}{$tab}{\"func\": \"Validity::length\", \"args\": [0, {$field_len}]}\n"; 
-                 } 
-   
-                 $json .= "{$tab}{$tab}{$tab}{$tab}]\n" . 
-                     "{$tab}{$tab}{$tab}}\n" . 
-                     "{$tab}{$tab}}"; 
-                 $fields_json[] = $json; 
-   
-                 $fields_manual[] = "<p><b>{$l}</b>\n<br><!-- description goes here --></p>\n"; 
-             } 
-   
-             $possible_main_fields = [ 
-                 'name', 
-                 'active', 
-                 'email', 
-                 'first_name', 
-                 'last_name', 
-                 'price', 
-                 'suburb', 
-                 'state', 
-                 'visible', 
-             ]; 
-   
-             $fields_main = []; 
-             foreach ($possible_main_fields as $ind) { 
-                     if (in_array($ind, ['active', 'visible'])) { 
-                         $field = "[new ColModifierBinary(), '{$ind}']"; 
-                     } else { 
-                         $field = "'{$ind}'"; 
-                     } 
-                     $fields_main[] = "'{$label}' => {$field},"; 
-                 } 
-             } 
-   
-             $_POST['_fields_xml'] = ''; 
-             $_POST['_fields_json'] = implode(",\n", $fields_json); 
-             $_POST['_fields_manual'] = rtrim(implode("\n", $fields_manual)); 
-             $_POST['_fields_main'] = implode("\n{$tab}{$tab}{$tab}", $fields_main); 
-   
-             $_POST['cname'] = $_POST['tables_cname'][$t]; 
-             $_POST['sname'] = $_POST['tables_sname'][$t]; 
-             $_POST['pname'] = $t; 
-             $_POST['snice'] = $_POST['tables_snice'][$t]; 
-             $_POST['pnice'] = $_POST['tables_pnice'][$t]; 
-   
-             foreach ($iterator as $file) { 
-                 if ($basename[0] == '.') continue; 
-                 if (strpos($file, '.svn') !== false) continue; 
-                 if (strpos($file, '~') !== false) continue; 
-   
-                 if ($relative_name == '/db_struct.xml') continue; 
-                 if ($relative_name == '/admin_load.php') continue; 
-                 if ($relative_name == '/sprout_load.php') continue; 
-   
-                 echo "Processing: '{$relative_name}'"; 
-   
-                 if ($file->isDir()) { 
-                     $new_name = self::mtTransform($relative_name); 
-                     @mkdir ("{$temp}/{$module_name}" . $new_name); 
-   
-                 } else if ($file->isFile()) { 
-                     $text = self::mtTransform($text); 
-   
-                     $new_name = "{$temp}/{$module_name}" . self::mtTransform($relative_name); 
-                 } 
-   
-                 echo " => '{$new_name}'.\n"; 
-             } 
-   
-             echo '</pre>'; 
-         } 
-   
-         copy(- APPPATH  . 'temp/' . $input_xml, "{$temp}/{$module_name}/db_struct.xml");
 
-   
-         echo "<p>Done building, now compressing...\n"; 
-   
-         echo '<pre>'; 
-         echo shell_exec("cd {$temp}; tar -cjvf mt_{$rand}.tar.bz2 {$module_name}"); 
-         echo '</pre>'; 
-   
-         echo "<p>Done.\n"; 
-         echo "<div class=\"action-bar\"><a href=\"SITE/dbtools/gettempfile/module-builder/mt_{$rand}.tar.bz2/sprout_module.tar.bz2\" class=\"button icon-after icon-save\">Download module</a></div>"; 
-   
-         $this->template('Module builder'); 
-     } 
-   
-   
-   
-     /** 
-     * Used by the module_template_action method 
-     **/ 
-     public static function mtTransform($text) 
-     { 
-         $text = str_replace('AUTHOR', $_POST['module_author'], $text); 
-         $text = str_replace('MODULE', $_POST['module_name'], $text); 
-   
-         $text = str_replace('FIELDS_XML', $_POST['_fields_xml'], $text); 
-         $text = str_replace('FIELDS_JSON', $_POST['_fields_json'], $text); 
-         $text = str_replace('FIELDS_MANUAL', $_POST['_fields_manual'], $text); 
-         $text = str_replace('FIELDS_MAIN', $_POST['_fields_main'], $text); 
-   
-         return $text; 
-     } 
-   
-   
-     /** 
-      * Browse and search exceptions 
-      */ 
-     public function exceptionLog() 
-     { 
-         if (!empty($_GET['class'])) { 
-             $conditions[] = ['class_name', '=', $_GET['class']]; 
-         } 
-         if (!empty($_GET['message'])) { 
-             $conditions[] = ['message', 'CONTAINS', $_GET['message']]; 
-         } 
-         if (empty($_GET['show_row_missing'])) { 
-             $conditions[] = ['class_name', '!=', 'karmabunny\pdb\Exceptions\RowMissingException']; 
-         } 
-         if (empty($_GET['show_404'])) { 
-             $conditions[] = ['class_name', '!=', 'Kohana_404_Exception']; 
-         } 
-         if (count($conditions) == 0) $conditions[] = '1'; 
-   
-         $page_size = 100; 
-         $page = max((- int )@$_GET['page'], 1);
 
-         $offset = ($page - 1) * $page_size; 
-   
-         $where = Pdb::buildClause($conditions, $binds); 
-         $q = "SELECT id, date_generated, class_name, message 
-             FROM ~exception_log 
-             WHERE {$where} 
-             ORDER BY id DESC 
-             LIMIT {$offset}, {$page_size}"; 
-         $res = Pdb::query($q, $binds, 'pdo'); 
-   
-         $row_count = $res->rowCount(); 
-         if ($row_count == 0) { 
-             $itemlist = '<p><em>No items found</em></p>'; 
-         } else { 
-             $itemlist = new Itemlist(); 
-             $itemlist->items = $res; 
-             $itemlist->addAction('edit', 'dbtools/exceptionDetail?id=%%'); 
-             $itemlist->main_columns = array( 
-                 'Date' => 'date_generated', 
-                 'Class' => 'class_name', 
-                 'Message' => 'message', 
-             ); 
-         } 
-   
-         // View 
-         $view = new View('sprout/dbtools/exception_log'); 
-         $view->itemlist = $itemlist; 
-         $view->page = $page; 
-         $view->row_count = $row_count; 
-         $view->page_size = $page_size; 
-         echo $view->render(); 
-   
-         $res->closeCursor(); 
-         $this->template('Exception log'); 
-     } 
-   
-   
-     /** 
-      * Browse recent exceptions - details 
-      */ 
-     public function exceptionDetail() 
-     { 
-   
-         try { 
-             $log = Pdb::get('exception_log', $_GET['id']); 
-             $title = $log['id']; 
-         } catch (RowMissingException $ex) { 
-             $log = []; 
-             $title = 'Not found'; 
-         } 
-   
-         // View 
-         $view = new View('sprout/dbtools/exception_details'); 
-   
-         echo $view->render(); 
-         $this->template('Exception #' . $title); 
-     } 
-   
-   
-     /** 
-      * Generate a password hash to store in a config file 
-      * The username and hash are used by {@see AdminAuth::processLocal} 
-      */ 
-     public function generatePasswordHash() 
-     { 
-         $username = trim(@$_POST['username']); 
-   
-         echo '<form method="post">'; 
-         echo '<div class="field-group-wrap -clearfix">'; 
-         echo '<div class="field-group-item col col--one-half">'; 
-         Form::nextFieldDetails('Username', true, 'Letters and numbers only'); 
-         echo Form::text('username', ['-wrapper-class' => 'white']); 
-         echo '</div><!-- .col.col--one-half -->'; 
-   
-         echo '<div class="field-group-item col col--one-half">'; 
-         Form::nextFieldDetails('Password', true, 'Will be displayed on screen'); 
-         echo Form::text('pass', ['-wrapper-class' => 'white']); 
-         echo '</div><!-- .col.col--one-half -->'; 
-         echo '</div><!-- .field-group-wrap -->'; 
-   
-         echo '<div class="action-bar"><button type="submit" class="button icon-after icon-keyboard_arrow_right">Generate hash</button></div>'; 
-         echo '</form>'; 
-   
-         if (!empty($username)-  and  !empty($_POST['pass'])) {
 
-             $data = Auth::hashPassword($_POST['pass'], Constants::PASSWORD_BCRYPT12); 
-   
-             $users = AdminAuth::injectLocalSuperConf($username, $data[0], $data[2]); 
-   
-             echo "<h4>Paste this into a config/super_ops.php file</h4>\n"; 
-   
-             echo "<pre><?php\n\$config['operators'] = [\n"; 
-             foreach ($users as $username => $user) { 
-                 echo "    '", Enc::html(Enc::js($username)); 
-                 echo "' => ['uid' => {$user['uid']}, 'hash' => '", Enc::html(Enc::js($user['hash'])); 
-                 echo "', 'salt' => '", Enc::html(Enc::js($user['salt'])), "'],\n"; 
-             } 
-             echo "];</pre>"; 
-         } 
-   
-         $this->template('Generate password hash'); 
-     } 
-   
-   
-     /** 
-      * Render view to see session and cookie data 
-      * 
-      * @return void Echos HTML 
-      */ 
-     public function varDump() 
-     { 
-         echo '<h2>$_SESSION</h2>'; 
-         echo '<h2>$_COOKIE</h2>'; 
-         echo '<h2>$_SERVER</h2>'; 
-   
-         $this->template('Var dump'); 
-     } 
-   
-     /** 
-      * Simple tool for testing skin templates 
-      * 
-      * @return void Outputs HTML directly 
-      */ 
-     public function testSkinTemplates() 
-     { 
-         $skins = Subsites::getCodes(); 
-         $skins[] = 'unavailable'; 
-   
-         echo '<div class="info highlight-warning">'; 
-         echo 'Note: Not all templates will work if they rely on variables being set which are not set by this tool.'; 
-         echo '</div>'; 
-   
-         foreach ($skins as $s) { 
-             echo '<h3>', Enc::html($s), '</h3>'; 
-   
-             $templates = glob(- DOCROOT  . 'skin/' . $s . '/*.php');
 
-             foreach ($templates as $f) { 
-                 if ($f[0] == '.' or $f[0] == '_') continue; 
-                 if ($f == 'exception' or $f == 'popup' or $f == 'google_analytics') continue; 
-   
-                 $url = '/dbtools/testSkinTemplatesAction/' . Enc::url($s) . '/' . Enc::url($f); 
-   
-                 echo '<p><a href="' . Enc::html($url) . '">' . Enc::html($f) . '</a></p>'; 
-             } 
-         } 
-   
-         $this->template('Template test tool'); 
-     } 
-   
-   
-     /** 
-      * Actual viewing UI for templates 
-      * 
-      * @param string $skin Skin name, e.g. 'default' 
-      * @param string $tmpl Template filename, e.g. 'inner' 
-      * @return void Outputs HTML directly 
-      */ 
-     public function testSkinTemplatesAction($skin, $tmpl) 
-     { 
-   
-             throw new Kohana_404_Exception(); 
-         } 
-   
-         try { 
-             $q = "SELECT id FROM ~subsites WHERE code = ?"; 
-             $subsite_id = Pdb::query($q, [$skin], 'val'); 
-         } catch (RowMissingException $ex) { 
-             $subsite_id = SubsiteSelector::$subsite_id; 
-         } 
-   
-         // Fake the subsite environment so nav and breadcrumb will work 
-         SubsiteSelector::$subsite_id = $subsite_id; 
-         SubsiteSelector::$subsite_code = $skin; 
-         SubsiteSelector::$content_id = $subsite_id; 
-   
-         // Force a reload of the tree (in case tree is already loaded for some reason) 
-         $root = Navigation::loadPageTree($subsite_id, false, true); 
-   
-         // Find a node for sidenav, breadcrumb, etc. 
-         // Preference goes to one with children, but fallback is one without 
-         if ($root-  and  count($root->children) > 0) {
 
-             $fake_node = null; 
-             foreach ($root->children as $nd) { 
-                 if ($nd['show_in_nav']-  and  count($nd->children)) {
 
-                     $fake_node = $nd; 
-                     break; 
-                 } 
-             } 
-   
-             if ($fake_node == null) { 
-                 foreach ($root->children as $nd) { 
-                     if ($nd['show_in_nav']) { 
-                         $fake_node = $nd; 
-                         break; 
-                     } 
-                 } 
-             } 
-   
-             if ($fake_node != null) { 
-                 $fake_node['name'] = 'Template test'; 
-                 Navigation::setPageNodeMatcher(new TreenodeValueMatcher('id', $fake_node['id'])); 
-             } 
-         } 
-   
-         $content = new View('sprout/dbtools/skin_test_content'); 
-         $email = new View('sprout/email/testing_long'); 
-   
-         // Page templates 
-         $view = new View('skin/' . $tmpl); 
-         $view->page_title = 'Template test'; 
-         $view->browser_title = 'Template test'; 
-         $view->main_content = $content->render(); 
-   
-         // Email template 
-         $view->html_title = $view->page_title; 
-         $view->content = $email->render(); 
-   
-         echo $view->render(); 
-     } 
-   
-   
-     /** 
-     * Generate multiedit code 
-     **/ 
-     public function multimake() 
-     { 
-         echo '<style type="text/css">'; 
-         echo '.mini {font-size: 9px; color: #555;}'; 
-         echo '</style>'; 
-   
-             $modules_dir = DOCROOT . 'modules'; 
-             $xml_files = glob("$modules_dir/*/db_struct.xml"); 
-             foreach ($xml_files as $file) { 
-                 $opts[$module] = $module; 
-             } 
-   
-             echo '<form method="POST">'; 
-             Form::nextFieldDetails('Select module', true); 
-             echo Form::dropdown('module', ['-wrapper-class' => 'white'], $opts); 
-             echo '<div class="action-bar"><button type="submit" class="button icon-after icon-keyboard_arrow_right">Next</button></div>'; 
-   
-             $this->template('Generate multiedit code'); 
-             return; 
-         } 
-   
-         if (!empty($_POST['module'])) { 
-             $doc = self::xmlLoad($_POST['module']); 
-             $tables = self::xmlGetTables($doc); 
-   
-             $module_tables = array(); 
-             $invalid_tables = array(); 
-             foreach ($tables as $table) { 
-                 $cols = self::xmlGetColumns($doc, $table); 
-                 if (!isset($cols['id'])) continue; 
-   
-                 $full_name = $_POST['module'] . '/' . $table; 
-   
-                 // Look for a {something}_id column. 
-                 // A table without such a column is probably invalid, so put it 
-                 // at the bottom of the list, with an asterisk to warn the user 
-                 $valid = false; 
-                 foreach ($cols as $name => $defn) { 
-                     if ($name == 'id') continue; 
-                     if ($name == 'subsite_id') continue; 
-                         $valid = true; 
-                         break; 
-                     } 
-                 } 
-   
-                 if ($valid) { 
-                     $module_tables[$full_name] = $table; 
-                 } else { 
-                     $invalid_tables[$full_name] = "* $table"; 
-                 } 
-             } 
-   
-             echo "<h3>Module: <b>{$_POST['module']}</b></h3>"; 
-                 echo '<form method="POST">'; 
-                 Form::nextFieldDetails('Select table which will store the data', true, 'i.e. the sub-table'); 
-                 echo Form::dropdown('table', ['-wrapper-class' => 'white'], $opts); 
-                 echo '<div class="action-bar"><button type="submit" class="button icon-after icon-keyboard_arrow_right">Next</button></div>'; 
-                 echo '</form>'; 
-             } else { 
-                 echo '<p>No useable tables in this module</p>'; 
-             } 
-   
-             $this->template('Generate multiedit code'); 
-             return; 
-         } 
-   
-         if (!empty($_POST['table'])) { 
-   
-             $doc = self::xmlLoad($module); 
-             $columns = self::xmlGetColumns($doc, $table); 
-             $selected = null; 
-             foreach ($columns as $name => $defn) { 
-                 if ($name == 'id') continue; 
-                 $value = "{$module}/{$table}/{$name}"; 
-                 $opts[$value] = $name; 
-   
-                 if ($selected) continue; 
-                 if ($name == 'subsite_id') continue; 
-   
-                 if (substr($name, -3) == '_id') $selected = $value; 
-             } 
-   
-             Fb ::setData(array('group' => @$matches[1], 'linker' => $selected));
-   
-             echo "<h3>Module: <b>{$module}</b><br>Table: <b>{$table}</b></h3>"; 
-   
-             echo '<form method="POST">'; 
-             echo '<div class="field-group-wrap -clearfix"><div class="field-group-item col col--one-half">'; 
-             Form::nextFieldDetails('Group name', true, 'e.g. people'); 
-             echo Form::text('group', ['-wrapper-class' => 'white']); 
-             echo '</div>'; 
-   
-             echo '<div class="field-group-item col col--one-half">'; 
-             Form::nextFieldDetails('Column which links to base table', true, 'e.g. user_id'); 
-             echo Form::dropdown('linker', ['-wrapper-class' => 'white'], $opts); 
-             echo '</div></div>'; 
-   
-             echo '<div class="action-bar"><button type="submit" class="button icon-after icon-keyboard_arrow_right">Generate code</button></div>'; 
-   
-             echo '</form>'; 
-   
-             $this->template('Generate multiedit code'); 
-             return; 
-         } 
-   
-         if (empty($_POST['linker'])-  or  empty($_POST['group'])) {
 
-             echo '<p>Huh?</p>'; 
-   
-             $this->template('Generate multiedit code'); 
-             return; 
-         } 
-   
-         list($module, $table, $linker) = explode('/', $_POST['linker']); 
-         $doc = self::xmlLoad($module); 
-         $columns = self::xmlGetColumns($doc, $table); 
-         $ordered = isset($columns['record_order']); 
-   
-         echo "<h3>Module: <b>{$module}</b><br>Table: <b>{$table}</b><br>Group name: <b>{$_POST['group']}</b><br>Linking column: <b>{$linker}</b></h3>"; 
-   
-         $file = DOCROOT . 'sprout/views/dbtools/multimake_template.php'; 
-         $template = preg_replace('/(multiedit[-_])people/', '$1' . $_POST['group'], $template); 
-         $template = str_replace('user_people', $table, $template); 
-         $template = str_replace('people', $_POST['group'], $template); 
-         $single = ucfirst(- Inflector ::singular($_POST['group']));
 
-         $template = str_replace('user_id', $linker, $template); 
-   
-         if ($ordered) { 
-             $reorder = '<?php MultiEdit::reorder(); ?>'; 
-             $init = '$order = 0;'; 
-         } else { 
-             $reorder = ''; 
-             $init = ''; 
-             $template = str_replace('record_order', 'id', $template); 
-         } 
-         $template = preg_replace('#//\s*REORDER\s*//#', $reorder, $template); 
-         $template = preg_replace('#//\s*INIT_ORDER\s*//#', $init, $template); 
-   
-         // Generate form fields for view 
-         $data = ''; 
-         foreach ($columns as $name => $type) { 
-             if ($name == 'id') continue; 
-             if ($name == $linker) continue; 
-             if ($name == 'record_order') continue; 
-             $data .= "\t<p><b>{$label}:</b>\n"; 
-             $data .= "\t" . '<br><input type="text" name="m_' . 
-                 Enc::html($name) . "\"></p>\n\n"; 
-         } 
-   
-         // Generate update data 
-         $data = ''; 
-         foreach ($columns as $name => $type) { 
-             if ($name == 'id') continue; 
-             $data .= "\t\$update_fields['" . $name . "'] = "; 
-             if ($name == 'record_order') { 
-                 $data .= "\$order++;\n"; 
-             } else if ($name == $linker) { 
-                 $data .= "\$item_id;\n"; 
-             } else { 
-                 $data .= "\$data['" . $name . "'];"; 
-                 if ($name == 'date_added' or $name == 'date_modified') { 
-                     $data .= ' // WARNING!!!'; 
-                 } 
-                 $data .= "\n"; 
-             } 
-         } 
-   
-   
-   
-         $this->template('Generate multiedit code'); 
-     } 
-   
-     /** 
-     * @return array 
-     */ 
-     private static function xmlGetTables(DOMDocument $doc) 
-     { 
-         $table_els = $doc->getElementsByTagName('table'); 
-         foreach ($table_els as $el) { 
-             $tables[] = $el->getAttribute('name'); 
-         } 
-         return $tables; 
-     } 
-   
-     /** 
-     * @return array 
-     */ 
-     private static function xmlGetColumns(DOMDocument $doc, $table_name) 
-     { 
-         $table_els = $doc->getElementsByTagName('table'); 
-         $table = null; 
-         foreach ($table_els as $el) { 
-             if ($el->getAttribute('name') == $table_name) { 
-                 $table = $el; 
-                 break; 
-             } 
-         } 
-         if (!$table) return array(); 
-   
-         $column_els = $table->getElementsByTagName('column'); 
-         foreach ($column_els as $el) { 
-             $name = $el->getAttribute('name'); 
-             $type = $el->getAttribute('type'); 
-             $columns[$name] = $type; 
-         } 
-   
-         return $columns; 
-     } 
-   
-     private static function xmlLoad($module) 
-     { 
-         $file = DOCROOT . "modules/{$module}/db_struct.xml"; 
-         $doc = new DOMDocument(); 
-         return $doc; 
-     } 
-   
-   
-     /** 
-     * Renders form to send emails 
-     **/ 
-     public function email() 
-     { 
-         $op = AdminAuth::getDetails(); 
-   
-         $data = []; 
-         $data['emails'] = $op['email']; 
-         $data['from'] = Kohana::config('sprout.site_email'); 
-         $data['msg'] = 'long'; 
-   
-         Form::setData($data); 
-   
-         $out = '<form action="SITE/dbtools/emailSend" method="post">'; 
-         $out .= Csrf::token(); 
-   
-         Form::nextFieldDetails('Who to send to', false, 'one address per line'); 
-         $out .= Form::multiline('emails', []); 
-   
-         Form::nextFieldDetails('Different FROM address', false); 
-         $out .= Form::email('from'); 
-   
-         Form::nextFieldDetails('Message to send', false); 
-         $out .= Form::multiradio('msg', [], ['long' => 'Long test email - tables, headings, unicode, etc.', 'short' => 'Short simple test email']); 
-   
-         Form::nextFieldDetails('Information', false); 
-         $out .= Form::checkboxBoolList('', [], ['debug' => 'Show debugging information']); 
-   
-         $out .= '<button class="button" type="submit">Send emails</button>'; 
-         $out .= '</form>'; 
-   
-         echo $out; 
-         $this->template('Email'); 
-     } 
-   
-   
-     /** 
-     * Process form submission 
-     **/ 
-     public function emailSend() 
-     { 
-         Csrf::checkOrDie(); 
-   
-         if (empty($_POST['emails'])) { 
-             Url::redirect('dbtools/email'); 
-         } 
-   
-         if ($_POST['msg'] == 'long') { 
-             $subject = "Test email containing a little bit of üńìĉȯḍē."; 
-             $view = new View('sprout/email/testing_long'); 
-             $body = $view->render(); 
-   
-         } else if ($_POST['msg'] == 'short') { 
-             $subject = 'Test'; 
-             $body = '<p>This is a test email.</p>'; 
-         } 
-   
-         if (!empty($_POST['from'])) { 
-             Validity::email($_POST['from']); 
-         } 
-   
-         $addresses = explode("\n", $_POST['emails']); 
-         $succ = $fail = 0; 
-         foreach ($addresses as $e) { 
-             if (! $e) continue; 
-   
-             echo '<h2>', Enc::html($e), '</h2>'; 
-   
-             try { 
-                 Validity::email($e); 
-             } catch (ValidationException $ex) { 
-                 echo '<p>', Enc::html($ex->getMessage()), '</p>'; 
-                 continue; 
-             } 
-   
-             $mail = new Email(); 
-             $mail->AddAddress($e); 
-             $mail->Subject = $subject; 
-             $mail->SkinnedHTML($body); 
-   
-             if (!empty($_POST['debug'])) { 
-                 $mail->SMTPDebug = 3; 
-             } 
-   
-             if (!empty($_POST['from'])) { 
-                 $mail->From = $_POST['from']; 
-             } 
-   
-             $result = $mail->Send(); 
-   
-             if ($log) { 
-                 echo '<pre>', Enc::html($log), '</pre>'; 
-             } 
-   
-             if ($result) { 
-                 echo '<p>Sent email to <b>', Enc::html($e), '</b>.</p>'; 
-                 $succ++; 
-             } else { 
-                 echo '<p>Sending to <b>', Enc::html($e), '</b> failed!</p>'; 
-                 $fail++; 
-             } 
-         } 
-   
-         echo '<h2>Summary</h2>'; 
-         echo '<p><b>Success:</b> ', $succ, '<br><b>Failed:</b> ', $fail, '</p>'; 
-         echo '<p><a href="SITE/dbtools/email" class="button">Send more!</a></p>'; 
-         $this->template('Email'); 
-     } 
-   
-   
-     /** 
-      * Renders form to imoprt Sprout2 Export XML 
-      * 
-      * @return void Echos HTML directly 
-      */ 
-     public function importXML() 
-     { 
-         $view = new View('sprout/dbtools/import_xml'); 
-         $view->subsites = Pdb::lookup('subsites'); 
-         echo $view->render(); 
-   
-         $this->template('Import Sprout 2 pages'); 
-     } 
-   
-   
-     /** 
-      * Process Sprout2 Export XML into this CMS 
-      * 
-      * @return void Redirects 
-      */ 
-     public function importXmlAction() 
-     { 
-         Csrf::checkOrDie(); 
-   
-         $_POST['subsite_id'] = (int) @$_POST['subsite_id']; 
-         $_POST['page_id'] = (int) @$_POST['page_id']; 
-   
-         // Validate sub-site 
-         if (empty($_POST['subsite_id'])) { 
-             Notification::error('Please select a sub-site'); 
-             Url::redirect('dbtools/importXML'); 
-         } 
-   
-         // Validate file type 
-         if ($ext != 'xml') { 
-             Notification::error('Invalid file type'); 
-             Url::redirect('dbtools/importXML'); 
-         } 
-   
-         // Determine temp filename 
-         $tempname = APPPATH . "temp/dbtools_import_{$timestamp}.{$ext}"; 
-   
-         // Attempt upload 
-         $res = @copy($_FILES['filename']['tmp_name'], $tempname); 
-         if (! $res) { 
-             Notification::error('Unable to copy file to temporary directory'); 
-             Url::redirect('dbtools/importXML'); 
-         } 
-   
-         // Run the import tool 
-         $pages = ImportCMS::import($tempname); 
-   
-   
-         // Render table of pages that need widgets replaced 
-         $list = new Itemlist(); 
-         $list->main_columns = [ 
-             'Old ID' => 'old_id', 
-             'New ID' => 'new_id', 
-             'Widgets' => 'widgets', 
-         ]; 
-         $list->items = $pages; 
-   
-         echo $list->render(); 
-   
-         $this->template('Successfully imported Sprout 2 pages'); 
-     } 
-   
-   
-     /** 
-      * Render page drop-down for given sub-site 
-      * 
-      * @param int $subsite_id 
-      * @return void Echos HTML directly 
-      */ 
-     public function ajaxPageIds($subsite_id) 
-     { 
-         $subsite_id = (int) $subsite_id; 
-         AdminAuth::checkLogin(); 
-   
-         Form::nextFieldDetails('Parent page', false, 'Import as child pages of selected parent page'); 
-         echo Form::pageDropdown('page_id', [], ['subsite' => $subsite_id]); 
-     } 
-   
-   
-     /** 
-      * Render API test form within DB tools 
-      * 
-      * @param string $class 
-      * @param string $method 
-      * @return void Echos HTML directly 
-      */ 
-     public function api($class, $method) 
-     { 
-         AdminAuth::checkLogin(); 
-   
-         $ctlr = Sprout::instance($class); 
-   
-         if (!method_exists($ctlr, $method)) throw new-  InvalidArgumentException (sprintf('Method "%s" does not exist', $method));
 
-   
-         $reflect = new ReflectionMethod($ctlr, $method); 
-         if (!$reflect->isPublic()) throw new-  InvalidArgumentException (sprintf('Method "%s" does not exist', $method));
 
-   
-   
-         // Fetch page title 
-         $title = 'API test'; 
-         foreach (self::$tools['APIs'] as $api) { 
-             preg_match('/dbtools\/api\/([a-zA-Z0-9_\%]+)\/([a-zA-Z0-9_\%]+)/', $api['url'], $matches); 
-   
-                 $title = $api['name']; 
-                 break; 
-             } 
-         } 
-   
-         $this->template($title, $html); 
-     } 
-   
-   
-     /** 
-      * Render form to set QR Code string 
-      * @return void Echos HTML directly 
-      */ 
-     public function qrCodeForm() 
-     { 
-         $view = new View('sprout/dbtools/qr_form'); 
-   
-         if (!empty($_GET['payload'])) { 
-             $view->img = sprintf('%sdbtools/qrCodeImage?payload=%s',-  Sprout ::absRoot(),-  Enc ::url($_GET['payload']));
 
-         } 
-   
-         echo $view->render(); 
-     } 
-   
-   
-     /** 
-      * Renders QR code image 
-      * @return void Echos PNG directly 
-      */ 
-     public function qrCodeImage() 
-     { 
-         header('Content-Type: image/png'); 
-     } 
- } 
-