1
0
mirror of https://github.com/vrana/adminer.git synced 2025-08-30 09:39:51 +02:00

Compare commits

...

174 Commits

Author SHA1 Message Date
Jakub Vrana
dcf11d8fc9 PgSQL <11: Fix error in table list (fix #1128) 2025-08-04 07:46:14 +02:00
Jakub Vrana
0de6a057d3 PgSQL <10: Don't try partitions 2025-08-04 07:44:46 +02:00
Jakub Vrana
18b62aba38 Fix typo (fix #1137) 2025-08-02 08:24:00 +02:00
Jakub Vrana
10ff74552d PostgreSQL: Quote edit value with interval operator 2025-07-29 16:47:47 +02:00
Jakub Vrana
f5c7ab54f7 Update JUSH 2025-07-29 11:05:25 +02:00
Jakub Vrana
cc110c448c Plugins: PHP 5 compatibility (fix #1125) 2025-07-28 22:46:31 +02:00
Jakub Vrana
6f766f8c52 PostgreSQL: Limit dollar-quoted strings 2025-07-16 11:16:58 +02:00
Jakub Vrana
9a60d158f1 Increase routine textarea size 2025-07-07 15:22:46 +02:00
Jakub Vrana
6dec0d63b0 New plugin: Specify query timeout 2025-07-07 15:21:02 +02:00
Jakub Vrana
c9a52cd28c Link routines from syntax highlighting 2025-07-07 15:06:41 +02:00
Jakub Vrana
9424b7431e Elastic: Avoid extra newline 2025-07-07 15:06:41 +02:00
Jakub Vrana
35b2b969be Save vertical real estate 2025-06-28 18:04:30 +02:00
Jakub Vrana
165f1b241c AdminerBackwardKeys: Work with null ns in non-PgSQL 2025-06-28 11:31:05 +02:00
Jakub Vrana
4d75f822e9 PostgreSQL: Shorten values in hstore columns 2025-06-25 18:39:32 +02:00
Jakub Vrana
5e9c185596 Display data length and index length for materialized views 2025-06-25 18:38:34 +02:00
Jakub Vrana
cfd69dfd8c Trigger script=db sooner 2025-06-25 17:15:34 +02:00
Jakub Vrana
1fc5acb389 AdminerBackwardKeys: Skip relations without selected column 2025-06-25 14:57:35 +02:00
Jakub Vrana
8ca0e36970 AdminerBackwardKeys: Link only tables in the same schema 2025-06-25 14:52:12 +02:00
Jakub Vrana
9e8de24e3d Fix a typo 2025-06-25 14:39:55 +02:00
Jakub Vrana
4cb09852e3 Display @ after username without server in existing logins 2025-06-23 19:08:55 +02:00
Jakub Vrana
e115dccdae MariaDB: Parse COLLATE in routine definition (fix #1104) 2025-06-19 10:03:39 +02:00
Jakub Vrana
1ba410cad1 MySQL: Simplify routines() 2025-06-19 09:56:36 +02:00
Jakub Vrana
0764a20a19 New plugin: Display row numbers in select (fix #1106) 2025-06-19 09:35:01 +02:00
Jakub Vrana
b2c4574325 AdminerBackwardKeys: Strip table prefix 2025-06-19 09:25:03 +02:00
Jakub Vrana
466eceff40 Rename highlight plugins 2025-06-19 09:12:48 +02:00
Jakub Vrana
27c5f6d21b AdminerBackwardKeys: Support PostgreSQL 2025-06-18 18:21:38 +02:00
Jakub Vrana
2744538c8c Fix type error in multiple database export (fix #1109) 2025-06-18 10:40:06 +02:00
Matthaiks
41456c9eb7 Update Polish translation 2025-06-18 10:04:38 +02:00
Jakub Vrana
9745c12769 AdminerSqlGemini: Remove extra comments (fix #1075) 2025-06-18 09:36:17 +02:00
Jakub Vrana
f24f72ac51 Allow exporting SQL in SQL command (fix #1092) 2025-06-18 09:19:15 +02:00
Jakub Vrana
2b52a9b653 PostgreSQL: Allow comparing json columns (fix #1107) 2025-06-18 09:00:34 +02:00
Jakub Vrana
9ba4b86916 AdminerTablesFilter: Add title 2025-06-18 08:08:54 +02:00
Jakub Vrana
1cc3fdf915 Add section links in database overview 2025-06-11 19:33:07 +02:00
Jakub Vrana
b960c41d63 Elastic: Display indexes in alias (unused for now) 2025-06-11 15:16:19 +02:00
Jakub Vrana
3ec750ef7e Hide view links without view support 2025-06-11 15:16:17 +02:00
Jakub Vrana
737b631dda Elasticsearch: Support dropping aliases 2025-06-11 08:45:21 +02:00
Jakub Vrana
8fc450946c Driver plugins: Readme 2025-06-11 08:16:57 +02:00
Jakub Vrana
e282d6eb89 PostgreSQL: Display index expressions 2025-06-06 10:42:20 +02:00
Jakub Vrana
86ffeb2a1e PostgreSQL: Add SQL operator to select 2025-06-06 10:19:24 +02:00
Jakub Vrana
31530ba03e Do not order descending in GROUP BY select 2025-06-05 10:26:11 +02:00
Jakub Vrana
146d3539d8 Warn about exceeded max_file_uploads in import 2025-06-04 14:04:24 +02:00
Jakub Vrana
04068a631e Allow null value in where_link() 2025-06-03 16:17:55 +02:00
Jakub Vrana
4698686232 PostgreSQL: Hide only partitions, not all inherited tables from menu 2025-06-03 15:05:12 +02:00
Jakub Vrana
57b6afc8cb Fix heading of inherited tables 2025-06-03 14:40:08 +02:00
Jakub Vrana
e589c80f42 PostgreSQL: Show structure of inherited tables 2025-06-03 14:33:30 +02:00
Ludek Benedik
acfebf1788 Plugins pretty-json-column: Encode value to JSON without white spaces 2025-05-30 14:02:18 +02:00
Jakub Vrana
246c3c489b MySQL 5.0-: Do not load partitioning info in alter table (fix #1099) 2025-05-30 13:28:28 +02:00
yamamoto
92b95606c1 Updates Japanese translation (#1096) 2025-05-27 16:28:59 +02:00
Jakub Vrana
ac8318f387 PostgreSQL 11-: Avoid duplicate oid in table status (fix #1089) 2025-05-27 13:58:22 +02:00
Maxim Milovanov
d1c2679acd Add Win98-style design theme 2025-05-27 13:58:19 +02:00
Jakub Vrana
caf6e495dc Update externals 2025-05-27 10:37:12 +02:00
salacr
ec5ad85470 Make kill_process / process_list extendable 2025-05-07 08:29:43 +02:00
Jakub Vrana
307fabaf22 Allow specifying operator in search anywhere 2025-05-05 09:56:59 +02:00
schucan
1862b84612 Mute chmod warning 2025-05-05 08:05:36 +02:00
Jakub Vrana
e76be9f890 Develop 2025-05-05 08:04:55 +02:00
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
122 changed files with 3030 additions and 605 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,59 @@
## Adminer dev
- Allow specifying operator in search anywhere
- Do not order descending in GROUP BY select
- Allow exporting SQL in SQL command (bug #1092)
- Add section links in database overview
- Warn about exceeded max_file_uploads in import
- Display @ after username without server in existing logins
- Display data length and index length for materialized views
- Link routines from syntax highlighting
- MySQL 5.0-: Do not load partitioning info in alter table (bug #1099)
- MariaDB: Parse COLLATE in routine definition (bug #1104)
- PostgreSQL: Show structure of inherited tables
- PostgreSQL: Display index expressions
- PostgreSQL: Add SQL operator to select
- PostgreSQL: Hide only partitions, not all inherited tables from menu
- PostgreSQL: Allow comparing json columns (bug #1107)
- PostgreSQL: Shorten values in hstore columns
- PostgreSQL: Quote edit value with interval operator
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089)
- Elasticsearch: Support dropping aliases
- Plugins: Methods afterConnect(), processList() and killProcess()
- New plugin: Display row numbers in select (bug #1106)
- New plugin: Specify query timeout
## Adminer 5.3.0 (released 2025-05-04)
- Align numeric functions right
- Autocomplete: Support table aliases
- Fix type error in Create function (bug #1053, regression from 5.1.1)
- Add border to column actions (bug #1072)
- Align money values right (bug #1071)
- MySQL: Avoid warning on selecting tables with fulltext indexes (bug #1036)
- MySQL, PostgreSQL: Support index algorithms (bug #1030)
- MySQL: Fix connecting to localhost:3306 (bug #1057, regression from 5.1.1)
- PostgreSQL, CockroachDB: Creating partitioned tables (bug #1031)
- PostgreSQL: Move partitioned tables from table list to parent table
- PostgreSQL: Support partial indices (bug #1048)
- PostgreSQL: Support calling functions returning table (bug #1040)
- PostgreSQL: Add NOT ILIKE operator (bug #1066)
- Editor: Fix bit and enum search (bug #1062)
- Designs: adminer.css with 'prefers-color-scheme: dark' doesn't disable dark mode
- Plugins: Method bodyClass() to add &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 +90,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 = ($partition_by ? driver()->partitionsInfo($TABLE) : array());
$referencable_primary = referencable_primary($TABLE);
$foreign_keys = array();
@@ -80,40 +78,26 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
}
}
$partitioning = "";
if (support("partitioning")) {
if (isset($partition_by[$row["partition_by"]])) {
$params = array();
foreach ($row as $key => $val) {
if (preg_match('~^partition~', $key)) {
$params[$key] = $val;
}
$partitioning = array();
if (in_array($row["partition_by"], $partition_by)) {
foreach ($row as $key => $val) {
if (preg_match('~^partition~', $key)) {
$partitioning[$key] = $val;
}
foreach ($params["partition_names"] as $key => $name) {
if ($name == "") {
unset($params["partition_names"][$key]);
unset($params["partition_values"][$key]);
}
}
if ($params != get_partitions_info($TABLE)) {
$partitions = array();
if ($params["partition_by"] == 'RANGE' || $params["partition_by"] == 'LIST') {
foreach ($params["partition_names"] as $key => $name) {
$value = $params["partition_values"][$key];
$partitions[] = "\n PARTITION " . idf_escape($name) . " VALUES " . ($params["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
}
}
// $params["partition"] can be expression, not only column
$partitioning .= "\nPARTITION BY $params[partition_by]($params[partition])";
if ($partitions) {
$partitioning .= " (" . implode(",", $partitions) . "\n)";
} elseif ($params["partitions"]) {
$partitioning .= " PARTITIONS " . (+$params["partitions"]);
}
}
} elseif (preg_match("~partitioned~", $table_status["Create_options"])) {
$partitioning .= "\nREMOVE PARTITIONING";
}
foreach ($partitioning["partition_names"] as $key => $name) {
if ($name == "") {
unset($partitioning["partition_names"][$key]);
unset($partitioning["partition_values"][$key]);
}
}
$partitioning["partition_names"] = array_values($partitioning["partition_names"]);
$partitioning["partition_values"] = array_values($partitioning["partition_values"]);
if ($partitioning == $partitions_info) {
$partitioning = array();
}
} elseif (preg_match("~partitioned~", $table_status["Create_options"])) {
$partitioning = null;
}
$message = lang('Table has been altered.');
@@ -159,8 +143,8 @@ if (!$_POST) {
$row["fields"][] = $field;
}
if (support("partitioning")) {
$row += get_partitions_info($TABLE);
if ($partition_by) {
$row += $partitions_info;
$row["partition_names"][] = "";
$row["partition_values"][] = "";
}
@@ -221,10 +205,10 @@ if (support("columns")) {
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $TABLE)); ?>
<?php } ?>
<?php
if (support("partitioning")) {
if ($partition_by && (JUSH == 'sql' || $TABLE == "")) {
$partition_table = preg_match('~RANGE|LIST~', $row["partition_by"]);
print_fieldset("partition", lang('Partition by'), $row["partition_by"]);
echo "<p>" . html_select("partition_by", array("" => "") + $partition_by, $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;");
echo "<p>" . html_select("partition_by", array_merge(array(""), $partition_by), $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;");
echo "(<input name='partition' value='" . h($row["partition"]) . "'>)\n";
echo lang('Partitions') . ": <input type='number' name='partitions' class='size" . ($partition_table || !$row["partition_by"] ? " hidden" : "") . "' value='" . h($row["partitions"]) . "'>\n";
echo "<table id='partition-table'" . ($partition_table ? "" : " class='hidden'") . ">\n";

View File

@@ -64,12 +64,13 @@ if (adminer()->homepage()) {
echo "<form action='' method='post'>\n";
if (support("table")) {
echo "<fieldset><legend>" . lang('Search data in tables') . " <span id='selected2'></span></legend><div>";
echo "<input type='search' name='query' value='" . h($_POST["query"]) . "'>";
echo html_select("op", adminer()->operators(), idx($_POST, "op", JUSH == "elastic" ? "should" : "LIKE %%"));
echo " <input type='search' name='query' value='" . h($_POST["query"]) . "'>";
echo script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');", "");
echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n";
echo "</div></fieldset>\n";
if ($_POST["search"] && $_POST["query"] != "") {
$_GET["where"][0]["op"] = driver()->convertOperator("LIKE %%");
$_GET["where"][0]["op"] = $_POST["op"];
search_tables();
}
}
@@ -95,8 +96,9 @@ if (adminer()->homepage()) {
$id = h("Table-" . $name);
echo '<tr><td>' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array("$name", $tables_views, true), "", "", "", $id); // "$name" to check numeric table names
echo '<th>' . (support("table") || support("indexes") ? "<a href='" . h(ME) . "table=" . urlencode($name) . "' title='" . lang('Show structure') . "' id='$id'>" . h($name) . '</a>' : h($name));
if ($view) {
echo '<td colspan="6"><a href="' . h(ME) . "view=" . urlencode($name) . '" title="' . lang('Alter view') . '">' . (preg_match('~materialized~i', $type) ? lang('Materialized view') : lang('View')) . '</a>';
if ($view && !preg_match('~materialized~i', $type)) {
$title = lang('View');
echo '<td colspan="6">' . (support("view") ? "<a href='" . h(ME) . "view=" . urlencode($name) . "' title='" . lang('Alter view') . "'>$title</a>" : $title);
echo '<td align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '" title="' . lang('Select data') . '">?</a>';
} else {
foreach (
@@ -131,6 +133,7 @@ if (adminer()->homepage()) {
echo "\n";
echo "</table>\n";
echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');");
echo "</div>\n";
if (!information_schema(DB)) {
echo "<div class='footer'><div>\n";
@@ -147,18 +150,23 @@ if (adminer()->homepage()) {
. "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm()
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n";
$databases = (support("scheme") ? adminer()->schemas() : adminer()->databases());
echo "</div></fieldset>\n";
$script = "";
if (count($databases) != 1 && JUSH != "sqlite") {
echo "<fieldset><legend>" . lang('Move to other database') . " <span id='selected3'></span></legend><div>";
$db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB));
echo "<p><label>" . lang('Move to other database') . ": ";
echo ($databases ? html_select("target", $databases, $db) : '<input name="target" value="' . h($db) . '" autocapitalize="off">');
echo "</label> <input type='submit' name='move' value='" . lang('Move') . "'>";
echo (support("copy") ? " <input type='submit' name='copy' value='" . lang('Copy') . "'> " . checkbox("overwrite", 1, $_POST["overwrite"], lang('overwrite')) : "");
echo "\n";
echo "</div></fieldset>\n";
$script = " selectCount('selected3', formChecked(this, /^(tables|views)\[/));";
}
echo "<input type='hidden' name='all' value=''>"; // used by trCheck()
echo script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^(tables|views)\[/));" . (support("table") ? " selectCount('selected2', formChecked(this, /^tables\[/) || $tables);" : "") . " }");
echo script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^(tables|views)\[/));"
. (support("table") ? " selectCount('selected2', formChecked(this, /^tables\[/) || $tables);" : "")
. "$script }"
);
echo input_token();
echo "</div></fieldset>\n";
echo "</div></div>\n";
}
echo "</form>\n";
@@ -239,9 +247,5 @@ if (adminer()->homepage()) {
}
echo '<p class="links"><a href="' . h(ME) . 'event=">' . lang('Create event') . "</a>\n";
}
if ($tables_list) {
echo script("ajaxSetHtml('" . js_escape(ME) . "script=db');");
}
}
}

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 = $this->conn->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
$return = array();
list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row();
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
$return["partition_names"] = array_keys($partitions);
$return["partition_values"] = array_values($partitions);
return $return;
}
function hasCStyleEscapes(): bool {
static $c_style;
if ($c_style === null) {
@@ -352,6 +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) {
@@ -855,7 +896,7 @@ if (!defined('Adminer\DRIVER')) {
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
$enum = driver()->enumLength;
$type_pattern = "((" . implode("|", array_merge(array_keys(driver()->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum)++)\\))?"
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?(?:\\s*COLLATE\\s*['\"]?([^'\"\\s,]+)['\"]?)?"; //! store COLLATE
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : driver()->inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
$create = get_val("SHOW CREATE $type " . idf_escape($name), 2);
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match);
@@ -887,7 +928,7 @@ if (!defined('Adminer\DRIVER')) {
* @return list<string[]> ["SPECIFIC_NAME" => , "ROUTINE_NAME" => , "ROUTINE_TYPE" => , "DTD_IDENTIFIER" => ]
*/
function routines(): array {
return get_rows("SELECT ROUTINE_NAME AS SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");
return get_rows("SELECT SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");
}
/** Get list of available routine languages
@@ -1011,18 +1052,26 @@ if (!defined('Adminer\DRIVER')) {
}
/** Check whether a feature is supported
* @param literal-string $feature "check|comment|copy|database|descidx|drop_col|dump|event|indexes|kill|materializedview|partitioning|privileges|procedure|processlist|routine|scheme|sequence|status|table|trigger|type|variables|view|view_trigger"
* @param literal-string $feature check|comment|columns|copy|database|descidx|drop_col|dump|event|indexes|kill|materializedview
* |move_col|privileges|procedure|processlist|routine|scheme|sequence|sql|status|table|trigger|type|variables|view|view_trigger
*/
function support(string $feature): bool {
return !preg_match("~scheme|sequence|type|view_trigger|materializedview" . (min_version(8) ? "" : "|descidx" . (min_version(5.1) ? "" : "|event|partitioning")) . (min_version('8.0.16', '10.2.1') ? "" : "|check") . "~", $feature);
return preg_match(
'~^(comment|columns|copy|database|drop_col|dump|indexes|kill|privileges|move_col|procedure|processlist|routine|sql|status|table|trigger|variables|view'
. (min_version(5.1) ? '|event' : '')
. (min_version(8) ? '|descidx' : '')
. (min_version('8.0.16', '10.2.1') ? '|check' : '')
. ')$~',
$feature
);
}
/** Kill a process
* @param numeric-string $val
* @param numeric-string $id
* @return Result|bool
*/
function kill_process(string $val) {
return queries("KILL " . number($val));
function kill_process(string $id) {
return queries("KILL " . number($id));
}
/** Return query to get connection ID */

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";
@@ -59,6 +60,10 @@ if (isset($_GET["oracle"])) {
}
return $return;
}
function timeout(int $ms): bool {
return oci_set_call_timeout($this->link, $ms);
}
}
class Result {
@@ -390,7 +395,6 @@ ORDER BY ac.constraint_type, aic.column_position", $connection2) as $row
$queries = array();
foreach ($alter as $val) {
if ($val[0] != "INDEX") {
//! descending UNIQUE indexes results in syntax error
$val[2] = preg_replace('~ DESC$~', '', $val[2]);
$create = ($val[2] == "DROP"
? "\nDROP CONSTRAINT " . idf_escape($val[1])

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", "SQL"); //! SQL - same-site CSRF
public $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
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 = (min_version(10) ? connection()->query("SELECT * FROM pg_partitioned_table WHERE partrelid = " . $this->tableOid($table))->fetch_assoc() : null);
if ($row) {
$attrs = get_vals("SELECT attname FROM pg_attribute WHERE attrelid = $row[partrelid] AND attnum IN (" . str_replace(" ", ", ", $row["partattrs"]) . ")"); //! ordering
$by = array('h' => 'HASH', 'l' => 'LIST', 'r' => 'RANGE');
return array(
"partition_by" => $by[$row["partstrat"]],
"partition" => implode(", ", array_map('Adminer\idf_escape', $attrs)),
);
}
return array();
}
function tableOid(string $table): string {
return "(SELECT oid FROM pg_class WHERE relnamespace = $this->nsOid AND relname = " . q($table) . " AND relkind IN ('r', 'm', 'v', 'f', 'p'))";
}
function indexAlgorithms(array $tableStatus): array {
static $return = array();
if (!$return) {
$return = get_vals("SELECT amname FROM pg_am" . (min_version(9.6) ? " WHERE amtype = 'i'" : "") . " ORDER BY amname = 'btree' DESC, amname");
}
return $return;
}
function supportsIndex(array $table_status): bool {
// returns true for "materialized view"
return $table_status["Engine"] != "view";
@@ -404,18 +446,19 @@ ORDER BY 1";
$return = array();
foreach (
get_rows("SELECT
c.relname AS \"Name\",
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'm' THEN 'materialized view' ELSE 'view' END AS \"Engine\"" . ($has_size ? ",
relname AS \"Name\",
CASE relkind WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' ELSE 'table' END AS \"Engine\"" . ($has_size ? ",
pg_table_size(c.oid) AS \"Data_length\",
pg_indexes_size(c.oid) AS \"Index_length\"" : "") . ",
obj_description(c.oid, 'pg_class') AS \"Comment\",
" . (min_version(12) ? "''" : "CASE WHEN c.relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
c.reltuples as \"Rows\",
n.nspname
" . (min_version(12) ? "''" : "CASE WHEN relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
reltuples AS \"Rows\",
" . (min_version(10) ? "relispartition::int AS partition," : "") . "
current_schema() AS nspname
FROM pg_class c
JOIN pg_namespace n ON(n.nspname = current_schema() AND n.oid = c.relnamespace)
WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Index_length, Auto_increment
AND relnamespace = " . driver()->nsOid . "
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Auto_increment
) {
$return[$row["Name"]] = $row;
}
@@ -442,15 +485,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,25 +526,28 @@ ORDER BY a.attnum") as $row
function indexes($table, $connection2 = null) {
$connection2 = connection($connection2);
$return = array();
$table_oid = get_val("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($table), 0, $connection2);
$table_oid = driver()->tableOid($table);
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
foreach (
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial
FROM pg_index i, pg_class ci
WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, amname, pg_get_expr(indpred, indrelid, true) AS partial, pg_get_expr(indexprs, indrelid) AS indexpr
FROM pg_index
JOIN pg_class ON indexrelid = oid
JOIN pg_am ON pg_am.oid = pg_class.relam
WHERE indrelid = $table_oid
ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
) {
$relname = $row["relname"];
$return[$relname]["type"] = ($row["indispartial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX")));
$return[$relname]["type"] = ($row["partial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX")));
$return[$relname]["columns"] = array();
$return[$relname]["descs"] = array();
if ($row["indkey"]) {
foreach (explode(" ", $row["indkey"]) as $indkey) {
$return[$relname]["columns"][] = $columns[$indkey];
}
foreach (explode(" ", $row["indoption"]) as $indoption) {
$return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
}
$return[$relname]["algorithm"] = $row["amname"];
$return[$relname]["partial"] = $row["partial"];
$indexpr = preg_split('~(?<=\)), (?=\()~', $row["indexpr"]); //! '), (' used in expression
foreach (explode(" ", $row["indkey"]) as $indkey) {
$return[$relname]["columns"][] = ($indkey ? $columns[$indkey] : array_shift($indexpr));
}
foreach (explode(" ", $row["indoption"]) as $indoption) {
$return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
}
$return[$relname]["lengths"] = array();
}
@@ -516,7 +559,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 +570,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 +579,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 +662,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 +713,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 +721,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 +868,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 +969,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 +1033,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
*/
@@ -81,6 +85,10 @@ class Adminer {
return 2;
}
/** Called after connecting and selecting a database */
function afterConnect(): void {
}
/** Headers to send before HTML output */
function headers(): void {
}
@@ -104,15 +112,24 @@ class Adminer {
return true;
}
/** Print extra classes in <body class>; must start with a space */
function bodyClass(): void {
echo " adminer";
}
/** Get URLs of the CSS files
* @return list<string>
* @return string[] key is URL, value is either 'light' (supports only light color scheme), 'dark' or '' (both)
*/
function css(): array {
$return = array();
foreach (array("", "-dark") as $mode) {
$filename = "adminer$mode.css";
if (file_exists($filename)) {
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
$file = file_get_contents($filename);
$return["$filename?v=" . crc32($file)] = ($mode
? "dark"
: (preg_match('~prefers-color-scheme:\s*dark~', $file) ? '' : 'light')
);
}
}
return $return;
@@ -175,6 +192,7 @@ class Adminer {
* @param ?string $set new item options, NULL for no new item
*/
function selectLinks(array $tableStatus, ?string $set = ""): void {
$name = $tableStatus["Name"];
echo '<p class="links">';
$links = array("select" => lang('Select data'));
if (support("table") || support("indexes")) {
@@ -183,16 +201,15 @@ class Adminer {
$is_view = false;
if (support("table")) {
$is_view = is_view($tableStatus);
if ($is_view) {
$links["view"] = lang('Alter view');
} else {
if (!$is_view) {
$links["create"] = lang('Alter table');
} elseif (support("view")) {
$links["view"] = lang('Alter view');
}
}
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 +356,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 +374,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 +421,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 +559,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;
@@ -726,7 +754,7 @@ class Adminer {
} elseif (preg_match('~^([+-]|\|\|)$~', $function)) {
$return = idf_escape($name) . " $function $return";
} elseif (preg_match('~^[+-] interval$~', $function)) {
$return = idf_escape($name) . " $function " . (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) ? $value : $return);
$return = idf_escape($name) . " $function " . (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) && JUSH != "pgsql" ? $value : $return);
} elseif (preg_match('~^(addtime|subtime|concat)$~', $function)) {
$return = "$function(" . idf_escape($name) . ", $return)";
} elseif (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) {
@@ -938,6 +966,12 @@ class Adminer {
echo (support("scheme") ? "<a href='" . h(ME) . "scheme='>" . ($_GET["ns"] != "" ? lang('Alter schema') : lang('Create schema')) . "</a>\n" : "");
echo ($_GET["ns"] !== "" ? '<a href="' . h(ME) . 'schema=">' . lang('Database schema') . "</a>\n" : "");
echo (support("privileges") ? "<a href='" . h(ME) . "privileges='>" . lang('Privileges') . "</a>\n" : "");
if ($_GET["ns"] !== "") {
echo (support("routine") ? "<a href='#routines'>" . lang('Routines') . "</a>\n" : "");
echo (support("sequence") ? "<a href='#sequences'>" . lang('Sequences') . "</a>\n" : "");
echo (support("type") ? "<a href='#user-types'>" . lang('User types') . "</a>\n" : "");
echo (support("event") ? "<a href='#events'>" . lang('Events') . "</a>\n" : "");
}
return true;
}
@@ -960,7 +994,7 @@ class Adminer {
if ($password !== null) {
$dbs = $_SESSION["db"][$vendor][$server][$username];
foreach (($dbs ? array_keys($dbs) : array("")) as $db) {
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($name) " . h($username . ($server != "" ? "@" . adminer()->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($name) " . h("$username@" . ($server != "" ? adminer()->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
}
}
}
@@ -1018,17 +1052,27 @@ class Adminer {
foreach ($tables as $table => $type) {
$links[] = preg_quote($table, '/');
}
echo "var jushLinks = { " . JUSH . ": [ '" . js_escape(ME) . (support("table") ? "table=" : "select=") . "\$&', /\\b(" . implode("|", $links) . ")\\b/g ] };\n";
echo "var jushLinks = { " . JUSH . ":";
json_row(js_escape(ME) . (support("table") ? "table" : "select") . '=$&', '/\b(' . implode('|', $links) . ')\b/g', false);
if (support('routine')) {
foreach (routines() as $row) {
json_row(js_escape(ME) . 'function=' . urlencode($row["SPECIFIC_NAME"]) . '&name=$&', '/\b' . preg_quote($row["ROUTINE_NAME"], '/') . '(?=["`]?\()/g', false);
}
}
json_row('');
echo "};\n";
foreach (array("bac", "bra", "sqlite_quo", "mssql_bra") as $val) {
echo "jushLinks.$val = jushLinks." . JUSH . ";\n";
}
$tablesColumns = array_fill_keys(array_keys($tables), array());
foreach (driver()->allFields() as $table => $fields) {
foreach ($fields as $field) {
$tablesColumns[$table][] = $field["field"];
if (isset($_GET["sql"]) || isset($_GET["trigger"]) || isset($_GET["check"])) {
$tablesColumns = array_fill_keys(array_keys($tables), array());
foreach (driver()->allFields() as $table => $fields) {
foreach ($fields as $field) {
$tablesColumns[$table][] = $field["field"];
}
}
echo "addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('" . idf_escape("") . "', " . json_encode($tablesColumns) . "); });\n";
}
echo "addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('" . idf_escape("") . "', " . json_encode($tablesColumns) . "); });\n";
}
echo "</script>\n";
}
@@ -1072,8 +1116,9 @@ class Adminer {
function tablesPrint(array $tables): void {
echo "<ul id='tables'>" . script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $table => $status) {
$table = "$table"; // do not highlight "0" as active everywhere
$name = adminer()->tableName($status);
if ($name != "") {
if ($name != "" && !$status["partition"]) {
echo '<li><a href="' . h(ME) . 'select=' . urlencode($table) . '"'
. bold($_GET["select"] == $table || $_GET["edit"] == $table, "select")
. " title='" . lang('Select data') . "'>" . lang('select') . "</a> "
@@ -1088,4 +1133,19 @@ class Adminer {
}
echo "</ul>\n";
}
/** Get process list
* @return list<string[]> [$row]
*/
function processList(): array {
return process_list();
}
/** Kill a process
* @param numeric-string $id
* @return Result|bool
*/
function killProcess(string $id) {
return kill_process($id);
}
}

View File

@@ -92,7 +92,7 @@ include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
define('Adminer\JUSH', Driver::$jush);
define('Adminer\SERVER', $_GET[DRIVER]); // read from pgsql=localhost, '' means default server, null means no server
define('Adminer\DB', $_GET["db"]); // for the sake of speed and size
define('Adminer\DB', "$_GET[db]"); // for the sake of speed and size
define(
'Adminer\ME',
preg_replace('~\?.*~', '', relative_uri()) . '?'
@@ -108,3 +108,5 @@ include "../adminer/include/xxtea.inc.php";
include "../adminer/include/auth.inc.php";
include "./include/editing.inc.php";
include "./include/connect.inc.php";
adminer()->afterConnect();

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
@@ -188,19 +189,11 @@ abstract class SqlDriver {
return $idf;
}
/** Convert operator so it can be used in search */
function convertOperator(string $operator): string {
return $operator;
}
/** Convert value returned by database to actual value
* @param Field $field
*/
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 +213,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 +253,14 @@ abstract class SqlDriver {
return !is_view($table_status);
}
/** Return list of supported index algorithms, first one is default
* @param TableStatus $tableStatus
* @return list<string>
*/
function indexAlgorithms(array $tableStatus): array {
return array();
}
/** Get defined check constraints
* @return string[] [$name => $clause]
*/
@@ -257,12 +279,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

@@ -150,13 +150,13 @@ function select_input(string $attrs, array $options, ?string $value = "", string
* @param string $key or "" to close the object
* @param string|int $val
*/
function json_row(string $key, $val = null): void {
function json_row(string $key, $val = null, bool $escape = true): void {
static $first = true;
if ($first) {
echo "{";
}
if ($key != "") {
echo ($first ? "" : ",") . "\n\t\"" . addcslashes($key, "\r\n\t\"\\/") . '": ' . ($val !== null ? '"' . addcslashes($val, "\r\n\"\\/") . '"' : 'null');
echo ($first ? "" : ",") . "\n\t\"" . addcslashes($key, "\r\n\t\"\\/") . '": ' . ($val !== null ? ($escape ? '"' . addcslashes($val, "\r\n\"\\/") . '"' : $val) : 'null');
$first = false;
} else {
echo "\n}\n";
@@ -201,20 +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

@@ -264,9 +264,10 @@ function where(array $where, array $fields = array()): string {
$field_type = $field["type"];
$return[] = $column
. (JUSH == "sql" && $field_type == "json" ? " = CAST(" . q($val) . " AS JSON)"
: (JUSH == "pgsql" && preg_match('~^json~', $field_type) ? "::jsonb = " . q($val) . "::jsonb"
: (JUSH == "sql" && is_numeric($val) && preg_match('~\.~', $val) ? " LIKE " . q($val) // LIKE because of floats but slow with ints
: (JUSH == "mssql" && strpos($field_type, "datetime") === false ? " LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val)) // LIKE because of text but it does not work with datetime
: " = " . unconvert_field($field, q($val)))))
: " = " . unconvert_field($field, q($val))))))
; //! enum and set
if (JUSH == "sql" && preg_match('~char|text~', $field_type) && preg_match("~[^ -@]~", $val)) { // not just [a-z] to catch non-ASCII characters
$return[] = "$column = " . q($val) . " COLLATE " . charset(connection()) . "_bin";
@@ -291,7 +292,7 @@ function where_check(string $val, array $fields = array()): string {
* @param int $i condition order
* @param string $column column identifier
*/
function where_link(int $i, string $column, string $value, string $operator = "="): string {
function where_link(int $i, string $column, ?string $value, string $operator = "="): string {
return "&where%5B$i%5D%5Bcol%5D=" . urlencode($column) . "&where%5B$i%5D%5Bop%5D=" . urlencode(($value !== null ? $operator : "IS NULL")) . "&where%5B$i%5D%5Bval%5D=" . urlencode($value);
}
@@ -337,11 +338,11 @@ function get_settings(string $cookie): array {
}
/** Get setting stored in a cookie
* @param mixed $default
* @return mixed
*/
function get_setting(string $key, string $cookie = "adminer_settings") {
$settings = get_settings($cookie);
return $settings[$key];
function get_setting(string $key, string $cookie = "adminer_settings", $default = null) {
return idx(get_settings($cookie), $key, $default);
}
/** Store settings to a cookie
@@ -595,10 +596,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 != "") {
@@ -685,7 +686,7 @@ function file_open_lock(string $filename) {
if (!$fp) {
return;
}
chmod($filename, 0660);
@chmod($filename, 0660); // @ - may not be permitted
if (!flock($fp, LOCK_EX)) {
fclose($fp);
return;
@@ -808,7 +809,7 @@ function is_url(?string $string): bool {
* @param Field $field
*/
function is_shortable(array $field): bool {
return preg_match('~char|text|json|lob|geometry|point|linestring|polygon|string|bytea~', $field["type"]);
return preg_match('~char|text|json|lob|geometry|point|linestring|polygon|string|bytea|hstore~', $field["type"]);
}
/** Get query to compute number of found rows

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

@@ -67,7 +67,7 @@ class Plugins {
function __call(string $name, array $params) {
$args = array();
foreach ($params as $key => $val) {
// some plugins accept params by reference - we don't need to propage it outside, just to the other plugins
// some plugins accept params by reference - we don't need to propagate it outside, just to the other plugins
$args[] = &$params[$key];
}
$return = null;

View File

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

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";
}
@@ -11,6 +12,7 @@ if (preg_match('~MyISAM|M?aria' . (min_version(5.7, '10.2.2') ? '|InnoDB' : '')
$index_types[] = "SPATIAL";
}
$indexes = indexes($TABLE);
$fields = fields($TABLE);
$primary = array();
if (JUSH == "mongo") { // doesn't support primary key
$primary = $indexes["_id_"];
@@ -29,13 +31,15 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
$columns = array();
$lengths = array();
$descs = array();
$index_condition = (support("partial_indexes") ? $index["partial"] : "");
$index_algorithm = (in_array($index["algorithm"], $index_algorithms) ? $index["algorithm"] : "");
$set = array();
ksort($index["columns"]);
foreach ($index["columns"] as $key => $column) {
if ($column != "") {
$length = idx($index["lengths"], $key);
$desc = idx($index["descs"], $key);
$set[] = idf_escape($column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : "");
$set[] = ($fields[$column] ? idf_escape($column) : $column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : "");
$columns[] = $column;
$lengths[] = ($length ?: null);
$descs[] = $desc;
@@ -52,6 +56,8 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
&& array_values($existing["columns"]) === $columns
&& (!$existing["lengths"] || array_values($existing["lengths"]) === $lengths)
&& array_values($existing["descs"]) === $descs
&& $existing["partial"] == $index_condition
&& (!$index_algorithms || $existing["algorithm"] == $index_algorithm)
) {
// skip existing index
unset($indexes[$name]);
@@ -59,7 +65,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
}
}
if ($columns) {
$alter[] = array($index["type"], $name, $set);
$alter[] = array($index["type"], $name, $set, $index_algorithm, $index_condition);
}
}
}
@@ -76,7 +82,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
page_header(lang('Indexes'), $error, array("table" => $TABLE), h($TABLE));
$fields = array_keys(fields($TABLE));
$fields_keys = array_keys($fields);
if ($_POST["add"]) {
foreach ($row["indexes"] as $key => $index) {
if ($index["columns"][count($index["columns"])] != "") {
@@ -105,20 +111,35 @@ $show_options = ($_POST ? $_POST["options"] : get_setting("index_options"));
<table class="nowrap">
<thead><tr>
<th id="label-type"><?php echo lang('Index Type'); ?>
<?php
$idxopts = " class='idxopts" . ($show_options ? "" : " hidden") . "'";
if ($index_algorithms) {
echo "<th id='label-algorithm'$idxopts>" . lang('Algorithm') . doc_link(array(
'sql' => 'create-index.html#create-index-storage-engine-index-types',
'mariadb' => 'storage-engine-index-types/',
'pgsql' => 'indexes-types.html',
));
}
?>
<th><input type="submit" class="wayoff"><?php
echo lang('Columns') . ($lengths ? "<span class='idxopts" . ($show_options ? "" : " hidden") . "'> (" . lang('length') . ")</span>" : "");
echo lang('Columns') . ($lengths ? "<span$idxopts> (" . lang('length') . ")</span>" : "");
if ($lengths || support("descidx")) {
echo checkbox("options", 1, $show_options, lang('Options'), "indexOptionsShow(this.checked)", "jsonly") . "\n";
}
?>
<th id="label-name"><?php echo lang('Name'); ?>
<?php
if (support("partial_indexes")) {
echo "<th id='label-condition'$idxopts>" . lang('Condition');
}
?>
<th><noscript><?php echo icon("plus", "add[0]", "+", lang('Add next')); ?></noscript>
</thead>
<?php
if ($primary) {
echo "<tr><td>PRIMARY<td>";
foreach ($primary["columns"] as $key => $column) {
echo select_input(" disabled", $fields, $column);
echo select_input(" disabled", $fields_keys, $column);
echo "<label><input disabled type='checkbox'>" . lang('descending') . "</label> ";
}
echo "<td><td>\n";
@@ -128,17 +149,21 @@ foreach ($row["indexes"] as $index) {
if (!$_POST["drop_col"] || $j != key($_POST["drop_col"])) {
echo "<tr><td>" . html_select("indexes[$j][type]", array(-1 => "") + $index_types, $index["type"], ($j == count($row["indexes"]) ? "indexesAddRow.call(this);" : ""), "label-type");
if ($index_algorithms) {
echo "<td$idxopts>" . html_select("indexes[$j][algorithm]", array_merge(array(""), $index_algorithms), $index['algorithm'], "label-algorithm");
}
echo "<td>";
ksort($index["columns"]);
$i = 1;
foreach ($index["columns"] as $key => $column) {
echo "<span>" . select_input(
" name='indexes[$j][columns][$i]' title='" . lang('Column') . "'",
($fields ? array_combine($fields, $fields) : $fields),
($fields && ($column == "" || $fields[$column]) ? array_combine($fields_keys, $fields_keys) : array()),
$column,
"partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')"
);
echo "<span class='idxopts" . ($show_options ? "" : " hidden") . "'>";
echo "<span$idxopts>";
echo ($lengths ? "<input type='number' name='indexes[$j][lengths][$i]' class='size' value='" . h(idx($index["lengths"], $key)) . "' title='" . lang('Length') . "'>" : "");
echo (support("descidx") ? checkbox("indexes[$j][descs][$i]", 1, idx($index["descs"], $key), lang('descending')) : "");
echo "</span> </span>";
@@ -146,6 +171,9 @@ foreach ($row["indexes"] as $index) {
}
echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n";
if (support("partial_indexes")) {
echo "<td$idxopts><input name='indexes[$j][partial]' value='" . h($index["partial"]) . "' autocapitalize='off' aria-labelledby='label-condition'>\n";
}
echo "<td>" . icon("cross", "drop_col[$j]", "x", lang('Remove')) . script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");
}
$j++;

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>.',
@@ -30,7 +27,7 @@ Lang::$translations = array(
'Connecting to privileged ports is not allowed.' => 'Připojování k privilegovaným portům není povoleno.',
'Disable %s or enable %s or %s extensions.' => 'Zakažte %s nebo povolte rozšíření %s nebo %s.',
'Session support must be enabled.' => 'Session proměnné musí být povolené.',
'Session expired, please login again.' => 'Session vypršela, přihlašte se prosím znovu.',
'Session expired, please login again.' => 'Session vypršela, přihlaste se prosím znovu.',
'The action will be performed after successful login with the same credentials.' => 'Akce bude provedena po úspěšném přihlášení se stejnými přihlašovacími údaji.',
'%s version: %s through PHP extension %s' => 'Verze %s: %s přes PHP rozšíření %s',
'Refresh' => 'Obnovit',
@@ -79,6 +76,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Soubor %s na webovém serveru',
'Run file' => 'Spustit soubor',
'File does not exist.' => 'Soubor neexistuje.',
'Increase %s.' => 'Zvyšte %s.',
'File uploads are disabled.' => 'Nahrávání souborů není povoleno.',
'Unable to upload a file.' => 'Nepodařilo se nahrát soubor.',
'Maximum allowed file size is %sB.' => 'Maximální povolená velikost souboru je %sB.',
@@ -197,6 +195,8 @@ Lang::$translations = array(
'Partitions' => 'Oddíly',
'Partition name' => 'Název oddílu',
'Values' => 'Hodnoty',
'Inherits from' => 'Zděděná z',
'Inherited by' => 'Zděděné',
'View' => 'Pohled',
'Materialized view' => 'Materializovaný pohled',
@@ -212,6 +212,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 +354,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

@@ -5,13 +5,13 @@ Lang::$translations = array(
'Login' => 'ログイン',
'Logout successful.' => 'ログアウトしました。',
'Invalid credentials.' => '不正なログインです。',
'Server' => 'サーバ',
'Username' => 'ユーザ名',
'Server' => 'サーバ',
'Username' => 'ユーザ名',
'Password' => 'パスワード',
'Loaded plugins' => '読込済プラグイン',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminerのご利用ありがとうございました。(寄付は<a href="https://www.adminer.org/en/donation/">こちら</a>)',
'%s must <a%s>return an array</a>.' => '%s は<a%s>配列を返す</a>必要があります。',
'<a%s>Configure</a> %s in %s.' => '%2$s の %1$s <a%s>設定してください</a>。',
'<a%s>Configure</a> %s in %s.' => '%2$s の %1$s <a%s>設定</a>してください。',
'There is a space in the input password which might be the cause.' => '入力されたパスワードに空白が含まれているので、それが原因かもしれません。',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer はパスワードのないデータベースへの接続には対応していません。(<a href="https://www.adminer.org/en/password/"%s>詳細</a>)',
'Database does not support password.' => 'データベースがパスワードに対応していません。',
@@ -20,14 +20,14 @@ Lang::$translations = array(
'Select database' => 'データベースを選択してください',
'Invalid database.' => '不正なデータベースです。',
'Table has been dropped.' => 'テーブルを削除しました。',
'Table has been altered.' => 'テーブルを変更しました。',
'Table has been altered.' => 'テーブルの設定を変更しました。',
'Table has been created.' => 'テーブルを作成しました。',
'Alter table' => 'テーブルを変更',
'Alter table' => 'テーブルの設定を変更',
'Create table' => 'テーブルを作成',
'Table name' => 'テーブル名',
'engine' => 'エンジン',
'collation' => '照合順序',
'Column name' => '名',
'collation' => 'コレーション',
'Column name' => 'カラム名',
'Type' => '型',
'Length' => '長さ',
'Auto Increment' => '連番',
@@ -38,16 +38,16 @@ Lang::$translations = array(
'Database has been dropped.' => 'データベースを削除しました。',
'Database has been created.' => 'データベースを作成しました。',
'Database has been renamed.' => 'データベースの名前を変えました。',
'Database has been altered.' => 'データベースを変更しました。',
'Alter database' => 'データベースを変更',
'Database has been altered.' => 'データベースの設定を変更しました。',
'Alter database' => 'データベースの設定を変更',
'Create database' => 'データベースを作成',
'SQL command' => 'SQLコマンド',
'Logout' => 'ログアウト',
'Use' => '使用',
'No tables.' => 'テーブルがありません。',
'select' => '選択',
'Item has been deleted.' => '項目を削除しました。',
'Item has been updated.' => '項目を更新しました。',
'Item has been deleted.' => 'レコードを削除しました。',
'Item has been updated.' => 'レコードを更新しました。',
'Edit' => '編集',
'Insert' => '挿入',
'Save and insert next' => '保存/追加',
@@ -56,18 +56,18 @@ Lang::$translations = array(
'Database' => 'データベース',
'DB' => 'DB',
'Routines' => 'ルーチン',
'Indexes have been altered.' => '索引を変更しました。',
'Indexes' => '索引',
'Alter indexes' => '索引の変更',
'Indexes have been altered.' => 'インデックスを変更しました。',
'Indexes' => 'インデックス',
'Alter indexes' => 'インデックスを変更',
'Add next' => '追加',
'Language' => '言語',
'Select' => '選択',
'New item' => '項目の作成',
'New item' => '新規レコードを挿入',
'Search' => '検索',
'Sort' => 'ソート',
'descending' => '降順',
'Limit' => '制約',
'Limit rows' => '行数の制約',
'Limit rows' => '表示行数を制限',
'No rows.' => '行がありません。',
'Action' => '動作',
'edit' => '編集',
@@ -82,17 +82,17 @@ Lang::$translations = array(
'Foreign keys' => '外部キー',
'Triggers' => 'トリガー',
'View' => 'ビュー',
'Materialized view' => 'マテビュー',
'Full table scan' => 'テーブル全スキャン',
'Materialized view' => 'マテリアライズドビュー',
'Full table scan' => 'テーブル全スキャン',
'Unable to select the table' => 'テーブルを選択できません',
'Invalid CSRF token. Send the form again.' => '不正なCSRFトークン再送信してください。',
'If you did not send this request from Adminer then close this page.' => 'Adminerからのリクエストを送信しない場合はこのページを閉じてください。',
'Invalid CSRF token. Send the form again.' => '不正なCSRFトークンです。フォームを再送信してください。',
'If you did not send this request from Adminer then close this page.' => 'Adminerからのリクエストない場合はこのページを閉じてください。',
'Comment' => 'コメント',
'Default values' => '定値',
'Default values' => '定値',
'%d byte(s)' => '%d バイト',
'No commands to execute.' => '実行するコマンドがありません。',
'Unable to upload a file.' => 'ファイルをアップロードできません。',
'File upload' => 'ファイルをアップロード',
'File upload' => 'アップロード',
'File uploads are disabled.' => 'ファイルのアップロードが無効です。',
'Routine has been called, %d row(s) affected.' => 'ルーチンを呼びました。%d 行を変更しました。',
'Call' => '呼出し',
@@ -103,21 +103,21 @@ Lang::$translations = array(
'Session support must be enabled.' => 'セッションを有効にしてください。',
'Session expired, please login again.' => 'セッションの期限切れ。ログインし直してください。',
'The action will be performed after successful login with the same credentials.' => '同じアカウントで正しくログインすると作業を実行します。',
'Text length' => '文字列の長さ',
'Text length' => '文字数を丸める',
'Foreign key has been dropped.' => '外部キーを削除しました。',
'Foreign key has been altered.' => '外部キーを変更しました。',
'Foreign key has been created.' => '外部キーを作成しました。',
'Foreign key' => '外キー',
'Target table' => 'テーブル',
'Foreign key' => '外キー',
'Target table' => '対象テーブル',
'Change' => '変更',
'Source' => 'ソース',
'Target' => 'ターゲット',
'Add column' => 'を追加',
'Add column' => 'カラムを追加',
'Alter' => '変更',
'Add foreign key' => '外部キーを追加',
'ON DELETE' => 'ON DELETE',
'ON UPDATE' => 'ON UPDATE',
'Index Type' => '索引の型',
'Index Type' => 'インデックスの型',
'length' => '長さ',
'View has been dropped.' => 'ビューを削除しました。',
'View has been altered.' => 'ビューを変更しました。',
@@ -126,25 +126,25 @@ Lang::$translations = array(
'Create view' => 'ビューを作成',
'Name' => '名称',
'Process list' => 'プロセス一覧',
'%d process(es) have been killed.' => '%d プロセスを強制終了しました。',
'Kill' => '強制終了',
'Parameter name' => '参数名',
'Database schema' => '構造',
'Create procedure' => 'プロシージャ作成',
'Create function' => '関数作成',
'Routine has been dropped.' => 'ルーチンを作成しました。',
'%d process(es) have been killed.' => '%d プロセスを終了しました。',
'Kill' => 'プロセスを終了',
'Parameter name' => 'パラメータ名',
'Database schema' => 'スキーマ',
'Create procedure' => 'プロシージャ作成',
'Create function' => '関数作成',
'Routine has been dropped.' => 'ルーチンを削除しました。',
'Routine has been altered.' => 'ルーチンを変更しました。',
'Routine has been created.' => 'ルーチンを作成しました。',
'Alter function' => '関数変更',
'Alter procedure' => 'プロシージャ変更',
'Alter function' => '関数変更',
'Alter procedure' => 'プロシージャ変更',
'Return type' => '戻り値の型',
'Add trigger' => 'トリガー追加',
'Add trigger' => 'トリガー追加',
'Trigger has been dropped.' => 'トリガーを削除しました。',
'Trigger has been altered.' => 'トリガーを変更しました。',
'Trigger has been created.' => 'トリガーを追加しました。',
'Alter trigger' => 'トリガー変更',
'Create trigger' => 'トリガー作成',
'Time' => '時間',
'Alter trigger' => 'トリガー変更',
'Create trigger' => 'トリガー作成',
'Time' => 'タイミング',
'Event' => 'イベント',
'%s version: %s through PHP extension %s' => '%sバージョン%s、 PHP拡張機能 %s',
'%d / ' => '%d / ',
@@ -157,10 +157,11 @@ Lang::$translations = array(
'User has been altered.' => 'ユーザを変更しました。',
'User has been created.' => 'ユーザを作成しました。',
'Hashed' => 'Hashed',
'Column' => '',
'Column' => 'カラム',
'Columns' => 'カラム',
'Routine' => 'ルーチン',
'Grant' => '権限付与',
'Revoke' => '権限の取消し',
'Grant' => '権限付与',
'Revoke' => '権限を取り消す',
'Logged as: %s' => 'ログ:%s',
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'POSTデータが大きすぎます。データサイズを小さくするか %s 設定を大きくしてください。',
'You can upload a big SQL file via FTP and import it from server.' => '大きなSQLファイルは、FTP経由でアップロードしてサーバからインポートしてください。',
@@ -171,16 +172,16 @@ Lang::$translations = array(
'Tables' => 'テーブル',
'Data' => 'データ',
'Output' => '出力',
'open' => '開く',
'open' => 'ブラウザに表示',
'save' => '保存',
'Format' => '形式',
'Functions' => '関数',
'Aggregation' => '集',
'Aggregation' => '集約関数',
'Event has been dropped.' => 'イベントを削除しました。',
'Event has been altered.' => 'イベントを変更しました。',
'Event has been created.' => 'イベントを作成しました。',
'Alter event' => '変更',
'Create event' => '作成',
'Alter event' => 'イベントを変更',
'Create event' => 'イベントを作成',
'Start' => '開始',
'End' => '終了',
'Every' => '毎回',
@@ -193,24 +194,24 @@ Lang::$translations = array(
'Tables have been moved.' => 'テーブルを移動しました。',
'Tables and views' => 'テーブルとビュー',
'Engine' => 'エンジン',
'Collation' => '照合順序',
'Collation' => 'コレーション',
'Data Length' => 'データ長',
'Index Length' => '索引長',
'Index Length' => 'インデックス長',
'Data Free' => '空き',
'Rows' => '行数',
',' => ',',
'0123456789' => '0123456789',
'Analyze' => '分析',
'Optimize' => '最適化',
'Vacuum' => '不要領域回収',
'Check' => 'チェック',
'Vacuum' => '不要領域回収(Vacuum)',
'Check' => '検査',
'Repair' => '修復',
'Truncate' => '空にする',
'Move to other database' => 'のデータベースへ移動',
'Move to other database' => 'のデータベースへ移動',
'Move' => '移動',
'Save and continue edit' => '保存して継続',
'original' => '元',
'%d item(s) have been affected.' => '%d を更新しました。',
'%d item(s) have been affected.' => '%d レコードを更新しました。',
'Whole result' => '全結果',
'Tables have been dropped.' => 'テーブルを削除しました。',
'Tables have been optimized.' => 'テーブルを最適化しました。',
@@ -222,7 +223,7 @@ Lang::$translations = array(
'Values' => '値',
'%d row(s) have been imported.' => '%d 行をインポートしました。',
'File must be in UTF-8 encoding.' => 'ファイルをUTF-8で保存してください。',
'Show structure' => '構造',
'Show structure' => 'スキーマ',
'anywhere' => '任意',
'Import' => 'インポート',
'Stop on error' => 'エラーの場合は停止',
@@ -232,9 +233,9 @@ Lang::$translations = array(
'[yyyy]-mm-dd' => '[yyyy]/mm/dd',
'History' => '履歴',
'Variables' => '変数',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットのは同じデータ型でなければなりません。ターゲット列に索引があり、データが存在しなければなりません。',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットのカラムは同じデータ型でなければなりません。ターゲットカラムにインデックスがあり、データが存在しなければなりません。',
'Relations' => '関係',
'Run file' => 'ファイルを実行',
'Run file' => '実行',
'Clear' => '消去',
'Maximum allowed file size is %sB.' => '最大ファイルサイズは %sB です。',
'Numbers' => '数字',
@@ -243,7 +244,7 @@ Lang::$translations = array(
'Binary' => 'バイナリ',
'Lists' => 'リスト',
'Editor' => 'エディタ',
'Webserver file %s' => 'Webサーバファイル %s',
'Webserver file %s' => 'ファイル %s',
'File does not exist.' => 'ファイルは存在しません。',
'%d in total' => '合計 %d',
'Permanent login' => '永続的にログイン',
@@ -267,15 +268,15 @@ Lang::$translations = array(
'Type has been dropped.' => 'ユーザー定義型を削除しました。',
'Type has been created.' => 'ユーザー定義型を追加しました。',
'Ctrl+click on a value to modify it.' => 'Ctrl+クリックで値を修正します。',
'Use edit link to modify this value.' => 'この値を修正するリンクを編集します。',
'Use edit link to modify this value.' => 'この値を修正するにはリンクを使用してください。',
'last' => '最終',
'From server' => 'サーバーから実行',
'From server' => 'サーバー上のファイル',
'System' => 'データベース種類',
'empty' => '空',
'Network' => 'ネットワーク型',
'Geometry' => 'ジオメトリ型',
'File exists.' => 'ファイルが既に存在します。',
'Item%s has been inserted.' => '%s項目を挿入しました。',
'Item%s has been inserted.' => '%sレコードを挿入しました。',
'now' => '現在の日時',
'%d query(s) executed OK.' => '%d クエリーを実行しました。',
'Show only errors' => 'エラーのみ表示',
@@ -286,27 +287,30 @@ Lang::$translations = array(
'Tables have been copied.' => 'テーブルをコピーしました。',
'Copy' => 'コピー',
'overwrite' => '上書き',
'Permanent link' => 'パーマネントリンク',
'Edit all' => 'すべて編集',
'Permanent link' => '固定リンク',
'Edit all' => '一括編集',
'Selected' => '選択中',
'Modify' => '編集',
'Load more data' => 'さらにデータを表示',
'Compute' => '再計算',
'Saving' => '保存しています...',
'Checks' => 'CHECK制約',
'Create check' => 'CHECK制約を追加',
'Alter check' => 'CHECK制約を編集',
'Check has been created.' => 'CHECK制約を追加しました。',
'Check has been altered.' => 'CHECK制約を編集しました。',
'Check has been dropped.' => 'CHECK制約を削除しました。',
'screenshot' => 'スクリーンショット',
'Algorithm' => 'アルゴリズム',
'Condition' => '条件',
'Inherits from' => '継承元',
'HH:MM:SS' => '時:分:秒',
'Selected' => '選択済',
'Modify' => '修正',
'Load more data' => '続きを読み込み',
'Loading' => '読み込み中',
'Size' => 'サイズ',
'Compute' => '算出',
'Saving' => '保存中',
'yes' => 'はい',
'no' => 'いいえ',
'Default value' => '既定値',
// Table check constraints
'Checks' => 'チェック',
'Create check' => 'チェックを作成',
'Alter check' => 'チェックを変更',
'Check has been created.' => 'チェックを作成しました。',
'Check has been altered.' => 'チェックを変更しました。',
'Check has been dropped.' => 'チェックを削除しました。',
);
// run `php ../../lang.php ja` to update this file

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>.',
@@ -79,6 +76,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Plik %s na serwerze',
'Run file' => 'Uruchom z pliku',
'File does not exist.' => 'Plik nie istnieje.',
'Increase %s.' => 'Zwiększ %s.',
'File uploads are disabled.' => 'Wgrywanie plików jest wyłączone.',
'Unable to upload a file.' => 'Wgranie pliku było niemożliwe.',
'Maximum allowed file size is %sB.' => 'Maksymalna wielkość pliku to %sB.',
@@ -90,6 +88,7 @@ Lang::$translations = array(
'Output' => 'Rezultat',
'open' => 'otwórz',
'save' => 'zapisz',
'Saving' => 'Zapisywanie',
'Format' => 'Format',
'Data' => 'Dane',
@@ -197,6 +196,8 @@ Lang::$translations = array(
'Partitions' => 'Partycje',
'Partition name' => 'Nazwa partycji',
'Values' => 'Wartości',
'Inherits from' => 'Dziedziczy po',
'Inherited by' => 'Odziedziczone przez',
'View' => 'Perspektywa',
'Materialized view' => 'Zmaterializowana perspektywa',
@@ -212,6 +213,8 @@ Lang::$translations = array(
'Add next' => 'Dodaj następny',
'Index Type' => 'Typ indeksu',
'length' => 'długość',
'Algorithm' => 'Algorytm',
'Condition' => 'Warunek',
'Foreign keys' => 'Klucze obce',
'Foreign key' => 'Klucz obcy',
@@ -288,7 +291,6 @@ Lang::$translations = array(
'Edit' => 'Edytuj',
'Insert' => 'Dodaj',
'Save' => 'Zapisz zmiany',
'Saving' => 'Zapisywanie',
'Save and continue edit' => 'Zapisz i kontynuuj edycję',
'Save and insert next' => 'Zapisz i dodaj następny',
'Selected' => 'Zaznaczone',
@@ -352,6 +354,11 @@ Lang::$translations = array(
'Check has been created.' => 'Kontrola została utworzona.',
'Check has been altered.' => 'Kontrola została zmieniona.',
'Check has been dropped.' => 'Kontrola została usunięta.',
'Loaded plugins' => 'Wczytane wtyczki',
'%s must <a%s>return an array</a>.' => '%s musi <a%s>zwrócić tablicę</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Skonfiguruj</a> %s w %s.',
'screenshot' => 'zrzut ekranu',
);
// run `php ../../lang.php pl` to update this file

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>.',
@@ -80,6 +77,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Xx %s',
'Run file' => 'Xx',
'File does not exist.' => 'Xx.',
'Increase %s.' => 'Xx %s.',
'File uploads are disabled.' => 'Xx.',
'Unable to upload a file.' => 'Xx.',
'Maximum allowed file size is %sB.' => 'Xx %sB.',
@@ -199,6 +197,8 @@ Lang::$translations = array(
'Partitions' => 'Xx',
'Partition name' => 'Xx',
'Values' => 'Xx',
'Inherits from' => 'Xx',
'Inherited by' => 'Xx',
'View' => 'Xx',
'Materialized view' => 'Xx',
@@ -214,6 +214,8 @@ Lang::$translations = array(
'Add next' => 'Xx',
'Index Type' => 'Xx',
'length' => 'xx',
'Algorithm' => 'Xx',
'Condition' => 'Xx',
'Foreign keys' => 'Xx',
'Foreign key' => 'Xx',
@@ -354,6 +356,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,13 +56,13 @@ echo ($collations ? "<datalist id='collations'>" . optionlist($collations) . "</
edit_fields($row["fields"], $collations, $routine);
if (isset($_GET["function"])) {
echo "<tr><td>" . lang('Return type');
edit_type("returns", $row["returns"], $collations, array(), (JUSH == "pgsql" ? array("void", "trigger") : array()));
edit_type("returns", (array) $row["returns"], $collations, array(), (JUSH == "pgsql" ? array("void", "trigger") : array()));
}
?>
</table>
<?php echo script("editFields();"); ?>
</div>
<p><?php textarea("definition", $row["definition"]); ?>
<p><?php textarea("definition", $row["definition"], 20); ?>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($PROCEDURE != "") { ?>

View File

@@ -5,7 +5,7 @@ if (support("kill")) {
if ($_POST && !$error) {
$killed = 0;
foreach ((array) $_POST["kill"] as $val) {
if (kill_process($val)) {
if (adminer()->killProcess($val)) {
$killed++;
}
}
@@ -23,7 +23,7 @@ page_header(lang('Process list'), $error);
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
// HTML valid because there is always at least one process
$i = -1;
foreach (process_list() as $i => $row) {
foreach (adminer()->processList() as $i => $row) {
if (!$i) {
echo "<thead><tr lang='en'>" . (support("kill") ? "<th>" : "");
foreach ($row as $key => $val) {

View File

@@ -7,7 +7,7 @@ if ($_GET["script"] == "db") {
$sums = array("Data_length" => 0, "Index_length" => 0, "Data_free" => 0);
foreach (table_status() as $name => $table_status) {
json_row("Comment-$name", h($table_status["Comment"]));
if (!is_view($table_status)) {
if (!is_view($table_status) || preg_match('~materialized~i', $table_status["Engine"])) {
foreach (array("Engine", "Collation") as $key) {
json_row("$key-$name", h($table_status[$key]));
}

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 ? $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

@@ -4,9 +4,13 @@ namespace Adminer;
if (!$error && $_POST["export"]) {
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers("sql");
adminer()->dumpTable("", "");
adminer()->dumpData("", "table", $_POST["query"]);
adminer()->dumpFooter();
if ($_POST["format"] == "sql") {
echo "$_POST[query]\n";
} else {
adminer()->dumpTable("", "");
adminer()->dumpData("", "table", $_POST["query"]);
adminer()->dumpFooter();
}
exit;
}
@@ -20,6 +24,7 @@ if (!$error && $_POST["clear"]) {
stop_session();
page_header((isset($_GET["import"]) ? lang('Import') : lang('SQL command')), $error);
$line_comment = '--' . (JUSH == 'sql' ? ' ' : '');
if (!$error && $_POST) {
$fp = false;
@@ -51,7 +56,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,11 +69,9 @@ if (!$error && $_POST) {
}
$commands = 0;
$errors = array();
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|--' . (JUSH == 'sql' ? ' ' : '') . '|$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : '');
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|' . $line_comment . '|$' . (JUSH == "pgsql" ? '|\$([a-zA-Z]\w*)?\$' : '');
$total_start = microtime(true);
$adminer_export = get_settings("adminer_import"); // this doesn't offer SQL export so we match the import/export style at select
$dump_format = adminer()->dumpFormat();
unset($dump_format["sql"]);
while ($query != "") {
if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) {
@@ -94,7 +97,7 @@ if (!$error && $_POST) {
$pattern =
($found == '/*' ? '\*/' :
($found == '[' ? ']' :
(preg_match('~^-- |^#~', $found) ? "\n" :
(preg_match("~^$line_comment|^#~", $found) ? "\n" :
preg_quote($found) . ($c_style_escapes ? '|\\\\.' : ''))))
;
@@ -173,7 +176,7 @@ if (!$error && $_POST) {
$id = "export-$commands";
echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: "
. html_select("output", adminer()->dumpOutput(), $adminer_export["output"]) . " "
. html_select("format", $dump_format, $adminer_export["format"])
. html_select("format", adminer()->dumpFormat(), $adminer_export["format"])
. input_hidden("query", $q)
. "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n"
. "</form>\n"
@@ -249,8 +252,11 @@ if (!isset($_GET["import"])) {
} else {
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
$gz = (extension_loaded("zlib") ? "[.gz]" : "");
$max_file_uploads = "max_file_uploads";
$max_file_uploads_value = ini_get($max_file_uploads);
echo (ini_bool("file_uploads")
? "SQL$gz (&lt; " . ini_get("upload_max_filesize") . "B): <input type='file' name='sql_file[]' multiple>\n$execute" // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
? "SQL$gz (&lt; " . ini_get("upload_max_filesize") . "B): <input type='file' name='sql_file[]' multiple>\n" // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
. script("qsl('input').onchange = partialArg(fileChange, $max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "')") . $execute
: lang('File uploads are disabled.')
);
echo "</div></fieldset>\n";
@@ -275,7 +281,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

@@ -665,6 +665,17 @@ function sqlSubmit(form, root) {
}
}
/** Check if PHP can handle the uploaded files
* @param number
* @param string
* @param Event
*/
function fileChange(event, maxFileUploads, message) {
if (event.target.files.length > maxFileUploads) {
alert(message);
}
}
/** Handle changing trigger time or event

View File

@@ -26,11 +26,25 @@ if ($fields) {
adminer()->tableStructurePrint($fields, $table_status);
}
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);
}
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 +109,13 @@ if (support(is_view($table_status) ? "view_trigger" : "trigger")) {
}
echo '<p class="links"><a href="' . h(ME) . 'trigger=' . urlencode($TABLE) . '">' . lang('Add trigger') . "</a>\n";
}
$inherited = driver()->inheritedTables($TABLE);
if ($inherited) {
echo "<h3 id='partitions'>" . lang('Inherited by') . "</h3>\n";
$partition = driver()->partitionsInfo($TABLE);
if ($partition) {
echo "<p><code class='jush-" . JUSH . "'>BY " . h("$partition[partition_by]($partition[partition])") . "</code>\n";
}
tables_links($inherited);
}

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

2
designs/win98/README.md Normal file
View File

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

298
designs/win98/adminer.css Normal file
View File

@@ -0,0 +1,298 @@
/* inspired by https://github.com/jdan/98.css */
:root {
--text-color: #222222;
--button-face: #dfdfdf;
--window-frame: #0a0a0a;
--dialog-blue-light: #1084d0;
--link-blue: #0000ff;
}
body {
background: teal;
color: var(--text-color);
font-family: Arial, Helvetica, sans-serif;
}
button, input[type=reset], input[type=submit] {
background: silver;
border: none;
border-radius: 0;
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px white, inset -2px -2px grey, inset 2px 2px var(--button-face);
box-sizing: border-box;
color: transparent;
font-size: 12px;
min-height: 23px;
min-width: 75px;
padding: 0 12px;
text-shadow: 0 0 var(--text-color);
}
button.default, input[type=reset].default, input[type=submit].default {
box-shadow: inset -2px -2px var(--window-frame), inset 1px 1px var(--window-frame), inset 2px 2px white, inset -3px -3px grey, inset 3px 3px var(--button-face);
}
button:not(:disabled):active, input[type=reset]:not(:disabled):active, input[type=submit]:not(:disabled):active {
box-shadow: inset -1px -1px white, inset 1px 1px var(--window-frame), inset -2px -2px var(--button-face), inset 2px 2px grey;
text-shadow: 1px 1px var(--text-color);
}
button.default:not(:disabled):active, input[type=reset].default:not(:disabled):active, input[type=submit].default:not(:disabled):active {
box-shadow: inset 2px 2px var(--window-frame), inset -1px -1px var(--window-frame), inset -2px -2px white, inset 3px 3px grey, inset -3px -3px var(--button-face);
}
button:focus, input[type=reset]:focus, input[type=submit]:focus {
outline: 1px dotted black;
outline-offset: -4px;
}
button::-moz-focus-inner, input[type=reset]::-moz-focus-inner, input[type=submit]::-moz-focus-inner {
border: 0;
}
:disabled, :disabled + label, input[readonly], input[readonly] + label, input[type=submit]:disabled, input[type=reset]:disabled, input[type=submit]:disabled {
color: grey;
}
:disabled + label, button:disabled, input[type=reset]:disabled, input[type=submit]:disabled {
text-shadow: 1px 1px 0 white;
}
#content, #menu, .footer, #breadcrumb {
background: silver;
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face), inset -2px -2px grey, inset 2px 2px white;
padding: 16px;
}
#content h2 {
align-items: center;
background: linear-gradient(90deg, navy, var(--dialog-blue-light));
color: white;
display: flex;
font-size: 120%;
font-weight: normal;
justify-content: space-between;
letter-spacing: 0;
margin: -13px -13px 16px -13px;
padding: 3px 2px 3px 3px;
}
fieldset {
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='5' height='5' fill='gray' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h5v5H0V2h2v1h1V2H0' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h4v4H0V1h1v2h2V1H0'/%3E%3C/svg%3E") 2;
margin: 0;
padding: 10px;
padding-block-start: 8px;
}
legend {
background: silver;
}
input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], input:not([type]) {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: none;
border-radius: 0;
}
input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], select, input:not([type]) {
background-color: white;
box-shadow: inset -1px -1px white, inset 1px 1px grey, inset -2px -2px var(--button-face), inset 2px 2px var(--window-frame);
box-sizing: border-box;
padding: 3px 4px;
}
select, textarea {
border: none;
}
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: white;
border-radius: 0;
box-shadow: inset -1px -1px white, inset 1px 1px grey, inset -2px -2px var(--button-face), inset 2px 2px var(--window-frame);
box-sizing: border-box;
padding: 3px 4px;
}
input[type=email], input[type=password], input[type=search], input[type=tel], input[type=text], input[type=number], select {
min-height: 21px;
}
input[type=search]::-ms-clear, input[type=search]::-ms-reveal {
display: none;
height: 0;
width: 0;
}
input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration {
display: none;
}
input[type=email]:disabled, input[type=email]:read-only, input[type=number]:disabled, input[type=number]:read-only, input[type=password]:disabled, input[type=password]:read-only, input[type=search]:disabled, input[type=search]:read-only, input[type=tel]:disabled, input[type=tel]:read-only, input[type=text]:disabled, input[type=text]:read-only, textarea:disabled {
background-color: silver;
}
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15 0H0v16h1V1h14V0z' fill='%23DFDFDF'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2 1H1v14h1V2h12V1H2z' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M16 17H0v-1h15V0h1v17z' fill='%23000'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15 1h-1v14H1v1h14V1z' fill='gray'/%3E%3Cpath fill='silver' d='M2 2h12v13H2z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11 6H4v1h1v1h1v1h1v1h1V9h1V8h1V7h1V6z' fill='%23000'/%3E%3C/svg%3E");
background-position: top 2px right 2px;
background-repeat: no-repeat;
border-radius: 0;
padding-right: 32px;
position: relative;
}
input[type=email]:focus, input[type=number]:focus, input[type=password]:focus, input[type=search]:focus, input[type=tel]:focus, input[type=text]:focus, select:focus, textarea:focus {
outline: none;
}
select:focus {
background-color: navy;
color: white;
}
select:focus option {
background-color: white;
color: black;
}
select:active {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h16v17H0V0zm1 16h14V1H1v15z' fill='gray'/%3E%3Cpath fill='silver' d='M1 1h14v15H1z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12 7H5v1h1v1h1v1h1v1h1v-1h1V9h1V8h1V7z' fill='%23000'/%3E%3C/svg%3E")
}
a {
color: var(--link-blue);
}
a:focus {
outline: 1px dotted var(--link-blue);
}
pre {
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='5' height='5' fill='gray' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h5v5H0V2h2v1h1V2H0' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0h4v4H0V1h1v2h2V1H0'/%3E%3C/svg%3E") 2;
border-style: solid;
border-width: 1px;
margin: 0;
padding: 12px 8px;
}
table {
background-color: white;
}
table > thead > tr > * {
background: silver;
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px white, inset -2px -2px grey, inset 2px 2px var(--button-face);
}
table.interactive > tbody > tr {
cursor: pointer;
}
#fieldset-sort > div:not(:last-of-type), #fieldset-search > div:not(:last-of-type), #fieldset-select > div:not(:last-of-type) {
margin-bottom: .5em;
}
#fieldset-partition p {
margin: 0;
}
.icon {
min-height: 0;
min-width: 0;
}
.links {
margin: 1em 0;
}
#logins a, #tables a, #tables span {
background: none;
}
.jush-autocomplete, .jush-autocomplete:active {
background: none;
height: auto;
}
.logout {
box-shadow: none;
margin-top: .8em;
}
#breadcrumb {
height: auto;
margin: 0;
padding: 2px 16px;
top: calc(1.5em - 14px);
}
code {
background: none;
}
#menu {
margin: 0;
top: 3em;
width: 18em;
}
#menu h1 {
background: none;
margin: 0;
padding: 0;
}
#menu h1 a {
color: var(--text-color);
}
#menu .links a {
display: block;
}
#lang {
top: -2.8em;
}
#lang label {
color: white;
}
#content {
margin-top: 3em;
}
.footer {
margin: 1em 0;
}
.footer > div {
background: none;
}
.js .column {
background: silver;
border: none;
border-radius: 0;
box-shadow: inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face), inset -2px -2px grey, inset 2px 2px white;
margin-top: -.4em;
padding: 4px;
}
@media all and (max-width: 800px) {
.js .logout {
box-shadow: none;
top: 0;
}
#lang label {
color: initial;
}
}

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,10 +52,16 @@ class Adminer {
return get_databases($flush);
}
function pluginsLinks(): void {
}
function queryTimeout() {
return 5;
}
function afterConnect() {
}
function headers() {
}
@@ -67,12 +73,20 @@ class Adminer {
return true;
}
function bodyClass(): void {
echo " editor";
}
function css() {
$return = array();
foreach (array("", "-dark") as $mode) {
$filename = "adminer$mode.css";
if (file_exists($filename)) {
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
$file = file_get_contents($filename);
$return["$filename?v=" . crc32($file)] = ($mode
? "dark"
: (preg_match('~prefers-color-scheme:\s*dark~', $file) ? '' : 'light')
);
}
}
return $return;
@@ -253,10 +267,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 +369,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 +504,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, partition?:numeric-string, nspname?:string}"
Field: "array{field?:string, full_type:string, type:string, length:numeric-string, unsigned:string, default?:string, null:bool, auto_increment:bool, collation:string, privileges:int[], comment:string, primary:bool, generated:string, orig?:string, on_update?:string, on_delete?:string, default_constraint?: string}"
FieldType: "array{type:string, length:numeric-string, unsigned:string, collation:string}" # subset of RoutineField and Field
RoutineField: "array{field:string, type:string, length:numeric-string, unsigned:string, null:bool, full_type:string, collation:string, inout?:string}"
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>}"
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>, algorithm?:string, partial?:string}"
ForeignKey: "array{db?:string, ns?:string, table:string, source:list<string>, target:list<?string>, on_delete:string, on_update?:string, definition?:string, deferrable?:string}"
Trigger: "array{Trigger?:string, Timing?:string, Event?:string, Of?:string, Type?:string, Statement?:string}"
Routine: "array{name?:string, fields:list<RoutineField>, comment:string, returns?:FieldType, definition:string, language?:string}"
Partitions: "array{partition_by?:string, partition?:string, partitions?:numeric-string, partition_names?:list<string>, partition_values?:list<string>}"
BackwardKey: "array{name:string, keys:string[][]}"

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,26 +1,36 @@
<?php
/** Display links to tables referencing current row, same as in Adminer Editor
* @link https://www.adminer.org/static/plugins/backward-keys.png
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerBackwardKeys {
class AdminerBackwardKeys extends Adminer\Plugin {
// this is copy-pasted from Adminer Editor
function backwardKeys($table, $tableName) {
$return = array();
// we couldn't use the same query in MySQL and PostgreSQL because unique_constraint_name is not table-specific in MySQL and referenced_table_name is not available in PostgreSQL
foreach (
Adminer\get_rows($q = "SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . "
AND REFERENCED_TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . "
AND REFERENCED_TABLE_NAME = " . Adminer\q($table) . "
ORDER BY ORDINAL_POSITION", null, "") as $row
Adminer\get_rows("SELECT s.table_name table_name, s.constraint_name constraint_name, s.column_name column_name, " . (Adminer\JUSH == "sql" ? "referenced_column_name" : "t.column_name") . " referenced_column_name
FROM information_schema.key_column_usage s" . (Adminer\JUSH == "sql" ? "
WHERE table_schema = " . Adminer\q(Adminer\DB) . "
AND referenced_table_schema = " . Adminer\q(Adminer\DB) . "
AND referenced_table_name" : "
JOIN information_schema.referential_constraints r USING (constraint_catalog, constraint_schema, constraint_name)
JOIN information_schema.key_column_usage t ON r.unique_constraint_catalog = t.constraint_catalog
AND r.unique_constraint_schema = t.constraint_schema
AND r.unique_constraint_name = t.constraint_name
AND r.constraint_catalog = t.constraint_catalog
AND r.constraint_schema = t.constraint_schema
AND r.unique_constraint_name = t.constraint_name
AND s.position_in_unique_constraint = t.ordinal_position
WHERE t.table_catalog = " . Adminer\q(Adminer\DB) . " AND t.table_schema = " . Adminer\q("$_GET[ns]") . "
AND t.table_name") . " = " . Adminer\q($table) . "
ORDER BY s.ordinal_position", null, "") as $row
) {
$return[$row["TABLE_NAME"]]["keys"][$row["CONSTRAINT_NAME"]][$row["COLUMN_NAME"]] = $row["REFERENCED_COLUMN_NAME"];
$return[$row["table_name"]]["keys"][$row["constraint_name"]][$row["column_name"]] = $row["referenced_column_name"];
}
foreach ($return as $key => $val) {
$name = Adminer\adminer()->tableName(Adminer\table_status1($key, true));
@@ -41,9 +51,12 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
$link = Adminer\ME . 'select=' . urlencode($table);
$i = 0;
foreach ($cols as $column => $val) {
if (!isset($row[$val])) {
continue 2;
}
$link .= Adminer\where_link($i++, $column, $row[$val]);
}
echo "<a href='" . Adminer\h($link) . "'>" . Adminer\h($backwardKey["name"]) . "</a>";
echo "<a href='" . Adminer\h($link) . "'>" . Adminer\h(preg_replace('(^' . preg_quote($_GET["select"]) . (substr($_GET["select"], -1) == 's' ? '?' : '') . '_)', '_', $backwardKey["name"])) . "</a>";
$link = Adminer\ME . 'edit=' . urlencode($table);
foreach ($cols as $column => $val) {
$link .= "&set" . urlencode("[" . Adminer\bracket_escape($column) . "]") . "=" . urlencode($row[$val]);
@@ -52,4 +65,15 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
}
}
}
function screenshot() {
return "https://www.adminer.org/static/plugins/backward-keys.png";
}
protected $translations = array(
'cs' => array('' => 'Zobrazí odkazy na tabulky odkazující aktuální řádek, stejně jako Adminer Editor'),
'de' => array('' => 'Links zu Tabellen anzeigen die auf die aktuelle Zeile verweisen, wie im Adminer Editor'),
'ja' => array('' => 'Adminer Editor と同様に、カレント行を参照しているテーブルへのリンクを表示'),
'pl' => array('' => 'Wyświetlaj linki do tabel odnoszących się do bieżącego wiersza, tak samo jak w Edytorze administratora'),
);
}

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

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

@@ -1,3 +1,5 @@
Using drivers: https://www.adminer.org/plugins/#use
Developing drivers: https://www.adminer.org/en/drivers/
The type declarations must be compatible both with source codes and the compiled version (where PHP5-incompatible types are stripped). It means:

View File

@@ -287,10 +287,6 @@ if (isset($_GET["elastic"])) {
return !!$this->conn->affected_rows;
}
function convertOperator($operator): string {
return $operator == "LIKE %%" ? "should" : $operator;
}
}
function support($feature) {
@@ -412,6 +408,11 @@ if (isset($_GET["elastic"])) {
return $table_status["Engine"] == "view";
}
function view(string $name): array {
$return = connection()->rootQuery("_alias/" . urlencode($name) . "");
return array("select" => implode("\n", array_keys($return)));
}
function error() {
return h(connection()->error);
}
@@ -533,15 +534,18 @@ if (isset($_GET["elastic"])) {
}
}
/** Drop types
* @param list<string> $tables
*/
function drop_views(array $tables): bool {
$return = connection()->rootQuery('_aliases', array('actions' => array_map(function ($table) {
return array('remove' => array('index' => '*', 'alias' => $table));
}, $tables)), 'POST');
return $return && !$return['errors'];
}
function drop_tables(array $tables): bool {
$return = true;
foreach ($tables as $table) { //! convert to bulk api
$return = $return && connection()->rootQuery(urlencode($table), null, 'DELETE');
}
return $return;
}

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

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

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

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

@@ -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,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 AdminerLoginOtp {
class AdminerLoginOtp extends Adminer\Plugin {
protected $secret;
/**
* @param string $secret decoded secret, e.g. base64_decode("SECRET")
*/
function __construct(string $secret) {
function __construct($secret) {
$this->secret = $secret;
if ($_POST["auth"]) {
$_SESSION["otp"] = (string) $_POST["auth"]["otp"];
@@ -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,18 +1,18 @@
<?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
* @param string $password_hash result of password_hash()
*/
function __construct(string $password_hash) {
function __construct($password_hash) {
$this->password_hash = $password_hash;
}
@@ -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('' => 'ログイン画面に定義済のサーバリストを表示'),
);
}

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