mirror of
https://github.com/vrana/adminer.git
synced 2026-03-26 14:20:54 +01:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5754b32693 | ||
|
|
11234ef939 | ||
|
|
e5e9b3a04d | ||
|
|
45c7082373 | ||
|
|
8fa7dedc4b | ||
|
|
f1c8f8532e | ||
|
|
00323425cb | ||
|
|
3d882578df | ||
|
|
318e304c7e | ||
|
|
50852b9036 | ||
|
|
c30f6227a2 | ||
|
|
a417a21ba6 | ||
|
|
fb1b76e411 | ||
|
|
38a937d966 | ||
|
|
f0142f0b8e | ||
|
|
cf1454f7a9 | ||
|
|
c0350d8893 | ||
|
|
1ac40e0c2b | ||
|
|
39b339b1b5 | ||
|
|
de4104f70d | ||
|
|
aeee1c1991 | ||
|
|
21d3a31503 | ||
|
|
e4ac9d611e | ||
|
|
bdc28bc959 | ||
|
|
83301e37da | ||
|
|
1b2354cdae | ||
|
|
a5faea641a | ||
|
|
9f4a51819e | ||
|
|
04473a21c8 | ||
|
|
9d1451e659 | ||
|
|
1879da5ad8 | ||
|
|
e471d8b0cf | ||
|
|
5b9f0186a8 | ||
|
|
da1ffdd34e | ||
|
|
56c18bcca0 | ||
|
|
948d57bb63 | ||
|
|
1a6735cac7 | ||
|
|
8e865cd650 | ||
|
|
864d831463 | ||
|
|
f61b085422 | ||
|
|
0ddb097cb6 | ||
|
|
2674250862 | ||
|
|
6f6f576c41 | ||
|
|
ecd9c74c99 | ||
|
|
2ea1f8a88c | ||
|
|
691edde5fc | ||
|
|
554e43aae4 | ||
|
|
503d83599c | ||
|
|
a7a704c818 | ||
|
|
d356091c2f | ||
|
|
a5ec07a77d | ||
|
|
68e8b5bf69 | ||
|
|
9c5215cf77 | ||
|
|
e6fe48516e | ||
|
|
58ff31a15d | ||
|
|
ca7c4d90e1 | ||
|
|
605ed2dcab | ||
|
|
76a8dbfb98 | ||
|
|
cca4d26784 | ||
|
|
80f9278d34 | ||
|
|
c1838743ed | ||
|
|
eb614963f8 | ||
|
|
1109ca6389 | ||
|
|
3856d0563e | ||
|
|
a49fcf4799 | ||
|
|
53d7e31bf6 | ||
|
|
15f4183fa6 | ||
|
|
e6be47941b | ||
|
|
240c8fc5e7 | ||
|
|
57c5370c67 | ||
|
|
489f78c21c | ||
|
|
6c8de72707 | ||
|
|
4d0e79234c | ||
|
|
c7ede7331e | ||
|
|
c5f3707bb9 | ||
|
|
1c008b7d71 | ||
|
|
c2be05f0e9 | ||
|
|
eaad45a781 | ||
|
|
9a5b8f1f92 | ||
|
|
1b43a6f034 | ||
|
|
fdc2326376 | ||
|
|
08f93d6d09 | ||
|
|
4fbe8ebf5a | ||
|
|
86285dcf34 | ||
|
|
26c4057946 | ||
|
|
d15d0b2ef3 | ||
|
|
bd1dffe086 | ||
|
|
4918ba407f | ||
|
|
46638ebd9a |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -3,6 +3,7 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
@@ -13,5 +14,6 @@ jobs:
|
||||
- uses: php-actions/composer@v6
|
||||
- uses: php-actions/phpcs@v1
|
||||
with:
|
||||
path: adminer/
|
||||
path: .
|
||||
standard: phpcs.xml
|
||||
- uses: php-actions/phpstan@v3
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -1,3 +1,51 @@
|
||||
## Adminer dev
|
||||
- Tables overview: allow sorting (bug #1231)
|
||||
- Select: Disable Ctrl+click inline edit without UPDATE privilege
|
||||
- Select: Display NULL in column title
|
||||
- Export: Remember unchecked objects (regression from 5.0.6)
|
||||
- Foreign key: Display new field in case of an error
|
||||
- PostgreSQL: Order NULL last
|
||||
- PostgreSQL: Display all SQL command warnings and only once
|
||||
- PostgreSQL: Export serial as serial, not nextval()
|
||||
- PostgreSQL: Export schema in nextval()
|
||||
- PostgreSQL: Export schema in REFERENCES
|
||||
- Editor: Display tinyint(1) as checkbox (bug #1246, regression from 5.4.2)
|
||||
- Croatian translation
|
||||
|
||||
## Adminer 5.4.2 (released 2026-02-08)
|
||||
- Avoid denial-of-service via version check (GHSA-q4f2-39gr-45jh, regression from 4.6.2)
|
||||
- Pretty print JSON in edit
|
||||
- Support multiline generated values in alter table
|
||||
- Link //domain.tld values
|
||||
- Improve print of nested tables
|
||||
- Hide sort links on unsortable columns
|
||||
- Display uneditable fields in edit form
|
||||
- Shorten all but numeric and date types in select
|
||||
- Fix escaping spaces in cookie value (bug #1208)
|
||||
- Don't quote comma in TSV export (bug #1238)
|
||||
- MariaDB: Don't display checks with the same name from another table (bug #1135)
|
||||
- PostgreSQL: Offer foreign keys in create table
|
||||
- PostgreSQL: Add missing parentheses to CHECK export
|
||||
- PostgreSQL: Allow creating NOT DEFERRABLE foreign keys
|
||||
- PostgreSQL: Remove duplicate DEFERRABLE in foreign key export
|
||||
- PostgreSQL: Add schema to sequence and view export
|
||||
- PostgreSQL: Fix definition of complex generated columns
|
||||
- PostgreSQL: Mark unique partial indexes as unique (bug #1172)
|
||||
- PostgreSQL: Fix namespace in inheritance links (bug #1221)
|
||||
- non-PostgreSQL: Display NOT NULL checks (bug #1237)
|
||||
- ClickHouse: Fix offset (bug #1188)
|
||||
- ClickHouse: Fix list of tables (bug #1176)
|
||||
- Plugins: Methods showVariables() and showStatus() (bug #1157)
|
||||
- Plugins: Allow to be in any namespace
|
||||
- New plugin: IGDB driver
|
||||
|
||||
## Adminer 5.4.1 (released 2025-09-26)
|
||||
- SQL command: Unlink NULL primary keys
|
||||
- Do not quote 0 in CSV export
|
||||
- Warn about exceeded upload_max_filesize in imports
|
||||
- Prolong queries saved from SQL command to URL (bug #1154)
|
||||
- MySQL: Fix displaying routine definition (bug #1156, regression from 5.4.0)
|
||||
|
||||
## Adminer 5.4.0 (released 2025-09-08)
|
||||
- Allow specifying operator in search anywhere
|
||||
- Do not order descending in GROUP BY select
|
||||
@@ -25,7 +73,7 @@
|
||||
- PostgreSQL: Fix calling functions returing table
|
||||
- PostgreSQL: Don't treat user types containing 'file' as blobs (bug #1118)
|
||||
- PostgreSQL: Export DROP and CREATE DATABASE (bug #1140)
|
||||
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089)
|
||||
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089, regression from 5.3.0)
|
||||
- Elasticsearch: Support dropping aliases
|
||||
- Plugins: Methods afterConnect(), processList() and killProcess()
|
||||
- New plugin: Display row numbers in select (bug #1106)
|
||||
@@ -149,8 +197,8 @@
|
||||
- MariaDB: Fix creating and altering generated columns (bug #897)
|
||||
- PostgreSQL: Fix "where" and "order" privileges (bug #902, regression from 5.0.2)
|
||||
- SQLite: Fix creating table in compiled version (bug #901, regression from 5.0.0)
|
||||
- Elastic: Do not pass null values on insert (PR #892)
|
||||
- Elastic: Fix displaying sparse rows (PR #893)
|
||||
- Elasticsearch: Do not pass null values on insert (PR #892)
|
||||
- Elasticsearch: Fix displaying sparse rows (PR #893)
|
||||
- Plugins: Add method dumpFooter()
|
||||
|
||||
## Adminer 5.0.2 (released 2025-03-10)
|
||||
@@ -231,6 +279,7 @@
|
||||
- SQLite: Fix expressions in default values (bug SF-860)
|
||||
- MS SQL: Foreign keys in non-default schema (bug SF-833)
|
||||
- Oracle: Include tables granted by other user
|
||||
- Elasticsearch: Move to plugin
|
||||
- MongoDB: Execute commands against the selected DB
|
||||
|
||||
## Adminer 4.15.0
|
||||
|
||||
@@ -36,7 +36,7 @@ if (!$error && $_POST) {
|
||||
}
|
||||
}
|
||||
|
||||
$query = (isset($_GET["callf"]) ? "SELECT " : "CALL ") . ($routine["returns"]["type"] == "record" ? "* FROM " : "") . table($PROCEDURE) . "(" . implode(", ", $call) . ")";
|
||||
$query = (isset($_GET["callf"]) ? "SELECT " : "CALL ") . (idx($routine["returns"], "type") == "record" ? "* FROM " : "") . table($PROCEDURE) . "(" . implode(", ", $call) . ")";
|
||||
$start = microtime(true);
|
||||
$result = connection()->multi_query($query);
|
||||
$affected = connection()->affected_rows; // getting warnings overwrites this
|
||||
|
||||
@@ -38,7 +38,7 @@ if ($tables_views && !$error && !$_POST["search"]) {
|
||||
} elseif (JUSH != "sql") {
|
||||
$result = (JUSH == "sqlite"
|
||||
? queries("VACUUM")
|
||||
: apply_queries("VACUUM" . ($_POST["optimize"] ? "" : " ANALYZE"), $_POST["tables"])
|
||||
: apply_queries("VACUUM" . ($_POST["optimize"] ? " ANALYZE" : ""), $_POST["tables"])
|
||||
);
|
||||
$message = lang('Tables have been optimized.');
|
||||
} elseif (!$_POST["tables"]) {
|
||||
@@ -49,15 +49,16 @@ if ($tables_views && !$error && !$_POST["search"]) {
|
||||
}
|
||||
}
|
||||
|
||||
queries_redirect(substr(ME, 0, -1), $message, $result);
|
||||
queries_redirect($_SERVER["REQUEST_URI"], $message, $result);
|
||||
}
|
||||
|
||||
page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true);
|
||||
|
||||
if (adminer()->homepage()) {
|
||||
if ($_GET["ns"] !== "") {
|
||||
$order = $_GET["order"];
|
||||
echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n";
|
||||
$tables_list = tables_list();
|
||||
$tables_list = ($order ? table_status() : tables_list());
|
||||
if (!$tables_list) {
|
||||
echo "<p class='message'>" . lang('No tables.') . "\n";
|
||||
} else {
|
||||
@@ -79,78 +80,92 @@ if (adminer()->homepage()) {
|
||||
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
|
||||
echo '<thead><tr class="wrap">';
|
||||
echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", "");
|
||||
echo '<th>' . lang('Table');
|
||||
echo '<td>' . lang('Engine') . doc_link(array('sql' => 'storage-engines.html'));
|
||||
echo '<td>' . lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/'));
|
||||
echo '<td>' . lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286'));
|
||||
echo '<td>' . lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT'));
|
||||
echo '<td>' . lang('Data Free') . doc_link(array('sql' => 'show-table-status.html'));
|
||||
echo '<td>' . lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/'));
|
||||
echo '<td>' . lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286'));
|
||||
echo (support("comment") ? '<td>' . lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')) : '');
|
||||
echo '<th><a href="' . h(substr(ME, 0, -1)) . '">' . lang('Table') . '</a>';
|
||||
$columns = array("Engine" => array(lang('Engine') . doc_link(array('sql' => 'storage-engines.html'))));
|
||||
if (collations()) {
|
||||
$columns["Collation"] = array(lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/')));
|
||||
}
|
||||
if (function_exists('Adminer\alter_table')) {
|
||||
$columns["Data_length"] = array(lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286')), "create", lang('Alter table'));
|
||||
}
|
||||
if (support('indexes')) {
|
||||
$columns["Index_length"] = array(lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')), "indexes", lang('Alter indexes'));
|
||||
}
|
||||
$columns["Data_free"] = array(lang('Data Free') . doc_link(array('sql' => 'show-table-status.html')), "edit", lang('New item'));
|
||||
if (function_exists('Adminer\alter_table')) {
|
||||
$columns["Auto_increment"] = array(lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/')), "auto_increment=1&create", lang('Alter table'));
|
||||
}
|
||||
$columns["Rows"] = array(lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286')), "select", lang('Select data'));
|
||||
if (support("comment")) {
|
||||
$columns["Comment"] = array(lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')));
|
||||
}
|
||||
foreach ($columns as $key => $column) {
|
||||
echo "<th><a href='" . h(ME) . "order=$key'>$column[0]</a>";
|
||||
}
|
||||
echo "</thead>\n";
|
||||
|
||||
if ($order) {
|
||||
uasort($tables_list, function ($a, $b) use ($order) {
|
||||
$return = ($a[$order] < $b[$order] ? -1 : ($a[$order] > $b[$order] ? 1 : 0)); // <=> available since PHP 7.1
|
||||
return (in_array($order, array('Engine', 'Collation', 'Comment')) ? $return : -$return);
|
||||
});
|
||||
}
|
||||
|
||||
$tables = 0;
|
||||
foreach ($tables_list as $name => $type) {
|
||||
$view = ($type !== null && !preg_match('~table|sequence~i', $type));
|
||||
foreach ($tables_list as $name => $status) {
|
||||
$view = ($order ? is_view($status) : $status !== null && !preg_match('~table|sequence~i', $status));
|
||||
$status = ($order ? $status : array('Engine' => $status));
|
||||
$id = h("Table-" . $name);
|
||||
echo '<tr><td>' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array("$name", $tables_views, true), "", "", "", $id); // "$name" to check numeric table names
|
||||
echo '<th>' . (support("table") || support("indexes") ? "<a href='" . h(ME) . "table=" . urlencode($name) . "' title='" . lang('Show structure') . "' id='$id'>" . h($name) . '</a>' : h($name));
|
||||
if ($view && !preg_match('~materialized~i', $type)) {
|
||||
if ($view && !preg_match('~materialized~i', $status['Engine'])) {
|
||||
$title = lang('View');
|
||||
echo '<td colspan="6">' . (support("view") ? "<a href='" . h(ME) . "view=" . urlencode($name) . "' title='" . lang('Alter view') . "'>$title</a>" : $title);
|
||||
echo '<td colspan="' . (count($columns) - 2) . '">' . (support("view") ? "<a href='" . h(ME) . "view=" . urlencode($name) . "' title='" . lang('Alter view') . "'>$title</a>" : $title);
|
||||
echo '<td align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '" title="' . lang('Select data') . '">?</a>';
|
||||
echo '<td>' . h($status['Comment']);
|
||||
} else {
|
||||
foreach (
|
||||
array(
|
||||
"Engine" => array(),
|
||||
"Collation" => array(),
|
||||
"Data_length" => array("create", lang('Alter table')),
|
||||
"Index_length" => array("indexes", lang('Alter indexes')),
|
||||
"Data_free" => array("edit", lang('New item')),
|
||||
"Auto_increment" => array("auto_increment=1&create", lang('Alter table')),
|
||||
"Rows" => array("select", lang('Select data')),
|
||||
) as $key => $link
|
||||
) {
|
||||
foreach ($columns as $key => $column) {
|
||||
$id = " id='$key-" . h($name) . "'";
|
||||
echo ($link ? "<td align='right'>" . (support("table") || $key == "Rows" || (support("indexes") && $key != "Data_length")
|
||||
? "<a href='" . h(ME . "$link[0]=") . urlencode($name) . "'$id title='$link[1]'>?</a>"
|
||||
: "<span$id>?</span>"
|
||||
) : "<td id='$key-" . h($name) . "'>");
|
||||
$val = idx($status, $key, '?');
|
||||
echo ($column[1]
|
||||
? "<td align='right'><a href='" . h(ME . "$column[1]=") . urlencode($name) . "'$id title='$column[2]'>" . (is_numeric($val)
|
||||
? ($val < 0 ? '?' : ($key == "Rows" && $val && $status["Engine"] == (JUSH == "pgsql" ? "table" : "InnoDB") ? '~ ' : '') . format_number($val))
|
||||
: $val
|
||||
) . "</a>"
|
||||
: "<td id='$key-" . h($name) . "'>" . h($val)
|
||||
);
|
||||
}
|
||||
$tables++;
|
||||
}
|
||||
echo (support("comment") ? "<td id='Comment-" . h($name) . "'>" : "");
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "<tr><td><th>" . lang('%d in total', count($tables_list));
|
||||
echo "<td>" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : "");
|
||||
echo "<td>" . h(db_collation(DB, collations()));
|
||||
echo (collations() ? "<td>" . h(db_collation(DB, collations())) : '');
|
||||
foreach (array("Data_length", "Index_length", "Data_free") as $key) {
|
||||
echo "<td align='right' id='sum-$key'>";
|
||||
echo ($columns[$key] ? "<td align='right' id='sum-$key'>" : "");
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
echo "</table>\n";
|
||||
echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');");
|
||||
echo ($order ? '' : script("ajaxSetHtml('" . js_escape(ME) . "script=db');"));
|
||||
echo "</div>\n";
|
||||
if (!information_schema(DB)) {
|
||||
echo "<div class='footer'><div>\n";
|
||||
$vacuum = "<input type='submit' value='" . lang('Vacuum') . "'> " . on_help("'VACUUM'");
|
||||
$optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . on_help(JUSH == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM OPTIMIZE'");
|
||||
echo "<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>"
|
||||
. (JUSH == "sqlite" ? $vacuum . "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'PRAGMA integrity_check'")
|
||||
$optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . on_help(JUSH == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM ANALYZE'");
|
||||
$print = (JUSH == "sqlite" ? $vacuum . "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'PRAGMA integrity_check'")
|
||||
: (JUSH == "pgsql" ? $vacuum . $optimize
|
||||
: (JUSH == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> " . on_help("'ANALYZE TABLE'")
|
||||
. $optimize
|
||||
. "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'CHECK TABLE'")
|
||||
. "<input type='submit' name='repair' value='" . lang('Repair') . "'> " . on_help("'REPAIR TABLE'")
|
||||
: "")))
|
||||
. "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm()
|
||||
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n";
|
||||
. (function_exists('Adminer\truncate_tables') ? "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm() : "")
|
||||
. (function_exists('Adminer\drop_tables') ? "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() : "");
|
||||
echo ($print ? "<div class='footer'><div>\n<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>$print\n</div></fieldset>\n" : "");
|
||||
|
||||
$databases = (support("scheme") ? adminer()->schemas() : adminer()->databases());
|
||||
echo "</div></fieldset>\n";
|
||||
$script = "";
|
||||
if (count($databases) != 1 && JUSH != "sqlite") {
|
||||
echo "<fieldset><legend>" . lang('Move to other database') . " <span id='selected3'></span></legend><div>";
|
||||
@@ -173,7 +188,7 @@ if (adminer()->homepage()) {
|
||||
echo script("tableCheck();");
|
||||
}
|
||||
|
||||
echo "<p class='links'><a href='" . h(ME) . "create='>" . lang('Create table') . "</a>\n";
|
||||
echo (function_exists('Adminer\alter_table') ? "<p class='links'><a href='" . h(ME) . "create='>" . lang('Create table') . "</a>\n" : '');
|
||||
echo (support("view") ? "<a href='" . h(ME) . "view='>" . lang('Create view') . "</a>\n" : "");
|
||||
|
||||
if (support("routine")) {
|
||||
|
||||
@@ -893,36 +893,27 @@ if (!defined('Adminer\DRIVER')) {
|
||||
* @return Routine
|
||||
*/
|
||||
function routine(string $name, string $type): array {
|
||||
$aliases = array("bool", "boolean", "integer", "double precision", "real", "dec", "numeric", "fixed", "national char", "national varchar");
|
||||
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
|
||||
$enum = driver()->enumLength;
|
||||
$type_pattern = "((" . implode("|", array_merge(array_keys(driver()->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum)++)\\))?"
|
||||
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?(?:\\s*COLLATE\\s*['\"]?([^'\"\\s,]+)['\"]?)?"; //! store COLLATE
|
||||
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : driver()->inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
|
||||
$create = get_val("SHOW CREATE $type " . idf_escape($name), 2);
|
||||
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match);
|
||||
$fields = array();
|
||||
preg_match_all("~$pattern\\s*,?~is", $match[1], $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $param) {
|
||||
$fields[] = array(
|
||||
"field" => str_replace("``", "`", $param[2]) . $param[3],
|
||||
"type" => strtolower($param[5]),
|
||||
"length" => preg_replace_callback("~$enum~s", 'Adminer\normalize_enum', $param[6]),
|
||||
"unsigned" => strtolower(preg_replace('~\s+~', ' ', trim("$param[8] $param[7]"))),
|
||||
"null" => true,
|
||||
"full_type" => $param[4],
|
||||
"inout" => strtoupper($param[1]),
|
||||
"collation" => strtolower($param[9]),
|
||||
);
|
||||
$fields = get_rows("SELECT
|
||||
PARAMETER_NAME field,
|
||||
DATA_TYPE type,
|
||||
CHARACTER_MAXIMUM_LENGTH length,
|
||||
REGEXP_REPLACE(DTD_IDENTIFIER, '^[^ ]+ ', '') `unsigned`,
|
||||
1 `null`,
|
||||
DTD_IDENTIFIER full_type,
|
||||
PARAMETER_MODE `inout`,
|
||||
CHARACTER_SET_NAME collation
|
||||
FROM information_schema.PARAMETERS
|
||||
WHERE SPECIFIC_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$type' AND SPECIFIC_NAME = " . q($name) . "
|
||||
ORDER BY ORDINAL_POSITION");
|
||||
$return = connection()->query("SELECT ROUTINE_COMMENT comment, ROUTINE_DEFINITION definition, 'SQL' language
|
||||
FROM information_schema.ROUTINES
|
||||
WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$type' AND ROUTINE_NAME = " . q($name))->fetch_assoc();
|
||||
if ($fields && $fields[0]['field'] == '') {
|
||||
$return['returns'] = array_shift($fields);
|
||||
}
|
||||
return array(
|
||||
"fields" => $fields,
|
||||
"comment" => get_val("SELECT ROUTINE_COMMENT FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_NAME = " . q($name)),
|
||||
) + ($type != "FUNCTION" ? array("definition" => $match[11]) : array(
|
||||
"returns" => array("type" => $match[12], "length" => $match[13], "unsigned" => $match[15], "collation" => $match[16]),
|
||||
"definition" => $match[17],
|
||||
"language" => "SQL", // available in information_schema.ROUTINES.BODY_STYLE
|
||||
));
|
||||
$return['fields'] = $fields;
|
||||
/** @phpstan-var Routine */
|
||||
return $return;
|
||||
}
|
||||
|
||||
/** Get list of routines
|
||||
|
||||
@@ -88,7 +88,13 @@ if (isset($_GET["pgsql"])) {
|
||||
}
|
||||
|
||||
function warnings() {
|
||||
return h(pg_last_notice($this->link)); // second parameter is available since PHP 7.1.0
|
||||
if (PHP_VERSION_ID >= 70100) {
|
||||
$return = implode("\n", pg_last_notice($this->link, 2)); // 2 - PGSQL_NOTICE_ALL
|
||||
pg_last_notice($this->link, 3); // 3 - PGSQL_NOTICE_CLEAR
|
||||
} else {
|
||||
$return = pg_last_notice($this->link);
|
||||
}
|
||||
return nl_br(h($return));
|
||||
}
|
||||
|
||||
/** Copy from array into a table
|
||||
@@ -334,11 +340,11 @@ if (isset($_GET["pgsql"])) {
|
||||
}
|
||||
|
||||
function inheritsFrom(string $table): array {
|
||||
return get_vals("SELECT relname FROM pg_class JOIN pg_inherits ON inhparent = oid WHERE inhrelid = " . $this->tableOid($table) . " ORDER BY 1");
|
||||
return get_rows("SELECT relname AS table, nspname AS ns FROM pg_class JOIN pg_inherits ON inhparent = oid JOIN pg_namespace ON relnamespace = pg_namespace.oid WHERE inhrelid = " . $this->tableOid($table) . " ORDER BY 2, 1");
|
||||
}
|
||||
|
||||
function inheritedTables(string $table): array {
|
||||
return get_vals("SELECT relname FROM pg_inherits JOIN pg_class ON inhrelid = oid WHERE inhparent = " . $this->tableOid($table) . " ORDER BY 1");
|
||||
return get_rows("SELECT relname AS table, nspname AS ns FROM pg_inherits JOIN pg_class ON inhrelid = oid JOIN pg_namespace ON relnamespace = pg_namespace.oid WHERE inhparent = " . $this->tableOid($table) . " ORDER BY 2, 1");
|
||||
}
|
||||
|
||||
function partitionsInfo(string $table): array {
|
||||
@@ -487,17 +493,19 @@ AND relnamespace = " . driver()->nsOid . "
|
||||
format_type(a.atttypid, a.atttypmod) AS full_type,
|
||||
pg_get_expr(d.adbin, d.adrelid) AS default,
|
||||
a.attnotnull::int,
|
||||
i.indrelid AS primary,
|
||||
col_description(a.attrelid, a.attnum) AS comment" . (min_version(10) ? ",
|
||||
a.attidentity" . (min_version(12) ? ",
|
||||
a.attgenerated" : "") : "") . "
|
||||
FROM pg_attribute a
|
||||
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
||||
LEFT JOIN pg_index i ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) AND i.indisprimary
|
||||
WHERE a.attrelid = " . driver()->tableOid($table) . "
|
||||
AND NOT a.attisdropped
|
||||
AND a.attnum > 0
|
||||
ORDER BY a.attnum") as $row
|
||||
) {
|
||||
//! collation, primary
|
||||
//! collation
|
||||
preg_match('~([^([]+)(\((.*)\))?([a-z ]+)?((\[[0-9]*])*)$~', $row["full_type"], $match);
|
||||
list(, $type, $length, $row["length"], $addon, $array) = $match;
|
||||
$row["length"] .= $array;
|
||||
@@ -517,7 +525,7 @@ ORDER BY a.attnum") as $row
|
||||
$row["auto_increment"] = $row['attidentity'] || preg_match('~^nextval\(~i', $row["default"])
|
||||
|| preg_match('~^unique_rowid\(~', $row["default"]); // CockroachDB
|
||||
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1, "where" => 1, "order" => 1);
|
||||
if (preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
|
||||
if (!$row['generated'] && preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
|
||||
$row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]);
|
||||
}
|
||||
$return[$row["field"]] = $row;
|
||||
@@ -539,7 +547,7 @@ WHERE indrelid = $table_oid
|
||||
ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
|
||||
) {
|
||||
$relname = $row["relname"];
|
||||
$return[$relname]["type"] = ($row["partial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX")));
|
||||
$return[$relname]["type"] = ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX"));
|
||||
$return[$relname]["columns"] = array();
|
||||
$return[$relname]["descs"] = array();
|
||||
$return[$relname]["algorithm"] = $row["amname"];
|
||||
@@ -559,12 +567,13 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
|
||||
function foreign_keys($table) {
|
||||
$return = array();
|
||||
foreach (
|
||||
get_rows("SELECT conname, condeferrable::int AS deferrable, pg_get_constraintdef(oid) AS definition
|
||||
get_rows("SELECT conname, condeferrable::int AS deferrable, condeferred::int AS deferred, pg_get_constraintdef(oid) AS definition
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = " . driver()->tableOid($table) . "
|
||||
AND contype = 'f'::char
|
||||
ORDER BY conkey, conname") as $row
|
||||
) {
|
||||
$row['deferrable'] = ($row['deferrable'] ? '' : 'NOT ') . 'DEFERRABLE' . ($row['deferred'] ? ' INITIALLY DEFERRED' : '');
|
||||
if (preg_match('~FOREIGN KEY\s*\((.+)\)\s*REFERENCES (.+)\((.+)\)(.*)$~iA', $row['definition'], $match)) {
|
||||
$row['source'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[1])));
|
||||
if (preg_match('~^(("([^"]|"")+"|[^"]+)\.)?"?("([^"]|"")+"|[^"]+)$~', $match[2], $match2)) {
|
||||
@@ -906,11 +915,12 @@ AND typelem = 0"
|
||||
$return = "";
|
||||
|
||||
$status = table_status1($table);
|
||||
$ns = idf_escape($status['nspname']);
|
||||
$fkeys = foreign_keys($table);
|
||||
ksort($fkeys);
|
||||
|
||||
foreach ($fkeys as $fkey_name => $fkey) {
|
||||
$return .= "ALTER TABLE ONLY " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " $fkey[definition] " . ($fkey['deferrable'] ? 'DEFERRABLE' : 'NOT DEFERRABLE') . ";\n";
|
||||
$return .= "ALTER TABLE ONLY $ns." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " " . preg_replace('~( REFERENCES )([^(.]+\()~', "\\1$ns.\\2", $fkey["definition"]) . ";\n";
|
||||
}
|
||||
|
||||
return ($return ? "$return\n" : $return);
|
||||
@@ -921,9 +931,10 @@ AND typelem = 0"
|
||||
$sequences = array();
|
||||
|
||||
$status = table_status1($table);
|
||||
$ns = idf_escape($status['nspname']);
|
||||
if (is_view($status)) {
|
||||
$view = view($table);
|
||||
return rtrim("CREATE VIEW " . idf_escape($table) . " AS $view[select]", ";");
|
||||
return rtrim("CREATE VIEW $ns." . idf_escape($table) . " AS $view[select]", ";");
|
||||
}
|
||||
$fields = fields($table);
|
||||
|
||||
@@ -931,12 +942,17 @@ AND typelem = 0"
|
||||
return false;
|
||||
}
|
||||
|
||||
$return = "CREATE TABLE " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " (\n ";
|
||||
$return = "CREATE TABLE $ns." . idf_escape($status['Name']) . " (\n ";
|
||||
|
||||
// fields' definitions
|
||||
foreach ($fields as $field) {
|
||||
if ($field['default'] == "nextval('$status[Name]_$field[field]_seq')") {
|
||||
$field['default'] = null;
|
||||
$field['full_type'] = preg_replace('~int(eger)?~', 'serial', $field['full_type']);
|
||||
}
|
||||
|
||||
$part = idf_escape($field['field']) . ' ' . $field['full_type']
|
||||
. default_value($field)
|
||||
. preg_replace('~(nextval\(\')([^.\']+\')~', '\1' . str_replace("'", "''", $status['nspname']) . '.\2', default_value($field))
|
||||
. ($field['null'] ? "" : " NOT NULL");
|
||||
$return_parts[] = $part;
|
||||
|
||||
@@ -947,8 +963,8 @@ AND typelem = 0"
|
||||
? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q(idf_unescape($sequence_name))
|
||||
: "SELECT * FROM $sequence_name"
|
||||
), null, "-- "));
|
||||
$sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $sequence_name;\n" : "")
|
||||
. "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]"
|
||||
$sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $ns.$sequence_name;\n" : "")
|
||||
. "CREATE SEQUENCE $ns.$sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]"
|
||||
. ($auto_increment && $sq['last_value'] ? " START " . ($sq["last_value"] + 1) : "")
|
||||
. " CACHE $sq[cache_value];"
|
||||
;
|
||||
@@ -969,7 +985,7 @@ AND typelem = 0"
|
||||
}
|
||||
|
||||
foreach (driver()->checkConstraints($table) as $conname => $consrc) {
|
||||
$return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK $consrc";
|
||||
$return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK ($consrc)";
|
||||
}
|
||||
$return .= implode(",\n ", $return_parts) . "\n)";
|
||||
|
||||
@@ -984,12 +1000,12 @@ AND typelem = 0"
|
||||
|
||||
// comments for table & fields
|
||||
if ($status['Comment']) {
|
||||
$return .= "\n\nCOMMENT ON TABLE " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " IS " . q($status['Comment']) . ";";
|
||||
$return .= "\n\nCOMMENT ON TABLE $ns." . idf_escape($status['Name']) . " IS " . q($status['Comment']) . ";";
|
||||
}
|
||||
|
||||
foreach ($fields as $field_name => $field) {
|
||||
if ($field['comment']) {
|
||||
$return .= "\n\nCOMMENT ON COLUMN " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . "." . idf_escape($field_name) . " IS " . q($field['comment']) . ";";
|
||||
$return .= "\n\nCOMMENT ON COLUMN $ns." . idf_escape($status['Name']) . "." . idf_escape($field_name) . " IS " . q($field['comment']) . ";";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,14 @@ namespace Adminer;
|
||||
$TABLE = $_GET["dump"];
|
||||
|
||||
if ($_POST && !$error) {
|
||||
$default = array("auto_increment" => '');
|
||||
foreach (array("type", "routine", "event", "trigger") as $support) {
|
||||
if (support($support)) {
|
||||
$default[$support . "s"] = '';
|
||||
}
|
||||
}
|
||||
save_settings(
|
||||
array_intersect_key($_POST, array_flip(array("output", "format", "db_style", "types", "routines", "events", "table_style", "auto_increment", "triggers", "data_style"))),
|
||||
array_intersect_key($_POST + $default, array_flip(array("output", "format", "db_style", "table_style", "data_style")) + $default),
|
||||
"adminer_export"
|
||||
);
|
||||
$tables = array_flip((array) $_POST["tables"]) + array_flip((array) $_POST["data"]);
|
||||
@@ -153,10 +159,6 @@ $row = get_settings("adminer_export");
|
||||
if (!$row) {
|
||||
$row = array("output" => "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT");
|
||||
}
|
||||
if (!isset($row["events"])) { // backwards compatibility
|
||||
$row["routines"] = $row["events"] = ($_GET["dump"] == "");
|
||||
$row["triggers"] = $row["table_style"];
|
||||
}
|
||||
|
||||
echo "<tr><th>" . lang('Output') . "<td>" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n";
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ $where = (isset($_GET["select"])
|
||||
);
|
||||
$update = (isset($_GET["select"]) ? $_POST["edit"] : $where);
|
||||
foreach ($fields as $name => $field) {
|
||||
if (!isset($field["privileges"][$update ? "update" : "insert"]) || adminer()->fieldName($field) == "" || $field["generated"]) {
|
||||
if ((!$update && !isset($field["privileges"]["insert"])) || adminer()->fieldName($field) == "") {
|
||||
unset($fields[$name]);
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
|
||||
}
|
||||
|
||||
$row = null;
|
||||
if ($_POST["save"]) {
|
||||
$row = (array) $_POST["fields"];
|
||||
} elseif ($where) {
|
||||
if ($where) {
|
||||
$select = array();
|
||||
foreach ($fields as $name => $field) {
|
||||
if (isset($field["privileges"]["select"])) {
|
||||
@@ -113,4 +111,8 @@ if (!support("table") && !$fields) { // used by Mongo and SimpleDB
|
||||
}
|
||||
}
|
||||
|
||||
if ($_POST["save"]) {
|
||||
$row = (array) $_POST["fields"] + ($row ? $row : array());
|
||||
}
|
||||
|
||||
edit_form($TABLE, $fields, $row, $update, $error);
|
||||
|
||||
@@ -34,7 +34,8 @@ if ($_GET["file"] == "default.css") {
|
||||
../externals/jush/modules/jush-sqlite.js;
|
||||
../externals/jush/modules/jush-mssql.js;
|
||||
../externals/jush/modules/jush-oracle.js;
|
||||
../externals/jush/modules/jush-simpledb.js', 'minify_js'));
|
||||
../externals/jush/modules/jush-simpledb.js;
|
||||
../externals/jush/modules/jush-igdb.js', 'minify_js'));
|
||||
} elseif ($_GET["file"] == "logo.png") {
|
||||
header("Content-Type: image/png");
|
||||
echo compile_file('../adminer/static/logo.png');
|
||||
|
||||
@@ -39,10 +39,10 @@ page_header(lang('Foreign key'), $error, array("table" => $TABLE), h($TABLE));
|
||||
|
||||
if ($_POST) {
|
||||
ksort($row["source"]);
|
||||
if ($_POST["add"]) {
|
||||
$row["source"][] = "";
|
||||
} elseif ($_POST["change"] || $_POST["change-js"]) {
|
||||
if ($_POST["change"] || $_POST["change-js"]) {
|
||||
$row["target"] = array();
|
||||
} else {
|
||||
$row["source"][] = "";
|
||||
}
|
||||
} elseif ($name != "") {
|
||||
$foreign_keys = foreign_keys($TABLE);
|
||||
@@ -103,10 +103,11 @@ foreach ($row["source"] as $key => $val) {
|
||||
<p>
|
||||
<label><?php echo lang('ON DELETE'); ?>: <?php echo html_select("on_delete", array(-1 => "") + explode("|", driver()->onActions), $row["on_delete"]); ?></label>
|
||||
<label><?php echo lang('ON UPDATE'); ?>: <?php echo html_select("on_update", array(-1 => "") + explode("|", driver()->onActions), $row["on_update"]); ?></label>
|
||||
<?php echo (DRIVER === 'pgsql' ? html_select("deferrable", array('NOT DEFERRABLE', 'DEFERRABLE', 'DEFERRABLE INITIALLY DEFERRED'), $row["deferrable"]) . ' ' : ''); ?>
|
||||
<?php echo doc_link(array(
|
||||
'sql' => "innodb-foreign-key-constraints.html",
|
||||
'mariadb' => "foreign-keys/",
|
||||
'pgsql' => "sql-createtable.html#SQL-CREATETABLE-REFERENCES",
|
||||
'pgsql' => "sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES",
|
||||
'mssql' => "t-sql/statements/create-table-transact-sql",
|
||||
'oracle' => "SQLRF01111",
|
||||
)); ?>
|
||||
|
||||
@@ -140,7 +140,7 @@ class Adminer {
|
||||
echo "<table class='layout'>\n";
|
||||
// this is matched by compile.php
|
||||
echo adminer()->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);"));
|
||||
echo adminer()->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="hostname[:port]" placeholder="localhost" autocapitalize="off">');
|
||||
echo adminer()->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="' . lang('hostname[:port] or :socket') . '" placeholder="localhost" autocapitalize="off">');
|
||||
// this is matched by compile.php
|
||||
echo adminer()->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" autofocus value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("const authDriver = qs('#username').form['auth[driver]']; authDriver && authDriver.onchange();"));
|
||||
echo adminer()->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">');
|
||||
@@ -182,7 +182,7 @@ class Adminer {
|
||||
* @return string HTML code, "" to ignore field
|
||||
*/
|
||||
function fieldName(array $field, int $order = 0): string {
|
||||
$type = $field["full_type"];
|
||||
$type = $field["full_type"] . ($field["null"] ? " NULL" : "");
|
||||
$comment = $field["comment"];
|
||||
return '<span title="' . h($type . ($comment != "" ? ($type ? ": " : "") . $comment : '')) . '">' . h($field["field"]) . '</span>';
|
||||
}
|
||||
@@ -201,10 +201,12 @@ class Adminer {
|
||||
$is_view = false;
|
||||
if (support("table")) {
|
||||
$is_view = is_view($tableStatus);
|
||||
if (!$is_view) {
|
||||
if ($is_view) {
|
||||
if (support("view")) {
|
||||
$links["view"] = lang('Alter view');
|
||||
}
|
||||
} elseif (function_exists('Adminer\alter_table')) {
|
||||
$links["create"] = lang('Alter table');
|
||||
} elseif (support("view")) {
|
||||
$links["view"] = lang('Alter view');
|
||||
}
|
||||
}
|
||||
if ($set !== null) {
|
||||
@@ -286,7 +288,7 @@ class Adminer {
|
||||
|
||||
/** Get a link to use in select table
|
||||
* @param string $val raw value of the field
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
* @return string|void null to create the default link
|
||||
*/
|
||||
function selectLink(?string $val, array $field) {
|
||||
@@ -295,7 +297,7 @@ class Adminer {
|
||||
/** Value printed in select table
|
||||
* @param ?string $val HTML-escaped value to print
|
||||
* @param ?string $link link to foreign key
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
* @param string $original original value before applying editVal() and escaping
|
||||
*/
|
||||
function selectVal(?string $val, ?string $link, array $field, ?string $original): string {
|
||||
@@ -311,7 +313,7 @@ class Adminer {
|
||||
}
|
||||
|
||||
/** Value conversion used in select and edit
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
*/
|
||||
function editVal(?string $val, array $field): ?string {
|
||||
return $val;
|
||||
@@ -423,7 +425,7 @@ class Adminer {
|
||||
echo "<div>(<i>" . implode("</i>, <i>", array_map('Adminer\h', $index["columns"])) . "</i>) AGAINST";
|
||||
echo " <input type='search' name='fulltext[$i]' value='" . h(idx($_GET["fulltext"], $i)) . "'>";
|
||||
echo script("qsl('input').oninput = selectFieldChange;", "");
|
||||
echo checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL");
|
||||
echo (JUSH == 'sql' ? checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL") : '');
|
||||
echo "</div>\n";
|
||||
}
|
||||
}
|
||||
@@ -612,7 +614,7 @@ class Adminer {
|
||||
foreach ((array) $_GET["order"] as $key => $val) {
|
||||
if ($val != "") {
|
||||
$return[] = (preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~', $val) ? $val : idf_escape($val)) //! MS SQL uses []
|
||||
. (isset($_GET["desc"][$key]) ? " DESC" : "")
|
||||
. (isset($_GET["desc"][$key]) ? " DESC" . (JUSH == 'pgsql' && idx($fields[$val], "null") ? " NULLS LAST" : "") : "")
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -674,7 +676,7 @@ class Adminer {
|
||||
$return = "<a href='#$id' class='toggle'>" . lang('Warnings') . "</a>, $return<div id='$id' class='hidden'>\n$warnings</div>\n";
|
||||
}
|
||||
return " <span class='time'>" . @date("H:i:s") . "</span>" // @ - time zone may be not set
|
||||
. " $return<div id='$sql_id' class='hidden'><pre><code class='jush-" . JUSH . "'>" . shorten_utf8($query, 1000) . "</code></pre>"
|
||||
. " $return<div id='$sql_id' class='hidden'><pre><code class='jush-" . JUSH . "'>" . shorten_utf8($query, 1e4) . "</code></pre>"
|
||||
. ($time ? " <span class='time'>($time)</span>" : '')
|
||||
. (support("sql") ? '<p><a href="' . h(str_replace("db=" . urlencode(DB), "db=" . urlencode($_GET["db"]), ME) . 'sql=&history=' . (count($history[$_GET["db"]]) - 1)) . '">' . lang('Edit') . '</a>' : '')
|
||||
. '</div>'
|
||||
@@ -1020,7 +1022,7 @@ class Adminer {
|
||||
$actions[] = "<a href='" . h(ME) . "dump=" . urlencode(isset($_GET["table"]) ? $_GET["table"] : $_GET["select"]) . "' id='dump'" . bold(isset($_GET["dump"])) . ">" . lang('Export') . "</a>";
|
||||
}
|
||||
$in_db = $_GET["ns"] !== "" && !$missing && DB != "";
|
||||
if ($in_db) {
|
||||
if ($in_db && function_exists('Adminer\alter_table')) {
|
||||
$actions[] = '<a href="' . h(ME) . 'create="' . bold($_GET["create"] === "") . ">" . lang('Create table') . "</a>";
|
||||
}
|
||||
echo ($actions ? "<p class='links'>\n" . implode("\n", $actions) . "\n" : "");
|
||||
@@ -1134,6 +1136,20 @@ class Adminer {
|
||||
echo "</ul>\n";
|
||||
}
|
||||
|
||||
/** Get server variables
|
||||
* @return list<string[]> [[$name, $value]]
|
||||
*/
|
||||
function showVariables(): array {
|
||||
return show_variables();
|
||||
}
|
||||
|
||||
/** Get status variables
|
||||
* @return list<string[]> [[$name, $value]]
|
||||
*/
|
||||
function showStatus(): array {
|
||||
return show_status();
|
||||
}
|
||||
|
||||
/** Get process list
|
||||
* @return list<string[]> [$row]
|
||||
*/
|
||||
|
||||
@@ -29,16 +29,6 @@ if (isset($_GET["file"])) {
|
||||
include "../adminer/file.inc.php";
|
||||
}
|
||||
|
||||
if ($_GET["script"] == "version") {
|
||||
$filename = get_temp_dir() . "/adminer.version";
|
||||
@unlink($filename); // it may not be writable by us, @ - it may not exist
|
||||
$fp = file_open_lock($filename);
|
||||
if ($fp) {
|
||||
file_write_unlock($fp, serialize(array("signature" => $_POST["signature"], "version" => $_POST["version"])));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Adminer doesn't use any global variables; they used to be declared here
|
||||
|
||||
if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility
|
||||
@@ -61,7 +51,11 @@ if (!defined("SID")) {
|
||||
}
|
||||
|
||||
// disable magic quotes to be able to use database escaping function
|
||||
remove_slashes(array(&$_GET, &$_POST, &$_COOKIE), $filter);
|
||||
if (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) {
|
||||
$_GET = remove_slashes($_GET, $filter);
|
||||
$_POST = remove_slashes($_POST, $filter);
|
||||
$_COOKIE = remove_slashes($_COOKIE, $filter);
|
||||
}
|
||||
if (function_exists("get_magic_quotes_runtime") && get_magic_quotes_runtime()) {
|
||||
set_magic_quotes_runtime(false);
|
||||
}
|
||||
|
||||
@@ -62,24 +62,8 @@ function page_header(string $title, string $error = "", $breadcrumb = array(), s
|
||||
adminer()->bodyClass();
|
||||
echo "'>\n";
|
||||
$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
|
||||
}
|
||||
}
|
||||
echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick"
|
||||
. (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '" . VERSION . "', '" . js_escape(ME) . "', '" . get_token() . "')")
|
||||
. (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '" . VERSION . "')")
|
||||
. "});
|
||||
document.body.classList.replace('nojs', 'js');
|
||||
const offlineMessage = '" . js_escape(lang('You are offline.')) . "';
|
||||
@@ -153,8 +137,8 @@ function csp(): array {
|
||||
return array(
|
||||
array(
|
||||
"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-'
|
||||
"connect-src" => "'self'",
|
||||
"frame-src" => "https://www.adminer.org",
|
||||
"connect-src" => "'self' https://www.adminer.org",
|
||||
"frame-src" => "'none'",
|
||||
"object-src" => "'none'",
|
||||
"base-uri" => "'none'",
|
||||
"form-action" => "'self'",
|
||||
|
||||
@@ -19,6 +19,7 @@ abstract class SqlDriver {
|
||||
|
||||
/** @var Db */ protected $conn;
|
||||
/** @var int[][] */ protected $types = array(); // [$group => [$type => $maximum_unsigned_length, ...], ...]
|
||||
/** @var string */ public $delimiter = ";";
|
||||
/** @var string[] */ public $insertFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit and insert
|
||||
/** @var string[] */ public $editFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit only
|
||||
/** @var list<string> */ public $unsigned = array(); // number variants
|
||||
@@ -190,7 +191,7 @@ abstract class SqlDriver {
|
||||
}
|
||||
|
||||
/** Convert value returned by database to actual value
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
*/
|
||||
function value(?string $val, array $field): ?string {
|
||||
return (method_exists($this->conn, 'value') ? $this->conn->value($val, $field) : $val);
|
||||
@@ -214,14 +215,14 @@ abstract class SqlDriver {
|
||||
}
|
||||
|
||||
/** Get tables this table inherits from
|
||||
* @return list<string>
|
||||
* @return list<array{table: string, ns: string}>
|
||||
*/
|
||||
function inheritsFrom(string $table): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/** Get inherited tables
|
||||
* @return list<string>
|
||||
* @return list<array{table: string, ns: string}>
|
||||
*/
|
||||
function inheritedTables(string $table): array {
|
||||
return array();
|
||||
@@ -268,10 +269,10 @@ abstract class SqlDriver {
|
||||
// MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not
|
||||
return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE
|
||||
FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c
|
||||
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME
|
||||
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME" . ($this->conn->flavor == 'maria' ? " AND c.TABLE_NAME = t.TABLE_NAME" : "") . "
|
||||
WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . "
|
||||
AND t.TABLE_NAME = " . q($table) . "
|
||||
AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'", $this->conn); // ignore default IS NOT NULL checks in PostrgreSQL
|
||||
AND t.TABLE_NAME = " . q($table) . (JUSH == "pgsql" ? "
|
||||
AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'" : ""), $this->conn); // ignore default IS NOT NULL checks in PostrgreSQL
|
||||
}
|
||||
|
||||
/** Get all fields in the current schema
|
||||
|
||||
@@ -72,25 +72,18 @@ function print_select_result($result, ?Db $connection2 = null, array $orgtables
|
||||
} else {
|
||||
$link = ME . "edit=" . urlencode($links[$key]);
|
||||
foreach ($indexes[$links[$key]] as $col => $j) {
|
||||
if ($row[$j] === null) {
|
||||
$link = "";
|
||||
break;
|
||||
}
|
||||
$link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]);
|
||||
}
|
||||
}
|
||||
} elseif (is_url($val)) {
|
||||
$link = $val;
|
||||
}
|
||||
if ($val === null) {
|
||||
$val = "<i>NULL</i>";
|
||||
} elseif ($blobs[$key] && !is_utf8($val)) {
|
||||
$val = "<i>" . lang('%d byte(s)', strlen($val)) . "</i>"; //! link to download
|
||||
} else {
|
||||
$val = h($val);
|
||||
if ($types[$key] == 254) { // 254 - char
|
||||
$val = "<code>$val</code>";
|
||||
}
|
||||
}
|
||||
if ($link) {
|
||||
$val = "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : '') . ">$val</a>";
|
||||
}
|
||||
$field = array(
|
||||
'type' => ($blobs[$key] ? 'blob' : ($types[$key] == 254 ? 'char' : '')),
|
||||
);
|
||||
$val = select_value($val, $link, $field, null);
|
||||
// https://dev.mysql.com/doc/dev/mysql-server/latest/field__types_8h.html
|
||||
echo "<td" . ($types[$key] <= 9 || $types[$key] == 246 ? " class='number'" : "") . ">$val";
|
||||
}
|
||||
@@ -246,15 +239,18 @@ function process_field(array $field, array $type_field): array {
|
||||
* @param Field $field
|
||||
*/
|
||||
function default_value(array $field): string {
|
||||
$default = $field["default"];
|
||||
if ($field["default"] === null) {
|
||||
return "";
|
||||
}
|
||||
$default = str_replace("\r", "", $field["default"]);
|
||||
$generated = $field["generated"];
|
||||
return ($default === null ? "" : (in_array($generated, driver()->generated)
|
||||
? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") . "" : " GENERATED ALWAYS AS ($default) $generated")
|
||||
return (in_array($generated, driver()->generated)
|
||||
? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") : " GENERATED ALWAYS AS ($default) $generated")
|
||||
: " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default))
|
||||
? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column
|
||||
: str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", (JUSH == "sqlite" ? "($default)" : $default))
|
||||
)
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
/** Get type class to use in CSS
|
||||
@@ -325,7 +321,9 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", array $f
|
||||
? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), driver()->generated), $field["generated"]) . " "
|
||||
: checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default")
|
||||
);
|
||||
echo "<input name='fields[$i][default]' value='" . h($field["default"]) . "' aria-labelledby='label-default'>";
|
||||
$attrs = " name='fields[$i][default]' aria-labelledby='label-default'";
|
||||
$value = h($field["default"]);
|
||||
echo (preg_match('~\n~', $field["default"]) ? "<textarea$attrs rows='2' cols='30' style='vertical-align: bottom;'>\n$value</textarea>" : "<input$attrs value='$value'>"); // \n to preserve the leading newline
|
||||
echo (support("comment") ? "<td$comment_class><input name='fields[$i][comment]' value='" . h($field["comment"]) . "' data-maxlength='" . (min_version(5.5) ? 1024 : 255) . "' aria-labelledby='label-comment'>" : "");
|
||||
}
|
||||
echo "<td>";
|
||||
@@ -487,6 +485,7 @@ function format_foreign_key(array $foreign_key): string {
|
||||
. " (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions
|
||||
. (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "")
|
||||
. (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "")
|
||||
. ($foreign_key["deferrable"] ? " $foreign_key[deferrable]" : "")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -507,20 +506,6 @@ function tar_file(string $filename, $tmp_file): void {
|
||||
echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512);
|
||||
}
|
||||
|
||||
/** Get INI bytes value */
|
||||
function ini_bytes(string $ini): int {
|
||||
$val = ini_get($ini);
|
||||
switch (strtolower(substr($val, -1))) {
|
||||
case 'g':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'm':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'k':
|
||||
$val = (int) $val * 1024;
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/** Create link to database documentation
|
||||
* @param string[] $paths JUSH => $path
|
||||
* @param string $text HTML code
|
||||
|
||||
@@ -76,24 +76,19 @@ function number_type(): string {
|
||||
}
|
||||
|
||||
/** Disable magic_quotes_gpc
|
||||
* @param list<array> $process e.g. [&$_GET, &$_POST, &$_COOKIE]
|
||||
* @param mixed[] $values
|
||||
* @param bool $filter whether to leave values as is
|
||||
* @return void modified in place
|
||||
* @return mixed[]
|
||||
*/
|
||||
function remove_slashes(array $process, bool $filter = false): void {
|
||||
if (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) {
|
||||
while (list($key, $val) = each($process)) {
|
||||
foreach ($val as $k => $v) {
|
||||
unset($process[$key][$k]);
|
||||
if (is_array($v)) {
|
||||
$process[$key][stripslashes($k)] = $v;
|
||||
$process[] = &$process[$key][stripslashes($k)];
|
||||
} else {
|
||||
$process[$key][stripslashes($k)] = ($filter ? $v : stripslashes($v));
|
||||
}
|
||||
}
|
||||
}
|
||||
function remove_slashes(array $values, bool $filter = false): array {
|
||||
$return = array();
|
||||
foreach ($values as $key => $val) {
|
||||
$return[stripslashes($key)] = (is_array($val)
|
||||
? remove_slashes($val, $filter)
|
||||
: ($filter ? $val : stripslashes($val))
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/** Escape or unescape string to use inside form [] */
|
||||
@@ -128,6 +123,20 @@ function ini_bool(string $ini): bool {
|
||||
return (preg_match('~^(on|true|yes)$~i', $val) || (int) $val); // boolean values set by php_value are strings
|
||||
}
|
||||
|
||||
/** Get INI bytes value */
|
||||
function ini_bytes(string $ini): int {
|
||||
$val = ini_get($ini);
|
||||
switch (strtolower(substr($val, -1))) {
|
||||
case 'g':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'm':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'k':
|
||||
$val = (int) $val * 1024;
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/** Check if SID is necessary */
|
||||
function sid(): bool {
|
||||
static $return;
|
||||
@@ -230,7 +239,7 @@ function get_rows(string $query, ?Db $connection2 = null, string $error = "<p cl
|
||||
*/
|
||||
function unique_array(?array $row, array $indexes) {
|
||||
foreach ($indexes as $index) {
|
||||
if (preg_match("~PRIMARY|UNIQUE~", $index["type"])) {
|
||||
if (preg_match("~PRIMARY|UNIQUE~", $index["type"]) && !$index["partial"]) {
|
||||
$return = array();
|
||||
foreach ($index["columns"] as $key) {
|
||||
if (!isset($row[$key])) { // NULL is ambiguous
|
||||
@@ -320,7 +329,7 @@ function convert_fields(array $columns, array $fields, array $select = array()):
|
||||
*/
|
||||
function cookie(string $name, ?string $value, int $lifetime = 2592000): void {
|
||||
header(
|
||||
"Set-Cookie: $name=" . urlencode($value)
|
||||
"Set-Cookie: $name=" . rawurlencode($value)
|
||||
. ($lifetime ? "; expires=" . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT" : "")
|
||||
. "; path=" . preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"])
|
||||
. (HTTPS ? "; secure" : "")
|
||||
@@ -461,7 +470,7 @@ function queries(string $query) {
|
||||
if (!Queries::$start) {
|
||||
Queries::$start = microtime(true);
|
||||
}
|
||||
Queries::$queries[] = (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";";
|
||||
Queries::$queries[] = (driver()->delimiter != ';' ? $query : (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";");
|
||||
return connection()->query($query);
|
||||
}
|
||||
|
||||
@@ -642,12 +651,13 @@ function dump_headers(string $identifier, bool $multi_table = false): string {
|
||||
* @param string[] $row
|
||||
*/
|
||||
function dump_csv(array $row): void {
|
||||
$tsv = $_POST["format"] == "tsv";
|
||||
foreach ($row as $key => $val) {
|
||||
if (preg_match('~["\n,;\t]|^0|\.\d*0$~', $val) || $val === "") {
|
||||
if (preg_match('~["\n]|^0[^.]|\.\d*0$|' . ($tsv ? '\t' : '[,;]|^$') . '~', $val)) {
|
||||
$row[$key] = '"' . str_replace('"', '""', $val) . '"';
|
||||
}
|
||||
}
|
||||
echo implode(($_POST["format"] == "csv" ? "," : ($_POST["format"] == "tsv" ? "\t" : ";")), $row) . "\r\n";
|
||||
echo implode(($_POST["format"] == "csv" ? "," : ($tsv ? "\t" : ";")), $row) . "\r\n";
|
||||
}
|
||||
|
||||
/** Apply SQL function
|
||||
@@ -751,19 +761,35 @@ function rand_string(): string {
|
||||
}
|
||||
|
||||
/** Format value to use in select
|
||||
* @param string|string[] $val
|
||||
* @param Field $field
|
||||
* @param string|string[]|list<string[]> $val
|
||||
* @param array{type: string} $field
|
||||
* @param ?numeric-string $text_length
|
||||
* @return string HTML
|
||||
*/
|
||||
function select_value($val, string $link, array $field, ?string $text_length): string {
|
||||
if (is_array($val)) {
|
||||
$return = "";
|
||||
foreach ($val as $k => $v) {
|
||||
$return .= "<tr>"
|
||||
. ($val != array_values($val) ? "<th>" . h($k) : "")
|
||||
. "<td>" . select_value($v, $link, $field, $text_length)
|
||||
;
|
||||
if (array_filter($val, 'is_array') == array_values($val)) { // list of arrays
|
||||
$keys = array();
|
||||
foreach ($val as $v) {
|
||||
$keys += array_fill_keys(array_keys($v), null);
|
||||
}
|
||||
foreach (array_keys($keys) as $k) {
|
||||
$return .= "<th>" . h($k);
|
||||
}
|
||||
foreach ($val as $v) {
|
||||
$return .= "<tr>";
|
||||
foreach (array_merge($keys, $v) as $v2) {
|
||||
$return .= "<td>" . select_value($v2, $link, $field, $text_length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($val as $k => $v) {
|
||||
$return .= "<tr>"
|
||||
. ($val != array_values($val) ? "<th>" . h($k) : "")
|
||||
. "<td>" . select_value($v, $link, $field, $text_length)
|
||||
;
|
||||
}
|
||||
}
|
||||
return "<table>$return</table>";
|
||||
}
|
||||
@@ -778,7 +804,7 @@ function select_value($val, string $link, array $field, ?string $text_length): s
|
||||
$link = $val; // IE 11 and all modern browsers hide referrer
|
||||
}
|
||||
}
|
||||
$return = adminer()->editVal($val, $field);
|
||||
$return = adminer()->editVal(driver()->value($val, $field), $field);
|
||||
if ($return !== null) {
|
||||
if (!is_utf8($return)) {
|
||||
$return = "\0"; // htmlspecialchars of binary data returns an empty string
|
||||
@@ -792,7 +818,7 @@ function select_value($val, string $link, array $field, ?string $text_length): s
|
||||
}
|
||||
|
||||
/** Check whether the field type is blob or equivalent
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
*/
|
||||
function is_blob(array $field): bool {
|
||||
return preg_match('~blob|bytea|raw|file~', $field["type"]) && !in_array($field["type"], idx(driver()->structuredTypes(), lang('User types'), array()));
|
||||
@@ -809,14 +835,14 @@ function is_mail(?string $email): bool {
|
||||
/** Check whether the string is URL address */
|
||||
function is_url(?string $string): bool {
|
||||
$domain = '[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])'; // one domain component //! IDN
|
||||
return preg_match("~^(https?)://($domain?\\.)+$domain(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i", $string); //! restrict path, query and fragment characters
|
||||
return preg_match("~^((https?):)?//($domain?\\.)+$domain(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i", $string); //! restrict path, query and fragment characters
|
||||
}
|
||||
|
||||
/** Check if field should be shortened
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
*/
|
||||
function is_shortable(array $field): bool {
|
||||
return preg_match('~char|text|json|lob|geometry|point|linestring|polygon|string|bytea|hstore~', $field["type"]);
|
||||
return !preg_match('~' . number_type() . '|date|time|year~', $field["type"]);
|
||||
}
|
||||
|
||||
/** Split server into host and (port or socket)
|
||||
|
||||
@@ -176,6 +176,20 @@ function hidden_fields_get(): void {
|
||||
echo input_hidden("username", $_GET["username"]);
|
||||
}
|
||||
|
||||
/** Get <input type='file'> */
|
||||
function file_input(string $input): string {
|
||||
$max_file_uploads = "max_file_uploads";
|
||||
$max_file_uploads_value = ini_get($max_file_uploads);
|
||||
$upload_max_filesize = "upload_max_filesize";
|
||||
$upload_max_filesize_value = ini_get($upload_max_filesize);
|
||||
return (ini_bool("file_uploads")
|
||||
? $input . script("qsl('input[type=\"file\"]').onchange = partialArg(fileChange, "
|
||||
. "$max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "', " // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
|
||||
. ini_bytes("upload_max_filesize") . ", '" . lang('Increase %s.', "$upload_max_filesize = $upload_max_filesize_value") . "')")
|
||||
: lang('File uploads are disabled.')
|
||||
);
|
||||
}
|
||||
|
||||
/** Print enum or set input field
|
||||
* @param 'radio'|'checkbox' $type
|
||||
* @param Field $field
|
||||
@@ -202,9 +216,12 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
$name = h(bracket_escape($field["field"]));
|
||||
echo "<td class='function'>";
|
||||
if (is_array($value) && !$function) {
|
||||
$value = json_encode($value, 128 | 64 | 256); // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4
|
||||
$function = "json";
|
||||
}
|
||||
$json = ($function == "json" || preg_match('~^jsonb?$~', $field["type"]));
|
||||
if ($json && $value != '' && (JUSH != "pgsql" || $field["type"] != "json")) {
|
||||
$value = json_encode(is_array($value) ? $value : json_decode($value), 128 | 64 | 256); // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4
|
||||
}
|
||||
$reset = (JUSH == "mssql" && $field["auto_increment"]);
|
||||
if ($reset && !$_POST["save"]) {
|
||||
$function = null;
|
||||
@@ -215,8 +232,7 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
$field["type"] = "enum";
|
||||
$field["length"] = $enums;
|
||||
}
|
||||
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
|
||||
$attrs = " name='fields[$name]" . ($field["type"] == "enum" || $field["type"] == "set" ? "[]" : "") . "'$disabled" . ($autofocus ? " autofocus" : "");
|
||||
$attrs = " name='fields[$name]" . ($field["type"] == "enum" || $field["type"] == "set" ? "[]" : "") . "'" . ($autofocus ? " autofocus" : "");
|
||||
echo driver()->unconvertFunction($field) . " ";
|
||||
$table = $_GET["edit"] ?: $_GET["select"];
|
||||
if ($field["type"] == "enum") {
|
||||
@@ -224,7 +240,7 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
} else {
|
||||
$has_function = (in_array($function, $functions) || isset($functions[$function]));
|
||||
echo (count($functions) > 1
|
||||
? "<select name='function[$name]'$disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
|
||||
? "<select name='function[$name]'>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
|
||||
. on_help("event.target.value.replace(/^SQL\$/, '')", 1)
|
||||
. script("qsl('select').onchange = functionChange;", "")
|
||||
: h(reset($functions))
|
||||
@@ -239,7 +255,7 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
echo enum_input("checkbox", $attrs, $field, (is_string($value) ? explode(",", $value) : $value));
|
||||
} elseif (is_blob($field) && ini_bool("file_uploads")) {
|
||||
echo "<input type='file' name='fields-$name'>";
|
||||
} elseif ($function == "json" || preg_match('~^jsonb?$~', $field["type"])) {
|
||||
} elseif ($json) {
|
||||
echo "<textarea$attrs cols='50' rows='12' class='jush-js'>" . h($value) . '</textarea>';
|
||||
} elseif (($text = preg_match('~text|lob|memo~i', $field["type"])) || preg_match("~\n~", $value)) {
|
||||
if ($text && JUSH != "sqlite") {
|
||||
@@ -287,15 +303,15 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
* @return mixed false to leave the original value
|
||||
*/
|
||||
function process_input(array $field) {
|
||||
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
|
||||
return;
|
||||
}
|
||||
$idf = bracket_escape($field["field"]);
|
||||
$function = idx($_POST["function"], $idf);
|
||||
$value = idx($_POST["fields"], $idf);
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
if ($field["type"] == "enum" || driver()->enumLength($field)) {
|
||||
$value = $value[0];
|
||||
if ($value == "orig") {
|
||||
$value = idx($value, 0);
|
||||
if ($value == "orig" || !$value) {
|
||||
return false;
|
||||
}
|
||||
if ($value == "null") {
|
||||
@@ -380,6 +396,7 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
|
||||
return;
|
||||
}
|
||||
echo "<form action='' method='post' enctype='multipart/form-data' id='form'>\n";
|
||||
$editable = false;
|
||||
if (!$fields) {
|
||||
echo "<p class='error'>" . lang('You have no privileges to update this table.') . "\n";
|
||||
} else {
|
||||
@@ -410,30 +427,35 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
|
||||
if (!$_POST["save"] && is_string($value)) {
|
||||
$value = adminer()->editVal($value, $field);
|
||||
}
|
||||
$function = ($_POST["save"]
|
||||
? idx($_POST["function"], $name, "")
|
||||
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
|
||||
? "now"
|
||||
: ($value === false ? null : ($value !== null ? '' : 'NULL'))
|
||||
)
|
||||
);
|
||||
if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) {
|
||||
$function = "SQL";
|
||||
}
|
||||
if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) {
|
||||
$value = "";
|
||||
$function = "now";
|
||||
}
|
||||
if ($field["type"] == "uuid" && $value == "uuid()") {
|
||||
$value = "";
|
||||
$function = "uuid";
|
||||
}
|
||||
if ($autofocus !== false) {
|
||||
$autofocus = ($field["auto_increment"] || $function == "now" || $function == "uuid" ? null : true); // null - don't autofocus this input but check the next one
|
||||
}
|
||||
input($field, $value, $function, $autofocus);
|
||||
if ($autofocus) {
|
||||
$autofocus = false;
|
||||
if (($update && !isset($field["privileges"]["update"])) || $field["generated"]) {
|
||||
echo "<td class='function'><td>" . select_value($value, '', $field, null);
|
||||
} else {
|
||||
$editable = true;
|
||||
$function = ($_POST["save"]
|
||||
? idx($_POST["function"], $name, "")
|
||||
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
|
||||
? "now"
|
||||
: ($value === false ? null : ($value !== null ? '' : 'NULL'))
|
||||
)
|
||||
);
|
||||
if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) {
|
||||
$function = "SQL";
|
||||
}
|
||||
if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) {
|
||||
$value = "";
|
||||
$function = "now";
|
||||
}
|
||||
if ($field["type"] == "uuid" && $value == "uuid()") {
|
||||
$value = "";
|
||||
$function = "uuid";
|
||||
}
|
||||
if ($autofocus !== false) {
|
||||
$autofocus = ($field["auto_increment"] || $function == "now" || $function == "uuid" ? null : true); // null - don't autofocus this input but check the next one
|
||||
}
|
||||
input($field, $value, $function, $autofocus);
|
||||
if ($autofocus) {
|
||||
$autofocus = false;
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
@@ -449,7 +471,7 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
|
||||
echo "</table>\n";
|
||||
}
|
||||
echo "<p>\n";
|
||||
if ($fields) {
|
||||
if ($editable) {
|
||||
echo "<input type='submit' value='" . lang('Save') . "'>\n";
|
||||
if (!isset($_GET["select"])) {
|
||||
echo "<input type='submit' name='insert' value='" . ($update
|
||||
|
||||
@@ -26,7 +26,7 @@ function lang_format($translation, $number = null): string {
|
||||
: (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other
|
||||
: (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other
|
||||
: (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0
|
||||
: (in_array(LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other
|
||||
: (in_array(LANG, array('bs', 'hr', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other
|
||||
: 1)))))))) // different forms for 1, other
|
||||
; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
|
||||
$translation = $translation[$pos];
|
||||
@@ -67,6 +67,7 @@ function langs(): array {
|
||||
'gl' => 'Galego', // Eduardo Penabad Ramos
|
||||
'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/
|
||||
'hi' => 'हिन्दी', // Joshi yogesh
|
||||
'hr' => 'Hrvatski', // Nikola Paradžik
|
||||
'hu' => 'Magyar', // Borsos Szilárd (Borsosfi) - http://www.borsosfi.hu, info@borsosfi.hu
|
||||
'id' => 'Bahasa Indonesia', // Ivan Lanin - http://ivan.lanin.org
|
||||
'it' => 'Italiano', // Alessandro Fiorotto, Paolo Asperti
|
||||
|
||||
@@ -17,12 +17,12 @@ class Plugins {
|
||||
$basename = "adminer-plugins";
|
||||
if (is_dir($basename)) {
|
||||
foreach (glob("$basename/*.php") as $filename) {
|
||||
$include = include_once "./$filename";
|
||||
$this->includeOnce($filename);
|
||||
}
|
||||
}
|
||||
$help = " href='https://www.adminer.org/plugins/#use'" . target_blank();
|
||||
if (file_exists("$basename.php")) {
|
||||
$include = include_once "./$basename.php"; // example: return array(new AdminerLoginOtp($secret))
|
||||
$include = $this->includeOnce("$basename.php"); // example: return array(new AdminerLoginOtp($secret));
|
||||
if (is_array($include)) {
|
||||
foreach ($include as $plugin) {
|
||||
$plugins[get_class($plugin)] = $plugin;
|
||||
@@ -32,7 +32,7 @@ class Plugins {
|
||||
}
|
||||
}
|
||||
foreach (get_declared_classes() as $class) {
|
||||
if (!$plugins[$class] && preg_match('~^Adminer\w~i', $class)) {
|
||||
if (!$plugins[$class] && (preg_match('~^Adminer\w~i', $class) || is_subclass_of($class, 'Adminer\Plugin'))) {
|
||||
// we need to use reflection because PHP 7.1 throws ArgumentCountError for missing arguments but older versions issue a warning
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$constructor = $reflection->getConstructor();
|
||||
@@ -59,6 +59,13 @@ class Plugins {
|
||||
}
|
||||
}
|
||||
|
||||
/** Separate function to not overwrite local variables
|
||||
* @return array<object>|true
|
||||
*/
|
||||
function includeOnce(string $filename) {
|
||||
return include_once "./$filename";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param literal-string $name
|
||||
* @param mixed[] $params
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
const VERSION = "5.4.0";
|
||||
const VERSION = "5.4.3-dev";
|
||||
|
||||
@@ -13,6 +13,7 @@ Lang::$translations = array(
|
||||
'Logged as: %s' => 'Přihlášen jako: %s',
|
||||
'Logout successful.' => 'Odhlášení proběhlo v pořádku.',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Díky za použití Admineru, <a href="https://www.adminer.org/cs/donation/">přispějte</a> na vývoj.',
|
||||
'hostname[:port] or :socket' => 'hostname[:port] nebo :socket',
|
||||
'Invalid credentials.' => 'Neplatné přihlašovací údaje.',
|
||||
'There is a space in the input password which might be the cause.' => 'Problém může být, že je v zadaném hesle mezera.',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje přístup k databázi bez hesla, <a href="https://www.adminer.org/cs/password/"%s>více informací</a>.',
|
||||
|
||||
364
adminer/lang/hr.inc.php
Normal file
364
adminer/lang/hr.inc.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
Lang::$translations = array(
|
||||
// label for database system selection (MySQL, SQLite, ...)
|
||||
'System' => 'Sustav',
|
||||
'Server' => 'Poslužitelj',
|
||||
'Username' => 'Korisničko ime',
|
||||
'Password' => 'Lozinka',
|
||||
'Permanent login' => 'Trajna prijava',
|
||||
'Login' => 'Prijava',
|
||||
'Logout' => 'Odjava',
|
||||
'Logged as: %s' => 'Prijavljen kao: %s',
|
||||
'Logout successful.' => 'Uspješna odjava.',
|
||||
'Invalid credentials.' => 'Neispravni podaci za prijavu.',
|
||||
'Language' => 'Jezik',
|
||||
'Invalid CSRF token. Send the form again.' => 'Nevažeći CSRF token. Pošaljite obrazac ponovo.',
|
||||
'No extension' => 'Nema proširenja',
|
||||
'None of the supported PHP extensions (%s) are available.' => 'Nijedno od podržanih PHP proširenja (%s) nije dostupno.',
|
||||
'Session support must be enabled.' => 'Podrška za sesije mora biti uključena.',
|
||||
'Session expired, please login again.' => 'Sesija je istekla, molimo prijavite se ponovo.',
|
||||
'%s version: %s through PHP extension %s' => '%s verzija: %s putem PHP proširenja %s',
|
||||
'Refresh' => 'Osvježi',
|
||||
|
||||
// text direction - 'ltr' or 'rtl'
|
||||
'ltr' => 'ltr',
|
||||
|
||||
'Privileges' => 'Ovlasti',
|
||||
'Create user' => 'Novi korisnik',
|
||||
'User has been dropped.' => 'Korisnik je izbrisan.',
|
||||
'User has been altered.' => 'Korisnik je izmijenjen.',
|
||||
'User has been created.' => 'Korisnik je kreiran.',
|
||||
'Hashed' => 'Hashirano',
|
||||
'Column' => 'Stupac',
|
||||
'Routine' => 'Rutina',
|
||||
'Grant' => 'Dodijeli',
|
||||
'Revoke' => 'Opozovi',
|
||||
|
||||
'Process list' => 'Popis procesa',
|
||||
'%d process(es) have been killed.' => array('%d proces je zaustavljen.', '%d procesa su zaustavljena.', '%d procesa je zaustavljeno.'),
|
||||
'Kill' => 'Zaustavi',
|
||||
|
||||
'Variables' => 'Varijable',
|
||||
'Status' => 'Status',
|
||||
|
||||
'SQL command' => 'SQL naredba',
|
||||
'%d query(s) executed OK.' => array('%d upit je uspješno izvršen.', '%d upita su uspješno izvršena.', '%d upita je uspješno izvršeno.'),
|
||||
'Query executed OK, %d row(s) affected.' => array('Upit je uspješno izvršen, %d redak je ažuriran.', 'Upit je uspješno izvršen, %d retka su ažurirana.', 'Upit je uspješno izvršen, %d redaka je ažurirano.'),
|
||||
'No commands to execute.' => 'Nema naredbi za izvršavanje.',
|
||||
'Error in query' => 'Greška u upitu',
|
||||
'Execute' => 'Izvrši',
|
||||
'Stop on error' => 'Zaustavi pri grešci',
|
||||
'Show only errors' => 'Prikaži samo greške',
|
||||
// sprintf() format for time of the command
|
||||
'%.3f s' => '%.3f s',
|
||||
'History' => 'Povijest',
|
||||
'Clear' => 'Očisti',
|
||||
'Edit all' => 'Uredi sve',
|
||||
|
||||
'File upload' => 'Prijenos datoteke',
|
||||
'From server' => 'S poslužitelja',
|
||||
'Webserver file %s' => 'Datoteka %s s web poslužitelja',
|
||||
'Run file' => 'Pokreni datoteku',
|
||||
'File does not exist.' => 'Datoteka ne postoji.',
|
||||
'File uploads are disabled.' => 'Prijenos datoteka je onemogućen.',
|
||||
'Unable to upload a file.' => 'Prijenos datoteke nije uspio.',
|
||||
'Maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veličina datoteke je %sB.',
|
||||
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'Preveliki POST podaci. Smanjite podatke ili povećajte vrijednost konfiguracijske direktive %s.',
|
||||
|
||||
'Export' => 'Izvoz',
|
||||
'Output' => 'Ispis',
|
||||
'open' => 'otvori',
|
||||
'save' => 'spremi',
|
||||
'Format' => 'Format',
|
||||
'Data' => 'Podaci',
|
||||
|
||||
'Database' => 'Baza podataka',
|
||||
'Use' => 'Koristi',
|
||||
'Select database' => 'Odaberite bazu',
|
||||
'Invalid database.' => 'Neispravna baza podataka.',
|
||||
'Database has been dropped.' => 'Baza podataka je izbrisana.',
|
||||
'Databases have been dropped.' => 'Baze podataka su izbrisane.',
|
||||
'Database has been created.' => 'Baza podataka je kreirana.',
|
||||
'Database has been renamed.' => 'Baza podataka je preimenovana.',
|
||||
'Database has been altered.' => 'Baza podataka je izmijenjena.',
|
||||
'Alter database' => 'Izmijeni bazu podataka',
|
||||
'Create database' => 'Kreiraj bazu podataka',
|
||||
'Database schema' => 'Shema baze podataka',
|
||||
|
||||
// link to current database schema layout
|
||||
'Permanent link' => 'Trajna veza',
|
||||
|
||||
// thousands separator - must contain single byte
|
||||
',' => '.',
|
||||
'0123456789' => '0123456789',
|
||||
'Engine' => 'Motor',
|
||||
'Collation' => 'Uspoređivanje',
|
||||
'Data Length' => 'Duljina podataka',
|
||||
'Index Length' => 'Duljina indeksa',
|
||||
'Data Free' => 'Slobodan prostor',
|
||||
'Rows' => 'Redaka',
|
||||
'%d in total' => 'ukupno %d',
|
||||
'Analyze' => 'Analiziraj',
|
||||
'Optimize' => 'Optimiziraj',
|
||||
'Check' => 'Provjeri',
|
||||
'Repair' => 'Popravi',
|
||||
'Truncate' => 'Isprazni',
|
||||
'Tables have been truncated.' => 'Tablice su ispražnjene.',
|
||||
'Move to other database' => 'Premjesti u drugu bazu podataka',
|
||||
'Move' => 'Premjesti',
|
||||
'Tables have been moved.' => 'Tablice su premještene.',
|
||||
'Copy' => 'Kopiraj',
|
||||
'Tables have been copied.' => 'Tablice su kopirane.',
|
||||
|
||||
'Routines' => 'Rutine',
|
||||
'Routine has been called, %d row(s) affected.' => array('Rutina je pozvana, %d redak je ažuriran.', 'Rutina je pozvana, %d retka su ažurirana.', 'Rutina je pozvana, %d redaka je ažurirano.'),
|
||||
'Call' => 'Pozovi',
|
||||
'Parameter name' => 'Naziv parametra',
|
||||
'Create procedure' => 'Kreiraj proceduru',
|
||||
'Create function' => 'Kreiraj funkciju',
|
||||
'Routine has been dropped.' => 'Rutina je izbrisana.',
|
||||
'Routine has been altered.' => 'Rutina je izmijenjena.',
|
||||
'Routine has been created.' => 'Rutina je kreirana.',
|
||||
'Alter function' => 'Izmijeni funkciju',
|
||||
'Alter procedure' => 'Izmijeni proceduru',
|
||||
'Return type' => 'Tip povratne vrijednosti',
|
||||
|
||||
'Events' => 'Događaji',
|
||||
'Event has been dropped.' => 'Događaj je izbrisan.',
|
||||
'Event has been altered.' => 'Događaj je izmijenjen.',
|
||||
'Event has been created.' => 'Događaj je kreiran.',
|
||||
'Alter event' => 'Izmijeni događaj',
|
||||
'Create event' => 'Kreiraj događaj',
|
||||
'At given time' => 'U zadano vrijeme',
|
||||
'Every' => 'Svako',
|
||||
'Schedule' => 'Raspored',
|
||||
'Start' => 'Početak',
|
||||
'End' => 'Kraj',
|
||||
'On completion preserve' => 'Zadrži po završetku',
|
||||
|
||||
'Tables' => 'Tablice',
|
||||
'Tables and views' => 'Tablice i pogledi',
|
||||
'Table' => 'Tablica',
|
||||
'No tables.' => 'Nema tablica.',
|
||||
'Alter table' => 'Izmijeni tablicu',
|
||||
'Create table' => 'Kreiraj tablicu',
|
||||
'Table has been dropped.' => 'Tablica je izbrisana.',
|
||||
'Tables have been dropped.' => 'Tablice su izbrisane.',
|
||||
'Tables have been optimized.' => 'Tablice su optimizirane.',
|
||||
'Table has been altered.' => 'Tablica je izmijenjena.',
|
||||
'Table has been created.' => 'Tablica je kreirana.',
|
||||
'Table name' => 'Naziv tablice',
|
||||
'Show structure' => 'Prikaži strukturu',
|
||||
'engine' => 'motor',
|
||||
'collation' => 'uspoređivanje',
|
||||
'Column name' => 'Naziv stupca',
|
||||
'Type' => 'Tip',
|
||||
'Length' => 'Duljina',
|
||||
'Auto Increment' => 'Auto-inkrement',
|
||||
'Options' => 'Opcije',
|
||||
'Comment' => 'Komentar',
|
||||
'Default values' => 'Zadane vrijednosti',
|
||||
'Drop' => 'Izbriši',
|
||||
'Are you sure?' => 'Jeste li sigurni?',
|
||||
'Move up' => 'Pomakni gore',
|
||||
'Move down' => 'Pomakni dolje',
|
||||
'Remove' => 'Ukloni',
|
||||
'Maximum number of allowed fields exceeded. Please increase %s.' => 'Premašen je maksimalni broj dozvoljenih polja. Molimo povećajte %s.',
|
||||
|
||||
'Partition by' => 'Particioniraj po',
|
||||
'Partitions' => 'Particije',
|
||||
'Partition name' => 'Naziv particije',
|
||||
'Values' => 'Vrijednosti',
|
||||
|
||||
'View' => 'Pogled',
|
||||
'View has been dropped.' => 'Pogled je izbrisan.',
|
||||
'View has been altered.' => 'Pogled je izmijenjen.',
|
||||
'View has been created.' => 'Pogled je kreiran.',
|
||||
'Alter view' => 'Izmijeni pogled',
|
||||
'Create view' => 'Kreiraj pogled',
|
||||
|
||||
'Indexes' => 'Indeksi',
|
||||
'Indexes have been altered.' => 'Indeksi su izmijenjeni.',
|
||||
'Alter indexes' => 'Izmijeni indekse',
|
||||
'Add next' => 'Dodaj sljedeći',
|
||||
'Index Type' => 'Tip indeksa',
|
||||
'length' => 'duljina',
|
||||
|
||||
'Foreign keys' => 'Strani ključevi',
|
||||
'Foreign key' => 'Strani ključ',
|
||||
'Foreign key has been dropped.' => 'Strani ključ je izbrisan.',
|
||||
'Foreign key has been altered.' => 'Strani ključ je izmijenjen.',
|
||||
'Foreign key has been created.' => 'Strani ključ je kreiran.',
|
||||
'Target table' => 'Ciljna tablica',
|
||||
'Change' => 'Izmijeni',
|
||||
'Source' => 'Izvor',
|
||||
'Target' => 'Cilj',
|
||||
'Add column' => 'Dodaj stupac',
|
||||
'Alter' => 'Izmijeni',
|
||||
'Add foreign key' => 'Dodaj strani ključ',
|
||||
'ON DELETE' => 'ON DELETE (pri brisanju)',
|
||||
'ON UPDATE' => 'ON UPDATE (pri ažuriranju)',
|
||||
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Izvorni i ciljni stupci moraju biti istog tipa podataka, ciljni stupci moraju biti indeksirani i referencirani podaci moraju postojati.',
|
||||
|
||||
'Triggers' => 'Okidači',
|
||||
'Add trigger' => 'Dodaj okidač',
|
||||
'Trigger has been dropped.' => 'Okidač je izbrisan.',
|
||||
'Trigger has been altered.' => 'Okidač je izmijenjen.',
|
||||
'Trigger has been created.' => 'Okidač je kreiran.',
|
||||
'Alter trigger' => 'Izmijeni okidač',
|
||||
'Create trigger' => 'Kreiraj okidač',
|
||||
'Time' => 'Vrijeme',
|
||||
'Event' => 'Događaj',
|
||||
'Name' => 'Naziv',
|
||||
|
||||
'select' => 'odaberi',
|
||||
'Select' => 'Odaberi',
|
||||
'Selected' => 'Odabrano',
|
||||
'Select data' => 'Odaberi podatke',
|
||||
'Functions' => 'Funkcije',
|
||||
'Aggregation' => 'Agregacija',
|
||||
'Search' => 'Pretraži',
|
||||
'anywhere' => 'bilo gdje',
|
||||
'Search data in tables' => 'Pretraži podatke u tablicama',
|
||||
'Sort' => 'Sortiraj',
|
||||
'descending' => 'silazno',
|
||||
'Limit' => 'Ograničenje',
|
||||
'Text length' => 'Duljina teksta',
|
||||
'Action' => 'Radnja',
|
||||
'Full table scan' => 'Puno pretraživanje tablice',
|
||||
'Unable to select the table' => 'Nije moguće odabrati tablicu',
|
||||
'No rows.' => 'Nema redaka.',
|
||||
'%d row(s)' => array('%d redak', '%d retka', '%d redaka'),
|
||||
'Page' => 'Stranica',
|
||||
'last' => 'posljednja',
|
||||
'Loading' => 'Učitavanje',
|
||||
'Load more data' => 'Učitaj više podataka',
|
||||
'Whole result' => 'Cijeli skup rezultata',
|
||||
'%d byte(s)' => array('%d bajt', '%d bajta', '%d bajtova'),
|
||||
|
||||
'Import' => 'Uvoz',
|
||||
'%d row(s) have been imported.' => array('%d redak je uvezen.', '%d retka su uvezena.', '%d redaka je uvezeno.'),
|
||||
|
||||
// in-place editing in select
|
||||
'Ctrl+click on a value to modify it.' => 'Ctrl+klik na vrijednost za izmjenu.',
|
||||
'Use edit link to modify this value.' => 'Koristite vezu za uređivanje ove vrijednosti.',
|
||||
|
||||
// %s can contain auto-increment value
|
||||
'Item%s has been inserted.' => 'Stavka %s je unesena.',
|
||||
'Item has been deleted.' => 'Stavka je izbrisana.',
|
||||
'Item has been updated.' => 'Stavka je ažurirana.',
|
||||
'%d item(s) have been affected.' => array('%d stavka je zahvaćena.', '%d stavke su zahvaćene.', '%d stavki je zahvaćeno.'),
|
||||
'New item' => 'Nova stavka',
|
||||
'original' => 'original',
|
||||
// label for value '' in enum data type
|
||||
'empty' => 'prazno',
|
||||
'edit' => 'uredi',
|
||||
'Edit' => 'Uredi',
|
||||
'Insert' => 'Unesi',
|
||||
'Save' => 'Spremi',
|
||||
'Save and continue edit' => 'Spremi i nastavi uređivanje',
|
||||
'Save and insert next' => 'Spremi i unesi sljedeće',
|
||||
'Clone' => 'Kloniraj',
|
||||
'Delete' => 'Izbriši',
|
||||
'Modify' => 'Izmijeni',
|
||||
|
||||
// data type descriptions
|
||||
'Numbers' => 'Brojevi',
|
||||
'Date and time' => 'Datum i vrijeme',
|
||||
'Strings' => 'Tekst',
|
||||
'Binary' => 'Binarno',
|
||||
'Lists' => 'Liste',
|
||||
'Network' => 'Mreža',
|
||||
'Geometry' => 'Geometrija',
|
||||
'Relations' => 'Odnosi',
|
||||
|
||||
'Editor' => 'Uređivač',
|
||||
// date format in Editor: $1 yyyy, $2 yy, $3 mm, $4 m, $5 dd, $6 d
|
||||
'$1-$3-$5' => '$5.$3.$1',
|
||||
// hint for date format - use language equivalents for day, month and year shortcuts
|
||||
'[yyyy]-mm-dd' => 'dd.mm.[yyyy]',
|
||||
// hint for time format - use language equivalents for hour, minute and second shortcuts
|
||||
'HH:MM:SS' => 'HH:MM:SS',
|
||||
'now' => 'sada',
|
||||
'yes' => 'da',
|
||||
'no' => 'ne',
|
||||
|
||||
// general SQLite error in create, drop or rename database
|
||||
'File exists.' => 'Datoteka već postoji.',
|
||||
'Please use one of the extensions %s.' => 'Molimo koristite jedan od nastavaka %s.',
|
||||
|
||||
// PostgreSQL and MS SQL schema support
|
||||
'Alter schema' => 'Izmijeni shemu',
|
||||
'Create schema' => 'Kreiraj shemu',
|
||||
'Schema has been dropped.' => 'Shema je izbrisana.',
|
||||
'Schema has been created.' => 'Shema je kreirana.',
|
||||
'Schema has been altered.' => 'Shema je izmijenjena.',
|
||||
'Schema' => 'Shema',
|
||||
'Invalid schema.' => 'Neispravna shema.',
|
||||
|
||||
// PostgreSQL sequences support
|
||||
'Sequences' => 'Nizovi',
|
||||
'Create sequence' => 'Kreiraj niz',
|
||||
'Sequence has been dropped.' => 'Niz je izbrisan.',
|
||||
'Sequence has been created.' => 'Niz je kreiran.',
|
||||
'Sequence has been altered.' => 'Niz je izmijenjen.',
|
||||
'Alter sequence' => 'Izmijeni niz',
|
||||
|
||||
// PostgreSQL user types support
|
||||
'User types' => 'Korisnički tipovi',
|
||||
'Create type' => 'Kreiraj tip',
|
||||
'Type has been dropped.' => 'Tip je izbrisan.',
|
||||
'Type has been created.' => 'Tip je kreiran.',
|
||||
'Alter type' => 'Izmijeni tip',
|
||||
|
||||
// MS SQL login
|
||||
'Too many unsuccessful logins, try again in %d minute(s).' => array('Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minutu.', 'Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minute.', 'Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minuta.'),
|
||||
'Check has been dropped.' => 'Provjera je izbrisana.',
|
||||
'Check has been altered.' => 'Provjera je izmijenjena.',
|
||||
'Check has been created.' => 'Provjera je kreirana.',
|
||||
'Alter check' => 'Izmijeni provjeru',
|
||||
'Create check' => 'Kreiraj provjeru',
|
||||
'Drop %s?' => 'Izbrisati %s?',
|
||||
'Vacuum' => 'Vakuumiranje',
|
||||
'overwrite' => 'prepiši',
|
||||
'Disable %s or enable %s or %s extensions.' => 'Onemogućite %s ili omogućite %s ili %s proširenja.',
|
||||
'Database does not support password.' => 'Baza podataka ne podržava lozinku.',
|
||||
'DB' => 'BP',
|
||||
'hostname[:port] or :socket' => 'hostname[:port] ili :socket',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer ne podržava pristup bazi podataka bez lozinke, <a href="https://www.adminer.org/en/password/"%s>više informacija</a>.',
|
||||
'Warnings' => 'Upozorenja',
|
||||
'Default value' => 'Zadana vrijednost',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Hvala što koristite Adminer, razmislite o <a href="https://www.adminer.org/en/donation/">donaciji</a>.',
|
||||
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Glavna lozinka je istekla. <a href="https://www.adminer.org/en/extension/"%s>Implementirajte</a> metodu %s kako biste je učinili trajnom.',
|
||||
'The action will be performed after successful login with the same credentials.' => 'Radnja će biti izvršena nakon uspješne prijave s istim podacima.',
|
||||
'Connecting to privileged ports is not allowed.' => 'Spajanje na privilegirane portove nije dopušteno.',
|
||||
'There is a space in the input password which might be the cause.' => 'U unesenoj lozinci postoji razmak koji bi mogao biti uzrok problema.',
|
||||
'If you did not send this request from Adminer then close this page.' => 'Ako ovaj zahtjev niste poslali iz Adminera, zatvorite ovu stranicu.',
|
||||
'You can upload a big SQL file via FTP and import it from server.' => 'Veliku SQL datoteku možete prenijeti putem FTP-a i uvesti je s poslužitelja.',
|
||||
'Size' => 'Veličina',
|
||||
'Compute' => 'Izračunaj',
|
||||
'Loaded plugins' => 'Učitani dodaci',
|
||||
'screenshot' => 'snimka zaslona',
|
||||
'You are offline.' => 'Niste povezani s mrežom.',
|
||||
'Increase %s.' => 'Povećajte %s.',
|
||||
'You have no privileges to update this table.' => 'Nemate ovlasti za ažuriranje ove tablice.',
|
||||
'Saving' => 'Spremanje',
|
||||
'Unknown error.' => 'Nepoznata greška.',
|
||||
'%s must <a%s>return an array</a>.' => '%s mora <a%s>vratiti niz</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Konfigurirajte</a> %s u %s.',
|
||||
'Algorithm' => 'Algoritam',
|
||||
'Columns' => 'Stupci',
|
||||
'Condition' => 'Uvjet',
|
||||
'File must be in UTF-8 encoding.' => 'Datoteka mora biti u UTF-8 kodiranju.',
|
||||
'ATTACH queries are not supported.' => 'ATTACH upiti nisu podržani.',
|
||||
'%d / ' => '%d / ',
|
||||
'Limit rows' => 'Ograniči retke',
|
||||
'Materialized view' => 'Materijaliziran pogled',
|
||||
'Inherits from' => 'Nasljeđuje od',
|
||||
'Checks' => 'Provjere',
|
||||
'Inherited by' => 'Nasljeđeno od',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php hr` to update this file
|
||||
@@ -13,6 +13,7 @@ Lang::$translations = array(
|
||||
'Logged as: %s' => 'Xx: %s',
|
||||
'Logout successful.' => 'Xx.',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Xx <a href="https://www.adminer.org/en/donation/">xx</a>.',
|
||||
'hostname[:port] or :socket' => 'xx',
|
||||
'Invalid credentials.' => 'Xx.',
|
||||
'There is a space in the input password which might be the cause.' => 'Xx.',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Xx, <a href="https://www.adminer.org/en/password/"%s>xx</a>.',
|
||||
|
||||
@@ -103,16 +103,18 @@ foreach ($schema as $name => $table) {
|
||||
|
||||
foreach ($schema as $name => $table) {
|
||||
foreach ((array) $table["references"] as $target_name => $refs) {
|
||||
foreach ($refs as $left => $ref) {
|
||||
$min_pos = $top;
|
||||
$max_pos = -10;
|
||||
foreach ($ref[0] as $key => $source) {
|
||||
$pos1 = $table["pos"][0] + $table["fields"][$source]["pos"];
|
||||
$pos2 = $schema[$target_name]["pos"][0] + $schema[$target_name]["fields"][$ref[1][$key]]["pos"];
|
||||
$min_pos = min($min_pos, $pos1, $pos2);
|
||||
$max_pos = max($max_pos, $pos1, $pos2);
|
||||
if ($schema[$target_name]) { // otherwise table in another schema
|
||||
foreach ($refs as $left => $ref) {
|
||||
$min_pos = $top;
|
||||
$max_pos = -10;
|
||||
foreach ($ref[0] as $key => $source) {
|
||||
$pos1 = $table["pos"][0] + $table["fields"][$source]["pos"];
|
||||
$pos2 = $schema[$target_name]["pos"][0] + $schema[$target_name]["fields"][$ref[1][$key]]["pos"];
|
||||
$min_pos = min($min_pos, $pos1, $pos2);
|
||||
$max_pos = max($max_pos, $pos1, $pos2);
|
||||
}
|
||||
echo "<div class='references' id='refl$left' style='left: $left" . "em; top: $min_pos" . "em; padding: .5em 0;'><div style='border-right: 1px solid gray; margin-top: 1px; height: " . ($max_pos - $min_pos) . "em;'></div></div>\n";
|
||||
}
|
||||
echo "<div class='references' id='refl$left' style='left: $left" . "em; top: $min_pos" . "em; padding: .5em 0;'><div style='border-right: 1px solid gray; margin-top: 1px; height: " . ($max_pos - $min_pos) . "em;'></div></div>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,17 +353,14 @@ if (!$columns && support("table")) {
|
||||
$desc = "&desc%5B0%5D=1";
|
||||
echo "<th id='th[" . h(bracket_escape($key)) . "]'>" . script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});", "");
|
||||
$fun = apply_sql_function($val["fun"], $name); //! columns looking like functions
|
||||
$sortable = isset($field["privileges"]["order"]) || $fun;
|
||||
$sortable = isset($field["privileges"]["order"]) || $fun != $name;
|
||||
echo ($sortable ? "<a href='" . h($href . ($order[0] == $column || $order[0] == $key ? $desc : '')) . "'>$fun</a>" : $fun); // $order[0] == $key - COUNT(*)
|
||||
echo "<span class='column hidden'>";
|
||||
if ($sortable) {
|
||||
echo "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>";
|
||||
}
|
||||
$menu = ($sortable ? "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>" : '');
|
||||
if (!$val["fun"] && isset($field["privileges"]["where"])) {
|
||||
echo '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>';
|
||||
echo script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');");
|
||||
$menu .= '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>';
|
||||
$menu .= script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');");
|
||||
}
|
||||
echo "</span>";
|
||||
echo ($menu ? "<span class='column hidden'>$menu</span>" : "");
|
||||
}
|
||||
$functions[$key] = $val["fun"];
|
||||
next($select);
|
||||
@@ -417,7 +414,6 @@ if (!$columns && support("table")) {
|
||||
if (isset($names[$key])) {
|
||||
$column = current($select);
|
||||
$field = (array) $fields[$key];
|
||||
$val = driver()->value($val, $field);
|
||||
if ($val != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
|
||||
$email_fields[$key] = (is_mail($val) ? $names[$key] : ""); //! filled e-mails can be contained on other pages
|
||||
}
|
||||
@@ -459,7 +455,8 @@ if (!$columns && support("table")) {
|
||||
$html = select_value($val, $link, $field, $text_length);
|
||||
$id = h("val[$unique_idf][" . bracket_escape($key) . "]");
|
||||
$posted = idx(idx($_POST["val"], $unique_idf), bracket_escape($key));
|
||||
$editable = !is_array($row[$key]) && is_utf8($html) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"];
|
||||
$update = idx($field["privileges"], "update");
|
||||
$editable = !is_array($row[$key]) && is_utf8($html) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"] && $update;
|
||||
$type = (preg_match('~^(AVG|MIN|MAX)\((.+)\)~', $column, $match) ? $fields[idf_unescape($match[2])]["type"] : $field["type"]);
|
||||
$text = preg_match('~text|json|lob~', $type);
|
||||
$is_number = preg_match(number_type(), $type) || preg_match('~^(CHAR_LENGTH|ROUND|FLOOR|CEIL|TIME_TO_SEC|COUNT|SUM)\(~', $column);
|
||||
@@ -469,10 +466,11 @@ if (!$columns && support("table")) {
|
||||
echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>");
|
||||
} else {
|
||||
$long = strpos($html, "<i>…</i>");
|
||||
echo " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'"
|
||||
. ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'")
|
||||
. ">$html"
|
||||
;
|
||||
echo ($update
|
||||
? " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'"
|
||||
. ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'")
|
||||
: ""
|
||||
) . ">$html";
|
||||
}
|
||||
}
|
||||
next($select);
|
||||
@@ -503,7 +501,7 @@ if (!$columns && support("table")) {
|
||||
if (intval($found_rows) < max(1e4, 2 * ($page + 1) * $limit)) {
|
||||
// slow with big tables
|
||||
$found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group)));
|
||||
} else {
|
||||
} elseif (JUSH == 'sql' || JUSH == 'pgsql') {
|
||||
$exact_count = false;
|
||||
}
|
||||
}
|
||||
@@ -595,9 +593,10 @@ if (!$columns && support("table")) {
|
||||
echo "<a href='#import'>" . lang('Import') . "</a>";
|
||||
echo script("qsl('a').onclick = partial(toggle, 'import');", "");
|
||||
echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: ";
|
||||
echo "<input type='file' name='csv_file'> ";
|
||||
echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]);
|
||||
echo " <input type='submit' name='import' value='" . lang('Import') . "'>";
|
||||
echo file_input("<input type='file' name='csv_file'> "
|
||||
. html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"])
|
||||
. " <input type='submit' name='import' value='" . lang('Import') . "'>")
|
||||
;
|
||||
echo "</span>";
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ if (!$error && $_POST) {
|
||||
}
|
||||
|
||||
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)";
|
||||
$delimiter = ";";
|
||||
$delimiter = driver()->delimiter;
|
||||
$offset = 0;
|
||||
$empty = true;
|
||||
$connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
|
||||
@@ -250,15 +250,9 @@ if (!isset($_GET["import"])) {
|
||||
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";
|
||||
|
||||
} else {
|
||||
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
|
||||
$gz = (extension_loaded("zlib") ? "[.gz]" : "");
|
||||
$max_file_uploads = "max_file_uploads";
|
||||
$max_file_uploads_value = ini_get($max_file_uploads);
|
||||
echo (ini_bool("file_uploads")
|
||||
? "SQL$gz (< " . ini_get("upload_max_filesize") . "B): <input type='file' name='sql_file[]' multiple>\n" // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
|
||||
. script("qsl('input').onchange = partialArg(fileChange, $max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "')") . $execute
|
||||
: lang('File uploads are disabled.')
|
||||
);
|
||||
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
|
||||
echo file_input("SQL$gz: <input type='file' name='sql_file[]' multiple>\n$execute");
|
||||
echo "</div></fieldset>\n";
|
||||
$importServerPath = adminer()->importServerPath();
|
||||
if ($importServerPath) {
|
||||
|
||||
@@ -57,6 +57,7 @@ input.wayoff { left: -1000px; position: absolute; }
|
||||
.date { color: #7F007F; }
|
||||
.enum { color: #007F7F; }
|
||||
.binary { color: red; }
|
||||
.odds tbody tr { background: var(--bg); }
|
||||
.odds tbody tr:nth-child(2n) { background: #F5F5F5; }
|
||||
.js .checkable .checked td, .js .checkable .checked th { background: var(--lit); }
|
||||
.time { color: silver; font-size: 70%; }
|
||||
|
||||
@@ -662,24 +662,29 @@ function indexesAddColumn(prefix) {
|
||||
* @param string
|
||||
*/
|
||||
function sqlSubmit(form, root) {
|
||||
if (encodeURIComponent(form['query'].value).length < 500) {
|
||||
form.action = root
|
||||
+ '&sql=' + encodeURIComponent(form['query'].value)
|
||||
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '')
|
||||
+ (form['error_stops'].checked ? '&error_stops=1' : '')
|
||||
+ (form['only_errors'].checked ? '&only_errors=1' : '')
|
||||
;
|
||||
const action = root
|
||||
+ '&sql=' + encodeURIComponent(form['query'].value)
|
||||
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '')
|
||||
+ (form['error_stops'].checked ? '&error_stops=1' : '')
|
||||
+ (form['only_errors'].checked ? '&only_errors=1' : '')
|
||||
;
|
||||
if ((document.location.origin + document.location.pathname + action).length < 2000) { // reasonable minimum is 2048
|
||||
form.action = action;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if PHP can handle the uploaded files
|
||||
* @param Event
|
||||
* @param number
|
||||
* @param string
|
||||
* @param number
|
||||
* @param string
|
||||
* @param Event
|
||||
*/
|
||||
function fileChange(event, maxFileUploads, message) {
|
||||
if (event.target.files.length > maxFileUploads) {
|
||||
alert(message);
|
||||
function fileChange(event, count, countMessage, size, sizeMessage) {
|
||||
if (event.target.files.length > count) {
|
||||
alert(countMessage);
|
||||
} else if (Array.from(event.target.files).reduce((sum, file) => sum + file.size, 0) > size) {
|
||||
alert(sizeMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,29 +96,15 @@ function cookie(assign, days) {
|
||||
|
||||
/** Verify current Adminer version
|
||||
* @param string
|
||||
* @param string own URL base
|
||||
* @param string
|
||||
*/
|
||||
function verifyVersion(current, url, token) {
|
||||
function verifyVersion(current) {
|
||||
cookie('adminer_version=0', 1);
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = 'https://www.adminer.org/version/?current=' + current;
|
||||
iframe.frameBorder = 0;
|
||||
iframe.marginHeight = 0;
|
||||
iframe.scrolling = 'no';
|
||||
iframe.style.width = '7ex';
|
||||
iframe.style.height = '1.25em';
|
||||
iframe.style.display = 'none';
|
||||
addEventListener('message', event => {
|
||||
if (event.origin == 'https://www.adminer.org') {
|
||||
const match = /version=(.+)/.exec(event.data);
|
||||
if (match) {
|
||||
cookie('adminer_version=' + match[1], 1);
|
||||
ajax(url + 'script=version', () => { }, event.data + '&token=' + token);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
qs('#version').appendChild(iframe);
|
||||
// do not send X-Requested-With to avoid preflight
|
||||
fetch('https://www.adminer.org/version/?current=' + current).then(async response => {
|
||||
const json = await response.json();
|
||||
cookie('adminer_version=' + (json.version || current), 7); // empty if there's no newer version
|
||||
qs('#version').textContent = json.version;
|
||||
});
|
||||
}
|
||||
|
||||
/** Get value of select
|
||||
|
||||
@@ -27,12 +27,13 @@ if ($fields) {
|
||||
}
|
||||
|
||||
/** Print links to tables
|
||||
* @param list<string> $tables
|
||||
* @param list<array{table: string, ns: string}> $tables
|
||||
*/
|
||||
function tables_links(array $tables): void {
|
||||
echo "<ul>\n";
|
||||
foreach ($tables as $table) {
|
||||
echo "<li><a href='" . h(ME . "table=" . urlencode($table)) . "'>" . h($table) . "</a>";
|
||||
foreach ($tables as $row) {
|
||||
$link = preg_replace('~ns=[^&]*~', "ns=" . urlencode($row["ns"]), ME);
|
||||
echo "<li><a href='" . h($link . "table=" . urlencode($row["table"])) . "'>" . ($row["ns"] != $_GET["ns"] ? "<b>" . h($row["ns"]) . "</b>." : "") . h($row["table"]) . "</a>";
|
||||
}
|
||||
echo "</ul>\n";
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Adminer;
|
||||
$status = isset($_GET["status"]);
|
||||
page_header($status ? lang('Status') : lang('Variables'));
|
||||
|
||||
$variables = ($status ? show_status() : show_variables());
|
||||
$variables = ($status ? adminer()->showStatus() : adminer()->showVariables());
|
||||
if (!$variables) {
|
||||
echo "<p class='message'>" . lang('No rows.') . "\n";
|
||||
} else {
|
||||
|
||||
@@ -47,7 +47,7 @@ function put_file($match) {
|
||||
// check function definition in drivers
|
||||
if ($vendor != "mysql") {
|
||||
preg_match_all(
|
||||
'~\bfunction ([^(]+)~',
|
||||
'~\bfunction (?!alter_table|drop_tables|truncate_tables)([^(]+)~', // used for feature detection
|
||||
preg_replace('~class Driver.*\n\t}~sU', '', file_get_contents(__DIR__ . "/adminer/drivers/mysql.inc.php")),
|
||||
$matches
|
||||
); //! respect context (extension, class)
|
||||
@@ -98,7 +98,7 @@ function put_file($match) {
|
||||
echo "lang() not found\n";
|
||||
}
|
||||
} else {
|
||||
$return = preg_replace('~// not used in a single language version from here\n.*~s', '', $return);
|
||||
$return = preg_replace('~// not used in a single language version from here.*~s', '', $return);
|
||||
$return = preg_replace_callback('~(\$pos = (.+\n).+;)~sU', function ($match) {
|
||||
return "\$pos = $match[2]\t\t\t: " . (preg_match("~'$_SESSION[lang]'.* \\? (.+)\n~U", $match[1], $match2) ? $match2[1] : "1") . "\n\t\t);";
|
||||
}, $return);
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -f adminer*.php editor*.php",
|
||||
"compile": "@php compile.php"
|
||||
"check": [
|
||||
"phpcs",
|
||||
"phpstan analyse -c phpstan.neon"
|
||||
],
|
||||
"compile": "@php compile.php",
|
||||
"clean": "rm -f adminer*.php editor*.php"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ table#table thead .checked th {
|
||||
padding: 3rem 0 1.5rem;
|
||||
}
|
||||
|
||||
#menu p, #logins, #tables {
|
||||
#menu p, #tables {
|
||||
padding: .8em 0em 1.2rem;
|
||||
}
|
||||
|
||||
@@ -237,6 +237,14 @@ table#table thead .checked th {
|
||||
background: initial;
|
||||
}
|
||||
|
||||
#logins ul {
|
||||
padding: 3rem 0 1rem;
|
||||
}
|
||||
|
||||
#logins ul li {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Elements
|
||||
*/
|
||||
|
||||
@@ -43,8 +43,8 @@ function send_mail(string $email, string $subject, string $message, string $from
|
||||
}
|
||||
|
||||
/** Check whether the column looks like boolean
|
||||
* @param Field $field single field returned from fields()
|
||||
* @param array{type: string, length?: string} $field single field returned from fields()
|
||||
*/
|
||||
function like_bool(array $field): bool {
|
||||
return preg_match("~bool|(tinyint|bit)\\(1\\)~", $field["full_type"]);
|
||||
return $field["type"] == "bool" || (preg_match('~bit|tinyint~', $field["type"]) && $field["length"] == 1);
|
||||
}
|
||||
|
||||
@@ -53,13 +53,3 @@ function whisperClick(event) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Add new attachment field
|
||||
* @this HTMLInputElement
|
||||
*/
|
||||
function emailFileChange() {
|
||||
const el = this.cloneNode(true);
|
||||
this.onchange = () => { };
|
||||
el.value = '';
|
||||
this.parentNode.appendChild(el);
|
||||
}
|
||||
|
||||
2
externals/jush
vendored
2
externals/jush
vendored
Submodule externals/jush updated: c6618a3016...66b81c4444
@@ -10,7 +10,6 @@ parameters:
|
||||
|
||||
# not real problems
|
||||
- identifier: include.fileNotFound # includes in include/ relative from index.php
|
||||
- identifier: includeOnce.fileNotFound # ./adminer-plugins.php
|
||||
- "~^Function (set_magic_quotes_runtime|mysql_)~" # PHP < 7 functions
|
||||
- "~an unknown class OCI-?Lob~" # this looks like PHPStan bug
|
||||
- "~^Variable \\$error might not be defined~" # declared in bootstrap.inc.php
|
||||
@@ -23,6 +22,7 @@ parameters:
|
||||
- "~expects bool~" # truthy values
|
||||
- "~fread expects int<1, max>, 100000~" # 1e6
|
||||
- "~'strlen' given~" # used as a bool callback
|
||||
- "~between int<70100~" # PHP_VERSION_ID check
|
||||
|
||||
-
|
||||
message: "~ type specified~" # duplicate functions and methods
|
||||
|
||||
@@ -23,5 +23,6 @@ class AdminerDotJs extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wczytuj adminer.js'),
|
||||
'ro' => array('' => 'Încarcă adminer.js'),
|
||||
'ja' => array('' => 'adminer.js を読込み'),
|
||||
'hr' => array('' => 'Učitava adminer.js'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,5 +75,6 @@ ORDER BY s.ordinal_position", null, "") as $row
|
||||
'de' => array('' => 'Links zu Tabellen anzeigen die auf die aktuelle Zeile verweisen, wie im Adminer Editor'),
|
||||
'ja' => array('' => 'Adminer Editor と同様に、カレント行を参照しているテーブルへのリンクを表示'),
|
||||
'pl' => array('' => 'Wyświetlaj linki do tabel odnoszących się do bieżącego wiersza, tak samo jak w Edytorze administratora'),
|
||||
'hr' => array('' => 'Prikazuje veze na tablice koje referenciraju trenutni redak, kao u Adminer Editoru'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,5 +36,6 @@ onbeforeunload = () => editChanged;
|
||||
'de' => array('' => 'Zeigt eine Bestätigung an bevor die Seite neu geladen wird, wenn ein Formularfeld geändert wurde'),
|
||||
'ja' => array('' => 'フォームの列が変更された時、ページを再読込みする前に確認を表示'),
|
||||
'pl' => array('' => 'Wyświetlaj potwierdzenie przed rozładowaniem strony, jeśli pole formularza zostało zmienione'),
|
||||
'hr' => array('' => 'Prikazuje potvrdu prije napuštanja stranice ako je polje obrasca promijenjeno'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -96,5 +96,14 @@ class AdminerConfig extends Adminer\Plugin {
|
||||
'Use %s if exists' => 'あれば %s を使う',
|
||||
'Use builtin design' => '組込みのデザインを使う',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Nikola Radovanović - cobisimo@gmail.com',
|
||||
'Configuration saved.' => 'Konfiguracija je spremljena.',
|
||||
'Configuration' => 'Konfiguracija',
|
||||
'Only some plugins support configuration, e.g. %s.' => 'Samo neki dodaci podržavaju konfiguraciju, npr. %s.',
|
||||
'Use %s if exists' => 'Koristi %s ako postoji',
|
||||
'Use builtin design' => 'Koristi ugrađeni dizajn',
|
||||
'Design' => 'Dizajn',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,5 +50,6 @@ if (saved) {
|
||||
'de' => array('' => 'Umschalten zwischen hellem und dunklem Design erlauben'),
|
||||
'ja' => array('' => 'ダークモードへの切替え'),
|
||||
'pl' => array('' => 'Zezwalaj na przełączanie trybu jasnego i ciemnego'),
|
||||
'hr' => array('' => 'Omogućuje prebacivanje između svijetlog i tamnog izgleda'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@ class AdminerDatabaseHide extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Ukryj niektóre bazy danych w interfejsie – tylko po to, aby ulepszyć motyw, a nie wtyczkę zabezpieczającą'),
|
||||
'ro' => array('' => 'Ascundeți unele baze de date din interfață - doar pentru a îmbunătăți designul, nu un plugin de securitate'),
|
||||
'ja' => array('' => '一部データベースを UI 上で表示禁止 (デザイン的な効果のみでセキュリティ的には効果なし)'),
|
||||
'hr' => array('' => 'Sakriva neke baze podataka iz sučelja – samo radi poboljšanja izgleda, nije sigurnosni dodatak'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class AdminerDesigns extends Adminer\Plugin {
|
||||
$this->designs = $designs;
|
||||
}
|
||||
|
||||
function headers() {
|
||||
function afterConnect() {
|
||||
if (isset($_POST["design"]) && Adminer\verify_token()) {
|
||||
Adminer\restart_session();
|
||||
$_SESSION["design"] = $_POST["design"];
|
||||
@@ -49,5 +49,6 @@ class AdminerDesigns extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zezwalaj na przełączanie motywów'),
|
||||
'ro' => array('' => 'Permiteți comutarea designurilor'),
|
||||
'ja' => array('' => 'テーマ設定を有効化'),
|
||||
'hr' => array('' => 'Omogućuje promjenu dizajna'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,14 +15,17 @@ if (isset($_GET["clickhouse"])) {
|
||||
function rootQuery($db, $query) {
|
||||
$file = @file_get_contents("$this->url/?database=$db", false, stream_context_create(array('http' => array(
|
||||
'method' => 'POST',
|
||||
'content' => $this->isQuerySelectLike($query) ? "$query FORMAT JSONCompact" : $query,
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $query,
|
||||
'header' => array(
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'X-ClickHouse-Format: JSONCompact',
|
||||
),
|
||||
'ignore_errors' => 1,
|
||||
'follow_location' => 0,
|
||||
'max_redirects' => 0,
|
||||
))));
|
||||
|
||||
if ($file === false || !preg_match('~^HTTP/[0-9.]+ 2~i', $http_response_header[0])) {
|
||||
if ($file === false || preg_match('~^HTTP/[0-9.]+ 403~i', $http_response_header[0])) {
|
||||
$this->error = lang('Invalid credentials.');
|
||||
return false;
|
||||
}
|
||||
@@ -45,11 +48,18 @@ if (isset($_GET["clickhouse"])) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 400 == Syntax error
|
||||
// 404 == Unknown expression identifier
|
||||
// 500 == Column 'x' is not under aggregate function and not in GROUP BY keys
|
||||
if (preg_match('~^HTTP/[0-9.]+ [45]~i', $http_response_header[0])) {
|
||||
$this->error = $return['exception'];
|
||||
return false;
|
||||
}
|
||||
return new Result($return);
|
||||
}
|
||||
|
||||
function isQuerySelectLike($query) {
|
||||
return (bool) preg_match('~^(select|show)~i', $query);
|
||||
return (bool) preg_match('~^\s*(select|show|with)~i', $query);
|
||||
}
|
||||
|
||||
function query($query, $unbuffered = false) {
|
||||
@@ -75,7 +85,7 @@ if (isset($_GET["clickhouse"])) {
|
||||
|
||||
class Result {
|
||||
public $num_rows, $columns, $meta;
|
||||
private $rows, $offset = 0;
|
||||
private $rows = array(), $offset = 0;
|
||||
|
||||
function __construct($result) {
|
||||
foreach ($result['data'] as $item) {
|
||||
@@ -243,7 +253,7 @@ if (isset($_GET["clickhouse"])) {
|
||||
}
|
||||
|
||||
function limit($query, $where, $limit, $offset = 0, $separator = " ") {
|
||||
return " $query$where" . ($limit ? $separator . "LIMIT $limit" . ($offset ? ", $offset" : "") : "");
|
||||
return " $query$where" . ($limit ? $separator . "LIMIT " . ($offset ? "$offset, " : "") . $limit : "");
|
||||
}
|
||||
|
||||
function limit1($table, $query, $where, $separator = "\n") {
|
||||
@@ -274,7 +284,7 @@ if (isset($_GET["clickhouse"])) {
|
||||
|
||||
function table_status($name = "", $fast = false) {
|
||||
$return = array();
|
||||
$tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q(connection()->_db));
|
||||
$tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q(connection()->_db) . ($name != "" ? " AND name = " . q($name) : ""));
|
||||
foreach ($tables as $table) {
|
||||
$return[$table['name']] = array(
|
||||
'Name' => $table['name'],
|
||||
|
||||
@@ -409,7 +409,7 @@ if (isset($_GET["elastic"])) {
|
||||
}
|
||||
|
||||
function view(string $name): array {
|
||||
$return = connection()->rootQuery("_alias/" . urlencode($name) . "");
|
||||
$return = connection()->rootQuery("_alias/" . urlencode($name));
|
||||
return array("select" => implode("\n", array_keys($return)));
|
||||
}
|
||||
|
||||
|
||||
459
plugins/drivers/igdb.php
Normal file
459
plugins/drivers/igdb.php
Normal file
@@ -0,0 +1,459 @@
|
||||
<?php
|
||||
/** Driver for https://api-docs.igdb.com/
|
||||
* @link https://demo.adminer.org/igdb/?igdb=IGDB&db=api
|
||||
* username: your Client-ID
|
||||
* password: your access token from https://id.twitch.tv/oauth2/token
|
||||
* @link https://www.adminer.org/static/plugins/igdb.png
|
||||
*/
|
||||
|
||||
namespace Adminer;
|
||||
|
||||
add_driver("igdb", "APICalypse");
|
||||
|
||||
if (isset($_GET["igdb"])) {
|
||||
define('Adminer\DRIVER', "igdb");
|
||||
|
||||
class Db extends SqlDb {
|
||||
public $extension = "json";
|
||||
public $server_info = "v4";
|
||||
private $username;
|
||||
private $password;
|
||||
|
||||
function attach($server, $username, $password): string {
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
return '';
|
||||
}
|
||||
|
||||
function select_db($database) {
|
||||
return ($database == "api");
|
||||
}
|
||||
|
||||
function request($endpoint, $query, $method = 'POST') {
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => $method,
|
||||
'header' => array(
|
||||
"Content-Type: text/plain",
|
||||
"Client-ID: $this->username",
|
||||
"Authorization: Bearer $this->password",
|
||||
),
|
||||
'content' => $query,
|
||||
'ignore_errors' => true,
|
||||
)));
|
||||
$response = file_get_contents("https://api.igdb.com/v4/$endpoint", false, $context);
|
||||
$return = json_decode($response, true);
|
||||
if ($http_response_header[0] != 'HTTP/1.1 200 OK') {
|
||||
if (is_array($return)) {
|
||||
foreach (is_array($return[0]) ? $return : array($return) as $rows) {
|
||||
foreach ($rows as $key => $val) {
|
||||
$this->error .= '<b>' . h($key) . ':</b> ' . (is_url($val) ? '<a href="' . h($val) . '"' . target_blank() . '>' . h($val) . '</a>' : h($val)) . '<br>';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->error = htmlspecialchars(strip_tags($response), 0, null, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function query($query, $unbuffered = false) {
|
||||
if (preg_match('~^SELECT COUNT\(\*\) FROM (\w+)( WHERE ((MATCH \(search\) AGAINST \((.+)\))|.+))?$~', $query, $match)) {
|
||||
return new Result(array($match[1] == 'dumps' ? array('count' => 50) : $this->request("$match[1]/count", ($match[5] ? 'search "' . addcslashes($match[5], '\\"') . '";'
|
||||
: ($match[3] ? 'where ' . str_replace(' AND ', ' & ', $match[3]) . ';'
|
||||
: ''
|
||||
)))));
|
||||
}
|
||||
if (preg_match('~^\s*(GET|POST|DELETE)\s+([\w/?=]+)\s*;\s*(.*)$~s', $query, $match)) {
|
||||
$endpoint = $match[2];
|
||||
$response = $this->request($endpoint, $match[3], $match[1]);
|
||||
if ($response === false) {
|
||||
return $response;
|
||||
}
|
||||
$return = new Result(is_array($response[0]) ? $response : array($response));
|
||||
$return->table = $endpoint;
|
||||
if ($endpoint == 'multiquery') {
|
||||
$return->results = $response;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
$this->error = "Syntax:<br>POST <endpoint>; fields ...;";
|
||||
return false;
|
||||
}
|
||||
|
||||
function store_result() {
|
||||
if ($this->multi && ($result = current($this->multi->results))) {
|
||||
echo "<h3>" . h($result['name']) . "</h3>\n";
|
||||
$this->multi->__construct($result['count'] ? array(array('count' => $result['count'])) : $result['result']);
|
||||
}
|
||||
return $this->multi;
|
||||
}
|
||||
|
||||
function next_result(): bool {
|
||||
return $this->multi && next($this->multi->results);
|
||||
}
|
||||
|
||||
function quote($string): string {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
class Result {
|
||||
public $num_rows;
|
||||
public $table;
|
||||
public $results = array();
|
||||
private $result;
|
||||
private $fields;
|
||||
|
||||
function __construct($result) {
|
||||
$keys = array();
|
||||
foreach ($result as $i => $row) {
|
||||
foreach ($row as $key => $val) {
|
||||
$keys[$key] = null;
|
||||
if (is_array($val) && is_int($val[0])) {
|
||||
$result[$i][$key] = "(" . implode(",", $val) . ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($result as $i => $row) {
|
||||
$result[$i] = array_merge($keys, $row);
|
||||
}
|
||||
$this->result = $result;
|
||||
$this->num_rows = count($result);
|
||||
$this->fields = array_keys(idx($result, 0, array()));
|
||||
}
|
||||
|
||||
function fetch_assoc() {
|
||||
$row = current($this->result);
|
||||
next($this->result);
|
||||
return $row;
|
||||
}
|
||||
|
||||
function fetch_row() {
|
||||
$row = $this->fetch_assoc();
|
||||
return ($row ? array_values($row) : false);
|
||||
}
|
||||
|
||||
function fetch_field(): \stdClass {
|
||||
$field = current($this->fields);
|
||||
next($this->fields);
|
||||
return ($field != '' ? (object) array('name' => $field, 'type' => 15, 'charsetnr' => 0, 'orgtable' => $this->table) : false);
|
||||
}
|
||||
}
|
||||
|
||||
class Driver extends SqlDriver {
|
||||
static $extensions = array("json");
|
||||
static $jush = "igdb";
|
||||
private static $docsFilename = __DIR__ . DIRECTORY_SEPARATOR . 'igdb-api.html';
|
||||
|
||||
public $delimiter = ";;";
|
||||
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~");
|
||||
|
||||
public $tables = array();
|
||||
public $links = array();
|
||||
public $fields = array();
|
||||
public $foreignKeys = array();
|
||||
public $foundRows = null;
|
||||
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
if (!file_exists(self::$docsFilename)) {
|
||||
return "Download https://api-docs.igdb.com/ and save it as " . self::$docsFilename; // copy() doesn't work - bot protection
|
||||
}
|
||||
return parent::connect($server, $username, $password);
|
||||
}
|
||||
|
||||
function __construct($connection) {
|
||||
parent::__construct($connection);
|
||||
libxml_use_internal_errors(true);
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTMLFile(self::$docsFilename);
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$els = $xpath->query('//div[@class="content"]/*');
|
||||
$link = '';
|
||||
foreach ($els as $i => $el) {
|
||||
if ($el->tagName == 'h2') {
|
||||
$link = $el->getAttribute('id');
|
||||
}
|
||||
if ($el->nodeValue == 'Request Path') {
|
||||
$table = preg_replace('~^https://api.igdb.com/v4/~', '', $els[$i+1]->firstElementChild->nodeValue);
|
||||
$comment = $els[$i-1]->tagName == 'p' ? $els[$i-1]->nodeValue : '';
|
||||
if (preg_match('~^DEPRECATED!~', $comment)) {
|
||||
continue;
|
||||
}
|
||||
$this->fields[$table]['id'] = array('full_type' => 'bigint', 'comment' => '');
|
||||
$this->links[$link] = $table;
|
||||
$this->tables[$table] = array('Name' => $table, 'Engine' => 'endpoint', 'Comment' => $comment);
|
||||
foreach ($xpath->query('tbody/tr', $els[$i+2]) as $tr) {
|
||||
$tds = $xpath->query('td', $tr);
|
||||
$field = $tds[0]->nodeValue;
|
||||
$comment = $tds[2]->nodeValue;
|
||||
if ($field != 'checksum' && $field != 'content_descriptions' && !preg_match('~^DEPRECATED!~', $comment)) {
|
||||
$this->fields[$table][$field] = array(
|
||||
'full_type' => str_replace(' ', ' ', $tds[1]->nodeValue),
|
||||
'comment' => str_replace(' ', ' ', $comment),
|
||||
);
|
||||
$ref = $xpath->query('a/@href', $tds[1]);
|
||||
if (count($ref) && !in_array($ref[0]->value, array('#game-version-feature-enums', '#tag-numbers'))) {
|
||||
$this->foreignKeys[$table][$field] = substr($ref[0]->value, 1);
|
||||
} elseif ($field === 'game_id') { // game_time_to_beats, popularity_primitives
|
||||
$this->foreignKeys[$table][$field] = 'game';
|
||||
}
|
||||
}
|
||||
}
|
||||
uksort($this->fields[$table], function ($a, $b) use ($table) {
|
||||
return (($b == 'id') - ($a == 'id'))
|
||||
?: (($b == 'name') - ($a == 'name'))
|
||||
?: (($a == 'updated_at') - ($b == 'updated_at'))
|
||||
?: (($a == 'created_at') - ($b == 'created_at'))
|
||||
?: (!idx($this->foreignKeys[$table], $b) - !idx($this->foreignKeys[$table], $a))
|
||||
?: ($a < $b ? -1 : 1)
|
||||
;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$this->tables['dumps'] = array(
|
||||
'Name' => 'dumps',
|
||||
'Engine' => 'dumps',
|
||||
'Comment' => 'Daily updated CSV Data Dumps which can be used to kick start your projects or keep your data up to date (within 24 hours)',
|
||||
);
|
||||
$this->links['data-dumps'] = 'dumps';
|
||||
$this->fields['dumps'] = array(
|
||||
's3_url' => array('comment' => 'The download Url is a presigned S3 url that is valid for 5 minutes'),
|
||||
'endpoint' => array(),
|
||||
'file_name' => array(),
|
||||
'size_bytes' => array(),
|
||||
'updated_at' => array('full_type' => 'datetime'),
|
||||
'schema_version' => array('full_type' => 'datetime', 'comment' => 'Will change when the schema changes'),
|
||||
'schema' => array('comment' => 'Reflects the current data structure and data type that the Dump is using'),
|
||||
);
|
||||
|
||||
$this->tables['webhooks'] = array(
|
||||
'Name' => 'webhooks',
|
||||
'Engine' => 'webhooks',
|
||||
'Comment' => 'Webhooks allow us to push data to you when it is added, updated, or deleted',
|
||||
);
|
||||
$this->links['webhooks'] = 'webhooks';
|
||||
$this->fields['webhooks'] = array(
|
||||
'endpoint' => array(
|
||||
'full_type' => 'String',
|
||||
'comment' => 'Specify what type of data you want from your webhook',
|
||||
'privileges' => array('insert' => 1),
|
||||
),
|
||||
'id' => array('comment' => 'A unique ID for the webhook'),
|
||||
'url' => array(
|
||||
'full_type' => 'String',
|
||||
'length' => '100',
|
||||
'comment' => 'Your prepared url that is ready to accept data from us',
|
||||
'privileges' => array('select' => 1, 'insert' => 1),
|
||||
),
|
||||
'method' => array(
|
||||
'full_type' => 'enum',
|
||||
'length' => "('create','delete','update')",
|
||||
'comment' => 'The type of data you are expecting to your url, there are three types of methods',
|
||||
'privileges' => array('insert' => 1),
|
||||
),
|
||||
'category' => array('comment' => 'Based on the endpoint you chose'),
|
||||
'sub_category' => array('comment' => 'Based on your method (can be 0, 1, 2)'),
|
||||
'active' => array('comment' => 'Is the webhook currently active'),
|
||||
'api_key' => array('comment' => 'Displays the api key the webhook is connected to'),
|
||||
'secret' => array(
|
||||
'full_type' => 'String',
|
||||
'comment' => 'Your “secret” password for your webhook',
|
||||
'privileges' => array('select' => 1, 'insert' => 1),
|
||||
),
|
||||
'created_at' => array('comment' => 'Created at date'),
|
||||
'updated_at' => array('comment' => 'Updated at date'),
|
||||
);
|
||||
}
|
||||
|
||||
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
|
||||
$query = '';
|
||||
$search = preg_match('~^MATCH \(search\) AGAINST \((.+)\)$~', $where[0], $match);
|
||||
if ($search) {
|
||||
$query = 'search "' . addcslashes($match[1], '\\"') . "\";\n";
|
||||
unset($where[0]);
|
||||
}
|
||||
foreach ($where as $i => $val) {
|
||||
$where[$i] = str_replace(' OR ', ' | ', $val);
|
||||
}
|
||||
$columns = ($select != array('*') ? $select : array_keys($this->fields[$table]));
|
||||
$common = ($where ? "\nwhere " . implode(" & ", $where) . ";" : "");
|
||||
$method = ($table == 'webhooks' || $table == 'dumps' ? 'GET' : 'POST');
|
||||
if ($method == 'POST') {
|
||||
$query .= "fields " . implode(",", $select) . ";"
|
||||
. ($select == array('*') ? "\nexclude checksum;" : "")
|
||||
. $common
|
||||
. ($order ? "\nsort " . strtolower(implode(",", $order)) . ";" : "")
|
||||
. "\nlimit $limit;"
|
||||
. ($page ? "\noffset " . ($page * $limit) . ";" : "")
|
||||
;
|
||||
}
|
||||
$start = microtime(true);
|
||||
$multi = (!$search && $method == 'POST' && array_key_exists($table, driver()->tables));
|
||||
$realQuery = str_replace("*;\nexclude checksum", implode(',', $columns), $query); // exclude deprecated columns
|
||||
$return = ($multi
|
||||
? $this->conn->request('multiquery', "query $table \"result\" { $realQuery };\nquery $table/count \"count\" { $common };")
|
||||
: $this->conn->request(($method == 'GET' && $where ? "$table/" . reset($_GET["where"]) : $table), $realQuery, $method)
|
||||
);
|
||||
if ($print) {
|
||||
echo adminer()->selectQuery("$method $table;\n$query", $start);
|
||||
}
|
||||
if ($return === false) {
|
||||
return $return;
|
||||
}
|
||||
$this->foundRows = ($multi ? $return[1]['count'] : null);
|
||||
$return = ($multi ? $return[0]['result'] : $return);
|
||||
if ($table == 'dumps' && $where) {
|
||||
$return = array($return);
|
||||
} elseif ($return && $method == 'POST') {
|
||||
$return[0] = array_merge(array_fill_keys($columns, null), $return[0]);
|
||||
}
|
||||
return new Result($return);
|
||||
}
|
||||
|
||||
function insert($table, $set) {
|
||||
$content = array();
|
||||
foreach ($set as $key => $val) {
|
||||
if ($key != 'endpoint') {
|
||||
$content[] = urlencode($key) . '=' . urlencode($val);
|
||||
}
|
||||
}
|
||||
return queries("POST $set[endpoint]/$table; " . implode('&', $content));
|
||||
}
|
||||
|
||||
function delete($table, $queryWhere, $limit = 0) {
|
||||
preg_match_all('~\bid = (\d+)~', $queryWhere, $matches);
|
||||
$this->conn->affected_rows = 0;
|
||||
foreach ($matches[1] as $id) {
|
||||
$result = queries("DELETE $table/$id;");
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
$row = $result->fetch_row();
|
||||
if (!$row[0]) {
|
||||
$this->conn->error = "ID $id not found.";
|
||||
return false;
|
||||
}
|
||||
$this->conn->affected_rows++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function value($val, $field): ?string {
|
||||
return ($val && in_array($field['full_type'], array('Unix Time Stamp', 'datetime')) ? str_replace(' 00:00:00', '', gmdate('Y-m-d H:i:s', $val)) : $val);
|
||||
}
|
||||
|
||||
function tableHelp($name, $is_view = false) {
|
||||
return strtolower("https://api-docs.igdb.com/#" . array_search($name, $this->links));
|
||||
}
|
||||
}
|
||||
|
||||
function logged_user() {
|
||||
return $_GET["username"];
|
||||
}
|
||||
|
||||
function get_databases($flush) {
|
||||
return array("api");
|
||||
}
|
||||
|
||||
function collations() {
|
||||
return array();
|
||||
}
|
||||
|
||||
function db_collation($db, $collations) {
|
||||
}
|
||||
|
||||
function information_schema($db) {
|
||||
}
|
||||
|
||||
function indexes($table, $connection2 = null) {
|
||||
$return = array(array('type' => 'PRIMARY', 'columns' => array($table == 'dumps' ? 'endpoint' : 'id')));
|
||||
if (in_array($table, array('characters', 'collections', 'games', 'platforms', 'themes'))) { // https://api-docs.igdb.com/#search-1
|
||||
$return[] = array("type" => "FULLTEXT", "columns" => array("search"));
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function fields($table) {
|
||||
$return = array();
|
||||
foreach (driver()->fields[$table] ?: array() as $key => $val) {
|
||||
$type = strtolower(preg_replace('~ .*~', '', $val['full_type']));
|
||||
$return[$key] = $val + array(
|
||||
"field" => $key,
|
||||
"type" => ($type == 'reference' ? 'int' : $type), // align right reference columns
|
||||
"privileges" => array("select" => 1) + ($table == 'webhooks' || $table == 'dumps' ? array() : array("where" => 1, "order" => 1)),
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function convert_field($field) {
|
||||
}
|
||||
|
||||
function unconvert_field($field, $return) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
function limit($query, $where, $limit, $offset = 0, $separator = " ") {
|
||||
return $query;
|
||||
}
|
||||
|
||||
function idf_escape($idf) {
|
||||
return $idf;
|
||||
}
|
||||
|
||||
function table($idf) {
|
||||
return idf_escape($idf);
|
||||
}
|
||||
|
||||
function foreign_keys($table) {
|
||||
$return = array();
|
||||
foreach (driver()->foreignKeys[$table] ?: array() as $key => $val) {
|
||||
$return[] = array(
|
||||
'table' => driver()->links[$val],
|
||||
'source' => array($key),
|
||||
'target' => array('id'),
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function tables_list() {
|
||||
return array_fill_keys(array_keys(table_status()), 'table');
|
||||
}
|
||||
|
||||
function table_status($name = "", $fast = false) {
|
||||
$tables = driver()->tables;
|
||||
return ($name != '' ? ($tables[$name] ? array($name => $tables[$name]) : array()) : $tables);
|
||||
}
|
||||
|
||||
function count_tables($databases) {
|
||||
return array(reset($databases) => count(tables_list()));
|
||||
}
|
||||
|
||||
function error() {
|
||||
return connection()->error;
|
||||
}
|
||||
|
||||
function is_view($table_status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function found_rows($table_status, $where) {
|
||||
return driver()->foundRows;
|
||||
}
|
||||
|
||||
function fk_support($table_status) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function last_id($result): string {
|
||||
$row = $result->fetch_assoc();
|
||||
return (string) $row['id'];
|
||||
}
|
||||
|
||||
function support($feature) {
|
||||
return in_array($feature, array('columns', 'comment', 'sql', 'table'));
|
||||
}
|
||||
}
|
||||
@@ -174,5 +174,6 @@ DROP PROCEDURE adminer_alter;
|
||||
'pl' => array('' => 'Eksportuje jedną bazę danych (np. programistyczną), aby można ją było zsynchronizować z inną bazą danych (np. produkcyjną)'),
|
||||
'ro' => array('' => 'Exportați o bază de date (de exemplu, development) astfel încât să poată fi sincronizată cu o altă bază de date (de exemplu, de producție)'),
|
||||
'ja' => array('' => 'データベース (開発用など) をエクスポートし、別のデータベース (本番用など) と同期'),
|
||||
'hr' => array('' => 'Izvozi bazu podataka (npr. razvojnu) tako da se može sinkronizirati s drugom bazom (npr. produkcijskom)'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,5 +43,6 @@ class AdminerDumpBz2 extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zrzuć do formatu Bzip2'),
|
||||
'ro' => array('' => 'Dump în format Bzip2'),
|
||||
'ja' => array('' => 'Bzip2 形式でエクスポート'),
|
||||
'hr' => array('' => 'Izvoz u Bzip2 format'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ class AdminerDumpDate extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Dołącz bieżącą datę i godzinę do nazwy pliku eksportu'),
|
||||
'ro' => array('' => 'Includeți data și ora curentă în numele fișierului de export'),
|
||||
'ja' => array('' => 'エクスポートファイル名に現在日時を含める'),
|
||||
'hr' => array('' => 'Dodaje trenutni datum i vrijeme u naziv datoteke izvoza'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,5 +64,6 @@ class AdminerDumpJson extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zrzuć do formatu JSON'),
|
||||
'ro' => array('' => 'Dump în format JSON'),
|
||||
'ja' => array('' => 'JSON 形式でエクスポート'),
|
||||
'hr' => array('' => 'Izvoz u JSON format'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@ class AdminerDumpPhp extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zrzucaj do formatu PHP'),
|
||||
'ro' => array('' => 'Dump în format PHP'),
|
||||
'ja' => array('' => 'PHP 形式でエクスポート'),
|
||||
'hr' => array('' => 'Izvoz u PHP format'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,5 +58,6 @@ class AdminerDumpXml extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zrzut do formatu XML w strukturze <database name=""><table name=""><column name="">value'),
|
||||
'ro' => array('' => 'Dump în format XML în structura <database name=""><table name=""><column name="">value'),
|
||||
'ja' => array('' => '構造化 XML 形式でエクスポート <database name=""><table name=""><column name="">value'),
|
||||
'hr' => array('' => 'Izvoz u XML format u strukturi <database name=""><table name=""><column name="">vrijednost'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,5 +47,6 @@ class AdminerDumpZip extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zrzuć do formatu ZIP'),
|
||||
'ro' => array('' => 'Dump în format ZIP'),
|
||||
'ja' => array('' => 'ZIP 形式でエクスポート'),
|
||||
'hr' => array('' => 'Izvoz u ZIP format'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,5 +57,6 @@ class AdminerEditCalendar extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wyświetl interfejs jQuery Timepicker dla każdego pola daty i godziny'),
|
||||
'ro' => array('' => 'Afișați jQuery UI Timepicker pentru fiecare câmp de dată și dată-timp'),
|
||||
'ja' => array('' => '各日時列に jQuery UI の Timepicker を表示'),
|
||||
'hr' => array('' => 'Prikazuje jQuery UI Timepicker za svako polje datuma i datuma-vremena'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,5 +46,6 @@ class AdminerEditForeign extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wybierz klucz obcy w formularzu edycji'),
|
||||
'ro' => array('' => 'Selectați cheia străină în formularul de editare'),
|
||||
'ja' => array('' => '外部キーを編集フォームで選択'),
|
||||
'hr' => array('' => 'Odabir stranog ključa u obrascu za uređivanje'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,5 +20,6 @@ class AdminerEditTextarea extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Użyj <textarea> dla char i varchar'),
|
||||
'ro' => array('' => 'Utilizați <textarea> pentru char și varchar'),
|
||||
'ja' => array('' => 'char や varchar に <textarea> を使用'),
|
||||
'hr' => array('' => 'Koristi <textarea> za char i varchar polja'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,5 +43,6 @@ class AdminerEditorSetup extends Adminer\Plugin {
|
||||
'de' => array('' => 'Treiber, Server und Datenbank für die Verwendung mit Adminer Editor einrichten'),
|
||||
'ja' => array('' => 'Adminer Editor で使用するドライバ、サーバ、データベースを設定'),
|
||||
'pl' => array('' => 'Konfiguruj sterownik, serwer i bazę danych do użycia z Adminer Editorem'),
|
||||
'hr' => array('' => 'Postavlja upravljački program, poslužitelj i bazu podataka za korištenje s Adminer Editorom'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ class AdminerEditorViews extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wyświetlaj widoki w Adminer Editorze'),
|
||||
'ro' => array('' => 'Afișează vizualizări în Adminer Editor'),
|
||||
'ja' => array('' => 'Adminer Editor にビューを表示'),
|
||||
'hr' => array('' => 'Prikazuje poglede u Adminer Editoru'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,5 +61,6 @@ class AdminerEmailTable extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Pobieraj temat i wiadomość e-mail z bazy danych (Adminer Editor)'),
|
||||
'ro' => array('' => 'Obțineți subiectul e-mailului și mesajul din baza de date (Adminer Editor)'),
|
||||
'ja' => array('' => 'メールの件名と本文をデータベースから取得 (Adminer Editor)'),
|
||||
'hr' => array('' => 'Dohvaća predmet i poruku e-pošte iz baze podataka (Adminer Editor)'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,23 +11,23 @@ class AdminerEnumOption extends Adminer\Plugin {
|
||||
function editInput($table, $field, $attrs, $value) {
|
||||
if ($field["type"] == "enum") {
|
||||
$options = array();
|
||||
$selected = $value;
|
||||
$selected = "val-$value";
|
||||
if (isset($_GET["select"])) {
|
||||
$options[-1] = Adminer\lang('original');
|
||||
if ($selected === null) {
|
||||
$selected = -1;
|
||||
$options["orig"] = Adminer\lang('original');
|
||||
if ($value === null) {
|
||||
$selected = "orig";
|
||||
}
|
||||
}
|
||||
if ($field["null"]) {
|
||||
$options[""] = "NULL";
|
||||
if ($selected === null) {
|
||||
$selected = "";
|
||||
$options["null"] = "NULL";
|
||||
if ($value === null) {
|
||||
$selected = "null";
|
||||
}
|
||||
}
|
||||
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
|
||||
foreach ($matches[1] as $val) {
|
||||
$val = stripcslashes(str_replace("''", "'", $val));
|
||||
$options[$val] = $val;
|
||||
$options["val-$val"] = $val;
|
||||
}
|
||||
return "<select$attrs>" . Adminer\optionlist($options, $selected, 1) . "</select>"; // 1 - use keys
|
||||
}
|
||||
@@ -39,5 +39,6 @@ class AdminerEnumOption extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Użyj <select><option> do edycji enum zamiast <input type="radio">'),
|
||||
'ro' => array('' => 'Utilizați <select><option> pentru editarea enum în loc de <input type="radio">'),
|
||||
'ja' => array('' => '列挙型の編集に <input type="radio"> ではなく <select><option> を使用'),
|
||||
'hr' => array('' => 'Koristi <select><option> za uređivanje enum polja umjesto <input type="radio">'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,5 +55,6 @@ class AdminerFileUpload extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Edytuj pola kończące się na "_path" za pomocą <input type="file"> i link do przesłanych plików z wybierz'),
|
||||
'ro' => array('' => 'Modificați câmpurile care se termină cu "_path" prin <input type="file"> și creați un link către fișierele încărcate din select'),
|
||||
'ja' => array('' => '列名が "_path" で終わる列を <input type="file"> で変更し、"選択" からアップロードされたファイルにリンク'),
|
||||
'hr' => array('' => 'Uređuje polja koja završavaju s "_path" putem <input type="file"> i povezuje ih s učitanim datotekama'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -191,5 +191,6 @@ class AdminerForeignSystem extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Połącz tabele systemowe (w bazach danych "mysql" i "information_schema") za pomocą kluczy obcych'),
|
||||
'ro' => array('' => 'Conectați tabelele de sistem (în bazele de date "mysql" și "information_schema") prin chei străine'),
|
||||
'ja' => array('' => 'システムテーブル ("mysql" と "information_schema") を外部キーを用いて接続'),
|
||||
'hr' => array('' => 'Povezuje sistemske tablice (u bazama "mysql" i "information_schema") stranim ključevima'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,5 +30,6 @@ class AdminerFrames extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Zezwalaj na używanie Adminera wewnątrz ramki'),
|
||||
'ro' => array('' => 'Permiteți utilizarea Adminer în interiorul unui cadru'),
|
||||
'ja' => array('' => 'フレーム内での Adminer 利用を許可'),
|
||||
'hr' => array('' => 'Dopušta korištenje Adminera unutar framea'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,5 +107,6 @@ addEventListener('DOMContentLoaded', () => {
|
||||
'de' => array('' => 'CodeMirror 5 verwenden für die Syntaxhervorhebung und <textarea> einschließlich der Überschrift von Schlüsselwörtern und Tabellen'),
|
||||
'ja' => array('' => 'CodeMirror 5 を用い、キーワードやテーブルを含む構文や <textarea> を強調表示'),
|
||||
'pl' => array('' => 'Użyj CodeMirror 5 do podświetlania składni i <textarea>, uwzględniając wcześniejsze wpisywanie słów kluczowych i tabel'),
|
||||
'hr' => array('' => 'Koristi CodeMirror 5 za isticanje sintakse i <textarea>, uključujući dovršavanje ključnih riječi i tablica'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,5 +77,6 @@ addEventListener('DOMContentLoaded', () => {
|
||||
'de' => array('' => 'Monaco-Editor von VS Code verwenden, für die Syntaxhervorhebung und SQL <textarea>'),
|
||||
'ja' => array('' => '構文や <textarea> の強調表示に VS Code の Monaco Editor を使用'),
|
||||
'pl' => array('' => 'Użyj Monaco Editora programu VS Code do podświetlania składni i <textarea> SQL'),
|
||||
'hr' => array('' => 'Koristi Monaco Editor iz VS Code za isticanje sintakse i SQL <textarea>'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,5 +65,6 @@ if (el) {
|
||||
'de' => array('' => 'Prism Code Editor verwenden, für die Syntaxhervorhebung und <textarea>'),
|
||||
'ja' => array('' => '構文や <textarea> の強調表示に Prism Code Editor を使用'),
|
||||
'pl' => array('' => 'Użyj Prism Code Editora do podświetlania składni i <textarea>'),
|
||||
'hr' => array('' => 'Koristi Prism Code Editor za isticanje sintakse i SQL <textarea>'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@ class AdminerJsonColumn extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wyświetl wartości JSON jako tabelę w edycji'),
|
||||
'ro' => array('' => 'Afișează valorile JSON sub formă de tabel în editare'),
|
||||
'ja' => array('' => 'JSON 値をテーブルとして編集画面に表示'),
|
||||
'hr' => array('' => 'Prikazuje JSON vrijednosti kao tablicu u uređivanju'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,5 +42,6 @@ class AdminerLoginIp extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Sprawdzaj adres IP i zezwakaj na puste hasło'),
|
||||
'ro' => array('' => 'Verificați adresa IP și permiteți parola goală'),
|
||||
'ja' => array('' => 'IP アドレスの確認、及び空パスワードの許可'),
|
||||
'hr' => array('' => 'Provjerava IP adresu i dopušta praznu lozinku'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,5 +75,10 @@ class AdminerLoginOtp extends Adminer\Plugin {
|
||||
'ja' => array(
|
||||
'' => 'ログイン時にワンタイムパスワード (二要素認証) が必要',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Jednokratna lozinka (dvofaktorska autentifikacija) potrebna pri prijavi',
|
||||
'One Time Password' => 'Jednokratna lozinka',
|
||||
'Invalid OTP.' => 'Nevažeća jednokratna lozinka.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@ class AdminerLoginPasswordLess extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Włącz logowanie bez hasła'),
|
||||
'ro' => array('' => 'Activați autentificarea fără parolă'),
|
||||
'ja' => array('' => 'パスワードなしのログインを許可'),
|
||||
'hr' => array('' => 'Omogućuje prijavu bez lozinke'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,5 +44,6 @@ class AdminerLoginServers extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wyświetlaj stałą listę serwerów w formularzu logowania'),
|
||||
'ro' => array('' => 'Afișarea unei liste constante de servere în formularul de conectare'),
|
||||
'ja' => array('' => 'ログイン画面に定義済のサーバリストを表示'),
|
||||
'hr' => array('' => 'Prikazuje konstantan popis poslužitelja u obrascu za prijavu'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,5 +28,6 @@ class AdminerLoginSsl extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Połącz się z MySQL, PostgreSQL, MS SQL za pomocą protokołu SSL'),
|
||||
'ro' => array('' => 'Conectați-vă la MySQL, PostgreSQL, MS SQL utilizând SSL'),
|
||||
'ja' => array('' => 'MySQL, PostgreSQL, MS SQL への接続時に SSL を利用'),
|
||||
'hr' => array('' => 'Spajanje na MySQL, PostgreSQL i MS SQL putem SSL-a'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@ class AdminerLoginTable extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Uwierzytelnij użytkownika z tabeli "login"'),
|
||||
'ro' => array('' => 'Autentificați un utilizator din tabelul "login"'),
|
||||
'ja' => array('' => '"login" テーブルによるユーザ認証'),
|
||||
'hr' => array('' => 'Autentificira korisnika prema tablici "login"'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,5 +44,6 @@ class AdminerMasterSlave extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wykonuje zapisy na komputerze głównym i odczyty na komputerze podrzędnym'),
|
||||
'ro' => array('' => 'Executarea scrierilor pe master și a citirilor pe slave'),
|
||||
'ja' => array('' => 'マスタ書込みとスレーブ読込みの有効化'),
|
||||
'hr' => array('' => 'Izvodi pisanje na masteru i čitanje na slaveu'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,5 +112,14 @@ class AdminerMenuLinks extends Adminer\Plugin {
|
||||
'Select data' => 'データ',
|
||||
'Show structure' => '構造',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Prikazuje veze na odabir podataka ili strukturu tablice u izborniku',
|
||||
'Select data' => 'Odaberi podatke',
|
||||
'Show structure' => 'Prikaži strukturu',
|
||||
'Both' => 'Oboje',
|
||||
'Auto (select on select page, structure otherwise)' => 'Automatski (odabir na stranici odabira, inače struktura)',
|
||||
'Menu table links' => 'Veze tablice u izborniku',
|
||||
'select' => 'odaberi',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,5 +37,6 @@ class AdminerPrettyJsonColumn extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Ładnie drukuj wartości JSON w edycji'),
|
||||
'ro' => array('' => 'Afisare frumoasa a valorilor JSON în editare'),
|
||||
'ja' => array('' => '編集時に JSON 文字列を見易く表示'),
|
||||
'hr' => array('' => 'Lijepo prikazuje JSON vrijednosti u uređivanju'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,16 @@ class AdminerRowNumbers extends Adminer\Plugin {
|
||||
}
|
||||
|
||||
function backwardKeysPrint($backwardKeys, $row) {
|
||||
static $n = $_GET["page"] * Adminer\adminer()->selectLimitProcess();
|
||||
static $n;
|
||||
if (!$n) {
|
||||
$n = $_GET["page"] * Adminer\adminer()->selectLimitProcess();
|
||||
}
|
||||
$n++;
|
||||
echo "$n.\n";
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Zobrazí čísla řádek ve výpisu'),
|
||||
'hr' => array('' => 'Prikazuje brojeve redaka u ispisu'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -457,5 +457,14 @@ class AdminerSelectEmail extends Adminer\Plugin {
|
||||
'Attachments' => '附件',
|
||||
'Send' => '发送',
|
||||
'%d e-mail(s) have been sent.' => '%d 封邮件已发送。'),
|
||||
'hr' => array(
|
||||
'' => 'Slanje e-pošte odabranim recima',
|
||||
'E-mail' => 'E-pošta',
|
||||
'From' => 'Od',
|
||||
'Subject' => 'Predmet',
|
||||
'Attachments' => 'Privici',
|
||||
'Send' => 'Pošalji',
|
||||
'%d e-mail(s) have been sent.' => array('%d e-mail je poslan.', '%d e-maila su poslana.', '%d e-mailova je poslano.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,5 +53,6 @@ class AdminerSlugify extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wstępnie wypełnij pole zawierające "_slug" osłabioną wartością poprzedniego pola (JavaScript)'),
|
||||
'ro' => array('' => 'Precompletați câmpul care conține "_slug" cu valoarea slugificată a unui câmp anterior (JavaScript)'),
|
||||
'ja' => array('' => '列名に "_slug" を含む列を、前列の URL 化された値でプレフィル (JavaScript)'),
|
||||
'hr' => array('' => 'Popunjava polje koje sadrži "_slug" slugificiranom vrijednošću prethodnog polja (JavaScript)'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,5 +116,10 @@ geminiText.onkeydown = event => {
|
||||
'Ask Gemini' => 'Gemini に聞く',
|
||||
'Just a sec...' => 'しばらくお待ち下さい...',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Generiranje SQL naredbi pomoću Google Gemini AI',
|
||||
'Ask Gemini' => 'Pitaj Gemini',
|
||||
'Just a sec...' => 'Samo trenutak...',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,5 +42,6 @@ class AdminerSqlLog extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Rejestruj wszystkie zapytania do pliku SQL'),
|
||||
'ro' => array('' => 'Logați toate interogările în fișierul SQL'),
|
||||
'ja' => array('' => '全クエリを SQL ファイルに記録'),
|
||||
'hr' => array('' => 'Bilježi sve upite u SQL datoteku'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@ class AdminerTableIndexesStructure extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Rozszerzona tabela wyników struktury indeksów'),
|
||||
'ro' => array('' => 'Ieșirea expandată a structurii indecsilor tabelului'),
|
||||
'ja' => array('' => 'テーブルのインデックス構造を拡張表示'),
|
||||
'hr' => array('' => 'Prošireni prikaz indeksa tablice'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,5 +44,6 @@ class AdminerTableStructure extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Rozszerzone wyjście struktury tabeli'),
|
||||
'ro' => array('' => 'Ieșirea expandată a structurii tabelei'),
|
||||
'ja' => array('' => 'テーブル構造を拡張表示'),
|
||||
'hr' => array('' => 'Prošireni prikaz strukture tablice'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,5 +85,9 @@ sessionStorage && document.addEventListener('DOMContentLoaded', () => {
|
||||
'ja' => array(
|
||||
'' => 'テーブル一覧をテーブル名でフィルタリング',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Filtriranje tablice prema imenu',
|
||||
'Filter' => 'Filtar',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,5 +50,10 @@ class AdminerTimeout extends Adminer\Plugin {
|
||||
'Queries timeout' => 'Timeout dotazů',
|
||||
'seconds' => 'sekund',
|
||||
),
|
||||
'hr' => array(
|
||||
'' => 'Postavljanje vremenskog ograničenja upita',
|
||||
'Queries timeout' => 'Vremensko ograničenje upita',
|
||||
'seconds' => 'sekundi',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,5 +73,6 @@ qs('#form').onsubmit = () => {
|
||||
'pl' => array('' => 'Edytuj wszystkie pola zawierające "_html" za pomocą edytora HTML TinyMCE i wyświetl kod HTML w wybranych'),
|
||||
'ro' => array('' => 'Editați toate câmpurile care conțin "_html" cu ajutorul editorului HTML TinyMCE și afișați HTML-ul în select'),
|
||||
'ja' => array('' => '列名が "_html" を含む列を TinyMCE の HTML エディタで編集し、編集結果の HTML コードを "選択" 画面に表示'),
|
||||
'hr' => array('' => 'Uređuje sva polja koja sadrže "_html" pomoću HTML editora TinyMCE i prikazuje HTML u ispisu'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,5 +57,6 @@ class AdminerTranslation extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Przetłumacz wszystkie komentarze do tabeli i pól, wartości enum i set z tabeli "translation" (automatycznie wstawia nowe tłumaczenia)'),
|
||||
'ro' => array('' => 'Traduceți toate comentariile tabelelor și câmpurilor, valorile enum și set din tabelul "translation" (inserează automat noi traduceri)'),
|
||||
'ja' => array('' => 'テーブル "translation" を用いてすべてのテーブルや列のコメント、列挙型、セット値を翻訳 (自動的に翻訳文で更新)'),
|
||||
'hr' => array('' => 'Prevodi sve komentare tablica i polja, vrijednosti enum i set iz tablice "translation" (automatski ubacuje nove prijevode)'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,13 +11,12 @@ class AdminerVersionGithub extends Adminer\Plugin {
|
||||
function head($dark = null) {
|
||||
?>
|
||||
<script <?php echo Adminer\nonce(); ?>>
|
||||
verifyVersion = (current, url, token) => {
|
||||
verifyVersion = current => {
|
||||
// dummy value to prevent repeated verifications after AJAX failure
|
||||
cookie('adminer_version=0', 1);
|
||||
ajax('https://api.github.com/repos/vrana/adminer/releases/latest', request => {
|
||||
const response = JSON.parse(request.responseText);
|
||||
const version = response.tag_name.replace(/^v/, '');
|
||||
// we don't save to adminer.version because the response is not signed; also GitHub can handle our volume of requests
|
||||
// we don't display the version here because we don't have version_compare(); design.inc.php will display it on the next load
|
||||
cookie('adminer_version=' + version, 1);
|
||||
}, null, null);
|
||||
@@ -35,5 +34,6 @@ verifyVersion = (current, url, token) => {
|
||||
'de' => array('' => 'Neue Versionen von GitHub verifizieren'),
|
||||
'ja' => array('' => 'GitHub の新版を管理'),
|
||||
'pl' => array('' => 'Weryfikuj nowe wersje z GitHuba'),
|
||||
'hr' => array('' => 'Provjera novih verzija s GitHuba'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ class AdminerVersionNoverify extends Adminer\Plugin {
|
||||
'pl' => array('' => 'Wyłącz sprawdzanie wersji'),
|
||||
'ro' => array('' => 'Dezactivați verificatorul de versiuni'),
|
||||
'ja' => array('' => 'バージョンチェックを無効化'),
|
||||
'hr' => array('' => 'Onemogućuje provjeru novih verzija'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>220</td><td></td></tr>
|
||||
|
||||
@@ -412,8 +412,8 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>virtual</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[virtual]</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>120</td><td></td></tr>
|
||||
|
||||
@@ -282,8 +282,8 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>virtual</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[virtual]</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>120</td><td></td></tr>
|
||||
|
||||
@@ -418,8 +418,8 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>virtual</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[virtual]</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>120</td><td></td></tr>
|
||||
|
||||
@@ -358,7 +358,7 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>220</td><td></td></tr>
|
||||
|
||||
@@ -288,7 +288,7 @@
|
||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||
<tr><td>click</td><td>link=New item</td><td></td></tr>
|
||||
<tr><td>verifyTextNotPresent</td><td>stored</td><td></td></tr>
|
||||
<tr><td>verifyElementNotPresent</td><td>fields[stored]</td><td></td></tr>
|
||||
<tr><td>type</td><td>name=fields[normal]</td><td>20</td></tr>
|
||||
<tr><td>click</td><td>//input[@value='Save']</td><td></td></tr>
|
||||
<tr><td>verifyTextPresent</td><td>220</td><td></td></tr>
|
||||
|
||||
Reference in New Issue
Block a user