diff --git a/.gitmodules b/.gitmodules
index a2760a75..56ec0fc5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "jsmin-php"]
path = externals/jsmin-php
url = git://github.com/rgrove/jsmin-php.git
+[submodule "tinymce"]
+ path = externals/tinymce
+ url = git://github.com/tinymce/tinymce.git
diff --git a/adminer/plugin.php b/adminer/plugin.php
new file mode 100644
index 00000000..1186815b
--- /dev/null
+++ b/adminer/plugin.php
@@ -0,0 +1,23 @@
+
value
+* @author Jakub Vrana, http://www.vrana.cz/
+* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
+*/
+class AdminerDumpXml {
+
+ function dumpFormat() {
+ return array('xml' => 'XML');
+ }
+
+ function dumpTable($table, $style, $is_view = false) {
+ if ($_POST["format"] == "xml") {
+ return true;
+ }
+ }
+
+ function dumpData($table, $style, $query) {
+ if ($_POST["format"] == "xml") {
+ echo "\n";
+ $connection = connection();
+ $result = $connection->query($query, 1);
+ if ($result) {
+ while ($row = $result->fetch_assoc()) {
+ echo "\t\n";
+ foreach ($row as $key => $val) {
+ echo "\t\t" . h($val) . "\n";
+ }
+ echo "\t
\n";
+ }
+ }
+ echo "\n";
+ return true;
+ }
+ }
+
+ function dumpHeaders($identifier, $multi_table = false) {
+ if ($_POST["format"] == "xml") {
+ header("Content-Type: text/xml; charset=utf-8");
+ return "xml";
+ }
+ }
+
+}
diff --git a/plugins/email-html.php b/plugins/email-html.php
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/file-upload.php b/plugins/file-upload.php
new file mode 100644
index 00000000..bf3ecbb9
--- /dev/null
+++ b/plugins/file-upload.php
@@ -0,0 +1,48 @@
+ and link to the uploaded files from select
+* @author Jakub Vrana, http://www.vrana.cz/
+* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
+*/
+class AdminerFileUpload {
+ var $uploadPath, $displayPath;
+
+ /**
+ * @param string prefix for uploading data (create writable subdirectory for each table containing uploadable fields)
+ * @param string prefix for displaying data, null stands for $uploadPath
+ */
+ function AdminerFileUpload($uploadPath = "../static/data/", $displayPath = null) {
+ $this->uploadPath = $uploadPath;
+ $this->displayPath = (isset($displayPath) ? $displayPath : $uploadPath);
+ }
+
+ function editInput($table, $field, $attrs, $value) {
+ if (ereg('(.*)_path$', $field["field"])) {
+ return "";
+ }
+ }
+
+ function processInput($field, $value, $function = "") {
+ if (ereg('(.*)_path$', $field["field"], $regs)) {
+ $table = ($_GET["edit"] != "" ? $_GET["edit"] : $_GET["select"]);
+ $name = "fields-$field[field]";
+ if ($_FILES[$name]["error"] || !eregi('(\\.([a-z0-9]+))?$', $_FILES[$name]["name"], $regs2)) {
+ return false;
+ }
+ //! unlink old
+ $filename = uniqid() . $regs2[0];
+ if (!move_uploaded_file($_FILES[$name]["tmp_name"], "$this->uploadPath$table/$regs[1]-$filename")) {
+ return false;
+ }
+ return q($filename);
+ }
+ }
+
+ function selectVal($val, &$link, $field) {
+ if ($val != " " && ereg('(.*)_path$', $field["field"], $regs)) {
+ $link = "$this->displayPath$_GET[select]/$regs[1]-$val";
+ }
+ }
+
+}
diff --git a/plugins/foreign-system.php b/plugins/foreign-system.php
new file mode 100644
index 00000000..014c4da5
--- /dev/null
+++ b/plugins/foreign-system.php
@@ -0,0 +1,55 @@
+ "user", "source" => array("Host", "User"), "target" => array("Host", "User")));
+ case "db": return array(array("table" => "user", "source" => array("Host", "User"), "target" => array("Host", "User")));
+ case "help_category": return array(array("table" => "help_category", "source" => array("parent_category_id"), "target" => array("help_category_id")));
+ case "help_relation": return array(array("table" => "help_topic", "source" => array("help_topic_id"), "target" => array("help_topic_id")), array("table" => "help_keyword", "source" => array("help_keyword_id"), "target" => array("help_keyword_id")));
+ case "help_topic": return array(array("table" => "help_category", "source" => array("help_category_id"), "target" => array("help_category_id")));
+ case "procs_priv": return array(array("table" => "user", "source" => array("Host", "User"), "target" => array("Host", "User")), array("table" => "proc", "source" => array("Db", "Routine_name"), "target" => array("db", "name")));
+ case "tables_priv": return array(array("table" => "user", "source" => array("Host", "User"), "target" => array("Host", "User")));
+ case "time_zone_name": return array(array("table" => "time_zone", "source" => array("Time_zone_id"), "target" => array("Time_zone_id")));
+ case "time_zone_transition": return array(array("table" => "time_zone", "source" => array("Time_zone_id"), "target" => array("Time_zone_id")), array("table" => "time_zone_transition_type", "source" => array("Time_zone_id", "Transition_type_id"), "target" => array("Time_zone_id", "Transition_type_id")));
+ case "time_zone_transition_type": return array(array("table" => "time_zone", "source" => array("Time_zone_id"), "target" => array("Time_zone_id")));
+ }
+ } elseif (DB == "information_schema") {
+ $schemata = array("table" => "SCHEMATA", "source" => array("TABLE_CATALOG", "TABLE_SCHEMA"), "target" => array("CATALOG_NAME", "SCHEMA_NAME"));
+ $tables = array("table" => "TABLES", "source" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"), "target" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"));
+ $columns = array("table" => "COLUMNS", "source" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "COLUMN_NAME"), "target" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "COLUMN_NAME"));
+ $character_sets = array("table" => "CHARACTER_SETS", "source" => array("CHARACTER_SET_NAME"), "target" => array("CHARACTER_SET_NAME"));
+ $collations = array("table" => "COLLATIONS", "source" => array("COLLATION_NAME"), "target" => array("COLLATION_NAME"));
+ $routine_charsets = array(array("source" => array("CHARACTER_SET_CLIENT")) + $character_sets, array("source" => array("COLLATION_CONNECTION")) + $collations, array("source" => array("DATABASE_COLLATION")) + $collations);
+ switch ($table) {
+ case "CHARACTER_SETS": return array(array("source" => array("DEFAULT_COLLATE_NAME")) + $collations);
+ case "COLLATIONS": return array($character_sets);
+ case "COLLATION_CHARACTER_SET_APPLICABILITY": return array($collations, $character_sets);
+ case "COLUMNS": return array($schemata, $tables, $character_sets, $collations);
+ case "COLUMN_PRIVILEGES": return array($schemata, $tables, $columns);
+ case "TABLES": return array($schemata, array("source" => array("TABLE_COLLATION")) + $collations);
+ case "SCHEMATA": return array(array("source" => array("DEFAULT_CHARACTER_SET_NAME")) + $character_sets, array("source" => array("DEFAULT_COLLATION_NAME")) + $collations);
+ case "EVENTS": return array_merge(array(array("source" => array("EVENT_CATALOG", "EVENT_SCHEMA")) + $schemata), $routine_charsets);
+ case "FILES": return array($schemata, $tables);
+ case "KEY_COLUMN_USAGE": return array(array("source" => array("CONSTRAINT_CATALOG", "CONSTRAINT_SCHEMA")) + $schemata, $schemata, $tables, $columns, array("source" => array("TABLE_CATALOG", "REFERENCED_TABLE_SCHEMA")) + $schemata, array("source" => array("TABLE_CATALOG", "REFERENCED_TABLE_SCHEMA", "REFERENCED_TABLE_NAME")) + $tables, array("source" => array("TABLE_CATALOG", "REFERENCED_TABLE_SCHEMA", "REFERENCED_TABLE_NAME", "REFERENCED_COLUMN_NAME")) + $columns);
+ case "PARTITIONS": return array($schemata, $tables);
+ case "REFERENTIAL_CONSTRAINTS": return array(array("source" => array("CONSTRAINT_CATALOG", "CONSTRAINT_SCHEMA")) + $schemata, array("source" => array("UNIQUE_CONSTRAINT_CATALOG", "UNIQUE_CONSTRAINT_SCHEMA")) + $schemata, array("source" => array("CONSTRAINT_CATALOG", "CONSTRAINT_SCHEMA", "TABLE_NAME")) + $tables, array("source" => array("CONSTRAINT_CATALOG", "CONSTRAINT_SCHEMA", "REFERENCED_TABLE_NAME")) + $tables);
+ case "ROUTINES": return array_merge(array(array("source" => array("ROUTINE_CATALOG", "ROUTINE_SCHEMA")) + $schemata), $routine_charsets);
+ case "SCHEMA_PRIVILEGES": return array($schemata);
+ case "STATISTICS": return array($schemata, $tables, $columns, array("source" => array("TABLE_CATALOG", "INDEX_SCHEMA")) + $schemata);
+ case "TABLE_CONSTRAINTS": return array(array("source" => array("CONSTRAINT_CATALOG", "CONSTRAINT_SCHEMA")) + $schemata, array("source" => array("CONSTRAINT_CATALOG", "TABLE_SCHEMA")) + $schemata, array("source" => array("CONSTRAINT_CATALOG", "TABLE_SCHEMA", "TABLE_NAME")) + $tables);
+ case "TABLE_PRIVILEGES": return array($schemata, $tables);
+ case "TRIGGERS": return array_merge(array(array("source" => array("TRIGGER_CATALOG", "TRIGGER_SCHEMA")) + $schemata, array("source" => array("EVENT_OBJECT_CATALOG", "EVENT_OBJECT_SCHEMA")) + $schemata, array("source" => array("EVENT_OBJECT_CATALOG", "EVENT_OBJECT_SCHEMA", "EVENT_OBJECT_TABLE")) + $tables), $routine_charsets);
+ case "VIEWS": return array($schemata);
+ }
+ }
+ }
+
+}
diff --git a/plugins/plugin.php b/plugins/plugin.php
new file mode 100644
index 00000000..5e83cd81
--- /dev/null
+++ b/plugins/plugin.php
@@ -0,0 +1,263 @@
+plugins = $plugins;
+ // it is possible to use ReflectionObject in PHP 5 to find out which plugins defines which methods at once
+ }
+
+ function _applyPlugin($function, $args) {
+ foreach ($this->plugins as $plugin) {
+ if (method_exists($plugin, $function)) {
+ foreach ($args as $key => $val) {
+ $args[$key] = &$args[$key]; // allows modification of parameters
+ }
+ $return = call_user_func_array(array($plugin, $function), $args);
+ if (isset($return)) {
+ return $return;
+ }
+ }
+ }
+ return call_user_func_array(array($this, "parent::$function"), $args);
+ }
+
+ function _appendPlugin($function, $args) {
+ $return = call_user_func_array(array($this, "parent::$function"), $args);
+ foreach ($this->plugins as $plugin) {
+ if (method_exists($plugin, $function)) {
+ $return += call_user_func_array(array($plugin, $function), $args);
+ }
+ }
+ return $return;
+ }
+
+ // appendPlugin
+
+ function dumpFormat() {
+ $args = func_get_args();
+ return $this->_appendPlugin(__FUNCTION__, $args);
+ }
+
+ function dumpOutput() {
+ $args = func_get_args();
+ return $this->_appendPlugin(__FUNCTION__, $args);
+ }
+
+ function editFunctions() {
+ $args = func_get_args();
+ return $this->_appendPlugin(__FUNCTION__, $args);
+ }
+
+ // applyPlugin
+
+ function name() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function credentials() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function permanentLogin() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function database() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function headers() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function loginForm() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function login() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function tableName() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function fieldName() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectLinks() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function foreignKeys() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function backwardKeys() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function backwardKeysPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectQuery() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function rowDescription() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function rowDescriptions() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectVal() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function editVal() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectColumnsPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectSearchPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectOrderPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectLimitPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectLengthPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectActionPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectEmailPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectColumnsProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectSearchProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectOrderProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectLimitProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectLengthProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function selectEmailProcess() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function messageQuery() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function editInput() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function processInput() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function dumpTable() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function dumpData() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function dumpHeaders() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function homepage() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function navigation() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+ function tablesPrint() {
+ $args = func_get_args();
+ return $this->_applyPlugin(__FUNCTION__, $args);
+ }
+
+}
diff --git a/plugins/readme.txt b/plugins/readme.txt
new file mode 100644
index 00000000..b70eb493
--- /dev/null
+++ b/plugins/readme.txt
@@ -0,0 +1,2 @@
+../adminer/plugin.php - demo usage
+http://www.adminer.org/en/plugins/ - documentation
diff --git a/plugins/slugify.php b/plugins/slugify.php
new file mode 100644
index 00000000..3344b22e
--- /dev/null
+++ b/plugins/slugify.php
@@ -0,0 +1,40 @@
+from = $from;
+ $this->to = $to;
+ }
+
+ function editInput($table, $field, $attrs, $value) {
+ static $slugify;
+ if (!$_GET["select"] && !$_GET["where"]) {
+ if (!isset($slugify)) {
+ $slugify = array();
+ $prev = null;
+ foreach (fields($table) as $name => $field) {
+ if ($prev && ereg('(^|_)slug(_|$)', $name)) {
+ $slugify[$prev] = $name;
+ }
+ $prev = $name;
+ }
+ }
+ $slug = $slugify[$field["field"]];
+ if (isset($slug)) {
+ return "";
+ }
+ }
+ }
+
+}
diff --git a/plugins/tinymce.php b/plugins/tinymce.php
new file mode 100644
index 00000000..4726f5de
--- /dev/null
+++ b/plugins/tinymce.php
@@ -0,0 +1,59 @@
+path = $path;
+ }
+
+ function selectVal(&$val, $link, $field) {
+ if (ereg("_html", $field["field"]) && $val != ' ') {
+ $val = preg_replace('~<[^>]*$~', '', html_entity_decode($val, ENT_QUOTES, 'utf-8')); //! close all opened tags (text can be shortened)
+ }
+ }
+
+ function editInput($table, $field, $attrs, $value) {
+ static $tiny_mce = false;
+ if (ereg("text", $field["type"]) && ereg("_html", $field["field"])) {
+ if (!$tiny_mce) {
+ $tiny_mce = true;
+ $lang = "en";
+ if (function_exists('get_lang')) { // since Adminer 3.2.0
+ $lang = get_lang();
+ $lang = ($lang == "zh" ? "zh-cn" : ($lang == "zh-tw" ? "zh" : $lang));
+ if (!file_exists(dirname($this->path) . "/langs/$lang.js")) {
+ $lang = "en";
+ }
+ }
+ ?>
+
+
+" . h($value) . "";
+ }
+ }
+
+}
diff --git a/plugins/translation.php b/plugins/translation.php
new file mode 100644
index 00000000..c3e585db
--- /dev/null
+++ b/plugins/translation.php
@@ -0,0 +1,55 @@
+query("INSERT INTO translation (language_id, idf, translation) VALUES (" . q($lang) . ", " . q($idf) . ", " . q($s) . ")");
+ }
+ return $return;
+ }
+
+ function tableName(&$tableStatus) {
+ $tableStatus["Comment"] = $this->_translate($tableStatus["Comment"]);
+ }
+
+ function fieldName(&$field, $order = 0) {
+ $field["comment"] = $this->_translate($field["comment"]);
+ }
+
+ function editVal(&$val, $field) {
+ if ($field["type"] == "enum") {
+ $val = $this->_translate($val);
+ }
+ }
+
+}
diff --git a/readme.txt b/readme.txt
index fbe4a161..380e078f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -9,6 +9,8 @@ Apache License 2.0 or GPL 2
adminer/index.php - Run development version of Adminer
editor/index.php - Run development version of Adminer Editor
editor/example.php - Example customization
+plugins/readme.txt - Plugins for Adminer and Adminer Editor
+adminer/plugin.php - Plugin demo
compile.php [driver] [lang] - Create a single file version
lang.php [lang] - Update translations
tests/selenium.html - Selenium test suite