Compare commits

...

55 Commits

Author SHA1 Message Date
Jakub Vrana
536999d4dc Release 4.5.0 2018-01-24 21:55:47 +01:00
Jakub Vrana
827a2b8a54 Fix tests after 95d02bb9 2018-01-24 20:52:44 +01:00
Jakub Vrana
a954f7d4b4 Simplify drop object confirmation questions 2018-01-24 18:36:19 +01:00
Jakub Vrana
a137f248f7 Translate message added in 4.4.0 2018-01-24 18:22:20 +01:00
Jakub Vrana
95d02bb924 Display name of the object in confirmation when dropping it 2018-01-24 18:13:05 +01:00
Jakub Vrana
921e57b9aa Display error when getting row to edit 2018-01-24 17:37:35 +01:00
Jakub Vrana
25f9655a38 Avoid CONVERT() except in MySQL (bug #509) 2018-01-24 17:37:14 +01:00
Jakub Vrana
919204dd38 Bump version 2018-01-24 16:51:53 +01:00
Jakub Vrana
34f25afe90 MySQL: Support routines with comments in parameters (bug #460) 2018-01-24 16:37:38 +01:00
Jakub Vrana
8b29ae910f Verify if adminer.version was created on www.adminer.org 2018-01-24 15:10:44 +01:00
Jakub Vrana
6c96b060e7 Store current Adminer version server-side to avoid excessive requests 2018-01-24 15:09:11 +01:00
Jakub Vrana
f4662d5e27 Send token as last param 2018-01-24 13:44:31 +01:00
Jakub Vrana
b15618fcfd Allow using adminer.invalid by other users in a group 2018-01-24 12:11:26 +01:00
Jakub Vrana
03e3f517a8 Extract file_open_lock and file_write_unlock 2018-01-24 12:04:53 +01:00
Jakub Vrana
f0d2af329a Display boolean values as code (bug #562) 2018-01-24 10:53:25 +01:00
Jakub Vrana
452b9ad7f1 MariaDB: Support fulltext and spatial indexes in InnoDB (bug #583) 2018-01-24 09:18:19 +01:00
Jakub Vrana
fb18e814de PostgreSQL: Display foreign tables (bug #576) 2018-01-23 15:03:53 +01:00
Jakub Vrana
75413c56d2 Add designs.php 2018-01-23 14:36:32 +01:00
Jakub Vrana
c3d83d5412 Fix compiled version after 5cc831c1 2018-01-23 14:10:21 +01:00
Jakub Vrana
b14aa75a98 Hide window.opener from pages opened in a new window (bug #561) 2018-01-23 12:53:22 +01:00
Jakub Vrana
dbc76a2b1e Fix GPL version 2018-01-23 12:21:42 +01:00
Jakub Vrana
6d774794c8 Add plugin for switching designs 2018-01-23 12:15:38 +01:00
Jakub Vrana
81be5edb97 Display bold parts of error (bug #565) 2018-01-23 11:16:28 +01:00
Jakub Vrana
e30e5dd059 Add bottom border to last cells 2018-01-23 11:11:41 +01:00
Jakub Vrana
0cdd8beab1 Editor: Fix displaying of false values in PostgreSQL (bug #568) 2018-01-23 11:02:15 +01:00
Jakub Vrana
7b9d5a29aa Decrease float precision (bug #578) 2018-01-23 10:52:57 +01:00
Andrea Giacobino
5cc831c1e1 Add ability to override file name (path) when loading dump from webserver 2018-01-23 09:24:57 +01:00
Jakub Vrana
72aa3c5127 Support current_timestamp() as default of time fields (bug #572) 2018-01-23 08:36:11 +01:00
Jakub Vrana
43ccfcef8f Display newlines in column comments (bug #573) 2018-01-22 21:05:56 +01:00
Jakub Vrana
c789c147b7 MySQL: Support geometry in MySQL 8 (bug #574) 2018-01-22 20:56:35 +01:00
Uherkovich Péter
a2adb67d2f MSSQL driver fixed for freetds drivers. See bug #167, #291 2018-01-22 18:35:08 +01:00
Jakub Vrana
34e2f47761 Fix search focus (regression from 4.4.0) 2018-01-22 18:34:16 +01:00
Jakub Vrana
0d2b232bd7 Compatibility with PHP < 5.3 2018-01-22 17:30:12 +01:00
Jakub Vrana
83e16e059f Add mongodb as a possible driver 2018-01-22 17:12:48 +01:00
Jakub Vrana
6e4dc3911f Save bytes 2018-01-22 17:05:49 +01:00
Vladimir Goncharov
2a08a11e89 php7 support with minimal changes of original code 2018-01-22 16:59:55 +01:00
Jakub Vrana
993dce398d PostgreSQL: Export DROP SEQUENCE if dropping table 2018-01-22 14:33:21 +01:00
Jakub Vrana
eee9a62c8f PostgreSQL: Do not export triggers if not requested 2018-01-22 14:25:27 +01:00
Jakub Vrana
ce17b6cf46 Ignore removed CREATE+ALTER mode in trigger_sql() 2018-01-22 14:14:42 +01:00
Jakub Vrana
923c0ffc87 Fix whitespace 2018-01-22 13:51:47 +01:00
Jakub Vrana
48ed20323f CSP: Allow any CSS 2018-01-22 12:22:25 +01:00
Troy
9a4cd8936d adminer-theme-mancave2-2.0.alpha 2018-01-22 12:20:33 +01:00
Jáchym Toušek
019ada8da5 Fix NULL value in edit form with Postgres 2018-01-22 12:06:15 +01:00
Jakub Vrana
6a95b71164 Do not warn about MySQL specific functions 2018-01-22 12:00:38 +01:00
Jakub Vrana
3b4ce4a0b4 Fix single driver non-MySQL compiled versions (regression from 150301ed) 2018-01-22 11:42:17 +01:00
Jakub Vrana
a414896885 Hide driver selection in single-driver version (regression from e762a6aa) 2018-01-22 11:34:25 +01:00
Peter Knut
47734d8ff4 Unify HTML for list of tables in Editor according to Adminer 2018-01-22 10:57:20 +01:00
Peter Knut
fef1808288 Encapsulate fulltext part in search box into <div> 2018-01-22 10:53:50 +01:00
Fabio Montefuscolo
d2c82fdeca Enhance ElasticSearch integration 2018-01-19 18:32:41 +01:00
Jakub Vrana
e657cdbc3a Allow PHP 5.6 in Travis 2018-01-19 18:20:02 +01:00
Jakub Vrana
bc14e8432f Select numeric database name in DB list (bug #580) 2018-01-19 17:39:27 +01:00
Jakub Vrana
34b03ef835 SQLite: Enable foreign key checks 2018-01-19 14:57:58 +01:00
Jakub Vrana
329b7de9cc CSP: Allow any images, media and fonts, disallow base-uri 2018-01-19 11:05:26 +01:00
Jakub Vrana
2dcad1f284 Define JS functions in AdminerTablesFilter sooner 2018-01-19 10:55:51 +01:00
Jakub Vrana
1b98a10100 Adminer: Fix Search data in tables 2018-01-19 10:46:35 +01:00
68 changed files with 2205 additions and 347 deletions

View File

@@ -1,4 +1,5 @@
language: php
php:
- '5.6'
- '7.1'
script: git diff --name-only $TRAVIS_COMMIT_RANGE | grep '\.php$' | xargs -n1 -P8 php -l | grep -v 'No syntax errors'; test $? -eq 1

View File

@@ -198,7 +198,7 @@ edit_fields($row["fields"], $collations, "TABLE", $foreign_keys, $comments);
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php } ?>
<?php if ($TABLE != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($TABLE != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $TABLE)); ?><?php } ?>
<?php
if (support("partitioning")) {
$partition_table = preg_match('~RANGE|LIST~', $row["partition_by"]);

View File

@@ -70,7 +70,7 @@ echo script("focus(qs('#name'));");
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php
if (DB != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm() . "\n";
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', DB)) . "\n";
} elseif (!$_POST["add_x"] && $_GET["db"] == "") {
echo "<input type='image' class='icon' name='add' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>\n";
}

14
adminer/designs.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
function adminer_object() {
include_once "../plugins/plugin.php";
include_once "../plugins/designs.php";
$designs = array();
foreach (glob("../designs/*", GLOB_ONLYDIR) as $filename) {
$designs["$filename/adminer.css"] = basename($filename);
}
return new AdminerPlugin(array(
new AdminerDesigns($designs),
));
}
include "./index.php";

View File

@@ -17,9 +17,10 @@ if (isset($_GET["elastic"])) {
*/
function rootQuery($path, $content = array(), $method = 'GET') {
@ini_set('track_errors', 1); // @ - may be disabled
$file = @file_get_contents($this->_url . '/' . ltrim($path, '/'), false, stream_context_create(array('http' => array(
$file = @file_get_contents("$this->_url/" . ltrim($path, '/'), false, stream_context_create(array('http' => array(
'method' => $method,
'content' => json_encode($content),
'content' => $content === null ? $content : json_encode($content),
'header' => 'Content-Type: application/json',
'ignore_errors' => 1, // available since PHP 5.2.10
))));
if (!$file) {
@@ -60,7 +61,7 @@ if (isset($_GET["elastic"])) {
function connect($server, $username, $password) {
preg_match('~^(https?://)?(.*)~', $server, $match);
$this->_url = ($match[1] ? $match[1] : "http://") . "$username:$password@$match[2]/";
$this->_url = ($match[1] ? $match[1] : "http://") . "$username:$password@$match[2]";
$return = $this->query('');
if ($return) {
$this->server_info = $return['version']['number'];
@@ -156,7 +157,7 @@ if (isset($_GET["elastic"])) {
foreach ($search['hits']['hits'] as $hit) {
$row = array();
if ($select == array("*")) {
$row["_id"] = $hit["_id"];
$row["_id"] = $hit["_id"];
}
$fields = $hit['_source'];
if ($select != array("*")) {
@@ -176,6 +177,47 @@ if (isset($_GET["elastic"])) {
return new Min_Result($return);
}
function update($type, $record, $queryWhere) {
$parts = preg_split('~ *= *~', $queryWhere);
if (count($parts) == 2) {
$id = trim($parts[1]);
$query = "$type/$id";
return $this->_conn->query($query, $record, 'POST');
}
return false;
}
function insert($type, $record) {
$id = ""; //! user should be able to inform _id
$query = "$type/$id";
$response = $this->_conn->query($query, $record, 'POST');
$this->_conn->last_id = $response['_id'];
return $response['created'];
}
function delete($type, $queryWhere) {
$ids = array();
if (is_array($_GET["where"]) && $_GET["where"]["_id"]) {
$ids[] = $_GET["where"]["_id"];
}
if (is_array($_POST['check'])) {
foreach ($_POST['check'] as $check) {
$parts = preg_split('~ *= *~', $check);
if (count($parts) == 2) {
$ids[] = trim($parts[1]);
}
}
}
$this->_conn->affected_rows = 0;
foreach ($ids as $id) {
$query = "{$type}/{$id}";
$response = $this->_conn->query($query, '{}', 'DELETE');
if (is_array($response) && $response['found'] == true) {
$this->_conn->affected_rows++;
}
}
return $this->_conn->affected_rows;
}
}
@@ -223,9 +265,14 @@ if (isset($_GET["elastic"])) {
function count_tables($databases) {
global $connection;
$return = $connection->query('_mapping');
if ($return) {
$return = array_map('count', $return);
$return = array();
$result = $connection->query('_stats');
if ($result && $result['indices']) {
$indices = $result['indices'];
foreach($indices as $indice => $stats) {
$indexing = $stats['total']['indexing'];
$return[$indice] = $indexing['index_total'];
}
}
return $return;
}
@@ -241,24 +288,26 @@ if (isset($_GET["elastic"])) {
function table_status($name = "", $fast = false) {
global $connection;
$search = $connection->query("_search?search_type=count", array(
"facets" => array(
$search = $connection->query("_search", array(
"size" => 0,
"aggregations" => array(
"count_by_type" => array(
"terms" => array(
"field" => "_type",
"field" => "_type"
)
)
)
), "POST");
$return = array();
if ($search) {
foreach ($search["facets"]["count_by_type"]["terms"] as $table) {
$return[$table["term"]] = array(
"Name" => $table["term"],
$tables = $search["aggregations"]["count_by_type"]["buckets"];
foreach ($tables as $table) {
$return[$table["key"]] = array(
"Name" => $table["key"],
"Engine" => "table",
"Rows" => $table["count"],
"Rows" => $table["doc_count"],
);
if ($name != "" && $name == $table["term"]) {
if ($name != "" && $name == $table["key"]) {
return $return[$name];
}
}
@@ -336,16 +385,16 @@ if (isset($_GET["elastic"])) {
return null;
}
/** Create database
/** Create index
* @param string
* @return mixed
*/
function create_database($db) {
global $connection;
return $connection->rootQuery(urlencode($db), array(), 'PUT');
return $connection->rootQuery(urlencode($db), null, 'PUT');
}
/** Drop databases
/** Remove index
* @param array
* @return mixed
*/
@@ -354,7 +403,27 @@ if (isset($_GET["elastic"])) {
return $connection->rootQuery(urlencode(implode(',', $databases)), array(), 'DELETE');
}
/** Drop tables
/** Alter type
* @param array
* @return mixed
*/
function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
global $connection;
$properties = array();
foreach($fields as $f) {
$field_name = trim($f[1][0]);
$field_type = trim($f[1][1] ?: "text");
$properties[$field_name] = array(
'type' => $field_type
);
}
if (!empty($properties)) {
$properties = array('properties' => $properties);
}
return $connection->query("_mapping/{$name}", $properties, 'PUT');
}
/** Drop types
* @param array
* @return bool
*/
@@ -367,9 +436,25 @@ if (isset($_GET["elastic"])) {
return $return;
}
function last_id() {
global $connection;
return $connection->last_id;
}
$jush = "elastic";
$operators = array("=", "query");
$functions = array();
$grouping = array();
$edit_functions = array(array("json"));
$types = array(); ///< @var array ($type => $maximum_unsigned_length, ...)
$structured_types = array(); ///< @var array ($description => array($type, ...), ...)
foreach (array(
lang('Numbers') => array("long" => 3, "integer" => 5, "short" => 8, "byte" => 10, "double" => 20, "float" => 66, "half_float" => 12, "scaled_float" => 21),
lang('Date and time') => array("date" => 10),
lang('Strings') => array("string" => 65535, "text" => 65535),
lang('Binary') => array("binary" => 255),
) as $key => $val) {
$types += $val;
$structured_types[$key] = array_keys($val);
}
}

View File

@@ -2,7 +2,7 @@
$drivers["mongo"] = "MongoDB (beta)";
if (isset($_GET["mongo"])) {
$possible_drivers = array("mongo");
$possible_drivers = array("mongo", "mongodb");
define("DRIVER", "mongo");
if (class_exists('MongoDB')) {
@@ -109,97 +109,502 @@ if (isset($_GET["mongo"])) {
}
}
}
class Min_Driver extends Min_SQL {
public $primary = "_id";
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
$select = ($select == array("*")
? array()
: array_fill_keys($select, true)
);
$sort = array();
foreach ($order as $val) {
$val = preg_replace('~ DESC$~', '', $val, 1, $count);
$sort[$val] = ($count ? -1 : 1);
class Min_Driver extends Min_SQL {
public $primary = "_id";
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
$select = ($select == array("*")
? array()
: array_fill_keys($select, true)
);
$sort = array();
foreach ($order as $val) {
$val = preg_replace('~ DESC$~', '', $val, 1, $count);
$sort[$val] = ($count ? -1 : 1);
}
return new Min_Result($this->_conn->_db->selectCollection($table)
->find(array(), $select)
->sort($sort)
->limit($limit != "" ? +$limit : 0)
->skip($page * $limit)
);
}
function insert($table, $set) {
try {
$return = $this->_conn->_db->selectCollection($table)->insert($set);
$this->_conn->errno = $return['code'];
$this->_conn->error = $return['err'];
$this->_conn->last_id = $set['_id'];
return !$return['err'];
} catch (Exception $ex) {
$this->_conn->error = $ex->getMessage();
return false;
}
}
return new Min_Result($this->_conn->_db->selectCollection($table)
->find(array(), $select)
->sort($sort)
->limit($limit != "" ? +$limit : 0)
->skip($page * $limit)
);
}
function insert($table, $set) {
try {
$return = $this->_conn->_db->selectCollection($table)->insert($set);
$this->_conn->errno = $return['code'];
$this->_conn->error = $return['err'];
$this->_conn->last_id = $set['_id'];
return !$return['err'];
} catch (Exception $ex) {
$this->_conn->error = $ex->getMessage();
function get_databases($flush) {
global $connection;
$return = array();
$dbs = $connection->_link->listDBs();
foreach ($dbs['databases'] as $db) {
$return[] = $db['name'];
}
return $return;
}
function count_tables($databases) {
global $connection;
$return = array();
foreach ($databases as $db) {
$return[$db] = count($connection->_link->selectDB($db)->getCollectionNames(true));
}
return $return;
}
function tables_list() {
global $connection;
return array_fill_keys($connection->_db->getCollectionNames(true), 'table');
}
function drop_databases($databases) {
global $connection;
foreach ($databases as $db) {
$response = $connection->_link->selectDB($db)->drop();
if (!$response['ok']) {
return false;
}
}
return true;
}
function indexes($table, $connection2 = null) {
global $connection;
$return = array();
foreach ($connection->_db->selectCollection($table)->getIndexInfo() as $index) {
$descs = array();
foreach ($index["key"] as $column => $type) {
$descs[] = ($type == -1 ? '1' : null);
}
$return[$index["name"]] = array(
"type" => ($index["name"] == "_id_" ? "PRIMARY" : ($index["unique"] ? "UNIQUE" : "INDEX")),
"columns" => array_keys($index["key"]),
"lengths" => array(),
"descs" => $descs,
);
}
return $return;
}
function fields($table) {
return fields_from_edit();
}
function found_rows($table_status, $where) {
global $connection;
//! don't call count_rows()
return $connection->_db->selectCollection($_GET["select"])->count($where);
}
$operators = array("=");
} elseif (class_exists('MongoDB\Driver\Manager')) {
class Min_DB {
var $extension = "MongoDB", $error, $last_id;
/** @var MongoDB\Driver\Manager */
var $_link;
var $_db, $_db_name;
function connect($server, $username, $password) {
global $adminer;
$db = $adminer->database();
$options = array();
if ($username != "") {
$options["username"] = $username;
$options["password"] = $password;
}
if ($db != "") {
$options["db"] = $db;
}
try {
$class = 'MongoDB\Driver\Manager';
$this->_link = new $class("mongodb://$server", $options);
return true;
} catch (Exception $ex) {
$this->error = $ex->getMessage();
return false;
}
}
function query($query) {
return false;
}
function select_db($database) {
try {
$this->_db_name = $database;
return true;
} catch (Exception $ex) {
$this->error = $ex->getMessage();
return false;
}
}
function quote($string) {
return $string;
}
}
}
class Min_Result {
var $num_rows, $_rows = array(), $_offset = 0, $_charset = array();
function __construct($result) {
foreach ($result as $item) {
$row = array();
foreach ($item as $key => $val) {
if (is_a($val, 'MongoDB\BSON\Binary')) {
$this->_charset[$key] = 63;
}
$row[$key] =
(is_a($val, 'MongoDB\BSON\ObjectID') ? 'MongoDB\BSON\ObjectID("' . strval($val) . '")' :
(is_a($val, 'MongoDB\BSON\UTCDatetime') ? $val->toDateTime()->format('Y-m-d H:i:s') :
(is_a($val, 'MongoDB\BSON\Binary') ? $val->bin : //! allow downloading
(is_a($val, 'MongoDB\BSON\Regex') ? strval($val) :
(is_object($val) ? json_encode($val, 256) : // 256 = JSON_UNESCAPED_UNICODE
$val // MongoMinKey, MongoMaxKey
)))));
}
$this->_rows[] = $row;
foreach ($row as $key => $val) {
if (!isset($this->_rows[0][$key])) {
$this->_rows[0][$key] = null;
}
}
}
$this->num_rows = $result->count;
}
function fetch_assoc() {
$row = current($this->_rows);
if (!$row) {
return $row;
}
$return = array();
foreach ($this->_rows[0] as $key => $val) {
$return[$key] = $row[$key];
}
next($this->_rows);
return $return;
}
function fetch_row() {
$return = $this->fetch_assoc();
if (!$return) {
return $return;
}
return array_values($return);
}
function fetch_field() {
$keys = array_keys($this->_rows[0]);
$name = $keys[$this->_offset++];
return (object) array(
'name' => $name,
'charsetnr' => $this->_charset[$name],
);
}
function connect() {
global $adminer;
$connection = new Min_DB;
$credentials = $adminer->credentials();
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
return $connection;
}
return $connection->error;
}
function error() {
global $connection;
return h($connection->error);
}
function logged_user() {
global $adminer;
$credentials = $adminer->credentials();
return $credentials[1];
}
class Min_Driver extends Min_SQL {
public $primary = "_id";
function get_databases($flush) {
global $connection;
$return = array();
$dbs = $connection->_link->listDBs();
foreach ($dbs['databases'] as $db) {
$return[] = $db['name'];
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
global $connection;
$select = ($select == array("*")
? array()
: array_fill_keys($select, 1)
);
if (count($select) && !isset($select['_id'])) {
$select['_id'] = 0;
}
$where = where_to_query($where);
$sort = array();
foreach ($order as $val) {
$val = preg_replace('~ DESC$~', '', $val, 1, $count);
$sort[$val] = ($count ? -1 : 1);
}
if (isset($_GET['limit']) && is_numeric($_GET['limit']) && $_GET['limit'] > 0) {
$limit = $_GET['limit'];
}
$limit = min(200, max(1, (int) $limit));
$skip = $page * $limit;
$class = 'MongoDB\Driver\Query';
$query = new $class($where, array('projection' => $select, 'limit' => $limit, 'skip' => $skip, 'sort' => $sort));
$results = $connection->_link->executeQuery("$connection->_db_name.$table", $query);
return new Min_Result($results);
}
function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
global $connection;
$db = $connection->_db_name;
$where = sql_query_where_parser($queryWhere);
$class = 'MongoDB\Driver\BulkWrite';
$bulk = new $class(array());
if (isset($set['_id'])) {
unset($set['_id']);
}
$removeFields = array();
foreach ($set as $key => $value) {
if ($value == 'NULL') {
$removeFields[$key] = 1;
unset($set[$key]);
}
}
$update = array('$set' => $set);
if (count($removeFields)) {
$update['$unset'] = $removeFields;
}
$bulk->update($where, $update, array('upsert' => false));
$results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
$connection->affected_rows = $results->getModifiedCount();
return true;
}
function delete($table, $queryWhere, $limit = 0) {
global $connection;
$db = $connection->_db_name;
$where = sql_query_where_parser($queryWhere);
$class = 'MongoDB\Driver\BulkWrite';
$bulk = new $class(array());
$bulk->delete($where, array('limit' => $limit));
$results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
$connection->affected_rows = $results->getDeletedCount();
return true;
}
function insert($table, $set) {
global $connection;
$db = $connection->_db_name;
$class = 'MongoDB\Driver\BulkWrite';
$bulk = new $class(array());
if (isset($set['_id']) && empty($set['_id'])) {
unset($set['_id']);
}
$bulk->insert($set);
$results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
$connection->affected_rows = $results->getInsertedCount();
return true;
}
}
return $return;
}
function collations() {
return array();
}
function db_collation($db, $collations) {
}
function count_tables($databases) {
global $connection;
$return = array();
foreach ($databases as $db) {
$return[$db] = count($connection->_link->selectDB($db)->getCollectionNames(true));
function get_databases($flush) {
/** @var $connection Min_DB */
global $connection;
$return = array();
$class = 'MongoDB\Driver\Command';
$command = new $class(array('listDatabases' => 1));
$results = $connection->_link->executeCommand('admin', $command);
foreach ($results as $dbs) {
foreach ($dbs->databases as $db) {
$return[] = $db->name;
}
}
return $return;
}
return $return;
function count_tables($databases) {
$return = array();
return $return;
}
function tables_list() {
global $connection;
$class = 'MongoDB\Driver\Command';
$command = new $class(array('listCollections' => 1));
$results = $connection->_link->executeCommand($connection->_db_name, $command);
$collections = array();
foreach ($results as $result) {
$collections[$result->name] = 'table';
}
return $collections;
}
function drop_databases($databases) {
return false;
}
function indexes($table, $connection2 = null) {
global $connection;
$return = array();
$class = 'MongoDB\Driver\Command';
$command = new $class(array('listIndexes' => $table));
$results = $connection->_link->executeCommand($connection->_db_name, $command);
foreach ($results as $index) {
$descs = array();
$columns = array();
foreach (get_object_vars($index->key) as $column => $type) {
$descs[] = ($type == -1 ? '1' : null);
$columns[] = $column;
}
$return[$index->name] = array(
"type" => ($index->name == "_id_" ? "PRIMARY" : (isset($index->unique) ? "UNIQUE" : "INDEX")),
"columns" => $columns,
"lengths" => array(),
"descs" => $descs,
);
}
return $return;
}
function fields($table) {
$fields = fields_from_edit();
if (!count($fields)) {
global $driver;
$result = $driver->select($table, array("*"), null, null, array(), 10);
while ($row = $result->fetch_assoc()) {
foreach ($row as $key => $val) {
$row[$key] = null;
$fields[$key] = array(
"field" => $key,
"type" => "string",
"null" => ($key != $driver->primary),
"auto_increment" => ($key == $driver->primary),
"privileges" => array(
"insert" => 1,
"select" => 1,
"update" => 1,
),
);
}
}
}
return $fields;
}
function found_rows($table_status, $where) {
global $connection;
$where = where_to_query($where);
$class = 'MongoDB\Driver\Command';
$command = new $class(array('count' => $table_status['Name'], 'query' => $where));
$results = $connection->_link->executeCommand($connection->_db_name, $command);
$toArray = $results->toArray();
return $toArray[0]->n;
}
function sql_query_where_parser($queryWhere) {
$queryWhere = trim(preg_replace('/WHERE[\s]?[(]?\(?/', '', $queryWhere));
$queryWhere = preg_replace('/\)\)\)$/', ')', $queryWhere);
$wheres = explode(' AND ', $queryWhere);
$wheresOr = explode(') OR (', $queryWhere);
$where = array();
foreach ($wheres as $whereStr) {
$where[] = trim($whereStr);
}
if (count($wheresOr) == 1) {
$wheresOr = array();
} elseif (count($wheresOr) > 1) {
$where = array();
}
return where_to_query($where, $wheresOr);
}
function where_to_query($whereAnd = array(), $whereOr = array()) {
global $operators;
$data = array();
foreach (array('and' => $whereAnd, 'or' => $whereOr) as $type => $where) {
if (is_array($where)) {
foreach ($where as $expression) {
list($col, $op, $val) = explode(" ", $expression, 3);
if ($col == "_id") {
$val = str_replace('MongoDB\BSON\ObjectID("', "", $val);
$val = str_replace('")', "", $val);
$class = 'MongoDB\BSON\ObjectID';
$val = new $class($val);
}
if (!in_array($op, $operators)) {
continue;
}
if (preg_match('~^\(f\)(.+)~', $op, $match)) {
$val = (float) $val;
$op = $match[1];
} elseif (preg_match('~^\(date\)(.+)~', $op, $match)) {
$dateTime = new DateTime($val);
$class = 'MongoDB\BSON\UTCDatetime';
$val = new $class($dateTime->getTimestamp() * 1000);
$op = $match[1];
}
switch ($op) {
case '=':
$op = '$eq';
break;
case '!=':
$op = '$ne';
break;
case '>':
$op = '$gt';
break;
case '<':
$op = '$lt';
break;
case '>=':
$op = '$gte';
break;
case '<=':
$op = '$lte';
break;
case 'regex':
$op = '$regex';
break;
default:
continue;
}
if ($type == 'and') {
$data['$and'][] = array($col => array($op => $val));
} elseif ($type == 'or') {
$data['$or'][] = array($col => array($op => $val));
}
}
}
}
return $data;
}
$operators = array(
"=",
"!=",
">",
"<",
">=",
"<=",
"regex",
"(f)=",
"(f)!=",
"(f)>",
"(f)<",
"(f)>=",
"(f)<=",
"(date)=",
"(date)!=",
"(date)>",
"(date)<",
"(date)>=",
"(date)<=",
);
}
function tables_list() {
global $connection;
return array_fill_keys($connection->_db->getCollectionNames(true), 'table');
function table($idf) {
return $idf;
}
function idf_escape($idf) {
return $idf;
}
function table_status($name = "", $fast = false) {
@@ -213,43 +618,73 @@ if (isset($_GET["mongo"])) {
return $return;
}
function information_schema() {
}
function is_view($table_status) {
}
function drop_databases($databases) {
function last_id() {
global $connection;
foreach ($databases as $db) {
$response = $connection->_link->selectDB($db)->drop();
if (!$response['ok']) {
return $connection->last_id;
}
function error() {
global $connection;
return h($connection->error);
}
function collations() {
return array();
}
function logged_user() {
global $adminer;
$credentials = $adminer->credentials();
return $credentials[1];
}
function connect() {
global $adminer;
$connection = new Min_DB;
$credentials = $adminer->credentials();
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
return $connection;
}
return $connection->error;
}
function alter_indexes($table, $alter) {
global $connection;
foreach ($alter as $val) {
list($type, $name, $set) = $val;
if ($set == "DROP") {
$return = $connection->_db->command(array("deleteIndexes" => $table, "index" => $name));
} else {
$columns = array();
foreach ($set as $column) {
$column = preg_replace('~ DESC$~', '', $column, 1, $count);
$columns[$column] = ($count ? -1 : 1);
}
$return = $connection->_db->selectCollection($table)->ensureIndex($columns, array(
"unique" => ($type == "UNIQUE"),
"name" => $name,
//! "sparse"
));
}
if ($return['errmsg']) {
$connection->error = $return['errmsg'];
return false;
}
}
return true;
}
function indexes($table, $connection2 = null) {
global $connection;
$return = array();
foreach ($connection->_db->selectCollection($table)->getIndexInfo() as $index) {
$descs = array();
foreach ($index["key"] as $column => $type) {
$descs[] = ($type == -1 ? '1' : null);
}
$return[$index["name"]] = array(
"type" => ($index["name"] == "_id_" ? "PRIMARY" : ($index["unique"] ? "UNIQUE" : "INDEX")),
"columns" => array_keys($index["key"]),
"lengths" => array(),
"descs" => $descs,
);
}
return $return;
function support($feature) {
return preg_match("~database|indexes~", $feature);
}
function fields($table) {
return fields_from_edit();
function db_collation($db, $collations) {
}
function information_schema() {
}
function is_view($table_status) {
}
function convert_field($field) {
@@ -270,12 +705,6 @@ if (isset($_GET["mongo"])) {
return array();
}
function found_rows($table_status, $where) {
global $connection;
//! don't call count_rows()
return $connection->_db->selectCollection($_GET["select"])->count($where);
}
function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
global $connection;
if ($table == "") {
@@ -306,51 +735,7 @@ if (isset($_GET["mongo"])) {
return true;
}
function alter_indexes($table, $alter) {
global $connection;
foreach ($alter as $val) {
list($type, $name, $set) = $val;
if ($set == "DROP") {
$return = $connection->_db->command(array("deleteIndexes" => $table, "index" => $name));
} else {
$columns = array();
foreach ($set as $column) {
$column = preg_replace('~ DESC$~', '', $column, 1, $count);
$columns[$column] = ($count ? -1 : 1);
}
$return = $connection->_db->selectCollection($table)->ensureIndex($columns, array(
"unique" => ($type == "UNIQUE"),
"name" => $name,
//! "sparse"
));
}
if ($return['errmsg']) {
$connection->error = $return['errmsg'];
return false;
}
}
return true;
}
function last_id() {
global $connection;
return $connection->last_id;
}
function table($idf) {
return $idf;
}
function idf_escape($idf) {
return $idf;
}
function support($feature) {
return preg_match("~database|indexes~", $feature);
}
$jush = "mongo";
$operators = array("=");
$functions = array();
$grouping = array();
$edit_functions = array(array("json"));

View File

@@ -314,7 +314,7 @@ if (isset($_GET["mssql"])) {
function db_collation($db, $collations) {
global $connection;
return $connection->result("SELECT collation_name FROM sys.databases WHERE name = " . q($db));
return $connection->result("SELECT collation_name FROM sys.databases WHERE name = " . q($db));
}
function engines() {
@@ -361,7 +361,7 @@ if (isset($_GET["mssql"])) {
function fields($table) {
$return = array();
foreach (get_rows("SELECT c.*, t.name type, d.definition [default]
foreach (get_rows("SELECT c.max_length, c.precision, c.scale, c.name, c.is_nullable, c.is_identity, c.collation_name, t.name type, CAST(d.definition as text) [default]
FROM sys.all_columns c
JOIN sys.all_objects o ON c.object_id = o.object_id
JOIN sys.types t ON c.user_type_id = t.user_type_id

View File

@@ -813,8 +813,9 @@ if (!defined("DRIVER")) {
function routine($name, $type) {
global $connection, $enum_length, $inout, $types;
$aliases = array("bool", "boolean", "integer", "double precision", "real", "dec", "numeric", "fixed", "national char", "national varchar");
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
$type_pattern = "((" . implode("|", array_merge(array_keys($types), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum_length)++)\\))?\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";
$pattern = "\\s*(" . ($type == "FUNCTION" ? "" : $inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : $inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
$create = $connection->result("SHOW CREATE $type " . idf_escape($name), 2);
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match);
$fields = array();
@@ -915,9 +916,10 @@ if (!defined("DRIVER")) {
/** Get SQL command to create table
* @param string
* @param bool
* @param string
* @return string
*/
function create_sql($table, $auto_increment) {
function create_sql($table, $auto_increment, $style) {
global $connection;
$return = $connection->result("SHOW CREATE TABLE " . table($table), 1);
if (!$auto_increment) {
@@ -944,14 +946,12 @@ if (!defined("DRIVER")) {
/** Get SQL commands to create triggers
* @param string
* @param string
* @return string
*/
function trigger_sql($table, $style) {
function trigger_sql($table) {
$return = "";
foreach (get_rows("SHOW TRIGGERS LIKE " . q(addcslashes($table, "%_\\")), null, "-- ") as $row) {
$return .= "\n" . ($style == 'CREATE+ALTER' ? "DROP TRIGGER IF EXISTS " . idf_escape($row["Trigger"]) . ";;\n" : "")
. "CREATE TRIGGER " . idf_escape($row["Trigger"]) . " $row[Timing] $row[Event] ON " . table($row["Table"]) . " FOR EACH ROW\n$row[Statement];;\n";
$return .= "\nCREATE TRIGGER " . idf_escape($row["Trigger"]) . " $row[Timing] $row[Event] ON " . table($row["Table"]) . " FOR EACH ROW\n$row[Statement];;\n";
}
return $return;
}
@@ -982,6 +982,7 @@ if (!defined("DRIVER")) {
* @return string
*/
function convert_field($field) {
global $connection;
if (preg_match("~binary~", $field["type"])) {
return "HEX(" . idf_escape($field["field"]) . ")";
}
@@ -989,7 +990,7 @@ if (!defined("DRIVER")) {
return "BIN(" . idf_escape($field["field"]) . " + 0)"; // + 0 is required outside MySQLnd
}
if (preg_match("~geometry|point|linestring|polygon~", $field["type"])) {
return "AsWKT(" . idf_escape($field["field"]) . ")";
return ($connection->server_info >= 8 ? "ST_" : "") . "AsWKT(" . idf_escape($field["field"]) . ")";
}
}

View File

@@ -255,7 +255,7 @@ ORDER BY 1";
foreach (get_rows("SELECT c.relname AS \"Name\", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'm' THEN 'materialized view' ELSE 'view' END AS \"Engine\", pg_relation_size(c.oid) AS \"Data_length\", pg_total_relation_size(c.oid) - pg_relation_size(c.oid) AS \"Index_length\", obj_description(c.oid, 'pg_class') AS \"Comment\", c.relhasoids::int AS \"Oid\", c.reltuples as \"Rows\", n.nspname
FROM pg_class c
JOIN pg_namespace n ON(n.nspname = current_schema() AND n.oid = c.relnamespace)
WHERE relkind IN ('r', 'm', 'v')
WHERE relkind IN ('r', 'm', 'v', 'f')
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")
) as $row) { //! Index_length, Auto_increment
$return[$row["Name"]] = $row;
@@ -304,7 +304,7 @@ ORDER BY a.attnum"
$row["auto_increment"] = preg_match('~^nextval\\(~i', $row["default"]);
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1);
if (preg_match('~(.+)::[^)]+(.*)~', $row["default"], $match)) {
$row["default"] = ($match[1][0] == "'" ? idf_unescape($match[1]) : $match[1]) . $match[2];
$row["default"] = ($match[1] == "NULL" ? null : (($match[1][0] == "'" ? idf_unescape($match[1]) : $match[1]) . $match[2]));
}
$return[$row["field"]] = $row;
}
@@ -617,12 +617,7 @@ AND typelem = 0"
return $return;
}
/** Get SQL command to create table
* @param string
* @param bool
* @return string
*/
function create_sql($table, $auto_increment) {
function create_sql($table, $auto_increment, $style) {
global $connection;
$return = '';
$return_parts = array();
@@ -634,7 +629,6 @@ AND typelem = 0"
ksort($indexes);
$fkeys = foreign_keys($table);
ksort($fkeys);
$triggers = triggers($table);
if (!$status || empty($fields)) {
return false;
@@ -653,7 +647,8 @@ AND typelem = 0"
if (preg_match('~nextval\(\'([^\']+)\'\)~', $field['default'], $matches)) {
$sequence_name = $matches[1];
$sq = reset(get_rows("SELECT * FROM $sequence_name"));
$sequences[] = "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value] START " . ($auto_increment ? $sq['last_value'] : 1) . " CACHE $sq[cache_value];";
$sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE $sequence_name;\n" : "")
. "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value] START " . ($auto_increment ? $sq['last_value'] : 1) . " CACHE $sq[cache_value];";
}
}
@@ -695,30 +690,17 @@ AND typelem = 0"
}
}
// triggers
foreach ($triggers as $trg_id => $trg) {
$trigger = trigger($trg_id, $status['Name']);
$return .= "\n\nCREATE TRIGGER " . idf_escape($trigger['Trigger']) . " $trigger[Timing] $trigger[Events] ON " . idf_escape($status["nspname"]) . "." . idf_escape($status['Name']) . " $trigger[Type] $trigger[Statement];";
}
return rtrim($return, ';');
}
/** Get SQL commands to create triggers
* @param string
* @param string
* @return string
*/
//@TODO
function trigger_sql($table, $style) {
function trigger_sql($table) {
$status = table_status($table);
$return = "";
//foreach (get_rows("SHOW TRIGGERS LIKE " . q(addcslashes($table, "%_\\")), null, "-- ") as $row) {
// $return .= "\n" . ($style == 'CREATE+ALTER' ? "DROP TRIGGER IF EXISTS " . idf_escape($row["Trigger"]) . ";;\n" : "")
// . "CREATE TRIGGER " . idf_escape($row["Trigger"]) . " $row[Timing] $row[Event] ON " . table($row["Table"]) . " FOR EACH ROW\n$row[Statement];;\n";
//}
//return $return;
return false;
foreach (triggers($table) as $trg_id => $trg) {
$trigger = trigger($trg_id, $status['Name']);
$return .= "\nCREATE TRIGGER " . idf_escape($trigger['Trigger']) . " $trigger[Timing] $trigger[Events] ON " . idf_escape($status["nspname"]) . "." . idf_escape($status['Name']) . " $trigger[Type] $trigger[Statement];;\n";
}
return $return;
}

View File

@@ -184,11 +184,13 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
function __construct() {
parent::__construct(":memory:");
$this->query("PRAGMA foreign_keys = 1");
}
function select_db($filename) {
if (is_readable($filename) && $this->query("ATTACH " . $this->quote(preg_match("~(^[/\\\\]|:)~", $filename) ? $filename : dirname($_SERVER["SCRIPT_FILENAME"]) . "/$filename") . " AS a")) { // is_readable - SQLite 3
parent::__construct($filename);
$this->query("PRAGMA foreign_keys = 1");
return true;
}
return false;
@@ -713,7 +715,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
return true;
}
function create_sql($table, $auto_increment) {
function create_sql($table, $auto_increment, $style) {
global $connection;
$return = $connection->result("SELECT sql FROM sqlite_master WHERE type IN ('table', 'view') AND name = " . q($table));
foreach (indexes($table) as $name => $index) {
@@ -732,7 +734,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
function use_sql($database) {
}
function trigger_sql($table, $style) {
function trigger_sql($table) {
return implode(get_vals("SELECT sql || ';;\n' FROM sqlite_master WHERE type = 'trigger' AND tbl_name = " . q($table)));
}

View File

@@ -92,7 +92,7 @@ SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
$fields = fields($name);
$adminer->dumpData($name, $_POST["data_style"], "SELECT *" . convert_fields($fields, $fields) . " FROM " . table($name));
}
if ($is_sql && $_POST["triggers"] && $table && ($triggers = trigger_sql($name, $_POST["table_style"]))) {
if ($is_sql && $_POST["triggers"] && $table && ($triggers = trigger_sql($name))) {
echo "\nDELIMITER ;;\n$triggers\nDELIMITER ;\n";
}

View File

@@ -82,9 +82,13 @@ if ($_POST["save"]) {
}
if ($select) {
$result = $driver->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1));
$row = $result->fetch_assoc();
if (!$row) { // MySQLi returns null
$row = false;
if (!$result) {
$error = error();
} else {
$row = $result->fetch_assoc();
if (!$row) { // MySQLi returns null
$row = false;
}
}
if (isset($_GET["select"]) && (!$row || $result->fetch_assoc())) { // $result->num_rows != 1 isn't available in all drivers
$row = null;

View File

@@ -47,6 +47,6 @@ if (!$row && $EVENT != "") {
<p><?php textarea("EVENT_DEFINITION", $row["EVENT_DEFINITION"]); ?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($EVENT != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($EVENT != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $EVENT)); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -86,6 +86,6 @@ foreach ($row["source"] as $key => $val) {
<input type="submit" value="<?php echo lang('Save'); ?>">
<noscript><p><input type="submit" name="add" value="<?php echo lang('Add column'); ?>"></noscript>
<?php } ?>
<?php if ($name != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($name != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $name)); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -9,7 +9,7 @@ class Adminer {
* @return string HTML code
*/
function name() {
return "<a href='https://www.adminer.org/' target='_blank' id='h1'>Adminer</a>";
return "<a href='https://www.adminer.org/'" . target_blank() . " id='h1'>Adminer</a>";
}
/** Connection parameters
@@ -78,7 +78,7 @@ class Adminer {
}
/** Print HTML code inside <head>
* @return bool true to link adminer.css if exists
* @return bool true to link favicon.ico and adminer.css if exists
*/
function head() {
?>
@@ -87,6 +87,18 @@ class Adminer {
return true;
}
/** Get URLs of the CSS files
* @return array of strings
*/
function css() {
$return = array();
$filename = "adminer.css";
if (file_exists($filename)) {
$return[] = $filename;
}
return $return;
}
/** Print login form
* @return null
*/
@@ -114,7 +126,7 @@ class Adminer {
function login($login, $password) {
global $jush;
if ($jush == "sqlite") {
return lang('<a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to use SQLite.', '<code>login()</code>');
return lang('<a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to use SQLite.', target_blank(), '<code>login()</code>');
}
return true;
}
@@ -243,7 +255,7 @@ class Adminer {
* @return string
*/
function selectVal($val, $link, $field, $original) {
$return = ($val === null ? "<i>NULL</i>" : (preg_match("~char|binary~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "<code>$val</code>" : $val));
$return = ($val === null ? "<i>NULL</i>" : (preg_match("~char|binary|boolean~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "<code>$val</code>" : $val));
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && !is_utf8($val)) {
$return = "<i>" . lang('%d byte(s)', strlen($original)) . "</i>";
}
@@ -267,7 +279,7 @@ class Adminer {
* @return null
*/
function tableStructurePrint($fields) {
echo "<table cellspacing='0'>\n";
echo "<table cellspacing='0' class='nowrap'>\n";
echo "<thead><tr><th>" . lang('Column') . "<td>" . lang('Type') . (support("comment") ? "<td>" . lang('Comment') : "") . "</thead>\n";
foreach ($fields as $field) {
echo "<tr" . odd() . "><th>" . h($field["field"]);
@@ -317,7 +329,7 @@ class Adminer {
" name='columns[$i][col]'",
$columns,
$val["col"],
($key !== "" ? "selectFieldChange" : "selectAddRow")
($key !== "" ? "selectFieldChange" : "selectAddRow")
);
echo "<div>" . ($functions || $grouping ? "<select name='columns[$i][fun]'>"
. optionlist(array(-1 => "") + array_filter(array(lang('Functions') => $functions, lang('Aggregation') => $grouping)), $val["fun"]) . "</select>"
@@ -339,11 +351,11 @@ class Adminer {
print_fieldset("search", lang('Search'), $where);
foreach ($indexes as $i => $index) {
if ($index["type"] == "FULLTEXT") {
echo "(<i>" . implode("</i>, <i>", array_map('h', $index["columns"])) . "</i>) AGAINST";
echo "<div>(<i>" . implode("</i>, <i>", array_map('h', $index["columns"])) . "</i>) AGAINST";
echo " <input type='search' name='fulltext[$i]' value='" . h($_GET["fulltext"][$i]) . "'>";
echo script("qsl('input').oninput = selectFieldChange;", "");
echo checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL");
echo "<br>\n";
echo "</div>\n";
}
}
$_GET["where"] = (array) $_GET["where"];
@@ -495,6 +507,9 @@ class Adminer {
}
}
foreach ((array) $_GET["where"] as $val) {
if ($val["op"] == "") {
$val["op"] = "LIKE %%";
}
if ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators)) {
$cond = " $val[op]";
if (preg_match('~IN$~', $val["op"])) {
@@ -520,7 +535,7 @@ class Adminer {
&& (!preg_match("~[\x80-\xFF]~", $val["val"]) || $is_text)
) {
$name = idf_escape($name);
$cols[] = ($jush == "sql" && $is_text && !preg_match("~^utf8_~", $field["collation"]) ? "CONVERT($name USING " . charset($connection) . ")" : $name);
$cols[] = ($jush == "sql" && $is_text && !preg_match("~^utf8~", $field["collation"]) ? "CONVERT($name USING " . charset($connection) . ")" : $name);
}
}
$return[] = ($cols ? "(" . implode("$cond OR ", $cols) . "$cond)" : "0");
@@ -734,7 +749,7 @@ class Adminer {
}
$create = "CREATE TABLE " . table($table) . " (" . implode(", ", $fields) . ")";
} else {
$create = create_sql($table, $_POST["auto_increment"]);
$create = create_sql($table, $_POST["auto_increment"], $style);
}
set_utf8mb4($create);
if ($style && $create) {
@@ -847,6 +862,13 @@ class Adminer {
return $ext;
}
/** Set the path of the file for webserver load
* @return string path of the sql dump file
*/
function importServerPath() {
return "adminer.sql";
}
/** Print homepage
* @return bool whether to print default homepage
*/
@@ -867,7 +889,7 @@ class Adminer {
?>
<h1>
<?php echo $this->name(); ?> <span class="version"><?php echo $VERSION; ?></span>
<a href="https://www.adminer.org/#download" target="_blank" id="version"><?php echo (version_compare($VERSION, $_COOKIE["adminer_version"]) < 0 ? h($_COOKIE["adminer_version"]) : ""); ?></a>
<a href="https://www.adminer.org/#download"<?php echo target_blank(); ?> id="version"><?php echo (version_compare($VERSION, $_COOKIE["adminer_version"]) < 0 ? h($_COOKIE["adminer_version"]) : ""); ?></a>
</h1>
<?php
if ($missing == "auth") {

View File

@@ -17,15 +17,10 @@ if ($_COOKIE["adminer_permanent"]) {
function add_invalid_login() {
global $adminer;
$filename = get_temp_dir() . "/adminer.invalid";
$fp = @fopen($filename, "r+"); // @ - may not exist
if (!$fp) { // c+ is available since PHP 5.2.6
$fp = @fopen($filename, "w"); // @ - may not be writable
if (!$fp) {
return;
}
$fp = file_open_lock(get_temp_dir() . "/adminer.invalid");
if (!$fp) {
return;
}
flock($fp, LOCK_EX);
$invalids = unserialize(stream_get_contents($fp));
$time = time();
if ($invalids) {
@@ -40,19 +35,14 @@ function add_invalid_login() {
$invalid = array($time + 30*60, 0); // active for 30 minutes
}
$invalid[1]++;
$serialized = serialize($invalids);
rewind($fp);
fwrite($fp, $serialized);
ftruncate($fp, strlen($serialized));
flock($fp, LOCK_UN);
fclose($fp);
file_write_unlock($fp, serialize($invalids));
}
function check_invalid_login() {
global $adminer;
$invalids = unserialize(@file_get_contents(get_temp_dir() . "/adminer.invalid")); // @ - may not exist
$invalid = $invalids[$adminer->bruteForceKey()];
$next_attempt = ($invalid[1] > 30 ? $invalid[0] - time() : 0); // allow 30 invalid attempts
$next_attempt = ($invalid[1] > 29 ? $invalid[0] - time() : 0); // allow 30 invalid attempts
if ($next_attempt > 0) { //! do the same with permanent login
auth_error(lang('Too many unsuccessful logins, try again in %d minute(s).', ceil($next_attempt / 60)));
}
@@ -134,7 +124,7 @@ function auth_error($error) {
$password = get_password();
if ($password !== null) {
if ($password === false) {
$error .= '<br>' . lang('Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.', '<code>permanentLogin()</code>');
$error .= '<br>' . lang('Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.', target_blank(), '<code>permanentLogin()</code>');
}
set_password(DRIVER, SERVER, $_GET["username"], null);
}
@@ -167,14 +157,13 @@ if (isset($_GET["username"])) {
}
list($host, $port) = explode(":", SERVER, 2);
if (is_numeric($port) && $port < 1024) {
auth_error('Connecting to privileged ports is not allowed.');
auth_error(lang('Connecting to privileged ports is not allowed.'));
}
check_invalid_login();
$connection = connect();
$driver = new Min_Driver($connection);
}
$driver = new Min_Driver($connection);
if (!is_object($connection) || ($login = $adminer->login($_GET["username"], get_password())) !== true) {
auth_error((is_string($connection) ? h($connection) : (is_string($login) ? $login : lang('Invalid credentials.'))));
}

View File

@@ -54,7 +54,7 @@ if (get_magic_quotes_runtime()) {
}
@set_time_limit(0); // @ - can be disabled
@ini_set("zend.ze1_compatibility_mode", false); // @ - deprecated
@ini_set("precision", 20); // @ - can be disabled
@ini_set("precision", 17); // @ - can be disabled, 17 - internal PHP precision
include "../adminer/include/lang.inc.php";
include "../adminer/lang/$LANG.inc.php";

View File

@@ -27,14 +27,35 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
<?php if ($adminer->head()) { ?>
<link rel="shortcut icon" type="image/x-icon" href="../adminer/static/favicon.ico">
<link rel="apple-touch-icon" href="../adminer/static/favicon.ico">
<?php if (file_exists("adminer.css")) { ?>
<link rel="stylesheet" type="text/css" href="adminer.css">
<?php foreach ($adminer->css() as $css) { ?>
<link rel="stylesheet" type="text/css" href="<?php echo h($css); ?>">
<?php } ?>
<?php } ?>
<body class="<?php echo lang('ltr'); ?> nojs">
<?php
$filename = get_temp_dir() . "/adminer.version";
if (!$_COOKIE["adminer_version"] && function_exists('openssl_verify') && file_exists($filename) && filemtime($filename) + 86400 > time()) { // 86400 - 1 day in seconds
$version = unserialize(file_get_contents($filename));
$public = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwqWOVuF5uw7/+Z70djoK
RlHIZFZPO0uYRezq90+7Amk+FDNd7KkL5eDve+vHRJBLAszF/7XKXe11xwliIsFs
DFWQlsABVZB3oisKCBEuI71J4kPH8dKGEWR9jDHFw3cWmoH3PmqImX6FISWbG3B8
h7FIx3jEaw5ckVPVTeo5JRm/1DZzJxjyDenXvBQ/6o9DgZKeNDgxwKzH+sw9/YCO
jHnq1cFpOIISzARlrHMa/43YfeNRAm/tsBXjSxembBPo7aQZLAWHmaj5+K19H10B
nCpz9Y++cipkVEiKRGih4ZEvjoFysEOdRLj6WiD/uUNky4xGeA6LaJqh5XpkFkcQ
fQIDAQAB
-----END PUBLIC KEY-----
";
if (openssl_verify($version["version"], base64_decode($version["signature"]), $public) == 1) {
$_COOKIE["adminer_version"] = $version["version"]; // doesn't need to send to the browser
}
}
?>
<script<?php echo nonce(); ?>>
mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick<?php echo (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '$VERSION')"); ?>});
mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick<?php
echo (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '$VERSION', '" . js_escape(ME) . "', '" . get_token() . "')"); // $token may be empty in auth.inc.php
?>});
document.body.className = document.body.className.replace(/ nojs/, ' js');
var offlineMessage = '<?php echo js_escape(lang('You are offline.')); ?>';
</script>
@@ -109,12 +130,11 @@ function page_headers() {
function csp() {
return array(
array(
"default-src" => "'none'",
"script-src" => "'self' 'unsafe-inline' 'nonce-" . get_nonce() . "' 'strict-dynamic'", // 'self' is a fallback for browsers not supporting 'strict-dynamic', 'unsafe-inline' is a fallback for browsers not supporting 'nonce-'
"style-src" => "'self' 'unsafe-inline'",
"connect-src" => "'self'",
"img-src" => "'self' data:",
"frame-src" => "https://www.adminer.org",
"object-src" => "'none'",
"base-uri" => "'none'",
"form-action" => "'self'",
),
);

View File

@@ -198,7 +198,7 @@ function process_field($field, $type_field) {
process_type($type_field),
($field["null"] ? " NULL" : " NOT NULL"), // NULL for timestamp
(isset($default) ? " DEFAULT " . (
(preg_match('~time~', $field["type"]) && preg_match('~^CURRENT_TIMESTAMP$~i', $default))
(preg_match('~time~', $field["type"]) && preg_match('~^CURRENT_TIMESTAMP(\(\))?$~i', $default))
|| ($jush == "sqlite" && preg_match('~^CURRENT_(TIME|TIMESTAMP|DATE)$~i', $default))
|| ($field["type"] == "bit" && preg_match("~^([0-9]+|b'[0-1]+')\$~", $default))
|| ($jush == "pgsql" && preg_match("~^[a-z]+\\(('[^']*')+\\)\$~", $default))
@@ -498,7 +498,7 @@ function doc_link($paths) {
'mssql' => "https://msdn.microsoft.com/library/",
'oracle' => "https://download.oracle.com/docs/cd/B19306_01/server.102/b14200/",
);
return ($paths[$jush] ? "<a href='$urls[$jush]$paths[$jush]' target='_blank' rel='noreferrer'><sup>?</sup></a>" : "");
return ($paths[$jush] ? "<a href='$urls[$jush]$paths[$jush]'" . target_blank() . "><sup>?</sup></a>" : "");
}
/** Wrap gzencode() for usage in ob_start()
@@ -531,7 +531,7 @@ function db_size($db) {
* @return null
*/
function set_utf8mb4($create) {
global $connection;
global $connection;
static $set = false;
if (!$set && preg_match('~\butf8mb4~i', $create)) { // possible false positive
$set = true;

View File

@@ -38,7 +38,7 @@ function escape_string($val) {
* @return string
*/
function number($val) {
return preg_replace('~[^0-9]+~', '', $val);
return preg_replace('~[^0-9]+~', '', $val);
}
/** Disable magic_quotes_gpc
@@ -105,6 +105,13 @@ function nonce() {
return ' nonce="' . get_nonce() . '"';
}
/** Get a target="_blank" attribute
* @return string
*/
function target_blank() {
return ' target="_blank" rel="noopener"';
}
/** Escape for HTML
* @param string
* @return string
@@ -214,10 +221,11 @@ function select_input($attrs, $options, $value = "", $onchange = "", $placeholde
/** Get onclick confirmation
* @param string
* @param string
* @return string
*/
function confirm($selector = "qsl('input')") {
return script("$selector.onclick = function () { return confirm('" . lang('Are you sure?') . "'); };", "");
function confirm($message = "", $selector = "qsl('input')") {
return script("$selector.onclick = function () { return confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "'); };", "");
}
/** Print header for hidden fieldset (close by </div></fieldset>)
@@ -360,9 +368,10 @@ function get_vals($query, $column = 0) {
* @param string
* @param Min_DB
* @param float
* @param bool
* @return array
*/
function get_key_vals($query, $connection2 = null, $timeout = 0) {
function get_key_vals($query, $connection2 = null, $timeout = 0, $set_keys = true) {
global $connection;
if (!is_object($connection2)) {
$connection2 = $connection;
@@ -373,7 +382,11 @@ function get_key_vals($query, $connection2 = null, $timeout = 0) {
$connection2->timeout = 0;
if (is_object($result)) {
while ($row = $result->fetch_row()) {
$return[$row[0]] = $row[1];
if ($set_keys) {
$return[$row[0]] = $row[1];
} else {
$return[] = $row[0];
}
}
}
return $return;
@@ -786,7 +799,7 @@ function shorten_utf8($string, $length = 80, $suffix = "") {
* @return string
*/
function format_number($val) {
return strtr(number_format($val, 0, ".", lang(',')), preg_split('~~u', lang('0123456789'), -1, PREG_SPLIT_NO_EMPTY));
return strtr(number_format($val, 0, ".", lang(',')), preg_split('~~u', lang('0123456789'), -1, PREG_SPLIT_NO_EMPTY));
}
/** Generate friendly URL
@@ -910,7 +923,7 @@ function input($field, $value, $function) {
echo $input;
} elseif (preg_match('~bool~', $field["type"])) {
echo "<input type='hidden'$attrs value='0'>" .
"<input type='checkbox'" . (in_array(strtolower($value), array('1', 't', 'true', 'y', 'yes', 'on')) ? " checked='checked'" : "") . "$attrs value='1'>";
"<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>";
} elseif ($field["type"] == "set") { //! 64 bits
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
foreach ($matches[1] as $i => $val) {
@@ -1119,6 +1132,35 @@ function get_temp_dir() {
return $return;
}
/** Open and exclusively lock a file
* @param string
* @return resource or null for error
*/
function file_open_lock($filename) {
$fp = @fopen($filename, "r+"); // @ - may not exist
if (!$fp) { // c+ is available since PHP 5.2.6
$fp = @fopen($filename, "w"); // @ - may not be writable
if (!$fp) {
return;
}
chmod($filename, 0660);
}
flock($fp, LOCK_EX);
return $fp;
}
/** Write and unlock a file
* @param resource
* @param string
*/
function file_write_unlock($fp, $data) {
rewind($fp);
fwrite($fp, $data);
ftruncate($fp, strlen($data));
flock($fp, LOCK_UN);
fclose($fp);
}
/** Read password from file adminer.key in temporary directory or create one
* @param bool
* @return string or false if the file can not be created
@@ -1252,7 +1294,7 @@ function slow_query($query) {
<script<?php echo nonce(); ?>>
var timeout = setTimeout(function () {
ajax('<?php echo js_escape(ME); ?>script=kill', function () {
}, 'token=<?php echo $token; ?>&kill=<?php echo $kill; ?>');
}, 'kill=<?php echo $kill; ?>&token=<?php echo $token; ?>');
}, <?php echo 1000 * $timeout; ?>);
</script>
<?php
@@ -1261,13 +1303,13 @@ var timeout = setTimeout(function () {
}
ob_flush();
flush();
$return = @get_key_vals($query, $connection2, $timeout); // @ - may be killed
$return = @get_key_vals($query, $connection2, $timeout, false); // @ - may be killed
if ($connection2) {
echo script("clearTimeout(timeout);");
ob_flush();
flush();
}
return array_keys($return);
return $return;
}
/** Generate BREACH resistant CSRF token

View File

@@ -36,7 +36,7 @@ $langs = array(
'sl' => 'Slovenski', // Matej Ferlan - www.itdinamik.com, matej.ferlan@itdinamik.com
'sr' => 'Српски', // Nikola Radovanović - cobisimo@gmail.com
'ta' => 'த‌மிழ்', // G. Sampath Kumar, Chennai, India, sampathkumar11@gmail.com
'th' => 'ภาษาไทย', // Panya Saraphi, elect.tu@gmail.com - http://www.opencart2u.com/
'th' => 'ภาษาไทย', // Panya Saraphi, elect.tu@gmail.com - http://www.opencart2u.com/
'tr' => 'Türkçe', // Bilgehan Korkmaz - turktron.com
'uk' => 'Українська', // Valerii Kryzhov
'vi' => 'Tiếng Việt', // Giang Manh @ manhgd google mail

View File

@@ -1,2 +1,2 @@
<?php
$VERSION = "4.4.0";
$VERSION = "4.5.0";

View File

@@ -2,10 +2,18 @@
$TABLE = $_GET["indexes"];
$index_types = array("PRIMARY", "UNIQUE", "INDEX");
$table_status = table_status($TABLE, true);
if (preg_match('~MyISAM|M?aria' . ($connection->server_info >= 5.6 ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
$server_info = $connection->server_info;
$fulltext = ($server_info >= 5.6);
$spatial = ($server_info >= 5.7);
if (preg_match('~([\d.]+)-MariaDB~', $server_info, $match)) {
$server_info = $match[1];
$fulltext = (version_compare($server_info, '10.0.5') >= 0);
$spatial = (version_compare($server_info, '10.2.2') >= 0);
}
if (preg_match('~MyISAM|M?aria' . ($fulltext ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
$index_types[] = "FULLTEXT";
}
if (preg_match('~MyISAM|M?aria' . ($connection->server_info >= 5.7 ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
if (preg_match('~MyISAM|M?aria' . ($spatial ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
$index_types[] = "SPATIAL";
}
$indexes = indexes($TABLE);

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'Излизането е успешно.',
'Invalid credentials.' => 'Невалидни потребителски данни.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Прекалено много неуспешни опити за вход, опитайте пак след %d минута.', 'Прекалено много неуспешни опити за вход, опитайте пак след %d минути.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Главната парола вече е невалидна. <a href="https://www.adminer.org/en/extension/" target="_blank">Изберете</a> %s метод, за да я направите постоянна.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Главната парола вече е невалидна. <a href="https://www.adminer.org/en/extension/"%s>Изберете</a> %s метод, за да я направите постоянна.',
'Language' => 'Език',
'Invalid CSRF token. Send the form again.' => 'Невалиден шифроващ ключ. Попълнете и изпратете формуляра отново.',
'If you did not send this request from Adminer then close this page.' => 'Ако не сте изпратили тази заявка през Adminer, затворете тази страница.',

View File

@@ -12,9 +12,9 @@ $translations = array(
'Logout successful.' => 'Odhlášení proběhlo v pořádku.',
'Thanks for using Adminer, consider <a href="%s">donating</a>.' => 'Díky za použití Admineru, zvažte <a href="%s">příspěvek</a>.',
'Invalid credentials.' => 'Neplatné přihlašovací údaje.',
'<a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to use SQLite.' => 'Pro přihlášení k SQLite <a href="https://www.adminer.org/cs/extension/" target="_blank">implementujte</a> metodu %s.',
'<a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to use SQLite.' => 'Pro přihlášení k SQLite <a href="https://www.adminer.org/cs/extension/"%s>implementujte</a> metodu %s.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Příliš mnoho pokusů o přihlášení, zkuste to znovu za %d minutu.', 'Příliš mnoho pokusů o přihlášení, zkuste to znovu za %d minuty.', 'Příliš mnoho pokusů o přihlášení, zkuste to znovu za %d minut.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Platnost hlavního hesla vypršela. <a href="https://www.adminer.org/cs/extension/" target="_blank">Implementujte</a> metodu %s, aby platilo stále.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Platnost hlavního hesla vypršela. <a href="https://www.adminer.org/cs/extension/"%s>Implementujte</a> metodu %s, aby platilo stále.',
'Language' => 'Jazyk',
'Invalid CSRF token. Send the form again.' => 'Neplatný token CSRF. Odešlete formulář znovu.',
'If you did not send this request from Adminer then close this page.' => 'Pokud jste tento požadavek neposlali z Adminera, tak tuto stránku zavřete.',
@@ -171,6 +171,7 @@ $translations = array(
'Default value' => 'Výchozí hodnota',
'Default values' => 'Výchozí hodnoty',
'Drop' => 'Odstranit',
'Drop %s?' => 'Odstranit %s?',
'Are you sure?' => 'Opravdu?',
'Size' => 'Velikost',
'Compute' => 'Spočítat',

View File

@@ -10,7 +10,7 @@ $translations = array(
'Logged as: %s' => 'Logget ind som: %s',
'Logout successful.' => 'Log af vellykket.',
'Invalid credentials.' => 'Ugyldige log ind oplysninger.',
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Master-kodeordet er udløbet. <a href="https://www.adminer.org/en/extension/" target="_blank">Implementer</a> en metode for %s for at gøre det permanent.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Master-kodeordet er udløbet. <a href="https://www.adminer.org/en/extension/"%s>Implementer</a> en metode for %s for at gøre det permanent.',
'Language' => 'Sprog',
'Invalid CSRF token. Send the form again.' => 'Ugyldigt CSRF-token - Genindsend formen.',
'No extension' => 'Ingen udvidelse',

View File

@@ -283,6 +283,6 @@ $translations = array(
'Saving' => 'Speichere',
'yes' => 'ja',
'no' => 'nein',
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Das Master-Passwort ist abgelaufen. <a href="https://www.adminer.org/de/extension/" target="_blank">Implementieren</a> Sie die %s Methode, um es permanent zu machen.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Das Master-Passwort ist abgelaufen. <a href="https://www.adminer.org/de/extension/"%s>Implementieren</a> Sie die %s Methode, um es permanent zu machen.',
'%d / ' => '%d / ',
);

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'Αποσυνδεθήκατε με επιτυχία.',
'Invalid credentials.' => 'Εσφαλμένα Διαπιστευτήρια.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Επανηλημμένες ανεπιτυχείς προσπάθειες σύνδεσης, δοκιμάστε ξανά σε %s λεπτό.', 'Επανηλημμένες ανεπιτυχείς προσπάθειες σύνδεσης, δοκιμάστε ξανά σε %s λεπτά.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Έλειξε ο Κύριος Κωδικός. <a href="https://www.adminer.org/en/extension/" target="_blank">Ενεργοποιήστε</a> τη μέθοδο %s για να τον κάνετε μόνιμο.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Έλειξε ο Κύριος Κωδικός. <a href="https://www.adminer.org/en/extension/"%s>Ενεργοποιήστε</a> τη μέθοδο %s για να τον κάνετε μόνιμο.',
'Language' => 'Γλώσσα',
'Invalid CSRF token. Send the form again.' => 'Άκυρο κουπόνι CSRF. Στείλτε τη φόρμα ξανά.',
'If you did not send this request from Adminer then close this page.' => 'Αν δε στείλατε αυτό το αίτημα από το Adminer, τότε κλείστε αυτή τη σελίδα.',
@@ -149,7 +149,7 @@ $translations = array(
'Table' => 'Πίνακας',
'No tables.' => 'Χωρίς πίνακες.',
'Alter table' => 'Τροποποίηση πίνακα',
'Create table' => 'Δημιουργία πίνακα',
'Create table' => 'Δημιουργία πίνακα',
'Table has been dropped.' => 'Ο πίνακας διαγράφηκε.',
'Tables have been dropped.' => 'Οι πινακες διαγράφηκαν.',
'Tables have been optimized.' => 'Οι πίνακες βελτιστοποιήθηκαν.',

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'با موفقیت خارج شدید.',
'Invalid credentials.' => 'اعتبار سنجی نامعتبر.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('ورودهای ناموفق بیش از حد، %d دقیقه دیگر تلاش نمایید.', 'ورودهای ناموفق بیش از حد، %d دقیقه دیگر تلاش نمایید.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'رمز اصلی باطل شده است. روش %s را <a href="https://www.adminer.org/en/extension/" target="_blank">پیاده سازی</a> کرده تا آن را دائمی سازید.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'رمز اصلی باطل شده است. روش %s را <a href="https://www.adminer.org/en/extension/"%s>پیاده سازی</a> کرده تا آن را دائمی سازید.',
'Language' => 'زبان',
'Invalid CSRF token. Send the form again.' => 'CSRF token نامعتبر است. دوباره سعی کنید.',
'No extension' => 'پسوند نامعتبر',

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'Uloskirjautuminen onnistui.',
'Invalid credentials.' => 'Virheelliset kirjautumistiedot.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Liian monta epäonnistunutta sisäänkirjautumisyritystä, kokeile uudestaan %d minuutin kuluttua.', 'Liian monta epäonnistunutta sisäänkirjautumisyritystä, kokeile uudestaan %d minuutin kuluttua.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Master-salasana ei ole enää voimassa. <a href="https://www.adminer.org/en/extension/" target="_blank">Toteuta</a> %s-metodi sen tekemiseksi pysyväksi.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Master-salasana ei ole enää voimassa. <a href="https://www.adminer.org/en/extension/"%s>Toteuta</a> %s-metodi sen tekemiseksi pysyväksi.',
'Language' => 'Kieli',
'Invalid CSRF token. Send the form again.' => 'Virheellinen CSRF-vastamerkki. Lähetä lomake uudelleen.',
'If you did not send this request from Adminer then close this page.' => 'Jollet lähettänyt tämä pyyntö Adminerista, sulje tämä sivu.',

View File

@@ -274,7 +274,7 @@ $translations = array(
'File must be in UTF-8 encoding.' => 'Les fichiers doivent être encodés en UTF-8.',
'Full table scan' => 'Scan de toute la table',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Trop de connexions échouées, essayez à nouveau dans %d minute.', 'Trop de connexions échouées, essayez à nouveau dans %d minutes.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Le mot de passe a expiré. <a href="https://www.adminer.org/en/extension/" target="_blank">Implémentez</a> la méthode %s afin de le rendre permanent.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Le mot de passe a expiré. <a href="https://www.adminer.org/en/extension/"%s>Implémentez</a> la méthode %s afin de le rendre permanent.',
'You can upload a big SQL file via FTP and import it from server.' => 'Vous pouvez uploader un gros fichier SQL par FTP et ensuite l\'importer depuis le serveur.',
'Size' => 'Taille',
'Compute' => 'Calcul',

View File

@@ -278,7 +278,7 @@ $translations = array(
'Default value' => 'Valor por defecto',
'Full table scan' => 'Escaneo completo da táboa',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Demasiados intentos de conexión, intentao de novo en %d minuto', 'Demasiados intentos de conexión, intentao de novo en %d minutos'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'O contrasinal principal caducou. <a href="https://www.adminer.org/en/extension/" target="_blank">Implementa</a> o método %s para facelo permanente.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'O contrasinal principal caducou. <a href="https://www.adminer.org/en/extension/"%s>Implementa</a> o método %s para facelo permanente.',
'If you did not send this request from Adminer then close this page.' => 'Se non enviaches esta petición dende o Adminer entón pecha esta páxina',
'You can upload a big SQL file via FTP and import it from server.' => 'Podes subir un ficheiro SQL de gran tamaño vía FTP e importalo dende o servidor',
'Size' => 'Tamaño',

View File

@@ -276,12 +276,12 @@ $translations = array(
'ATTACH queries are not supported.' => 'שאילתת ATTACH אינה נתמכת',
'%d / ' => '%d / ',
'Limit rows' => 'הגבל שורות',
'<a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to use SQLite.' => '<a href="https://www.adminer.org/en/extension/" target="_blank">התקן</a> את תוסף SQLite בשביל להתחבר',
'<a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to use SQLite.' => '<a href="https://www.adminer.org/en/extension/"%s>התקן</a> את תוסף SQLite בשביל להתחבר',
'Default value' => 'ערך ברירת מחדל',
'Full table scan' => 'סריקה טבלה מלאה',
'Too many unsuccessful logins, try again in %d minute(s).' => 'יותר מידי נסיונות כניסה נכשלו, אנא נסה עוד %d דקות',
'Thanks for using Adminer, consider <a href="%s">donating</a>.' => 'תודה שהשתמש ב-adminer אנא שקול <a href="%s">לתרום</a>.',
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'סיסמת המאסטר פגה <a href="https://www.adminer.org/en/extension/" target="_blank">התקן תוסף</a> על מנת להפוך את זה לתמידי',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'סיסמת המאסטר פגה <a href="https://www.adminer.org/en/extension/"%s>התקן תוסף</a> על מנת להפוך את זה לתמידי',
'If you did not send this request from Adminer then close this page.' => 'אם לא אתה שלחת בקשה ל-Adminer הינך יכול לסגור חלון זה',
'You can upload a big SQL file via FTP and import it from server.' => 'ניתן לעלות קבצים ב-FTP ואז למשוך אותם מהשרת',
'Size' => 'גודל',

View File

@@ -10,7 +10,7 @@ $translations = array(
'Logged as: %s' => 'Logget inn som: %s',
'Logout successful.' => 'Utlogging vellykket.',
'Invalid credentials.' => 'Ugylding innloggingsinformasjon.',
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Master-passord er utløpt. <a href="https://www.adminer.org/en/extension/" target="_blank">Implementer</a> en metode for %s for å gjøre det permanent.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Master-passord er utløpt. <a href="https://www.adminer.org/en/extension/"%s>Implementer</a> en metode for %s for å gjøre det permanent.',
'Language' => 'Språk',
'Invalid CSRF token. Send the form again.' => 'Ugylding CSRF-token - Send inn skjemaet igjen.',
'No extension' => 'Ingen utvidelse',

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'Wylogowano pomyślnie.',
'Invalid credentials.' => 'Nieprawidłowe dane logowania.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Za dużo nieudanych prób logowania, spróbuj ponownie za %d minutę.', 'Za dużo nieudanych prób logowania, spróbuj ponownie za %d minuty.', 'Za dużo nieudanych prób logowania, spróbuj ponownie za %d minut.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Ważność hasła głównego wygasła. <a href="https://www.adminer.org/pl/extension/" target="_blank">Zaimplementuj</a> własną metodę %s, aby ustawić je na stałe.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Ważność hasła głównego wygasła. <a href="https://www.adminer.org/pl/extension/"%s>Zaimplementuj</a> własną metodę %s, aby ustawić je na stałe.',
'Language' => 'Język',
'Invalid CSRF token. Send the form again.' => 'Nieprawidłowy token CSRF. Spróbuj wysłać formularz ponownie.',
'If you did not send this request from Adminer then close this page.' => 'Jeżeli nie wywołałeś tej strony z Adminera, zamknij to okno.',

View File

@@ -276,11 +276,11 @@ $translations = array(
'ATTACH queries are not supported.' => 'ATTACH-запросы не поддерживаются.',
'%d / ' => '%d / ',
'Limit rows' => 'Лимит строк',
'<a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to use SQLite.' => '<a href="https://www.adminer.org/en/extension/" target="_blank">Реализуйте</a> метод %s, чтобы использовать SQLite.',
'<a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to use SQLite.' => '<a href="https://www.adminer.org/en/extension/"%s>Реализуйте</a> метод %s, чтобы использовать SQLite.',
'Default value' => 'Значение по умолчанию',
'Full table scan' => 'Анализ полной таблицы',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Слишком много неудачных попыток входа. Попробуйте снова через %d минуту.', 'Слишком много неудачных попыток входа. Попробуйте снова через %d минуты.', 'Слишком много неудачных попыток входа. Попробуйте снова через %d минут.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Мастер-пароль истёк. <a href="https://www.adminer.org/en/extension/" target="_blank">Реализуйте</a> метод %s, чтобы сделать его постоянным.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Мастер-пароль истёк. <a href="https://www.adminer.org/en/extension/"%s>Реализуйте</a> метод %s, чтобы сделать его постоянным.',
'If you did not send this request from Adminer then close this page.' => 'Если вы не посылали этот запрос из Adminer, закройте эту страницу.',
'You can upload a big SQL file via FTP and import it from server.' => 'Вы можете закачать большой SQL-файл по FTP и затем импортировать его с сервера.',
'Size' => 'Размер',

View File

@@ -231,8 +231,8 @@ $translations = array(
'%d row(s)' => array('%d ред', '%d реда', '%d редова'),
'Page' => 'Страна',
'last' => 'последња',
'Loading' => 'Учитавам',
'Load more data' => 'Учитавам још података',
'Loading' => 'Учитавам',
'Load more data' => 'Учитавам још података',
'whole result' => 'цео резултат',
'%d byte(s)' => array('%d бајт', '%d бајта', '%d бајтова'),

View File

@@ -12,7 +12,7 @@ $translations = array(
'Logout successful.' => 'Đã thoát xong.',
'Invalid credentials.' => 'Tài khoản sai.',
'Too many unsuccessful logins, try again in %d minute(s).' => 'Bạn gõ sai tài khoản quá nhiều lần, hãy thử lại sau %d phút nữa.',
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Mật khẩu đã hết hạn. <a href="https://www.adminer.org/en/extension/" target="_blank">Thử cách làm</a> để giữ cố định.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Mật khẩu đã hết hạn. <a href="https://www.adminer.org/en/extension/"%s>Thử cách làm</a> để giữ cố định.',
'Language' => 'Ngôn ngữ',
'Invalid CSRF token. Send the form again.' => 'Mã kiểm tra CSRF sai, hãy nhập lại biểu mẫu.',
'No extension' => 'Không có phần mở rộng',

View File

@@ -12,14 +12,15 @@ $translations = array(
'Logout successful.' => 'Xx.',
'Thanks for using Adminer, consider <a href="%s">donating</a>.' => 'Xx.',
'Invalid credentials.' => 'Xx.',
'<a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to use SQLite.' => 'Xx.',
'<a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to use SQLite.' => 'Xx.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Xx.', 'Xx.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/" target="_blank">Implement</a> %s method to make it permanent.' => 'Xx.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Xx.',
'Language' => 'Xx',
'Invalid CSRF token. Send the form again.' => 'Xx.',
'If you did not send this request from Adminer then close this page.' => 'Xx.',
'No extension' => 'Xx',
'None of the supported PHP extensions (%s) are available.' => 'Xx.',
'Connecting to privileged ports is not allowed.' => 'Xx.',
'Session support must be enabled.' => 'Xx.',
'Session expired, please login again.' => 'Xx.',
'%s version: %s through PHP extension %s' => 'Xx',
@@ -171,7 +172,8 @@ $translations = array(
'Default value' => 'Xx',
'Default values' => 'Xx',
'Drop' => 'Xx',
'Are you sure?' => 'Xx',
'Drop %s?' => 'Xx?',
'Are you sure?' => 'Xx?',
'Size' => 'Xx',
'Compute' => 'Xx',
'Move up' => 'Xx',

View File

@@ -49,6 +49,6 @@ if (isset($_GET["function"])) {
<p><?php textarea("definition", $row["definition"]); ?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($PROCEDURE != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($PROCEDURE != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $PROCEDURE)); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -31,7 +31,7 @@ if (!$row) {
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php
if ($_GET["ns"] != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm() . "\n";
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', $_GET["ns"])) . "\n";
}
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">

View File

@@ -34,6 +34,12 @@ if ($_GET["script"] == "db") {
} elseif ($_GET["script"] == "kill") {
$connection->query("KILL " . number($_POST["kill"]));
} elseif ($_GET["script"] == "version") {
$fp = file_open_lock(get_temp_dir() . "/adminer.version");
if ($fp) {
file_write_unlock($fp, serialize(array("signature" => $_POST["signature"], "version" => $_POST["version"])));
}
} else { // connect
foreach (count_tables($adminer->databases()) as $db => $val) {
json_row("tables-$db", $val);

View File

@@ -364,9 +364,9 @@ if (!$columns && support("table")) {
}
$unique_idf = "";
foreach ($unique_array as $key => $val) {
if (($jush == "sql" || $jush == "pgsql") && strlen($val) > 64) {
if (($jush == "sql" || $jush == "pgsql") && preg_match('~char|text|enum|set~', $fields[$key]["type"]) && strlen($val) > 64) {
$key = (strpos($key, '(') ? $key : idf_escape($key)); //! columns looking like functions
$key = "MD5(" . ($jush == 'sql' && preg_match("~^utf8_~", $fields[$key]["collation"]) ? $key : "CONVERT($key USING " . charset($connection) . ")") . ")";
$key = "MD5(" . ($jush != 'sql' || preg_match("~^utf8~", $fields[$key]["collation"]) ? $key : "CONVERT($key USING " . charset($connection) . ")") . ")";
$val = md5($val);
}
$unique_idf .= "&" . ($val !== null ? urlencode("where[" . bracket_escape($key) . "]") . "=" . urlencode($val) : "null%5B%5D=" . urlencode($key));

View File

@@ -28,7 +28,7 @@ if (!$row) {
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php
if ($SEQUENCE != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm() . "\n";
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', $SEQUENCE)) . "\n";
}
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">

View File

@@ -21,9 +21,10 @@ if (!$error && $_POST) {
if (!isset($_GET["import"])) {
$query = $_POST["query"];
} elseif ($_POST["webfile"]) {
$fp = @fopen((file_exists("adminer.sql")
? "adminer.sql"
: "compress.zlib://adminer.sql.gz"
$sql_file_path = $adminer->importServerPath();
$fp = @fopen((file_exists($sql_file_path)
? $sql_file_path
: "compress.zlib://$sql_file_path.gz"
), "rb");
$query = ($fp ? fread($fp, 1e6) : false);
} else {
@@ -220,7 +221,7 @@ if (!isset($_GET["import"])) {
);
echo "</div></fieldset>\n";
echo "<fieldset><legend>" . lang('From server') . "</legend><div>";
echo lang('Webserver file %s', "<code>adminer.sql" . (extension_loaded("zlib") ? "[.gz]" : "") . "</code>");
echo lang('Webserver file %s', "<code>" . h($adminer->importServerPath()) . (extension_loaded("zlib") ? "[.gz]" : "") . "</code>");
echo ' <input type="submit" name="webfile" value="' . lang('Run file') . '">';
echo "</div></fieldset>\n";
echo "<p>";

View File

@@ -32,7 +32,7 @@ input.wayoff { left: -1000px; position: absolute; }
.version { color: #777; font-size: 67%; }
.js .hidden, .nojs .jsonly { display: none; }
.js .column { position: absolute; background: #ddf; padding: .27em 1ex .3em 0; margin-top: -.27em; }
.nowrap td, .nowrap th, td.nowrap { white-space: pre; }
.nowrap td, .nowrap th, td.nowrap, p.nowrap { white-space: pre; }
.wrap td { white-space: normal; }
.error { color: red; background: #fee; }
.error b { background: #fff; font-weight: normal; }

View File

@@ -5,7 +5,7 @@
*/
function bodyLoad(version) {
if (window.jush) {
jush.create_links = ' target="_blank" rel="noreferrer"';
jush.create_links = ' target="_blank" rel="noopener"';
if (version) {
for (var key in jush.urls) {
var obj = jush.urls;

View File

@@ -95,8 +95,10 @@ function cookie(assign, days) {
/** Verify current Adminer version
* @param string
* @param string own URL base
* @param string
*/
function verifyVersion(current) {
function verifyVersion(current, url, token) {
cookie('adminer_version=0', 1);
var iframe = document.createElement('iframe');
iframe.src = 'https://www.adminer.org/version/?current=' + current;
@@ -112,6 +114,8 @@ function verifyVersion(current) {
var match = /version=(.+)/.exec(event.data);
if (match) {
cookie('adminer_version=' + match[1], 1);
ajax(url + 'script=version', function () {
}, event.data + '&token=' + token);
}
}
}, false);
@@ -437,7 +441,7 @@ function selectSearch(name) {
div.firstChild.value = name;
div.firstChild.onchange();
}
div.lastChild.focus();
qs('[name$="[val]"]', div).focus();
return false;
}
@@ -823,6 +827,9 @@ function findDefaultSubmit(el) {
if (el.jushTextarea) {
el = el.jushTextarea;
}
if (!el.form) {
return null;
}
var inputs = qsa('input', el.form);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];

View File

@@ -11,7 +11,7 @@ page_header(($fields && is_view($table_status) ? $table_status['Engine'] == 'mat
$adminer->selectLinks($table_status);
$comment = $table_status["Comment"];
if ($comment != "") {
echo "<p>" . lang('Comment') . ": " . h($comment) . "\n";
echo "<p class='nowrap'>" . lang('Comment') . ": " . h($comment) . "\n";
}
if ($fields) {

View File

@@ -44,6 +44,6 @@ page_header(($name != "" ? lang('Alter trigger') . ": " . h($name) : lang('Creat
<p><?php textarea("Statement", $row["Statement"]); ?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($name != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($name != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $name)); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -22,7 +22,7 @@ if (!$row) {
<p>
<?php
if ($TYPE != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm() . "\n";
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', $TYPE)) . "\n";
} else {
echo "<input name='name' value='" . h($row['name']) . "' autocapitalize='off'>\n";
textarea("as", $row["as"]);

View File

@@ -184,6 +184,6 @@ echo "</table>\n";
?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if (isset($_GET["host"])) { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if (isset($_GET["host"])) { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', "$USER@$_GET[host]")); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -53,6 +53,6 @@ page_header(($TABLE != "" ? lang('Alter view') : lang('Create view')), $error, a
<p><?php textarea("select", $row["select"]); ?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($_GET["view"] != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(); ?><?php } ?>
<?php if ($TABLE != "") { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $TABLE)); ?><?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -1,3 +1,27 @@
Adminer 4.5.0 (released 2018-01-24):
Display name of the object in confirmation when dropping it
Display newlines in column comments (bug #573)
Support current_timestamp() as default of time fields (bug #572)
Hide window.opener from pages opened in a new window (bug #561)
Display error when getting row to edit
Store current Adminer version server-side to avoid excessive requests
Adminer: Fix Search data in tables (regression from 4.4.0)
CSP: Allow any styles, images, media and fonts, disallow base-uri
MySQL: Support geometry in MySQL 8 (bug #574)
MySQL: Support routines with comments in parameters (bug #460)
MariaDB: Support fulltext and spatial indexes in InnoDB (bug #583)
SQLite: Enable foreign key checks
PostgreSQL: Respect NULL default value
PostgreSQL: Display foreign tables (bug #576)
PostgreSQL: Do not export triggers if not requested
PostgreSQL: Export DROP SEQUENCE if dropping table
PostgreSQL: Display boolean values as code (bug #562)
MS SQL: Support freetds
non-MySQL: Avoid CONVERT() (bug #509)
Elasticsearch: Insert, update, delete
MongoDB: Support mongodb PHP extension
Editor: Fix displaying of false values in PostgreSQL (bug #568)
Adminer 4.4.0 (released 2018-01-17):
Add Content Security Policy
Disallow scripts without nonce

View File

@@ -327,10 +327,12 @@ if ($_SERVER["argv"][1]) {
}
// check function definition in drivers
$filename = dirname(__FILE__) . "/adminer/drivers/mysql.inc.php";
preg_match_all('~\\bfunction ([^(]+)~', file_get_contents($filename), $matches); //! respect context (extension, class)
$file = file_get_contents(dirname(__FILE__) . "/adminer/drivers/mysql.inc.php");
$file = preg_replace('~( *)class Min_Driver.*}~sU', '', $file);
preg_match_all('~\\bfunction ([^(]+)~', $file, $matches); //! respect context (extension, class)
$functions = array_combine($matches[1], $matches[0]);
unset($functions["__destruct"], $functions["Min_DB"], $functions["Min_Result"], $functions["Min_Driver"]);
//! do not warn about functions without declared support()
unset($functions["__construct"], $functions["__destruct"], $functions["set_charset"]);
foreach (glob(dirname(__FILE__) . "/adminer/drivers/" . ($driver ? $driver : "*") . ".inc.php") as $filename) {
if ($filename != "mysql.inc.php") {
$file = file_get_contents($filename);
@@ -376,7 +378,7 @@ if ($driver) {
}
}
if (count($drivers) == 1) {
$file = str_replace('<?php echo html_select("auth[driver]", $drivers, DRIVER); ?>', "<input type='hidden' name='auth[driver]' value='" . ($driver == "mysql" ? "server" : $driver) . "'>" . reset($drivers), $file);
$file = str_replace('<?php echo html_select("auth[driver]", $drivers, DRIVER) . "\n"; ?>', "<input type='hidden' name='auth[driver]' value='" . ($driver == "mysql" ? "server" : $driver) . "'>" . reset($drivers), $file);
}
$file = preg_replace('(;../externals/jush/modules/jush-(?!textarea\.|txt\.|js\.|' . preg_quote($driver == "mysql" ? "sql" : $driver) . '\.)[^.]+.js)', '', $file);
}

View File

@@ -23,7 +23,7 @@
},
"license": [
"Apache-2.0",
"GPL-2.0"
"GPL-2.0-only"
],
"scripts": {
"compile": "php compile.php"

View File

@@ -73,9 +73,12 @@ input[type="button"] {
margin-right: 0;
color: #fff;
}
.error {
.error, .error b {
background: #ae1010;
}
.error b {
font-weight: bold;
}
.message {
background: #379f17;
}
@@ -259,7 +262,7 @@ th {
}
td:last-child,
th:last-child {
border-style: none;
border-right-style: none;
}
thead th,
thead td {

1189
designs/mancave/adminer.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ class Adminer {
var $_values = array();
function name() {
return "<a href='https://www.adminer.org/editor/' target='_blank' id='h1'>" . lang('Editor') . "</a>";
return "<a href='https://www.adminer.org/editor/'" . target_blank() . " id='h1'>" . lang('Editor') . "</a>";
}
//! driver, ns
@@ -55,6 +55,15 @@ class Adminer {
return true;
}
function css() {
$return = array();
$filename = "adminer.css";
if (file_exists($filename)) {
$return[] = $filename;
}
return $return;
}
function loginForm() {
?>
<table cellspacing="0">
@@ -183,7 +192,7 @@ ORDER BY ORDINAL_POSITION", null, "") as $row) { //! requires MySQL 5
}
}
if (like_bool($field) && $return != "&nbsp;") { // bool
$return = ($val ? lang('yes') : lang('no'));
$return = (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? lang('yes') : lang('no'));
}
if ($link) {
$return = "<a href='$link'" . (is_url($link) ? " rel='noreferrer'" : "") . ">$return</a>";
@@ -461,7 +470,7 @@ ORDER BY ORDINAL_POSITION", null, "") as $row) { //! requires MySQL 5
if ($options !== null) {
return (is_array($options)
? "<select$attrs>" . optionlist($options, $value, true) . "</select>"
: "<input value='" . h($value) . "'$attrs class='hidden'>"
: "<input value='" . h($value) . "'$attrs class='hidden'>"
. "<input value='" . h($options) . "' class='jsonly'>"
. "<div></div>"
. script("qsl('input').oninput = partial(whisper, '" . ME . "script=complete&source=" . urlencode($table) . "&field=" . urlencode($field["field"]) . "&value=');
@@ -549,6 +558,9 @@ qsl('div').onclick = whisperClick;", "")
return $ext;
}
function importServerPath() {
}
function homepage() {
return true;
}
@@ -558,7 +570,7 @@ qsl('div').onclick = whisperClick;", "")
?>
<h1>
<?php echo $this->name(); ?> <span class="version"><?php echo $VERSION; ?></span>
<a href="https://www.adminer.org/editor/#download" target="_blank" id="version"><?php echo (version_compare($VERSION, $_COOKIE["adminer_version"]) < 0 ? h($_COOKIE["adminer_version"]) : ""); ?></a>
<a href="https://www.adminer.org/editor/#download"<?php echo target_blank(); ?> id="version"><?php echo (version_compare($VERSION, $_COOKIE["adminer_version"]) < 0 ? h($_COOKIE["adminer_version"]) : ""); ?></a>
</h1>
<?php
if ($missing == "auth") {
@@ -592,17 +604,19 @@ qsl('div').onclick = whisperClick;", "")
}
function tablesPrint($tables) {
echo "<p id='tables'>";
echo "<ul id='tables'>";
echo script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $row) {
echo '<li>';
$name = $this->tableName($row);
if (isset($row["Engine"]) && $name != "") { // ignore views and tables without name
echo "<a href='" . h(ME) . 'select=' . urlencode($row["Name"]) . "'"
. bold($_GET["select"] == $row["Name"] || $_GET["edit"] == $row["Name"], "select")
. " title='" . lang('Select data') . "'>$name</a><br>\n"
. " title='" . lang('Select data') . "'>$name</a>\n"
;
}
}
echo "</ul>\n";
}
function _foreignColumn($foreignKeys, $column) {

43
plugins/designs.php Normal file
View File

@@ -0,0 +1,43 @@
<?php
/** Allow switching designs
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerDesigns {
/** @access protected */
var $designs;
/**
* @param array URL in key, name in value
*/
function __construct($designs) {
$this->designs = $designs;
}
function headers() {
if (isset($_POST["design"]) && verify_token()) {
restart_session();
$_SESSION["design"] = $_POST["design"];
redirect($_SERVER["REQUEST_URI"]);
}
}
function css() {
$return = array();
if (array_key_exists($_SESSION["design"], $this->designs)) {
$return[] = $_SESSION["design"];
}
return $return;
}
function navigation($missing) {
echo "<form action='' method='post' style='position: fixed; bottom: .5em; right: .5em;'>";
echo html_select("design", array("" => "(design)") + $this->designs, $_SESSION["design"], "this.form.submit();");
echo '<input type="hidden" name="token" value="' . get_token() . '">';
echo "</form>\n";
}
}

View File

@@ -39,7 +39,7 @@ class AdminerLoginServers {
?>
<table cellspacing="0">
<tr><th><?php echo lang('Server'); ?><td><input type="hidden" name="auth[driver]" value="<?php echo $this->driver; ?>"><select name="auth[server]"><?php echo optionlist($this->servers, SERVER); ?></select>
<tr><th><?php echo lang('Username'); ?><td><input id="username" name="auth[username]" value="<?php echo h($_GET["username"]); ?>">
<tr><th><?php echo lang('Username'); ?><td><input id="username" name="auth[username]" value="<?php echo h($_GET["username"]); ?>">
<tr><th><?php echo lang('Password'); ?><td><input type="password" name="auth[password]">
</table>
<p><input type="submit" value="<?php echo lang('Login'); ?>">

View File

@@ -137,6 +137,11 @@ class AdminerPlugin extends Adminer {
return $this->_applyPlugin(__FUNCTION__, $args);
}
function css() {
$args = func_get_args();
return $this->_applyPlugin(__FUNCTION__, $args);
}
function loginForm() {
$args = func_get_args();
return $this->_applyPlugin(__FUNCTION__, $args);
@@ -347,6 +352,11 @@ class AdminerPlugin extends Adminer {
return $this->_applyPlugin(__FUNCTION__, $args);
}
function importServerPath() {
$args = func_get_args();
return $this->_applyPlugin(__FUNCTION__, $args);
}
function homepage() {
$args = func_get_args();
return $this->_applyPlugin(__FUNCTION__, $args);

View File

@@ -13,7 +13,7 @@ class AdminerTableStructure {
* @return bool
*/
function tableStructurePrint($fields) {
echo "<table cellspacing='0'>\n";
echo "<table cellspacing='0' class='nowrap'>\n";
echo "<thead><tr><th>" . lang('Column') . "<th>" . lang('Type') . "<th>" . lang('Nullable') . "<th>" . lang('Default') . (support("comment") ? "<th>" . lang('Comment') : "") . "</thead>\n";
foreach ($fields as $field) {
echo "<tr" . odd() . "><th>" . h($field["field"]) . ($field["primary"] ? " (PRIMARY)" : "");

View File

@@ -8,22 +8,6 @@
*/
class AdminerTablesFilter {
function tablesPrint($tables) { ?>
<p class="jsonly"><input id="filter-field" autocomplete="off"><?php echo script("qs('#filter-field').oninput = tablesFilterInput;"); ?>
<ul id='tables'>
<?php
echo script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $table => $status) {
echo '<li data-table-name="' . h($table) . '"><a href="' . h(ME) . 'select=' . urlencode($table) . '"' . bold($_GET["select"] == $table || $_GET["edit"] == $table, "select") . ">" . lang('select') . "</a> ";
$name = h($status["Name"]);
echo (support("table") || support("indexes")
? '<a href="' . h(ME) . 'table=' . urlencode($table) . '"'
. bold(in_array($table, array($_GET["table"], $_GET["create"], $_GET["indexes"], $_GET["foreign"], $_GET["trigger"])), (is_view($status) ? "view" : "structure"))
. " title='" . lang('Show structure') . "'>$name</a>"
: "<span>$name</span>"
) . "\n";
}
?>
</ul>
<script<?php echo nonce(); ?>>
var tablesFilterTimeout = null;
var tablesFilterValue = '';
@@ -70,6 +54,22 @@ if (sessionStorage){
sessionStorage.setItem('adminer_tables_filter_db', db);
}
</script>
<p class="jsonly"><input id="filter-field" autocomplete="off"><?php echo script("qs('#filter-field').oninput = tablesFilterInput;"); ?>
<ul id='tables'>
<?php
echo script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $table => $status) {
echo '<li data-table-name="' . h($table) . '"><a href="' . h(ME) . 'select=' . urlencode($table) . '"' . bold($_GET["select"] == $table || $_GET["edit"] == $table, "select") . ">" . lang('select') . "</a> ";
$name = h($status["Name"]);
echo (support("table") || support("indexes")
? '<a href="' . h(ME) . 'table=' . urlencode($table) . '"'
. bold(in_array($table, array($_GET["table"], $_GET["create"], $_GET["indexes"], $_GET["foreign"], $_GET["trigger"])), (is_view($status) ? "view" : "structure"))
. " title='" . lang('Show structure') . "'>$name</a>"
: "<span>$name</span>"
) . "\n";
}
?>
</ul>
<?php
return true;
}

View File

@@ -12,6 +12,7 @@ editor/example.php - Example customization
plugins/readme.txt - Plugins for Adminer and Adminer Editor
adminer/plugin.php - Plugin demo
adminer/sqlite.php - Development version of Adminer with SQLite allowed
adminer/designs.php - Development version of Adminer with adminer.css switcher
compile.php - Create a single file version
lang.php - Update translations
tests/selenium.html - Selenium test suite

View File

@@ -118,7 +118,7 @@
</tr>
<tr>
<td>assertConfirmation</td>
<td>Are you sure?</td>
<td>Drop adminer_test@localhost?</td>
<td></td>
</tr>
<tr>

View File

@@ -63,7 +63,7 @@
</tr>
<tr>
<td>assertConfirmation</td>
<td>Are you sure?</td>
<td>Drop no_albums?</td>
<td></td>
</tr>
<tr>

View File

@@ -113,7 +113,7 @@
</tr>
<tr>
<td>assertConfirmation</td>
<td>Are you sure?</td>
<td>Drop insert_album?</td>
<td></td>
</tr>
<tr>