mirror of
https://github.com/vrana/adminer.git
synced 2026-03-25 14:00:05 +01:00
Compare commits
274 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
6f339bac6c | ||
|
|
f32f84cf57 | ||
|
|
ce063a64dd | ||
|
|
1d5d441271 | ||
|
|
fd28e64d8c | ||
|
|
3874148064 | ||
|
|
0640326df4 | ||
|
|
16f7080ff2 | ||
|
|
516530485d | ||
|
|
f921dafa61 | ||
|
|
d1831641a9 | ||
|
|
b1c825484d | ||
|
|
6c662e1f96 | ||
|
|
7ef0949539 | ||
|
|
9fc63eb9d0 | ||
|
|
2041d5c6e3 | ||
|
|
fc2ab7de16 | ||
|
|
2db30ba7e2 | ||
|
|
8d6ed3fffa | ||
|
|
9e0db9f1cd | ||
|
|
a979b4be22 | ||
|
|
26319460ef | ||
|
|
b41699624b | ||
|
|
17a0e84718 | ||
|
|
5c5f7d17fe | ||
|
|
4ad7eb0b70 | ||
|
|
b83da41739 | ||
|
|
e551efb031 | ||
|
|
6d0351ec7c | ||
|
|
dcf11d8fc9 | ||
|
|
0de6a057d3 | ||
|
|
18b62aba38 | ||
|
|
10ff74552d | ||
|
|
f5c7ab54f7 | ||
|
|
cc110c448c | ||
|
|
6f766f8c52 | ||
|
|
9a60d158f1 | ||
|
|
6dec0d63b0 | ||
|
|
c9a52cd28c | ||
|
|
9424b7431e | ||
|
|
35b2b969be | ||
|
|
165f1b241c | ||
|
|
4d75f822e9 | ||
|
|
5e9c185596 | ||
|
|
cfd69dfd8c | ||
|
|
1fc5acb389 | ||
|
|
8ca0e36970 | ||
|
|
9e8de24e3d | ||
|
|
4cb09852e3 | ||
|
|
e115dccdae | ||
|
|
1ba410cad1 | ||
|
|
0764a20a19 | ||
|
|
b2c4574325 | ||
|
|
466eceff40 | ||
|
|
27c5f6d21b | ||
|
|
2744538c8c | ||
|
|
41456c9eb7 | ||
|
|
9745c12769 | ||
|
|
f24f72ac51 | ||
|
|
2b52a9b653 | ||
|
|
9ba4b86916 | ||
|
|
1cc3fdf915 | ||
|
|
b960c41d63 | ||
|
|
3ec750ef7e | ||
|
|
737b631dda | ||
|
|
8fc450946c | ||
|
|
e282d6eb89 | ||
|
|
86ffeb2a1e | ||
|
|
31530ba03e | ||
|
|
146d3539d8 | ||
|
|
04068a631e | ||
|
|
4698686232 | ||
|
|
57b6afc8cb | ||
|
|
e589c80f42 | ||
|
|
acfebf1788 | ||
|
|
246c3c489b | ||
|
|
92b95606c1 | ||
|
|
ac8318f387 | ||
|
|
d1c2679acd | ||
|
|
caf6e495dc | ||
|
|
ec5ad85470 | ||
|
|
307fabaf22 | ||
|
|
1862b84612 | ||
|
|
e76be9f890 | ||
|
|
1cfc8451ef | ||
|
|
62a9cf3e3f | ||
|
|
e707c7a5f4 | ||
|
|
c92b127b56 | ||
|
|
cf52c4c0a8 | ||
|
|
5f7fb62803 | ||
|
|
9b0acfa7c9 | ||
|
|
45d61803c4 | ||
|
|
48160f2cd7 | ||
|
|
61f2b370df | ||
|
|
e13910b5c5 | ||
|
|
de86789bfc | ||
|
|
ebcf4feeb2 | ||
|
|
d881f51deb | ||
|
|
b9c39e77fc | ||
|
|
c734deca84 | ||
|
|
1466051402 | ||
|
|
36e55a3f55 | ||
|
|
155668906d | ||
|
|
70ce02a798 | ||
|
|
de3220acc7 | ||
|
|
1030e84904 | ||
|
|
f4ca974623 | ||
|
|
af627e7116 | ||
|
|
0c5bba47da | ||
|
|
e340a2973e | ||
|
|
be2afb49c5 | ||
|
|
c8fa515ed5 | ||
|
|
3dad86d279 | ||
|
|
d9c1ac00f3 | ||
|
|
2a4d2cfb39 | ||
|
|
67a64c8d72 | ||
|
|
d59d6c4075 | ||
|
|
dbec3a1b92 | ||
|
|
a93f0003ae | ||
|
|
29a31c6b9c | ||
|
|
a04823f4c4 | ||
|
|
82c544514d | ||
|
|
7185d7854f | ||
|
|
d845d4b358 | ||
|
|
92ce506243 | ||
|
|
6d6998c3d3 | ||
|
|
45799f4605 | ||
|
|
b999f123c8 | ||
|
|
7642b00877 | ||
|
|
45be56e4e1 | ||
|
|
e1b92f73aa | ||
|
|
63850ebf19 | ||
|
|
49fd96f8b9 | ||
|
|
fcae403f60 | ||
|
|
7dc152b732 | ||
|
|
29f7b2df96 | ||
|
|
3466ab730b | ||
|
|
9235cb8350 | ||
|
|
020285772b | ||
|
|
8238838285 | ||
|
|
a165d4ed81 | ||
|
|
f5b42eae55 | ||
|
|
460a24ea2d | ||
|
|
4646298015 | ||
|
|
cde6b9008c | ||
|
|
8783f4d3ac | ||
|
|
3d2395fc59 | ||
|
|
3a73815ba4 | ||
|
|
d0a2de53ef | ||
|
|
982233d7e5 | ||
|
|
cedfe97f40 | ||
|
|
9beb72edc2 | ||
|
|
9555c96d6a | ||
|
|
a3d0bbba8f | ||
|
|
a9bcde334f | ||
|
|
a735b795b2 | ||
|
|
008cd33058 | ||
|
|
036ce4f1c5 | ||
|
|
5867b0724f | ||
|
|
71f2578af6 | ||
|
|
c809216a56 | ||
|
|
eb43ea3025 | ||
|
|
2ba833409a | ||
|
|
5eaaa498d3 | ||
|
|
746c0a7b0b | ||
|
|
b30526213d | ||
|
|
6819815b88 | ||
|
|
a83626c8af | ||
|
|
d4ddbc0639 | ||
|
|
a6cb91f0d2 | ||
|
|
78d3ce830d | ||
|
|
88099b7dd7 | ||
|
|
8bce359fae | ||
|
|
8ca7066625 | ||
|
|
51bcc2a064 | ||
|
|
588af652d4 | ||
|
|
b0c345f9be | ||
|
|
b2677187f1 | ||
|
|
52ee085ca7 | ||
|
|
00459b302a | ||
|
|
17598c7ab3 | ||
|
|
b489cec651 | ||
|
|
6da8bb670a | ||
|
|
e601a3d8ce | ||
|
|
efde7fcc6c | ||
|
|
91b3526e8d | ||
|
|
a3ddd59015 | ||
|
|
7908d86c9f | ||
|
|
0cb41c63c7 | ||
|
|
50de50571d | ||
|
|
de38cb65b6 | ||
|
|
e6cc8bf91e | ||
|
|
be6cf07d26 | ||
|
|
3f979793f7 | ||
|
|
c8878d1652 | ||
|
|
bf24198e68 | ||
|
|
e33ead15e5 | ||
|
|
1087d55913 | ||
|
|
38bdd0a961 | ||
|
|
3dd040abd1 | ||
|
|
3e455a4787 | ||
|
|
95f14bca56 | ||
|
|
121b77e866 |
15
.github/ISSUE_TEMPLATE/bug_report.md
vendored
15
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,9 +7,14 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Adminer version:** please use latest published or Git
|
||||
**Driver:** e.g. MySQLi
|
||||
**Database version:** e.g. 10.2.12-MariaDB
|
||||
**Adminer version:** <!-- please use latest published or Git -->
|
||||
**Compiled:** single file / single language / source codes / custom compilation
|
||||
**Driver:** <!-- e.g. MySQLi -->
|
||||
**Database version:** <!-- e.g. 10.2.12-MariaDB -->
|
||||
**Plugins used:**
|
||||
|
||||
_Please provide reproducible steps including a SQL dump (with no personal information) if applicable.
|
||||
Also please include a screenshot._
|
||||
<!--
|
||||
Please provide reproducible steps including a SQL dump (with no personal information) if applicable.
|
||||
Also please include a screenshot.
|
||||
Report issues with Adminer Docker image at https://github.com/TimWolla/docker-adminer._
|
||||
-->
|
||||
|
||||
19
.github/workflows/ci.yml
vendored
Normal file
19
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: php-actions/composer@v6
|
||||
- uses: php-actions/phpcs@v1
|
||||
with:
|
||||
path: .
|
||||
standard: phpcs.xml
|
||||
- uses: php-actions/phpstan@v3
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,9 @@
|
||||
[submodule "jush"]
|
||||
path = externals/jush
|
||||
url = https://github.com/vrana/jush
|
||||
[submodule "JsShrink"]
|
||||
path = externals/JsShrink
|
||||
url = https://github.com/vrana/JsShrink
|
||||
[submodule "PhpShrink"]
|
||||
path = externals/PhpShrink
|
||||
url = https://github.com/vrana/PhpShrink
|
||||
|
||||
107
CHANGELOG.md
107
CHANGELOG.md
@@ -1,8 +1,102 @@
|
||||
## Adminer dev
|
||||
## 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
|
||||
- Allow exporting SQL in SQL command (bug #1092)
|
||||
- Add section links in database overview
|
||||
- Warn about exceeded max_file_uploads in import
|
||||
- Display @ after username without server in existing logins
|
||||
- Display data length and index length for materialized views
|
||||
- Link routines from syntax highlighting
|
||||
- Autofocus added field in alter table
|
||||
- Executed SQL commands: Add button for copy to clipboard
|
||||
- Load more: run syntax highlighter
|
||||
- Allow connecting to IPv6 (bug #1095)
|
||||
- MySQL: Fix saving empty enum (bug #1152)
|
||||
- MySQL 5.0-: Do not load partitioning info in alter table (bug #1099)
|
||||
- MariaDB: Parse COLLATE in routine definition (bug #1104)
|
||||
- PostgreSQL: Show structure of inherited tables
|
||||
- PostgreSQL: Display index expressions
|
||||
- PostgreSQL: Add SQL operator to select
|
||||
- PostgreSQL: Hide only partitions, not all inherited tables from menu
|
||||
- PostgreSQL: Allow comparing json columns (bug #1107)
|
||||
- PostgreSQL: Shorten values in hstore columns
|
||||
- PostgreSQL: Quote edit value with interval operator
|
||||
- PostgreSQL: Fix calling functions with name-less parameters
|
||||
- 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, regression from 5.3.0)
|
||||
- Elasticsearch: Support dropping aliases
|
||||
- Plugins: Methods afterConnect(), processList() and killProcess()
|
||||
- New plugin: Display row numbers in select (bug #1106)
|
||||
- New plugin: Specify query timeout
|
||||
|
||||
## Adminer 5.3.0 (released 2025-05-04)
|
||||
- Align numeric functions right
|
||||
- Autocomplete: Support table aliases
|
||||
- Fix type error in Create function (bug #1053, regression from 5.1.1)
|
||||
- Add border to column actions (bug #1072)
|
||||
- Align money values right (bug #1071)
|
||||
- MySQL: Avoid warning on selecting tables with fulltext indexes (bug #1036)
|
||||
- MySQL, PostgreSQL: Support index algorithms (bug #1030)
|
||||
- MySQL: Fix connecting to localhost:3306 (bug #1057, regression from 5.1.1)
|
||||
- PostgreSQL, CockroachDB: Creating partitioned tables (bug #1031)
|
||||
- PostgreSQL: Move partitioned tables from table list to parent table
|
||||
- PostgreSQL: Support partial indices (bug #1048)
|
||||
- PostgreSQL: Support calling functions returning table (bug #1040)
|
||||
- PostgreSQL: Add NOT ILIKE operator (bug #1066)
|
||||
- Editor: Fix bit and enum search (bug #1062)
|
||||
- Designs: adminer.css with 'prefers-color-scheme: dark' doesn't disable dark mode
|
||||
- Plugins: Method bodyClass() to add <body class>
|
||||
- Plugins: Allow setting dark mode in css()
|
||||
- Hindi translation
|
||||
|
||||
## Adminer 5.2.1 (released 2025-04-11)
|
||||
- Fix search anywhere (bug #1004, regression from 5.1.1)
|
||||
- Fix import without primary key (bug #1017, regression from 5.1.1)
|
||||
- PostgreSQL PDO: Fix bytea without primary key (bug #1021)
|
||||
- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug #1025, regression from 5.2.0)
|
||||
|
||||
## Adminer 5.2.0 (released 2025-04-08)
|
||||
- Autocomplete SQL commands
|
||||
- Do not edit NULL values by Modify (bug #967)
|
||||
- PostgreSQL: Support COPY FROM stdin in SQL query (bug #942)
|
||||
- Fix foreign key actions (regression from 5.1.1)
|
||||
- MySQL: Display number of found rows in group queries (regression from 5.1.1)
|
||||
- PostgreSQL: Support COPY FROM stdin in SQL query (bug #942)
|
||||
- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug SF-842)
|
||||
- MS SQL: Limit one INSERT in export to 1000 rows (bug #983)
|
||||
- CSS: Add logo
|
||||
@@ -39,8 +133,8 @@
|
||||
- CSS: Allow more custom styles with dark mode (bug #925)
|
||||
- CSS: Increase maximum width of string edit (bug #930)
|
||||
- CSS: Increase space after SQL result (bug #937)
|
||||
- Plugins: autoload plugins from adminer-plugins/
|
||||
- Plugins: configure plugins with adminer-plugins.php
|
||||
- Plugins: Autoload plugins from adminer-plugins/
|
||||
- Plugins: Configure plugins with adminer-plugins.php
|
||||
- Plugins: Display loaded plugins in server overview
|
||||
- New plugin: AI prompt in SQL command generating the queries with Google Gemini
|
||||
- New plugin: Verify new versions from GitHub
|
||||
@@ -89,8 +183,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)
|
||||
@@ -171,6 +265,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
|
||||
|
||||
@@ -8,7 +8,7 @@ $routine = routine($_GET["call"], (isset($_GET["callf"]) ? "FUNCTION" : "PROCEDU
|
||||
$in = array();
|
||||
$out = array();
|
||||
foreach ($routine["fields"] as $i => $field) {
|
||||
if (substr($field["inout"], -3) == "OUT") {
|
||||
if (substr($field["inout"], -3) == "OUT" && JUSH == 'sql') {
|
||||
$out[$i] = "@" . idf_escape($field["field"]) . " AS " . idf_escape($field["field"]);
|
||||
}
|
||||
if (!$field["inout"] || substr($field["inout"], 0, 2) == "IN") {
|
||||
@@ -29,10 +29,14 @@ if (!$error && $_POST) {
|
||||
connection()->query("SET @" . idf_escape($field["field"]) . " = $val");
|
||||
}
|
||||
}
|
||||
$call[] = (isset($out[$key]) ? "@" . idf_escape($field["field"]) : $val);
|
||||
if (isset($out[$key])) {
|
||||
$call[] = "@" . idf_escape($field["field"]);
|
||||
} elseif (in_array($key, $in)) {
|
||||
$call[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$query = (isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . 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
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
namespace Adminer;
|
||||
|
||||
$TABLE = $_GET["create"];
|
||||
$partition_by = array();
|
||||
foreach (array('HASH', 'LINEAR HASH', 'KEY', 'LINEAR KEY', 'RANGE', 'LIST') as $key) {
|
||||
$partition_by[$key] = $key;
|
||||
}
|
||||
$partition_by = driver()->partitionBy;
|
||||
$partitions_info = ($partition_by ? driver()->partitionsInfo($TABLE) : array());
|
||||
|
||||
$referencable_primary = referencable_primary($TABLE);
|
||||
$foreign_keys = array();
|
||||
@@ -80,40 +78,26 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
|
||||
}
|
||||
}
|
||||
|
||||
$partitioning = "";
|
||||
if (support("partitioning")) {
|
||||
if (isset($partition_by[$row["partition_by"]])) {
|
||||
$params = array();
|
||||
foreach ($row as $key => $val) {
|
||||
if (preg_match('~^partition~', $key)) {
|
||||
$params[$key] = $val;
|
||||
}
|
||||
$partitioning = array();
|
||||
if (in_array($row["partition_by"], $partition_by)) {
|
||||
foreach ($row as $key => $val) {
|
||||
if (preg_match('~^partition~', $key)) {
|
||||
$partitioning[$key] = $val;
|
||||
}
|
||||
foreach ($params["partition_names"] as $key => $name) {
|
||||
if ($name == "") {
|
||||
unset($params["partition_names"][$key]);
|
||||
unset($params["partition_values"][$key]);
|
||||
}
|
||||
}
|
||||
if ($params != get_partitions_info($TABLE)) {
|
||||
$partitions = array();
|
||||
if ($params["partition_by"] == 'RANGE' || $params["partition_by"] == 'LIST') {
|
||||
foreach ($params["partition_names"] as $key => $name) {
|
||||
$value = $params["partition_values"][$key];
|
||||
$partitions[] = "\n PARTITION " . idf_escape($name) . " VALUES " . ($params["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
|
||||
}
|
||||
}
|
||||
// $params["partition"] can be expression, not only column
|
||||
$partitioning .= "\nPARTITION BY $params[partition_by]($params[partition])";
|
||||
if ($partitions) {
|
||||
$partitioning .= " (" . implode(",", $partitions) . "\n)";
|
||||
} elseif ($params["partitions"]) {
|
||||
$partitioning .= " PARTITIONS " . (+$params["partitions"]);
|
||||
}
|
||||
}
|
||||
} elseif (preg_match("~partitioned~", $table_status["Create_options"])) {
|
||||
$partitioning .= "\nREMOVE PARTITIONING";
|
||||
}
|
||||
foreach ($partitioning["partition_names"] as $key => $name) {
|
||||
if ($name == "") {
|
||||
unset($partitioning["partition_names"][$key]);
|
||||
unset($partitioning["partition_values"][$key]);
|
||||
}
|
||||
}
|
||||
$partitioning["partition_names"] = array_values($partitioning["partition_names"]);
|
||||
$partitioning["partition_values"] = array_values($partitioning["partition_values"]);
|
||||
if ($partitioning == $partitions_info) {
|
||||
$partitioning = array();
|
||||
}
|
||||
} elseif (preg_match("~partitioned~", $table_status["Create_options"])) {
|
||||
$partitioning = null;
|
||||
}
|
||||
|
||||
$message = lang('Table has been altered.');
|
||||
@@ -159,8 +143,8 @@ if (!$_POST) {
|
||||
$row["fields"][] = $field;
|
||||
}
|
||||
|
||||
if (support("partitioning")) {
|
||||
$row += get_partitions_info($TABLE);
|
||||
if ($partition_by) {
|
||||
$row += $partitions_info;
|
||||
$row["partition_names"][] = "";
|
||||
$row["partition_values"][] = "";
|
||||
}
|
||||
@@ -221,10 +205,10 @@ if (support("columns")) {
|
||||
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $TABLE)); ?>
|
||||
<?php } ?>
|
||||
<?php
|
||||
if (support("partitioning")) {
|
||||
if ($partition_by && (JUSH == 'sql' || $TABLE == "")) {
|
||||
$partition_table = preg_match('~RANGE|LIST~', $row["partition_by"]);
|
||||
print_fieldset("partition", lang('Partition by'), $row["partition_by"]);
|
||||
echo "<p>" . html_select("partition_by", array("" => "") + $partition_by, $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;");
|
||||
echo "<p>" . html_select("partition_by", array_merge(array(""), $partition_by), $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;");
|
||||
echo "(<input name='partition' value='" . h($row["partition"]) . "'>)\n";
|
||||
echo lang('Partitions') . ": <input type='number' name='partitions' class='size" . ($partition_table || !$row["partition_by"] ? " hidden" : "") . "' value='" . h($row["partitions"]) . "'>\n";
|
||||
echo "<table id='partition-table'" . ($partition_table ? "" : " class='hidden'") . ">\n";
|
||||
|
||||
@@ -64,12 +64,13 @@ if (adminer()->homepage()) {
|
||||
echo "<form action='' method='post'>\n";
|
||||
if (support("table")) {
|
||||
echo "<fieldset><legend>" . lang('Search data in tables') . " <span id='selected2'></span></legend><div>";
|
||||
echo "<input type='search' name='query' value='" . h($_POST["query"]) . "'>";
|
||||
echo html_select("op", adminer()->operators(), idx($_POST, "op", JUSH == "elastic" ? "should" : "LIKE %%"));
|
||||
echo " <input type='search' name='query' value='" . h($_POST["query"]) . "'>";
|
||||
echo script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');", "");
|
||||
echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n";
|
||||
echo "</div></fieldset>\n";
|
||||
if ($_POST["search"] && $_POST["query"] != "") {
|
||||
$_GET["where"][0]["op"] = driver()->convertOperator("LIKE %%");
|
||||
$_GET["where"][0]["op"] = $_POST["op"];
|
||||
search_tables();
|
||||
}
|
||||
}
|
||||
@@ -79,14 +80,27 @@ if (adminer()->homepage()) {
|
||||
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')) : '');
|
||||
$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 $column) {
|
||||
echo "<td>$column[0]";
|
||||
}
|
||||
echo "</thead>\n";
|
||||
|
||||
$tables = 0;
|
||||
@@ -95,30 +109,20 @@ if (adminer()->homepage()) {
|
||||
$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) {
|
||||
echo '<td colspan="6"><a href="' . h(ME) . "view=" . urlencode($name) . '" title="' . lang('Alter view') . '">' . (preg_match('~materialized~i', $type) ? lang('Materialized view') : lang('View')) . '</a>';
|
||||
if ($view && !preg_match('~materialized~i', $type)) {
|
||||
$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 align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '" title="' . lang('Select data') . '">?</a>';
|
||||
} 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) . "'>");
|
||||
echo ($column[1]
|
||||
? "<td align='right'><a href='" . h(ME . "$column[1]=") . urlencode($name) . "'$id title='$column[2]'>?</a>"
|
||||
: "<td id='$key-" . h($name) . "'>"
|
||||
);
|
||||
}
|
||||
$tables++;
|
||||
}
|
||||
echo (support("comment") ? "<td id='Comment-" . h($name) . "'>" : "");
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
@@ -126,46 +130,51 @@ if (adminer()->homepage()) {
|
||||
echo "<td>" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : "");
|
||||
echo "<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 "</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'")
|
||||
$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());
|
||||
$script = "";
|
||||
if (count($databases) != 1 && JUSH != "sqlite") {
|
||||
echo "<fieldset><legend>" . lang('Move to other database') . " <span id='selected3'></span></legend><div>";
|
||||
$db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB));
|
||||
echo "<p><label>" . lang('Move to other database') . ": ";
|
||||
echo ($databases ? html_select("target", $databases, $db) : '<input name="target" value="' . h($db) . '" autocapitalize="off">');
|
||||
echo "</label> <input type='submit' name='move' value='" . lang('Move') . "'>";
|
||||
echo (support("copy") ? " <input type='submit' name='copy' value='" . lang('Copy') . "'> " . checkbox("overwrite", 1, $_POST["overwrite"], lang('overwrite')) : "");
|
||||
echo "\n";
|
||||
echo "</div></fieldset>\n";
|
||||
$script = " selectCount('selected3', formChecked(this, /^(tables|views)\[/));";
|
||||
}
|
||||
echo "<input type='hidden' name='all' value=''>"; // used by trCheck()
|
||||
echo script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^(tables|views)\[/));" . (support("table") ? " selectCount('selected2', formChecked(this, /^tables\[/) || $tables);" : "") . " }");
|
||||
echo script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^(tables|views)\[/));"
|
||||
. (support("table") ? " selectCount('selected2', formChecked(this, /^tables\[/) || $tables);" : "")
|
||||
. "$script }")
|
||||
;
|
||||
echo input_token();
|
||||
echo "</div></fieldset>\n";
|
||||
echo "</div></div>\n";
|
||||
}
|
||||
echo "</form>\n";
|
||||
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")) {
|
||||
@@ -239,9 +248,5 @@ if (adminer()->homepage()) {
|
||||
}
|
||||
echo '<p class="links"><a href="' . h(ME) . 'event=">' . lang('Create event') . "</a>\n";
|
||||
}
|
||||
|
||||
if ($tables_list) {
|
||||
echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ add_driver("mssql", "MS SQL");
|
||||
|
||||
if (isset($_GET["mssql"])) {
|
||||
define('Adminer\DRIVER', "mssql");
|
||||
|
||||
if (extension_loaded("sqlsrv") && $_GET["ext"] != "pdo") {
|
||||
class Db extends SqlDb {
|
||||
public $extension = "sqlsrv";
|
||||
@@ -25,7 +26,7 @@ if (isset($_GET["mssql"])) {
|
||||
$this->error = rtrim($this->error);
|
||||
}
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
$connection_info = array("UID" => $username, "PWD" => $password, "CharacterSet" => "UTF-8");
|
||||
$ssl = adminer()->connectSsl();
|
||||
if (isset($ssl["Encrypt"])) {
|
||||
@@ -38,7 +39,8 @@ if (isset($_GET["mssql"])) {
|
||||
if ($db != "") {
|
||||
$connection_info["Database"] = $db;
|
||||
}
|
||||
$this->link = @sqlsrv_connect(preg_replace('~:~', ',', $server), $connection_info);
|
||||
list($host, $port) = host_port($server);
|
||||
$this->link = @sqlsrv_connect($host . ($port ? ",$port" : ""), $connection_info);
|
||||
if ($this->link) {
|
||||
$info = sqlsrv_server_info($this->link);
|
||||
$this->server_info = $info['SQLServerVersion'];
|
||||
@@ -180,8 +182,9 @@ if (isset($_GET["mssql"])) {
|
||||
class Db extends MssqlDb {
|
||||
public $extension = "PDO_SQLSRV";
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
return $this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password);
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
list($host, $port) = host_port($server);
|
||||
return $this->dsn("sqlsrv:Server=$host" . ($port ? ",$port" : ""), $username, $password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,8 +192,9 @@ if (isset($_GET["mssql"])) {
|
||||
class Db extends MssqlDb {
|
||||
public $extension = "PDO_DBLIB";
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
return $this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password);
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
list($host, $port) = host_port($server);
|
||||
return $this->dsn("dblib:charset=utf8;host=$host" . ($port ? (is_numeric($port) ? ";port=" : ";unix_socket=") . $port : ""), $username, $password);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +217,7 @@ if (isset($_GET["mssql"])) {
|
||||
public $generated = array("PERSISTED", "VIRTUAL");
|
||||
public $onActions = "NO ACTION|CASCADE|SET NULL|SET DEFAULT";
|
||||
|
||||
static function connect(?string $server, string $username, string $password) {
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
if ($server == "") {
|
||||
$server = "localhost:1433";
|
||||
}
|
||||
@@ -677,7 +681,7 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)) as $row
|
||||
return "TRUNCATE TABLE " . table($table);
|
||||
}
|
||||
|
||||
function use_sql($database) {
|
||||
function use_sql($database, $style = "") {
|
||||
return "USE " . idf_escape($database);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ SqlDriver::$drivers = array("server" => "MySQL / MariaDB") + SqlDriver::$drivers
|
||||
|
||||
if (!defined('Adminer\DRIVER')) {
|
||||
define('Adminer\DRIVER', "server"); // server - backwards compatibility
|
||||
|
||||
// MySQLi supports everything, MySQL doesn't support multiple result sets, PDO_MySQL doesn't support orgtable
|
||||
if (extension_loaded("mysqli") && $_GET["ext"] != "pdo") {
|
||||
class Db extends \MySQLi {
|
||||
@@ -15,9 +16,9 @@ if (!defined('Adminer\DRIVER')) {
|
||||
parent::init();
|
||||
}
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4
|
||||
list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket
|
||||
list($host, $port) = host_port($server);
|
||||
$ssl = adminer()->connectSsl();
|
||||
if ($ssl) {
|
||||
$this->ssl_set($ssl['key'], $ssl['cert'], $ssl['ca'], '', '');
|
||||
@@ -28,10 +29,10 @@ if (!defined('Adminer\DRIVER')) {
|
||||
($server . $username . $password != "" ? $password : ini_get("mysqli.default_pw")),
|
||||
null,
|
||||
(is_numeric($port) ? intval($port) : ini_get("mysqli.default_port")),
|
||||
(is_numeric($port) ? $port : null),
|
||||
(is_numeric($port) ? null : $port),
|
||||
($ssl ? ($ssl['verify'] !== false ? 2048 : 64) : 0) // 2048 - MYSQLI_CLIENT_SSL, 64 - MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT (not available before PHP 5.6.16)
|
||||
);
|
||||
$this->options(MYSQLI_OPT_LOCAL_INFILE, false);
|
||||
$this->options(MYSQLI_OPT_LOCAL_INFILE, 0);
|
||||
return ($return ? '' : $this->error);
|
||||
}
|
||||
|
||||
@@ -57,14 +58,14 @@ if (!defined('Adminer\DRIVER')) {
|
||||
class Db extends SqlDb {
|
||||
/** @var resource */ private $link;
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
if (ini_bool("mysql.allow_local_infile")) {
|
||||
return lang('Disable %s or enable %s or %s extensions.', "'mysql.allow_local_infile'", "MySQLi", "PDO_MySQL");
|
||||
}
|
||||
$this->link = @mysql_connect(
|
||||
($server != "" ? $server : ini_get("mysql.default_host")),
|
||||
("$server$username" != "" ? $username : ini_get("mysql.default_user")),
|
||||
("$server$username$password" != "" ? $password : ini_get("mysql.default_password")),
|
||||
($server . $username != "" ? $username : ini_get("mysql.default_user")),
|
||||
($server . $username . $password != "" ? $password : ini_get("mysql.default_password")),
|
||||
true,
|
||||
131072 // CLIENT_MULTI_RESULTS for CALL
|
||||
);
|
||||
@@ -157,7 +158,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
class Db extends PdoDb {
|
||||
public $extension = "PDO_MySQL";
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
$options = array(\PDO::MYSQL_ATTR_LOCAL_INFILE => false);
|
||||
$ssl = adminer()->connectSsl();
|
||||
if ($ssl) {
|
||||
@@ -174,8 +175,9 @@ if (!defined('Adminer\DRIVER')) {
|
||||
$options[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $ssl['verify'];
|
||||
}
|
||||
}
|
||||
list($host, $port) = host_port($server);
|
||||
return $this->dsn(
|
||||
"mysql:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)),
|
||||
"mysql:charset=utf8;host=$host" . ($port ? (is_numeric($port) ? ";port=" : ";unix_socket=") . $port : ""),
|
||||
$username,
|
||||
$password,
|
||||
$options
|
||||
@@ -210,7 +212,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
public $functions = array("char_length", "date", "from_unixtime", "lower", "round", "floor", "ceil", "sec_to_time", "time_to_sec", "upper");
|
||||
public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
|
||||
|
||||
static function connect(?string $server, string $username, string $password) {
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
$connection = parent::connect($server, $username, $password);
|
||||
if (is_string($connection)) {
|
||||
if (function_exists('iconv') && !is_utf8($connection) && strlen($s = iconv("windows-1250", "utf-8", $connection)) > strlen($connection)) { // windows-1250 - most common Windows encoding
|
||||
@@ -257,6 +259,9 @@ if (!defined('Adminer\DRIVER')) {
|
||||
$this->types[lang('Numbers')]["vector"] = 16383;
|
||||
$this->insertFunctions['vector'] = 'string_to_vector';
|
||||
}
|
||||
if (min_version(5.1, '', $connection)) {
|
||||
$this->partitionBy = array("HASH", "LINEAR HASH", "KEY", "LINEAR KEY", "RANGE", "LIST");
|
||||
}
|
||||
if (min_version(5.7, 10.2, $connection)) {
|
||||
$this->generated = array("STORED", "VIRTUAL");
|
||||
}
|
||||
@@ -334,6 +339,17 @@ if (!defined('Adminer\DRIVER')) {
|
||||
}
|
||||
}
|
||||
|
||||
function partitionsInfo(string $table): array {
|
||||
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table);
|
||||
$result = $this->conn->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
|
||||
$return = array();
|
||||
list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row();
|
||||
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
|
||||
$return["partition_names"] = array_keys($partitions);
|
||||
$return["partition_values"] = array_values($partitions);
|
||||
return $return;
|
||||
}
|
||||
|
||||
function hasCStyleEscapes(): bool {
|
||||
static $c_style;
|
||||
if ($c_style === null) {
|
||||
@@ -352,6 +368,10 @@ if (!defined('Adminer\DRIVER')) {
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function indexAlgorithms(array $tableStatus): array {
|
||||
return (preg_match('~^(MEMORY|NDB)$~', $tableStatus["Engine"]) ? array("HASH", "BTREE") : array());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +458,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
|
||||
/** Get table status
|
||||
* @param bool $fast return only "Name", "Engine" and "Comment" fields
|
||||
* @return TableStatus[]
|
||||
* @return array<string, TableStatus>
|
||||
*/
|
||||
function table_status(string $name = "", bool $fast = false): array {
|
||||
$return = array();
|
||||
@@ -543,6 +563,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
$return[$name]["columns"][] = $row["Column_name"];
|
||||
$return[$name]["lengths"][] = ($row["Index_type"] == "SPATIAL" ? null : $row["Sub_part"]);
|
||||
$return[$name]["descs"][] = null;
|
||||
$return[$name]["algorithm"] = $row["Index_type"];
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
@@ -556,7 +577,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
$create_table = get_val("SHOW CREATE TABLE " . table($table), 1);
|
||||
if ($create_table) {
|
||||
preg_match_all(
|
||||
"~CONSTRAINT ($pattern) FOREIGN KEY ?\\(((?:$pattern,? ?)+)\\) REFERENCES ($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE (driver()->onActions))?(?: ON UPDATE (driver()->onActions))?~",
|
||||
"~CONSTRAINT ($pattern) FOREIGN KEY ?\\(((?:$pattern,? ?)+)\\) REFERENCES ($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE (" . driver()->onActions . "))?(?: ON UPDATE (" . driver()->onActions . "))?~",
|
||||
$create_table,
|
||||
$matches,
|
||||
PREG_SET_ORDER
|
||||
@@ -675,10 +696,11 @@ if (!defined('Adminer\DRIVER')) {
|
||||
* @param string $name new name
|
||||
* @param list<array{string, list<string>, string}> $fields of [$orig, $process_field, $after]
|
||||
* @param string[] $foreign
|
||||
* @param numeric-string $auto_increment
|
||||
* @param numeric-string|'' $auto_increment
|
||||
* @param ?Partitions $partitioning null means remove partitioning
|
||||
* @return Result|bool
|
||||
*/
|
||||
function alter_table(string $table, string $name, array $fields, array $foreign, ?string $comment, string $engine, string $collation, string $auto_increment, string $partitioning) {
|
||||
function alter_table(string $table, string $name, array $fields, array $foreign, ?string $comment, string $engine, string $collation, string $auto_increment, ?array $partitioning) {
|
||||
$alter = array();
|
||||
foreach ($fields as $field) {
|
||||
if ($field[1]) {
|
||||
@@ -699,8 +721,28 @@ if (!defined('Adminer\DRIVER')) {
|
||||
. ($collation ? " COLLATE " . q($collation) : "")
|
||||
. ($auto_increment != "" ? " AUTO_INCREMENT=$auto_increment" : "")
|
||||
;
|
||||
|
||||
if ($partitioning) {
|
||||
$partitions = array();
|
||||
if ($partitioning["partition_by"] == 'RANGE' || $partitioning["partition_by"] == 'LIST') {
|
||||
foreach ($partitioning["partition_names"] as $key => $val) {
|
||||
$value = $partitioning["partition_values"][$key];
|
||||
$partitions[] = "\n PARTITION " . idf_escape($val) . " VALUES " . ($partitioning["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
|
||||
}
|
||||
}
|
||||
// $partitioning["partition"] can be expression, not only column
|
||||
$status .= "\nPARTITION BY $partitioning[partition_by]($partitioning[partition])";
|
||||
if ($partitions) {
|
||||
$status .= " (" . implode(",", $partitions) . "\n)";
|
||||
} elseif ($partitioning["partitions"]) {
|
||||
$status .= " PARTITIONS " . (+$partitioning["partitions"]);
|
||||
}
|
||||
} elseif ($partitioning === null) {
|
||||
$status .= "\nREMOVE PARTITIONING";
|
||||
}
|
||||
|
||||
if ($table == "") {
|
||||
return queries("CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)$status$partitioning");
|
||||
return queries("CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)$status");
|
||||
}
|
||||
if ($table != $name) {
|
||||
$alter[] = "RENAME TO " . table($name);
|
||||
@@ -708,12 +750,12 @@ if (!defined('Adminer\DRIVER')) {
|
||||
if ($status) {
|
||||
$alter[] = ltrim($status);
|
||||
}
|
||||
return ($alter || $partitioning ? queries("ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter) . $partitioning) : true);
|
||||
return ($alter ? queries("ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter)) : true);
|
||||
}
|
||||
|
||||
/** Run commands to alter indexes
|
||||
* @param string $table escaped table name
|
||||
* @param list<array{string, string, 'DROP'|list<string>}> $alter of ["index type", "name", ["column definition", ...]] or ["index type", "name", "DROP"]
|
||||
* @param list<array{string, string, 'DROP'|list<string>, 3?: string, 4?: string}> $alter of ["index type", "name", ["column definition", ...], "algorithm", "condition"] or ["index type", "name", "DROP"]
|
||||
* @return Result|bool
|
||||
*/
|
||||
function alter_indexes(string $table, $alter) {
|
||||
@@ -851,43 +893,34 @@ 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,]+)['\"]?)?";
|
||||
$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
|
||||
* @return list<string[]> ["SPECIFIC_NAME" => , "ROUTINE_NAME" => , "ROUTINE_TYPE" => , "DTD_IDENTIFIER" => ]
|
||||
*/
|
||||
function routines(): array {
|
||||
return get_rows("SELECT ROUTINE_NAME AS SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");
|
||||
return get_rows("SELECT SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");
|
||||
}
|
||||
|
||||
/** Get list of available routine languages
|
||||
@@ -942,8 +975,17 @@ if (!defined('Adminer\DRIVER')) {
|
||||
}
|
||||
|
||||
/** Get SQL command to change database */
|
||||
function use_sql(string $database): string {
|
||||
return "USE " . idf_escape($database);
|
||||
function use_sql(string $database, string $style = ""): string {
|
||||
$name = idf_escape($database);
|
||||
$return = "";
|
||||
if (preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE $name", 1))) {
|
||||
set_utf8mb4($create);
|
||||
if ($style == "DROP+CREATE") {
|
||||
$return = "DROP DATABASE IF EXISTS $name;\n";
|
||||
}
|
||||
$return .= "$create;\n";
|
||||
}
|
||||
return $return . "USE $name";
|
||||
}
|
||||
|
||||
/** Get SQL commands to create triggers */
|
||||
@@ -1011,18 +1053,26 @@ if (!defined('Adminer\DRIVER')) {
|
||||
}
|
||||
|
||||
/** Check whether a feature is supported
|
||||
* @param literal-string $feature "check|comment|copy|database|descidx|drop_col|dump|event|indexes|kill|materializedview|partitioning|privileges|procedure|processlist|routine|scheme|sequence|status|table|trigger|type|variables|view|view_trigger"
|
||||
* @param literal-string $feature check|comment|columns|copy|database|descidx|drop_col|dump|event|indexes|kill|materializedview
|
||||
* |move_col|privileges|procedure|processlist|routine|scheme|sequence|sql|status|table|trigger|type|variables|view|view_trigger
|
||||
*/
|
||||
function support(string $feature): bool {
|
||||
return !preg_match("~scheme|sequence|type|view_trigger|materializedview" . (min_version(8) ? "" : "|descidx" . (min_version(5.1) ? "" : "|event|partitioning")) . (min_version('8.0.16', '10.2.1') ? "" : "|check") . "~", $feature);
|
||||
return preg_match(
|
||||
'~^(comment|columns|copy|database|drop_col|dump|indexes|kill|privileges|move_col|procedure|processlist|routine|sql|status|table|trigger|variables|view'
|
||||
. (min_version(5.1) ? '|event' : '')
|
||||
. (min_version(8) ? '|descidx' : '')
|
||||
. (min_version('8.0.16', '10.2.1') ? '|check' : '')
|
||||
. ')$~',
|
||||
$feature
|
||||
);
|
||||
}
|
||||
|
||||
/** Kill a process
|
||||
* @param numeric-string $val
|
||||
* @param numeric-string $id
|
||||
* @return Result|bool
|
||||
*/
|
||||
function kill_process(string $val) {
|
||||
return queries("KILL " . number($val));
|
||||
function kill_process(string $id) {
|
||||
return queries("KILL " . number($id));
|
||||
}
|
||||
|
||||
/** Return query to get connection ID */
|
||||
|
||||
@@ -5,6 +5,7 @@ add_driver("oracle", "Oracle (beta)");
|
||||
|
||||
if (isset($_GET["oracle"])) {
|
||||
define('Adminer\DRIVER', "oracle");
|
||||
|
||||
if (extension_loaded("oci8") && $_GET["ext"] != "pdo") {
|
||||
class Db extends SqlDb {
|
||||
public $extension = "oci8";
|
||||
@@ -19,7 +20,7 @@ if (isset($_GET["oracle"])) {
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
$this->link = @oci_new_connect($username, $password, $server, "AL32UTF8");
|
||||
if ($this->link) {
|
||||
$this->server_info = oci_server_version($this->link);
|
||||
@@ -59,6 +60,10 @@ if (isset($_GET["oracle"])) {
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function timeout(int $ms): bool {
|
||||
return oci_set_call_timeout($this->link, $ms);
|
||||
}
|
||||
}
|
||||
|
||||
class Result {
|
||||
@@ -105,7 +110,7 @@ if (isset($_GET["oracle"])) {
|
||||
public $extension = "PDO_OCI";
|
||||
public $_current_db;
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
return $this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password);
|
||||
}
|
||||
|
||||
@@ -164,7 +169,7 @@ if (isset($_GET["oracle"])) {
|
||||
}
|
||||
}
|
||||
if (
|
||||
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows)
|
||||
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $this->conn->affected_rows)
|
||||
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
|
||||
) {
|
||||
return false;
|
||||
@@ -390,7 +395,6 @@ ORDER BY ac.constraint_type, aic.column_position", $connection2) as $row
|
||||
$queries = array();
|
||||
foreach ($alter as $val) {
|
||||
if ($val[0] != "INDEX") {
|
||||
//! descending UNIQUE indexes results in syntax error
|
||||
$val[2] = preg_replace('~ DESC$~', '', $val[2]);
|
||||
$create = ($val[2] == "DROP"
|
||||
? "\nDROP CONSTRAINT " . idf_escape($val[1])
|
||||
|
||||
@@ -5,6 +5,7 @@ add_driver("pgsql", "PostgreSQL");
|
||||
|
||||
if (isset($_GET["pgsql"])) {
|
||||
define('Adminer\DRIVER', "pgsql");
|
||||
|
||||
if (extension_loaded("pgsql") && $_GET["ext"] != "pdo") {
|
||||
class PgsqlDb extends SqlDb {
|
||||
public $extension = "PgSQL";
|
||||
@@ -19,10 +20,11 @@ if (isset($_GET["pgsql"])) {
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
$db = adminer()->database();
|
||||
set_error_handler(array($this, '_error'));
|
||||
$this->string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
|
||||
list($host, $port) = host_port(addcslashes($server, "'\\"));
|
||||
$this->string = "host='$host'" . ($port ? " port='$port'" : "") . " user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
|
||||
$ssl = adminer()->connectSsl();
|
||||
if (isset($ssl["mode"])) {
|
||||
$this->string .= " sslmode='" . $ssl["mode"] . "'";
|
||||
@@ -94,8 +96,9 @@ if (isset($_GET["pgsql"])) {
|
||||
*/
|
||||
function copyFrom(string $table, array $rows): bool {
|
||||
$this->error = '';
|
||||
set_error_handler(function ($errno, $error) {
|
||||
set_error_handler(function (int $errno, string $error): bool {
|
||||
$this->error = (ini_bool('html_errors') ? html_entity_decode($error) : $error);
|
||||
return true;
|
||||
});
|
||||
$return = pg_copy_from($this->link, $table, $rows);
|
||||
restore_error_handler();
|
||||
@@ -125,8 +128,9 @@ if (isset($_GET["pgsql"])) {
|
||||
$return = new \stdClass;
|
||||
$return->orgtable = pg_field_table($this->result, $column);
|
||||
$return->name = pg_field_name($this->result, $column);
|
||||
$return->type = pg_field_type($this->result, $column); //! map to MySQL numbers
|
||||
$return->charsetnr = ($return->type == "bytea" ? 63 : 0); // 63 - binary
|
||||
$type = pg_field_type($this->result, $column);
|
||||
$return->type = (preg_match(number_type(), $type) ? 0 : 15);
|
||||
$return->charsetnr = ($type == "bytea" ? 63 : 0); // 63 - binary
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -140,10 +144,11 @@ if (isset($_GET["pgsql"])) {
|
||||
public $extension = "PDO_PgSQL";
|
||||
public $timeout = 0;
|
||||
|
||||
function attach(?string $server, string $username, string $password): string {
|
||||
function attach(string $server, string $username, string $password): string {
|
||||
$db = adminer()->database();
|
||||
list($host, $port) = host_port(addcslashes($server, "'\\"));
|
||||
//! client_encoding is supported since 9.1, but we can't yet use min_version here
|
||||
$dsn = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'";
|
||||
$dsn = "pgsql:host='$host'" . ($port ? " port='$port'" : "") . " client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'";
|
||||
$ssl = adminer()->connectSsl();
|
||||
if (isset($ssl["mode"])) {
|
||||
$dsn .= " sslmode='" . $ssl["mode"] . "'";
|
||||
@@ -201,11 +206,13 @@ if (isset($_GET["pgsql"])) {
|
||||
static $extensions = array("PgSQL", "PDO_PgSQL");
|
||||
static $jush = "pgsql";
|
||||
|
||||
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); // no "SQL" to avoid CSRF
|
||||
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT ILIKE", "NOT IN", "IS NOT NULL", "SQL"); //! SQL - same-site CSRF
|
||||
public $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
|
||||
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
|
||||
|
||||
static function connect(?string $server, string $username, string $password) {
|
||||
public string $nsOid = "(SELECT oid FROM pg_namespace WHERE nspname = current_schema())";
|
||||
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
$connection = parent::connect($server, $username, $password);
|
||||
if (is_string($connection)) {
|
||||
return $connection;
|
||||
@@ -250,6 +257,10 @@ if (isset($_GET["pgsql"])) {
|
||||
if (min_version(12, 0, $connection)) {
|
||||
$this->generated = array("STORED");
|
||||
}
|
||||
$this->partitionBy = array("RANGE", "LIST");
|
||||
if (!$connection->flavor) {
|
||||
$this->partitionBy[] = "HASH";
|
||||
}
|
||||
}
|
||||
|
||||
function enumLength(array $field) {
|
||||
@@ -279,7 +290,7 @@ if (isset($_GET["pgsql"])) {
|
||||
}
|
||||
}
|
||||
if (
|
||||
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows)
|
||||
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $this->conn->affected_rows)
|
||||
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
|
||||
) {
|
||||
return false;
|
||||
@@ -322,6 +333,39 @@ if (isset($_GET["pgsql"])) {
|
||||
}
|
||||
}
|
||||
|
||||
function inheritsFrom(string $table): array {
|
||||
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_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 {
|
||||
$row = (min_version(10) ? $this->conn->query("SELECT * FROM pg_partitioned_table WHERE partrelid = " . $this->tableOid($table))->fetch_assoc() : null);
|
||||
if ($row) {
|
||||
$attrs = get_vals("SELECT attname FROM pg_attribute WHERE attrelid = $row[partrelid] AND attnum IN (" . str_replace(" ", ", ", $row["partattrs"]) . ")"); //! ordering
|
||||
$by = array('h' => 'HASH', 'l' => 'LIST', 'r' => 'RANGE');
|
||||
return array(
|
||||
"partition_by" => $by[$row["partstrat"]],
|
||||
"partition" => implode(", ", array_map('Adminer\idf_escape', $attrs)),
|
||||
);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
function tableOid(string $table): string {
|
||||
return "(SELECT oid FROM pg_class WHERE relnamespace = $this->nsOid AND relname = " . q($table) . " AND relkind IN ('r', 'm', 'v', 'f', 'p'))";
|
||||
}
|
||||
|
||||
function indexAlgorithms(array $tableStatus): array {
|
||||
static $return = array();
|
||||
if (!$return) {
|
||||
$return = get_vals("SELECT amname FROM pg_am" . (min_version(9.6) ? " WHERE amtype = 'i'" : "") . " ORDER BY amname = '" . ($this->conn->flavor == 'cockroach' ? "prefix" : "btree") . "' DESC, amname");
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function supportsIndex(array $table_status): bool {
|
||||
// returns true for "materialized view"
|
||||
return $table_status["Engine"] != "view";
|
||||
@@ -404,18 +448,19 @@ ORDER BY 1";
|
||||
$return = array();
|
||||
foreach (
|
||||
get_rows("SELECT
|
||||
c.relname AS \"Name\",
|
||||
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'm' THEN 'materialized view' ELSE 'view' END AS \"Engine\"" . ($has_size ? ",
|
||||
relname AS \"Name\",
|
||||
CASE relkind WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' ELSE 'table' END AS \"Engine\"" . ($has_size ? ",
|
||||
pg_table_size(c.oid) AS \"Data_length\",
|
||||
pg_indexes_size(c.oid) AS \"Index_length\"" : "") . ",
|
||||
obj_description(c.oid, 'pg_class') AS \"Comment\",
|
||||
" . (min_version(12) ? "''" : "CASE WHEN c.relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
|
||||
c.reltuples as \"Rows\",
|
||||
n.nspname
|
||||
" . (min_version(12) ? "''" : "CASE WHEN relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
|
||||
reltuples AS \"Rows\",
|
||||
" . (min_version(10) ? "relispartition::int AS partition," : "") . "
|
||||
current_schema() AS nspname
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON(n.nspname = current_schema() AND n.oid = c.relnamespace)
|
||||
WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
|
||||
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Index_length, Auto_increment
|
||||
AND relnamespace = " . driver()->nsOid . "
|
||||
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Auto_increment
|
||||
) {
|
||||
$return[$row["Name"]] = $row;
|
||||
}
|
||||
@@ -442,20 +487,19 @@ WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
|
||||
format_type(a.atttypid, a.atttypmod) AS full_type,
|
||||
pg_get_expr(d.adbin, d.adrelid) AS default,
|
||||
a.attnotnull::int,
|
||||
col_description(c.oid, a.attnum) AS comment" . (min_version(10) ? ",
|
||||
i.indrelid AS primary,
|
||||
col_description(a.attrelid, a.attnum) AS comment" . (min_version(10) ? ",
|
||||
a.attidentity" . (min_version(12) ? ",
|
||||
a.attgenerated" : "") : "") . "
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_attribute a ON c.oid = a.attrelid
|
||||
LEFT JOIN pg_attrdef d ON c.oid = d.adrelid AND a.attnum = d.adnum
|
||||
WHERE c.relname = " . q($table) . "
|
||||
AND n.nspname = current_schema()
|
||||
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;
|
||||
@@ -475,7 +519,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;
|
||||
@@ -486,25 +530,28 @@ ORDER BY a.attnum") as $row
|
||||
function indexes($table, $connection2 = null) {
|
||||
$connection2 = connection($connection2);
|
||||
$return = array();
|
||||
$table_oid = get_val("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($table), 0, $connection2);
|
||||
$table_oid = driver()->tableOid($table);
|
||||
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
|
||||
foreach (
|
||||
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial
|
||||
FROM pg_index i, pg_class ci
|
||||
WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid
|
||||
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, amname, pg_get_expr(indpred, indrelid, true) AS partial, pg_get_expr(indexprs, indrelid) AS indexpr
|
||||
FROM pg_index
|
||||
JOIN pg_class ON indexrelid = oid
|
||||
JOIN pg_am ON pg_am.oid = pg_class.relam
|
||||
WHERE indrelid = $table_oid
|
||||
ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
|
||||
) {
|
||||
$relname = $row["relname"];
|
||||
$return[$relname]["type"] = ($row["indispartial"] ? "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();
|
||||
if ($row["indkey"]) {
|
||||
foreach (explode(" ", $row["indkey"]) as $indkey) {
|
||||
$return[$relname]["columns"][] = $columns[$indkey];
|
||||
}
|
||||
foreach (explode(" ", $row["indoption"]) as $indoption) {
|
||||
$return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
|
||||
}
|
||||
$return[$relname]["algorithm"] = $row["amname"];
|
||||
$return[$relname]["partial"] = $row["partial"];
|
||||
$indexpr = preg_split('~(?<=\)), (?=\()~', $row["indexpr"]); //! '), (' used in expression
|
||||
foreach (explode(" ", $row["indkey"]) as $indkey) {
|
||||
$return[$relname]["columns"][] = ($indkey ? $columns[$indkey] : array_shift($indexpr));
|
||||
}
|
||||
foreach (explode(" ", $row["indoption"]) as $indoption) {
|
||||
$return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
|
||||
}
|
||||
$return[$relname]["lengths"] = array();
|
||||
}
|
||||
@@ -514,12 +561,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 = (SELECT pc.oid FROM pg_class AS pc INNER JOIN pg_namespace AS pn ON (pn.oid = pc.relnamespace) WHERE pc.relname = " . q($table) . " AND pn.nspname = current_schema())
|
||||
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)) {
|
||||
@@ -527,8 +575,8 @@ ORDER BY conkey, conname") as $row
|
||||
$row['table'] = idf_unescape($match2[4]);
|
||||
}
|
||||
$row['target'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[3])));
|
||||
$row['on_delete'] = (preg_match("~ON DELETE (driver()->onActions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
|
||||
$row['on_update'] = (preg_match("~ON UPDATE (driver()->onActions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
|
||||
$row['on_delete'] = (preg_match("~ON DELETE (" . driver()->onActions . ")~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
|
||||
$row['on_update'] = (preg_match("~ON UPDATE (" . driver()->onActions . ")~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
|
||||
$return[$row['conname']] = $row;
|
||||
}
|
||||
}
|
||||
@@ -536,7 +584,7 @@ ORDER BY conkey, conname") as $row
|
||||
}
|
||||
|
||||
function view($name) {
|
||||
return array("select" => trim(get_val("SELECT pg_get_viewdef(" . get_val("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($name)) . ")")));
|
||||
return array("select" => trim(get_val("SELECT pg_get_viewdef(" . driver()->tableOid($name) . ")")));
|
||||
}
|
||||
|
||||
function collations() {
|
||||
@@ -619,7 +667,31 @@ ORDER BY conkey, conname") as $row
|
||||
}
|
||||
$alter = array_merge($alter, $foreign);
|
||||
if ($table == "") {
|
||||
array_unshift($queries, "CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)");
|
||||
$status = "";
|
||||
if ($partitioning) {
|
||||
$cockroach = (connection()->flavor == 'cockroach');
|
||||
$status = " PARTITION BY $partitioning[partition_by]($partitioning[partition])";
|
||||
if ($partitioning["partition_by"] == 'HASH') {
|
||||
$partitions = +$partitioning["partitions"];
|
||||
for ($i=0; $i < $partitions; $i++) {
|
||||
$queries[] = "CREATE TABLE " . idf_escape($name . "_$i") . " PARTITION OF " . idf_escape($name) . " FOR VALUES WITH (MODULUS $partitions, REMAINDER $i)";
|
||||
}
|
||||
} else {
|
||||
$prev = "MINVALUE";
|
||||
foreach ($partitioning["partition_names"] as $i => $val) {
|
||||
$value = $partitioning["partition_values"][$i];
|
||||
$partition = " VALUES " . ($partitioning["partition_by"] == 'LIST' ? "IN ($value)" : "FROM ($prev) TO ($value)");
|
||||
if ($cockroach) {
|
||||
$status .= ($i ? "," : " (") . "\n PARTITION " . (preg_match('~^DEFAULT$~i', $val) ? $val : idf_escape($val)) . "$partition";
|
||||
} else {
|
||||
$queries[] = "CREATE TABLE " . idf_escape($name . "_$val") . " PARTITION OF " . idf_escape($name) . " FOR$partition";
|
||||
}
|
||||
$prev = $value;
|
||||
}
|
||||
$status .= ($cockroach ? "\n)" : "");
|
||||
}
|
||||
}
|
||||
array_unshift($queries, "CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)$status");
|
||||
} elseif ($alter) {
|
||||
array_unshift($queries, "ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter));
|
||||
}
|
||||
@@ -646,7 +718,7 @@ ORDER BY conkey, conname") as $row
|
||||
$queries = array();
|
||||
foreach ($alter as $val) {
|
||||
if ($val[0] != "INDEX") {
|
||||
//! descending UNIQUE indexes results in syntax error
|
||||
//! descending UNIQUE indexes result in syntax error
|
||||
$create[] = ($val[2] == "DROP"
|
||||
? "\nDROP CONSTRAINT " . idf_escape($val[1])
|
||||
: "\nADD" . ($val[1] != "" ? " CONSTRAINT " . idf_escape($val[1]) : "") . " $val[0] " . ($val[0] == "PRIMARY" ? "KEY " : "") . "(" . implode(", ", $val[2]) . ")"
|
||||
@@ -654,7 +726,12 @@ ORDER BY conkey, conname") as $row
|
||||
} elseif ($val[2] == "DROP") {
|
||||
$drop[] = idf_escape($val[1]);
|
||||
} else {
|
||||
$queries[] = "CREATE INDEX " . idf_escape($val[1] != "" ? $val[1] : uniqid($table . "_")) . " ON " . table($table) . " (" . implode(", ", $val[2]) . ")";
|
||||
$queries[] = "CREATE INDEX " . idf_escape($val[1] != "" ? $val[1] : uniqid($table . "_"))
|
||||
. " ON " . table($table)
|
||||
. ($val[3] ? " USING $val[3]" : "")
|
||||
. " (" . implode(", ", $val[2]) . ")"
|
||||
. ($val[4] ? " WHERE $val[4]" : "")
|
||||
;
|
||||
}
|
||||
}
|
||||
if ($create) {
|
||||
@@ -750,7 +827,7 @@ FROM information_schema.routines
|
||||
WHERE routine_schema = current_schema() AND specific_name = ' . q($name));
|
||||
$return = idx($rows, 0, array());
|
||||
$return["returns"] = array("type" => $return["type_udt_name"]);
|
||||
$return["fields"] = get_rows('SELECT parameter_name AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout
|
||||
$return["fields"] = get_rows('SELECT COALESCE(parameter_name, ordinal_position::text) AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout
|
||||
FROM information_schema.parameters
|
||||
WHERE specific_schema = current_schema() AND specific_name = ' . q($name) . '
|
||||
ORDER BY ordinal_position');
|
||||
@@ -796,7 +873,7 @@ ORDER BY SPECIFIC_NAME');
|
||||
return get_key_vals(
|
||||
"SELECT oid, typname
|
||||
FROM pg_type
|
||||
WHERE typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema())
|
||||
WHERE typnamespace = " . driver()->nsOid . "
|
||||
AND typtype IN ('b','d','e')
|
||||
AND typelem = 0"
|
||||
);
|
||||
@@ -836,7 +913,7 @@ AND typelem = 0"
|
||||
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 " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " $fkey[definition];\n";
|
||||
}
|
||||
|
||||
return ($return ? "$return\n" : $return);
|
||||
@@ -847,9 +924,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);
|
||||
|
||||
@@ -857,7 +935,7 @@ 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) {
|
||||
@@ -873,8 +951,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];"
|
||||
;
|
||||
@@ -895,19 +973,27 @@ 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)";
|
||||
|
||||
$return .= implode(",\n ", $return_parts) . "\n) WITH (oids = " . ($status['Oid'] ? 'true' : 'false') . ");";
|
||||
$partition = driver()->partitionsInfo($status['Name']);
|
||||
if ($partition) {
|
||||
$return .= "\nPARTITION BY $partition[partition_by]($partition[partition])";
|
||||
}
|
||||
//! parse pg_class.relpartbound to create PARTITION OF
|
||||
//! don't insert partitioned data twice
|
||||
|
||||
$return .= "\nWITH (oids = " . ($status['Oid'] ? 'true' : 'false') . ");";
|
||||
|
||||
// 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']) . ";";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,8 +1019,16 @@ AND typelem = 0"
|
||||
}
|
||||
|
||||
|
||||
function use_sql($database) {
|
||||
return "\connect " . idf_escape($database);
|
||||
function use_sql($database, $style = "") {
|
||||
$name = idf_escape($database);
|
||||
$return = "";
|
||||
if (preg_match('~CREATE~', $style)) {
|
||||
if ($style == "DROP+CREATE") {
|
||||
$return = "DROP DATABASE IF EXISTS $name;\n";
|
||||
}
|
||||
$return .= "CREATE DATABASE $name;\n"; //! get info from pg_database
|
||||
}
|
||||
return "$return\\connect $name";
|
||||
}
|
||||
|
||||
function show_variables() {
|
||||
@@ -953,9 +1047,11 @@ AND typelem = 0"
|
||||
}
|
||||
|
||||
function support($feature) {
|
||||
return preg_match('~^(check|database|table|columns|sql|indexes|descidx|comment|view|' . (min_version(9.3) ? 'materializedview|' : '') . 'scheme|' . (min_version(11) ? 'procedure|' : '') . 'routine|sequence|trigger|type|variables|drop_col'
|
||||
return preg_match('~^(check|columns|comment|database|drop_col|dump|descidx|indexes|kill|partial_indexes|routine|scheme|sequence|sql|table|trigger|type|variables|view'
|
||||
. (min_version(9.3) ? '|materializedview' : '')
|
||||
. (min_version(11) ? '|procedure' : '')
|
||||
. (connection()->flavor == 'cockroach' ? '' : '|processlist') // https://github.com/cockroachdb/cockroach/issues/24745
|
||||
. '|kill|dump)$~', $feature)
|
||||
. ')$~', $feature)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ add_driver("sqlite", "SQLite");
|
||||
|
||||
if (isset($_GET["sqlite"])) {
|
||||
define('Adminer\DRIVER', "sqlite");
|
||||
if (class_exists("SQLite3") && $_GET["ext"] != "pdo") {
|
||||
|
||||
if (class_exists("SQLite3") && $_GET["ext"] != "pdo") {
|
||||
abstract class SqliteDb extends SqlDb {
|
||||
public $extension = "SQLite3";
|
||||
private $link;
|
||||
|
||||
function attach(?string $filename, string $username, string $password): string {
|
||||
function attach(string $filename, string $username, string $password): string {
|
||||
$this->link = new \SQLite3($filename);
|
||||
$version = $this->link->version();
|
||||
$this->server_info = $version["versionString"];
|
||||
@@ -75,11 +75,8 @@ if (isset($_GET["sqlite"])) {
|
||||
abstract class SqliteDb extends PdoDb {
|
||||
public $extension = "PDO_SQLite";
|
||||
|
||||
function attach(?string $filename, string $username, string $password): string {
|
||||
$this->dsn(DRIVER . ":$filename", "", "");
|
||||
$this->query("PRAGMA foreign_keys = 1");
|
||||
$this->query("PRAGMA busy_timeout = 500");
|
||||
return '';
|
||||
function attach(string $filename, string $username, string $password): string {
|
||||
return $this->dsn(DRIVER . ":$filename", "", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +84,7 @@ if (isset($_GET["sqlite"])) {
|
||||
|
||||
if (class_exists('Adminer\SqliteDb')) {
|
||||
class Db extends SqliteDb {
|
||||
function attach(?string $filename, string $username, string $password): string {
|
||||
function attach(string $filename, string $username, string $password): string {
|
||||
parent::attach($filename, $username, $password);
|
||||
$this->query("PRAGMA foreign_keys = 1");
|
||||
$this->query("PRAGMA busy_timeout = 500");
|
||||
@@ -122,7 +119,7 @@ if (isset($_GET["sqlite"])) {
|
||||
public $functions = array("hex", "length", "lower", "round", "unixepoch", "upper");
|
||||
public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
|
||||
|
||||
static function connect(?string $server, string $username, string $password) {
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
if ($password != "") {
|
||||
return lang('Database does not support password.');
|
||||
}
|
||||
@@ -452,7 +449,7 @@ if (isset($_GET["sqlite"])) {
|
||||
* @param list<list<string>> $fields [process_field()], empty to preserve
|
||||
* @param string[] $originals [$original => idf_escape($new_column)], empty to preserve
|
||||
* @param string[] $foreign [format_foreign_key()], empty to preserve
|
||||
* @param numeric-string $auto_increment set auto_increment to this value, "" to preserve
|
||||
* @param numeric-string|'' $auto_increment set auto_increment to this value, "" to preserve
|
||||
* @param list<array{string, string, list<string>|'DROP'}> $indexes [[$type, $name, $columns]], empty to preserve
|
||||
* @param string $drop_check CHECK constraint to drop
|
||||
* @param string $add_check CHECK constraint to add
|
||||
@@ -680,7 +677,7 @@ if (isset($_GET["sqlite"])) {
|
||||
return "DELETE FROM " . table($table);
|
||||
}
|
||||
|
||||
function use_sql($database) {
|
||||
function use_sql($database, $style = "") {
|
||||
}
|
||||
|
||||
function trigger_sql($table) {
|
||||
|
||||
@@ -41,16 +41,9 @@ SET foreign_key_checks = 0;
|
||||
foreach ((array) $databases as $db) {
|
||||
adminer()->dumpDatabase($db);
|
||||
if (connection()->select_db($db)) {
|
||||
if ($is_sql && preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE " . idf_escape($db), 1))) {
|
||||
set_utf8mb4($create);
|
||||
if ($style == "DROP+CREATE") {
|
||||
echo "DROP DATABASE IF EXISTS " . idf_escape($db) . ";\n";
|
||||
}
|
||||
echo "$create;\n";
|
||||
}
|
||||
if ($is_sql) {
|
||||
if ($style) {
|
||||
echo use_sql($db) . ";\n\n";
|
||||
echo use_sql($db, $style) . ";\n\n";
|
||||
}
|
||||
$out = "";
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,7 +11,7 @@ if (substr(VERSION, -4) != '-dev') {
|
||||
header("Cache-Control: immutable");
|
||||
}
|
||||
|
||||
@ini_set("zlib.output_compression", 1); // @ - may be disabled
|
||||
@ini_set("zlib.output_compression", '1'); // @ - may be disabled
|
||||
|
||||
if ($_GET["file"] == "default.css") {
|
||||
header("Content-Type: text/css; charset=utf-8");
|
||||
@@ -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');
|
||||
|
||||
@@ -31,7 +31,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-
|
||||
$result
|
||||
);
|
||||
if (!$row["drop"]) {
|
||||
$error = "$error<br>" . lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.'); //! no partitioning
|
||||
$error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.'); //! no partitioning
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)); ?>
|
||||
|
||||
@@ -60,6 +60,10 @@ class Adminer {
|
||||
return get_databases($flush);
|
||||
}
|
||||
|
||||
/** Print links after list of plugins */
|
||||
function pluginsLinks(): void {
|
||||
}
|
||||
|
||||
/** Operators used in select
|
||||
* @return list<string> operators
|
||||
*/
|
||||
@@ -81,6 +85,10 @@ class Adminer {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/** Called after connecting and selecting a database */
|
||||
function afterConnect(): void {
|
||||
}
|
||||
|
||||
/** Headers to send before HTML output */
|
||||
function headers(): void {
|
||||
}
|
||||
@@ -104,15 +112,24 @@ class Adminer {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Print extra classes in <body class>; must start with a space */
|
||||
function bodyClass(): void {
|
||||
echo " adminer";
|
||||
}
|
||||
|
||||
/** Get URLs of the CSS files
|
||||
* @return list<string>
|
||||
* @return string[] key is URL, value is either 'light' (supports only light color scheme), 'dark' or '' (both)
|
||||
*/
|
||||
function css(): array {
|
||||
$return = array();
|
||||
foreach (array("", "-dark") as $mode) {
|
||||
$filename = "adminer$mode.css";
|
||||
if (file_exists($filename)) {
|
||||
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
|
||||
$file = file_get_contents($filename);
|
||||
$return["$filename?v=" . crc32($file)] = ($mode
|
||||
? "dark"
|
||||
: (preg_match('~prefers-color-scheme:\s*dark~', $file) ? '' : 'light')
|
||||
);
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
@@ -123,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">');
|
||||
@@ -175,6 +192,7 @@ class Adminer {
|
||||
* @param ?string $set new item options, NULL for no new item
|
||||
*/
|
||||
function selectLinks(array $tableStatus, ?string $set = ""): void {
|
||||
$name = $tableStatus["Name"];
|
||||
echo '<p class="links">';
|
||||
$links = array("select" => lang('Select data'));
|
||||
if (support("table") || support("indexes")) {
|
||||
@@ -184,15 +202,16 @@ class Adminer {
|
||||
if (support("table")) {
|
||||
$is_view = is_view($tableStatus);
|
||||
if ($is_view) {
|
||||
$links["view"] = lang('Alter view');
|
||||
} else {
|
||||
if (support("view")) {
|
||||
$links["view"] = lang('Alter view');
|
||||
}
|
||||
} elseif (function_exists('Adminer\alter_table')) {
|
||||
$links["create"] = lang('Alter table');
|
||||
}
|
||||
}
|
||||
if ($set !== null) {
|
||||
$links["edit"] = lang('New item');
|
||||
}
|
||||
$name = $tableStatus["Name"];
|
||||
foreach ($links as $key => $val) {
|
||||
echo " <a href='" . h(ME) . "$key=" . urlencode($name) . ($key == "edit" ? $set : "") . "'" . bold(isset($_GET[$key])) . ">$val</a>";
|
||||
}
|
||||
@@ -269,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) {
|
||||
@@ -278,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 {
|
||||
@@ -287,14 +306,14 @@ class Adminer {
|
||||
: (preg_match('~json~', $field["type"]) ? "<code class='jush-js'>$val</code>"
|
||||
: $val)
|
||||
));
|
||||
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && !is_utf8($val)) {
|
||||
if (is_blob($field) && !is_utf8($val)) {
|
||||
$return = "<i>" . lang('%d byte(s)', strlen($original)) . "</i>";
|
||||
}
|
||||
return ($link ? "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : "") . ">$return</a>" : $return);
|
||||
}
|
||||
|
||||
/** Value conversion used in select and edit
|
||||
* @param Field $field
|
||||
* @param array{type: string} $field
|
||||
*/
|
||||
function editVal(?string $val, array $field): ?string {
|
||||
return $val;
|
||||
@@ -339,9 +358,15 @@ class Adminer {
|
||||
|
||||
/** Print list of indexes on table in tabular format
|
||||
* @param Index[] $indexes
|
||||
* @param TableStatus $tableStatus
|
||||
*/
|
||||
function tableIndexesPrint(array $indexes): void {
|
||||
function tableIndexesPrint(array $indexes, array $tableStatus): void {
|
||||
$partial = false;
|
||||
foreach ($indexes as $name => $index) {
|
||||
$partial |= !!$index["partial"];
|
||||
}
|
||||
echo "<table>\n";
|
||||
$default_algorithm = first(driver()->indexAlgorithms($tableStatus));
|
||||
foreach ($indexes as $name => $index) {
|
||||
ksort($index["columns"]); // enforce correct columns order
|
||||
$print = array();
|
||||
@@ -351,7 +376,14 @@ class Adminer {
|
||||
. ($index["descs"][$key] ? " DESC" : "")
|
||||
;
|
||||
}
|
||||
echo "<tr title='" . h($name) . "'><th>$index[type]<td>" . implode(", ", $print) . "\n";
|
||||
|
||||
echo "<tr title='" . h($name) . "'>";
|
||||
echo "<th>$index[type]" . ($default_algorithm && $index['algorithm'] != $default_algorithm ? " ($index[algorithm])" : "");
|
||||
echo "<td>" . implode(", ", $print);
|
||||
if ($partial) {
|
||||
echo "<td>" . ($index['partial'] ? "<code class='jush-" . JUSH . "'>WHERE " . h($index['partial']) : "");
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
echo "</table>\n";
|
||||
}
|
||||
@@ -391,9 +423,9 @@ class Adminer {
|
||||
foreach ($indexes as $i => $index) {
|
||||
if ($index["type"] == "FULLTEXT") {
|
||||
echo "<div>(<i>" . implode("</i>, <i>", array_map('Adminer\h', $index["columns"])) . "</i>) AGAINST";
|
||||
echo " <input type='search' name='fulltext[$i]' value='" . h($_GET["fulltext"][$i]) . "'>";
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -529,46 +561,44 @@ class Adminer {
|
||||
function selectSearchProcess(array $fields, array $indexes): array {
|
||||
$return = array();
|
||||
foreach ($indexes as $i => $index) {
|
||||
if ($index["type"] == "FULLTEXT" && $_GET["fulltext"][$i] != "") {
|
||||
if ($index["type"] == "FULLTEXT" && idx($_GET["fulltext"], $i) != "") {
|
||||
$return[] = "MATCH (" . implode(", ", array_map('Adminer\idf_escape', $index["columns"])) . ") AGAINST (" . q($_GET["fulltext"][$i]) . (isset($_GET["boolean"][$i]) ? " IN BOOLEAN MODE" : "") . ")";
|
||||
}
|
||||
}
|
||||
foreach ((array) $_GET["where"] as $key => $val) {
|
||||
if ("$val[col]$val[val]" != "" && in_array($val["op"], adminer()->operators())) {
|
||||
$prefix = "";
|
||||
$cond = " $val[op]";
|
||||
if (preg_match('~IN$~', $val["op"])) {
|
||||
$in = process_length($val["val"]);
|
||||
$cond .= " " . ($in != "" ? $in : "(NULL)");
|
||||
} elseif ($val["op"] == "SQL") {
|
||||
$cond = " $val[val]"; // SQL injection
|
||||
} elseif ($val["op"] == "LIKE %%") {
|
||||
$cond = " LIKE " . adminer()->processInput(idx($fields, $val["col"], array()), "%$val[val]%"); // this is used by search anywhere which doesn't set $val["col"]
|
||||
} elseif ($val["op"] == "ILIKE %%") {
|
||||
$cond = " ILIKE " . adminer()->processInput($fields[$val["col"]], "%$val[val]%");
|
||||
} elseif ($val["op"] == "FIND_IN_SET") {
|
||||
$prefix = "$val[op](" . q($val["val"]) . ", ";
|
||||
$cond = ")";
|
||||
} elseif (!preg_match('~NULL$~', $val["op"])) {
|
||||
$cond .= " " . adminer()->processInput($fields[$val["col"]], $val["val"]);
|
||||
}
|
||||
if ($val["col"] != "") {
|
||||
$return[] = $prefix . driver()->convertSearch(idf_escape($val["col"]), $val, $fields[$val["col"]]) . $cond;
|
||||
} else {
|
||||
// find anywhere
|
||||
$cols = array();
|
||||
foreach ($fields as $name => $field) {
|
||||
if (
|
||||
isset($field["privileges"]["where"])
|
||||
&& (preg_match('~^[-\d.' . (preg_match('~IN$~', $val["op"]) ? ',' : '') . ']+$~', $val["val"]) || !preg_match('~' . number_type() . '|bit~', $field["type"]))
|
||||
&& (!preg_match("~[\x80-\xFF]~", $val["val"]) || preg_match('~char|text|enum|set~', $field["type"]))
|
||||
&& (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val["val"]))
|
||||
) {
|
||||
$cols[] = $prefix . driver()->convertSearch(idf_escape($name), $val, $field) . $cond;
|
||||
}
|
||||
$col = $val["col"];
|
||||
if ("$col$val[val]" != "" && in_array($val["op"], adminer()->operators())) {
|
||||
$conds = array();
|
||||
foreach (($col != "" ? array($col => $fields[$col]) : $fields) as $name => $field) {
|
||||
$prefix = "";
|
||||
$cond = " $val[op]";
|
||||
if (preg_match('~IN$~', $val["op"])) {
|
||||
$in = process_length($val["val"]);
|
||||
$cond .= " " . ($in != "" ? $in : "(NULL)");
|
||||
} elseif ($val["op"] == "SQL") {
|
||||
$cond = " $val[val]"; // SQL injection
|
||||
} elseif (preg_match('~^(I?LIKE) %%$~', $val["op"], $match)) {
|
||||
$cond = " $match[1] " . adminer()->processInput($field, "%$val[val]%");
|
||||
} elseif ($val["op"] == "FIND_IN_SET") {
|
||||
$prefix = "$val[op](" . q($val["val"]) . ", ";
|
||||
$cond = ")";
|
||||
} elseif (!preg_match('~NULL$~', $val["op"])) {
|
||||
$cond .= " " . adminer()->processInput($field, $val["val"]);
|
||||
}
|
||||
if ($col != "" || ( // find anywhere
|
||||
isset($field["privileges"]["where"])
|
||||
&& (preg_match('~^[-\d.' . (preg_match('~IN$~', $val["op"]) ? ',' : '') . ']+$~', $val["val"]) || !preg_match('~' . number_type() . '|bit~', $field["type"]))
|
||||
&& (!preg_match("~[\x80-\xFF]~", $val["val"]) || preg_match('~char|text|enum|set~', $field["type"]))
|
||||
&& (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val["val"]))
|
||||
)) {
|
||||
$conds[] = $prefix . driver()->convertSearch(idf_escape($name), $val, $field) . $cond;
|
||||
}
|
||||
$return[] = ($cols ? "(" . implode(" OR ", $cols) . ")" : "1 = 0");
|
||||
}
|
||||
$return[] =
|
||||
(count($conds) == 1 ? $conds[0] :
|
||||
($conds ? "(" . implode(" OR ", $conds) . ")" :
|
||||
"1 = 0"
|
||||
));
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
@@ -640,13 +670,13 @@ class Adminer {
|
||||
}
|
||||
$history[$_GET["db"]][] = array($query, time(), $time); // not DB - $_GET["db"] is changed in database.inc.php //! respect $_GET["ns"]
|
||||
$sql_id = "sql-" . count($history[$_GET["db"]]);
|
||||
$return = "<a href='#$sql_id' class='toggle'>" . lang('SQL command') . "</a>\n";
|
||||
$return = "<a href='#$sql_id' class='toggle'>" . lang('SQL command') . "</a> <a href='' class='jsonly copy'>🗐</a>\n";
|
||||
if (!$failed && ($warnings = driver()->warnings())) {
|
||||
$id = "warnings-" . count($history[$_GET["db"]]);
|
||||
$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>'
|
||||
@@ -675,7 +705,7 @@ class Adminer {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($key && $functions && !preg_match('~set|blob|bytea|raw|file|bool~', $field["type"])) {
|
||||
if ($key && $functions && !preg_match('~set|bool~', $field["type"]) && !is_blob($field)) {
|
||||
$return .= "/SQL";
|
||||
}
|
||||
}
|
||||
@@ -689,13 +719,13 @@ class Adminer {
|
||||
* @param ?string $table null in call.inc.php
|
||||
* @param Field $field
|
||||
* @param string $attrs attributes to use inside the tag
|
||||
* @param string|string[]|false|null $value false means original value
|
||||
* @return string custom input field or empty string for default
|
||||
*/
|
||||
function editInput(?string $table, array $field, string $attrs, ?string $value): string {
|
||||
function editInput(?string $table, array $field, string $attrs, $value): string {
|
||||
if ($field["type"] == "enum") {
|
||||
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='-1' checked><i>" . lang('original') . "</i></label> " : "")
|
||||
. ($field["null"] ? "<label><input type='radio'$attrs value=''" . ($value !== null || isset($_GET["select"]) ? "" : " checked") . "><i>NULL</i></label> " : "")
|
||||
. enum_input("radio", $attrs, $field, $value, $value === 0 ? 0 : null) // 0 - empty value
|
||||
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='orig' checked><i>" . lang('original') . "</i></label> " : "")
|
||||
. enum_input("radio", $attrs, $field, $value, "NULL")
|
||||
;
|
||||
}
|
||||
return "";
|
||||
@@ -726,7 +756,7 @@ class Adminer {
|
||||
} elseif (preg_match('~^([+-]|\|\|)$~', $function)) {
|
||||
$return = idf_escape($name) . " $function $return";
|
||||
} elseif (preg_match('~^[+-] interval$~', $function)) {
|
||||
$return = idf_escape($name) . " $function " . (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) ? $value : $return);
|
||||
$return = idf_escape($name) . " $function " . (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) && JUSH != "pgsql" ? $value : $return);
|
||||
} elseif (preg_match('~^(addtime|subtime|concat)$~', $function)) {
|
||||
$return = "$function(" . idf_escape($name) . ", $return)";
|
||||
} elseif (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) {
|
||||
@@ -815,7 +845,7 @@ class Adminer {
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = connection()->query($query, 1); // 1 - MYSQLI_USE_RESULT //! enum and set as numbers
|
||||
$result = connection()->query($query, 1); // 1 - MYSQLI_USE_RESULT
|
||||
if ($result) {
|
||||
$insert = "";
|
||||
$buffer = "";
|
||||
@@ -891,7 +921,7 @@ class Adminer {
|
||||
* @return string filename without extension
|
||||
*/
|
||||
function dumpFilename(string $identifier): string {
|
||||
return friendly_url($identifier != "" ? $identifier : (SERVER != "" ? SERVER : "localhost"));
|
||||
return friendly_url($identifier != "" ? $identifier : (SERVER ?: "localhost"));
|
||||
}
|
||||
|
||||
/** Send headers for export
|
||||
@@ -938,6 +968,12 @@ class Adminer {
|
||||
echo (support("scheme") ? "<a href='" . h(ME) . "scheme='>" . ($_GET["ns"] != "" ? lang('Alter schema') : lang('Create schema')) . "</a>\n" : "");
|
||||
echo ($_GET["ns"] !== "" ? '<a href="' . h(ME) . 'schema=">' . lang('Database schema') . "</a>\n" : "");
|
||||
echo (support("privileges") ? "<a href='" . h(ME) . "privileges='>" . lang('Privileges') . "</a>\n" : "");
|
||||
if ($_GET["ns"] !== "") {
|
||||
echo (support("routine") ? "<a href='#routines'>" . lang('Routines') . "</a>\n" : "");
|
||||
echo (support("sequence") ? "<a href='#sequences'>" . lang('Sequences') . "</a>\n" : "");
|
||||
echo (support("type") ? "<a href='#user-types'>" . lang('User types') . "</a>\n" : "");
|
||||
echo (support("event") ? "<a href='#events'>" . lang('Events') . "</a>\n" : "");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -960,7 +996,7 @@ class Adminer {
|
||||
if ($password !== null) {
|
||||
$dbs = $_SESSION["db"][$vendor][$server][$username];
|
||||
foreach (($dbs ? array_keys($dbs) : array("")) as $db) {
|
||||
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($name) " . h($username . ($server != "" ? "@" . adminer()->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
|
||||
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($name) " . h("$username@" . ($server != "" ? adminer()->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,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" : "");
|
||||
@@ -1018,17 +1054,27 @@ class Adminer {
|
||||
foreach ($tables as $table => $type) {
|
||||
$links[] = preg_quote($table, '/');
|
||||
}
|
||||
echo "var jushLinks = { " . JUSH . ": [ '" . js_escape(ME) . (support("table") ? "table=" : "select=") . "\$&', /\\b(" . implode("|", $links) . ")\\b/g ] };\n";
|
||||
echo "var jushLinks = { " . JUSH . ":";
|
||||
json_row(js_escape(ME) . (support("table") ? "table" : "select") . '=$&', '/\b(' . implode('|', $links) . ')\b/g', false);
|
||||
if (support('routine')) {
|
||||
foreach (routines() as $row) {
|
||||
json_row(js_escape(ME) . 'function=' . urlencode($row["SPECIFIC_NAME"]) . '&name=$&', '/\b' . preg_quote($row["ROUTINE_NAME"], '/') . '(?=["`]?\()/g', false);
|
||||
}
|
||||
}
|
||||
json_row('');
|
||||
echo "};\n";
|
||||
foreach (array("bac", "bra", "sqlite_quo", "mssql_bra") as $val) {
|
||||
echo "jushLinks.$val = jushLinks." . JUSH . ";\n";
|
||||
}
|
||||
$tablesColumns = array_fill_keys(array_keys($tables), array());
|
||||
foreach (driver()->allFields() as $table => $fields) {
|
||||
foreach ($fields as $field) {
|
||||
$tablesColumns[$table][] = $field["field"];
|
||||
if (isset($_GET["sql"]) || isset($_GET["trigger"]) || isset($_GET["check"])) {
|
||||
$tablesColumns = array_fill_keys(array_keys($tables), array());
|
||||
foreach (driver()->allFields() as $table => $fields) {
|
||||
foreach ($fields as $field) {
|
||||
$tablesColumns[$table][] = $field["field"];
|
||||
}
|
||||
}
|
||||
echo "addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('" . idf_escape("") . "', " . json_encode($tablesColumns) . "); });\n";
|
||||
}
|
||||
echo "addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('" . idf_escape("") . "', " . json_encode($tablesColumns) . "); });\n";
|
||||
}
|
||||
echo "</script>\n";
|
||||
}
|
||||
@@ -1072,8 +1118,9 @@ class Adminer {
|
||||
function tablesPrint(array $tables): void {
|
||||
echo "<ul id='tables'>" . script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
|
||||
foreach ($tables as $table => $status) {
|
||||
$table = "$table"; // do not highlight "0" as active everywhere
|
||||
$name = adminer()->tableName($status);
|
||||
if ($name != "") {
|
||||
if ($name != "" && !$status["partition"]) {
|
||||
echo '<li><a href="' . h(ME) . 'select=' . urlencode($table) . '"'
|
||||
. bold($_GET["select"] == $table || $_GET["edit"] == $table, "select")
|
||||
. " title='" . lang('Select data') . "'>" . lang('select') . "</a> "
|
||||
@@ -1088,4 +1135,33 @@ 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]
|
||||
*/
|
||||
function processList(): array {
|
||||
return process_list();
|
||||
}
|
||||
|
||||
/** Kill a process
|
||||
* @param numeric-string $id
|
||||
* @return Result|bool
|
||||
*/
|
||||
function killProcess(string $id) {
|
||||
return kill_process($id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ if (isset($_GET["username"]) && !class_exists('Adminer\Db')) {
|
||||
|
||||
$connection = '';
|
||||
if (isset($_GET["username"]) && is_string(get_password())) {
|
||||
list($host, $port) = explode(":", SERVER, 2);
|
||||
list(, $port) = host_port(SERVER);
|
||||
if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80
|
||||
auth_error(lang('Connecting to privileged ports is not allowed.'), $permanent);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -91,8 +85,8 @@ Adminer::$instance =
|
||||
include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
|
||||
|
||||
define('Adminer\JUSH', Driver::$jush);
|
||||
define('Adminer\SERVER', $_GET[DRIVER]); // read from pgsql=localhost, '' means default server, null means no server
|
||||
define('Adminer\DB', $_GET["db"]); // for the sake of speed and size
|
||||
define('Adminer\SERVER', "" . $_GET[DRIVER]); // read from pgsql=localhost, '' means default server
|
||||
define('Adminer\DB', "$_GET[db]"); // for the sake of speed and size
|
||||
define(
|
||||
'Adminer\ME',
|
||||
preg_replace('~\?.*~', '', relative_uri()) . '?'
|
||||
@@ -108,3 +102,5 @@ include "../adminer/include/xxtea.inc.php";
|
||||
include "../adminer/include/auth.inc.php";
|
||||
include "./include/editing.inc.php";
|
||||
include "./include/connect.inc.php";
|
||||
|
||||
adminer()->afterConnect();
|
||||
|
||||
@@ -89,14 +89,26 @@ if (
|
||||
echo script("tableCheck();");
|
||||
}
|
||||
|
||||
if (isset(adminer()->plugins) && is_array(adminer()->plugins)) {
|
||||
if (!empty(adminer()->plugins)) {
|
||||
echo "<div class='plugins'>\n";
|
||||
echo "<h3>" . lang('Loaded plugins') . "</h3>\n<ul>\n";
|
||||
foreach (adminer()->plugins as $plugin) {
|
||||
$reflection = new \ReflectionObject($plugin);
|
||||
echo "<li><b>" . get_class($plugin) . "</b>" . h(preg_match('~^/[\s*]+(.+)~', $reflection->getDocComment(), $match) ? ": $match[1]" : "") . "\n";
|
||||
$description = (method_exists($plugin, 'description') ? $plugin->description() : "");
|
||||
if (!$description) {
|
||||
$reflection = new \ReflectionObject($plugin);
|
||||
if (preg_match('~^/[\s*]+(.+)~', $reflection->getDocComment(), $match)) {
|
||||
$description = $match[1];
|
||||
}
|
||||
}
|
||||
$screenshot = (method_exists($plugin, 'screenshot') ? $plugin->screenshot() : "");
|
||||
echo "<li><b>" . get_class($plugin) . "</b>"
|
||||
. h($description ? ": $description" : "")
|
||||
. ($screenshot ? " (<a href='" . h($screenshot) . "'" . target_blank() . ">" . lang('screenshot') . "</a>)" : "")
|
||||
. "\n"
|
||||
;
|
||||
}
|
||||
echo "</ul>\n";
|
||||
adminer()->pluginsLinks();
|
||||
echo "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ abstract class SqlDb {
|
||||
/** Connect to server
|
||||
* @return string error message
|
||||
*/
|
||||
abstract function attach(?string $server, string $username, string $password): string;
|
||||
abstract function attach(string $server, string $username, string $password): string;
|
||||
|
||||
/** Quote string to use in SQL
|
||||
* @return string escaped string enclosed in '
|
||||
|
||||
@@ -27,17 +27,13 @@ function page_header(string $title, string $error = "", $breadcrumb = array(), s
|
||||
<title><?php echo $title_page; ?></title>
|
||||
<link rel="stylesheet" href="../adminer/static/default.css">
|
||||
<?php
|
||||
|
||||
$css = adminer()->css();
|
||||
$has_light = false;
|
||||
$has_dark = false;
|
||||
foreach ($css as $filename) {
|
||||
if (strpos($filename, "adminer.css") !== false) {
|
||||
$has_light = true;
|
||||
}
|
||||
if (strpos($filename, "adminer-dark.css") !== false) {
|
||||
$has_dark = true;
|
||||
}
|
||||
if (is_int(key($css))) { // legacy return value
|
||||
$css = array_fill_keys($css, 'light');
|
||||
}
|
||||
$has_light = in_array('light', $css) || in_array('', $css);
|
||||
$has_dark = in_array('dark', $css) || in_array('', $css);
|
||||
$dark = ($has_light
|
||||
? ($has_dark ? null : false) // both styles - autoswitching, only adminer.css - light
|
||||
: ($has_dark ?: null) // only adminer-dark.css - dark, neither - autoswitching
|
||||
@@ -47,6 +43,7 @@ function page_header(string $title, string $error = "", $breadcrumb = array(), s
|
||||
echo "<link rel='stylesheet'" . ($dark ? "" : $media) . " href='../adminer/static/dark.css'>\n";
|
||||
}
|
||||
echo "<meta name='color-scheme' content='" . ($dark === null ? "light dark" : ($dark ? "dark" : "light")) . "'>\n";
|
||||
|
||||
// this is matched by compile.php
|
||||
echo script_src("../adminer/static/functions.js");
|
||||
echo script_src("static/editing.js");
|
||||
@@ -54,29 +51,19 @@ function page_header(string $title, string $error = "", $breadcrumb = array(), s
|
||||
echo "<link rel='icon' href='data:image/gif;base64,R0lGODlhEAAQAJEAAAQCBPz+/PwCBAROZCH5BAEAAAAALAAAAAAQABAAAAI2hI+pGO1rmghihiUdvUBnZ3XBQA7f05mOak1RWXrNq5nQWHMKvuoJ37BhVEEfYxQzHjWQ5qIAADs='>\n";
|
||||
echo "<link rel='apple-touch-icon' href='../adminer/static/logo.png'>\n";
|
||||
}
|
||||
foreach ($css as $val) {
|
||||
echo "<link rel='stylesheet'" . (preg_match('~-dark\.~', $val) && !$dark ? $media : "") . " href='" . h($val) . "'>\n";
|
||||
foreach ($css as $url => $mode) {
|
||||
$attrs = ($mode == 'dark' && !$dark
|
||||
? $media
|
||||
: ($mode == 'light' && $has_dark ? " media='(prefers-color-scheme: light)'" : "")
|
||||
);
|
||||
echo "<link rel='stylesheet'$attrs href='" . h($url) . "'>\n";
|
||||
}
|
||||
echo "\n<body class='" . lang('ltr') . " nojs'>\n";
|
||||
echo "\n<body class='" . lang('ltr') . " nojs";
|
||||
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.')) . "';
|
||||
@@ -150,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'",
|
||||
|
||||
@@ -7,7 +7,7 @@ function add_driver(string $id, string $name): void {
|
||||
}
|
||||
|
||||
/** Get driver name */
|
||||
function get_driver(string $id): string {
|
||||
function get_driver(string $id): ?string {
|
||||
return SqlDriver::$drivers[$id];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -26,6 +27,7 @@ abstract class SqlDriver {
|
||||
/** @var list<string> */ public $functions = array(); // functions used in select
|
||||
/** @var list<string> */ public $grouping = array(); // grouping functions used in select
|
||||
/** @var string */ public $onActions = "RESTRICT|NO ACTION|CASCADE|SET NULL|SET DEFAULT"; // used in foreign_keys()
|
||||
/** @var list<string> */ public $partitionBy = array(); // supported partitioning types
|
||||
/** @var string */ public $inout = "IN|OUT|INOUT"; // used in routines
|
||||
/** @var string */ public $enumLength = "'(?:''|[^'\\\\]|\\\\.)*'"; // regular expression for parsing enum lengths
|
||||
/** @var list<string> */ public $generated = array(); // allowed types of generated columns
|
||||
@@ -33,7 +35,7 @@ abstract class SqlDriver {
|
||||
/** Connect to the database
|
||||
* @return Db|string string for error
|
||||
*/
|
||||
static function connect(?string $server, string $username, string $password) {
|
||||
static function connect(string $server, string $username, string $password) {
|
||||
$connection = new Db;
|
||||
return ($connection->attach($server, $username, $password) ?: $connection);
|
||||
}
|
||||
@@ -188,19 +190,11 @@ abstract class SqlDriver {
|
||||
return $idf;
|
||||
}
|
||||
|
||||
/** Convert operator so it can be used in search */
|
||||
function convertOperator(string $operator): string {
|
||||
return $operator;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
: (is_resource($val) ? stream_get_contents($val) : $val)
|
||||
);
|
||||
return (method_exists($this->conn, 'value') ? $this->conn->value($val, $field) : $val);
|
||||
}
|
||||
|
||||
/** Quote binary string */
|
||||
@@ -220,6 +214,27 @@ abstract class SqlDriver {
|
||||
function tableHelp(string $name, bool $is_view = false) {
|
||||
}
|
||||
|
||||
/** Get tables this table inherits from
|
||||
* @return list<array{table: string, ns: string}>
|
||||
*/
|
||||
function inheritsFrom(string $table): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/** Get inherited tables
|
||||
* @return list<array{table: string, ns: string}>
|
||||
*/
|
||||
function inheritedTables(string $table): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/** Get partitions info
|
||||
* @return Partitions
|
||||
*/
|
||||
function partitionsInfo(string $table): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/** Check if C-style escapes are supported */
|
||||
function hasCStyleEscapes(): bool {
|
||||
return false;
|
||||
@@ -239,6 +254,14 @@ abstract class SqlDriver {
|
||||
return !is_view($table_status);
|
||||
}
|
||||
|
||||
/** Return list of supported index algorithms, first one is default
|
||||
* @param TableStatus $tableStatus
|
||||
* @return list<string>
|
||||
*/
|
||||
function indexAlgorithms(array $tableStatus): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/** Get defined check constraints
|
||||
* @return string[] [$name => $clause]
|
||||
*/
|
||||
@@ -246,23 +269,27 @@ 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
|
||||
* @return array<list<array{field:string, null:bool, type:string, length:?numeric-string, primary?:numeric-string}>>
|
||||
* @return array<list<array{field:string, null:bool, type:string, length:?numeric-string}>> optionally also 'primary'
|
||||
*/
|
||||
function allFields(): array {
|
||||
$return = array();
|
||||
foreach (get_rows("SELECT TABLE_NAME AS tab, COLUMN_NAME AS field, IS_NULLABLE AS nullable, DATA_TYPE AS type, CHARACTER_MAXIMUM_LENGTH AS length" . (JUSH == 'sql' ? ", COLUMN_KEY = 'PRI' AS `primary`" : "") . "
|
||||
if (DB != "") {
|
||||
foreach (
|
||||
get_rows("SELECT TABLE_NAME AS tab, COLUMN_NAME AS field, IS_NULLABLE AS nullable, DATA_TYPE AS type, CHARACTER_MAXIMUM_LENGTH AS length" . (JUSH == 'sql' ? ", COLUMN_KEY = 'PRI' AS `primary`" : "") . "
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . "
|
||||
ORDER BY TABLE_NAME, ORDINAL_POSITION", $this->conn) as $row) {
|
||||
$row["null"] = ($row["nullable"] == "YES");
|
||||
$return[$row["tab"]][] = $row;
|
||||
ORDER BY TABLE_NAME, ORDINAL_POSITION", $this->conn) as $row
|
||||
) {
|
||||
$row["null"] = ($row["nullable"] == "YES");
|
||||
$return[$row["tab"]][] = $row;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -150,13 +143,13 @@ function select_input(string $attrs, array $options, ?string $value = "", string
|
||||
* @param string $key or "" to close the object
|
||||
* @param string|int $val
|
||||
*/
|
||||
function json_row(string $key, $val = null): void {
|
||||
function json_row(string $key, $val = null, bool $escape = true): void {
|
||||
static $first = true;
|
||||
if ($first) {
|
||||
echo "{";
|
||||
}
|
||||
if ($key != "") {
|
||||
echo ($first ? "" : ",") . "\n\t\"" . addcslashes($key, "\r\n\t\"\\/") . '": ' . ($val !== null ? '"' . addcslashes($val, "\r\n\"\\/") . '"' : 'null');
|
||||
echo ($first ? "" : ",") . "\n\t\"" . addcslashes($key, "\r\n\t\"\\/") . '": ' . ($val !== null ? ($escape ? '"' . addcslashes($val, "\r\n\"\\/") . '"' : $val) : 'null');
|
||||
$first = false;
|
||||
} else {
|
||||
echo "\n}\n";
|
||||
@@ -201,20 +194,6 @@ function edit_type(string $key, array $field, array $collations, array $foreign_
|
||||
);
|
||||
}
|
||||
|
||||
/** Get partition info
|
||||
* @return array{partition_by:string, partition:string, partitions:string, partition_names:list<string>, partition_values:list<string>}
|
||||
*/
|
||||
function get_partitions_info(string $table): array {
|
||||
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table);
|
||||
$result = connection()->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
|
||||
$return = array();
|
||||
list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row();
|
||||
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
|
||||
$return["partition_names"] = array_keys($partitions);
|
||||
$return["partition_values"] = array_values($partitions);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/** Filter length value including enums */
|
||||
function process_length(?string $length): string {
|
||||
$enum_length = driver()->enumLength;
|
||||
@@ -260,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
|
||||
@@ -328,7 +310,7 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", array $f
|
||||
echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n";
|
||||
echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", driver()->inout), $field["inout"]) : "") . "<th>";
|
||||
if ($display) {
|
||||
echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'>";
|
||||
echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'" . (isset($_POST["add"][$i-1]) ? " autofocus" : "") . ">";
|
||||
}
|
||||
echo input_hidden("fields[$i][orig]", $orig);
|
||||
edit_type("fields[$i]", $field, $collations, $foreign_keys);
|
||||
@@ -339,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>";
|
||||
@@ -470,7 +454,7 @@ function create_routine($routine, array $row): string {
|
||||
ksort($fields); // enforce fields order
|
||||
foreach ($fields as $field) {
|
||||
if ($field["field"] != "") {
|
||||
$set[] = (preg_match("~^(driver()->inout)\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET");
|
||||
$set[] = (preg_match("~^(" . driver()->inout . ")\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET");
|
||||
}
|
||||
}
|
||||
$definition = rtrim($row["definition"], ";");
|
||||
@@ -499,8 +483,9 @@ function format_foreign_key(array $foreign_key): string {
|
||||
. ($ns != "" && $ns != $_GET["ns"] ? idf_escape($ns) . "." : "")
|
||||
. idf_escape($foreign_key["table"])
|
||||
. " (" . 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]" : "")
|
||||
. (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]" : "")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -521,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
|
||||
@@ -264,9 +273,10 @@ function where(array $where, array $fields = array()): string {
|
||||
$field_type = $field["type"];
|
||||
$return[] = $column
|
||||
. (JUSH == "sql" && $field_type == "json" ? " = CAST(" . q($val) . " AS JSON)"
|
||||
: (JUSH == "pgsql" && preg_match('~^json~', $field_type) ? "::jsonb = " . q($val) . "::jsonb"
|
||||
: (JUSH == "sql" && is_numeric($val) && preg_match('~\.~', $val) ? " LIKE " . q($val) // LIKE because of floats but slow with ints
|
||||
: (JUSH == "mssql" && strpos($field_type, "datetime") === false ? " LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val)) // LIKE because of text but it does not work with datetime
|
||||
: " = " . unconvert_field($field, q($val)))))
|
||||
: " = " . unconvert_field($field, q($val))))))
|
||||
; //! enum and set
|
||||
if (JUSH == "sql" && preg_match('~char|text~', $field_type) && preg_match("~[^ -@]~", $val)) { // not just [a-z] to catch non-ASCII characters
|
||||
$return[] = "$column = " . q($val) . " COLLATE " . charset(connection()) . "_bin";
|
||||
@@ -291,7 +301,7 @@ function where_check(string $val, array $fields = array()): string {
|
||||
* @param int $i condition order
|
||||
* @param string $column column identifier
|
||||
*/
|
||||
function where_link(int $i, string $column, string $value, string $operator = "="): string {
|
||||
function where_link(int $i, string $column, ?string $value, string $operator = "="): string {
|
||||
return "&where%5B$i%5D%5Bcol%5D=" . urlencode($column) . "&where%5B$i%5D%5Bop%5D=" . urlencode(($value !== null ? $operator : "IS NULL")) . "&where%5B$i%5D%5Bval%5D=" . urlencode($value);
|
||||
}
|
||||
|
||||
@@ -319,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" : "")
|
||||
@@ -337,11 +347,11 @@ function get_settings(string $cookie): array {
|
||||
}
|
||||
|
||||
/** Get setting stored in a cookie
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
function get_setting(string $key, string $cookie = "adminer_settings") {
|
||||
$settings = get_settings($cookie);
|
||||
return $settings[$key];
|
||||
function get_setting(string $key, string $cookie = "adminer_settings", $default = null) {
|
||||
return idx(get_settings($cookie), $key, $default);
|
||||
}
|
||||
|
||||
/** Store settings to a cookie
|
||||
@@ -460,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);
|
||||
}
|
||||
|
||||
@@ -595,10 +605,10 @@ function column_foreign_keys(string $table): array {
|
||||
return $return;
|
||||
}
|
||||
|
||||
/** Compute fields() from $_POST edit data
|
||||
/** Compute fields() from $_POST edit data; used by Mongo and SimpleDB
|
||||
* @return Field[] same as fields()
|
||||
*/
|
||||
function fields_from_edit(): array { // used by Mongo and SimpleDB
|
||||
function fields_from_edit(): array {
|
||||
$return = array();
|
||||
foreach ((array) $_POST["field_keys"] as $key => $val) {
|
||||
if ($val != "") {
|
||||
@@ -641,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
|
||||
@@ -685,7 +696,7 @@ function file_open_lock(string $filename) {
|
||||
if (!$fp) {
|
||||
return;
|
||||
}
|
||||
chmod($filename, 0660);
|
||||
@chmod($filename, 0660); // @ - may not be permitted
|
||||
if (!flock($fp, LOCK_EX)) {
|
||||
fclose($fp);
|
||||
return;
|
||||
@@ -750,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>";
|
||||
}
|
||||
@@ -777,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
|
||||
@@ -790,6 +817,13 @@ function select_value($val, string $link, array $field, ?string $text_length): s
|
||||
return adminer()->selectVal($return, $link, $field, $val);
|
||||
}
|
||||
|
||||
/** Check whether the field type is blob or equivalent
|
||||
* @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()));
|
||||
}
|
||||
|
||||
/** Check whether the string is e-mail address */
|
||||
function is_mail(?string $email): bool {
|
||||
$atom = '[-a-z0-9!#$%&\'*+/=?^_`{|}~]'; // characters of local-name
|
||||
@@ -801,14 +835,24 @@ 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~', $field["type"]);
|
||||
return !preg_match('~' . number_type() . '|date|time|year~', $field["type"]);
|
||||
}
|
||||
|
||||
/** Split server into host and (port or socket)
|
||||
* @return array{0: string, 1: string}
|
||||
*/
|
||||
function host_port(string $server) {
|
||||
return (preg_match('~^(\[(.+)]|([^:]+)):([^:]+)$~', $server, $match) // [a:b] - IPv6
|
||||
? array($match[2] . $match[3], $match[4])
|
||||
: array($server, '')
|
||||
);
|
||||
}
|
||||
|
||||
/** Get query to compute number of found rows
|
||||
|
||||
@@ -176,18 +176,34 @@ 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
|
||||
* @param mixed $value string|array
|
||||
* @param string|string[]|false|null $value false means original value
|
||||
*/
|
||||
function enum_input(string $type, string $attrs, array $field, $value, ?string $empty = null): string {
|
||||
function enum_input(string $type, string $attrs, array $field, $value, string $empty = ""): string {
|
||||
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
|
||||
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === $empty) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
|
||||
foreach ($matches[1] as $i => $val) {
|
||||
$prefix = ($field["type"] == "enum" ? "val-" : "");
|
||||
$checked = (is_array($value) ? in_array("null", $value) : $value === null);
|
||||
$return = ($field["null"] && $prefix ? "<label><input type='$type'$attrs value='null'" . ($checked ? " checked" : "") . "><i>$empty</i></label>" : "");
|
||||
foreach ($matches[1] as $val) {
|
||||
$val = stripcslashes(str_replace("''", "'", $val));
|
||||
$checked = (is_array($value) ? in_array($val, $value) : $value === $val);
|
||||
$return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h(adminer()->editVal($val, $field)) . '</label>';
|
||||
$checked = (is_array($value) ? in_array($prefix . $val, $value) : $value === $val);
|
||||
$return .= " <label><input type='$type'$attrs value='" . h($prefix . $val) . "'" . ($checked ? ' checked' : '') . '>' . h(adminer()->editVal($val, $field)) . '</label>';
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
@@ -200,21 +216,23 @@ 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;
|
||||
}
|
||||
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + adminer()->editFunctions($field);
|
||||
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
|
||||
$attrs = " name='fields[$name]'$disabled" . ($autofocus ? " autofocus" : "");
|
||||
$enums = driver()->enumLength($field);
|
||||
if ($enums) {
|
||||
$field["type"] = "enum";
|
||||
$field["length"] = $enums;
|
||||
}
|
||||
$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") {
|
||||
@@ -222,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))
|
||||
@@ -234,15 +252,10 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
|
||||
echo "<input type='hidden'$attrs value='0'>"
|
||||
. "<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>";
|
||||
} elseif ($field["type"] == "set") {
|
||||
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
|
||||
foreach ($matches[1] as $i => $val) {
|
||||
$val = stripcslashes(str_replace("''", "'", $val));
|
||||
$checked = in_array($val, explode(",", $value), true);
|
||||
echo " <label><input type='checkbox' name='fields[$name][$i]' value='" . h($val) . "'" . ($checked ? ' checked' : '') . ">" . h(adminer()->editVal($val, $field)) . '</label>';
|
||||
}
|
||||
} elseif (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
|
||||
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") {
|
||||
@@ -290,19 +303,21 @@ 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 = $_POST["fields"][$idf];
|
||||
$value = idx($_POST["fields"], $idf);
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
if ($field["type"] == "enum" || driver()->enumLength($field)) {
|
||||
if ($value == -1) {
|
||||
$value = idx($value, 0);
|
||||
if ($value == "orig" || !$value) {
|
||||
return false;
|
||||
}
|
||||
if ($value == "") {
|
||||
if ($value == "null") {
|
||||
return "NULL";
|
||||
}
|
||||
$value = substr($value, 4); // 4 - strlen("val-")
|
||||
}
|
||||
if ($field["auto_increment"] && $value == "") {
|
||||
return null;
|
||||
@@ -324,7 +339,7 @@ function process_input(array $field) {
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
|
||||
if (is_blob($field) && ini_bool("file_uploads")) {
|
||||
$file = get_file("fields-$idf");
|
||||
if (!is_string($file)) {
|
||||
return false; //! report errors
|
||||
@@ -381,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 {
|
||||
@@ -411,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";
|
||||
}
|
||||
@@ -450,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
|
||||
|
||||
@@ -66,6 +66,7 @@ function langs(): array {
|
||||
'fr' => 'Français', // Francis Gagné, Aurélien Royer
|
||||
'gl' => 'Galego', // Eduardo Penabad Ramos
|
||||
'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/
|
||||
'hi' => 'हिन्दी', // Joshi yogesh
|
||||
'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
|
||||
|
||||
@@ -71,11 +71,20 @@ if (extension_loaded('pdo')) {
|
||||
public $_offset = 0, $num_rows;
|
||||
|
||||
function fetch_assoc() {
|
||||
return $this->fetch(\PDO::FETCH_ASSOC);
|
||||
return $this->fetch_array(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
function fetch_row() {
|
||||
return $this->fetch(\PDO::FETCH_NUM);
|
||||
return $this->fetch_array(\PDO::FETCH_NUM);
|
||||
}
|
||||
|
||||
private function fetch_array(int $mode) {
|
||||
$return = $this->fetch($mode);
|
||||
return ($return ? array_map(array($this, 'unresource'), $return) : $return);
|
||||
}
|
||||
|
||||
private function unresource($val) {
|
||||
return (is_resource($val) ? stream_get_contents($val) : $val);
|
||||
}
|
||||
|
||||
function fetch_field(): \stdClass {
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
// the overridable methods don't use return type declarations so that plugins can be compatible with PHP 5
|
||||
abstract class Plugin {
|
||||
/** @var array<literal-string, string|list<string>>[] */ protected static $translations = array(); // key is language code
|
||||
/** @var array<literal-string, string|list<string>>[] */ protected $translations = array(); // key is language code
|
||||
|
||||
/** Translate a string from static::$translations; use Adminer\lang() for strings used by Adminer
|
||||
/** Get plain text plugin description; empty string means to use the first line of class doc-comment
|
||||
* @return string
|
||||
*/
|
||||
function description() {
|
||||
return $this->lang('');
|
||||
}
|
||||
|
||||
/** Get URL of plugin screenshot
|
||||
* @return string
|
||||
*/
|
||||
function screenshot() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Translate a string from $this->translations; Adminer\lang() doesn't work for single language versions
|
||||
* @param literal-string $idf
|
||||
* @param float|string $number
|
||||
*/
|
||||
protected function lang(string $idf, $number = null) {
|
||||
protected function lang(string $idf, $number = null): string {
|
||||
$args = func_get_args();
|
||||
$args[0] = idx(static::$translations[LANG], $idf) ?: $idf;
|
||||
$args[0] = idx($this->translations[LANG], $idf) ?: $idf;
|
||||
return call_user_func_array('Adminer\lang_format', $args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -67,7 +74,7 @@ class Plugins {
|
||||
function __call(string $name, array $params) {
|
||||
$args = array();
|
||||
foreach ($params as $key => $val) {
|
||||
// some plugins accept params by reference - we don't need to propage it outside, just to the other plugins
|
||||
// some plugins accept params by reference - we don't need to propagate it outside, just to the other plugins
|
||||
$args[] = &$params[$key];
|
||||
}
|
||||
$return = null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
const VERSION = "5.2.0-dev";
|
||||
const VERSION = "5.4.2";
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Adminer;
|
||||
$TABLE = $_GET["indexes"];
|
||||
$index_types = array("PRIMARY", "UNIQUE", "INDEX");
|
||||
$table_status = table_status1($TABLE, true);
|
||||
$index_algorithms = driver()->indexAlgorithms($table_status);
|
||||
if (preg_match('~MyISAM|M?aria' . (min_version(5.6, '10.0.5') ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
|
||||
$index_types[] = "FULLTEXT";
|
||||
}
|
||||
@@ -11,6 +12,7 @@ if (preg_match('~MyISAM|M?aria' . (min_version(5.7, '10.2.2') ? '|InnoDB' : '')
|
||||
$index_types[] = "SPATIAL";
|
||||
}
|
||||
$indexes = indexes($TABLE);
|
||||
$fields = fields($TABLE);
|
||||
$primary = array();
|
||||
if (JUSH == "mongo") { // doesn't support primary key
|
||||
$primary = $indexes["_id_"];
|
||||
@@ -29,13 +31,15 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
||||
$columns = array();
|
||||
$lengths = array();
|
||||
$descs = array();
|
||||
$index_condition = (support("partial_indexes") ? $index["partial"] : "");
|
||||
$index_algorithm = (in_array($index["algorithm"], $index_algorithms) ? $index["algorithm"] : "");
|
||||
$set = array();
|
||||
ksort($index["columns"]);
|
||||
foreach ($index["columns"] as $key => $column) {
|
||||
if ($column != "") {
|
||||
$length = idx($index["lengths"], $key);
|
||||
$desc = idx($index["descs"], $key);
|
||||
$set[] = idf_escape($column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : "");
|
||||
$set[] = ($fields[$column] ? idf_escape($column) : $column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : "");
|
||||
$columns[] = $column;
|
||||
$lengths[] = ($length ?: null);
|
||||
$descs[] = $desc;
|
||||
@@ -52,6 +56,8 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
||||
&& array_values($existing["columns"]) === $columns
|
||||
&& (!$existing["lengths"] || array_values($existing["lengths"]) === $lengths)
|
||||
&& array_values($existing["descs"]) === $descs
|
||||
&& $existing["partial"] == $index_condition
|
||||
&& (!$index_algorithms || $existing["algorithm"] == $index_algorithm)
|
||||
) {
|
||||
// skip existing index
|
||||
unset($indexes[$name]);
|
||||
@@ -59,7 +65,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
||||
}
|
||||
}
|
||||
if ($columns) {
|
||||
$alter[] = array($index["type"], $name, $set);
|
||||
$alter[] = array($index["type"], $name, $set, $index_algorithm, $index_condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +82,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
||||
|
||||
page_header(lang('Indexes'), $error, array("table" => $TABLE), h($TABLE));
|
||||
|
||||
$fields = array_keys(fields($TABLE));
|
||||
$fields_keys = array_keys($fields);
|
||||
if ($_POST["add"]) {
|
||||
foreach ($row["indexes"] as $key => $index) {
|
||||
if ($index["columns"][count($index["columns"])] != "") {
|
||||
@@ -105,20 +111,35 @@ $show_options = ($_POST ? $_POST["options"] : get_setting("index_options"));
|
||||
<table class="nowrap">
|
||||
<thead><tr>
|
||||
<th id="label-type"><?php echo lang('Index Type'); ?>
|
||||
<?php
|
||||
$idxopts = " class='idxopts" . ($show_options ? "" : " hidden") . "'";
|
||||
if ($index_algorithms) {
|
||||
echo "<th id='label-algorithm'$idxopts>" . lang('Algorithm') . doc_link(array(
|
||||
'sql' => 'create-index.html#create-index-storage-engine-index-types',
|
||||
'mariadb' => 'storage-engine-index-types/',
|
||||
'pgsql' => 'indexes-types.html',
|
||||
));
|
||||
}
|
||||
?>
|
||||
<th><input type="submit" class="wayoff"><?php
|
||||
echo lang('Columns') . ($lengths ? "<span class='idxopts" . ($show_options ? "" : " hidden") . "'> (" . lang('length') . ")</span>" : "");
|
||||
echo lang('Columns') . ($lengths ? "<span$idxopts> (" . lang('length') . ")</span>" : "");
|
||||
if ($lengths || support("descidx")) {
|
||||
echo checkbox("options", 1, $show_options, lang('Options'), "indexOptionsShow(this.checked)", "jsonly") . "\n";
|
||||
}
|
||||
?>
|
||||
<th id="label-name"><?php echo lang('Name'); ?>
|
||||
<?php
|
||||
if (support("partial_indexes")) {
|
||||
echo "<th id='label-condition'$idxopts>" . lang('Condition');
|
||||
}
|
||||
?>
|
||||
<th><noscript><?php echo icon("plus", "add[0]", "+", lang('Add next')); ?></noscript>
|
||||
</thead>
|
||||
<?php
|
||||
if ($primary) {
|
||||
echo "<tr><td>PRIMARY<td>";
|
||||
foreach ($primary["columns"] as $key => $column) {
|
||||
echo select_input(" disabled", $fields, $column);
|
||||
echo select_input(" disabled", $fields_keys, $column);
|
||||
echo "<label><input disabled type='checkbox'>" . lang('descending') . "</label> ";
|
||||
}
|
||||
echo "<td><td>\n";
|
||||
@@ -128,17 +149,21 @@ foreach ($row["indexes"] as $index) {
|
||||
if (!$_POST["drop_col"] || $j != key($_POST["drop_col"])) {
|
||||
echo "<tr><td>" . html_select("indexes[$j][type]", array(-1 => "") + $index_types, $index["type"], ($j == count($row["indexes"]) ? "indexesAddRow.call(this);" : ""), "label-type");
|
||||
|
||||
if ($index_algorithms) {
|
||||
echo "<td$idxopts>" . html_select("indexes[$j][algorithm]", array_merge(array(""), $index_algorithms), $index['algorithm'], "label-algorithm");
|
||||
}
|
||||
|
||||
echo "<td>";
|
||||
ksort($index["columns"]);
|
||||
$i = 1;
|
||||
foreach ($index["columns"] as $key => $column) {
|
||||
echo "<span>" . select_input(
|
||||
" name='indexes[$j][columns][$i]' title='" . lang('Column') . "'",
|
||||
($fields ? array_combine($fields, $fields) : $fields),
|
||||
($fields && ($column == "" || $fields[$column]) ? array_combine($fields_keys, $fields_keys) : array()),
|
||||
$column,
|
||||
"partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')"
|
||||
);
|
||||
echo "<span class='idxopts" . ($show_options ? "" : " hidden") . "'>";
|
||||
echo "<span$idxopts>";
|
||||
echo ($lengths ? "<input type='number' name='indexes[$j][lengths][$i]' class='size' value='" . h(idx($index["lengths"], $key)) . "' title='" . lang('Length') . "'>" : "");
|
||||
echo (support("descidx") ? checkbox("indexes[$j][descs][$i]", 1, idx($index["descs"], $key), lang('descending')) : "");
|
||||
echo "</span> </span>";
|
||||
@@ -146,6 +171,9 @@ foreach ($row["indexes"] as $index) {
|
||||
}
|
||||
|
||||
echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n";
|
||||
if (support("partial_indexes")) {
|
||||
echo "<td$idxopts><input name='indexes[$j][partial]' value='" . h($index["partial"]) . "' autocapitalize='off' aria-labelledby='label-condition'>\n";
|
||||
}
|
||||
echo "<td>" . icon("cross", "drop_col[$j]", "x", lang('Remove')) . script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");
|
||||
}
|
||||
$j++;
|
||||
|
||||
@@ -259,6 +259,56 @@ Lang::$translations = array(
|
||||
'Permanent link' => 'স্থায়ী লিংক',
|
||||
'Edit all' => 'সবগুলো সম্পাদনা করুন',
|
||||
'HH:MM:SS' => 'HH:MM:SS',
|
||||
'Check has been dropped.' => 'চেক ড্রপ করা হয়েছে।',
|
||||
'Check has been altered.' => 'চেক পরিবর্তন করা হয়েছে।',
|
||||
'Check has been created.' => 'চেক তৈরি করা হয়েছে।',
|
||||
'Alter check' => 'চেক পরিবর্তন করুন',
|
||||
'Create check' => 'চেক তৈরি করুন',
|
||||
'Drop %s?' => '%s ড্রপ করবেন?',
|
||||
'Tables have been optimized.' => 'টেবিলগুলি অপ্টিমাইজ করা হয়েছে।',
|
||||
'Materialized view' => 'মেটেরিয়ালাইজড ভিউ',
|
||||
'Vacuum' => 'ভ্যাকুয়াম',
|
||||
'Selected' => 'নির্বাচিত',
|
||||
'overwrite' => 'ওভাররাইট',
|
||||
'DB' => 'ডিবি',
|
||||
'Algorithm' => 'অ্যালগরিদম',
|
||||
'Columns' => 'কলাম',
|
||||
'Ctrl+click on a value to modify it.' => 'একটি মান পরিবর্তন করতে Ctrl+ক্লিক করুন।',
|
||||
'File must be in UTF-8 encoding.' => 'ফাইলটি UTF-8 এনকোডিংয়ে হতে হবে।',
|
||||
'Modify' => 'পরিবর্তন করুন',
|
||||
'Load more data' => 'আরও ডেটা লোড করুন',
|
||||
'Loading' => 'লোড হচ্ছে',
|
||||
'ATTACH queries are not supported.' => 'ATTACH কোয়েরি সমর্থিত নয়।',
|
||||
'Warnings' => 'সতর্কতা',
|
||||
'%d / ' => array('%d / '),
|
||||
'Limit rows' => 'সারি সীমিত করুন',
|
||||
'Inherits from' => 'থেকে উত্তরাধিকারসূত্রে প্রাপ্ত',
|
||||
'Checks' => 'চেকস',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer পাসওয়ার্ড ছাড়া ডাটাবেস অ্যাক্সেস সমর্থন করে না, <a href="https://www.adminer.org/en/password/"%s>আরও তথ্য</a>।',
|
||||
'Default value' => 'ডিফল্ট মান',
|
||||
'Full table scan' => 'সম্পূর্ণ টেবিল স্ক্যান',
|
||||
'Too many unsuccessful logins, try again in %d minute(s).' => array('অনেকগুলি ব্যর্থ লগইন প্রচেষ্টা, %d মিনিট পরে আবার চেষ্টা করুন।'),
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminer ব্যবহার করার জন্য ধন্যবাদ, <a href="https://www.adminer.org/en/donation/">দান করার</a> কথা বিবেচনা করুন।',
|
||||
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'মাস্টার পাসওয়ার্ডের মেয়াদ শেষ হয়েছে। এটিকে স্থায়ী করতে <a href="https://www.adminer.org/en/extension/"%s>ইমপ্লিমেন্ট</a> %s মেথড।',
|
||||
'The action will be performed after successful login with the same credentials.' => 'একই ক্রেডেনশিয়ালস দিয়ে সফলভাবে লগইন করার পরে এই কর্মটি সম্পাদন করা হবে।',
|
||||
'Connecting to privileged ports is not allowed.' => 'প্রিভিলেজড পোর্টে সংযোগ করা অনুমোদিত নয়।',
|
||||
'There is a space in the input password which might be the cause.' => 'ইনপুট পাসওয়ার্ডে একটি স্পেস রয়েছে যা এর কারণ হতে পারে।',
|
||||
'If you did not send this request from Adminer then close this page.' => 'আপনি যদি Adminer থেকে এই অনুরোধ না করে থাকেন তবে এই পৃষ্ঠাটি বন্ধ করুন।',
|
||||
'You can upload a big SQL file via FTP and import it from server.' => 'আপনি FTP এর মাধ্যমে একটি বড় SQL ফাইল আপলোড করতে পারেন এবং সার্ভার থেকে এটি ইম্পোর্ট করতে পারেন।',
|
||||
'Size' => 'আকার',
|
||||
'Compute' => 'কম্পিউট',
|
||||
'Loaded plugins' => 'লোড করা প্লাগইনগুলি',
|
||||
'screenshot' => 'স্ক্রিনশট',
|
||||
'You are offline.' => 'আপনি অফলাইনে আছেন।',
|
||||
'You have no privileges to update this table.' => 'এই টেবিল আপডেট করার জন্য আপনার কোন অনুমতি নেই।',
|
||||
'Saving' => 'সংরক্ষণ করা হচ্ছে',
|
||||
'Unknown error.' => 'অজানা ত্রুটি।',
|
||||
'%s must <a%s>return an array</a>.' => '%s অবশ্যই <a%s>একটি অ্যারে রিটার্ন করতে হবে</a>।',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>কনফিগার করুন</a> %s এ %s।',
|
||||
'Disable %s or enable %s or %s extensions.' => '%s নিষ্ক্রিয় করুন অথবা %s বা %s এক্সটেনশন সক্রিয় করুন।',
|
||||
'Database does not support password.' => 'ডাটাবেস পাসওয়ার্ড সমর্থন করে না।',
|
||||
'yes' => 'হ্যাঁ',
|
||||
'no' => 'না',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php bn` to update this file
|
||||
|
||||
@@ -13,9 +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.',
|
||||
'Loaded plugins' => 'Nahrané pluginy',
|
||||
'%s must <a%s>return an array</a>.' => '%s musí <a%s>vracet pole</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Nakonfigurujte</a> %s v %s.',
|
||||
'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>.',
|
||||
@@ -30,7 +28,7 @@ Lang::$translations = array(
|
||||
'Connecting to privileged ports is not allowed.' => 'Připojování k privilegovaným portům není povoleno.',
|
||||
'Disable %s or enable %s or %s extensions.' => 'Zakažte %s nebo povolte rozšíření %s nebo %s.',
|
||||
'Session support must be enabled.' => 'Session proměnné musí být povolené.',
|
||||
'Session expired, please login again.' => 'Session vypršela, přihlašte se prosím znovu.',
|
||||
'Session expired, please login again.' => 'Session vypršela, přihlaste se prosím znovu.',
|
||||
'The action will be performed after successful login with the same credentials.' => 'Akce bude provedena po úspěšném přihlášení se stejnými přihlašovacími údaji.',
|
||||
'%s version: %s through PHP extension %s' => 'Verze %s: %s přes PHP rozšíření %s',
|
||||
'Refresh' => 'Obnovit',
|
||||
@@ -79,6 +77,7 @@ Lang::$translations = array(
|
||||
'Webserver file %s' => 'Soubor %s na webovém serveru',
|
||||
'Run file' => 'Spustit soubor',
|
||||
'File does not exist.' => 'Soubor neexistuje.',
|
||||
'Increase %s.' => 'Zvyšte %s.',
|
||||
'File uploads are disabled.' => 'Nahrávání souborů není povoleno.',
|
||||
'Unable to upload a file.' => 'Nepodařilo se nahrát soubor.',
|
||||
'Maximum allowed file size is %sB.' => 'Maximální povolená velikost souboru je %sB.',
|
||||
@@ -197,6 +196,8 @@ Lang::$translations = array(
|
||||
'Partitions' => 'Oddíly',
|
||||
'Partition name' => 'Název oddílu',
|
||||
'Values' => 'Hodnoty',
|
||||
'Inherits from' => 'Zděděná z',
|
||||
'Inherited by' => 'Zděděné',
|
||||
|
||||
'View' => 'Pohled',
|
||||
'Materialized view' => 'Materializovaný pohled',
|
||||
@@ -212,6 +213,8 @@ Lang::$translations = array(
|
||||
'Add next' => 'Přidat další',
|
||||
'Index Type' => 'Typ indexu',
|
||||
'length' => 'délka',
|
||||
'Algorithm' => 'Algoritmus',
|
||||
'Condition' => 'Podmínka',
|
||||
|
||||
'Foreign keys' => 'Cizí klíče',
|
||||
'Foreign key' => 'Cizí klíč',
|
||||
@@ -352,6 +355,11 @@ Lang::$translations = array(
|
||||
'Check has been created.' => 'Kontrola byla vytvořena.',
|
||||
'Check has been altered.' => 'Kontrola byla změněna.',
|
||||
'Check has been dropped.' => 'Kontrola byla odstraněna.',
|
||||
|
||||
'Loaded plugins' => 'Nahrané pluginy',
|
||||
'%s must <a%s>return an array</a>.' => '%s musí <a%s>vracet pole</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Nakonfigurujte</a> %s v %s.',
|
||||
'screenshot' => 'obrázek',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php cs` to update this file
|
||||
|
||||
@@ -137,6 +137,7 @@ Lang::$translations = array(
|
||||
'User has been created.' => 'Benutzer wurde erstellt.',
|
||||
'Hashed' => 'Hashed',
|
||||
'Column' => 'Spalte',
|
||||
'Columns' => 'Spalten',
|
||||
'Routine' => 'Routine',
|
||||
'Grant' => 'Erlauben',
|
||||
'Revoke' => 'Widerrufen',
|
||||
@@ -305,6 +306,7 @@ Lang::$translations = array(
|
||||
'Loaded plugins' => 'Geladene Plugins',
|
||||
'%s must <a%s>return an array</a>.' => '%s muss <a%s>ein Array zurückgeben</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Konfigure</a> %s mit %s.',
|
||||
'screenshot' => 'Screenshot',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php de` to update this file
|
||||
|
||||
314
adminer/lang/hi.inc.php
Normal file
314
adminer/lang/hi.inc.php
Normal file
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
Lang::$translations = array(
|
||||
'Login' => 'लॉगिन',
|
||||
'Logout successful.' => 'सफलतापूर्वक लॉगआउट हो गया।',
|
||||
'Invalid credentials.' => 'गलत पासवर्ड।',
|
||||
'Server' => 'सर्वर',
|
||||
'Username' => 'उपयोगकर्ता नाम',
|
||||
'Password' => 'पासवर्ड',
|
||||
'Select database' => 'डेटाबेस चुनें',
|
||||
'Invalid database.' => 'अमान्य डेटाबेस।',
|
||||
'Table has been dropped.' => 'टेबल हटा दिया गया है।',
|
||||
'Table has been altered.' => 'टेबल बदल दिया गया है।',
|
||||
'Table has been created.' => 'टेबल बनाया गया है।',
|
||||
'Alter table' => 'टेबल बदलें',
|
||||
'Create table' => 'टेबल बनाएं',
|
||||
'Table name' => 'टेबल का नाम',
|
||||
'engine' => 'इंजन',
|
||||
'collation' => 'कॉलेशन',
|
||||
'Column name' => 'कॉलम का नाम',
|
||||
'Type' => 'प्रकार',
|
||||
'Length' => 'लंबाई',
|
||||
'Auto Increment' => 'ऑटो इंक्रीमेंट',
|
||||
'Options' => 'विकल्प',
|
||||
'Save' => 'सहेजें',
|
||||
'Drop' => 'हटाएं',
|
||||
'Database has been dropped.' => 'डेटाबेस हटा दिया गया है।',
|
||||
'Database has been created.' => 'डेटाबेस बनाया गया है।',
|
||||
'Database has been renamed.' => 'डेटाबेस का नाम बदल दिया गया है।',
|
||||
'Database has been altered.' => 'डेटाबेस बदल दिया गया है।',
|
||||
'Alter database' => 'डेटाबेस बदलें',
|
||||
'Create database' => 'डेटाबेस बनाएं',
|
||||
'SQL command' => 'SQL कमांड',
|
||||
'Logout' => 'लॉगआउट',
|
||||
'Use' => 'उपयोग करें',
|
||||
'No tables.' => 'कोई टेबल नहीं।',
|
||||
'select' => 'चुनें',
|
||||
'Item has been deleted.' => 'आइटम हटा दिया गया है।',
|
||||
'Item has been updated.' => 'आइटम अपडेट किया गया है।',
|
||||
'Item%s has been inserted.' => 'आइटम%s डाला गया है।',
|
||||
'Edit' => 'संपादित करें',
|
||||
'Insert' => 'डालें',
|
||||
'Save and insert next' => 'सहेजें और अगला डालें',
|
||||
'Delete' => 'हटाएं',
|
||||
'Database' => 'डेटाबेस',
|
||||
'Routines' => 'रूटीन्स',
|
||||
'Indexes have been altered.' => 'इंडेक्स बदल दिए गए हैं।',
|
||||
'Indexes' => 'इंडेक्स',
|
||||
'Alter indexes' => 'इंडेक्स बदलें',
|
||||
'Add next' => 'अगला जोड़ें',
|
||||
'Language' => 'भाषा',
|
||||
'Select' => 'चुनें',
|
||||
'New item' => 'नया आइटम',
|
||||
'Search' => 'खोजें',
|
||||
'Sort' => 'क्रमबद्ध करें',
|
||||
'descending' => 'अवरोही',
|
||||
'Limit' => 'सीमा',
|
||||
'No rows.' => 'कोई पंक्ति नहीं।',
|
||||
'Action' => 'कार्रवाई',
|
||||
'edit' => 'संपादित करें',
|
||||
'Page' => 'पृष्ठ',
|
||||
'Query executed OK, %d row(s) affected.' => array('क्वेरी सफलतापूर्वक निष्पादित, %d पंक्ति प्रभावित।', 'क्वेरी सफलतापूर्वक निष्पादित, %d पंक्तियां प्रभावित।'),
|
||||
'Error in query' => 'क्वेरी में त्रुटि',
|
||||
'Execute' => 'निष्पादित करें',
|
||||
'Table' => 'टेबल',
|
||||
'Foreign keys' => 'फॉरेन की',
|
||||
'Triggers' => 'ट्रिगर्स',
|
||||
'View' => 'व्यू',
|
||||
'Unable to select the table' => 'टेबल चुनने में असमर्थ',
|
||||
'Invalid CSRF token. Send the form again.' => 'अमान्य CSRF टोकन। फॉर्म फिर से भेजें।',
|
||||
'Comment' => 'टिप्पणी',
|
||||
'Default values' => 'डिफ़ॉल्ट मान',
|
||||
'%d byte(s)' => array('%d बाइट', '%d बाइट्स'),
|
||||
'No commands to execute.' => 'निष्पादित करने के लिए कोई कमांड नहीं।',
|
||||
'Unable to upload a file.' => 'फाइल अपलोड करने में असमर्थ।',
|
||||
'File upload' => 'फाइल अपलोड',
|
||||
'File uploads are disabled.' => 'फाइल अपलोड अक्षम हैं।',
|
||||
'Routine has been called, %d row(s) affected.' => array('रूटीन कॉल किया गया, %d पंक्ति प्रभावित।', 'रूटीन कॉल किया गया, %d पंक्तियां प्रभावित।'),
|
||||
'Call' => 'कॉल',
|
||||
'No extension' => 'कोई एक्सटेंशन नहीं',
|
||||
'None of the supported PHP extensions (%s) are available.' => 'कोई समर्थित PHP एक्सटेंशन (%s) उपलब्ध नहीं है।',
|
||||
'Session support must be enabled.' => 'सेशन सपोर्ट सक्षम होना चाहिए।',
|
||||
'Session expired, please login again.' => 'सेशन समाप्त, कृपया फिर से लॉगिन करें।',
|
||||
'Text length' => 'टेक्स्ट लंबाई',
|
||||
'Foreign key has been dropped.' => 'फॉरेन की हटा दी गई है।',
|
||||
'Foreign key has been altered.' => 'फॉरेन की बदल दी गई है।',
|
||||
'Foreign key has been created.' => 'फॉरेन की बनाई गई है।',
|
||||
'Foreign key' => 'फॉरेन की',
|
||||
'Target table' => 'लक्ष्य टेबल',
|
||||
'Change' => 'बदलें',
|
||||
'Source' => 'स्रोत',
|
||||
'Target' => 'लक्ष्य',
|
||||
'Add column' => 'कॉलम जोड़ें',
|
||||
'Alter' => 'बदलें',
|
||||
'Add foreign key' => 'फॉरेन की जोड़ें',
|
||||
'ON DELETE' => 'ऑन डिलीट',
|
||||
'ON UPDATE' => 'ऑन अपडेट',
|
||||
'Index Type' => 'इंडेक्स प्रकार',
|
||||
'length' => 'लंबाई',
|
||||
'View has been dropped.' => 'व्यू हटा दिया गया है।',
|
||||
'View has been altered.' => 'व्यू बदल दिया गया है।',
|
||||
'View has been created.' => 'व्यू बनाया गया है।',
|
||||
'Alter view' => 'व्यू बदलें',
|
||||
'Create view' => 'व्यू बनाएं',
|
||||
'Name' => 'नाम',
|
||||
'Process list' => 'प्रक्रिया सूची',
|
||||
'%d process(es) have been killed.' => array('%d प्रक्रिया समाप्त की गई है।', '%d प्रक्रियाएं समाप्त की गई हैं।'),
|
||||
'Kill' => 'समाप्त करें',
|
||||
'Parameter name' => 'पैरामीटर नाम',
|
||||
'Database schema' => 'डेटाबेस स्कीमा',
|
||||
'Create procedure' => 'प्रक्रिया बनाएं',
|
||||
'Create function' => 'फंक्शन बनाएं',
|
||||
'Routine has been dropped.' => 'रूटीन हटा दिया गया है।',
|
||||
'Routine has been altered.' => 'रूटीन बदल दिया गया है।',
|
||||
'Routine has been created.' => 'रूटीन बनाया गया है।',
|
||||
'Alter function' => 'फंक्शन बदलें',
|
||||
'Alter procedure' => 'प्रक्रिया बदलें',
|
||||
'Return type' => 'वापसी प्रकार',
|
||||
'Add trigger' => 'ट्रिगर जोड़ें',
|
||||
'Trigger has been dropped.' => 'ट्रिगर हटा दिया गया है।',
|
||||
'Trigger has been altered.' => 'ट्रिगर बदल दिया गया है।',
|
||||
'Trigger has been created.' => 'ट्रिगर बनाया गया है।',
|
||||
'Alter trigger' => 'ट्रिगर बदलें',
|
||||
'Create trigger' => 'ट्रिगर बनाएं',
|
||||
'Time' => 'समय',
|
||||
'Event' => 'घटना',
|
||||
'%s version: %s through PHP extension %s' => 'संस्करण %s: %s, PHP एक्सटेंशन %s के माध्यम से',
|
||||
'%d row(s)' => array('%d पंक्ति', '%d पंक्तियां'),
|
||||
'Remove' => 'हटाएं',
|
||||
'Are you sure?' => 'क्या आप सुनिश्चित हैं?',
|
||||
'Privileges' => 'विशेषाधिकार',
|
||||
'Create user' => 'उपयोगकर्ता बनाएं',
|
||||
'User has been dropped.' => 'उपयोगकर्ता हटा दिया गया है।',
|
||||
'User has been altered.' => 'उपयोगकर्ता बदल दिया गया है।',
|
||||
'User has been created.' => 'उपयोगकर्ता बनाया गया है।',
|
||||
'Hashed' => 'हैश्ड',
|
||||
'Column' => 'कॉलम',
|
||||
'Routine' => 'रूटीन',
|
||||
'Grant' => 'अनुदान',
|
||||
'Revoke' => 'रद्द करें',
|
||||
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'बहुत बड़ा POST डेटा। डेटा कम करें या %s कॉन्फ़िगरेशन निर्देश बढ़ाएं।',
|
||||
'Logged as: %s' => '%s के रूप में लॉगिन',
|
||||
'Move up' => 'ऊपर ले जाएं',
|
||||
'Move down' => 'नीचे ले जाएं',
|
||||
'Functions' => 'फंक्शन्स',
|
||||
'Aggregation' => 'एग्रीगेशन',
|
||||
'Export' => 'निर्यात',
|
||||
'Output' => 'आउटपुट',
|
||||
'open' => 'खोलें',
|
||||
'save' => 'सहेजें',
|
||||
'Format' => 'प्रारूप',
|
||||
'Tables' => 'टेबल्स',
|
||||
'Data' => 'डेटा',
|
||||
'Event has been dropped.' => 'घटना हटा दी गई है।',
|
||||
'Event has been altered.' => 'घटना बदल दी गई है।',
|
||||
'Event has been created.' => 'घटना बनाई गई है।',
|
||||
'Alter event' => 'घटना बदलें',
|
||||
'Create event' => 'घटना बनाएं',
|
||||
'At given time' => 'निर्धारित समय पर',
|
||||
'Every' => 'हर',
|
||||
'Events' => 'घटनाएं',
|
||||
'Schedule' => 'अनुसूची',
|
||||
'Start' => 'शुरू',
|
||||
'End' => 'समाप्त',
|
||||
'Status' => 'स्थिति',
|
||||
'On completion preserve' => 'पूरा होने पर संरक्षित करें',
|
||||
'Tables and views' => 'टेबल्स और व्यूज',
|
||||
'Data Length' => 'डेटा लंबाई',
|
||||
'Index Length' => 'इंडेक्स लंबाई',
|
||||
'Data Free' => 'डेटा मुक्त',
|
||||
'Collation' => 'कॉलेशन',
|
||||
'Analyze' => 'विश्लेषण',
|
||||
'Optimize' => 'अनुकूलित',
|
||||
'Check' => 'जांच',
|
||||
'Repair' => 'मरम्मत',
|
||||
'Truncate' => 'ट्रंकेट',
|
||||
'Tables have been truncated.' => 'टेबल्स ट्रंकेट कर दिए गए हैं।',
|
||||
'Rows' => 'पंक्तियां',
|
||||
',' => ',',
|
||||
'0123456789' => '०१२३४५६७८९',
|
||||
'Tables have been moved.' => 'टेबल्स स्थानांतरित कर दिए गए हैं।',
|
||||
'Move to other database' => 'अन्य डेटाबेस में स्थानांतरित करें',
|
||||
'Move' => 'स्थानांतरित करें',
|
||||
'Engine' => 'इंजन',
|
||||
'Save and continue edit' => 'सहेजें और संपादन जारी रखें',
|
||||
'original' => 'मूल',
|
||||
'Tables have been dropped.' => 'टेबल्स हटा दिए गए हैं।',
|
||||
'%d item(s) have been affected.' => '%d आइटम प्रभावित हुए हैं।',
|
||||
'Whole result' => 'पूरा परिणाम',
|
||||
'Clone' => 'क्लोन',
|
||||
'Maximum number of allowed fields exceeded. Please increase %s.' => 'अनुमत फील्ड्स की अधिकतम संख्या पार हो गई। कृपया %s बढ़ाएं।',
|
||||
'Partition by' => 'द्वारा विभाजन',
|
||||
'Partitions' => 'पार्टीशन्स',
|
||||
'Partition name' => 'पार्टीशन नाम',
|
||||
'Values' => 'मान',
|
||||
'%d row(s) have been imported.' => array('%d पंक्ति आयात की गई है।', '%d पंक्तियां आयात की गई हैं।'),
|
||||
'anywhere' => 'कहीं भी',
|
||||
'Import' => 'आयात',
|
||||
'Stop on error' => 'त्रुटि पर रुकें',
|
||||
'%.3f s' => '%.3f सेकंड',
|
||||
'$1-$3-$5' => '$1-$3-$5',
|
||||
'[yyyy]-mm-dd' => '[yyyy]-mm-dd',
|
||||
'History' => 'इतिहास',
|
||||
'Variables' => 'चर',
|
||||
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'स्रोत और लक्ष्य कॉलम्स का डेटा प्रकार समान होना चाहिए, लक्ष्य कॉलम्स पर एक इंडेक्स होना चाहिए और संदर्भित डेटा मौजूद होना चाहिए।',
|
||||
'Relations' => 'संबंध',
|
||||
'Run file' => 'फाइल चलाएं',
|
||||
'Clear' => 'साफ़ करें',
|
||||
'Maximum allowed file size is %sB.' => 'अधिकतम अनुमत फाइल आकार %sB है।',
|
||||
'Numbers' => 'संख्याएं',
|
||||
'Date and time' => 'तिथि और समय',
|
||||
'Strings' => 'स्ट्रिंग्स',
|
||||
'Binary' => 'बाइनरी',
|
||||
'Lists' => 'सूचियां',
|
||||
'Editor' => 'संपादक',
|
||||
'Webserver file %s' => 'वेबसर्वर फाइल %s',
|
||||
'File does not exist.' => 'फाइल मौजूद नहीं है।',
|
||||
'%d in total' => 'कुल %d',
|
||||
'Permanent login' => 'स्थायी लॉगिन',
|
||||
'Databases have been dropped.' => 'डेटाबेस हटा दिए गए हैं।',
|
||||
'Search data in tables' => 'टेबल्स में डेटा खोजें',
|
||||
'Schema' => 'स्कीमा',
|
||||
'Alter schema' => 'स्कीमा बदलें',
|
||||
'Create schema' => 'स्कीमा बनाएं',
|
||||
'Schema has been dropped.' => 'स्कीमा हटा दी गई है।',
|
||||
'Schema has been created.' => 'स्कीमा बनाई गई है।',
|
||||
'Schema has been altered.' => 'स्कीमा बदल दी गई है।',
|
||||
'Sequences' => 'अनुक्रम',
|
||||
'Create sequence' => 'अनुक्रम बनाएं',
|
||||
'Alter sequence' => 'अनुक्रम बदलें',
|
||||
'Sequence has been dropped.' => 'अनुक्रम हटा दिया गया है।',
|
||||
'Sequence has been created.' => 'अनुक्रम बनाया गया है।',
|
||||
'Sequence has been altered.' => 'अनुक्रम बदल दिया गया है।',
|
||||
'User types' => 'उपयोगकर्ता प्रकार',
|
||||
'Create type' => 'प्रकार बनाएं',
|
||||
'Alter type' => 'प्रकार बदलें',
|
||||
'Type has been dropped.' => 'प्रकार हटा दिया गया है।',
|
||||
'Type has been created.' => 'प्रकार बनाया गया है।',
|
||||
'Use edit link to modify this value.' => 'इस मान को संशोधित करने के लिए संपादन लिंक का उपयोग करें।',
|
||||
'last' => 'अंतिम',
|
||||
'From server' => 'सर्वर से',
|
||||
'System' => 'सिस्टम',
|
||||
'Select data' => 'डेटा चुनें',
|
||||
'Show structure' => 'संरचना दिखाएं',
|
||||
'empty' => 'खाली',
|
||||
'Network' => 'नेटवर्क',
|
||||
'Geometry' => 'ज्यामिति',
|
||||
'File exists.' => 'फाइल मौजूद है।',
|
||||
'%d query(s) executed OK.' => array('%d क्वेरी सफलतापूर्वक निष्पादित।', '%d क्वेरीज़ सफलतापूर्वक निष्पादित।'),
|
||||
'Show only errors' => 'केवल त्रुटियां दिखाएं',
|
||||
'Refresh' => 'ताज़ा करें',
|
||||
'Invalid schema.' => 'अमान्य स्कीमा।',
|
||||
'Please use one of the extensions %s.' => 'कृपया %s एक्सटेंशन्स में से एक का उपयोग करें।',
|
||||
'now' => 'अब',
|
||||
'ltr' => 'ltr',
|
||||
'Tables have been copied.' => 'टेबल्स कॉपी कर दिए गए हैं।',
|
||||
'Copy' => 'कॉपी',
|
||||
'Permanent link' => 'स्थायी लिंक',
|
||||
'Edit all' => 'सभी संपादित करें',
|
||||
'HH:MM:SS' => 'HH:MM:SS',
|
||||
'Check has been dropped.' => 'चेक हटा दिया गया है।',
|
||||
'Check has been altered.' => 'चेक को बदल दिया गया है।',
|
||||
'Check has been created.' => 'चेक बनाया गया है।',
|
||||
'Alter check' => 'चेक बदलें',
|
||||
'Create check' => 'चेक बनाएँ',
|
||||
'Drop %s?' => '%s हटाएँ?',
|
||||
'Tables have been optimized.' => 'टेबल्स को ऑप्टिमाइज़ कर दिया गया है।',
|
||||
'Materialized view' => 'मटेरियलाइज़्ड व्यू',
|
||||
'Vacuum' => 'वैक्यूम',
|
||||
'Selected' => 'चयनित',
|
||||
'overwrite' => 'ओवरराइट',
|
||||
'DB' => 'डेटाबेस',
|
||||
'Algorithm' => 'एल्गोरिदम',
|
||||
'Columns' => 'कॉलम',
|
||||
'Ctrl+click on a value to modify it.' => 'किसी मान को संशोधित करने के लिए Ctrl+क्लिक करें।',
|
||||
'File must be in UTF-8 encoding.' => 'फ़ाइल UTF-8 एन्कोडिंग में होनी चाहिए।',
|
||||
'Modify' => 'संशोधित करें',
|
||||
'Load more data' => 'और डेटा लोड करें',
|
||||
'Loading' => 'लोड हो रहा है',
|
||||
'ATTACH queries are not supported.' => 'संलग्न क्वेरीज़ समर्थित नहीं हैं।',
|
||||
'Warnings' => 'चेतावनियाँ',
|
||||
'%d / ' => '%d / ',
|
||||
'Limit rows' => 'पंक्तियाँ सीमित करें',
|
||||
'Inherits from' => 'इनहेरिट करता है',
|
||||
'Checks' => 'चेक्स',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'एडमिनर बिना पासवर्ड के डेटाबेस एक्सेस करने का समर्थन नहीं करता, <a href="https://www.adminer.org/en/password/"%s>अधिक जानकारी</a>।',
|
||||
'Default value' => 'डिफ़ॉल्ट मान',
|
||||
'Full table scan' => 'पूरी टेबल स्कैन',
|
||||
'Too many unsuccessful logins, try again in %d minute(s).' => 'बहुत अधिक असफल लॉगिन प्रयास, %d मिनट बाद पुनः प्रयास करें।',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'एडमिनर उपयोग करने के लिए धन्यवाद, <a href="https://www.adminer.org/en/donation/">दान</a> करने पर विचार करें।',
|
||||
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'मास्टर पासवर्ड समाप्त हो गया। इसे स्थायी बनाने के लिए %s मेथड <a href="https://www.adminer.org/en/extension/"%s>इम्प्लीमेंट</a> करें।',
|
||||
'The action will be performed after successful login with the same credentials.' => 'यह क्रिया उसी क्रेडेंशियल्स से सफल लॉगिन के बाद की जाएगी।',
|
||||
'Connecting to privileged ports is not allowed.' => 'प्रिविलेज्ड पोर्ट्स से कनेक्ट करने की अनुमति नहीं है।',
|
||||
'There is a space in the input password which might be the cause.' => 'इनपुट पासवर्ड में एक स्पेस है जो कारण हो सकता है।',
|
||||
'If you did not send this request from Adminer then close this page.' => 'अगर आपने यह अनुरोध एडमिनर से नहीं भेजा है तो इस पेज को बंद करें।',
|
||||
'You can upload a big SQL file via FTP and import it from server.' => 'आप एक बड़ी SQL फ़ाइल FTP के माध्यम से अपलोड कर सकते हैं और सर्वर से इम्पोर्ट कर सकते हैं।',
|
||||
'Size' => 'आकार',
|
||||
'Compute' => 'कम्प्यूट',
|
||||
'Loaded plugins' => 'लोडेड प्लगइन्स',
|
||||
'screenshot' => 'स्क्रीनशॉट',
|
||||
'You are offline.' => 'आप ऑफ़लाइन हैं।',
|
||||
'You have no privileges to update this table.' => 'आपके पास इस टेबल को अपडेट करने की अनुमति नहीं है।',
|
||||
'Saving' => 'सेव हो रहा है',
|
||||
'Unknown error.' => 'अज्ञात त्रुटि।',
|
||||
'%s must <a%s>return an array</a>.' => '%s को <a%s>एक ऐरे रिटर्न</a> करना चाहिए।',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>कॉन्फ़िगर</a> %s में %s।',
|
||||
'Disable %s or enable %s or %s extensions.' => '%s को डिसेबल करें या %s या %s एक्सटेंशन्स को एनेबल करें।',
|
||||
'Database does not support password.' => 'डेटाबेस पासवर्ड का समर्थन नहीं करता।',
|
||||
'yes' => 'हाँ',
|
||||
'no' => 'नहीं',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php hi` to update this file
|
||||
@@ -5,13 +5,13 @@ Lang::$translations = array(
|
||||
'Login' => 'ログイン',
|
||||
'Logout successful.' => 'ログアウトしました。',
|
||||
'Invalid credentials.' => '不正なログインです。',
|
||||
'Server' => 'サーバ',
|
||||
'Username' => 'ユーザ名',
|
||||
'Server' => 'サーバー',
|
||||
'Username' => 'ユーザー名',
|
||||
'Password' => 'パスワード',
|
||||
'Loaded plugins' => '読込済プラグイン',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminerのご利用ありがとうございました。(寄付は<a href="https://www.adminer.org/en/donation/">こちら</a>)',
|
||||
'%s must <a%s>return an array</a>.' => '%s は<a%s>配列を返す</a>必要があります。',
|
||||
'<a%s>Configure</a> %s in %s.' => '%2$s の %1$s <a%s>を設定してください</a>。',
|
||||
'<a%s>Configure</a> %s in %s.' => '%2$s の %1$s を<a%s>設定</a>してください。',
|
||||
'There is a space in the input password which might be the cause.' => '入力されたパスワードに空白が含まれているので、それが原因かもしれません。',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer はパスワードのないデータベースへの接続には対応していません。(<a href="https://www.adminer.org/en/password/"%s>詳細</a>)',
|
||||
'Database does not support password.' => 'データベースがパスワードに対応していません。',
|
||||
@@ -20,14 +20,14 @@ Lang::$translations = array(
|
||||
'Select database' => 'データベースを選択してください',
|
||||
'Invalid database.' => '不正なデータベースです。',
|
||||
'Table has been dropped.' => 'テーブルを削除しました。',
|
||||
'Table has been altered.' => 'テーブルを変更しました。',
|
||||
'Table has been altered.' => 'テーブルの設定を変更しました。',
|
||||
'Table has been created.' => 'テーブルを作成しました。',
|
||||
'Alter table' => 'テーブルを変更',
|
||||
'Alter table' => 'テーブルの設定を変更',
|
||||
'Create table' => 'テーブルを作成',
|
||||
'Table name' => 'テーブル名',
|
||||
'engine' => 'エンジン',
|
||||
'collation' => '照合順序',
|
||||
'Column name' => '列名',
|
||||
'collation' => 'コレーション',
|
||||
'Column name' => 'カラム名',
|
||||
'Type' => '型',
|
||||
'Length' => '長さ',
|
||||
'Auto Increment' => '連番',
|
||||
@@ -38,16 +38,16 @@ Lang::$translations = array(
|
||||
'Database has been dropped.' => 'データベースを削除しました。',
|
||||
'Database has been created.' => 'データベースを作成しました。',
|
||||
'Database has been renamed.' => 'データベースの名前を変えました。',
|
||||
'Database has been altered.' => 'データベースを変更しました。',
|
||||
'Alter database' => 'データベースを変更',
|
||||
'Database has been altered.' => 'データベースの設定を変更しました。',
|
||||
'Alter database' => 'データベースの設定を変更',
|
||||
'Create database' => 'データベースを作成',
|
||||
'SQL command' => 'SQLコマンド',
|
||||
'Logout' => 'ログアウト',
|
||||
'Use' => '使用',
|
||||
'No tables.' => 'テーブルがありません。',
|
||||
'select' => '選択',
|
||||
'Item has been deleted.' => '項目を削除しました。',
|
||||
'Item has been updated.' => '項目を更新しました。',
|
||||
'Item has been deleted.' => 'レコードを削除しました。',
|
||||
'Item has been updated.' => 'レコードを更新しました。',
|
||||
'Edit' => '編集',
|
||||
'Insert' => '挿入',
|
||||
'Save and insert next' => '保存/追加',
|
||||
@@ -56,18 +56,18 @@ Lang::$translations = array(
|
||||
'Database' => 'データベース',
|
||||
'DB' => 'DB',
|
||||
'Routines' => 'ルーチン',
|
||||
'Indexes have been altered.' => '索引を変更しました。',
|
||||
'Indexes' => '索引',
|
||||
'Alter indexes' => '索引の変更',
|
||||
'Indexes have been altered.' => 'インデックスを変更しました。',
|
||||
'Indexes' => 'インデックス',
|
||||
'Alter indexes' => 'インデックスを変更',
|
||||
'Add next' => '追加',
|
||||
'Language' => '言語',
|
||||
'Select' => '選択',
|
||||
'New item' => '項目の作成',
|
||||
'New item' => '新規レコードを挿入',
|
||||
'Search' => '検索',
|
||||
'Sort' => 'ソート',
|
||||
'descending' => '降順',
|
||||
'Limit' => '制約',
|
||||
'Limit rows' => '行数の制約',
|
||||
'Limit rows' => '表示行数を制限',
|
||||
'No rows.' => '行がありません。',
|
||||
'Action' => '動作',
|
||||
'edit' => '編集',
|
||||
@@ -82,17 +82,17 @@ Lang::$translations = array(
|
||||
'Foreign keys' => '外部キー',
|
||||
'Triggers' => 'トリガー',
|
||||
'View' => 'ビュー',
|
||||
'Materialized view' => 'マテビュー',
|
||||
'Full table scan' => 'テーブルの全スキャン',
|
||||
'Materialized view' => 'マテリアライズドビュー',
|
||||
'Full table scan' => 'テーブルを全スキャン',
|
||||
'Unable to select the table' => 'テーブルを選択できません',
|
||||
'Invalid CSRF token. Send the form again.' => '不正なCSRFトークン。再送信してください。',
|
||||
'If you did not send this request from Adminer then close this page.' => 'Adminerからのリクエストを送信しない場合はこのページを閉じてください。',
|
||||
'Invalid CSRF token. Send the form again.' => '不正なCSRFトークンです。フォームを再送信してください。',
|
||||
'If you did not send this request from Adminer then close this page.' => 'Adminerからのリクエストでない場合はこのページを閉じてください。',
|
||||
'Comment' => 'コメント',
|
||||
'Default values' => '規定値',
|
||||
'Default values' => '既定値',
|
||||
'%d byte(s)' => '%d バイト',
|
||||
'No commands to execute.' => '実行するコマンドがありません。',
|
||||
'Unable to upload a file.' => 'ファイルをアップロードできません。',
|
||||
'File upload' => 'ファイルをアップロード',
|
||||
'File upload' => 'アップロード',
|
||||
'File uploads are disabled.' => 'ファイルのアップロードが無効です。',
|
||||
'Routine has been called, %d row(s) affected.' => 'ルーチンを呼びました。%d 行を変更しました。',
|
||||
'Call' => '呼出し',
|
||||
@@ -103,21 +103,21 @@ Lang::$translations = array(
|
||||
'Session support must be enabled.' => 'セッションを有効にしてください。',
|
||||
'Session expired, please login again.' => 'セッションの期限切れ。ログインし直してください。',
|
||||
'The action will be performed after successful login with the same credentials.' => '同じアカウントで正しくログインすると作業を実行します。',
|
||||
'Text length' => '文字列の長さ',
|
||||
'Text length' => '文字数を丸める',
|
||||
'Foreign key has been dropped.' => '外部キーを削除しました。',
|
||||
'Foreign key has been altered.' => '外部キーを変更しました。',
|
||||
'Foreign key has been created.' => '外部キーを作成しました。',
|
||||
'Foreign key' => '外キー',
|
||||
'Target table' => 'テーブル',
|
||||
'Foreign key' => '外部キー',
|
||||
'Target table' => '対象テーブル',
|
||||
'Change' => '変更',
|
||||
'Source' => 'ソース',
|
||||
'Target' => 'ターゲット',
|
||||
'Add column' => '列を追加',
|
||||
'Add column' => 'カラムを追加',
|
||||
'Alter' => '変更',
|
||||
'Add foreign key' => '外部キーを追加',
|
||||
'ON DELETE' => 'ON DELETE',
|
||||
'ON UPDATE' => 'ON UPDATE',
|
||||
'Index Type' => '索引の型',
|
||||
'Index Type' => 'インデックスの型',
|
||||
'length' => '長さ',
|
||||
'View has been dropped.' => 'ビューを削除しました。',
|
||||
'View has been altered.' => 'ビューを変更しました。',
|
||||
@@ -126,25 +126,25 @@ Lang::$translations = array(
|
||||
'Create view' => 'ビューを作成',
|
||||
'Name' => '名称',
|
||||
'Process list' => 'プロセス一覧',
|
||||
'%d process(es) have been killed.' => '%d プロセスを強制終了しました。',
|
||||
'Kill' => '強制終了',
|
||||
'Parameter name' => '参数名',
|
||||
'Database schema' => '構造',
|
||||
'Create procedure' => 'プロシージャの作成',
|
||||
'Create function' => '関数の作成',
|
||||
'Routine has been dropped.' => 'ルーチンを作成しました。',
|
||||
'%d process(es) have been killed.' => '%d プロセスを終了しました。',
|
||||
'Kill' => 'プロセスを終了',
|
||||
'Parameter name' => 'パラメータ名',
|
||||
'Database schema' => 'スキーマ',
|
||||
'Create procedure' => 'プロシージャを作成',
|
||||
'Create function' => '関数を作成',
|
||||
'Routine has been dropped.' => 'ルーチンを削除しました。',
|
||||
'Routine has been altered.' => 'ルーチンを変更しました。',
|
||||
'Routine has been created.' => 'ルーチンを作成しました。',
|
||||
'Alter function' => '関数の変更',
|
||||
'Alter procedure' => 'プロシージャの変更',
|
||||
'Alter function' => '関数を変更',
|
||||
'Alter procedure' => 'プロシージャを変更',
|
||||
'Return type' => '戻り値の型',
|
||||
'Add trigger' => 'トリガーの追加',
|
||||
'Add trigger' => 'トリガーを追加',
|
||||
'Trigger has been dropped.' => 'トリガーを削除しました。',
|
||||
'Trigger has been altered.' => 'トリガーを変更しました。',
|
||||
'Trigger has been created.' => 'トリガーを追加しました。',
|
||||
'Alter trigger' => 'トリガーの変更',
|
||||
'Create trigger' => 'トリガーの作成',
|
||||
'Time' => '時間',
|
||||
'Alter trigger' => 'トリガーを変更',
|
||||
'Create trigger' => 'トリガーを作成',
|
||||
'Time' => 'タイミング',
|
||||
'Event' => 'イベント',
|
||||
'%s version: %s through PHP extension %s' => '%sバージョン:%s、 PHP拡張機能 %s',
|
||||
'%d / ' => '%d / ',
|
||||
@@ -157,10 +157,11 @@ Lang::$translations = array(
|
||||
'User has been altered.' => 'ユーザを変更しました。',
|
||||
'User has been created.' => 'ユーザを作成しました。',
|
||||
'Hashed' => 'Hashed',
|
||||
'Column' => '列',
|
||||
'Column' => 'カラム',
|
||||
'Columns' => 'カラム',
|
||||
'Routine' => 'ルーチン',
|
||||
'Grant' => '権限の付与',
|
||||
'Revoke' => '権限の取消し',
|
||||
'Grant' => '権限を付与',
|
||||
'Revoke' => '権限を取り消す',
|
||||
'Logged as: %s' => 'ログ:%s',
|
||||
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'POSTデータが大きすぎます。データサイズを小さくするか %s 設定を大きくしてください。',
|
||||
'You can upload a big SQL file via FTP and import it from server.' => '大きなSQLファイルは、FTP経由でアップロードしてサーバからインポートしてください。',
|
||||
@@ -171,16 +172,16 @@ Lang::$translations = array(
|
||||
'Tables' => 'テーブル',
|
||||
'Data' => 'データ',
|
||||
'Output' => '出力',
|
||||
'open' => '開く',
|
||||
'open' => 'ブラウザに表示',
|
||||
'save' => '保存',
|
||||
'Format' => '形式',
|
||||
'Functions' => '関数',
|
||||
'Aggregation' => '集合',
|
||||
'Aggregation' => '集約関数',
|
||||
'Event has been dropped.' => 'イベントを削除しました。',
|
||||
'Event has been altered.' => 'イベントを変更しました。',
|
||||
'Event has been created.' => 'イベントを作成しました。',
|
||||
'Alter event' => '変更',
|
||||
'Create event' => '作成',
|
||||
'Alter event' => 'イベントを変更',
|
||||
'Create event' => 'イベントを作成',
|
||||
'Start' => '開始',
|
||||
'End' => '終了',
|
||||
'Every' => '毎回',
|
||||
@@ -193,24 +194,24 @@ Lang::$translations = array(
|
||||
'Tables have been moved.' => 'テーブルを移動しました。',
|
||||
'Tables and views' => 'テーブルとビュー',
|
||||
'Engine' => 'エンジン',
|
||||
'Collation' => '照合順序',
|
||||
'Collation' => 'コレーション',
|
||||
'Data Length' => 'データ長',
|
||||
'Index Length' => '索引長',
|
||||
'Index Length' => 'インデックス長',
|
||||
'Data Free' => '空き',
|
||||
'Rows' => '行数',
|
||||
',' => ',',
|
||||
'0123456789' => '0123456789',
|
||||
'Analyze' => '分析',
|
||||
'Optimize' => '最適化',
|
||||
'Vacuum' => '不要領域の回収',
|
||||
'Check' => 'チェック',
|
||||
'Vacuum' => '不要領域を回収(Vacuum)',
|
||||
'Check' => '検査',
|
||||
'Repair' => '修復',
|
||||
'Truncate' => '空にする',
|
||||
'Move to other database' => '別のデータベースへ移動',
|
||||
'Move to other database' => '他のデータベースへ移動',
|
||||
'Move' => '移動',
|
||||
'Save and continue edit' => '保存して継続',
|
||||
'original' => '元',
|
||||
'%d item(s) have been affected.' => '%d 個を更新しました。',
|
||||
'%d item(s) have been affected.' => '%d レコードを更新しました。',
|
||||
'Whole result' => '全結果',
|
||||
'Tables have been dropped.' => 'テーブルを削除しました。',
|
||||
'Tables have been optimized.' => 'テーブルを最適化しました。',
|
||||
@@ -222,7 +223,7 @@ Lang::$translations = array(
|
||||
'Values' => '値',
|
||||
'%d row(s) have been imported.' => '%d 行をインポートしました。',
|
||||
'File must be in UTF-8 encoding.' => 'ファイルをUTF-8で保存してください。',
|
||||
'Show structure' => '構造',
|
||||
'Show structure' => 'スキーマ',
|
||||
'anywhere' => '任意',
|
||||
'Import' => 'インポート',
|
||||
'Stop on error' => 'エラーの場合は停止',
|
||||
@@ -232,9 +233,9 @@ Lang::$translations = array(
|
||||
'[yyyy]-mm-dd' => '[yyyy]/mm/dd',
|
||||
'History' => '履歴',
|
||||
'Variables' => '変数',
|
||||
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットの列は同じデータ型でなければなりません。ターゲット列に索引があり、データが存在しなければなりません。',
|
||||
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットのカラムは同じデータ型でなければなりません。ターゲットカラムにインデックスがあり、データが存在しなければなりません。',
|
||||
'Relations' => '関係',
|
||||
'Run file' => 'ファイルを実行',
|
||||
'Run file' => '実行',
|
||||
'Clear' => '消去',
|
||||
'Maximum allowed file size is %sB.' => '最大ファイルサイズは %sB です。',
|
||||
'Numbers' => '数字',
|
||||
@@ -243,7 +244,7 @@ Lang::$translations = array(
|
||||
'Binary' => 'バイナリ',
|
||||
'Lists' => 'リスト',
|
||||
'Editor' => 'エディタ',
|
||||
'Webserver file %s' => 'Webサーバファイル %s',
|
||||
'Webserver file %s' => 'ファイル名 %s',
|
||||
'File does not exist.' => 'ファイルは存在しません。',
|
||||
'%d in total' => '合計 %d',
|
||||
'Permanent login' => '永続的にログイン',
|
||||
@@ -267,15 +268,15 @@ Lang::$translations = array(
|
||||
'Type has been dropped.' => 'ユーザー定義型を削除しました。',
|
||||
'Type has been created.' => 'ユーザー定義型を追加しました。',
|
||||
'Ctrl+click on a value to modify it.' => 'Ctrl+クリックで値を修正します。',
|
||||
'Use edit link to modify this value.' => 'この値を修正するとリンクを編集します。',
|
||||
'Use edit link to modify this value.' => 'この値を修正するにはリンクを使用してください。',
|
||||
'last' => '最終',
|
||||
'From server' => 'サーバーから実行',
|
||||
'From server' => 'サーバー上のファイル',
|
||||
'System' => 'データベース種類',
|
||||
'empty' => '空',
|
||||
'Network' => 'ネットワーク型',
|
||||
'Geometry' => 'ジオメトリ型',
|
||||
'File exists.' => 'ファイルが既に存在します。',
|
||||
'Item%s has been inserted.' => '%s項目を挿入しました。',
|
||||
'Item%s has been inserted.' => '%sレコードを挿入しました。',
|
||||
'now' => '現在の日時',
|
||||
'%d query(s) executed OK.' => '%d クエリーを実行しました。',
|
||||
'Show only errors' => 'エラーのみ表示',
|
||||
@@ -286,27 +287,30 @@ Lang::$translations = array(
|
||||
'Tables have been copied.' => 'テーブルをコピーしました。',
|
||||
'Copy' => 'コピー',
|
||||
'overwrite' => '上書き',
|
||||
'Permanent link' => 'パーマネントリンク',
|
||||
'Edit all' => 'すべて編集',
|
||||
'Permanent link' => '固定リンク',
|
||||
'Edit all' => '一括編集',
|
||||
'Selected' => '選択中',
|
||||
'Modify' => '編集',
|
||||
'Load more data' => 'さらにデータを表示',
|
||||
'Compute' => '再計算',
|
||||
'Saving' => '保存しています...',
|
||||
'Checks' => 'CHECK制約',
|
||||
'Create check' => 'CHECK制約を追加',
|
||||
'Alter check' => 'CHECK制約を編集',
|
||||
'Check has been created.' => 'CHECK制約を追加しました。',
|
||||
'Check has been altered.' => 'CHECK制約を編集しました。',
|
||||
'Check has been dropped.' => 'CHECK制約を削除しました。',
|
||||
|
||||
'screenshot' => 'スクリーンショット',
|
||||
'Algorithm' => 'アルゴリズム',
|
||||
'Condition' => '条件',
|
||||
'Inherits from' => '継承元',
|
||||
'HH:MM:SS' => '時:分:秒',
|
||||
'Selected' => '選択済',
|
||||
'Modify' => '修正',
|
||||
'Load more data' => '続きを読み込み',
|
||||
'Loading' => '読み込み中',
|
||||
'Size' => 'サイズ',
|
||||
'Compute' => '算出',
|
||||
'Saving' => '保存中',
|
||||
'yes' => 'はい',
|
||||
'no' => 'いいえ',
|
||||
'Default value' => '既定値',
|
||||
|
||||
// Table check constraints
|
||||
'Checks' => 'チェック',
|
||||
'Create check' => 'チェックを作成',
|
||||
'Alter check' => 'チェックを変更',
|
||||
'Check has been created.' => 'チェックを作成しました。',
|
||||
'Check has been altered.' => 'チェックを変更しました。',
|
||||
'Check has been dropped.' => 'チェックを削除しました。',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php ja` to update this file
|
||||
|
||||
@@ -13,9 +13,6 @@ Lang::$translations = array(
|
||||
'Logged as: %s' => 'Zalogowany jako: %s',
|
||||
'Logout successful.' => 'Wylogowano pomyślnie.',
|
||||
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Dziękujemy za używanie Adminera, rozważ <a href="https://www.adminer.org/pl/donation/">dotację</a>.',
|
||||
'Loaded plugins' => 'Wczytane wtyczki',
|
||||
'%s must <a%s>return an array</a>.' => '%s musi <a%s>zwrócić tablicę</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Skonfiguruj</a> %s w %s.',
|
||||
'Invalid credentials.' => 'Nieprawidłowe dane logowania.',
|
||||
'There is a space in the input password which might be the cause.' => 'W haśle wejściowym znajduje się spacja, która może być przyczyną.',
|
||||
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nie obsługuje dostępu do bazy danych bez hasła, <a href="https://www.adminer.org/pl/password/"%s>więcej informacji</a>.',
|
||||
@@ -79,6 +76,7 @@ Lang::$translations = array(
|
||||
'Webserver file %s' => 'Plik %s na serwerze',
|
||||
'Run file' => 'Uruchom z pliku',
|
||||
'File does not exist.' => 'Plik nie istnieje.',
|
||||
'Increase %s.' => 'Zwiększ %s.',
|
||||
'File uploads are disabled.' => 'Wgrywanie plików jest wyłączone.',
|
||||
'Unable to upload a file.' => 'Wgranie pliku było niemożliwe.',
|
||||
'Maximum allowed file size is %sB.' => 'Maksymalna wielkość pliku to %sB.',
|
||||
@@ -90,6 +88,7 @@ Lang::$translations = array(
|
||||
'Output' => 'Rezultat',
|
||||
'open' => 'otwórz',
|
||||
'save' => 'zapisz',
|
||||
'Saving' => 'Zapisywanie',
|
||||
'Format' => 'Format',
|
||||
'Data' => 'Dane',
|
||||
|
||||
@@ -197,6 +196,8 @@ Lang::$translations = array(
|
||||
'Partitions' => 'Partycje',
|
||||
'Partition name' => 'Nazwa partycji',
|
||||
'Values' => 'Wartości',
|
||||
'Inherits from' => 'Dziedziczy po',
|
||||
'Inherited by' => 'Odziedziczone przez',
|
||||
|
||||
'View' => 'Perspektywa',
|
||||
'Materialized view' => 'Zmaterializowana perspektywa',
|
||||
@@ -212,6 +213,8 @@ Lang::$translations = array(
|
||||
'Add next' => 'Dodaj następny',
|
||||
'Index Type' => 'Typ indeksu',
|
||||
'length' => 'długość',
|
||||
'Algorithm' => 'Algorytm',
|
||||
'Condition' => 'Warunek',
|
||||
|
||||
'Foreign keys' => 'Klucze obce',
|
||||
'Foreign key' => 'Klucz obcy',
|
||||
@@ -288,7 +291,6 @@ Lang::$translations = array(
|
||||
'Edit' => 'Edytuj',
|
||||
'Insert' => 'Dodaj',
|
||||
'Save' => 'Zapisz zmiany',
|
||||
'Saving' => 'Zapisywanie',
|
||||
'Save and continue edit' => 'Zapisz i kontynuuj edycję',
|
||||
'Save and insert next' => 'Zapisz i dodaj następny',
|
||||
'Selected' => 'Zaznaczone',
|
||||
@@ -352,6 +354,11 @@ Lang::$translations = array(
|
||||
'Check has been created.' => 'Kontrola została utworzona.',
|
||||
'Check has been altered.' => 'Kontrola została zmieniona.',
|
||||
'Check has been dropped.' => 'Kontrola została usunięta.',
|
||||
|
||||
'Loaded plugins' => 'Wczytane wtyczki',
|
||||
'%s must <a%s>return an array</a>.' => '%s musi <a%s>zwrócić tablicę</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Skonfiguruj</a> %s w %s.',
|
||||
'screenshot' => 'zrzut ekranu',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php pl` to update this file
|
||||
|
||||
@@ -13,9 +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>.',
|
||||
'Loaded plugins' => 'Xx',
|
||||
'%s must <a%s>return an array</a>.' => '%s xx <a%s>xx</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Xx</a> %s xx %s.',
|
||||
'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>.',
|
||||
@@ -80,6 +78,7 @@ Lang::$translations = array(
|
||||
'Webserver file %s' => 'Xx %s',
|
||||
'Run file' => 'Xx',
|
||||
'File does not exist.' => 'Xx.',
|
||||
'Increase %s.' => 'Xx %s.',
|
||||
'File uploads are disabled.' => 'Xx.',
|
||||
'Unable to upload a file.' => 'Xx.',
|
||||
'Maximum allowed file size is %sB.' => 'Xx %sB.',
|
||||
@@ -199,6 +198,8 @@ Lang::$translations = array(
|
||||
'Partitions' => 'Xx',
|
||||
'Partition name' => 'Xx',
|
||||
'Values' => 'Xx',
|
||||
'Inherits from' => 'Xx',
|
||||
'Inherited by' => 'Xx',
|
||||
|
||||
'View' => 'Xx',
|
||||
'Materialized view' => 'Xx',
|
||||
@@ -214,6 +215,8 @@ Lang::$translations = array(
|
||||
'Add next' => 'Xx',
|
||||
'Index Type' => 'Xx',
|
||||
'length' => 'xx',
|
||||
'Algorithm' => 'Xx',
|
||||
'Condition' => 'Xx',
|
||||
|
||||
'Foreign keys' => 'Xx',
|
||||
'Foreign key' => 'Xx',
|
||||
@@ -354,6 +357,11 @@ Lang::$translations = array(
|
||||
'Check has been created.' => 'Xx.',
|
||||
'Check has been altered.' => 'Xx.',
|
||||
'Check has been dropped.' => 'Xx.',
|
||||
|
||||
'Loaded plugins' => 'Xx',
|
||||
'%s must <a%s>return an array</a>.' => '%s xx <a%s>xx</a>.',
|
||||
'<a%s>Configure</a> %s in %s.' => '<a%s>Xx</a> %s xx %s.',
|
||||
'screenshot' => 'xx',
|
||||
);
|
||||
|
||||
// run `php ../../lang.php xx` to update this file
|
||||
|
||||
@@ -56,13 +56,13 @@ echo ($collations ? "<datalist id='collations'>" . optionlist($collations) . "</
|
||||
edit_fields($row["fields"], $collations, $routine);
|
||||
if (isset($_GET["function"])) {
|
||||
echo "<tr><td>" . lang('Return type');
|
||||
edit_type("returns", $row["returns"], $collations, array(), (JUSH == "pgsql" ? array("void", "trigger") : array()));
|
||||
edit_type("returns", (array) $row["returns"], $collations, array(), (JUSH == "pgsql" ? array("void", "trigger") : array()));
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
<?php echo script("editFields();"); ?>
|
||||
</div>
|
||||
<p><?php textarea("definition", $row["definition"]); ?>
|
||||
<p><?php textarea("definition", $row["definition"], 20); ?>
|
||||
<p>
|
||||
<input type="submit" value="<?php echo lang('Save'); ?>">
|
||||
<?php if ($PROCEDURE != "") { ?>
|
||||
|
||||
@@ -5,7 +5,7 @@ if (support("kill")) {
|
||||
if ($_POST && !$error) {
|
||||
$killed = 0;
|
||||
foreach ((array) $_POST["kill"] as $val) {
|
||||
if (kill_process($val)) {
|
||||
if (adminer()->killProcess($val)) {
|
||||
$killed++;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ page_header(lang('Process list'), $error);
|
||||
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
|
||||
// HTML valid because there is always at least one process
|
||||
$i = -1;
|
||||
foreach (process_list() as $i => $row) {
|
||||
foreach (adminer()->processList() as $i => $row) {
|
||||
if (!$i) {
|
||||
echo "<thead><tr lang='en'>" . (support("kill") ? "<th>" : "");
|
||||
foreach ($row as $key => $val) {
|
||||
|
||||
@@ -18,7 +18,8 @@ $base_left = -1;
|
||||
/** @var array{fields:Field[], pos:array{float, float}, references:string[][][]}[] */
|
||||
$schema = array(); // table => array("fields" => array(name => field), "pos" => array(top, left), "references" => array(table => array(left => array(source, target))))
|
||||
$referenced = array(); // target_table => array(table => array(left => target_column))
|
||||
$lefts = array(); // float => bool
|
||||
/** @var array<numeric-string, bool> */
|
||||
$lefts = array();
|
||||
$all_fields = driver()->allFields();
|
||||
foreach (table_status('', true) as $table => $table_status) {
|
||||
if (is_view($table_status)) {
|
||||
|
||||
@@ -7,7 +7,7 @@ if ($_GET["script"] == "db") {
|
||||
$sums = array("Data_length" => 0, "Index_length" => 0, "Data_free" => 0);
|
||||
foreach (table_status() as $name => $table_status) {
|
||||
json_row("Comment-$name", h($table_status["Comment"]));
|
||||
if (!is_view($table_status)) {
|
||||
if (!is_view($table_status) || preg_match('~materialized~i', $table_status["Engine"])) {
|
||||
foreach (array("Engine", "Collation") as $key) {
|
||||
json_row("$key-$name", h($table_status[$key]));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ if ($_GET["val"] && is_ajax()) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$primary = $unselected = null;
|
||||
$primary = $unselected = array();
|
||||
foreach ($indexes as $index) {
|
||||
if ($index["type"] == "PRIMARY") {
|
||||
$primary = array_flip($index["columns"]);
|
||||
@@ -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;
|
||||
echo ($sortable ? '<a href="' . h($href . ($order[0] == $column || $order[0] == $key || (!$order && $is_group && $group[0] == $column) ? $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>";
|
||||
}
|
||||
$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(*)
|
||||
$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);
|
||||
@@ -389,10 +386,12 @@ if (!$columns && support("table")) {
|
||||
$unique_array = unique_array($rows[$n], $indexes);
|
||||
if (!$unique_array) {
|
||||
$unique_array = array();
|
||||
reset($select);
|
||||
foreach ($rows[$n] as $key => $val) {
|
||||
if (!preg_match('~^(COUNT\((\*|(DISTINCT )?`(?:[^`]|``)+`)\)|(AVG|GROUP_CONCAT|MAX|MIN|SUM)\(`(?:[^`]|``)+`\))$~', $key)) { //! columns looking like functions
|
||||
if (!preg_match('~^(COUNT|AVG|GROUP_CONCAT|MAX|MIN|SUM)\(~', current($select))) {
|
||||
$unique_array[$key] = $val;
|
||||
}
|
||||
next($select);
|
||||
}
|
||||
}
|
||||
$unique_idf = "";
|
||||
@@ -410,16 +409,17 @@ if (!$columns && support("table")) {
|
||||
. ($is_group || information_schema(DB) ? "" : " <a href='" . h(ME . "edit=" . urlencode($TABLE) . $unique_idf) . "' class='edit'>" . lang('edit') . "</a>")
|
||||
);
|
||||
|
||||
reset($select);
|
||||
foreach ($row as $key => $val) {
|
||||
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
|
||||
}
|
||||
|
||||
$link = "";
|
||||
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && $val != "") {
|
||||
if (is_blob($field) && $val != "") {
|
||||
$link = ME . 'download=' . urlencode($TABLE) . '&field=' . urlencode($key) . $unique_idf;
|
||||
}
|
||||
if (!$link && $val !== null) { // link related items
|
||||
@@ -439,7 +439,7 @@ if (!$columns && support("table")) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($key == "COUNT(*)") { //! columns looking like functions
|
||||
if ($column == "COUNT(*)") {
|
||||
$link = ME . "select=" . urlencode($TABLE);
|
||||
$i = 0;
|
||||
foreach ((array) $_GET["where"] as $v) {
|
||||
@@ -456,8 +456,10 @@ if (!$columns && support("table")) {
|
||||
$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"];
|
||||
$text = preg_match('~text|json|lob~', $field["type"]);
|
||||
echo "<td id='$id'" . (preg_match(number_type(), $field["type"]) && ($val === null || is_numeric(strip_tags($html))) ? " class='number'" : "");
|
||||
$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);
|
||||
echo "<td id='$id'" . ($is_number && ($val === null || is_numeric(strip_tags($html)) || $type == "money") ? " class='number'" : "");
|
||||
if (($_GET["modify"] && $editable && $val !== null) || $posted !== null) {
|
||||
$h_value = h($posted !== null ? $posted : $row[$key]);
|
||||
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]'>");
|
||||
@@ -469,6 +471,7 @@ if (!$columns && support("table")) {
|
||||
;
|
||||
}
|
||||
}
|
||||
next($select);
|
||||
}
|
||||
|
||||
if ($backward_keys) {
|
||||
@@ -496,7 +499,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;
|
||||
}
|
||||
}
|
||||
@@ -584,15 +587,15 @@ if (!$columns && support("table")) {
|
||||
}
|
||||
|
||||
if (adminer()->selectImportPrint()) {
|
||||
echo "<div>";
|
||||
echo "<p>";
|
||||
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>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo input_token();
|
||||
|
||||
@@ -4,9 +4,13 @@ namespace Adminer;
|
||||
if (!$error && $_POST["export"]) {
|
||||
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
|
||||
dump_headers("sql");
|
||||
adminer()->dumpTable("", "");
|
||||
adminer()->dumpData("", "table", $_POST["query"]);
|
||||
adminer()->dumpFooter();
|
||||
if ($_POST["format"] == "sql") {
|
||||
echo "$_POST[query]\n";
|
||||
} else {
|
||||
adminer()->dumpTable("", "");
|
||||
adminer()->dumpData("", "table", $_POST["query"]);
|
||||
adminer()->dumpFooter();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -20,6 +24,7 @@ if (!$error && $_POST["clear"]) {
|
||||
stop_session();
|
||||
|
||||
page_header((isset($_GET["import"]) ? lang('Import') : lang('SQL command')), $error);
|
||||
$line_comment = '--' . (JUSH == 'sql' ? ' ' : '');
|
||||
|
||||
if (!$error && $_POST) {
|
||||
$fp = false;
|
||||
@@ -51,8 +56,8 @@ if (!$error && $_POST) {
|
||||
}
|
||||
}
|
||||
|
||||
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
|
||||
$delimiter = ";";
|
||||
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)";
|
||||
$delimiter = driver()->delimiter;
|
||||
$offset = 0;
|
||||
$empty = true;
|
||||
$connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
|
||||
@@ -64,11 +69,9 @@ if (!$error && $_POST) {
|
||||
}
|
||||
$commands = 0;
|
||||
$errors = array();
|
||||
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|--' . (JUSH == 'sql' ? ' ' : '') . '|$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : '');
|
||||
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|' . $line_comment . '|$' . (JUSH == "pgsql" ? '|\$([a-zA-Z]\w*)?\$' : '');
|
||||
$total_start = microtime(true);
|
||||
$adminer_export = get_settings("adminer_import"); // this doesn't offer SQL export so we match the import/export style at select
|
||||
$dump_format = adminer()->dumpFormat();
|
||||
unset($dump_format["sql"]);
|
||||
|
||||
while ($query != "") {
|
||||
if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) {
|
||||
@@ -94,7 +97,7 @@ if (!$error && $_POST) {
|
||||
$pattern =
|
||||
($found == '/*' ? '\*/' :
|
||||
($found == '[' ? ']' :
|
||||
(preg_match('~^-- |^#~', $found) ? "\n" :
|
||||
(preg_match("~^$line_comment|^#~", $found) ? "\n" :
|
||||
preg_quote($found) . ($c_style_escapes ? '|\\\\.' : ''))))
|
||||
;
|
||||
|
||||
@@ -173,7 +176,7 @@ if (!$error && $_POST) {
|
||||
$id = "export-$commands";
|
||||
echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: "
|
||||
. html_select("output", adminer()->dumpOutput(), $adminer_export["output"]) . " "
|
||||
. html_select("format", $dump_format, $adminer_export["format"])
|
||||
. html_select("format", adminer()->dumpFormat(), $adminer_export["format"])
|
||||
. input_hidden("query", $q)
|
||||
. "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n"
|
||||
. "</form>\n"
|
||||
@@ -247,12 +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]" : "");
|
||||
echo (ini_bool("file_uploads")
|
||||
? "SQL$gz (< " . ini_get("upload_max_filesize") . "B): <input type='file' name='sql_file[]' multiple>\n$execute" // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
|
||||
: 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) {
|
||||
@@ -275,7 +275,7 @@ if (!isset($_GET["import"]) && $history) {
|
||||
list($q, $time, $elapsed) = $val;
|
||||
echo '<a href="' . h(ME . "sql=&history=$key") . '">' . lang('Edit') . "</a>"
|
||||
. " <span class='time' title='" . @date('Y-m-d', $time) . "'>" . @date("H:i:s", $time) . "</span>" // @ - time zone may be not set
|
||||
. " <code class='jush-" . JUSH . "'>" . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace('~^(#|-- ).*~m', '', $q)))), 80, "</code>")
|
||||
. " <code class='jush-" . JUSH . "'>" . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace("~^(#|$line_comment).*~m", '', $q)))), 80, "</code>")
|
||||
. ($elapsed ? " <span class='time'>($elapsed)</span>" : "")
|
||||
. "<br>\n"
|
||||
;
|
||||
|
||||
@@ -4,17 +4,16 @@ html {
|
||||
--bg: #002240;
|
||||
--fg: #829bb0;
|
||||
--dim: #154269;
|
||||
--lit: #011d35;
|
||||
}
|
||||
|
||||
a { color: #618CB3; }
|
||||
a:visited { color: #618CB3; }
|
||||
a, a:visited { color: #618cb3; }
|
||||
a:link:hover, a:visited:hover { color: #9bc0e1; }
|
||||
h1 { border-color: #5e94c1; color: #ffddbf; }
|
||||
h2 { border-color: #a3bdd3; color: #000; background: #3c678d; }
|
||||
table, td, th { border-color: #0e416d; }
|
||||
table, td, th, .js .column { border-color: #0e416d; }
|
||||
th { background: #11385a; }
|
||||
thead td, thead th { color: #a8b05f; background: #011d35; }
|
||||
thead th a { color: #a8b05f; }
|
||||
thead td, thead th, thead th a { color: #a8b05f; }
|
||||
fieldset { border-color: #16548a; }
|
||||
code { background: #11385a; }
|
||||
tbody tr:hover td, tbody tr:hover th { background: #133553; }
|
||||
@@ -22,7 +21,6 @@ pre.jush { background: #11385a; }
|
||||
input.default { box-shadow: 1px 1px 1px #888; }
|
||||
input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
|
||||
.version { color: #888; }
|
||||
.js .column { background: #011d35; }
|
||||
.error { color: red; background: #efdada; border: 1px solid #e76f6f; }
|
||||
.error b { background: #efeaea; }
|
||||
.message { color: #0b860b; background: #efe; border: 1px solid #7fbd7f; }
|
||||
@@ -45,3 +43,4 @@ input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
|
||||
#schema div.table a { color: #3c7bb3; }
|
||||
#menu .active { color: #398c8d; }
|
||||
#edit-fields tbody tr:hover td, #edit-fields tbody tr:hover th { background: #3b6f9d; }
|
||||
:target { color: #a8b05f; }
|
||||
|
||||
@@ -4,6 +4,7 @@ html {
|
||||
--bg: #fff;
|
||||
--fg: #000;
|
||||
--dim: #eee;
|
||||
--lit: #ddf;
|
||||
}
|
||||
|
||||
body { color: var(--fg); background: var(--bg); font: 90%/1.25 Verdana, Arial, Helvetica, sans-serif; margin: 0; min-width: fit-content; }
|
||||
@@ -13,17 +14,17 @@ a:link:hover, a:visited:hover { color: red; text-decoration: underline; }
|
||||
a.text:hover { text-decoration: none; }
|
||||
a.jush-help:hover { color: inherit; }
|
||||
h1 { font-size: 150%; margin: 0; padding: .8em .667em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: var(--dim); }
|
||||
h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom: 1px solid var(--fg); font-weight: normal; background: #ddf; }
|
||||
h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom: 1px solid var(--fg); font-weight: normal; background: var(--lit); }
|
||||
h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; }
|
||||
form { margin: 0; }
|
||||
td table { width: 100%; margin: 0; }
|
||||
table { margin: 1em 20px 0 0; font-size: 90%; border-spacing: 0; border-width: 1px 0 0 1px; }
|
||||
table, td, th { border-color: #999; border-style: solid; }
|
||||
table, td, th, .js .column { border-color: #999; border-style: solid; }
|
||||
td, th { border-width: 0 1px 1px 0; padding: .2em .3em; margin: 0; }
|
||||
th { background: var(--dim); text-align: left; }
|
||||
thead { position: sticky; top: 0; }
|
||||
thead th { text-align: center; padding: .2em .5em; }
|
||||
thead td, thead th { background: #ddf; }
|
||||
thead td, thead th { background: var(--lit); }
|
||||
fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8em .5em 0 0; border: 1px solid #999; border-radius: 5px; }
|
||||
p { margin: .8em 20px 0 0; }
|
||||
img { vertical-align: middle; border: 0; }
|
||||
@@ -44,7 +45,7 @@ input.wayoff { left: -1000px; position: absolute; }
|
||||
.block { display: block; }
|
||||
.version { color: #777; font-size: 62%; }
|
||||
.js .hidden, .nojs .jsonly { display: none; }
|
||||
.js .column { position: absolute; background: #ddf; padding: .27em 1ex .3em 0; margin-top: -.27em; }
|
||||
.js .column { position: absolute; background: var(--lit); padding: .27em 1ex .33em 0; margin-top: -.37em; border-width: 1px 1px 1px 0; border-radius: 0 8px 8px 0; }
|
||||
.nowrap td, .nowrap th, td.nowrap, p.nowrap { white-space: pre; }
|
||||
.wrap td { white-space: normal; }
|
||||
.error { color: red; background: #fee; }
|
||||
@@ -56,8 +57,9 @@ 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: #ddf; }
|
||||
.js .checkable .checked td, .js .checkable .checked th { background: var(--lit); }
|
||||
.time { color: silver; font-size: 70%; }
|
||||
.function, .number, .datetime { text-align: right; }
|
||||
.type { width: 15ex; }
|
||||
@@ -67,12 +69,12 @@ input.wayoff { left: -1000px; position: absolute; }
|
||||
.sqlarea { width: 98%; }
|
||||
.sql-footer { margin-bottom: 2.5em; }
|
||||
.explain table { white-space: pre; }
|
||||
.icon { width: 18px; height: 18px; background: navy center no-repeat; border: 0; vertical-align: middle; }
|
||||
.icon { width: 18px; height: 18px; background: navy center no-repeat; border: 0; padding: 0; vertical-align: middle; }
|
||||
.icon span { display: none; }
|
||||
.icon:hover { background-color: red; }
|
||||
.size { width: 7ex; }
|
||||
.help { cursor: help; }
|
||||
.footer { position: sticky; bottom: 0; margin: 1em -20px .5em 0; box-shadow: 0 -5px 10px 10px var(--bg); }
|
||||
.footer { position: sticky; bottom: 0; margin: 23px -20px .5em 0; box-shadow: 0 -5px 10px 10px var(--bg); }
|
||||
.footer > div { background: var(--bg); padding: 0 0 .5em; }
|
||||
.footer fieldset { margin-top: 0; }
|
||||
.links a { white-space: nowrap; margin-right: 20px; }
|
||||
@@ -96,6 +98,7 @@ input.wayoff { left: -1000px; position: absolute; }
|
||||
#schema .table { border: 1px solid silver; padding: 0 2px; cursor: move; position: absolute; }
|
||||
#schema .references { position: absolute; }
|
||||
#help { position: absolute; border: 1px solid #999; background: var(--dim); padding: 5px; font-family: monospace; z-index: 1; }
|
||||
:target { background: var(--lit); }
|
||||
|
||||
/* inlined here and not in compile.php because otherwise the development version flickers a little bit when loading the images */
|
||||
.icon-up { background-image: url(data:image/gif;base64,R0lGODlhEgASAIEAMe7u7gAAgJmZmQAAACH5BAEAAAEALAAAAAASABIAAQIghI+py+0PTQhRTgrvfRP0nmEVOIoReZphxbauAMfyHBcAOw==); }
|
||||
@@ -126,14 +129,18 @@ input.wayoff { left: -1000px; position: absolute; }
|
||||
.js .foot { display: none; }
|
||||
.js #menuopen { display: block; position: absolute; top: 3px; left: 6px; }
|
||||
.nojs #menu { position: static; }
|
||||
.rtl.js #foot { left: auto; right: 0; }
|
||||
.rtl .pages { right: auto; }
|
||||
.rtl #content { margin-right: 10px; }
|
||||
.rtl #breadcrumb { right: auto; }
|
||||
.rtl.js #menuopen { left: auto; right: 6px; }
|
||||
.rtl #content { margin-left: 0 !important; margin-right: 10px; }
|
||||
.rtl #breadcrumb { left: auto !important; right: 48px; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
#lang, #menu { display: none; }
|
||||
#lang, #menu, .logout { display: none; }
|
||||
#content { margin-left: 1em; }
|
||||
#breadcrumb { left: 1em; }
|
||||
.rtl #content { margin-left: auto; margin-right: 1em; }
|
||||
.rtl #breadcrumb { left: auto; right: 1em; }
|
||||
.nowrap td, .nowrap th, td.nowrap { white-space: normal; }
|
||||
}
|
||||
|
||||
@@ -93,6 +93,13 @@ function messagesPrint(parent) {
|
||||
for (const el of qsa('.toggle', parent)) {
|
||||
el.onclick = partial(toggle, el.getAttribute('href').substr(1));
|
||||
}
|
||||
for (const el of qsa('.copy', parent)) {
|
||||
el.onclick = () => {
|
||||
navigator.clipboard.writeText(qs('code', el.parentElement).innerText).then(() => el.textContent = '✓');
|
||||
setTimeout(() => el.textContent = '🗐', 1000);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +391,7 @@ function editingAddRow(focus) {
|
||||
*/
|
||||
function editingRemoveRow(name) {
|
||||
const field = formField(this.form, this.name.replace(/[^[]+(.+)/, name));
|
||||
field.parentNode.removeChild(field);
|
||||
field.remove();
|
||||
parentTag(this, 'tr').style.display = 'none';
|
||||
return false;
|
||||
}
|
||||
@@ -456,7 +463,7 @@ function editingLengthChange() {
|
||||
*/
|
||||
function editingLengthFocus() {
|
||||
const td = this.parentNode;
|
||||
if (/(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) {
|
||||
if (/^(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) {
|
||||
const edit = qs('#enum-edit');
|
||||
edit.value = enumValues(this.value);
|
||||
td.appendChild(edit);
|
||||
@@ -655,13 +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
|
||||
*/
|
||||
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
|
||||
@@ -235,8 +221,8 @@ function formChecked(input, name) {
|
||||
function tableClick(event, click) {
|
||||
const td = parentTag(event.target, 'td');
|
||||
let text;
|
||||
if (td && (text = td.getAttribute('data-text'))) {
|
||||
if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) {
|
||||
if (td && (text = td.dataset.text)) {
|
||||
if (selectClick.call(td, event, +text, td.dataset.warning)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -525,14 +511,14 @@ function functionChange() {
|
||||
if (selectValue(this)) {
|
||||
if (input.origType === undefined) {
|
||||
input.origType = input.type;
|
||||
input.origMaxLength = input.getAttribute('data-maxlength');
|
||||
input.origMaxLength = input.dataset.maxlength;
|
||||
}
|
||||
input.removeAttribute('data-maxlength');
|
||||
delete input.dataset.maxlength;
|
||||
input.type = 'text';
|
||||
} else if (input.origType) {
|
||||
input.type = input.origType;
|
||||
if (input.origMaxLength >= 0) {
|
||||
input.setAttribute('data-maxlength', input.origMaxLength);
|
||||
input.dataset.maxlength = input.origMaxLength;
|
||||
}
|
||||
}
|
||||
oninput({target: input});
|
||||
@@ -736,9 +722,10 @@ function selectLoadMore(limit, loading) {
|
||||
return !ajax(href, request => {
|
||||
const tbody = document.createElement('tbody');
|
||||
tbody.innerHTML = request.responseText;
|
||||
adminerHighlighter(qsa('code', tbody));
|
||||
qs('#table').appendChild(tbody);
|
||||
if (tbody.children.length < limit) {
|
||||
a.parentNode.removeChild(a);
|
||||
a.remove();
|
||||
} else {
|
||||
a.href = href.replace(/\d+$/, page => +page + 1);
|
||||
a.innerHTML = title;
|
||||
@@ -843,7 +830,7 @@ function cloneNode(el) {
|
||||
|
||||
oninput = event => {
|
||||
const target = event.target;
|
||||
const maxLength = target.getAttribute('data-maxlength');
|
||||
const maxLength = target.dataset.maxlength;
|
||||
alterClass(target, 'maxlength', target.value && maxLength != null && target.value.length > maxLength); // maxLength could be 0
|
||||
};
|
||||
|
||||
|
||||
@@ -26,11 +26,29 @@ if ($fields) {
|
||||
adminer()->tableStructurePrint($fields, $table_status);
|
||||
}
|
||||
|
||||
/** Print links to tables
|
||||
* @param list<array{table: string, ns: string}> $tables
|
||||
*/
|
||||
function tables_links(array $tables): void {
|
||||
echo "<ul>\n";
|
||||
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";
|
||||
}
|
||||
|
||||
$inherits = driver()->inheritsFrom($TABLE);
|
||||
if ($inherits) {
|
||||
echo "<h3>" . lang('Inherits from') . "</h3>\n";
|
||||
tables_links($inherits);
|
||||
}
|
||||
|
||||
if (support("indexes") && driver()->supportsIndex($table_status)) {
|
||||
echo "<h3 id='indexes'>" . lang('Indexes') . "</h3>\n";
|
||||
$indexes = indexes($TABLE);
|
||||
if ($indexes) {
|
||||
adminer()->tableIndexesPrint($indexes);
|
||||
adminer()->tableIndexesPrint($indexes, $table_status);
|
||||
}
|
||||
echo '<p class="links"><a href="' . h(ME) . 'indexes=' . urlencode($TABLE) . '">' . lang('Alter indexes') . "</a>\n";
|
||||
}
|
||||
@@ -95,3 +113,13 @@ if (support(is_view($table_status) ? "view_trigger" : "trigger")) {
|
||||
}
|
||||
echo '<p class="links"><a href="' . h(ME) . 'trigger=' . urlencode($TABLE) . '">' . lang('Add trigger') . "</a>\n";
|
||||
}
|
||||
|
||||
$inherited = driver()->inheritedTables($TABLE);
|
||||
if ($inherited) {
|
||||
echo "<h3 id='partitions'>" . lang('Inherited by') . "</h3>\n";
|
||||
$partition = driver()->partitionsInfo($TABLE);
|
||||
if ($partition) {
|
||||
echo "<p><code class='jush-" . JUSH . "'>BY " . h("$partition[partition_by]($partition[partition])") . "</code>\n";
|
||||
}
|
||||
tables_links($inherited);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
33
compile.php
33
compile.php
@@ -2,6 +2,7 @@
|
||||
<?php
|
||||
include __DIR__ . "/adminer/include/version.inc.php";
|
||||
include __DIR__ . "/adminer/include/errors.inc.php";
|
||||
include __DIR__ . "/externals/JsShrink/jsShrink.php";
|
||||
include __DIR__ . "/externals/PhpShrink/phpShrink.php";
|
||||
|
||||
function add_apo_slashes($s) {
|
||||
@@ -46,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)
|
||||
@@ -97,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);
|
||||
@@ -164,7 +165,7 @@ function put_file_lang($match) {
|
||||
case "' . $lang . '": $compressed = "' . add_quo_slashes(lzw_compress(implode("\n", $translation_ids))) . '"; break;';
|
||||
}
|
||||
$translations_version = crc32($return);
|
||||
return 'Lang::$translations = $_SESSION["translations"];
|
||||
return 'Lang::$translations = (array) $_SESSION["translations"];
|
||||
if ($_SESSION["translations_version"] != LANG . ' . $translations_version . ') {
|
||||
Lang::$translations = array();
|
||||
$_SESSION["translations_version"] = LANG . ' . $translations_version . ';
|
||||
@@ -198,16 +199,19 @@ function minify_css($file) {
|
||||
}
|
||||
|
||||
function minify_js($file) {
|
||||
file_put_contents("compile.js", $file);
|
||||
$terser = shell_exec("terser -c --comments false compile.js"); // prints warning to stderr if terser is not available
|
||||
if ($terser) {
|
||||
$file = $terser;
|
||||
$file = preg_replace_callback("~'use strict';~", function ($match) {
|
||||
static $count = 0;
|
||||
$count++;
|
||||
return ($count == 1 ? $match[0] : ''); // keep only the first one
|
||||
}, $file);
|
||||
if (function_exists('jsShrink')) {
|
||||
$file = jsShrink($file);
|
||||
}
|
||||
unlink("compile.js");
|
||||
return lzw_compress($file);
|
||||
}
|
||||
|
||||
function compile_file($match, $callback = '') { // $callback only to match signature
|
||||
// $callback only to match signature
|
||||
function compile_file($match, $callback = '') {
|
||||
global $project;
|
||||
$file = "";
|
||||
list(, $filenames, $callback) = $match;
|
||||
@@ -300,7 +304,12 @@ if ($vendor) {
|
||||
}
|
||||
}
|
||||
if ($project != "editor" && count(Adminer\SqlDriver::$drivers) == 1) {
|
||||
$file = str_replace('html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);")', 'input_hidden("auth[driver]", "' . ($vendor == "mysql" ? "server" : $vendor) . '") . "' . reset(Adminer\SqlDriver::$drivers) . '"', $file, $count);
|
||||
$file = str_replace(
|
||||
'html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);")',
|
||||
'input_hidden("auth[driver]", "' . ($vendor == "mysql" ? "server" : $vendor) . '") . "' . reset(Adminer\SqlDriver::$drivers) . '"',
|
||||
$file,
|
||||
$count
|
||||
);
|
||||
if (!$count) {
|
||||
echo "auth[driver] form field not found\n";
|
||||
}
|
||||
@@ -321,12 +330,12 @@ if ($project == "editor") {
|
||||
$file = preg_replace('~;.\.\/externals/jush/jush(-dark)?\.css~', '', $file);
|
||||
$file = preg_replace('~compile_file\(\'\.\./(externals/jush/modules/jush\.js)[^)]+\)~', "''", $file);
|
||||
}
|
||||
$file = preg_replace_callback("~lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])~s", 'lang_ids', $file);
|
||||
$file = preg_replace_callback("~(?<!>)lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])~s", 'lang_ids', $file);
|
||||
$file = preg_replace_callback('~\b(include|require) "([^"]*" . LANG . ".inc.php)";~', 'put_file_lang', $file);
|
||||
$file = str_replace("\r", "", $file);
|
||||
if ($_SESSION["lang"]) {
|
||||
// single language version
|
||||
$file = preg_replace_callback("~(<\\?php\\s*echo )?lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])(;\\s*\\?>)?~s", 'remove_lang', $file);
|
||||
$file = preg_replace_callback("~(<\\?php\\s*echo )?(?<!>)lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])(;\\s*\\?>)?~s", 'remove_lang', $file);
|
||||
$file = str_replace("switch_lang();", "", $file);
|
||||
$file = str_replace('<?php echo LANG; ?>', $_SESSION["lang"], $file);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ outline:0;background:url(//www.bradezone.com/random/adminer_logo.gif) no-repeat;
|
||||
* html #lang{padding-top:10px;height:30px}
|
||||
* html form#form{height:100%}
|
||||
#logins a,#tables a{background: none}
|
||||
.logout{color:#fff}
|
||||
.logout{color:#fff;background-color:#333;box-shadow:0 0 5px 5px #333;z-index:1}
|
||||
#logout{color:#333;text-decoration:none;border-bottom:1px dotted}
|
||||
#logout:hover{border-color:#333;background:#333;color:#fff}
|
||||
.js .column{background:#ddd}
|
||||
|
||||
@@ -240,6 +240,8 @@ th {
|
||||
|
||||
.logout {
|
||||
z-index: 2;
|
||||
background-color: #f2eee1;
|
||||
box-shadow: 0 0 5px 5px #f2eee1;
|
||||
}
|
||||
|
||||
.js .column {
|
||||
|
||||
2
designs/lavender-light/README.md
Normal file
2
designs/lavender-light/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
## Screenshot
|
||||

|
||||
291
designs/lavender-light/adminer.css
Normal file
291
designs/lavender-light/adminer.css
Normal file
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* lavender-light theme for Adminer
|
||||
* by Alex Yu
|
||||
*
|
||||
* Color palette from:
|
||||
* https://color.adobe.com/Lavender-v2-color-theme-294b8aa6-1935-401f-a763-13b98ec68b90/
|
||||
*
|
||||
* - white
|
||||
* - ghostwhite (f8f8ff)
|
||||
* - lavender (f0f0ff)
|
||||
* - darkslateblue (483d8b)
|
||||
* - aliceblue (f0f8ff)
|
||||
* - gray (313b43), (21282e)
|
||||
* - blue (001dff), (002799)
|
||||
*/
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
column-gap: 1rem;
|
||||
row-gap: 20px;
|
||||
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
grid-template-areas:
|
||||
"menu content";
|
||||
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: 21rem 1fr;
|
||||
|
||||
align-items: stretch;
|
||||
justify-items: start;
|
||||
|
||||
font: 0.96rem/1.2rem sans-serif !important;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #001dff;
|
||||
text-underline-offset: 1px;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(.4,0,.2,1);
|
||||
transition-duration: .15s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic tags
|
||||
*/
|
||||
|
||||
a:visited {
|
||||
color: #002799;
|
||||
}
|
||||
|
||||
a:link:hover, a:visited:hover {
|
||||
color: #7388c9;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 20px 0px;
|
||||
line-height: 1.68rem;
|
||||
border-bottom: 1px solid #20165a; /* darkslateblue; */
|
||||
background: #3c3085;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#h1 {
|
||||
font-size: 1.2rem;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#h1 img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tables
|
||||
*/
|
||||
|
||||
table {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
|
||||
#table .column {
|
||||
display: none;
|
||||
}
|
||||
|
||||
td, th {
|
||||
font-size: 1.05em;
|
||||
border-right: 1px solid silver;
|
||||
border-bottom: 1px solid silver;
|
||||
padding: .3em .6em;
|
||||
background: none;
|
||||
}
|
||||
|
||||
thead th, thead td {
|
||||
background: #313b43;
|
||||
color: white;
|
||||
border-right: 1px solid #21282e;
|
||||
border-bottom: 1px solid #21282e;
|
||||
padding: .5em .5em;
|
||||
}
|
||||
|
||||
thead th a, thead td a,
|
||||
thead th a:visited, thead td a:visited,
|
||||
thead th a:hover, thead td a:hover {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
th span.column a.text {
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
.js span.column {
|
||||
background: white;
|
||||
}
|
||||
|
||||
|
||||
table#table thead .checked td,
|
||||
table#table thead .checked th {
|
||||
background: #313b43;
|
||||
}
|
||||
|
||||
.pages {
|
||||
border: none;
|
||||
box-shadow: -1px -1px 4px silver;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common sections
|
||||
*/
|
||||
|
||||
#breadcrumb {
|
||||
position: static;
|
||||
top: initial;
|
||||
left: initial;
|
||||
margin: 0;
|
||||
padding: 0.6rem 0 0.4rem 0;
|
||||
color: #596167;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#breadcrumb a {
|
||||
color: #002799;
|
||||
}
|
||||
|
||||
#content {
|
||||
grid-area: content;
|
||||
margin: 0;
|
||||
padding: 0 20px 0 0;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
#logout {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
padding: 0.3rem 0.7rem;
|
||||
}
|
||||
|
||||
#menu {
|
||||
grid-area: menu;
|
||||
position: initial;
|
||||
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
width: 18rem;
|
||||
|
||||
background: ghostwhite;
|
||||
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#menu h1 {
|
||||
padding: 0 0 1rem 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#menu #dbs {
|
||||
padding: 3rem 0 1.5rem;
|
||||
}
|
||||
|
||||
#menu p, #tables {
|
||||
padding: .8em 0em 1.2rem;
|
||||
}
|
||||
|
||||
#menu #tables a[href*="&select="] {
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHISURBVDjLpVPNK0RRFP+9D98syMwUspHkm9I0YkFZWBFKkZ0s7a3Ewh+ilChK7FgoZCJFKYlYKB8zk2+Z5t0P577He29kQU7dd+6575zf+d1zztWklPiPmOozt/U4SThjXIoyIQS4AJjSXO0lGGlvcXAm6Vzsz4xUhm0AIeX4QLig+C+ZpxbOG1wGhGYHr1zMUmZGWRgs0ha3PE1nX/8mWmdgWTzLB+DUYbhm9FfZ35IEyrhXA3VXJfPbsV8B9LQUIeUHYJ8ASobag1jcucNgW8g9W4reYSDi2YnnZDoDiwCokDANct6NwTB0LEdj0HRA/wxa2SN25JNBEdWluUhZ366gqmAaGvrCAXKOozccTGPgt8+vn8GYSGcgyTYp3dpBnBg42nbQPRBTo5bTvqYkmxL6AQhNTWQGBXY3B7BxlEBXozcW64dxRKoKUZBju+P06gl5WaaviMJBM3TNDlbypemIZgHYOnlwASsCmW7nHADGnBoQ3c76YmweJ9BR5zFYjsbRHwm4tmJg6PhWA7pCXXk+bu7fURHKweXtq/sWaksz7SC/CCGFrwtyZ3r+rCnFRZ7qr1qc6mLZj4f9OEyPL8lVpbX/PucPv5QPKHB1TdEAAAAASUVORK5CYII=) no-repeat scroll right bottom;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
padding-left: 18px;
|
||||
border-left: 1px solid transparent;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#menu #tables a.active {
|
||||
border-left: 1px solid #20165a;
|
||||
}
|
||||
|
||||
#menu .links {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem 0 0 1.6rem;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#menu #tables li {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#menu #tables li a+a {
|
||||
padding-left: 0.3rem;
|
||||
}
|
||||
|
||||
#lang {
|
||||
position: absolute;
|
||||
top: 4rem;
|
||||
z-index: 100;
|
||||
padding: 0 0 0 1.5rem;
|
||||
}
|
||||
|
||||
#logins a, #tables a, #tables span {
|
||||
background: initial;
|
||||
}
|
||||
|
||||
#logins ul {
|
||||
padding: 3rem 0 1rem;
|
||||
}
|
||||
|
||||
#logins ul li {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Elements
|
||||
*/
|
||||
|
||||
sup {
|
||||
padding: 3px 7px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border-radius: 2em;
|
||||
}
|
||||
|
||||
code.jush-sql {
|
||||
display: block;
|
||||
padding: .4em .7em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.jush-bac, .jush-php_bac, .jush-bra, .jush-mssql_bra, .jush-sqlite_quo {
|
||||
color: #dc33f9;
|
||||
}
|
||||
|
||||
pre, textarea {
|
||||
padding: 1.5rem;
|
||||
font: 100% / 1.25 monospace;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
background: #f2f2ff;
|
||||
}
|
||||
|
||||
pre.sqlarea {
|
||||
padding: 1rem !important;
|
||||
border: 2px solid #a9a9a9 !important;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
a.jush-custom:link, a.jush-custom:visited {
|
||||
color: midnightblue;
|
||||
}
|
||||
|
||||
select, input {
|
||||
padding: 2px;
|
||||
}
|
||||
@@ -16,33 +16,62 @@ Icons from http://FlatIcon.com:
|
||||
Background adapted from "All Work and No Play", http://thenewcode.com/1008/SVG-Movie-Backgrounds-Andys-Room-and-Overlook-Hotel
|
||||
*/
|
||||
html {
|
||||
--bg: #41658a;
|
||||
--bleu: #41658a;
|
||||
--lahtbleu: var(--bleu);
|
||||
--poiple: #414073;
|
||||
--lahtpoiple: var(--poiple);
|
||||
--oringe: #ec5f12;
|
||||
--oringe-c-fru: rgba(236, 95, 18, 0.6);
|
||||
--pail-oringe: #f39561;
|
||||
--menu-w: 20em;
|
||||
--icon-w: 2em;
|
||||
--bg: var(--bleu);
|
||||
--inv-fg: #fff;
|
||||
--shado: var(--fg);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
:focus {
|
||||
outline: thin solid var(--oringe-c-fru);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
--bleu: #314c68;
|
||||
--lahtbleu: #649dd6;
|
||||
--lahtpoiple: #706fc7;
|
||||
--inv-fg: #eee;
|
||||
--pail-oringe: #c66734;
|
||||
--fg: var(--inv-fg);
|
||||
--dim: #111;
|
||||
--shado: var(--dim);
|
||||
}
|
||||
.jush {
|
||||
--text-color: #f0f0f0;
|
||||
}
|
||||
pre.jush {
|
||||
background-color: var(--dim);
|
||||
}
|
||||
}
|
||||
body {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
background: #41658a;
|
||||
align-items: stretch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
p {
|
||||
margin-right: 0;
|
||||
}
|
||||
a {
|
||||
color: #41658a;
|
||||
color: var(--lahtbleu);
|
||||
}
|
||||
a:visited {
|
||||
color: #414073;
|
||||
color: var(--lahtpoiple);
|
||||
}
|
||||
a:link:hover,
|
||||
a:visited:hover,
|
||||
a:link:focus,
|
||||
a:visited:focus {
|
||||
color: #ec5f12;
|
||||
color: var(--oringe);
|
||||
text-decoration: underline;
|
||||
outline: none;
|
||||
}
|
||||
@@ -59,11 +88,18 @@ textarea,
|
||||
fieldset {
|
||||
border: thin solid rgba(65, 101, 138, 0.3);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
input:not([type="image"]),
|
||||
select,
|
||||
textarea {
|
||||
background: #333;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sqlarea {
|
||||
background: #fff;
|
||||
border: thin solid rgba(65, 101, 138, 0.3) !important;
|
||||
width: auto !important;
|
||||
}
|
||||
@@ -80,7 +116,7 @@ input[type="button"] {
|
||||
.error,
|
||||
.message {
|
||||
margin-right: 0;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
}
|
||||
.error,
|
||||
.error b {
|
||||
@@ -99,7 +135,9 @@ input[type="button"] {
|
||||
.message .time {
|
||||
color: #e7ffaf;
|
||||
}
|
||||
thead a sup,
|
||||
.js .checkable .checked a,
|
||||
thead a,
|
||||
thead th a,
|
||||
.error > a,
|
||||
.error div > a,
|
||||
.error p > a,
|
||||
@@ -108,10 +146,10 @@ thead a sup,
|
||||
.message p > a {
|
||||
color: #cce2f8;
|
||||
}
|
||||
thead a:link:hover,
|
||||
thead a:visited:hover,
|
||||
thead a:link:focus,
|
||||
thead a:visited:focus,
|
||||
thead a:hover,
|
||||
thead a:focus,
|
||||
thead th a:hover,
|
||||
thead th a:focus,
|
||||
.error > a:link:hover,
|
||||
.error > a:visited:hover,
|
||||
.error > a:link:focus,
|
||||
@@ -136,11 +174,17 @@ thead a:visited:focus,
|
||||
.message p > a:visited:hover,
|
||||
.message p > a:link:focus,
|
||||
.message p > a:visited:focus {
|
||||
color: #f39561;
|
||||
color: var(--pail-oringe);
|
||||
}
|
||||
thead a sup {
|
||||
color: inherit;
|
||||
}
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
}
|
||||
code {
|
||||
background: var(--dim);
|
||||
}
|
||||
code.jush-sql {
|
||||
display: inline-block;
|
||||
padding: 0.3em 0.5em 0.2em;
|
||||
@@ -152,21 +196,21 @@ th > code {
|
||||
background: transparent;
|
||||
}
|
||||
.version {
|
||||
color: #fff;
|
||||
color: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#content,
|
||||
#menu,
|
||||
.rtl #content,
|
||||
.rtl #menu {
|
||||
margin: 0;
|
||||
margin: 0 !important;
|
||||
padding: 0 20px 1.5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#content {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
max-width: calc(100% - 20em);
|
||||
flex: 0 0 auto;
|
||||
width: calc(100vw - var(--menu-w));
|
||||
}
|
||||
#content,
|
||||
.footer {
|
||||
@@ -186,12 +230,12 @@ h2,
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
color: var(--inv-fg);
|
||||
}
|
||||
#breadcrumb {
|
||||
z-index: 2;
|
||||
white-space: normal;
|
||||
background: #70a37f;
|
||||
color: #fff;
|
||||
padding: 0.1em 2.5em 0.1em 20px;
|
||||
height: auto;
|
||||
margin: 0 -20px -2em;
|
||||
@@ -212,9 +256,9 @@ thead a:visited,
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
padding: 2em 20px 0.5em;
|
||||
padding: var(--icon-w) 20px 0.5em;
|
||||
border-bottom-style: none;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
h1,
|
||||
@@ -233,12 +277,28 @@ h2 a {
|
||||
h2 {
|
||||
background: #79b473;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#content,
|
||||
.footer {
|
||||
background-color: var(--dim);
|
||||
}
|
||||
#breadcrumb {
|
||||
background: #547a5f;
|
||||
}
|
||||
h2 {
|
||||
background: #5f8c59;
|
||||
}
|
||||
}
|
||||
h2 + *,
|
||||
h2 + .hidden + *,
|
||||
h2 + * > :first-child,
|
||||
h2 + .hidden + * > :first-child {
|
||||
h2 + * > :first-child:not(fieldset),
|
||||
h2 + .hidden + * > :first-child:not(fieldset) {
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 + form:has( > fieldset:first-child),
|
||||
h2 + .hidden + form:has( > fieldset:first-child) {
|
||||
margin-top: -0.8em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
@@ -251,14 +311,17 @@ fieldset {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
input.default {
|
||||
background-color: #414073;
|
||||
background-color: var(--poiple);
|
||||
box-shadow: none;
|
||||
}
|
||||
input.required {
|
||||
outline: thin dashed #ec5f12;
|
||||
outline: thin dashed var(--oringe);
|
||||
outline-offset: 1px;
|
||||
box-shadow: none;
|
||||
}
|
||||
.odds tbody tr:nth-child(2n) {
|
||||
background: transparent;
|
||||
}
|
||||
table {
|
||||
border-style: none;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
@@ -287,9 +350,13 @@ th:last-child {
|
||||
}
|
||||
thead th,
|
||||
thead td {
|
||||
border-color: #fff;
|
||||
border-color: var(--inv-fg);
|
||||
padding: 0.5em 0.8em 0.6em;
|
||||
color: #fff;
|
||||
}
|
||||
.js .checkable thead .checked th,
|
||||
thead th,
|
||||
thead td {
|
||||
color: var(--inv-fg);
|
||||
}
|
||||
thead th {
|
||||
text-align: left;
|
||||
@@ -297,7 +364,7 @@ thead th {
|
||||
}
|
||||
.js .checkable thead .checked th,
|
||||
thead th {
|
||||
background: #414073;
|
||||
background: var(--poiple);
|
||||
position: relative;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
@@ -306,14 +373,15 @@ thead th {
|
||||
}
|
||||
.js .checkable thead .checked td,
|
||||
thead td {
|
||||
background: #41658a;
|
||||
background: var(--bleu);
|
||||
}
|
||||
.js .column {
|
||||
z-index: 1;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-top: 0;
|
||||
line-height: 1.25em
|
||||
line-height: 1.25em;
|
||||
border: none;
|
||||
}
|
||||
.column a {
|
||||
margin-left: 0.2em;
|
||||
@@ -332,7 +400,7 @@ thead td {
|
||||
}
|
||||
.column a:hover,
|
||||
.column a:focus {
|
||||
background-color: #ec5f12;
|
||||
background-color: var(--oringe);
|
||||
}
|
||||
tbody tr:nth-child(even) td {
|
||||
background: rgba(65, 101, 138, 0.06);
|
||||
@@ -346,6 +414,13 @@ tbody tr:nth-child(n):hover td {
|
||||
tbody tr:nth-child(n):hover th {
|
||||
background: rgba(236, 72, 18, 0.2);
|
||||
}
|
||||
.js .checkable tbody .checked td,
|
||||
.js .checkable tbody .checked th {
|
||||
color: var(--fg);
|
||||
}
|
||||
.js .checkable tbody .checked td code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.js .checkable tbody .checked td {
|
||||
background: rgba(236, 72, 18, 0.25);
|
||||
}
|
||||
@@ -362,27 +437,47 @@ tbody tr:nth-child(n):hover th {
|
||||
.js .checkable tbody .checked:hover th {
|
||||
background: rgba(236, 72, 18, 0.45);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
table {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
td,
|
||||
th {
|
||||
border-color: #494c4f;
|
||||
}
|
||||
thead th,
|
||||
thead td {
|
||||
border-color: #444;
|
||||
}
|
||||
tbody tr:nth-child(even) td {
|
||||
background: rgba(65, 101, 138, 0.09);
|
||||
}
|
||||
tbody tr:nth-child(even) th {
|
||||
background: rgba(65, 64, 115, 0.09);
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
width: 1.2em;
|
||||
background-color: #4c3957;
|
||||
background-size: 66%;
|
||||
filter: none;
|
||||
}
|
||||
.icon-plus {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' x='0px' y='0px' width='512px' height='512px' viewBox='0 0 456 456' style='enable-background:new 0 0 456 456;'%3E%3Cg%3E%3Cpolygon points='456,157.566 298.433,157.566 298.433,0 157.567,0 157.567,157.566 0,157.566 0,298.434 157.567,298.434 157.567,456 298.433,456 298.433,298.434 456,298.434' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' width='512px' height='512px' viewBox='0 0 456 456'%3E%3Cg%3E%3Cpolygon points='456,157.566 298.433,157.566 298.433,0 157.567,0 157.567,157.566 0,157.566 0,298.434 157.567,298.434 157.567,456 298.433,456 298.433,298.434 456,298.434' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.column a[href*="&asc%5B"],
|
||||
.icon-up {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' x='0px' y='0px' viewBox='0 0 490 490' style='enable-background:new 0 0 490 490;' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M490,474.459H0L245.009,15.541L490,474.459z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 490 490' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M490,474.459H0L245.009,15.541L490,474.459z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.column a[href*="&desc%5B"],
|
||||
.icon-down {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' x='0px' y='0px' viewBox='0 0 490 490' style='enable-background:new 0 0 490 490;' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M0,15.541h490L244.991,474.459L0,15.541z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 490 490' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M0,15.541h490L244.991,474.459L0,15.541z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.icon-cross {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' x='0px' y='0px' viewBox='0 0 174.239 174.239' style='enable-background:new 0 0 174.239 174.239;' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M146.537,1.047c-1.396-1.396-3.681-1.396-5.077,0L89.658,52.849c-1.396,1.396-3.681,1.396-5.077,0L32.78,1.047 c-1.396-1.396-3.681-1.396-5.077,0L1.047,27.702c-1.396,1.396-1.396,3.681,0,5.077l51.802,51.802c1.396,1.396,1.396,3.681,0,5.077 L1.047,141.46c-1.396,1.396-1.396,3.681,0,5.077l26.655,26.655c1.396,1.396,3.681,1.396,5.077,0l51.802-51.802 c1.396-1.396,3.681-1.396,5.077,0l51.801,51.801c1.396,1.396,3.681,1.396,5.077,0l26.655-26.655c1.396-1.396,1.396-3.681,0-5.077 l-51.801-51.801c-1.396-1.396-1.396-3.681,0-5.077l51.801-51.801c1.396-1.396,1.396-3.681,0-5.077L146.537,1.047z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 174.239 174.239' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M146.537,1.047c-1.396-1.396-3.681-1.396-5.077,0L89.658,52.849c-1.396,1.396-3.681,1.396-5.077,0L32.78,1.047 c-1.396-1.396-3.681-1.396-5.077,0L1.047,27.702c-1.396,1.396-1.396,3.681,0,5.077l51.802,51.802c1.396,1.396,1.396,3.681,0,5.077 L1.047,141.46c-1.396,1.396-1.396,3.681,0,5.077l26.655,26.655c1.396,1.396,3.681,1.396,5.077,0l51.802-51.802 c1.396-1.396,3.681-1.396,5.077,0l51.801,51.801c1.396,1.396,3.681,1.396,5.077,0l26.655-26.655c1.396-1.396,1.396-3.681,0-5.077 l-51.801-51.801c-1.396-1.396-1.396-3.681,0-5.077l51.801-51.801c1.396-1.396,1.396-3.681,0-5.077L146.537,1.047z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.column a[href="#fieldset-search"] {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' viewBox='0 0 310.088 310.088' enable-background='new 0 0 310.088 310.088' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m299.85,250.413l-62.808-62.808c-3.982-3.982-10.437-3.982-14.418,0l-3.539,3.539-18.586-18.586c29.709-42.872 25.472-102.152-12.716-140.34-42.958-42.958-112.606-42.958-155.563,0s-42.958,112.606 0,155.563c38.189,38.188 97.468,42.425 140.34,12.716l18.586,18.586-3.539,3.539c-3.982,3.981-3.982,10.437 0,14.418l62.808,62.808c13.651,13.651 35.785,13.651 49.436,0s13.65-35.784-0.001-49.435zm-251.368-78.895c-33.921-33.921-33.921-89.115-0.001-123.036 33.922-33.921 89.117-33.922 123.037-0.001v0.001c33.922,33.921 33.922,89.115 0,123.036-16.96,16.961-39.239,25.441-61.518,25.441-22.279,0-44.558-8.48-61.518-25.441z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 310.088 310.088' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m299.85,250.413l-62.808-62.808c-3.982-3.982-10.437-3.982-14.418,0l-3.539,3.539-18.586-18.586c29.709-42.872 25.472-102.152-12.716-140.34-42.958-42.958-112.606-42.958-155.563,0s-42.958,112.606 0,155.563c38.189,38.188 97.468,42.425 140.34,12.716l18.586,18.586-3.539,3.539c-3.982,3.981-3.982,10.437 0,14.418l62.808,62.808c13.651,13.651 35.785,13.651 49.436,0s13.65-35.784-0.001-49.435zm-251.368-78.895c-33.921-33.921-33.921-89.115-0.001-123.036 33.922-33.921 89.117-33.922 123.037-0.001v0.001c33.922,33.921 33.922,89.115 0,123.036-16.96,16.961-39.239,25.441-61.518,25.441-22.279,0-44.558-8.48-61.518-25.441z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.loadmore,
|
||||
.rtl .loadmore {
|
||||
@@ -402,26 +497,33 @@ tbody tr:nth-child(n):hover th {
|
||||
.footer ~ div {
|
||||
margin-top: 0.8em;
|
||||
}
|
||||
.js #menuopen,
|
||||
#lang,
|
||||
.logout {
|
||||
z-index: 3;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
width: var(--icon-w);
|
||||
height: var(--icon-w);
|
||||
box-shadow: none;
|
||||
}
|
||||
#lang,
|
||||
.logout {
|
||||
position: fixed;
|
||||
}
|
||||
#lang {
|
||||
top: 0;
|
||||
left: 18em;
|
||||
--lang-offset: calc(var(--menu-w) - var(--icon-w));
|
||||
left: var(--lang-offset);
|
||||
}
|
||||
.rtl #lang {
|
||||
right: 18em;
|
||||
right: var(--lang-offset);
|
||||
}
|
||||
.rtl .logout {
|
||||
margin: 0;
|
||||
}
|
||||
.js #menuopen button,
|
||||
#lang select,
|
||||
#logout {
|
||||
opacity: 0;
|
||||
@@ -433,8 +535,9 @@ tbody tr:nth-child(n):hover th {
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
#lang:before,
|
||||
.logout:before {
|
||||
.js #menuopen::before,
|
||||
#lang label::before,
|
||||
.logout::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -446,33 +549,70 @@ tbody tr:nth-child(n):hover th {
|
||||
background: #2d3047 center no-repeat;
|
||||
background-size: 70%;
|
||||
}
|
||||
#lang:before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' viewBox='0 0 470 470' enable-background='new 0 0 470 470' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m432.5,227.5h-77.031c-0.611-37.438-5.782-73.616-14.771-105.694h50.518c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.5-7.5-7.5h-55.112c-8.018-24.165-18.316-45.521-30.553-62.656-2.408-3.371-7.093-4.153-10.462-1.745-3.371,2.407-4.152,7.092-1.745,10.462 10.618,14.868 19.688,33.199 26.965,53.939h-77.809v-69.306c0-4.143-3.357-7.5-7.5-7.5s-7.5,3.357-7.5,7.5v69.306h-77.81c7.277-20.74 16.347-39.071 26.965-53.939 2.407-3.37 1.626-8.055-1.745-10.462-3.372-2.407-8.055-1.625-10.462,1.745-12.237,17.135-22.535,38.492-30.553,62.656h-55.112c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h50.518c-8.988,32.078-14.159,68.256-14.771,105.694h-77.03c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h77.031c0.611,37.438 5.782,73.616 14.771,105.694h-50.519c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h55.112c8.019,24.169 18.32,45.529 30.56,62.666 1.464,2.049 3.77,3.142 6.11,3.142 1.508,0 3.031-0.454 4.353-1.397 3.37-2.408 4.151-7.092 1.744-10.463-10.621-14.869-19.693-33.204-26.972-53.947h77.81v69.305c0,4.143 3.357,7.5 7.5,7.5s7.5-3.357 7.5-7.5v-69.306h77.81c-7.278,20.744-16.351,39.078-26.972,53.947-2.407,3.371-1.626,8.055 1.744,10.463 1.321,0.943 2.844,1.397 4.353,1.397 2.341,0 4.646-1.093 6.11-3.142 12.24-17.137 22.54-38.497 30.56-62.666h55.112c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.5-7.5-7.5h-50.519c8.989-32.078 14.16-68.256 14.771-105.694h77.031c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.499-7.5-7.499zm-107.36-105.694c9.313,31.683 14.695,67.958 15.326,105.694h-97.966v-105.694h82.64zm-180.28,0h82.64v105.694h-97.966c0.632-37.737 6.013-74.011 15.326-105.694zm0,226.388c-9.313-31.683-14.695-67.958-15.326-105.694h97.966v105.694h-82.64zm180.28,0h-82.64v-105.694h97.966c-0.632,37.737-6.013,74.012-15.326,105.694z' fill='%23ECEBE4'/%3E%3Cpath d='M401.17,68.83C356.784,24.444,297.771,0,235,0S113.216,24.444,68.83,68.83S0,172.229,0,235.001 c0,46.271,13.391,90.899,38.764,129.316l-28.718,86.148c-0.898,2.695-0.197,5.667,1.812,7.676c2.009,2.008,4.979,2.708,7.676,1.812 l86.15-28.716C144.102,456.609,188.729,470,235,470c62.771,0,121.784-24.444,166.17-68.83S470,297.771,470,235.001 C470,172.229,445.556,113.216,401.17,68.83z M235,455c-44.491,0-87.355-13.222-123.961-38.235 c-1.262-0.862-2.739-1.308-4.231-1.308c-0.797,0-1.598,0.127-2.372,0.385L29.02,440.979l25.14-75.414 c0.741-2.225,0.399-4.668-0.923-6.604C28.222,322.357,15,279.492,15,235.001C15,113.692,113.691,15,235,15s220,98.692,220,220.001 C455,356.309,356.309,455,235,455z' fill='%23ECEBE4'/%3E%3C/g%3E%3C/svg%3E");
|
||||
#lang label::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 470 470' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m432.5,227.5h-77.031c-0.611-37.438-5.782-73.616-14.771-105.694h50.518c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.5-7.5-7.5h-55.112c-8.018-24.165-18.316-45.521-30.553-62.656-2.408-3.371-7.093-4.153-10.462-1.745-3.371,2.407-4.152,7.092-1.745,10.462 10.618,14.868 19.688,33.199 26.965,53.939h-77.809v-69.306c0-4.143-3.357-7.5-7.5-7.5s-7.5,3.357-7.5,7.5v69.306h-77.81c7.277-20.74 16.347-39.071 26.965-53.939 2.407-3.37 1.626-8.055-1.745-10.462-3.372-2.407-8.055-1.625-10.462,1.745-12.237,17.135-22.535,38.492-30.553,62.656h-55.112c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h50.518c-8.988,32.078-14.159,68.256-14.771,105.694h-77.03c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h77.031c0.611,37.438 5.782,73.616 14.771,105.694h-50.519c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5 7.5,7.5h55.112c8.019,24.169 18.32,45.529 30.56,62.666 1.464,2.049 3.77,3.142 6.11,3.142 1.508,0 3.031-0.454 4.353-1.397 3.37-2.408 4.151-7.092 1.744-10.463-10.621-14.869-19.693-33.204-26.972-53.947h77.81v69.305c0,4.143 3.357,7.5 7.5,7.5s7.5-3.357 7.5-7.5v-69.306h77.81c-7.278,20.744-16.351,39.078-26.972,53.947-2.407,3.371-1.626,8.055 1.744,10.463 1.321,0.943 2.844,1.397 4.353,1.397 2.341,0 4.646-1.093 6.11-3.142 12.24-17.137 22.54-38.497 30.56-62.666h55.112c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.5-7.5-7.5h-50.519c8.989-32.078 14.16-68.256 14.771-105.694h77.031c4.143,0 7.5-3.357 7.5-7.5s-3.357-7.499-7.5-7.499zm-107.36-105.694c9.313,31.683 14.695,67.958 15.326,105.694h-97.966v-105.694h82.64zm-180.28,0h82.64v105.694h-97.966c0.632-37.737 6.013-74.011 15.326-105.694zm0,226.388c-9.313-31.683-14.695-67.958-15.326-105.694h97.966v105.694h-82.64zm180.28,0h-82.64v-105.694h97.966c-0.632,37.737-6.013,74.012-15.326,105.694z' fill='%23ECEBE4'/%3E%3Cpath d='M401.17,68.83C356.784,24.444,297.771,0,235,0S113.216,24.444,68.83,68.83S0,172.229,0,235.001 c0,46.271,13.391,90.899,38.764,129.316l-28.718,86.148c-0.898,2.695-0.197,5.667,1.812,7.676c2.009,2.008,4.979,2.708,7.676,1.812 l86.15-28.716C144.102,456.609,188.729,470,235,470c62.771,0,121.784-24.444,166.17-68.83S470,297.771,470,235.001 C470,172.229,445.556,113.216,401.17,68.83z M235,455c-44.491,0-87.355-13.222-123.961-38.235 c-1.262-0.862-2.739-1.308-4.231-1.308c-0.797,0-1.598,0.127-2.372,0.385L29.02,440.979l25.14-75.414 c0.741-2.225,0.399-4.668-0.923-6.604C28.222,322.357,15,279.492,15,235.001C15,113.692,113.691,15,235,15s220,98.692,220,220.001 C455,356.309,356.309,455,235,455z' fill='%23ECEBE4'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
.logout:before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' viewBox='0 0 512 512' enable-background='new 0 0 512 512' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m256,501c-129.6,0-235-102.2-235-227.8 0-87.8 50.6-166.3 132.1-204.9 10.2-4.8 22.4-0.5 27.2,9.7 4.8,10.2 0.5,22.4-9.7,27.2-67.1,31.8-108.7,96.1-108.7,168-7.10543e-15,103.1 87.1,187 194.1,187 107,0 194.1-83.9 194.1-187 0-72.4-44-138.9-112.2-169.5-10.3-4.6-14.9-16.7-10.3-27 4.6-10.3 16.7-14.9 27-10.2 82.9,37.1 136.4,118.3 136.4,206.7 0,125.6-105.4,227.8-235,227.8z' fill='%23FFFFFF'/%3E%3Cpath d='m256,287.9c-11.3,0-20.4-9.1-20.4-20.4v-236.1c0-11.3 9.2-20.4 20.4-20.4 11.3,0 20.4,9.1 20.4,20.4v236.1c0,11.3-9.1,20.4-20.4,20.4z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
.logout::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 512 512' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='m256,501c-129.6,0-235-102.2-235-227.8 0-87.8 50.6-166.3 132.1-204.9 10.2-4.8 22.4-0.5 27.2,9.7 4.8,10.2 0.5,22.4-9.7,27.2-67.1,31.8-108.7,96.1-108.7,168-7.10543e-15,103.1 87.1,187 194.1,187 107,0 194.1-83.9 194.1-187 0-72.4-44-138.9-112.2-169.5-10.3-4.6-14.9-16.7-10.3-27 4.6-10.3 16.7-14.9 27-10.2 82.9,37.1 136.4,118.3 136.4,206.7 0,125.6-105.4,227.8-235,227.8z' fill='%23FFFFFF'/%3E%3Cpath d='m256,287.9c-11.3,0-20.4-9.1-20.4-20.4v-236.1c0-11.3 9.2-20.4 20.4-20.4 11.3,0 20.4,9.1 20.4,20.4v236.1c0,11.3-9.1,20.4-20.4,20.4z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
#lang:focus-within:before,
|
||||
#lang:hover:before,
|
||||
.logout:focus-within:before,
|
||||
.logout:hover:before {
|
||||
background-color: #ec5f12;
|
||||
.js #menuopen::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg version='1.1' baseProfile='full' xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 120 120'%3E%3Cdefs%3E%3Cpath id='bar' fill='%23FFFFFF' d='m87,-3 a3 3 180 0 1 0,6 h-84 a3 3 180 0 1 0,-6 z'/%3E%3C/defs%3E%3Cuse href='%23bar' x='15' y='18'/%3E%3Cuse href='%23bar' x='15' y='60'/%3E%3Cuse href='%23bar' x='15' y='102'/%3E%3C/svg%3E");
|
||||
}
|
||||
.js #menuopen:focus-within::before,
|
||||
.js #menuopen:hover::before,
|
||||
#lang label:focus-within::before,
|
||||
#lang label:hover::before,
|
||||
.logout:focus-within::before,
|
||||
.logout:hover::before {
|
||||
background-color: var(--oringe);
|
||||
}
|
||||
.foot,
|
||||
#menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
#foot {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
width: var(--menu-w);
|
||||
}
|
||||
#menu {
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: auto;
|
||||
flex: 0 0 20em;
|
||||
max-width: 20em;
|
||||
z-index: 1;
|
||||
position: static;
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
h1 {
|
||||
background: #414073;
|
||||
background: var(--poiple);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#h1 {
|
||||
font-style: normal;
|
||||
}
|
||||
#dbs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
color: transparent;
|
||||
}
|
||||
#dbs label {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#dbs label::before {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-inline-end: -1em;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='0 0 58.201 58.201' width='512px' height='512px'%3E%3Cg%3E%3Cpath d='M31.707,33.07c-0.87,0.027-1.74,0.042-2.606,0.042c-0.869,0-1.742-0.014-2.614-0.042 c-7.341-0.201-13.191-1.238-17.403-2.717C7.104,29.685,5.409,28.899,4.1,28v7.111v0.5v0.5V37.4c2.846,2.971,12.394,5.711,25,5.711 s22.154-2.74,25-5.711v-1.289v-0.5v-0.5V28c-1.318,0.905-3.028,1.697-5.025,2.367C44.865,31.839,39.027,32.87,31.707,33.07z' fill='%23FFFFFF'/%3E%3Cpath d='M4.1,14.889V22v0.5V23v1.289c2.638,2.754,11.033,5.31,22.286,5.668c0.115,0.004,0.233,0.005,0.349,0.008 c0.326,0.009,0.651,0.018,0.982,0.023C28.174,29.996,28.635,30,29.1,30s0.926-0.004,1.383-0.011 c0.33-0.005,0.656-0.014,0.982-0.023c0.116-0.003,0.234-0.005,0.349-0.008c11.253-0.359,19.648-2.915,22.286-5.668V23v-0.5V22 v-7.111C49.233,18.232,38.944,20,29.1,20S8.968,18.232,4.1,14.889z' fill='%23FFFFFF'/%3E%3Cpath d='M53.965,8.542C52.843,4.241,44.215,0,29.1,0C14.023,0,5.404,4.22,4.247,8.51C4.162,8.657,4.1,8.818,4.1,9v0.5v1.806 C6.937,14.267,16.417,17,29.1,17s22.164-2.733,25-5.694V9.5V9C54.1,8.832,54.044,8.681,53.965,8.542z' fill='%23FFFFFF'/%3E%3Cpath d='M4.1,41v8.201c0,0.162,0.043,0.315,0.117,0.451c1.181,4.895,11.747,8.549,24.883,8.549c13.106,0,23.655-3.639,24.875-8.516 c0.08-0.144,0.125-0.309,0.125-0.484v-8.199c-4.135,2.911-12.655,5.199-25,5.199C16.754,46.201,8.234,43.911,4.1,41z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/svg%3E") center no-repeat;
|
||||
background-size: auto 100%;
|
||||
}
|
||||
#dbs select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
#menu p {
|
||||
margin: 1.5em 0 0;
|
||||
}
|
||||
@@ -486,6 +626,13 @@ h1 {
|
||||
#menu .error {
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
p:has(#filter-field) {
|
||||
display: flex;
|
||||
}
|
||||
#filter-field {
|
||||
flex: 1 1 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.tables-filter {
|
||||
padding: 0;
|
||||
margin-top: 1.2em;
|
||||
@@ -499,7 +646,7 @@ button,
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
background-color: #4c3957;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
padding: 0.5em 0.8em 0.6em;
|
||||
margin: 0;
|
||||
border-style: none;
|
||||
@@ -516,9 +663,11 @@ button:focus,
|
||||
.links a:link:hover,
|
||||
.links a:visited:hover,
|
||||
.links a:link:focus,
|
||||
.links a:visited:focus {
|
||||
color: #fff;
|
||||
background-color: #ec5f12;
|
||||
.links a:visited:focus,
|
||||
#menu .links a:hover,
|
||||
#menu .links a:focus {
|
||||
color: var(--inv-fg);
|
||||
background-color: var(--oringe);
|
||||
text-decoration: none;
|
||||
}
|
||||
input[type="submit"]:disabled,
|
||||
@@ -533,7 +682,7 @@ input[type="file"]::-ms-browse {
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
background: #4c3957;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
padding: 0.5em 0.8em 0.6em;
|
||||
margin: 0 0.5em;
|
||||
border-style: none;
|
||||
@@ -541,8 +690,8 @@ input[type="file"]::-ms-browse {
|
||||
}
|
||||
input[type="file"]:hover::-ms-browse,
|
||||
input[type="file"]:focus::-ms-browse {
|
||||
color: #fff;
|
||||
background: #ec5f12;
|
||||
color: var(--inv-fg);
|
||||
background: var(--oringe);
|
||||
text-decoration: none;
|
||||
}
|
||||
input[type="file"]:disabled::-ms-browse {
|
||||
@@ -555,7 +704,7 @@ input[type="file"]::-webkit-file-upload-button {
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
background: #4c3957;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
padding: 0.5em 0.8em 0.6em;
|
||||
margin: 0 0.5em;
|
||||
border-style: none;
|
||||
@@ -563,8 +712,8 @@ input[type="file"]::-webkit-file-upload-button {
|
||||
}
|
||||
input[type="file"]:hover::-webkit-file-upload-button,
|
||||
input[type="file"]:focus::-webkit-file-upload-button {
|
||||
color: #fff;
|
||||
background: #ec5f12;
|
||||
color: var(--inv-fg);
|
||||
background: var(--oringe);
|
||||
text-decoration: none;
|
||||
}
|
||||
input[type="file"]:disabled::-webkit-file-upload-button {
|
||||
@@ -576,7 +725,7 @@ input[type="file"]::file-selector-button {
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
background: #4c3957;
|
||||
color: #fff;
|
||||
color: var(--inv-fg);
|
||||
padding: 0.5em 0.8em 0.6em;
|
||||
margin: 0 0.5em;
|
||||
border-style: none;
|
||||
@@ -584,24 +733,25 @@ input[type="file"]::file-selector-button {
|
||||
}
|
||||
input[type="file"]:hover::file-selector-button,
|
||||
input[type="file"]:focus::file-selector-button {
|
||||
color: #fff;
|
||||
background: #ec5f12;
|
||||
color: var(--inv-fg);
|
||||
background: var(--oringe);
|
||||
text-decoration: none;
|
||||
}
|
||||
input[type="file"]:disabled::file-selector-button {
|
||||
background-color: rgba(76, 57, 87, 0.35);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.links .active {
|
||||
#menu .active {
|
||||
color: var(--inv-fg);
|
||||
font-weight: normal;
|
||||
background-color: #414073;
|
||||
background-color: var(--poiple);
|
||||
}
|
||||
#menu .links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 1em -5px -5px;
|
||||
}
|
||||
#menu .links:after {
|
||||
#menu .links::after {
|
||||
content: " ";
|
||||
display: table;
|
||||
clear: both;
|
||||
@@ -612,23 +762,26 @@ input[type="file"]:disabled::file-selector-button {
|
||||
}
|
||||
#logins,
|
||||
#tables {
|
||||
flex-grow: 1;
|
||||
margin: 0.7em -20px -20px;
|
||||
padding: 0 20px 20px;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
#logins:focus-within,
|
||||
#tables:focus-within,
|
||||
#logins:hover,
|
||||
#tables:hover {
|
||||
overflow: visible !important;
|
||||
}
|
||||
#logins li,
|
||||
#tables li {
|
||||
background: #41658a;
|
||||
background: var(--bleu);
|
||||
}
|
||||
#logins a,
|
||||
#tables a,
|
||||
#tables span {
|
||||
background: #41658a;
|
||||
color: #fff;
|
||||
background: var(--bleu);
|
||||
color: var(--inv-fg);
|
||||
padding: 0.2em 0.4em 0.3em 0;
|
||||
}
|
||||
.rtl #logins a,
|
||||
@@ -648,18 +801,14 @@ input[type="file"]:disabled::file-selector-button {
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
color: transparent;
|
||||
margin-left: -0.2em;
|
||||
margin-inline-start: -0.2em;
|
||||
white-space: nowrap;
|
||||
padding: 0.1em 0.2em;
|
||||
top: 0.4em;
|
||||
}
|
||||
.rtl #tables a.select {
|
||||
margin-left: 0;
|
||||
margin-right: -0.2em;
|
||||
}
|
||||
#tables a.select:before {
|
||||
#tables a.select::before {
|
||||
content: ' ';
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' width='512px' height='512px' viewBox='0 0 16 16'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M0 1v15h16v-15h-16zM5 15h-4v-2h4v2zM5 12h-4v-2h4v2zM5 9h-4v-2h4v2zM5 6h-4v-2h4v2zM10 15h-4v-2h4v2zM10 12h-4v-2h4v2zM10 9h-4v-2h4v2zM10 6h-4v-2h4v2zM15 15h-4v-2h4v2zM15 12h-4v-2h4v2zM15 9h-4v-2h4v2zM15 6h-4v-2h4v2z'/%3E%3C/g%3E%3C/svg%3E") center no-repeat;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' width='512px' height='512px' viewBox='0 0 16 16'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M0 1v15h16v-15h-16zM5 15h-4v-2h4v2zM5 12h-4v-2h4v2zM5 9h-4v-2h4v2zM5 6h-4v-2h4v2zM10 15h-4v-2h4v2zM10 12h-4v-2h4v2zM10 9h-4v-2h4v2zM10 6h-4v-2h4v2zM15 15h-4v-2h4v2zM15 12h-4v-2h4v2zM15 9h-4v-2h4v2zM15 6h-4v-2h4v2z'/%3E%3C/g%3E%3C/svg%3E") center no-repeat;
|
||||
background-size: contain;
|
||||
display: block;
|
||||
height: 100%;
|
||||
@@ -667,23 +816,16 @@ input[type="file"]:disabled::file-selector-button {
|
||||
#tables a.select:hover,
|
||||
#tables a.select:focus,
|
||||
#tables a.select.active {
|
||||
background-color: #ec5f12;
|
||||
background-color: var(--oringe);
|
||||
}
|
||||
#tables a.select.active ~ .structure {
|
||||
font-weight: bold;
|
||||
}
|
||||
#routines + .links a {
|
||||
margin-right: 0.45em;
|
||||
margin-inline-end: 0.45em;
|
||||
}
|
||||
#routines + .links a:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.rtl #routines + .links a {
|
||||
margin-right: 0;
|
||||
margin-left: 0.45em;
|
||||
}
|
||||
.rtl #routines + .links a:last-child {
|
||||
margin-left: 0;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
.rtl p,
|
||||
.rtl table,
|
||||
@@ -693,11 +835,22 @@ input[type="file"]:disabled::file-selector-button {
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
body {
|
||||
.js body {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
.js #menuopen {
|
||||
left: 14px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--icon-w);
|
||||
height: var(--icon-w);
|
||||
}
|
||||
.rtl.js #menuopen {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.js #menu {
|
||||
border: none;
|
||||
box-shadow: 0 0 10px 2px var(--shado);
|
||||
}
|
||||
body,
|
||||
#content,
|
||||
@@ -711,24 +864,33 @@ input[type="file"]:disabled::file-selector-button {
|
||||
#menu {
|
||||
max-width: none;
|
||||
padding: 0 10px 2em;
|
||||
width: auto;
|
||||
width: 100vw;
|
||||
}
|
||||
#foot {
|
||||
width: 100vw;
|
||||
}
|
||||
.rtl #breadcrumb,
|
||||
#breadcrumb {
|
||||
padding: 0 10px;
|
||||
margin: 0 -10px;
|
||||
}
|
||||
#breadcrumb {
|
||||
padding: 0 28px;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
margin: 0 -10px;
|
||||
overflow: auto;
|
||||
position: static;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.rtl #breadcrumb {
|
||||
padding: 0 10px 0 0;
|
||||
margin: 0 -10px;
|
||||
.js #breadcrumb {
|
||||
padding-left: calc(10px + var(--icon-w));
|
||||
}
|
||||
#breadcrumb:after {
|
||||
.rtl.js #breadcrumb {
|
||||
padding-left: 10px;
|
||||
padding-right: calc(10px + var(--icon-w));
|
||||
}
|
||||
#breadcrumb::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 2.4em;
|
||||
@@ -741,6 +903,12 @@ input[type="file"]:disabled::file-selector-button {
|
||||
margin: 0 -10px 1em;
|
||||
padding: 1em 10px 0.5em;
|
||||
}
|
||||
.js #menuopen ~ h2 {
|
||||
padding-top: var(--icon-w);
|
||||
}
|
||||
.js #menuopen ~ #breadcrumb ~ h2 {
|
||||
padding-top: 1em;
|
||||
}
|
||||
#content .links a {
|
||||
white-space: normal;
|
||||
margin-bottom: 0.2em;
|
||||
@@ -751,16 +919,19 @@ input[type="file"]:disabled::file-selector-button {
|
||||
.logout {
|
||||
position: absolute;
|
||||
}
|
||||
.js .logout {
|
||||
top: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
#lang {
|
||||
margin-left: auto;
|
||||
margin: -1.5em -10px 0 auto;
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
margin-bottom: -2em;
|
||||
}
|
||||
.rtl #lang {
|
||||
right: auto;
|
||||
margin-left: 0;
|
||||
margin-left: -10px;
|
||||
margin-right: auto;
|
||||
}
|
||||
#logins,
|
||||
|
||||
@@ -1077,6 +1077,10 @@ body > form{
|
||||
/*position: relative;*/
|
||||
}
|
||||
|
||||
.logout{
|
||||
z-index: 5000;
|
||||
}
|
||||
|
||||
#lang {
|
||||
z-index: 5000;
|
||||
position: absolute;
|
||||
|
||||
@@ -413,6 +413,8 @@ thead td abbr, thead td sup, thead th acronym, thead th sup {
|
||||
|
||||
.logout {
|
||||
top: 28px;
|
||||
background-color: #31587d;
|
||||
box-shadow: 0 0 3px 3px #31587d;
|
||||
}
|
||||
|
||||
.js .column {
|
||||
|
||||
@@ -558,6 +558,8 @@ input[name="delete"]:hover, input[name="drop"]:hover {
|
||||
|
||||
.logout {
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
box-shadow: 0 0 4px 4px #333;
|
||||
margin-top: 0.7em;
|
||||
}
|
||||
|
||||
|
||||
@@ -238,6 +238,8 @@ th {
|
||||
|
||||
.logout {
|
||||
z-index: 5;
|
||||
background-color: #f2eee1;
|
||||
box-shadow: 0 0 4px 4px #f2eee1;
|
||||
}
|
||||
|
||||
.js .column {
|
||||
|
||||
@@ -318,6 +318,8 @@ input[name=logout]:hover {
|
||||
.logout {
|
||||
position: fixed;
|
||||
z-index:3;
|
||||
background-color: #48A5BF;
|
||||
box-shadow: 0 0 4px 4px #48A5BF;
|
||||
}
|
||||
|
||||
.logout form {
|
||||
|
||||
@@ -69,6 +69,7 @@ p.tabs{margin:0 20px 0 0}
|
||||
fieldset{border:1px solid #999;border-radius:5px;display:inline;margin:.8em .5em 0 0;padding:.5em .8em;vertical-align:top}
|
||||
a{color:#369}
|
||||
a:hover{color:#28c}
|
||||
.logout{background:transparent;box-shadow:none}
|
||||
#logout,#logins a,#logins a:link,#breadcrumb a,#breadcrumb a:link,.logout a,.logout a:link,.tabs a,.tabs a:link{display:inline-block;border:1px solid #667eac;border-radius:4px;padding:3px 8px;margin:2px 0;vertical-align:middle;text-decoration:none;color:#fff;background:#7abcff;background:-moz-linear-gradient(top,#7abcff 0,#60abf8 44%,#4096ee 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#7abcff),color-stop(44%,#60abf8),color-stop(100%,#4096ee));background:-webkit-linear-gradient(top,#7abcff 0,#60abf8 44%,#4096ee 100%);background:-o-linear-gradient(top,#7abcff 0,#60abf8 44%,#4096ee 100%);background:-ms-linear-gradient(top,#7abcff 0,#60abf8 44%,#4096ee 100%);background:linear-gradient(to bottom,#7abcff 0,#60abf8 44%,#4096ee 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7abcff',endColorstr='#4096ee',GradientType=0)}
|
||||
.logout a,.logout a:link,#logout{padding:3px 4px}
|
||||
#logout:hover,#logins a:hover,#tables a:hover,#breadcrumb a:hover,.logout a:hover,.tabs a:hover{color:#fff;text-decoration:none;background:#93c9ff;background:-moz-linear-gradient(top,#93c9ff 0,#79b8f7 44%,#57a2ed 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#93c9ff),color-stop(44%,#79b8f7),color-stop(100%,#57a2ed));background:-webkit-linear-gradient(top,#93c9ff 0,#79b8f7 44%,#57a2ed 100%);background:-o-linear-gradient(top,#93c9ff 0,#79b8f7 44%,#57a2ed 100%);background:-ms-linear-gradient(top,#93c9ff 0,#79b8f7 44%,#57a2ed 100%);background:linear-gradient(to bottom,#93c9ff 0,#79b8f7 44%,#57a2ed 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#93c9ff',endColorstr='#57a2ed',GradientType=0)}
|
||||
|
||||
2
designs/win98/README.md
Normal file
2
designs/win98/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
## Screenshot
|
||||

|
||||
298
designs/win98/adminer.css
Normal file
298
designs/win98/adminer.css
Normal file
@@ -0,0 +1,298 @@
|
||||
/* inspired by https://github.com/jdan/98.css */
|
||||
|
||||
:root {
|
||||
--text-color: #222222;
|
||||
--button-face: #dfdfdf;
|
||||
--window-frame: #0a0a0a;
|
||||
--dialog-blue-light: #1084d0;
|
||||
--link-blue: #0000ff;
|
||||
}
|
||||
|
||||
body {
|
||||
background: teal;
|
||||
color: var(--text-color);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
button, input[type=reset], input[type=submit] {
|
||||
background: silver;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px white, inset -2px -2px grey, inset 2px 2px var(--button-face);
|
||||
box-sizing: border-box;
|
||||
color: transparent;
|
||||
font-size: 12px;
|
||||
min-height: 23px;
|
||||
min-width: 75px;
|
||||
padding: 0 12px;
|
||||
text-shadow: 0 0 var(--text-color);
|
||||
}
|
||||
|
||||
button.default, input[type=reset].default, input[type=submit].default {
|
||||
box-shadow: inset -2px -2px var(--window-frame), inset 1px 1px var(--window-frame), inset 2px 2px white, inset -3px -3px grey, inset 3px 3px var(--button-face);
|
||||
}
|
||||
|
||||
button:not(:disabled):active, input[type=reset]:not(:disabled):active, input[type=submit]:not(:disabled):active {
|
||||
box-shadow: inset -1px -1px white, inset 1px 1px var(--window-frame), inset -2px -2px var(--button-face), inset 2px 2px grey;
|
||||
text-shadow: 1px 1px var(--text-color);
|
||||
}
|
||||
|
||||
button.default:not(:disabled):active, input[type=reset].default:not(:disabled):active, input[type=submit].default:not(:disabled):active {
|
||||
box-shadow: inset 2px 2px var(--window-frame), inset -1px -1px var(--window-frame), inset -2px -2px white, inset 3px 3px grey, inset -3px -3px var(--button-face);
|
||||
}
|
||||
|
||||
button:focus, input[type=reset]:focus, input[type=submit]:focus {
|
||||
outline: 1px dotted black;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner, input[type=reset]::-moz-focus-inner, input[type=submit]::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:disabled, :disabled + label, input[readonly], input[readonly] + label, input[type=submit]:disabled, input[type=reset]:disabled, input[type=submit]:disabled {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
:disabled + label, button:disabled, input[type=reset]:disabled, input[type=submit]:disabled {
|
||||
text-shadow: 1px 1px 0 white;
|
||||
}
|
||||
|
||||
#content, #menu, .footer, #breadcrumb {
|
||||
background: silver;
|
||||
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face), inset -2px -2px grey, inset 2px 2px white;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
#content h2 {
|
||||
align-items: center;
|
||||
background: linear-gradient(90deg, navy, var(--dialog-blue-light));
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: 120%;
|
||||
font-weight: normal;
|
||||
justify-content: space-between;
|
||||
letter-spacing: 0;
|
||||
margin: -13px -13px 16px -13px;
|
||||
padding: 3px 2px 3px 3px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='5' height='5' fill='gray' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h5v5H0V2h2v1h1V2H0' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h4v4H0V1h1v2h2V1H0'/%3E%3C/svg%3E") 2;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
padding-block-start: 8px;
|
||||
}
|
||||
|
||||
legend {
|
||||
background: silver;
|
||||
}
|
||||
|
||||
input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], input:not([type]) {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], select, input:not([type]) {
|
||||
background-color: white;
|
||||
box-shadow: inset -1px -1px white, inset 1px 1px grey, inset -2px -2px var(--button-face), inset 2px 2px var(--window-frame);
|
||||
box-sizing: border-box;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
select, textarea {
|
||||
border: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: white;
|
||||
border-radius: 0;
|
||||
box-shadow: inset -1px -1px white, inset 1px 1px grey, inset -2px -2px var(--button-face), inset 2px 2px var(--window-frame);
|
||||
box-sizing: border-box;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
input[type=email], input[type=password], input[type=search], input[type=tel], input[type=text], input[type=number], select {
|
||||
min-height: 21px;
|
||||
}
|
||||
|
||||
input[type=search]::-ms-clear, input[type=search]::-ms-reveal {
|
||||
display: none;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type=email]:disabled, input[type=email]:read-only, input[type=number]:disabled, input[type=number]:read-only, input[type=password]:disabled, input[type=password]:read-only, input[type=search]:disabled, input[type=search]:read-only, input[type=tel]:disabled, input[type=tel]:read-only, input[type=text]:disabled, input[type=text]:read-only, textarea:disabled {
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15 0H0v16h1V1h14V0z' fill='%23DFDFDF'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2 1H1v14h1V2h12V1H2z' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M16 17H0v-1h15V0h1v17z' fill='%23000'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15 1h-1v14H1v1h14V1z' fill='gray'/%3E%3Cpath fill='silver' d='M2 2h12v13H2z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11 6H4v1h1v1h1v1h1v1h1V9h1V8h1V7h1V6z' fill='%23000'/%3E%3C/svg%3E");
|
||||
background-position: top 2px right 2px;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 0;
|
||||
padding-right: 32px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type=email]:focus, input[type=number]:focus, input[type=password]:focus, input[type=search]:focus, input[type=tel]:focus, input[type=text]:focus, select:focus, textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
background-color: navy;
|
||||
color: white;
|
||||
}
|
||||
|
||||
select:focus option {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
select:active {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h16v17H0V0zm1 16h14V1H1v15z' fill='gray'/%3E%3Cpath fill='silver' d='M1 1h14v15H1z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12 7H5v1h1v1h1v1h1v1h1v-1h1V9h1V8h1V7z' fill='%23000'/%3E%3C/svg%3E")
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-blue);
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: 1px dotted var(--link-blue);
|
||||
}
|
||||
|
||||
pre {
|
||||
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='5' height='5' fill='gray' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h5v5H0V2h2v1h1V2H0' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h4v4H0V1h1v2h2V1H0'/%3E%3C/svg%3E") 2;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
margin: 0;
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
table {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
table > thead > tr > * {
|
||||
background: silver;
|
||||
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px white, inset -2px -2px grey, inset 2px 2px var(--button-face);
|
||||
}
|
||||
|
||||
table.interactive > tbody > tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#fieldset-sort > div:not(:last-of-type), #fieldset-search > div:not(:last-of-type), #fieldset-select > div:not(:last-of-type) {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
#fieldset-partition p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.links {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#logins a, #tables a, #tables span {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.jush-autocomplete, .jush-autocomplete:active {
|
||||
background: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.logout {
|
||||
box-shadow: none;
|
||||
margin-top: .8em;
|
||||
}
|
||||
|
||||
#breadcrumb {
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 2px 16px;
|
||||
top: calc(1.5em - 14px);
|
||||
}
|
||||
|
||||
code {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#menu {
|
||||
margin: 0;
|
||||
top: 3em;
|
||||
width: 18em;
|
||||
}
|
||||
|
||||
#menu h1 {
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#menu h1 a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
#menu .links a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#lang {
|
||||
top: -2.8em;
|
||||
}
|
||||
|
||||
#lang label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.footer > div {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.js .column {
|
||||
background: silver;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face), inset -2px -2px grey, inset 2px 2px white;
|
||||
margin-top: -.4em;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
.js .logout {
|
||||
box-shadow: none;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#lang label {
|
||||
color: initial;
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,18 @@ Adminer generates simple HTML and styles it with basic CSS, respecting user pref
|
||||
Users can customize styles via `adminer.css`.
|
||||
If styling an element without a class name is difficult, I generally accept patches that add meaningful class names.
|
||||
|
||||
## Translations
|
||||
|
||||
All user-visible strings should be translatable using `lang('')`.
|
||||
This extracts them for translation and applies translations if available.
|
||||
|
||||
Translations are updated via [lang.php](/lang.php), which also checks for style consistency, such as matching punctuation.
|
||||
Plurals are stored as arrays, with selection logic handled in [lang.inc.php](/adminer/include/lang.inc.php).
|
||||
|
||||
Plugins extending [`Adminer\Plugin`](/adminer/include/plugin.inc.php) can use `$this->lang()` and store translations in `$translations = array('en' => array('' => 'Plugin description'))`.
|
||||
|
||||
The website translations are managed at https://www.adminer.org/en/translations/.
|
||||
|
||||
## Compilation
|
||||
|
||||
Adminer’s source code is divided into a manageable number of reasonably small files.
|
||||
@@ -273,15 +285,6 @@ I do not review logs with this information, and no one else has access to the se
|
||||
A [plugin](/plugins/version-noverify.php) disables version checks, but users should verify versions by other means to ensure security updates.
|
||||
There's also a [plugin](/plugins/version-github.php) checking for new versions [from GitHub](https://github.com/vrana/adminer/releases).
|
||||
|
||||
## Translations
|
||||
|
||||
All user-visible strings should be translatable using `lang('')`.
|
||||
This extracts them for translation and applies translations if available.
|
||||
|
||||
Translations are updated via [lang.php](/lang.php), which also checks for style consistency, such as matching punctuation.
|
||||
Plurals are stored as arrays, with selection logic handled in [lang.inc.php](/adminer/include/lang.inc.php).
|
||||
The website translations are managed separately via Google Sheets.
|
||||
|
||||
## Commits
|
||||
|
||||
Every commit should do only one thing and be as small as possible.
|
||||
|
||||
@@ -32,4 +32,5 @@ if (adminer()->homepage()) {
|
||||
echo "</div>\n";
|
||||
echo "</form>\n";
|
||||
echo script("tableCheck();");
|
||||
adminer()->pluginsLinks();
|
||||
}
|
||||
|
||||
@@ -52,10 +52,16 @@ class Adminer {
|
||||
return get_databases($flush);
|
||||
}
|
||||
|
||||
function pluginsLinks(): void {
|
||||
}
|
||||
|
||||
function queryTimeout() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
function afterConnect() {
|
||||
}
|
||||
|
||||
function headers() {
|
||||
}
|
||||
|
||||
@@ -67,12 +73,20 @@ class Adminer {
|
||||
return true;
|
||||
}
|
||||
|
||||
function bodyClass(): void {
|
||||
echo " editor";
|
||||
}
|
||||
|
||||
function css() {
|
||||
$return = array();
|
||||
foreach (array("", "-dark") as $mode) {
|
||||
$filename = "adminer$mode.css";
|
||||
if (file_exists($filename)) {
|
||||
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
|
||||
$file = file_get_contents($filename);
|
||||
$return["$filename?v=" . crc32($file)] = ($mode
|
||||
? "dark"
|
||||
: (preg_match('~prefers-color-scheme:\s*dark~', $file) ? '' : 'light')
|
||||
);
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
@@ -203,9 +217,9 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
}
|
||||
|
||||
function selectVal($val, $link, $field, $original) {
|
||||
$return = $val;
|
||||
$return = "$val";
|
||||
$link = h($link);
|
||||
if (preg_match('~blob|bytea~', $field["type"]) && !is_utf8($val)) {
|
||||
if (is_blob($field) && !is_utf8($val)) {
|
||||
$return = lang('%d byte(s)', strlen($original));
|
||||
if (preg_match("~^(GIF|\xFF\xD8\xFF|\x89PNG\x0D\x0A\x1A\x0A)~", $original)) { // GIF|JPG|PNG, getimagetype() works with filename
|
||||
$return = "<img src='$link' alt='$return'>";
|
||||
@@ -250,13 +264,14 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
$fields = fields($_GET["select"]);
|
||||
foreach ($columns as $name => $desc) {
|
||||
$field = $fields[$name];
|
||||
if (preg_match("~enum~", $field["type"]) || like_bool($field)) { //! set - uses 1 << $i and FIND_IN_SET()
|
||||
if ($field["type"] == "enum" || like_bool($field)) { //! set - uses 1 << $i and FIND_IN_SET()
|
||||
$key = $keys[$name];
|
||||
$i--;
|
||||
echo "<div>" . h($desc) . input_hidden("where[$i][col]", $name) . ":";
|
||||
echo "<div>" . h($desc) . ":" . input_hidden("where[$i][col]", $name);
|
||||
$val = idx($where[$key], "val");
|
||||
echo (like_bool($field)
|
||||
? " <select name='where[$i][val]'>" . optionlist(array("" => "", lang('no'), lang('yes')), $where[$key]["val"], true) . "</select>"
|
||||
: enum_input("checkbox", " name='where[$i][val][]'", $field, (array) $where[$key]["val"], ($field["null"] ? 0 : null))
|
||||
? "<select name='where[$i][val]'>" . optionlist(array("" => "", lang('no'), lang('yes')), $val, true) . "</select>"
|
||||
: enum_input("checkbox", " name='where[$i][val][]'", $field, (array) $val, lang('empty'))
|
||||
);
|
||||
echo "</div>\n";
|
||||
unset($columns[$name]);
|
||||
@@ -354,14 +369,20 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
if ($col != "" || is_numeric($val) || !preg_match(number_type(), $field["type"])) {
|
||||
$name = idf_escape($name);
|
||||
if ($col != "" && $field["type"] == "enum") {
|
||||
$conds[] = (in_array(0, $val) ? "$name IS NULL OR " : "") . "$name IN (" . implode(", ", array_map('intval', $val)) . ")";
|
||||
$in = array();
|
||||
foreach ($val as $val1) {
|
||||
if (preg_match('~val-~', $val1)) {
|
||||
$in[] = q(substr($val1, 4));
|
||||
}
|
||||
}
|
||||
$conds[] = (in_array("null", $val) ? "$name IS NULL OR " : "") . ($in ? "$name IN (" . implode(", ", $in) . ")" : "0");
|
||||
} else {
|
||||
$text_type = preg_match('~char|text|enum|set~', $field["type"]);
|
||||
$value = adminer()->processInput($field, (!$op && $text_type && preg_match('~^[^%]+$~', $val) ? "%$val%" : $val));
|
||||
$conds[] = driver()->convertSearch($name, $where, $field) . ($value == "NULL" ? " IS" . ($op == ">=" ? " NOT" : "") . " $value"
|
||||
: (in_array($op, adminer()->operators()) || $op == "=" ? " $op $value"
|
||||
: ($text_type ? " LIKE $value"
|
||||
: " IN (" . str_replace(",", "', '", $value) . ")"
|
||||
: " IN (" . ($value[0] == "'" ? str_replace(",", "', '", $value) : $value) . ")"
|
||||
)));
|
||||
if ($key < 0 && $val == "0") {
|
||||
$conds[] = "$name IS NULL";
|
||||
@@ -444,8 +465,8 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
|
||||
function editInput($table, $field, $attrs, $value) {
|
||||
if ($field["type"] == "enum") {
|
||||
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='-1' checked><i>" . lang('original') . "</i></label> " : "")
|
||||
. enum_input("radio", $attrs, $field, ($value || isset($_GET["select"]) ? $value : ""), ($field["null"] ? "" : null))
|
||||
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='orig' checked><i>" . lang('original') . "</i></label> " : "")
|
||||
. enum_input("radio", $attrs, $field, $value, lang('empty'))
|
||||
;
|
||||
}
|
||||
$options = $this->foreignKeyOptions($table, $field["field"], $value);
|
||||
@@ -489,7 +510,7 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
if (preg_match('~date|timestamp~', $field["type"]) && preg_match('(^' . str_replace('\$1', '(?P<p1>\d*)', preg_replace('~(\\\\\\$([2-6]))~', '(?P<p\2>\d{1,2})', preg_quote(lang('$1-$3-$5')))) . '(.*))', $value, $match)) {
|
||||
$return = ($match["p1"] != "" ? $match["p1"] : ($match["p2"] != "" ? ($match["p2"] < 70 ? 20 : 19) . $match["p2"] : gmdate("Y"))) . "-$match[p3]$match[p4]-$match[p5]$match[p6]" . end($match);
|
||||
}
|
||||
$return = ($field["type"] == "bit" && preg_match('~^[0-9]+$~', $value) ? $return : q($return));
|
||||
$return = q($return);
|
||||
if ($value == "" && like_bool($field)) {
|
||||
$return = "'0'";
|
||||
} elseif ($value == "" && ($field["null"] || !preg_match('~char|text~', $field["type"]))) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
function doc_link(array $paths, string $text = ""): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Encode e-mail header in UTF-8 */
|
||||
function email_header(string $header): string {
|
||||
// iconv_mime_encode requires iconv, imap_8bit requires IMAP extension
|
||||
@@ -39,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} $field single field returned from fields()
|
||||
*/
|
||||
function like_bool(array $field): bool {
|
||||
return preg_match("~bool|(tinyint|bit)\\(1\\)~", $field["full_type"]);
|
||||
return preg_match("~bool|bit~", $field["type"]);
|
||||
}
|
||||
|
||||
@@ -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/PhpShrink
vendored
2
externals/PhpShrink
vendored
Submodule externals/PhpShrink updated: 7a4c6ac9ac...b3d92cdf76
2
externals/jush
vendored
2
externals/jush
vendored
Submodule externals/jush updated: c2c986e0d7...66b81c4444
30
lang.php
30
lang.php
@@ -12,17 +12,9 @@ if (isset($_SESSION["lang"])) {
|
||||
}
|
||||
|
||||
$messages_all = array();
|
||||
foreach (
|
||||
array_merge(
|
||||
glob(__DIR__ . "/adminer/*.php"),
|
||||
glob(__DIR__ . "/adminer/include/*.php"),
|
||||
glob(__DIR__ . "/adminer/drivers/*.php"),
|
||||
glob(__DIR__ . "/editor/*.php"),
|
||||
glob(__DIR__ . "/editor/include/*.php")
|
||||
) as $include
|
||||
) {
|
||||
foreach (glob(__DIR__ . "/{adminer,adminer/include,adminer/drivers,editor,editor/include}/*.php", GLOB_BRACE) as $include) {
|
||||
$file = file_get_contents($include);
|
||||
if (preg_match_all("~lang\\(('(?:[^\\\\']+|\\\\.)*')([),])~", $file, $matches)) { // lang() always uses apostrophes
|
||||
if (preg_match_all("~[^>]lang\\(('(?:[^\\\\']+|\\\\.)*')([),])~", $file, $matches)) { // lang() always uses apostrophes
|
||||
$messages_all += array_combine($matches[1], $matches[2]);
|
||||
}
|
||||
}
|
||||
@@ -33,11 +25,12 @@ foreach (glob(__DIR__ . "/adminer/lang/" . ($_SESSION["lang"] ?: "*") . ".inc.ph
|
||||
if ($lang != "xx") {
|
||||
foreach (glob(__DIR__ . "/plugins/*.php") as $filename) {
|
||||
$file = file_get_contents($filename);
|
||||
if (preg_match_all("~\\\$this->lang\\(('(?:[^\\\\']+|\\\\.)*')([),])~", $file, $matches)) {
|
||||
$messages = array_combine($matches[1], $matches[2]);
|
||||
$file = preg_replace("~(static \\\$translations = array\\((?!.*'$lang').*?)\t\\);~s", "\\1\t\t'$lang' => array(\n\t\t),\n\t);", $file);
|
||||
if (preg_match('~extends Adminer\\\\Plugin~', $file)) {
|
||||
preg_match_all("~\\\$this->lang\\(('(?:[^\\\\']+|\\\\.)*')([),])~", $file, $matches);
|
||||
$messages = array("''" => "") + array_combine($matches[1], $matches[2]);
|
||||
$file = preg_replace("~(\\\$translations = array\\((?!.*'$lang').*?)\t\\);~s", "\\1\t\t'$lang' => array(\n\t\t),\n\t);", $file);
|
||||
file_put_contents($filename, $file);
|
||||
update_translations($lang, $messages, $filename, "~(static \\\$translations = array\\(.*'$lang' => array\\(\n)(.*)(?=^\t\t\\),)~msU", "\t\t\t");
|
||||
update_translations($lang, $messages, $filename, "~(\\\$translations = array\\(.*'$lang' => array\\(\n)(.*)(?=^\t\t\\),)~msU", "\t\t\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +39,13 @@ foreach (glob(__DIR__ . "/adminer/lang/" . ($_SESSION["lang"] ?: "*") . ".inc.ph
|
||||
function update_translations($lang, $messages, $filename, $pattern, $tabs = "\t") {
|
||||
$file = file_get_contents($filename);
|
||||
$file = str_replace("\r", "", $file);
|
||||
$s = preg_replace_callback($pattern, function ($match) use ($lang, $messages, $filename, $file, $tabs) {
|
||||
$start = 0;
|
||||
$s = preg_replace_callback($pattern, function ($match) use ($lang, $messages, $filename, $file, $tabs, &$start) {
|
||||
$prefix = $match[1][0];
|
||||
$start = $match[2][1];
|
||||
preg_match_all("~^(\\s*(?:// [^'].*\\s+)?)(?:// )?(('(?:[^\\\\']+|\\\\.)*') => (.*[^,\n])),?~m", $match[2][0], $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||
$s = "";
|
||||
$fullstop = ($lang == "bn" ? '।' : (preg_match('~^(ja|zh)~', $lang) ? '。' : ($lang == 'he' ? '[^.]' : '\.')));
|
||||
$fullstop = ($lang == 'bn' || $lang == 'hi' ? '।' : (preg_match('~^(ja|zh)~', $lang) ? '。' : ($lang == 'he' ? '[^.]' : '\.')));
|
||||
foreach ($matches as $match) {
|
||||
list(, list($indent), list($line, $offset), list($en), list($translation)) = $match;
|
||||
if (isset($messages[$en])) {
|
||||
@@ -74,6 +68,7 @@ function update_translations($lang, $messages, $filename, $pattern, $tabs = "\t"
|
||||
}
|
||||
}
|
||||
if ($messages) {
|
||||
$start += strlen($s);
|
||||
foreach ($messages as $idf => $val) {
|
||||
// add new messages
|
||||
if ($val == "," && strpos($idf, "%d")) {
|
||||
@@ -86,7 +81,8 @@ function update_translations($lang, $messages, $filename, $pattern, $tabs = "\t"
|
||||
return $prefix . $s;
|
||||
}, $file, -1, $count, PREG_OFFSET_CAPTURE);
|
||||
if ($s != $file) {
|
||||
$s = str_replace("array(\n\t\t\t'' => null,\n\t\t),", "array('' => null),", $s);
|
||||
file_put_contents($filename, $s);
|
||||
echo "$filename updated.\n";
|
||||
echo "$filename:" . (substr_count($s, "\n", 0, $start) + 1) . ":Updated.\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<exclude-pattern>/(adminer|editor)[-.]</exclude-pattern>
|
||||
|
||||
<rule ref="PSR12">
|
||||
<exclude name="Generic.Whitespace.DisallowTabIndent"/><!-- Replaced by: Generic.Whitespace.DisallowSpaceIndent -->
|
||||
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/><!-- Replaced by: Generic.WhiteSpace.DisallowSpaceIndent -->
|
||||
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols"/>
|
||||
<exclude name="PSR1.Classes.ClassDeclaration.MultipleClasses"/>
|
||||
<exclude name="PSR2.Classes.ClassDeclaration.OpenBraceNewLine"/><!-- Replaced by: Generic.Classes.OpeningBraceSameLine -->
|
||||
@@ -47,7 +47,9 @@
|
||||
|
||||
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
|
||||
<exclude-pattern>adminer/drivers/</exclude-pattern>
|
||||
<exclude-pattern>adminer/include/db.inc.php</exclude-pattern>
|
||||
<exclude-pattern>adminer/include/pdo.inc.php</exclude-pattern>
|
||||
<exclude-pattern>adminer/plugins/foreign-system.php</exclude-pattern>
|
||||
<exclude-pattern>adminer/plugins/drivers/</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
@@ -56,6 +58,7 @@
|
||||
<property name="lineLimit" value="250"/>
|
||||
</properties>
|
||||
<exclude-pattern>adminer/lang/</exclude-pattern>
|
||||
<exclude-pattern>plugins/foreign-system.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
|
||||
@@ -84,7 +87,7 @@
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions"/>
|
||||
<rule ref="Generic.Strings.UnnecessaryHeredoc"/>
|
||||
<rule ref="Generic.VersionControl.GitMergeConflict"/>
|
||||
<rule ref="Generic.Whitespace.DisallowSpaceIndent"/>
|
||||
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
|
||||
<rule ref="Generic.WhiteSpace.LanguageConstructSpacing"/>
|
||||
|
||||
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
|
||||
@@ -113,7 +116,6 @@
|
||||
<rule ref="Squiz.PHP.NonExecutableCode"/>
|
||||
<rule ref="Squiz.Scope.StaticThisUsage"/>
|
||||
<rule ref="Squiz.WhiteSpace.FunctionOpeningBraceSpace"/>
|
||||
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing"/>
|
||||
|
||||
|
||||
15
phpstan.neon
15
phpstan.neon
@@ -10,14 +10,14 @@ 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
|
||||
- "~^Constant LANG not found~" # defined in lang.inc.php
|
||||
- "~ an undefined \\w+ Adminer\\\\Db::~" # defined in that versions of Db
|
||||
- "~^Call to an undefined method Adminer\\\\Result::seek~" # defined in MS SQL
|
||||
- "~^Call to an undefined method Adminer\\\\Driver::setUserTypes~" # defined in PostgreSQL
|
||||
- "~^Call to an undefined method Adminer\\\\Driver::(setUserTypes|tableOid)~" # defined in PostgreSQL
|
||||
- "~^Access to an undefined property Adminer\\\\Driver::\\$nsOid~" # defined in PostgreSQL
|
||||
- "~expects int, float given~" # this will work
|
||||
- "~expects bool~" # truthy values
|
||||
- "~fread expects int<1, max>, 100000~" # 1e6
|
||||
@@ -39,11 +39,13 @@ parameters:
|
||||
- identifier: booleanAnd.leftAlwaysTrue
|
||||
- identifier: booleanAnd.rightAlwaysTrue
|
||||
- identifier: booleanAnd.rightAlwaysFalse
|
||||
- identifier: booleanOr.rightAlwaysTrue
|
||||
- identifier: ternary.alwaysTrue
|
||||
- identifier: if.alwaysTrue
|
||||
- identifier: while.alwaysTrue
|
||||
- identifier: isset.offset
|
||||
- identifier: deadCode.unreachable
|
||||
- "~Function Adminer\\\\get_driver\\(\\) never returns null~"
|
||||
- "~on array\\{}~"
|
||||
|
||||
paths:
|
||||
- adminer/drivers/mysql.inc.php # other drivers inherit the annotations so we take them from here
|
||||
@@ -61,12 +63,13 @@ parameters:
|
||||
max: 80499
|
||||
|
||||
typeAliases:
|
||||
TableStatus: "array{Name:string, Engine?:?string, Comment?:string, Oid?:numeric-string, Rows?:?numeric-string, Collation?:string, Auto_increment?:?numeric-string, Data_length?:numeric-string, Index_length?:numeric-string, Data_free?:numeric-string, Create_options?:string, nspname?:string}"
|
||||
Field: "array{field?:string, full_type:string, type:string, length:numeric-string, unsigned:string, default?:string, null:bool, auto_increment:bool, collation:string, privileges:int[], comment:string, primary:bool, generated:string, orig?:string, on_update?:string, on_delete?:string, default_constraint?: string}"
|
||||
TableStatus: "array{Name:string, Engine?:?string, Comment?:string, Oid?:numeric-string, Rows?:?numeric-string, Collation?:string, Auto_increment?:?numeric-string, Data_length?:numeric-string, Index_length?:numeric-string, Data_free?:numeric-string, Create_options?:string, partition?:numeric-string, nspname?:string}"
|
||||
Field: "array{field?:string, full_type:string, type:string, length:?numeric-string, unsigned:string, default?:string, null:bool, auto_increment:bool, collation:string, privileges:int[], comment:string, primary:bool, generated:string, orig?:string, on_update?:string, on_delete?:string, default_constraint?: string}"
|
||||
FieldType: "array{type:string, length:numeric-string, unsigned:string, collation:string}" # subset of RoutineField and Field
|
||||
RoutineField: "array{field:string, type:string, length:numeric-string, unsigned:string, null:bool, full_type:string, collation:string, inout?:string}"
|
||||
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>}"
|
||||
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>, algorithm?:string, partial?:string}"
|
||||
ForeignKey: "array{db?:string, ns?:string, table:string, source:list<string>, target:list<?string>, on_delete:string, on_update?:string, definition?:string, deferrable?:string}"
|
||||
Trigger: "array{Trigger?:string, Timing?:string, Event?:string, Of?:string, Type?:string, Statement?:string}"
|
||||
Routine: "array{name?:string, fields:list<RoutineField>, comment:string, returns?:FieldType, definition:string, language?:string}"
|
||||
Partitions: "array{partition_by?:string, partition?:string, partitions?:numeric-string, partition_names?:list<string>, partition_values?:list<string>}"
|
||||
BackwardKey: "array{name:string, keys:string[][]}"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDotJs {
|
||||
class AdminerDotJs extends Adminer\Plugin {
|
||||
const FILENAME = "adminer.js";
|
||||
|
||||
function head($dark = null) {
|
||||
@@ -16,4 +16,12 @@ class AdminerDotJs {
|
||||
echo Adminer\script_src(self::FILENAME . "?v=" . crc32(file_get_contents(self::FILENAME))), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Nahraje adminer.js'),
|
||||
'de' => array('' => 'Laden Sie adminer.js'),
|
||||
'pl' => array('' => 'Wczytuj adminer.js'),
|
||||
'ro' => array('' => 'Încarcă adminer.js'),
|
||||
'ja' => array('' => 'adminer.js を読込み'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
<?php
|
||||
|
||||
/** Display links to tables referencing current row, same as in Adminer Editor
|
||||
* @link https://www.adminer.org/static/plugins/backward-keys.png
|
||||
* @link https://www.adminer.org/plugins/#use
|
||||
* @author Jakub Vrana, https://www.vrana.cz/
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerBackwardKeys {
|
||||
class AdminerBackwardKeys extends Adminer\Plugin {
|
||||
// this is copy-pasted from Adminer Editor
|
||||
|
||||
function backwardKeys($table, $tableName) {
|
||||
$return = array();
|
||||
// we couldn't use the same query in MySQL and PostgreSQL because unique_constraint_name is not table-specific in MySQL and referenced_table_name is not available in PostgreSQL
|
||||
foreach (
|
||||
Adminer\get_rows($q = "SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME
|
||||
FROM information_schema.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . "
|
||||
AND REFERENCED_TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . "
|
||||
AND REFERENCED_TABLE_NAME = " . Adminer\q($table) . "
|
||||
ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
Adminer\get_rows("SELECT s.table_name table_name, s.constraint_name constraint_name, s.column_name column_name, " . (Adminer\JUSH == "sql" ? "referenced_column_name" : "t.column_name") . " referenced_column_name
|
||||
FROM information_schema.key_column_usage s" . (Adminer\JUSH == "sql" ? "
|
||||
WHERE table_schema = " . Adminer\q(Adminer\DB) . "
|
||||
AND referenced_table_schema = " . Adminer\q(Adminer\DB) . "
|
||||
AND referenced_table_name" : "
|
||||
JOIN information_schema.referential_constraints r USING (constraint_catalog, constraint_schema, constraint_name)
|
||||
JOIN information_schema.key_column_usage t ON r.unique_constraint_catalog = t.constraint_catalog
|
||||
AND r.unique_constraint_schema = t.constraint_schema
|
||||
AND r.unique_constraint_name = t.constraint_name
|
||||
AND r.constraint_catalog = t.constraint_catalog
|
||||
AND r.constraint_schema = t.constraint_schema
|
||||
AND r.unique_constraint_name = t.constraint_name
|
||||
AND s.position_in_unique_constraint = t.ordinal_position
|
||||
WHERE t.table_catalog = " . Adminer\q(Adminer\DB) . " AND t.table_schema = " . Adminer\q("$_GET[ns]") . "
|
||||
AND t.table_name") . " = " . Adminer\q($table) . "
|
||||
ORDER BY s.ordinal_position", null, "") as $row
|
||||
) {
|
||||
$return[$row["TABLE_NAME"]]["keys"][$row["CONSTRAINT_NAME"]][$row["COLUMN_NAME"]] = $row["REFERENCED_COLUMN_NAME"];
|
||||
$return[$row["table_name"]]["keys"][$row["constraint_name"]][$row["column_name"]] = $row["referenced_column_name"];
|
||||
}
|
||||
foreach ($return as $key => $val) {
|
||||
$name = Adminer\adminer()->tableName(Adminer\table_status1($key, true));
|
||||
@@ -41,9 +51,12 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
$link = Adminer\ME . 'select=' . urlencode($table);
|
||||
$i = 0;
|
||||
foreach ($cols as $column => $val) {
|
||||
if (!isset($row[$val])) {
|
||||
continue 2;
|
||||
}
|
||||
$link .= Adminer\where_link($i++, $column, $row[$val]);
|
||||
}
|
||||
echo "<a href='" . Adminer\h($link) . "'>" . Adminer\h($backwardKey["name"]) . "</a>";
|
||||
echo "<a href='" . Adminer\h($link) . "'>" . Adminer\h(preg_replace('(^' . preg_quote($_GET["select"]) . (substr($_GET["select"], -1) == 's' ? '?' : '') . '_)', '_', $backwardKey["name"])) . "</a>";
|
||||
$link = Adminer\ME . 'edit=' . urlencode($table);
|
||||
foreach ($cols as $column => $val) {
|
||||
$link .= "&set" . urlencode("[" . Adminer\bracket_escape($column) . "]") . "=" . urlencode($row[$val]);
|
||||
@@ -52,4 +65,15 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function screenshot() {
|
||||
return "https://www.adminer.org/static/plugins/backward-keys.png";
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Zobrazí odkazy na tabulky odkazující aktuální řádek, stejně jako Adminer Editor'),
|
||||
'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'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerBeforeUnload {
|
||||
class AdminerBeforeUnload extends Adminer\Plugin {
|
||||
|
||||
function head($dark = null) {
|
||||
?>
|
||||
@@ -30,4 +30,11 @@ onbeforeunload = () => editChanged;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Zobrazí potvrzení před odnahráním stránky, pokud bylo změněno formulářové políčko'),
|
||||
'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'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,24 +51,18 @@ class AdminerConfig extends Adminer\Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
function navigation() {
|
||||
if (Adminer\connection()) { // don't display on login page
|
||||
$link = substr(preg_replace('~\b(db|ns)=[^&]*&~', '', Adminer\ME), 0, -1);
|
||||
?>
|
||||
<style>
|
||||
#configlink { position: absolute; top: -2.6em; left: 17.8em; }
|
||||
#configlink a { font-size: 150%; }
|
||||
@media all and (max-width: 800px) {
|
||||
#configlink { top: 5em; left: auto; right: 20px; }
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
echo "<div id='configlink'><a href='" . Adminer\h($link) . "&config=' title='" . $this->lang('Configuration') . "'>⚙</a></div>\n";
|
||||
}
|
||||
function pluginsLinks() {
|
||||
$link = preg_replace('~\b(db|ns)=[^&]*&~', '', Adminer\ME);
|
||||
echo "<p><a href='" . Adminer\h($link) . "config='>" . $this->lang('Configuration') . "</a>\n";
|
||||
}
|
||||
|
||||
protected static $translations = array(
|
||||
function screenshot() {
|
||||
return "https://www.adminer.org/static/plugins/config.png";
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array(
|
||||
'' => 'Konfigurace možností uživateli a jejich uložení do cookie',
|
||||
'Configuration' => 'Konfigurace',
|
||||
'Configuration saved.' => 'Konfigurace uložena.',
|
||||
'Only some plugins support configuration, e.g. %s.' => 'Konfiguraci podporují jen některé pluginy, např. %s.',
|
||||
@@ -84,5 +78,23 @@ class AdminerConfig extends Adminer\Plugin {
|
||||
'Use %s if exists' => 'Użyj %s, jeśli istnieje',
|
||||
'Use builtin design' => 'Użyj wbudowanego wyglądu',
|
||||
),
|
||||
'de' => array(
|
||||
'' => 'Optionen durch den Endbenutzer konfigurieren und dies in einem Cookie speichern',
|
||||
'Configuration' => 'Konfiguration',
|
||||
'Configuration saved.' => 'Konfiguration gespeichert.',
|
||||
'Only some plugins support configuration, e.g. %s.' => 'Nur einige Plugins unterstützen die Konfiguration, z.B. %s.',
|
||||
'Design' => 'Design',
|
||||
'Use %s if exists' => '%s verwenden, falls vorhanden',
|
||||
'Use builtin design' => 'Standard Design verwenden',
|
||||
),
|
||||
'ja' => array(
|
||||
'' => 'ユーザオプションを設定し cookie に保存',
|
||||
'Configuration' => '設定',
|
||||
'Configuration saved.' => '設定を保存しました。',
|
||||
'Only some plugins support configuration, e.g. %s.' => '設定変更に対応しているのは一部のプラグインのみです。例: %s。',
|
||||
'Design' => 'デザイン',
|
||||
'Use %s if exists' => 'あれば %s を使う',
|
||||
'Use builtin design' => '組込みのデザインを使う',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDarkSwitcher {
|
||||
class AdminerDarkSwitcher extends Adminer\Plugin {
|
||||
|
||||
function head($dark = null) {
|
||||
?>
|
||||
@@ -28,6 +28,8 @@ const saved = document.cookie.match(/adminer_dark=(\d)/);
|
||||
if (saved) {
|
||||
adminerDark = +saved[1];
|
||||
adminerDarkSet();
|
||||
} else {
|
||||
adminerDark = +matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
@@ -35,7 +37,18 @@ if (saved) {
|
||||
|
||||
function navigation($missing) {
|
||||
echo "<big style='position: fixed; bottom: .5em; right: .5em; cursor: pointer;'>☀</big>"
|
||||
. Adminer\script("if (adminerDark != null) adminerDarkSet(); qsl('big').onclick = adminerDarkSwitch;") . "\n"
|
||||
. Adminer\script("adminerDarkSet(); qsl('big').onclick = adminerDarkSwitch;") . "\n"
|
||||
;
|
||||
}
|
||||
|
||||
function screenshot() {
|
||||
return "https://www.adminer.org/static/plugins/dark-switcher.gif";
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Dovoluje přepínání světlého a tmavého vzhledu'),
|
||||
'de' => array('' => 'Umschalten zwischen hellem und dunklem Design erlauben'),
|
||||
'ja' => array('' => 'ダークモードへの切替え'),
|
||||
'pl' => array('' => 'Zezwalaj na przełączanie trybu jasnego i ciemnego'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDatabaseHide {
|
||||
class AdminerDatabaseHide extends Adminer\Plugin {
|
||||
protected $disabled;
|
||||
|
||||
/**
|
||||
@@ -25,4 +25,12 @@ class AdminerDatabaseHide {
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Skryje některé databáze z rozhraní – pouze vylepší vzhled, nikoliv bezpečnost'),
|
||||
'de' => array('' => 'Verstecken Sie einige Datenbanken vor der Benutzeroberfläche – nur um das Design zu verbessern, verbessert nicht die Sicherheit'),
|
||||
'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 上で表示禁止 (デザイン的な効果のみでセキュリティ的には効果なし)'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDesigns {
|
||||
class AdminerDesigns extends Adminer\Plugin {
|
||||
protected $designs;
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ class AdminerDesigns {
|
||||
$this->designs = $designs;
|
||||
}
|
||||
|
||||
function headers() {
|
||||
function afterConnect() {
|
||||
if (isset($_POST["design"]) && Adminer\verify_token()) {
|
||||
Adminer\restart_session();
|
||||
$_SESSION["design"] = $_POST["design"];
|
||||
@@ -27,7 +27,7 @@ class AdminerDesigns {
|
||||
function css() {
|
||||
$return = array();
|
||||
if (array_key_exists($_SESSION["design"], $this->designs)) {
|
||||
$return[] = $_SESSION["design"];
|
||||
$return[$_SESSION["design"]] = (preg_match('~-dark~', $_SESSION["design"]) ? "dark" : "light");
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
@@ -38,4 +38,16 @@ class AdminerDesigns {
|
||||
echo Adminer\input_token();
|
||||
echo "</form>\n";
|
||||
}
|
||||
|
||||
function screenshot() {
|
||||
return "https://www.adminer.org/static/plugins/designs.png";
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Umožní změnit vzhled'),
|
||||
'de' => array('' => 'Designwechsel ermöglichen'),
|
||||
'pl' => array('' => 'Zezwalaj na przełączanie motywów'),
|
||||
'ro' => array('' => 'Permiteți comutarea designurilor'),
|
||||
'ja' => array('' => 'テーマ設定を有効化'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Using drivers: https://www.adminer.org/plugins/#use
|
||||
|
||||
Developing drivers: https://www.adminer.org/en/drivers/
|
||||
|
||||
The type declarations must be compatible both with source codes and the compiled version (where PHP5-incompatible types are stripped). It means:
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -287,10 +287,6 @@ if (isset($_GET["elastic"])) {
|
||||
|
||||
return !!$this->conn->affected_rows;
|
||||
}
|
||||
|
||||
function convertOperator($operator): string {
|
||||
return $operator == "LIKE %%" ? "should" : $operator;
|
||||
}
|
||||
}
|
||||
|
||||
function support($feature) {
|
||||
@@ -412,6 +408,11 @@ if (isset($_GET["elastic"])) {
|
||||
return $table_status["Engine"] == "view";
|
||||
}
|
||||
|
||||
function view(string $name): array {
|
||||
$return = connection()->rootQuery("_alias/" . urlencode($name));
|
||||
return array("select" => implode("\n", array_keys($return)));
|
||||
}
|
||||
|
||||
function error() {
|
||||
return h(connection()->error);
|
||||
}
|
||||
@@ -533,15 +534,18 @@ if (isset($_GET["elastic"])) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Drop types
|
||||
* @param list<string> $tables
|
||||
*/
|
||||
function drop_views(array $tables): bool {
|
||||
$return = connection()->rootQuery('_aliases', array('actions' => array_map(function ($table) {
|
||||
return array('remove' => array('index' => '*', 'alias' => $table));
|
||||
}, $tables)), 'POST');
|
||||
return $return && !$return['errors'];
|
||||
}
|
||||
|
||||
function drop_tables(array $tables): bool {
|
||||
$return = true;
|
||||
foreach ($tables as $table) { //! convert to bulk api
|
||||
$return = $return && connection()->rootQuery(urlencode($table), null, 'DELETE');
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
436
plugins/drivers/igdb.php
Normal file
436
plugins/drivers/igdb.php
Normal file
@@ -0,0 +1,436 @@
|
||||
<?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($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, '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['webhooks'] = array('Name' => '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) . ";" : "");
|
||||
if ($table != 'webhooks') {
|
||||
$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 && $table != 'webhooks' && array_key_exists($table, driver()->tables));
|
||||
$method = ($table == 'webhooks' ? 'GET' : 'POST');
|
||||
$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($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 ($return && $table != 'webhooks') {
|
||||
$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("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' ? 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'));
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,8 @@ if (isset($_GET["imap"])) {
|
||||
private $imap;
|
||||
|
||||
function attach($server, $username, $password): string {
|
||||
$this->mailbox = "{" . "$server:993/ssl}"; // Adminer disallows specifying privileged port in server name
|
||||
list($host, $port) = host_port($server);
|
||||
$this->mailbox = "{" . "$host:" . ($port ?: 993) . "/ssl}"; // Adminer disallows specifying privileged port in server name
|
||||
$this->imap = @imap_open($this->mailbox, $username, $password, OP_HALFOPEN, 1);
|
||||
return ($this->imap ? '' : imap_last_error());
|
||||
}
|
||||
@@ -117,7 +118,7 @@ if (isset($_GET["imap"])) {
|
||||
function __construct($result) {
|
||||
$this->result = $result;
|
||||
$this->num_rows = count($result);
|
||||
$this->fields = array_keys(idx($result, 0, array()));
|
||||
$this->fields = array_keys(idx($result, 0, array()));
|
||||
}
|
||||
|
||||
function fetch_assoc() {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpAlter {
|
||||
class AdminerDumpAlter extends Adminer\Plugin {
|
||||
|
||||
function dumpFormat() {
|
||||
if (Adminer\DRIVER == 'server') {
|
||||
@@ -167,4 +167,12 @@ DROP PROCEDURE adminer_alter;
|
||||
$this->dumpAlter();
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Exportuje jednu databázi (např. vývojovou) tak, že může být synchronizována s jinou databází (např. produkční)'),
|
||||
'de' => array('' => 'Exportiert eine Datenbank (z. B. Entwicklung), damit sie mit einer anderen Datenbank (z. B. Produktion) synchronisiert werden kann'),
|
||||
'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('' => 'データベース (開発用など) をエクスポートし、別のデータベース (本番用など) と同期'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpBz2 {
|
||||
class AdminerDumpBz2 extends Adminer\Plugin {
|
||||
protected $filename, $fp;
|
||||
|
||||
function dumpOutput() {
|
||||
@@ -36,4 +36,12 @@ class AdminerDumpBz2 {
|
||||
ob_start(array($this, '_bz2'), 1e6);
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Export do formátu Bzip2'),
|
||||
'de' => array('' => 'Export im Bzip2-Format'),
|
||||
'pl' => array('' => 'Zrzuć do formatu Bzip2'),
|
||||
'ro' => array('' => 'Dump în format Bzip2'),
|
||||
'ja' => array('' => 'Bzip2 形式でエクスポート'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,17 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpDate {
|
||||
class AdminerDumpDate extends Adminer\Plugin {
|
||||
|
||||
function dumpFilename($identifier) {
|
||||
return Adminer\friendly_url(($identifier != "" ? $identifier : (Adminer\SERVER != "" ? Adminer\SERVER : "localhost")) . "-" . Adminer\get_val("SELECT NOW()"));
|
||||
return Adminer\friendly_url(($identifier != "" ? $identifier : (Adminer\SERVER ?: "localhost")) . "-" . Adminer\get_val("SELECT NOW()"));
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Do názvu souboru s exportem přidá aktuální datum a čas'),
|
||||
'de' => array('' => 'Aktuelles Datum und die aktuelle Uhrzeit in den Namen der Exportdatei einfügen'),
|
||||
'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('' => 'エクスポートファイル名に現在日時を含める'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpJson {
|
||||
class AdminerDumpJson extends Adminer\Plugin {
|
||||
protected $database = false;
|
||||
|
||||
function dumpFormat() {
|
||||
@@ -57,4 +57,12 @@ class AdminerDumpJson {
|
||||
echo "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Export do formátu JSON'),
|
||||
'de' => array('' => 'Export im JSON-Format'),
|
||||
'pl' => array('' => 'Zrzuć do formatu JSON'),
|
||||
'ro' => array('' => 'Dump în format JSON'),
|
||||
'ja' => array('' => 'JSON 形式でエクスポート'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpPhp {
|
||||
class AdminerDumpPhp extends Adminer\Plugin {
|
||||
protected $output = array();
|
||||
|
||||
function dumpFormat() {
|
||||
@@ -45,4 +45,12 @@ class AdminerDumpPhp {
|
||||
echo ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Export do formátu PHP'),
|
||||
'de' => array('' => 'Export im PHP-Format'),
|
||||
'pl' => array('' => 'Zrzucaj do formatu PHP'),
|
||||
'ro' => array('' => 'Dump în format PHP'),
|
||||
'ja' => array('' => 'PHP 形式でエクスポート'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpXml {
|
||||
class AdminerDumpXml extends Adminer\Plugin {
|
||||
protected $database = false;
|
||||
|
||||
function dumpFormat() {
|
||||
@@ -51,4 +51,12 @@ class AdminerDumpXml {
|
||||
echo "</database>\n";
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Export do formátu XML ve struktuře <database name=""><table name=""><column name="">value'),
|
||||
'de' => array('' => 'Export im XML-Format in der Struktur <database name="><table name=""><column name="">value'),
|
||||
'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'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerDumpZip {
|
||||
class AdminerDumpZip extends Adminer\Plugin {
|
||||
protected $filename, $data;
|
||||
|
||||
function dumpOutput() {
|
||||
@@ -40,4 +40,12 @@ class AdminerDumpZip {
|
||||
ob_start(array($this, '_zip'));
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Export do formátu ZIP'),
|
||||
'de' => array('' => 'Export Im ZIP-Format'),
|
||||
'pl' => array('' => 'Zrzuć do formatu ZIP'),
|
||||
'ro' => array('' => 'Dump în format ZIP'),
|
||||
'ja' => array('' => 'ZIP 形式でエクスポート'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerEditCalendar {
|
||||
class AdminerEditCalendar extends Adminer\Plugin {
|
||||
protected $prepend, $langPath;
|
||||
|
||||
/**
|
||||
@@ -50,4 +50,12 @@ class AdminerEditCalendar {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Zobrazí jQuery UI Timepicker pro každé datumové a časové políčko'),
|
||||
'de' => array('' => 'Zeigen Sie die jQuery-UI Timepicker für jedes Datums- und Datum/Uhrzeit-Feld an'),
|
||||
'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 を表示'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerEditForeign {
|
||||
class AdminerEditForeign extends Adminer\Plugin {
|
||||
protected $limit;
|
||||
|
||||
function __construct($limit = 0) {
|
||||
@@ -39,4 +39,12 @@ class AdminerEditForeign {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Výběr cizího klíče v editačním formuláři'),
|
||||
'de' => array('' => 'Wählen Sie im Bearbeitungsformular den Fremdschlüssel aus'),
|
||||
'pl' => array('' => 'Wybierz klucz obcy w formularzu edycji'),
|
||||
'ro' => array('' => 'Selectați cheia străină în formularul de editare'),
|
||||
'ja' => array('' => '外部キーを編集フォームで選択'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,19 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerEditTextarea {
|
||||
class AdminerEditTextarea extends Adminer\Plugin {
|
||||
|
||||
function editInput($table, $field, $attrs, $value) {
|
||||
if (preg_match('~char~', $field["type"])) {
|
||||
return "<textarea cols='30' rows='1'$attrs>" . Adminer\h($value) . '</textarea>';
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Použije <textarea> pro char a varchar'),
|
||||
'de' => array('' => 'Verwenden Sie <textarea> für char und varchar Felder'),
|
||||
'pl' => array('' => 'Użyj <textarea> dla char i varchar'),
|
||||
'ro' => array('' => 'Utilizați <textarea> pentru char și varchar'),
|
||||
'ja' => array('' => 'char や varchar に <textarea> を使用'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerEditorSetup {
|
||||
class AdminerEditorSetup extends Adminer\Plugin {
|
||||
private $driver;
|
||||
private $server;
|
||||
private $database;
|
||||
@@ -37,4 +37,11 @@ class AdminerEditorSetup {
|
||||
return $this->database;
|
||||
}
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Nastavit ovladač, server a databázi pro použití s Adminer Editorem'),
|
||||
'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'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,17 @@
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
|
||||
*/
|
||||
class AdminerEditorViews {
|
||||
class AdminerEditorViews extends Adminer\Plugin {
|
||||
|
||||
function tableName($tableStatus) {
|
||||
return Adminer\h($tableStatus["Comment"] != "" ? $tableStatus["Comment"] : $tableStatus["Name"]);
|
||||
}
|
||||
|
||||
protected $translations = array(
|
||||
'cs' => array('' => 'Zobrazení pohledů v Adminer Editoru'),
|
||||
'de' => array('' => 'Views im Adminer Editor anzeigen'),
|
||||
'pl' => array('' => 'Wyświetlaj widoki w Adminer Editorze'),
|
||||
'ro' => array('' => 'Afișează vizualizări în Adminer Editor'),
|
||||
'ja' => array('' => 'Adminer Editor にビューを表示'),
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user