1
0
mirror of https://github.com/vrana/adminer.git synced 2025-09-02 02:42:37 +02:00

Compare commits

...

119 Commits

Author SHA1 Message Date
Jakub Vrana
1cfc8451ef Release 5.3.0 2025-05-04 18:30:08 +02:00
Jakub Vrana
62a9cf3e3f Editor: Fix bit edit 2025-05-04 15:20:11 +02:00
Jakub Vrana
e707c7a5f4 Editor: Fix bit and enum search (fix #1062) 2025-05-04 15:20:11 +02:00
Alex Yu
c92b127b56 Add lavender-light theme 2025-05-04 15:20:09 +02:00
Pexle Chris
cf52c4c0a8 AdminerDarkSwitcher: Handle first click
Fixed an issue where the Dark Mode Switcher icon did not respond on the first click if the Dark Mode cookie had not been set yet. Now, clicking the icon correctly sets the cookie and immediately toggles Dark Mode.
2025-05-04 13:10:38 +02:00
Jakub Vrana
5f7fb62803 PostgreSQL: Add NOT ILIKE operator (fix #1066) 2025-05-04 13:03:47 +02:00
Jakub Vrana
9b0acfa7c9 PostgreSQL: Align numbers in SQL command right (fix #1071) 2025-05-04 13:03:47 +02:00
Jakub Vrana
45d61803c4 Align money values right (bug #1071) 2025-05-04 13:03:47 +02:00
Jakub Vrana
48160f2cd7 Add border to column actions (fix #1072) 2025-05-04 13:03:46 +02:00
Jakub Vrana
61f2b370df AdminerSelectEmail: Use Adminer translation 2025-04-26 05:53:10 +02:00
Jakub Vrana
e13910b5c5 AdminerSelectEmail: Wrap long lines 2025-04-26 05:51:07 +02:00
Jakub Vrana
de86789bfc Indexes: Link algorithm doc 2025-04-26 05:43:25 +02:00
Jakub Vrana
ebcf4feeb2 Use variable 2025-04-26 05:31:48 +02:00
Jakub Vrana
d881f51deb MySQL: Fix connecting with port (fix #1057, regression from 5.1.1) 2025-04-24 22:31:03 +02:00
Matthaiks
b9c39e77fc Update Polish translation 2025-04-24 16:20:16 +02:00
salacr
c734deca84 PostgreSQL: Partial Indexes 2025-04-24 11:39:59 +02:00
Jakub Vrana
1466051402 Fix type error in Create function (fix #1053) 2025-04-21 08:36:32 +02:00
Joshi yogesh
36e55a3f55 Add Hindi translations for previously untranslated strings (#1052) 2025-04-19 20:05:11 +02:00
Joshi yogesh
155668906d Add Bengali (bn) translations for missing phrases (#1051) 2025-04-18 23:00:45 +02:00
Jakub Vrana
70ce02a798 AdminerTableIndexesStructure: Print algorithm 2025-04-18 15:00:08 +02:00
Jakub Vrana
de3220acc7 MySQL: Hide index algorithm if only one 2025-04-18 14:58:41 +02:00
Jakub Vrana
1030e84904 CSS: Remove icon padding on iOS 2025-04-18 14:37:16 +02:00
Jakub Vrana
f4ca974623 Add forgotten move_col 2025-04-18 06:53:03 +02:00
Jakub Vrana
af627e7116 AdminerForeignSystem: Link pg_catalog 2025-04-17 19:57:14 +02:00
Jakub Vrana
0c5bba47da non-MySQL: Fix computing unique array 2025-04-17 18:07:23 +02:00
Jakub Vrana
e340a2973e Select: Align numeric functions right 2025-04-17 18:00:49 +02:00
Jakub Vrana
be2afb49c5 PostgreSQL: Link COUNT(*) 2025-04-17 17:46:43 +02:00
Jakub Vrana
c8fa515ed5 CSS: Switch between independent adminer[-dark].css 2025-04-17 17:23:57 +02:00
Jakub Vrana
3dad86d279 Plugins: Allow setting dark mode in css() (bug #1049) 2025-04-17 16:58:58 +02:00
Jakub Vrana
d9c1ac00f3 Extract variable 2025-04-17 15:38:16 +02:00
Jakub Vrana
2a4d2cfb39 Fix typo in comment 2025-04-17 15:34:20 +02:00
Jakub Vrana
67a64c8d72 JUSH: PostgreSQL keywords, autocomplate table aliases 2025-04-17 14:26:55 +02:00
Jakub Vrana
d59d6c4075 MySQL: Support index algorithms with MEMORY and NDB engines 2025-04-17 10:54:04 +02:00
Jakub Vrana
dbec3a1b92 Pass $tableStatus to indexMethods, rename 2025-04-17 10:22:32 +02:00
Jakub Vrana
a93f0003ae MySQL: Remove negation from support() 2025-04-17 10:06:56 +02:00
Jakub Vrana
29a31c6b9c Lang: Use GLOB_BRACE 2025-04-17 08:49:53 +02:00
Joshi yogesh
a04823f4c4 Add Hindi (hi) language translation file for Adminer
This commit adds a new Hindi (hi) translation file for Adminer, based on the existing Bengali (bn) language file.

All interface strings have been translated from Bengali to Hindi to make Adminer more accessible to Hindi-speaking users. The structure of the file follows the format used in other language files within the repository.

The translations were carefully reviewed to ensure accuracy, readability, and consistency with the context of the Adminer application.

File added:
- lang/hi.inc.php

Looking forward to your feedback and happy to make any improvements as needed!
2025-04-17 08:43:05 +02:00
Jakub Vrana
82c544514d Move translation 2025-04-17 08:39:00 +02:00
salacr
7185d7854f Add PHP_CodeSniffer to pipeline (#1044) 2025-04-17 08:31:05 +02:00
Matthaiks
d845d4b358 Update Polish translation 2025-04-17 08:27:48 +02:00
salacr
92ce506243 PostgreSQL: Support index algorithms 2025-04-16 09:22:28 +02:00
Jakub Vrana
6d6998c3d3 PostgreSQL: Support calling functions returning table (fix #1040) 2025-04-15 23:22:36 +02:00
Jakub Vrana
45799f4605 AdminerSqlLog: Store schema 2025-04-15 22:47:48 +02:00
Jakub Vrana
b999f123c8 Swap ' and " 2025-04-15 21:59:41 +02:00
Jakub Vrana
7642b00877 Designs: Don't sniff protocol-relative URLs 2025-04-15 21:43:18 +02:00
Jakub Vrana
45be56e4e1 Add todo 2025-04-15 21:38:29 +02:00
Jakub Vrana
e1b92f73aa Issues: Link Docker image repo 2025-04-15 16:43:58 +02:00
Jakub Vrana
63850ebf19 CSS: Highlight anchor 2025-04-15 09:48:33 +02:00
Jakub Vrana
49fd96f8b9 PostgreSQL: Display partition info (bug #1031) 2025-04-15 08:58:24 +02:00
Matthaiks
fcae403f60 Update Polish translation 2025-04-15 08:40:48 +02:00
Jakub Vrana
7dc152b732 CockroachDB: Partitioning 2025-04-15 07:18:22 +02:00
Jakub Vrana
29f7b2df96 Tests PostgreSQL: Partitioning 2025-04-15 06:51:13 +02:00
Jakub Vrana
3466ab730b Partitioning: Compact array earlier 2025-04-15 06:46:23 +02:00
Jakub Vrana
9235cb8350 Doc: Update 2025-04-15 06:20:13 +02:00
Jakub Vrana
020285772b MySQL: Avoid warning on selecting tables with fulltext indexes (fix #1036) 2025-04-14 17:52:55 +02:00
Jakub Vrana
8238838285 Bug template: offer single language 2025-04-14 17:45:47 +02:00
Jakub Vrana
a165d4ed81 PostgreSQL: Export PARTITION BY (bug #1031) 2025-04-14 16:41:31 +02:00
Jakub Vrana
f5b42eae55 PostgreSQL: Allow renaming inherited tables 2025-04-14 16:03:44 +02:00
Jakub Vrana
460a24ea2d PostgreSQL: Creating partitioned tables (fix #1031) 2025-04-14 15:50:17 +02:00
Jakub Vrana
4646298015 Partitioning: Move to MySQL 2025-04-14 15:33:58 +02:00
Jakub Vrana
cde6b9008c Move partitioning functions 2025-04-14 13:54:20 +02:00
Jakub Vrana
8783f4d3ac PostgreSQL: Include inherited tables in table_status (bug #1031) 2025-04-14 13:13:08 +02:00
Jakub Vrana
3d2395fc59 Remove unnecessary aliases 2025-04-14 12:41:36 +02:00
Jakub Vrana
3a73815ba4 Do not attempt allFields without DB (fix #1033) 2025-04-14 09:21:51 +02:00
Jakub Vrana
d0a2de53ef Bug template: Ask for more info 2025-04-14 08:50:17 +02:00
Jakub Vrana
982233d7e5 Code style: Ignore long translations 2025-04-14 07:58:29 +02:00
salacr
cedfe97f40 Generic.Whitespace -> Generic.WhiteSpace 2025-04-14 07:56:56 +02:00
Jakub Vrana
9beb72edc2 PostgreSQL: Simplify OID 2025-04-13 22:42:05 +02:00
Jakub Vrana
9555c96d6a PostgreSQL: Link parent from inherited tables (bug #1031) 2025-04-13 16:59:16 +02:00
Jakub Vrana
a3d0bbba8f Compile: Fix type (fix #1027) 2025-04-13 16:36:15 +02:00
Jakub Vrana
a9bcde334f PostgreSQL: Move partitioned tables from table list to parent table (bug #1031) 2025-04-13 16:36:10 +02:00
Jakub Vrana
a735b795b2 PostgreSQL: Show partitioned tables as tables, not views (bug #1031) 2025-04-13 14:05:54 +02:00
Jakub Vrana
008cd33058 Designs: adminer.css with 'prefers-color-scheme: dark' don't disable dark mode (fix #1009) 2025-04-13 11:44:58 +02:00
Jakub Vrana
036ce4f1c5 Handle unloaded driver plugins 2025-04-13 08:31:56 +02:00
Jakub Vrana
5867b0724f Plugins: Method bodyClass() to add <body class> (fix #309) 2025-04-13 08:24:16 +02:00
Jakub Vrana
71f2578af6 Driver plugins: Show possible extensions 2025-04-12 09:01:30 +02:00
Jakub Vrana
c809216a56 Develop 2025-04-11 22:27:01 +02:00
Jakub Vrana
eb43ea3025 Release 5.2.1 2025-04-11 22:26:41 +02:00
Jakub Vrana
2ba833409a non-MySQL: Parse '--' without trailing space as comment in SQL command (fix #1025, regression from 5.2.0) 2025-04-11 22:15:14 +02:00
Jakub Vrana
5eaaa498d3 PostgreSQL PDO: Fix bytea without primary key (fix #1021) 2025-04-11 15:29:59 +02:00
Jakub Vrana
746c0a7b0b Update JUSH 2025-04-11 14:53:33 +02:00
Jakub Vrana
b30526213d Lang: Display line of the update 2025-04-10 19:04:06 +02:00
Jakub Vrana
6819815b88 Don't display Loaded plugins with only driver plugins 2025-04-10 18:56:38 +02:00
Jakub Vrana
a83626c8af Fix import without primary key (fix #1017, regression from 5.1.1) 2025-04-10 17:58:22 +02:00
Jakub Vrana
d4ddbc0639 Import: Add margin (fix #1012) 2025-04-10 17:32:03 +02:00
Jakub Vrana
a6cb91f0d2 AdminerLoginOtp: Translate 2025-04-09 08:05:06 +02:00
Jakub Vrana
78d3ce830d Do not highlight table 0 as active 2025-04-09 06:27:09 +02:00
Jakub Vrana
88099b7dd7 Code style: Fix 2025-04-08 21:26:08 +02:00
Jakub Vrana
8bce359fae Fix search anywhere (fix #1004, regression from 5.1.1) 2025-04-08 20:41:44 +02:00
Jakub Vrana
8ca7066625 AdminerMenuLinks: Translation in single language version (fix #1001) 2025-04-08 20:11:47 +02:00
Jakub Vrana
51bcc2a064 Develop 2025-04-08 18:29:13 +02:00
Jakub Vrana
588af652d4 Release 5.2.0 2025-04-08 18:28:50 +02:00
Jakub Vrana
b0c345f9be Update Poslish translations 2025-04-08 17:03:02 +02:00
Jakub Vrana
b2677187f1 Autocomplete: Populate only on pages where useful 2025-04-08 16:57:25 +02:00
Jakub Vrana
52ee085ca7 Update German translation (bug #1001) 2025-04-08 14:33:58 +02:00
Jakub Vrana
00459b302a Translations: Remove trailing fullstops 2025-04-08 13:49:21 +02:00
Jakub Vrana
17598c7ab3 Fix typo in translation 2025-04-08 13:47:36 +02:00
Jakub Vrana
b489cec651 Remove doubled spaces 2025-04-08 13:36:26 +02:00
Jakub Vrana
6da8bb670a Plugins: Compatibility with PHP 5 2025-04-08 13:14:32 +02:00
Takashi SHIRAI
e601a3d8ce Update Japanese translation
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-04-08 13:10:05 +02:00
Jakub Vrana
efde7fcc6c Lang: Non-static $translations 2025-04-08 13:06:27 +02:00
Jakub Vrana
91b3526e8d Plugins: non-static $translations (fix #1000)
The main reason is that static properties are minified.
2025-04-08 12:57:03 +02:00
Jakub Vrana
a3ddd59015 Update externals 2025-04-08 12:54:06 +02:00
Jakub Vrana
7908d86c9f Doc: Plugin translations 2025-04-08 11:00:31 +02:00
Jakub Vrana
0cb41c63c7 Compile: Ignore $this->lang 2025-04-07 21:35:25 +02:00
Jakub Vrana
50de50571d AdminerSqlGemini: Version in User-Agent 2025-04-07 21:27:33 +02:00
Jakub Vrana
de38cb65b6 Designs: Fix logout background 2025-04-07 21:16:30 +02:00
Jakub Vrana
e6cc8bf91e AdminerConfig: Move link (fix #995) 2025-04-07 20:55:01 +02:00
Jakub Vrana
be6cf07d26 Foreign key: Avoid extra newline in error 2025-04-07 19:59:53 +02:00
Jakub Vrana
3f979793f7 Fix foreign key actions (regression from 5.1.1)
https://github.com/vrana/adminer/discussions/969#discussioncomment-12752117
2025-04-07 19:51:43 +02:00
Jakub Vrana
c8878d1652 CSS: Avoid footer shadow over text 2025-04-07 19:24:33 +02:00
Jakub Vrana
bf24198e68 Plugins: Link screenshot 2025-04-07 19:18:43 +02:00
Jakub Vrana
e33ead15e5 AdminerPlugin: Delete 2025-04-07 18:55:14 +02:00
Jakub Vrana
1087d55913 AdminerDotJs: Translate description 2025-04-07 18:53:38 +02:00
Jakub Vrana
38bdd0a961 lang.php: Add translation of plugin description 2025-04-07 18:26:31 +02:00
Jakub Vrana
3dd040abd1 Plugins: Translate descriptions (fix #994) 2025-04-07 18:26:28 +02:00
Jakub Vrana
3e455a4787 Plugins: Extend Adminer\Plugin 2025-04-07 17:02:16 +02:00
Jakub Vrana
95f14bca56 Plugins: Allow providing description 2025-04-07 15:54:31 +02:00
Jakub Vrana
121b77e866 Update German translation (bug #994) 2025-04-07 15:37:08 +02:00
110 changed files with 2384 additions and 451 deletions

View File

@@ -8,8 +8,11 @@ assignees: ''
---
**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._
Also please include a screenshot.
Report issues with Adminer Docker image at https://github.com/TimWolla/docker-adminer._

17
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: CI
on:
pull_request:
branches: [ master ]
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: php-actions/composer@v6
- uses: php-actions/phpcs@v1
with:
path: adminer/
standard: phpcs.xml

View File

@@ -1,8 +1,35 @@
## Adminer dev
## 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 &lt;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 +66,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

View File

@@ -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,7 +29,11 @@ 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) . ")";

View File

@@ -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 = driver()->partitionsInfo($TABLE);
$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";

View File

@@ -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";

View File

@@ -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 {
@@ -28,7 +29,7 @@ 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);
@@ -257,6 +258,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 +338,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 = 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;
}
function hasCStyleEscapes(): bool {
static $c_style;
if ($c_style === null) {
@@ -352,6 +367,10 @@ if (!defined('Adminer\DRIVER')) {
}
return $return;
}
function indexAlgorithms(array $tableStatus): array {
return (preg_match('~^(MEMORY|NDB)$~', $tableStatus["Engine"]) ? array("HASH", "BTREE") : array());
}
}
@@ -543,6 +562,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 +576,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
@@ -676,9 +696,10 @@ if (!defined('Adminer\DRIVER')) {
* @param list<array{string, list<string>, string}> $fields of [$orig, $process_field, $after]
* @param string[] $foreign
* @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 +720,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 +749,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) {
@@ -1011,10 +1052,18 @@ 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

View File

@@ -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";
@@ -390,7 +391,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])

View File

@@ -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";
@@ -94,8 +95,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 +127,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;
}
@@ -201,10 +204,12 @@ 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"); // no "SQL" to avoid CSRF
public $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
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)) {
@@ -250,6 +255,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) {
@@ -322,6 +331,39 @@ if (isset($_GET["pgsql"])) {
}
}
function inheritsFrom(string $table): array {
return get_vals("SELECT relname FROM pg_class JOIN pg_inherits ON inhparent = oid WHERE inhrelid = " . $this->tableOid($table) . " ORDER BY 1");
}
function inheritedTables(string $table): array {
return get_vals("SELECT relname FROM pg_inherits JOIN pg_class ON inhrelid = oid WHERE inhparent = " . $this->tableOid($table) . " ORDER BY 1");
}
function partitionsInfo(string $table): array {
$row = connection()->query("SELECT * FROM pg_partitioned_table WHERE partrelid = " . driver()->tableOid($table))->fetch_assoc();
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 = 'btree' DESC, amname");
}
return $return;
}
function supportsIndex(array $table_status): bool {
// returns true for "materialized view"
return $table_status["Engine"] != "view";
@@ -404,18 +446,20 @@ 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 ? ",
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
FROM pg_class c
JOIN pg_namespace n ON(n.nspname = current_schema() AND n.oid = c.relnamespace)
relname AS \"Name\",
CASE relkind WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' ELSE 'table' END AS \"Engine\"" . ($has_size ? ",
pg_table_size(oid) AS \"Data_length\",
pg_indexes_size(oid) AS \"Index_length\"" : "") . ",
obj_description(oid, 'pg_class') AS \"Comment\",
" . (min_version(12) ? "''" : "CASE WHEN relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
reltuples as \"Rows\",
inhparent AS inherited,
current_schema() AS nspname
FROM pg_class
LEFT JOIN pg_inherits ON inhrelid = oid
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,15 +486,12 @@ 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) ? ",
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
WHERE a.attrelid = " . driver()->tableOid($table) . "
AND NOT a.attisdropped
AND a.attnum > 0
ORDER BY a.attnum") as $row
@@ -486,18 +527,22 @@ 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, (indpred IS NOT NULL)::int as indispartial, pg_am.amname as algorithm, pg_get_expr(pg_index.indpred, pg_index.indrelid, true) AS partial
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]["columns"] = array();
$return[$relname]["descs"] = array();
$return[$relname]["algorithm"] = $row["algorithm"];
$return[$relname]["partial"] = $row["partial"];
if ($row["indkey"]) {
foreach (explode(" ", $row["indkey"]) as $indkey) {
$return[$relname]["columns"][] = $columns[$indkey];
@@ -516,7 +561,7 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
foreach (
get_rows("SELECT conname, condeferrable::int AS deferrable, 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
) {
@@ -527,8 +572,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 +581,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 +664,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 +715,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 +723,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) {
@@ -796,7 +870,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"
);
@@ -897,8 +971,16 @@ AND typelem = 0"
foreach (driver()->checkConstraints($table) as $conname => $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']) {
@@ -953,9 +1035,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)
;
}

View File

@@ -5,8 +5,8 @@ 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;

View File

@@ -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");

View File

@@ -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
}
}

View File

@@ -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
*/
@@ -104,15 +108,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;
@@ -175,6 +188,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")) {
@@ -192,7 +206,6 @@ class Adminer {
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>";
}
@@ -339,9 +352,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 +370,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,7 +417,7 @@ 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 "</div>\n";
@@ -529,46 +555,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;
@@ -1022,13 +1046,15 @@ class Adminer {
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 +1098,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["inherited"]) {
echo '<li><a href="' . h(ME) . 'select=' . urlencode($table) . '"'
. bold($_GET["select"] == $table || $_GET["edit"] == $table, "select")
. " title='" . lang('Select data') . "'>" . lang('select') . "</a> "

View File

@@ -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";
}
}

View File

@@ -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,10 +51,16 @@ 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));

View File

@@ -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];
}
@@ -26,6 +26,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
@@ -197,10 +198,7 @@ abstract class SqlDriver {
* @param Field $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 +218,27 @@ abstract class SqlDriver {
function tableHelp(string $name, bool $is_view = false) {
}
/** Get tables this table inherits from
* @return list<string>
*/
function inheritsFrom(string $table): array {
return array();
}
/** Get inherited tables
* @return list<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 +258,15 @@ 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]
*/
@@ -257,12 +285,16 @@ AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'", $this->conn); // ignore default IS N
*/
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;
}

View File

@@ -201,20 +201,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;
@@ -470,7 +456,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 +485,8 @@ 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]" : "")
;
}

View File

@@ -595,10 +595,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 != "") {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -1,4 +1,4 @@
<?php
namespace Adminer;
const VERSION = "5.2.0-dev";
const VERSION = "5.3.0";

View File

@@ -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";
}
@@ -29,6 +30,8 @@ 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) {
@@ -52,6 +55,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 +64,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);
}
}
}
@@ -105,13 +110,28 @@ $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
@@ -128,6 +148,10 @@ 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;
@@ -138,7 +162,7 @@ foreach ($row["indexes"] as $index) {
$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 +170,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++;

View File

@@ -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

View File

@@ -13,9 +13,6 @@ 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.',
'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>.',
@@ -197,6 +194,7 @@ Lang::$translations = array(
'Partitions' => 'Oddíly',
'Partition name' => 'Název oddílu',
'Values' => 'Hodnoty',
'Inherits from' => 'Zděděná z',
'View' => 'Pohled',
'Materialized view' => 'Materializovaný pohled',
@@ -212,6 +210,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 +352,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

View 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
View 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

View File

@@ -11,7 +11,7 @@ Lang::$translations = array(
'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.' => 'データベースがパスワードに対応していません。',
@@ -107,8 +107,8 @@ Lang::$translations = array(
'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' => 'ターゲット',
@@ -158,6 +158,7 @@ Lang::$translations = array(
'User has been created.' => 'ユーザを作成しました。',
'Hashed' => 'Hashed',
'Column' => '列',
'Columns' => '列',
'Routine' => 'ルーチン',
'Grant' => '権限の付与',
'Revoke' => '権限の取消し',
@@ -307,6 +308,8 @@ Lang::$translations = array(
'Check has been created.' => 'チェックを作成しました。',
'Check has been altered.' => 'チェックを変更しました。',
'Check has been dropped.' => 'チェックを削除しました。',
'screenshot' => 'スクリーンショット',
);
// run `php ../../lang.php ja` to update this file

View 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>.',
@@ -90,6 +87,7 @@ Lang::$translations = array(
'Output' => 'Rezultat',
'open' => 'otwórz',
'save' => 'zapisz',
'Saving' => 'Zapisywanie',
'Format' => 'Format',
'Data' => 'Dane',
@@ -197,6 +195,7 @@ Lang::$translations = array(
'Partitions' => 'Partycje',
'Partition name' => 'Nazwa partycji',
'Values' => 'Wartości',
'Inherits from' => 'Dziedziczy po',
'View' => 'Perspektywa',
'Materialized view' => 'Zmaterializowana perspektywa',
@@ -212,6 +211,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 +289,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 +352,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

View File

@@ -13,9 +13,6 @@ 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.',
'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>.',
@@ -199,6 +196,7 @@ Lang::$translations = array(
'Partitions' => 'Xx',
'Partition name' => 'Xx',
'Values' => 'Xx',
'Inherits from' => 'Xx',
'View' => 'Xx',
'Materialized view' => 'Xx',
@@ -214,6 +212,8 @@ Lang::$translations = array(
'Add next' => 'Xx',
'Index Type' => 'Xx',
'length' => 'xx',
'Algorithm' => 'Xx',
'Condition' => 'Xx',
'Foreign keys' => 'Xx',
'Foreign key' => 'Xx',
@@ -354,6 +354,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

View File

@@ -56,7 +56,7 @@ 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>

View File

@@ -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"]);
@@ -354,7 +354,7 @@ if (!$columns && support("table")) {
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 ($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>";
@@ -389,10 +389,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,8 +412,10 @@ 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] != "")) {
@@ -439,7 +443,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 +460,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 +475,7 @@ if (!$columns && support("table")) {
;
}
}
next($select);
}
if ($backward_keys) {
@@ -584,7 +591,7 @@ 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'") . ">: ";
@@ -592,7 +599,6 @@ if (!$columns && support("table")) {
echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]);
echo " <input type='submit' name='import' value='" . lang('Import') . "'>";
echo "</span>";
echo "</div>";
}
echo input_token();

View File

@@ -20,6 +20,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,7 +52,7 @@ if (!$error && $_POST) {
}
}
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)";
$delimiter = ";";
$offset = 0;
$empty = true;
@@ -64,7 +65,7 @@ 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" ? '|\$[^$]*\$' : '');
$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();
@@ -94,7 +95,7 @@ if (!$error && $_POST) {
$pattern =
($found == '/*' ? '\*/' :
($found == '[' ? ']' :
(preg_match('~^-- |^#~', $found) ? "\n" :
(preg_match("~^$line_comment|^#~", $found) ? "\n" :
preg_quote($found) . ($c_style_escapes ? '|\\\\.' : ''))))
;
@@ -275,7 +276,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"
;

View File

@@ -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; }

View File

@@ -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; }
@@ -57,7 +58,7 @@ input.wayoff { left: -1000px; position: absolute; }
.enum { color: #007F7F; }
.binary { color: red; }
.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 +68,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 +97,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==); }

View File

@@ -22,7 +22,19 @@ if ($comment != "") {
echo "<p class='nowrap'>" . lang('Comment') . ": " . h($comment) . "\n";
}
if ($fields) {
function tables_links($tables) {
echo "<ul>\n";
foreach ($tables as $table) {
echo "<li><a href='" . h(ME . "table=" . urlencode($table)) . "'>" . h($table) . "</a>";
}
echo "</ul>\n";
}
$inherits = driver()->inheritsFrom($TABLE);
if ($inherits) {
echo "<h3>" . lang('Inherits from') . "</h3>\n";
tables_links($inherits);
} elseif ($fields) {
adminer()->tableStructurePrint($fields, $table_status);
}
@@ -30,7 +42,7 @@ 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 +107,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('Partitions') . "</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);
}

View File

@@ -165,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 . ';
@@ -199,10 +199,10 @@ function minify_css($file) {
}
function minify_js($file) {
$file = preg_replace_callback("~'use strict';~", function ($match) { // keep only the first one
$file = preg_replace_callback("~'use strict';~", function ($match) {
static $count = 0;
$count++;
return ($count == 1 ? $match[0] : '');
return ($count == 1 ? $match[0] : ''); // keep only the first one
}, $file);
if (function_exists('jsShrink')) {
$file = jsShrink($file);
@@ -210,7 +210,8 @@ function minify_js($file) {
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;
@@ -303,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";
}
@@ -324,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);
}

View File

@@ -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}

View File

@@ -240,6 +240,8 @@ th {
.logout {
z-index: 2;
background-color: #f2eee1;
box-shadow: 0 0 5px 5px #f2eee1;
}
.js .column {

View File

@@ -0,0 +1,2 @@
## Screenshot
![screenshot](https://www.adminer.org/static/designs/lavender-light/screenshot.png)

View File

@@ -0,0 +1,283 @@
/**
* 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, #logins, #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;
}
/*
* 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;
}

View File

@@ -411,6 +411,7 @@ tbody tr:nth-child(n):hover th {
width: 2em;
height: 2em;
position: fixed;
box-shadow: none;
}
#lang {
top: 0;

View File

@@ -1077,6 +1077,10 @@ body > form{
/*position: relative;*/
}
.logout{
z-index: 5000;
}
#lang {
z-index: 5000;
position: absolute;

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -238,6 +238,8 @@ th {
.logout {
z-index: 5;
background-color: #f2eee1;
box-shadow: 0 0 4px 4px #f2eee1;
}
.js .column {

View File

@@ -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 {

View File

@@ -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)}

View File

@@ -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
Adminers 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.

View File

@@ -32,4 +32,5 @@ if (adminer()->homepage()) {
echo "</div>\n";
echo "</form>\n";
echo script("tableCheck();");
adminer()->pluginsLinks();
}

View File

@@ -52,6 +52,9 @@ class Adminer {
return get_databases($flush);
}
function pluginsLinks(): void {
}
function queryTimeout() {
return 5;
}
@@ -67,12 +70,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;
@@ -253,10 +264,11 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
if (preg_match("~enum~", $field["type"]) || 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, ($field["null"] ? 0 : null))
);
echo "</div>\n";
unset($columns[$name]);
@@ -354,14 +366,14 @@ 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)) . ")";
$conds[] = (in_array(0, $val) ? "$name IS NULL OR " : "") . "$name IN (" . implode(", ", array_map('Adminer\q', $val)) . ")";
} 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";
@@ -489,7 +501,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"]))) {

View File

@@ -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

2
externals/jush vendored

View File

@@ -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";
}
}

View File

@@ -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"/>

View File

@@ -61,12 +61,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}"
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, inherited?: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[][]}"

View File

@@ -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 を読込み'),
);
}

View File

@@ -1,13 +1,12 @@
<?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) {
@@ -52,4 +51,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'),
);
}

View File

@@ -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'),
);
}

View File

@@ -1,13 +1,13 @@
<?php
/** Use Codemirror 5 for syntax highlighting and SQL <textarea> including type-ahead of keywords and tables
/** Use CodeMirror 5 for syntax highlighting and <textarea> including type-ahead of keywords and tables
* @link https://codemirror.net/5/
* @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 AdminerCodemirror {
class AdminerCodemirror extends Adminer\Plugin {
private $root;
private $minified;
@@ -97,4 +97,15 @@ addEventListener('DOMContentLoaded', () => {
<?php
return true;
}
function screenshot() {
return "https://www.adminer.org/static/plugins/codemirror.gif";
}
protected $translations = array(
'cs' => array('' => 'Použít CodeMirror 5 pro zvýrazňování syntaxe a <textarea> včetně našeptávání klíčových slov a tabulek'),
'de' => array('' => 'CodeMirror 5 verwenden für die Syntaxhervorhebung und <textarea> einschließlich der Überschrift von Schlüsselwörtern und Tabellen'),
'ja' => array('' => 'CodeMirror 5 を用い、キーワードやテーブルを含む構文や <textarea> を強調表示'),
'pl' => array('' => 'Użyj CodeMirror 5 do podświetlania składni i <textarea>, uwzględniając wcześniejsze wpisywanie słów kluczowych i tabel'),
);
}

View File

@@ -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' => '組込みのデザインを使う',
),
);
}

View File

@@ -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'),
);
}

View File

@@ -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 上で表示禁止 (デザイン的な効果のみでセキュリティ的には効果なし)'),
);
}

View File

@@ -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;
/**
@@ -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('' => 'テーマ設定を有効化'),
);
}

View File

@@ -117,7 +117,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() {

View File

@@ -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('' => 'データベース (開発用など) をエクスポートし、別のデータベース (本番用など) と同期'),
);
}

View File

@@ -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 形式でエクスポート'),
);
}

View File

@@ -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()"));
}
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('' => 'エクスポートファイル名に現在日時を含める'),
);
}

View File

@@ -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 形式でエクスポート'),
);
}

View File

@@ -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 形式でエクスポート'),
);
}

View File

@@ -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'),
);
}

View File

@@ -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 形式でエクスポート'),
);
}

View File

@@ -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 を表示'),
);
}

View File

@@ -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('' => '外部キーを編集フォームで選択'),
);
}

View File

@@ -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> を使用'),
);
}

View File

@@ -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'),
);
}

View File

@@ -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 にビューを表示'),
);
}

View File

@@ -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 AdminerEmailTable {
class AdminerEmailTable extends Adminer\Plugin {
protected $table, $id, $title, $subject, $message;
/**
@@ -16,7 +16,7 @@ class AdminerEmailTable {
* @param string $subject quoted column name
* @param string $message quoted column name
*/
function __construct(string $table = "email", string $id = "id", string $title = "subject", string $subject = "subject", string $message = "message") {
function __construct($table = "email", $id = "id", $title = "subject", $subject = "subject", $message = "message") {
$this->table = $table;
$this->id = $id;
$this->title = $title;
@@ -54,4 +54,12 @@ class AdminerEmailTable {
$_POST["email_message"] = $row[1];
}
}
protected $translations = array(
'cs' => array('' => 'Získá předmět a zprávu e-mailu z databáze (Adminer Editor)'),
'de' => array('' => 'E-Mail-Betreff und Nachricht aus der Datenbank abrufen (Adminer Editor)'),
'pl' => array('' => 'Pobieraj temat i wiadomość e-mail z bazy danych (Adminer Editor)'),
'ro' => array('' => 'Obțineți subiectul e-mailului și mesajul din baza de date (Adminer Editor)'),
'ja' => array('' => 'メールの件名と本文をデータベースから取得 (Adminer Editor)'),
);
}

View File

@@ -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 AdminerEnumOption {
class AdminerEnumOption extends Adminer\Plugin {
function editInput($table, $field, $attrs, $value) {
if ($field["type"] == "enum") {
@@ -32,4 +32,12 @@ class AdminerEnumOption {
return "<select$attrs>" . Adminer\optionlist($options, $selected, 1) . "</select>"; // 1 - use keys
}
}
protected $translations = array(
'cs' => array('' => 'Editace políčka enum pomocí <select><option> místo <input type="radio">'),
'de' => array('' => 'Verwenden Sie <select><option> für die enum-Bearbeitung anstelle von <input type="radio">'),
'pl' => array('' => 'Użyj <select><option> do edycji enum zamiast <input type="radio">'),
'ro' => array('' => 'Utilizați <select><option> pentru editarea enum în loc de <input type="radio">'),
'ja' => array('' => '列挙型の編集に <input type="radio"> ではなく <select><option> を使用'),
);
}

View File

@@ -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 AdminerFileUpload {
class AdminerFileUpload extends Adminer\Plugin {
protected $uploadPath, $displayPath, $extensions;
/**
@@ -48,4 +48,12 @@ class AdminerFileUpload {
$link = "$this->displayPath$_GET[select]/$regs[1]-$val";
}
}
protected $translations = array(
'cs' => array('' => 'Políčka končící na "_path" upravuje pomocí <input type="file"> a odkazuje na nahrané soubory z výpisu'),
'de' => array('' => 'Bearbeiten Sie Felder, die mit "_path" enden, um <input type="file"> und verknüpfen Sie sie mit den hochgeladenen Dateien beim Select'),
'pl' => array('' => 'Edytuj pola kończące się na "_path" za pomocą <input type="file"> i link do przesłanych plików z wybierz'),
'ro' => array('' => 'Modificați câmpurile care se termină cu "_path" prin <input type="file"> și creați un link către fișierele încărcate din select'),
'ja' => array('' => '列名が "_path" で終わる列を <input type="file"> で変更し、"選択" からアップロードされたファイルにリンク'),
);
}

View File

@@ -1,12 +1,12 @@
<?php
/** Link system tables (in mysql and information_schema databases) by foreign keys
/** Link system tables (in "mysql", "information_schema" and "pg_catalog" schemas) by foreign keys
* @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 AdminerForeignSystem {
class AdminerForeignSystem extends Adminer\Plugin {
function foreignKeys($table) {
if (Adminer\DRIVER == "server" && Adminer\DB == "mysql") {
@@ -29,7 +29,8 @@ class AdminerForeignSystem {
"time_zone_transition_type" => array(array("table" => "time_zone", "source" => array("Time_zone_id"), "target" => array("Time_zone_id"))),
);
return $return[$table];
} elseif (Adminer\DB == "information_schema") {
} elseif (Adminer\DB == "information_schema" || $_GET["ns"] == "information_schema") {
$schemata = $this->schemata("TABLE");
$tables = $this->tables("TABLE");
$columns = array("table" => "COLUMNS", "source" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "COLUMN_NAME"), "target" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "COLUMN_NAME"));
@@ -85,10 +86,88 @@ class AdminerForeignSystem {
"VIEWS" => array($schemata, $this->character_sets("CHARACTER_SET_CLIENT"), $this->collations("COLLATION_CONNECTION")),
"VIEW_TABLE_USAGE" => array($schemata, $this->schemata("VIEW"), $tables, array("table" => "VIEWS", "source" => array("VIEW_CATALOG", "VIEW_SCHEMA", "VIEW_NAME"), "target" => array("TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"))),
);
return $return[$table];
if ($_GET["ns"] == "information_schema") {
$return = $this->lowerCase($return);
}
return $return[strtoupper($table)];
} elseif (Adminer\DRIVER == "pgsql" && $_GET["ns"] == "pg_catalog") {
$mapping = array(
'pg_aggregate' => array('aggfnoid.proc', 'aggtransfn.proc', 'aggfinalfn.proc', 'aggcombinefn.proc', 'aggserialfn.proc', 'aggdeserialfn.proc', 'aggmtransfn.proc', 'aggminvtransfn.proc', 'aggmfinalfn.proc', 'aggsortop.operator', 'aggtranstype.type', 'aggmtranstype.type'),
'pg_am' => array('amhandler.proc'),
'pg_amop' => array('amopfamily.opfamily', 'amoplefttype.type', 'amoprighttype.type', 'amopopr.operator', 'amopmethod.am', 'amopsortfamily.opfamily'),
'pg_amproc' => array('amprocfamily.opfamily', 'amproclefttype.type', 'amprocrighttype.type', 'amproc.proc'),
'pg_attrdef' => array('adrelid.class', 'adnum.attribute.attnum'),
'pg_attribute' => array('attrelid.class', 'atttypid.type', 'attcollation.collation'),
'pg_auth_members' => array('roleid.authid', 'member.authid', 'grantor.authid'),
'pg_cast' => array('castsource.type', 'casttarget.type', 'castfunc.proc'),
'pg_class' => array('relnamespace.namespace', 'reltype.type', 'reloftype.type', 'relowner.authid', 'relam.am', 'reltablespace.tablespace', 'reltoastrelid.class', 'relrewrite.class'),
'pg_collation' => array('collnamespace.namespace', 'collowner.authid'),
'pg_constraint' => array('connamespace.namespace', 'conrelid.class', 'contypid.type', 'conindid.class', 'conparentid.constraint', 'confrelid.class', 'conkey.attribute.attnum', 'confkey.attribute.attnum', 'conpfeqop.operator', 'conppeqop.operator', 'conffeqop.operator', 'confdelsetcols.attribute.attnum', 'conexclop.operator'),
'pg_conversion' => array('connamespace.namespace', 'conowner.authid', 'conproc.proc'),
'pg_database' => array('datdba.authid', 'dattablespace.tablespace'),
'pg_db_role_setting' => array('setdatabase.database', 'setrole.authid'),
'pg_default_acl' => array('defaclrole.authid', 'defaclnamespace.namespace'),
'pg_depend' => array('classid.class', 'refclassid.class'),
'pg_description' => array('classoid.class'),
'pg_enum' => array('enumtypid.type'),
'pg_event_trigger' => array('evtowner.authid', 'evtfoid.proc'),
'pg_extension' => array('extowner.authid', 'extnamespace.namespace', 'extconfig.class'),
'pg_foreign_data_wrapper' => array('fdwowner.authid', 'fdwhandler.proc', 'fdwvalidator.proc'),
'pg_foreign_server' => array('srvowner.authid', 'srvfdw.foreign_data_wrapper'),
'pg_foreign_table' => array('ftrelid.class', 'ftserver.foreign_server'),
'pg_index' => array('indexrelid.class', 'indrelid.class', 'indkey.attribute.attnum', 'indcollation.collation', 'indclass.opclass'),
'pg_inherits' => array('inhrelid.class', 'inhparent.class'),
'pg_init_privs' => array('classoid.class'),
'pg_language' => array('lanowner.authid', 'lanplcallfoid.proc', 'laninline.proc', 'lanvalidator.proc'),
'pg_largeobject' => array('loid.largeobject_metadata'),
'pg_largeobject_metadata' => array('lomowner.authid'),
'pg_namespace' => array('nspowner.authid'),
'pg_opclass' => array('opcmethod.am', 'opcnamespace.namespace', 'opcowner.authid', 'opcfamily.opfamily', 'opcintype.type', 'opckeytype.type'),
'pg_operator' => array('oprnamespace.namespace', 'oprowner.authid', 'oprleft.type', 'oprright.type', 'oprresult.type', 'oprcom.operator', 'oprnegate.operator', 'oprcode.proc', 'oprrest.proc', 'oprjoin.proc'),
'pg_opfamily' => array('opfmethod.am', 'opfnamespace.namespace', 'opfowner.authid'),
'pg_partitioned_table' => array('partrelid.class', 'partdefid.class', 'partattrs.attribute.attnum', 'partclass.opclass', 'partcollation.collation'),
'pg_policy' => array('polrelid.class', 'polroles.authid'),
'pg_proc' => array('pronamespace.namespace', 'proowner.authid', 'prolang.language', 'provariadic.type', 'prosupport.proc', 'prorettype.type', 'proargtypes.type', 'proallargtypes.type', 'protrftypes.type'),
'pg_publication' => array('pubowner.authid'),
'pg_publication_namespace' => array('pnpubid.publication', 'pnnspid.namespace'),
'pg_publication_rel' => array('prpubid.publication', 'prrelid.class', 'prattrs.attribute.attnum'),
'pg_range' => array('rngtypid.type', 'rngsubtype.type', 'rngmultitypid.type', 'rngcollation.collation', 'rngsubopc.opclass', 'rngcanonical.proc', 'rngsubdiff.proc'),
'pg_rewrite' => array('ev_class.class'),
'pg_seclabel' => array('classoid.class'),
'pg_sequence' => array('seqrelid.class', 'seqtypid.type'),
'pg_shdepend' => array('dbid.database', 'classid.class', 'refclassid.class'),
'pg_shdescription' => array('classoid.class'),
'pg_shseclabel' => array('classoid.class'),
'pg_statistic' => array('starelid.class', 'staattnum.attribute.attnum', 'staop.operator', 'stacoll.collation'),
'pg_statistic_ext' => array('stxrelid.class', 'stxnamespace.namespace', 'stxowner.authid', 'stxkeys.attribute.attnum'),
'pg_statistic_ext_data' => array('stxoid.statistic_ext'),
'pg_subscription' => array('subdbid.database', 'subowner.authid'),
'pg_subscription_rel' => array('srsubid.subscription', 'srrelid.class'),
'pg_tablespace' => array('spcowner.authid'),
'pg_transform' => array('trftype.type', 'trflang.language', 'trffromsql.proc', 'trftosql.proc'),
'pg_trigger' => array('tgrelid.class', 'tgparentid.trigger', 'tgfoid.proc', 'tgconstrrelid.class', 'tgconstrindid.class', 'tgconstraint.constraint', 'tgattr.attribute.attnum'),
'pg_ts_config' => array('cfgnamespace.namespace', 'cfgowner.authid', 'cfgparser.ts_parser'),
'pg_ts_config_map' => array('mapcfg.ts_config', 'mapdict.ts_dict'),
'pg_ts_dict' => array('dictnamespace.namespace', 'dictowner.authid', 'dicttemplate.ts_template'),
'pg_ts_parser' => array('prsnamespace.namespace', 'prsstart.proc', 'prstoken.proc', 'prsend.proc', 'prsheadline.proc', 'prslextype.proc'),
'pg_ts_template' => array('tmplnamespace.namespace', 'tmplinit.proc', 'tmpllexize.proc'),
'pg_type' => array('typnamespace.namespace', 'typowner.authid', 'typrelid.class', 'typsubscript.proc', 'typelem.type', 'typarray.type', 'typinput.proc', 'typoutput.proc', 'typreceive.proc', 'typsend.proc', 'typmodin.proc', 'typmodout.proc', 'typanalyze.proc', 'typbasetype.type', 'typcollation.collation'),
'pg_user_mapping' => array('umuser.authid', 'umserver.foreign_server'),
);
$return = array();
foreach ((array) $mapping[$table] as $val) {
list($source, $target, $column) = explode(".", "$val.oid");
$return[] = array("table" => "pg_$target", "source" => array($source), "target" => array($column));
}
return $return;
}
}
private function lowerCase($value) {
return (is_array($value) ? array_map(array($this, 'lowerCase'), $value) : strtolower($value));
}
private function schemata($catalog, $schema = null) {
return array("table" => "SCHEMATA", "source" => array($catalog . "_CATALOG", ($schema ?: $catalog) . "_SCHEMA"), "target" => array("CATALOG_NAME", "SCHEMA_NAME"));
}
@@ -105,4 +184,12 @@ class AdminerForeignSystem {
private function collations($source) {
return array("table" => "COLLATIONS", "source" => array($source), "target" => array("COLLATION_NAME"));
}
protected $translations = array(
'cs' => array('' => 'Propojuje systémové tabulky (v databázích "mysql" a "information_schema") pomocí cizích klíčů'),
'de' => array('' => 'Verknüpfen Sie Systemtabellen (in "mysql"- und "information_schema"-Datenbanken) durch Fremdschlüssel'),
'pl' => array('' => 'Połącz tabele systemowe (w bazach danych "mysql" i "information_schema") za pomocą kluczy obcych'),
'ro' => array('' => 'Conectați tabelele de sistem (în bazele de date "mysql" și "information_schema") prin chei străine'),
'ja' => array('' => 'システムテーブル ("mysql" と "information_schema") を外部キーを用いて接続'),
);
}

View File

@@ -1,18 +1,18 @@
<?php
/** Allow using Adminer inside a frame (disables ClickJacking protection)
/** Allow using Adminer inside a frame
* @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 AdminerFrames {
class AdminerFrames extends Adminer\Plugin {
protected $sameOrigin;
/**
* @param bool $sameOrigin allow running from the same origin only
*/
function __construct(bool $sameOrigin = false) {
function __construct($sameOrigin = false) {
$this->sameOrigin = $sameOrigin;
}
@@ -23,4 +23,12 @@ class AdminerFrames {
header_remove("X-Frame-Options");
}
}
protected $translations = array(
'cs' => array('' => 'Dovolí pracovat Admineru uvnitř rámu'),
'de' => array('' => 'Erlauben Sie die Verwendung von Adminer innerhalb eines Frames'),
'pl' => array('' => 'Zezwalaj na używanie Adminera wewnątrz ramki'),
'ro' => array('' => 'Permiteți utilizarea Adminer în interiorul unui cadru'),
'ja' => array('' => 'フレーム内での Adminer 利用を許可'),
);
}

View File

@@ -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 AdminerJsonColumn {
class AdminerJsonColumn extends Adminer\Plugin {
private function testJson($value) {
if ((substr($value, 0, 1) == '{' || substr($value, 0, 1) == '[') && ($json = json_decode($value, true))) {
return $json;
@@ -45,4 +45,12 @@ class AdminerJsonColumn {
$this->buildTable($json);
}
}
protected $translations = array(
'cs' => array('' => 'Hodnoty JSON v editaci zobrazí formou tabulky'),
'de' => array('' => 'Zeigen Sie JSON-Werte als Tabelle in der Bearbeitung an'),
'pl' => array('' => 'Wyświetl wartości JSON jako tabelę w edycji'),
'ro' => array('' => 'Afișează valorile JSON sub formă de tabel în editare'),
'ja' => array('' => 'JSON 値をテーブルとして編集画面に表示'),
);
}

View File

@@ -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 AdminerLoginIp {
class AdminerLoginIp extends Adminer\Plugin {
protected $ips, $forwarded_for;
/** Set allowed IP addresses
@@ -35,4 +35,12 @@ class AdminerLoginIp {
}
return false;
}
protected $translations = array(
'cs' => array('' => 'Zkontroluje IP adresu a povolí prázdné heslo'),
'de' => array('' => 'Überprüft die IP-Adresse und lässt ein leeres Passwort zu'),
'pl' => array('' => 'Sprawdzaj adres IP i zezwakaj na puste hasło'),
'ro' => array('' => 'Verificați adresa IP și permiteți parola goală'),
'ja' => array('' => 'IP アドレスの確認、及び空パスワードの許可'),
);
}

View File

@@ -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 AdminerLoginOtp {
class AdminerLoginOtp extends Adminer\Plugin {
protected $secret;
/**
@@ -22,7 +22,7 @@ class AdminerLoginOtp {
function loginFormField($name, $heading, $value) {
if ($name == 'password') {
return $heading . $value . "\n"
. "<tr><th><acronym title='One Time Password' lang='en'>OTP</acronym>"
. "<tr><th><abbr title='" . $this->lang('One Time Password') . "'>OTP</abbr>"
. "<td><input type='number' name='auth[otp]' value='" . Adminer\h($_SESSION["otp"]) . "' size='6' autocomplete='one-time-code' inputmode='numeric' maxlength='6' pattern='\d{6}'>\n"
;
}
@@ -39,7 +39,7 @@ class AdminerLoginOtp {
return;
}
}
return 'Invalid OTP.';
return $this->lang('Invalid OTP.');
}
}
@@ -50,4 +50,30 @@ class AdminerLoginOtp {
$unpacked = unpack('N', substr($hash, $offset, 4));
return ($unpacked[1] & 0x7FFFFFFF) % 1e6;
}
function screenshot() {
return "https://www.adminer.org/static/login-otp.png";
}
protected $translations = array(
'cs' => array(
'' => 'Při přihlášení požaduje jednorázové heslo',
'One Time Password' => 'Jednorázové heslo',
'Invalid OTP.' => 'Neplatné jednorázové heslo.',
),
'de' => array(
'' => 'Bei der Anmeldung ist ein Einmalpasswort (Zwei-Faktor-Authentifizierung) erforderlich',
'One Time Password' => 'Einmal-Passwort',
'Invalid OTP.' => 'Ungültiger OTP.',
),
'pl' => array(
'' => 'Wymagaj jednorazowego hasła przy logowaniu',
),
'ro' => array(
'' => 'Cereți o parolă unică la autentificare',
),
'ja' => array(
'' => 'ログイン時にワンタイムパスワード (二要素認証) が必要',
),
);
}

View File

@@ -1,12 +1,12 @@
<?php
/** Enable login for password-less database
/** Enable login without password
* @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 AdminerLoginPasswordLess {
class AdminerLoginPasswordLess extends Adminer\Plugin {
protected $password_hash;
/** Set allowed password
@@ -26,4 +26,12 @@ class AdminerLoginPasswordLess {
return true;
}
}
protected $translations = array(
'cs' => array('' => 'Povolí přihlášení bez hesla'),
'de' => array('' => 'Ermöglicht die Anmeldung ohne Passwort'),
'pl' => array('' => 'Włącz logowanie bez hasła'),
'ro' => array('' => 'Activați autentificarea fără parolă'),
'ja' => array('' => 'パスワードなしのログインを許可'),
);
}

View File

@@ -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 AdminerLoginServers {
class AdminerLoginServers extends Adminer\Plugin {
protected $servers;
/** Set supported servers
@@ -37,4 +37,12 @@ class AdminerLoginServers {
return $heading . Adminer\html_select("auth[server]", array_keys($this->servers), Adminer\SERVER) . "\n";
}
}
protected $translations = array(
'cs' => array('' => 'V přihlašovacím formuláři zobrazuje předdefinovaný seznam serverů'),
'de' => array('' => 'Anzeige einer konstanten Serverliste im Anmeldeformular'),
'pl' => array('' => 'Wyświetlaj stałą listę serwerów w formularzu logowania'),
'ro' => array('' => 'Afișarea unei liste constante de servere în formularul de conectare'),
'ja' => array('' => 'ログイン画面に定義済のサーバリストを表示'),
);
}

View File

@@ -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 AdminerLoginSsl {
class AdminerLoginSsl extends Adminer\Plugin {
protected $ssl;
/**
@@ -21,4 +21,12 @@ class AdminerLoginSsl {
function connectSsl() {
return $this->ssl;
}
protected $translations = array(
'cs' => array('' => 'Připojení k MySQL, PostgreSQL a MS SQL pomocí SSL'),
'de' => array('' => 'Stellen Sie eine Verbindung zu MySQL, PostgreSQL, MS SQL über SSL her'),
'pl' => array('' => 'Połącz się z MySQL, PostgreSQL, MS SQL za pomocą protokołu SSL'),
'ro' => array('' => 'Conectați-vă la MySQL, PostgreSQL, MS SQL utilizând SSL'),
'ja' => array('' => 'MySQL, PostgreSQL, MS SQL への接続時に SSL を利用'),
);
}

View File

@@ -9,13 +9,13 @@ CREATE TABLE login (
);
*/
/** Authenticate a user from the login table
/** Authenticate a user from the "login" table
* @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 AdminerLoginTable {
class AdminerLoginTable extends Adminer\Plugin {
protected $database;
/** Set database of login table */
@@ -26,4 +26,12 @@ class AdminerLoginTable {
function login($login, $password) {
return (bool) Adminer\get_val("SELECT COUNT(*) FROM " . Adminer\idf_escape($this->database) . ".login WHERE login = " . Adminer\q($login) . " AND password_sha1 = " . Adminer\q(sha1($password)));
}
protected $translations = array(
'cs' => array('' => 'Autentizace uživatele podle tabulky "login"'),
'de' => array('' => 'Authentifizieren Sie einen Benutzer über die Tabelle "login"'),
'pl' => array('' => 'Uwierzytelnij użytkownika z tabeli "login"'),
'ro' => array('' => 'Autentificați un utilizator din tabelul "login"'),
'ja' => array('' => '"login" テーブルによるユーザ認証'),
);
}

View File

@@ -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 AdminerMasterSlave {
class AdminerMasterSlave extends Adminer\Plugin {
private $masters = array();
/**
@@ -37,4 +37,12 @@ class AdminerMasterSlave {
$_SESSION["master"] = $result->fetch_assoc();
}
}
protected $translations = array(
'cs' => array('' => 'Zápisy provádět na masteru a čtení na slave'),
'de' => array('' => 'Schreibvorgänge auf dem Master und Lesevorgänge auf dem Slave ausführen'),
'pl' => array('' => 'Wykonuje zapisy na komputerze głównym i odczyty na komputerze podrzędnym'),
'ro' => array('' => 'Executarea scrierilor pe master și a citirilor pe slave'),
'ja' => array('' => 'マスタ書込みとスレーブ読込みの有効化'),
);
}

View File

@@ -16,8 +16,8 @@ class AdminerMenuLinks extends Adminer\Plugin {
function config() {
$options = array(
'select' => Adminer\lang('Select data'),
'table' => Adminer\lang('Show structure'),
'select' => $this->lang('Select data'),
'table' => $this->lang('Show structure'),
'' => $this->lang('Both'),
'auto' => $this->lang('Auto (select on select page, structure otherwise)'),
);
@@ -28,19 +28,20 @@ class AdminerMenuLinks extends Adminer\Plugin {
function tablesPrint(array $tables) {
$menu = Adminer\get_setting("menu", "adminer_config") ?: $this->menu;
$titles = array(
'select' => Adminer\lang('Select data'),
'table' => Adminer\lang('Show structure'),
'select' => $this->lang('Select data'),
'table' => $this->lang('Show structure'),
);
// this is copied from Adminer::tablesPrint()
echo "<ul id='tables'>" . Adminer\script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $table => $status) {
$table = "$table"; // do not highlight "0" as active everywhere
$name = Adminer\adminer()->tableName($status);
if ($name != "") {
if ($name != "" && !$status["inherited"]) {
echo '<li>';
if (!$menu) {
echo '<a href="' . Adminer\h(Adminer\ME) . 'select=' . urlencode($table) . '"'
. Adminer\bold($_GET["select"] == $table || $_GET["edit"] == $table, "select")
. " title='$titles[select]'>" . Adminer\lang('select') . "</a> "
. " title='$titles[select]'>" . $this->lang('select') . "</a> "
;
}
$actives = array($_GET["table"], $_GET["create"], $_GET["indexes"], $_GET["foreign"], $_GET["trigger"], $_GET["check"], $_GET["view"]);
@@ -67,16 +68,49 @@ class AdminerMenuLinks extends Adminer\Plugin {
return true;
}
protected static $translations = array(
function screenshot() {
return "https://www.adminer.org/static/plugins/menu-links.png";
}
protected $translations = array(
'cs' => array(
'' => 'Konfigurace odkazů na tabulky v menu; kombinovatelné s AdminerConfig',
'Menu table links' => 'Odkazy na tabulky v menu',
'Both' => 'Oboje',
'Auto (select on select page, structure otherwise)' => 'Auto (vypsat na výpisech, jinak struktura)',
// this is copied from adminer/lang/
'select' => 'vypsat',
'Select data' => 'Vypsat data',
'Show structure' => 'Zobrazit strukturu',
),
'pl' => array(
'Menu table links' => 'Linki do tabel w menu',
'Both' => 'Obie',
'Auto (select on select page, structure otherwise)' => 'Auto (pokaż na stronie przeglądania, w przeciwnym razie struktura)',
// this is copied from adminer/lang/
'select' => 'przeglądaj',
'Select data' => 'Pokaż dane',
'Show structure' => 'Struktura tabeli',
),
'de' => array(
'' => 'Menü- und Tabellen-Links konfigurieren. Kombinierbar mit AdminerConfig',
'Both' => 'Beide',
'Auto (select on select page, structure otherwise)' => 'Auto (Auswahl auf der ausgewählten Seite, sonst Struktur)',
'Menu table links' => 'Links verwenden in „Tabelle“',
// this is copied from adminer/lang/
'select' => 'zeigen',
'Select data' => 'Daten auswählen',
'Show structure' => 'Struktur anzeigen',
),
'ja' => array(
'' => 'メニュー内テーブルへのリンク設定; AdminerConfig との併用可',
'Both' => '両方',
'Auto (select on select page, structure otherwise)' => '自動 (選択ページでは選択、それ以外では構造)',
'Menu table links' => 'メニューテーブルへのリンク',
// this is copied from adminer/lang/
'select' => '選択',
'Select data' => 'データ',
'Show structure' => '構造',
),
);
}

View File

@@ -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 AdminerMonaco {
class AdminerMonaco extends Adminer\Plugin {
private $root;
function __construct($root = "https://cdn.jsdelivr.net/npm/monaco-editor@0.52/min/vs") {
@@ -71,4 +71,11 @@ addEventListener('DOMContentLoaded', () => {
<?php
return true;
}
protected $translations = array(
'cs' => array('' => 'Použije Monaco Editor z VS Code pro zvýrazňování syntaxe a <textarea>'),
'de' => array('' => 'Monaco-Editor von VS Code verwenden, für die Syntaxhervorhebung und SQL <textarea>'),
'ja' => array('' => '構文や <textarea> の強調表示に VS Code の Monaco Editor を使用'),
'pl' => array('' => 'Użyj Monaco Editora programu VS Code do podświetlania składni i <textarea> SQL'),
);
}

View File

@@ -1,11 +0,0 @@
<?php
// this file is not used anymore and you don't need to include it; it's kept only for backwards compatibility
/** Adminer customization allowing usage of plugins
* @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 AdminerPlugin extends Adminer\Plugins {
}

View File

@@ -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 AdminerPrettyJsonColumn {
class AdminerPrettyJsonColumn extends Adminer\Plugin {
private function testJson($value) {
if ((substr($value, 0, 1) == '{' || substr($value, 0, 1) == '[') && ($json = json_decode($value, true))) {
return $json;
@@ -30,4 +30,12 @@ class AdminerPrettyJsonColumn {
}
}
}
protected $translations = array(
'cs' => array('' => 'V editaci zobrazí syntaxi u JSONu'),
'de' => array('' => 'JSON-Werte in der Bearbeitung hübsch drucken'),
'pl' => array('' => 'Ładnie drukuj wartości JSON w edycji'),
'ro' => array('' => 'Afisare frumoasa a valorilor JSON în editare'),
'ja' => array('' => '編集時に JSON 文字列を見易く表示'),
);
}

View File

@@ -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 AdminerPrism {
class AdminerPrism extends Adminer\Plugin {
private $editorRoot;
private $minified;
private $theme;
@@ -59,4 +59,11 @@ if (el) {
<?php
return true;
}
protected $translations = array(
'cs' => array('' => 'Použije Prism Code Editor pro zvýrazňování syntaxe a <textarea>'),
'de' => array('' => 'Prism Code Editor verwenden, für die Syntaxhervorhebung und <textarea>'),
'ja' => array('' => '構文や <textarea> の強調表示に Prism Code Editor を使用'),
'pl' => array('' => 'Użyj Prism Code Editora do podświetlania składni i <textarea>'),
);
}

View File

@@ -18,7 +18,7 @@ class AdminerSelectEmail extends Adminer\Plugin {
echo $this->lang('Subject') . ": <input name='email_subject' value='" . Adminer\h($_POST["email_subject"]) . "'>\n";
echo "<p><textarea name='email_message' rows='15' cols='75'>" . Adminer\h($_POST["email_message"] . ($_POST["email_append"] ? '{$' . "$_POST[email_addition]}" : "")) . "</textarea>\n";
echo "<p>" . Adminer\script("qsl('p').onkeydown = partialArg(bodyKeydown, 'email_append');", "") . Adminer\html_select("email_addition", $columns, $_POST["email_addition"])
. " <input type='submit' name='email_append' value='" . $this->lang('Insert') . "'>\n"; //! JavaScript
. " <input type='submit' name='email_append' value='" . Adminer\lang('Insert') . "'>\n"; //! JavaScript
echo "<p>" . $this->lang('Attachments') . ": <input type='file' name='email_files[]'>" . Adminer\script("qsl('input').onchange = emailFileChange;");
echo "<p>" . (count($emailFields) == 1 ? Adminer\input_hidden("email_field", key($emailFields)) : Adminer\html_select("email_field", $emailFields));
echo "<input type='submit' name='email' value='" . $this->lang('Send') . "'>" . Adminer\confirm();
@@ -100,51 +100,362 @@ class AdminerSelectEmail extends Adminer\Plugin {
return mail($email, $this->emailHeader($subject), $beginning . $message . $attachments, $headers);
}
protected static $translations = array(
'ar' => array('E-mail' => 'البريد الإلكتروني', 'From' => 'من', 'Subject' => 'الموضوع', 'Send' => 'إرسال', '%d e-mail(s) have been sent.' => 'تم إرسال %d رسالة.', 'Attachments' => 'ملفات مرفقة'),
'bg' => array('E-mail' => 'E-mail', 'From' => 'От', 'Subject' => 'Тема', 'Attachments' => 'Прикачени', 'Send' => 'Изпращане', '%d e-mail(s) have been sent.' => array('%d писмо беше изпратено.', '%d писма бяха изпратени.')),
'bn' => array('E-mail' => '​​ই-মেইল', 'From' => 'থেকে', 'Subject' => 'বিষয়', 'Send' => 'পাঠান', '%d e-mail(s) have been sent.' => array('%d ইমেইল(গুলি) পাঠানো হয়েছে।', '%d ইমেইল(গুলি) পাঠানো হয়েছে।'), 'Attachments' => 'সংযুক্তিগুলো'),
'bs' => array('E-mail' => 'El. pošta', 'From' => 'Od', 'Subject' => 'Naslov', 'Attachments' => 'Prilozi', 'Send' => 'Pošalji', '%d e-mail(s) have been sent.' => array('%d poruka el. pošte je poslata.', '%d poruke el. pošte su poslate.', '%d poruka el. pošte je poslato.')),
'ca' => array('E-mail' => 'Correu electrònic', 'From' => 'De', 'Subject' => 'Assumpte', 'Send' => 'Envia', '%d e-mail(s) have been sent.' => array('S\'ha enviat %d correu electrònic.', 'S\'han enviat %d correus electrònics.'), 'Attachments' => 'Adjuncions'),
'cs' => array('E-mail' => 'E-mail', 'From' => 'Odesílatel', 'Subject' => 'Předmět', 'Attachments' => 'Přílohy', 'Send' => 'Odeslat', '%d e-mail(s) have been sent.' => array('Byl odeslán %d e-mail.', 'Byly odeslány %d e-maily.', 'Bylo odesláno %d e-mailů.')),
'da' => array('E-mail' => 'E-mail', 'From' => 'Fra', 'Subject' => 'Titel', 'Attachments' => 'Vedhæft', 'Send' => 'Send', '%d e-mail(s) have been sent.' => array('%d email sendt.', '%d emails sendt.')),
'de' => array('E-mail' => 'E-Mail', 'From' => 'Von', 'Subject' => 'Betreff', 'Send' => 'Abschicken', '%d e-mail(s) have been sent.' => array('%d E-Mail abgeschickt.', '%d E-Mails abgeschickt.'), 'Attachments' => 'Anhänge'),
'el' => array('E-mail' => 'E-mail', 'From' => 'Από', 'Subject' => 'Θέμα', 'Attachments' => 'Συνημμένα', 'Send' => 'Αποστολή', '%d e-mail(s) have been sent.' => array('%d e-mail απεστάλη.', '%d e-mail απεστάλησαν.')),
'en' => array('%d e-mail(s) have been sent.' => array('%d e-mail has been sent.', '%d e-mails have been sent.')),
'es' => array('E-mail' => 'Email', 'From' => 'De', 'Subject' => 'Asunto', 'Send' => 'Enviar', '%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'), 'Attachments' => 'Adjuntos'),
'et' => array('E-mail' => 'E-post', 'From' => 'Kellelt', 'Subject' => 'Pealkiri', 'Send' => 'Saada', '%d e-mail(s) have been sent.' => 'Saadetud kirju: %d.', 'Attachments' => 'Manused'),
'fa' => array('E-mail' => 'پست الکترونیک', 'From' => 'فرستنده', 'Subject' => 'موضوع', 'Attachments' => 'پیوست ها', 'Send' => 'ارسال', '%d e-mail(s) have been sent.' => array('%d ایمیل ارسال شد.', '%d ایمیل ارسال شد.')),
'fi' => array('E-mail' => 'S-posti', 'From' => 'Lähettäjä', 'Subject' => 'Aihe', 'Attachments' => 'Liitteet', 'Send' => 'Lähetä', '%d e-mail(s) have been sent.' => array('% sähköpostiviestiä lähetetty.', '% sähköpostiviestiä lähetetty.')),
'fr' => array('E-mail' => 'Courriel', 'From' => 'De', 'Subject' => 'Sujet', 'Send' => 'Envoyer', '%d e-mail(s) have been sent.' => array('%d message a été envoyé.', '%d messages ont été envoyés.'), 'Attachments' => 'Pièces jointes'),
'gl' => array('E-mail' => 'Email', 'From' => 'De', 'Subject' => 'Asunto', 'Send' => 'Enviar', '%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'), 'Attachments' => 'Adxuntos'),
'he' => array('E-mail' => 'דוא"ל', 'From' => 'מ:', 'Subject' => 'נושא', 'Send' => 'שלח', '%d e-mail(s) have been sent.' => '%d הודעות דוא"ל נשלחו', 'Attachments' => 'קבצים מצורפים'),
'hu' => array('E-mail' => 'E-mail', 'From' => 'Feladó', 'Subject' => 'Tárgy', 'Send' => 'Küldés', '%d e-mail(s) have been sent.' => array('%d e-mail elküldve.', '%d e-mail elküldve.', '%d e-mail elküldve.'), 'Attachments' => 'Csatolmány'),
'id' => array('E-mail' => 'Surel', 'From' => 'Dari', 'Subject' => 'Judul', 'Attachments' => 'Lampiran', 'Send' => 'Kirim', '%d e-mail(s) have been sent.' => '%d surel berhasil dikirim.'),
'it' => array('E-mail' => 'E-mail', 'From' => 'Da', 'Subject' => 'Oggetto', 'Send' => 'Invia', '%d e-mail(s) have been sent.' => array('%d e-mail inviata.', '%d e-mail inviate.'), 'Attachments' => 'Allegati'),
'ja' => array('E-mail' => 'メール', 'From' => '差出人', 'Subject' => '題名', 'Send' => '送信', '%d e-mail(s) have been sent.' => '%d メールを送信しました。', 'Attachments' => '添付ファイル'),
'ka' => array('E-mail' => 'ელ. ფოსტა', 'From' => 'ავტორი:', 'Subject' => 'თემა', 'Send' => 'გაგზავნა', '%d e-mail(s) have been sent.' => 'გაიგზავნა %d წერილი.', 'Attachments' => 'მიმაგრებული ფაილები'),
'ko' => array('%d e-mail(s) have been sent.' => '%d개 메일을 보냈습니다.', 'Attachments' => '첨부 파일', 'E-mail' => '메일', 'From' => '보낸 사람', 'Send' => '보내기', 'Subject' => '제목'),
'lt' => array('E-mail' => 'El. paštas', 'From' => 'Nuo', 'Subject' => 'Antraštė', 'Attachments' => 'Priedai', 'Send' => 'Siųsti', '%d e-mail(s) have been sent.' => array('Išsiųstas %d laiškas.', 'Išsiųsti %d laiškai.', 'Išsiųsta %d laiškų.')),
'lv' => array('E-mail' => 'Epasts', 'From' => 'No', 'Subject' => 'Tēma', 'Send' => 'Sūtīt', '%d e-mail(s) have been sent.' => array('Nosūtīts %d epasts.', 'Nosūtīti %d epasti.', 'Nosūtīti %d epasti.'), 'Attachments' => 'Pielikumi'),
'ms' => array('E-mail' => 'Emel', 'From' => 'Dari', 'Subject' => 'Subjek', 'Attachments' => 'Lampiran', 'Send' => 'Hantar', '%d e-mail(s) have been sent.' => '%d emel telah dihantar.'),
'nl' => array('E-mail' => 'E-mail', 'From' => 'Van', 'Subject' => 'Onderwerp', 'Send' => 'Verzenden', '%d e-mail(s) have been sent.' => array('%d e-mail verzonden.', '%d e-mails verzonden.'), 'Attachments' => 'Bijlagen'),
'no' => array('E-mail' => 'E-post', 'From' => 'Fra', 'Subject' => 'Tittel', 'Attachments' => 'Vedlegg', 'Send' => 'Send', '%d e-mail(s) have been sent.' => array('%d epost sendt.', '%d eposter sendt.')),
'pl' => array('E-mail' => 'E-mail', 'From' => 'Nadawca', 'Subject' => 'Temat', 'Attachments' => 'Załączniki', 'Send' => 'Wyślij', '%d e-mail(s) have been sent.' => array('Wysłano %d e-mail.', 'Wysłano %d e-maile.', 'Wysłano %d e-maili.')),
'pt-br' => array('E-mail' => 'E-mail', 'From' => 'De', 'Subject' => 'Assunto', 'Send' => 'Enviar', '%d e-mail(s) have been sent.' => array('%d email foi enviado.', '%d emails foram enviados.'), 'Attachments' => 'Anexos'),
'pt' => array('E-mail' => 'E-mail', 'From' => 'De', 'Subject' => 'Assunto', 'Send' => 'Enviar', '%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'), 'Attachments' => 'Anexos'),
'ro' => array('E-mail' => 'Poșta electronică', 'From' => 'De la', 'Subject' => 'Pentru', 'Send' => 'Trimite', '%d e-mail(s) have been sent.' => array('A fost trimis %d mail.', 'Au fost trimise %d mail-uri.'), 'Attachments' => 'Fișiere atașate'),
'ru' => array('E-mail' => 'Эл. почта', 'From' => 'От', 'Subject' => 'Тема', 'Send' => 'Послать', '%d e-mail(s) have been sent.' => array('Было отправлено %d письмо.', 'Было отправлено %d письма.', 'Было отправлено %d писем.'), 'Attachments' => 'Прикреплённые файлы'),
'sk' => array('E-mail' => 'E-mail', 'From' => 'Odosielateľ', 'Subject' => 'Predmet', 'Send' => 'Odoslať', '%d e-mail(s) have been sent.' => array('Bol odoslaný %d e-mail.', 'Boli odoslané %d e-maily.', 'Bolo odoslaných %d e-mailov.'), 'Attachments' => 'Prílohy'),
'sl' => array('E-mail' => 'E-mail', 'From' => 'Od', 'Subject' => 'Zadeva', 'Attachments' => 'Priponke', 'Send' => 'Pošlji', '%d e-mail(s) have been sent.' => array('Poslan je %d e-mail.', 'Poslana sta %d e-maila.', 'Poslani so %d e-maili.', 'Poslanih je %d e-mailov.')),
'sr' => array('E-mail' => 'Ел. пошта', 'From' => 'Од', 'Subject' => 'Наслов', 'Attachments' => 'Прилози', 'Send' => 'Пошаљи', '%d e-mail(s) have been sent.' => array('%d порука ел. поште је послата.', '%d поруке ел. поште су послате.', '%d порука ел. поште је послато.')),
'sv' => array('E-mail' => 'Email', 'From' => 'Från', 'Subject' => 'Ämne', 'Attachments' => 'Bilagor', 'Send' => 'Skicka', '%d e-mail(s) have been sent.' => array('%d email har blivit skickat.', '%d email har blivit skickade.')),
'ta' => array('E-mail' => 'மின்ன‌ஞ்ச‌ல்', 'From' => 'அனுப்புனர்', 'Subject' => 'பொருள்', 'Send' => 'அனுப்பு', '%d e-mail(s) have been sent.' => array('%d மின்ன‌ஞ்ச‌ல் அனுப்ப‌ப‌ட்ட‌து.', '%d மின்ன‌ஞ்ச‌ல்க‌ள் அனுப்ப‌ப்ப‌ட்ட‌ன‌.'), 'Attachments' => 'இணைப்புக‌ள்'),
'th' => array('E-mail' => 'อีเมล์', 'From' => 'จาก', 'Subject' => 'หัวข้อ', 'Send' => 'ส่ง', '%d e-mail(s) have been sent.' => 'มี %d อีเมล์ ถูกส่งออกแล้ว.', 'Attachments' => 'ไฟล์แนบ'),
'tr' => array('E-mail' => 'E-posta', 'From' => 'Gönderen', 'Subject' => 'Konu', 'Attachments' => 'Ekler', 'Send' => 'Gönder', '%d e-mail(s) have been sent.' => array('%d e-posta gönderildi.', '%d adet e-posta gönderildi.')),
'uk' => array('E-mail' => 'E-mail', 'From' => 'Від', 'Subject' => 'Заголовок', 'Attachments' => 'Додатки', 'Send' => 'Надіслати', '%d e-mail(s) have been sent.' => array('Було надіслано %d повідомлення.', 'Було надіслано %d повідомлення.', 'Було надіслано %d повідомлень.')),
'uz' => array('E-mail' => 'E-pochta', 'From' => 'Kimdan', 'Subject' => 'Mavzu', 'Attachments' => 'Ilovalar', 'Send' => 'Yuborish', '%d e-mail(s) have been sent.' => array('%d e-pochta yuborildi.', '%d e-pochtalar yuborildi.')),
'vi' => array('E-mail' => 'Địa chỉ email', 'From' => 'Người gửi', 'Subject' => 'Chủ đề', 'Attachments' => 'Đính kèm', 'Send' => 'Gửi', '%d e-mail(s) have been sent.' => '%d thư đã gửi.'),
'zh-tw' => array('E-mail' => '電子郵件', 'From' => '來自', 'Subject' => '主旨', 'Attachments' => '附件', 'Send' => '寄出', '%d e-mail(s) have been sent.' => '已寄出 %d 封郵件。'),
'zh' => array('E-mail' => '电子邮件', 'From' => '来自', 'Subject' => '主题', 'Attachments' => '附件', 'Send' => '发送', '%d e-mail(s) have been sent.' => '%d 封邮件已发送。'),
protected $translations = array(
'ar' => array(
'E-mail' => 'البريد الإلكتروني',
'From' => 'من',
'Subject' => 'الموضوع',
'Send' => 'إرسال',
'%d e-mail(s) have been sent.' => 'تم إرسال %d رسالة.',
'Attachments' => 'ملفات مرفقة',
),
'bg' => array(
'E-mail' => 'E-mail',
'From' => 'От',
'Subject' => 'Тема',
'Attachments' => 'Прикачени',
'Send' => 'Изпращане',
'%d e-mail(s) have been sent.' => array('%d писмо беше изпратено.', '%d писма бяха изпратени.'),
),
'bn' => array(
'E-mail' => '​​ই-মেইল',
'From' => 'থেকে',
'Subject' => 'বিষয়',
'Send' => 'পাঠান',
'%d e-mail(s) have been sent.' => array('%d ইমেইল(গুলি) পাঠানো হয়েছে।', '%d ইমেইল(গুলি) পাঠানো হয়েছে।'),
'Attachments' => 'সংযুক্তিগুলো',
),
'bs' => array(
'E-mail' => 'El. pošta',
'From' => 'Od',
'Subject' => 'Naslov',
'Attachments' => 'Prilozi',
'Send' => 'Pošalji',
'%d e-mail(s) have been sent.' => array('%d poruka el. pošte je poslata.', '%d poruke el. pošte su poslate.', '%d poruka el. pošte je poslato.'),
),
'ca' => array(
'E-mail' => 'Correu electrònic',
'From' => 'De',
'Subject' => 'Assumpte',
'Send' => 'Envia',
'%d e-mail(s) have been sent.' => array('S\'ha enviat %d correu electrònic.', 'S\'han enviat %d correus electrònics.'),
'Attachments' => 'Adjuncions',
),
'cs' => array(
'' => 'Umožňuje posílat e-maily na adresy v tabulce',
'E-mail' => 'E-mail',
'From' => 'Odesílatel',
'Subject' => 'Předmět',
'Attachments' => 'Přílohy',
'Send' => 'Odeslat',
'%d e-mail(s) have been sent.' => array('Byl odeslán %d e-mail.', 'Byly odeslány %d e-maily.', 'Bylo odesláno %d e-mailů.'),
),
'da' => array(
'E-mail' => 'E-mail',
'From' => 'Fra',
'Subject' => 'Titel',
'Attachments' => 'Vedhæft',
'Send' => 'Send',
'%d e-mail(s) have been sent.' => array('%d email sendt.', '%d emails sendt.'),
),
'de' => array(
'E-mail' => 'E-Mail',
'From' => 'Von',
'Subject' => 'Betreff',
'Send' => 'Abschicken',
'%d e-mail(s) have been sent.' => array('%d E-Mail abgeschickt.', '%d E-Mails abgeschickt.'),
'Attachments' => 'Anhänge',
),
'el' => array(
'E-mail' => 'E-mail',
'From' => 'Από',
'Subject' => 'Θέμα',
'Attachments' => 'Συνημμένα',
'Send' => 'Αποστολή',
'%d e-mail(s) have been sent.' => array('%d e-mail απεστάλη.', '%d e-mail απεστάλησαν.'),
),
'en' => array(
'%d e-mail(s) have been sent.' => array('%d e-mail has been sent.', '%d e-mails have been sent.'),
),
'es' => array(
'E-mail' => 'Email',
'From' => 'De',
'Subject' => 'Asunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Attachments' => 'Adjuntos',
),
'et' => array(
'E-mail' => 'E-post',
'From' => 'Kellelt',
'Subject' => 'Pealkiri',
'Send' => 'Saada',
'%d e-mail(s) have been sent.' => 'Saadetud kirju: %d.',
'Attachments' => 'Manused',
),
'fa' => array(
'E-mail' => 'پست الکترونیک',
'From' => 'فرستنده',
'Subject' => 'موضوع',
'Attachments' => 'پیوست ها',
'Send' => 'ارسال',
'%d e-mail(s) have been sent.' => array('%d ایمیل ارسال شد.', '%d ایمیل ارسال شد.'),
),
'fi' => array(
'E-mail' => 'S-posti',
'From' => 'Lähettäjä',
'Subject' => 'Aihe',
'Attachments' => 'Liitteet',
'Send' => 'Lähetä',
'%d e-mail(s) have been sent.' => array('% sähköpostiviestiä lähetetty.', '% sähköpostiviestiä lähetetty.'),
),
'fr' => array(
'E-mail' => 'Courriel',
'From' => 'De',
'Subject' => 'Sujet',
'Send' => 'Envoyer',
'%d e-mail(s) have been sent.' => array('%d message a été envoyé.', '%d messages ont été envoyés.'),
'Attachments' => 'Pièces jointes',
),
'gl' => array(
'E-mail' => 'Email',
'From' => 'De',
'Subject' => 'Asunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Attachments' => 'Adxuntos',
),
'he' => array(
'E-mail' => 'דוא"ל',
'From' => 'מ:',
'Subject' => 'נושא',
'Send' => 'שלח',
'%d e-mail(s) have been sent.' => '%d הודעות דוא"ל נשלחו',
'Attachments' => 'קבצים מצורפים',
),
'hu' => array(
'E-mail' => 'E-mail',
'From' => 'Feladó',
'Subject' => 'Tárgy',
'Send' => 'Küldés',
'%d e-mail(s) have been sent.' => array('%d e-mail elküldve.', '%d e-mail elküldve.', '%d e-mail elküldve.'),
'Attachments' => 'Csatolmány',
),
'id' => array(
'E-mail' => 'Surel',
'From' => 'Dari',
'Subject' => 'Judul',
'Attachments' => 'Lampiran',
'Send' => 'Kirim',
'%d e-mail(s) have been sent.' => '%d surel berhasil dikirim.',
),
'it' => array(
'E-mail' => 'E-mail',
'From' => 'Da',
'Subject' => 'Oggetto',
'Send' => 'Invia',
'%d e-mail(s) have been sent.' => array('%d e-mail inviata.', '%d e-mail inviate.'),
'Attachments' => 'Allegati',
),
'ja' => array(
'' => 'テーブルに含まれるアドレスにメールを送信',
'E-mail' => 'メール',
'From' => '差出人',
'Subject' => '題名',
'Send' => '送信',
'%d e-mail(s) have been sent.' => '%d メールを送信しました。',
'Attachments' => '添付ファイル',
),
'ka' => array(
'E-mail' => 'ელ. ფოსტა',
'From' => 'ავტორი:',
'Subject' => 'თემა',
'Send' => 'გაგზავნა',
'%d e-mail(s) have been sent.' => 'გაიგზავნა %d წერილი.',
'Attachments' => 'მიმაგრებული ფაილები',
),
'ko' => array(
'%d e-mail(s) have been sent.' => '%d개 메일을 보냈습니다.',
'Attachments' => '첨부 파일',
'E-mail' => '메일',
'From' => '보낸 사람',
'Send' => '보내기',
'Subject' => '제목',
),
'lt' => array(
'E-mail' => 'El. paštas',
'From' => 'Nuo',
'Subject' => 'Antraštė',
'Attachments' => 'Priedai',
'Send' => 'Siųsti',
'%d e-mail(s) have been sent.' => array('Išsiųstas %d laiškas.', 'Išsiųsti %d laiškai.', 'Išsiųsta %d laiškų.'),
),
'lv' => array(
'E-mail' => 'Epasts',
'From' => 'No',
'Subject' => 'Tēma',
'Send' => 'Sūtīt',
'%d e-mail(s) have been sent.' => array('Nosūtīts %d epasts.', 'Nosūtīti %d epasti.', 'Nosūtīti %d epasti.'),
'Attachments' => 'Pielikumi',
),
'ms' => array(
'E-mail' => 'Emel',
'From' => 'Dari',
'Subject' => 'Subjek',
'Attachments' => 'Lampiran',
'Send' => 'Hantar',
'%d e-mail(s) have been sent.' => '%d emel telah dihantar.',
),
'nl' => array(
'E-mail' => 'E-mail',
'From' => 'Van',
'Subject' => 'Onderwerp',
'Send' => 'Verzenden',
'%d e-mail(s) have been sent.' => array('%d e-mail verzonden.', '%d e-mails verzonden.'),
'Attachments' => 'Bijlagen',
),
'no' => array(
'E-mail' => 'E-post',
'From' => 'Fra',
'Subject' => 'Tittel',
'Attachments' => 'Vedlegg',
'Send' => 'Send',
'%d e-mail(s) have been sent.' => array('%d epost sendt.', '%d eposter sendt.'),
),
'pl' => array(
'E-mail' => 'E-mail',
'From' => 'Nadawca',
'Subject' => 'Temat',
'Attachments' => 'Załączniki',
'Send' => 'Wyślij',
'%d e-mail(s) have been sent.' => array('Wysłano %d e-mail.', 'Wysłano %d e-maile.', 'Wysłano %d e-maili.'),
),
'pt-br' => array(
'E-mail' => 'E-mail',
'From' => 'De',
'Subject' => 'Assunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email foi enviado.', '%d emails foram enviados.'),
'Attachments' => 'Anexos',
),
'pt' => array(
'E-mail' => 'E-mail',
'From' => 'De',
'Subject' => 'Assunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Attachments' => 'Anexos',
),
'ro' => array(
'E-mail' => 'Poșta electronică',
'From' => 'De la',
'Subject' => 'Pentru',
'Send' => 'Trimite',
'%d e-mail(s) have been sent.' => array('A fost trimis %d mail.', 'Au fost trimise %d mail-uri.'),
'Attachments' => 'Fișiere atașate',
),
'ru' => array(
'E-mail' => 'Эл. почта',
'From' => 'От',
'Subject' => 'Тема',
'Send' => 'Послать',
'%d e-mail(s) have been sent.' => array('Было отправлено %d письмо.', 'Было отправлено %d письма.', 'Было отправлено %d писем.'),
'Attachments' => 'Прикреплённые файлы',
),
'sk' => array(
'E-mail' => 'E-mail',
'From' => 'Odosielateľ',
'Subject' => 'Predmet',
'Send' => 'Odoslať',
'%d e-mail(s) have been sent.' => array('Bol odoslaný %d e-mail.', 'Boli odoslané %d e-maily.', 'Bolo odoslaných %d e-mailov.'),
'Attachments' => 'Prílohy',
),
'sl' => array(
'E-mail' => 'E-mail',
'From' => 'Od',
'Subject' => 'Zadeva',
'Attachments' => 'Priponke',
'Send' => 'Pošlji',
'%d e-mail(s) have been sent.' => array('Poslan je %d e-mail.', 'Poslana sta %d e-maila.', 'Poslani so %d e-maili.', 'Poslanih je %d e-mailov.'),
),
'sr' => array(
'E-mail' => 'Ел. пошта',
'From' => 'Од',
'Subject' => 'Наслов',
'Attachments' => 'Прилози',
'Send' => 'Пошаљи',
'%d e-mail(s) have been sent.' => array('%d порука ел. поште је послата.', '%d поруке ел. поште су послате.', '%d порука ел. поште је послато.'),
),
'sv' => array(
'E-mail' => 'Email',
'From' => 'Från',
'Subject' => 'Ämne',
'Attachments' => 'Bilagor',
'Send' => 'Skicka',
'%d e-mail(s) have been sent.' => array('%d email har blivit skickat.', '%d email har blivit skickade.'),
),
'ta' => array(
'E-mail' => 'மின்ன‌ஞ்ச‌ல்',
'From' => 'அனுப்புனர்',
'Subject' => 'பொருள்',
'Send' => 'அனுப்பு',
'%d e-mail(s) have been sent.' => array('%d மின்ன‌ஞ்ச‌ல் அனுப்ப‌ப‌ட்ட‌து.', '%d மின்ன‌ஞ்ச‌ல்க‌ள் அனுப்ப‌ப்ப‌ட்ட‌ன‌.'),
'Attachments' => 'இணைப்புக‌ள்',
),
'th' => array(
'E-mail' => 'อีเมล์',
'From' => 'จาก',
'Subject' => 'หัวข้อ',
'Send' => 'ส่ง',
'%d e-mail(s) have been sent.' => 'มี %d อีเมล์ ถูกส่งออกแล้ว.',
'Attachments' => 'ไฟล์แนบ',
),
'tr' => array(
'E-mail' => 'E-posta',
'From' => 'Gönderen',
'Subject' => 'Konu',
'Attachments' => 'Ekler',
'Send' => 'Gönder',
'%d e-mail(s) have been sent.' => array('%d e-posta gönderildi.', '%d adet e-posta gönderildi.'),
),
'uk' => array(
'E-mail' => 'E-mail',
'From' => 'Від',
'Subject' => 'Заголовок',
'Attachments' => 'Додатки',
'Send' => 'Надіслати',
'%d e-mail(s) have been sent.' => array('Було надіслано %d повідомлення.', 'Було надіслано %d повідомлення.', 'Було надіслано %d повідомлень.'),
),
'uz' => array(
'E-mail' => 'E-pochta',
'From' => 'Kimdan',
'Subject' => 'Mavzu',
'Attachments' => 'Ilovalar',
'Send' => 'Yuborish',
'%d e-mail(s) have been sent.' => array('%d e-pochta yuborildi.', '%d e-pochtalar yuborildi.'),
),
'vi' => array(
'E-mail' => 'Địa chỉ email',
'From' => 'Người gửi',
'Subject' => 'Chủ đề',
'Attachments' => 'Đính kèm',
'Send' => 'Gửi',
'%d e-mail(s) have been sent.' => '%d thư đã gửi.',
),
'zh-tw' => array(
'E-mail' => '電子郵件',
'From' => '來自',
'Subject' => '主旨',
'Attachments' => '附件',
'Send' => '寄出',
'%d e-mail(s) have been sent.' => '已寄出 %d 封郵件。',
),
'zh' => array(
'E-mail' => '电子邮件',
'From' => '来自',
'Subject' => '主题',
'Attachments' => '附件',
'Send' => '发送',
'%d e-mail(s) have been sent.' => '%d 封邮件已发送。'),
);
}

View File

@@ -6,14 +6,14 @@
* @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 AdminerSlugify {
class AdminerSlugify extends Adminer\Plugin {
protected $from, $to;
/**
* @param string $from find these characters ...
* @param string $to ... and replace them by these
*/
function __construct(string $from = 'áčďéěíňóřšťúůýž', string $to = 'acdeeinorstuuyz') {
function __construct($from = 'áčďéěíňóřšťúůýž', $to = 'acdeeinorstuuyz') {
$this->from = $from;
$this->to = $to;
}
@@ -46,4 +46,12 @@ class AdminerSlugify {
}
}
}
protected $translations = array(
'cs' => array('' => 'Předvyplní políčko obsahující "_slug" URLizovanou hodnotou předchozího políčka (JavaScript)'),
'de' => array('' => 'Feld, das "_slug" enthält, mit dem Slugified-Wert eines vorherigen Felds vorab füllen (JavaScript)'),
'pl' => array('' => 'Wstępnie wypełnij pole zawierające "_slug" osłabioną wartością poprzedniego pola (JavaScript)'),
'ro' => array('' => 'Precompletați câmpul care conține "_slug" cu valoarea slugificată a unui câmp anterior (JavaScript)'),
'ja' => array('' => '列名に "_slug" を含む列を、前列の URL 化された値でプレフィル (JavaScript)'),
);
}

View File

@@ -2,7 +2,6 @@
/** AI prompt in SQL command generating the queries with Google Gemini
* Beware that this sends your whole database structure (not data) to Google Gemini.
* @link https://www.adminer.org/static/plugins/sql-gemini.gif
* @link https://gemini.google.com/
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
@@ -17,7 +16,7 @@ class AdminerSqlGemini extends Adminer\Plugin {
* @param string $apiKey The default key is shared with all users and may run out of quota; get your own API key at: https://aistudio.google.com/apikey
* @param string $model Available models: https://ai.google.dev/gemini-api/docs/models#available-models
*/
function __construct(string $apiKey = 'AIzaSyDWDbPjmvH9_hphsnY_yJGdue42qRMG3do', string $model = "gemini-2.0-flash") {
function __construct($apiKey = 'AIzaSyDWDbPjmvH9_hphsnY_yJGdue42qRMG3do', $model = "gemini-2.0-flash") {
$this->apiKey = $apiKey;
$this->model = $model;
}
@@ -33,7 +32,7 @@ class AdminerSqlGemini extends Adminer\Plugin {
//~ echo $prompt; exit;
$context = stream_context_create(array("http" => array(
"method" => "POST",
"header" => array("User-Agent: AdminerSqlGemini", "Content-Type: application/json"),
"header" => array("User-Agent: AdminerSqlGemini/" . Adminer\VERSION, "Content-Type: application/json"),
"content" => '{"contents": [{"parts":[{"text": ' . json_encode($prompt) . '}]}]}',
"ignore_errors" => true,
)));
@@ -92,9 +91,14 @@ geminiText.onkeydown = event => {
<?php
}
function screenshot() {
return "https://www.adminer.org/static/plugins/sql-gemini.gif";
}
// use the phrases from https://gemini.google.com/
protected static $translations = array(
protected $translations = array(
'cs' => array(
'' => 'Generování SQL příkazů pomocí umělé inteligence Google Gemini',
'Ask Gemini' => 'Zeptat se Gemini',
'Just a sec...' => 'Chviličku...',
),
@@ -102,5 +106,15 @@ geminiText.onkeydown = event => {
'Ask Gemini' => 'Zapytaj Gemini',
'Just a sec...' => 'Chwileczkę...',
),
'de' => array(
'' => 'KI-Eingabeaufforderung im SQL-Befehl zur Erstellung der Abfragen mit Google Gemini',
'Ask Gemini' => 'Gemini fragen',
'Just a sec...' => 'Einen Moment...',
),
'ja' => array(
'' => 'Google Gemini AI を用いて SQL 文を生成',
'Ask Gemini' => 'Gemini に聞く',
'Just a sec...' => 'しばらくお待ち下さい...',
),
);
}

View File

@@ -6,13 +6,13 @@
* @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 AdminerSqlLog {
class AdminerSqlLog extends Adminer\Plugin {
protected $filename;
/**
* @param string $filename defaults to "$database.sql"
*/
function __construct(string $filename = "") {
function __construct($filename = "") {
$this->filename = $filename;
}
@@ -26,7 +26,7 @@ class AdminerSqlLog {
private function log($query) {
if ($this->filename == "") {
$this->filename = Adminer\adminer()->database() . ".sql"; // no database goes to ".sql" to avoid collisions
$this->filename = Adminer\adminer()->database() . ($_GET["ns"] != "" ? ".$_GET[ns]" : "") . ".sql"; // no database goes to ".sql" to avoid collisions
}
$fp = fopen($this->filename, "a");
flock($fp, LOCK_EX);
@@ -35,4 +35,12 @@ class AdminerSqlLog {
flock($fp, LOCK_UN);
fclose($fp);
}
protected $translations = array(
'cs' => array('' => 'Zaznamenává všechny příkazy do souboru SQL'),
'de' => array('' => 'Protokollieren Sie alle Abfragen in einer SQL-Datei'),
'pl' => array('' => 'Rejestruj wszystkie zapytania do pliku SQL'),
'ro' => array('' => 'Logați toate interogările în fișierul SQL'),
'ja' => array('' => '全クエリを SQL ファイルに記録'),
);
}

Some files were not shown because too many files have changed in this diff Show More