Compare commits

..

173 Commits

Author SHA1 Message Date
Jakub Vrana
5754b32693 PostgreSQL: Export serial as serial, not nextval() 2026-03-18 15:14:39 +01:00
Jakub Vrana
11234ef939 PostgreSQL: Export schema in REFERENCES 2026-03-18 15:14:39 +01:00
Jakub Vrana
e5e9b3a04d PostgreSQL: Export schema in nextval() 2026-03-18 15:14:39 +01:00
Jakub Vrana
45c7082373 Editor: Display tinyint(1) as checkbox (fix #1246, regression from 5.4.2) 2026-03-18 15:14:36 +01:00
Jakub Vrana
8fa7dedc4b IGDB: Data dumps 2026-03-18 15:13:24 +01:00
Jakub Vrana
f1c8f8532e Tables overview: keep sort 2026-03-18 15:13:24 +01:00
Jakub Vrana
00323425cb PostgreSQL: Run VACUUM ANALYZE for Optimize 2026-03-18 15:13:24 +01:00
Nikola
3d882578df Add Croatian (hr) translation
Add complete Croatian translation for Adminer, including all strings
in adminer/lang/hr.inc.php, plural forms support in lang.inc.php,
and Croatian descriptions and translations for all plugins.
2026-03-18 15:13:23 +01:00
Jakub Vrana
318e304c7e Tables overview: allow sorting (fix #1231) 2026-02-12 10:11:22 +01:00
Jakub Vrana
50852b9036 Select: Display NULL in column title 2026-02-12 10:11:22 +01:00
Jakub Vrana
c30f6227a2 Schema: Avoid PHP warning when referencing another schema 2026-02-12 10:11:22 +01:00
Jakub Vrana
a417a21ba6 Foreign key: Display new field in case of an error 2026-02-12 10:11:22 +01:00
Jakub Vrana
fb1b76e411 PostgreSQL: Display all SQL command warnings and only once 2026-02-12 10:11:22 +01:00
Jakub Vrana
38a937d966 Disable Ctrl+click inline edit without UPDATE privilege 2026-02-10 19:51:38 +01:00
Jakub Vrana
f0142f0b8e Export: Remember unchecked objects (regression from 5.0.6) 2026-02-10 18:52:16 +01:00
Jakub Vrana
cf1454f7a9 PostgreSQL: Order NULL last 2026-02-10 09:11:35 +01:00
Jakub Vrana
c0350d8893 IGDB: Engine 2026-02-10 07:34:20 +01:00
Jakub Vrana
1ac40e0c2b Develop 2026-02-08 16:44:14 +01:00
Jakub Vrana
39b339b1b5 Release 5.4.2 2026-02-08 16:44:05 +01:00
Jakub Vrana
de4104f70d Tests: Fix 2026-02-08 15:06:24 +01:00
Jakub Vrana
aeee1c1991 Fix typos 2026-02-08 13:34:51 +01:00
Jakub Vrana
21d3a31503 Avoid denial-of-service via version check (GHSA-q4f2-39gr-45jh, regression from 4.6.2) 2026-02-08 13:26:56 +01:00
Jakub Vrana
e4ac9d611e Don't quote comma in TSV export (fix #1238) 2026-02-08 09:01:52 +01:00
Jakub Vrana
bdc28bc959 non-PostgreSQL: display NOT NULL checks (fix #1237) 2026-02-08 08:44:40 +01:00
Jakub Vrana
83301e37da MariaDB: don't display checks with the same name from another table (bug #1135) 2026-02-08 08:38:50 +01:00
Jakub Vrana
1b2354cdae Plugins: ignore variables created in included files 2026-02-08 07:52:06 +01:00
Jakub Vrana
a5faea641a Plugin Designs: process before page (fix #1232) 2026-02-08 01:49:05 +01:00
Jakub Vrana
9f4a51819e Avoid deprecated each (fix #1218) 2026-02-08 01:22:38 +01:00
Anton Polonskiy
04473a21c8 ClickHouse: fix minor problems
* ClickHouse: Fix warning on empty tables

Warning:  reset() expects parameter 1 to be array, null given

* ClickHouse: Show correct error message

* ClickHouse: add CTE support + allow whitespace at the beginning of the query

* ClickHouse: set default format
2026-02-08 01:01:59 +01:00
Inoyatulloh
9d1451e659 PostgreSQL: fix namespace in inheritance links 2026-02-07 23:50:55 +01:00
M.K.
1879da5ad8 GitHub: allow for manual on-demand CI runs 2026-02-07 23:50:55 +01:00
Shun Zi
e471d8b0cf Fix escaping spaces in cookie value
`urlencode` encodes space into `+`, which breaks `adminer_permanent`, which is space-separated.
2026-02-07 23:50:54 +01:00
Frank Shrestha
5b9f0186a8 Compile: fix single language version on Windows 2026-02-07 21:49:09 +01:00
Ucky Deni Ulinnuha
da1ffdd34e Theme lavender-light: fix login list 2026-02-07 21:43:46 +01:00
Jakub Vrana
56c18bcca0 IGDB: hide Truncate and Drop 2026-02-07 21:35:09 +01:00
Jakub Vrana
948d57bb63 Shorten all but numeric and date types in select 2026-02-07 21:35:09 +01:00
Jakub Vrana
1a6735cac7 IGDB: display date in edit 2026-02-07 21:35:07 +01:00
Jakub Vrana
8e865cd650 IGDB: nicer printed select query 2026-02-07 21:32:50 +01:00
Jakub Vrana
864d831463 IGDB: print executed queries 2026-02-07 21:32:50 +01:00
Jakub Vrana
f61b085422 Plugins: Allow to be in any namespace 2026-02-07 21:32:50 +01:00
Jakub Vrana
0ddb097cb6 Hide Save button if there are no editable fields 2026-02-07 21:32:50 +01:00
Jakub Vrana
2674250862 Display uneditable fields in edit form 2026-02-07 21:32:45 +01:00
Jakub Vrana
6f6f576c41 Driver: customizable delimiter 2026-02-03 14:16:26 +01:00
Jakub Vrana
ecd9c74c99 Unify JSON pretty print 2026-02-03 14:16:26 +01:00
Jakub Vrana
2ea1f8a88c IGDB: webhooks 2026-02-03 14:16:24 +01:00
Jakub Vrana
691edde5fc Hide sort links on unsortable columns 2026-02-03 09:23:32 +01:00
Jakub Vrana
554e43aae4 Fix warning for unset enum value 2026-02-03 08:13:01 +01:00
Jakub Vrana
503d83599c IGDB: sort columns 2026-02-02 13:29:31 +01:00
Jakub Vrana
a7a704c818 New plugin: IGDB driver 2026-02-01 20:23:52 +01:00
Jakub Vrana
d356091c2f Hide unsupported alter table links 2026-02-01 12:09:57 +01:00
Jakub Vrana
a5ec07a77d Exact found rows except MySQL and PgSQL 2026-02-01 09:32:35 +01:00
Jakub Vrana
68e8b5bf69 Highlight odd rows in nested tables 2026-02-01 09:32:35 +01:00
Jakub Vrana
9c5215cf77 SQL: Unify value formatting with select 2026-02-01 09:32:33 +01:00
Jakub Vrana
e6fe48516e Improve print of nested tables 2026-02-01 09:31:37 +01:00
Jakub Vrana
58ff31a15d Save bytes 2026-02-01 09:18:22 +01:00
Jakub Vrana
ca7c4d90e1 Link //domain.tld values 2026-02-01 09:18:22 +01:00
Jakub Vrana
605ed2dcab Remove useless . "" 2026-02-01 09:18:22 +01:00
Jakub Vrana
76a8dbfb98 Support multiline generated values in alter table 2026-02-01 09:18:22 +01:00
Jakub Vrana
cca4d26784 PostgreSQL: Fix definition of complex generated columns 2026-02-01 09:18:22 +01:00
Jakub Vrana
80f9278d34 PostgreSQL: Allow creating NOT DEFERRABLE foreign keys 2026-02-01 09:18:22 +01:00
Jakub Vrana
c1838743ed PostgreSQL: Add schema to sequence and and view export 2026-02-01 09:18:22 +01:00
Jakub Vrana
eb614963f8 PostgreSQL: Remove duplicate DEFERRABLE in foreign key export 2026-02-01 09:18:22 +01:00
Jakub Vrana
1109ca6389 Add missing parentheses to CHECK export 2026-02-01 09:18:22 +01:00
Jakub Vrana
3856d0563e PostgreSQL: Offer foreign keys in create table 2026-02-01 09:18:22 +01:00
Jakub Vrana
a49fcf4799 Pretty print JSON in edit 2026-02-01 09:18:19 +01:00
Jakub Vrana
53d7e31bf6 PostgreSQL: Fix help link 2025-11-10 17:16:36 +01:00
Jakub Vrana
15f4183fa6 Add help for socket login (bug #1198) 2025-10-28 10:27:50 +01:00
Jakub Vrana
e6be47941b AdminerEnumOption: Fix after bug #1152 (fix #1191) 2025-10-27 15:34:38 +01:00
Marcus
240c8fc5e7 ClickHouse: Fix list of tables (bug #1176) 2025-10-27 15:24:03 +01:00
Jakub Vrana
57c5370c67 Prolong printed SQL query to 10000 characters (fix #1186) 2025-10-27 15:17:39 +01:00
Jakub Vrana
489f78c21c MySQL: Use information_schema to get routine definition (fix #1179) 2025-10-27 15:17:39 +01:00
Jakub Vrana
6c8de72707 Autocomplete: fix in empty textarea (fix #1173) 2025-10-27 15:17:39 +01:00
Marcus Nightingale
4d0e79234c ClickHouse: Fix offset (bug #1188)
Previously, the driver used `LIMIT <limit>, <offset>` syntax, causing incorrect
pagination behavior (page 2 repeating results from page 1, etc.). Updated the
`limit()` function to use ClickHouse-compatible `LIMIT <count> OFFSET <offset>`
syntax, ensuring correct row offsets across pages.
2025-10-27 15:17:28 +01:00
Jakub Vrana
c7ede7331e PostgreSQL: Mark unique partial indexes as unique (fix #1172) 2025-10-26 15:40:31 +01:00
Jakub Vrana
c5f3707bb9 AdminerRowNumbers: Fix before PHP 8.3.0 (fix #1167) 2025-10-26 15:27:48 +01:00
Jakub Vrana
1c008b7d71 Plugins: Methods showVariables() and showStatus() (bug #1157) 2025-10-26 15:15:10 +01:00
Jakub Vrana
c2be05f0e9 Develop 2025-09-26 17:48:03 +02:00
Jakub Vrana
eaad45a781 Release 5.4.1 2025-09-26 17:38:02 +02:00
Jakub Vrana
9a5b8f1f92 Do not quote 0 in CSV export 2025-09-16 10:18:07 +02:00
Jakub Vrana
1b43a6f034 Warn about exceeded upload_max_filesize in imports 2025-09-15 20:12:30 +02:00
Jakub Vrana
fdc2326376 Remove unused function 2025-09-15 19:51:56 +02:00
Jakub Vrana
08f93d6d09 Save bytes 2025-09-12 07:42:10 +02:00
Jakub Vrana
4fbe8ebf5a Update changelog 2025-09-11 15:10:07 +02:00
Jakub Vrana
86285dcf34 MySQL: Fix displaying routine definition (fix #1156, regression from 5.4.0) 2025-09-11 14:35:48 +02:00
Jakub Vrana
26c4057946 SQL command: Unlink NULL primary keys 2025-09-10 00:23:26 +02:00
Jakub Vrana
d15d0b2ef3 Add Composer script check 2025-09-10 00:17:44 +02:00
Jakub Vrána
bd1dffe086 CI: Run PHPStan 2025-09-09 08:28:20 +02:00
Jakub Vrana
4918ba407f Increase max. ?sql= length (fix #1154) 2025-09-08 21:30:44 +02:00
Jakub Vrana
46638ebd9a Develop 2025-09-08 11:43:22 +02:00
Jakub Vrana
6f339bac6c Release 5.4.0 2025-09-08 11:43:01 +02:00
Jakub Vrana
f32f84cf57 Use $this->conn 2025-09-08 11:01:25 +02:00
Jakub Vrana
ce063a64dd CockroachDB: Set default index algorithm 2025-09-08 10:59:19 +02:00
Jakub Vrana
1d5d441271 Fix tests 2025-09-08 10:52:58 +02:00
Jakub Vrana
fd28e64d8c MSSQL: Fix chechConstraints() 2025-09-08 10:21:58 +02:00
Jakub Vrana
3874148064 Save bytes 2025-09-08 08:44:44 +02:00
Jakub Vrana
0640326df4 SQLite PDO: Avoid double calling PRAGMA 2025-09-08 08:41:45 +02:00
Jakub Vrana
16f7080ff2 Define SERVER always as string 2025-09-08 08:37:49 +02:00
Jakub Vrana
516530485d Save bytes 2025-09-08 08:31:38 +02:00
Jakub Vrana
f921dafa61 Allow connecting to IPv6 (fix #1095) 2025-09-08 08:27:09 +02:00
Jakub Vrana
d1831641a9 MySQL: Fix saving empty enum (fix #1152) 2025-09-08 07:45:59 +02:00
Jakub Vrana
b1c825484d non-MySQL: Disable enum and set editing 2025-09-07 16:35:14 +02:00
Jakub Vrana
6c662e1f96 AdminerFileUpload: Replace invalid characters in table name 2025-09-07 16:18:57 +02:00
Jakub Vrana
7ef0949539 AdminerTimeout: Fix typo 2025-09-07 15:17:38 +02:00
Jakub Vrana
9fc63eb9d0 PostgreSQL: Export DROP and CREATE DATABASE (fix #1140) 2025-09-07 15:08:27 +02:00
Jakub Vrana
2041d5c6e3 Save bytes 2025-09-07 14:00:21 +02:00
Jakub Vrana
fc2ab7de16 PostgreSQL: Don't treat user types containing 'file' as blobs (fix #1118) 2025-09-07 13:52:04 +02:00
Jakub Vrana
2db30ba7e2 Bug report template: use comments 2025-09-07 12:11:07 +02:00
Lucas Sandery
8d6ed3fffa Design lucas-sandery: Theme update for v5 + dark mode 2025-09-07 12:00:42 +02:00
Lucas Sandery
9e0db9f1cd RTL precedence & JS-only fixes 2025-09-07 12:00:36 +02:00
Jakub Vrana
a979b4be22 Modernize JS: Use dataset 2025-09-07 11:45:42 +02:00
Jakub Vrana
26319460ef Fix code style 2025-09-07 11:21:05 +02:00
Jakub Vrana
b41699624b JS: modernize 2025-09-05 20:44:43 +02:00
Jakub Vrana
17a0e84718 Load more: run syntax highlighter 2025-09-05 20:42:12 +02:00
Jakub Vrana
5c5f7d17fe Copy hidden newlines to clipboard 2025-09-05 20:33:58 +02:00
Jakub Vrana
4ad7eb0b70 PostgreSQL: Fix calling functions returing table 2025-08-15 23:43:25 +02:00
Jakub Vrana
b83da41739 PostgreSQL: Fix calling functions with name-less parameters 2025-08-15 23:36:54 +02:00
Jakub Vrana
e551efb031 Executed SQL commands: Add button for copy to clipboard 2025-08-09 14:09:07 +02:00
Jakub Vrana
6d0351ec7c Autofocus added field in alter table 2025-08-09 07:28:04 +02:00
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
118 changed files with 2560 additions and 797 deletions

View File

@@ -7,12 +7,14 @@ assignees: ''
--- ---
**Adminer version:** please use latest published or Git **Adminer version:** <!-- please use latest published or Git -->
**Compiled:** single file / single language / source codes / custom compilation **Compiled:** single file / single language / source codes / custom compilation
**Driver:** e.g. MySQLi **Driver:** <!-- e.g. MySQLi -->
**Database version:** e.g. 10.2.12-MariaDB **Database version:** <!-- e.g. 10.2.12-MariaDB -->
**Plugins used:** **Plugins used:**
_Please provide reproducible steps including a SQL dump (with no personal information) if applicable. <!--
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._ Report issues with Adminer Docker image at https://github.com/TimWolla/docker-adminer._
-->

View File

@@ -3,6 +3,7 @@ name: CI
on: on:
pull_request: pull_request:
branches: [ master ] branches: [ master ]
workflow_dispatch:
jobs: jobs:
build-test: build-test:
@@ -13,5 +14,6 @@ jobs:
- uses: php-actions/composer@v6 - uses: php-actions/composer@v6
- uses: php-actions/phpcs@v1 - uses: php-actions/phpcs@v1
with: with:
path: adminer/ path: .
standard: phpcs.xml standard: phpcs.xml
- uses: php-actions/phpstan@v3

View File

@@ -1,3 +1,84 @@
## Adminer dev
- Tables overview: allow sorting (bug #1231)
- Select: Disable Ctrl+click inline edit without UPDATE privilege
- Select: Display NULL in column title
- Export: Remember unchecked objects (regression from 5.0.6)
- Foreign key: Display new field in case of an error
- PostgreSQL: Order NULL last
- PostgreSQL: Display all SQL command warnings and only once
- PostgreSQL: Export serial as serial, not nextval()
- PostgreSQL: Export schema in nextval()
- PostgreSQL: Export schema in REFERENCES
- Editor: Display tinyint(1) as checkbox (bug #1246, regression from 5.4.2)
- Croatian translation
## Adminer 5.4.2 (released 2026-02-08)
- Avoid denial-of-service via version check (GHSA-q4f2-39gr-45jh, regression from 4.6.2)
- Pretty print JSON in edit
- Support multiline generated values in alter table
- Link //domain.tld values
- Improve print of nested tables
- Hide sort links on unsortable columns
- Display uneditable fields in edit form
- Shorten all but numeric and date types in select
- Fix escaping spaces in cookie value (bug #1208)
- Don't quote comma in TSV export (bug #1238)
- MariaDB: Don't display checks with the same name from another table (bug #1135)
- PostgreSQL: Offer foreign keys in create table
- PostgreSQL: Add missing parentheses to CHECK export
- PostgreSQL: Allow creating NOT DEFERRABLE foreign keys
- PostgreSQL: Remove duplicate DEFERRABLE in foreign key export
- PostgreSQL: Add schema to sequence and view export
- PostgreSQL: Fix definition of complex generated columns
- PostgreSQL: Mark unique partial indexes as unique (bug #1172)
- PostgreSQL: Fix namespace in inheritance links (bug #1221)
- non-PostgreSQL: Display NOT NULL checks (bug #1237)
- ClickHouse: Fix offset (bug #1188)
- ClickHouse: Fix list of tables (bug #1176)
- Plugins: Methods showVariables() and showStatus() (bug #1157)
- Plugins: Allow to be in any namespace
- New plugin: IGDB driver
## Adminer 5.4.1 (released 2025-09-26)
- SQL command: Unlink NULL primary keys
- Do not quote 0 in CSV export
- Warn about exceeded upload_max_filesize in imports
- Prolong queries saved from SQL command to URL (bug #1154)
- MySQL: Fix displaying routine definition (bug #1156, regression from 5.4.0)
## Adminer 5.4.0 (released 2025-09-08)
- Allow specifying operator in search anywhere
- Do not order descending in GROUP BY select
- Allow exporting SQL in SQL command (bug #1092)
- Add section links in database overview
- Warn about exceeded max_file_uploads in import
- Display @ after username without server in existing logins
- Display data length and index length for materialized views
- Link routines from syntax highlighting
- Autofocus added field in alter table
- Executed SQL commands: Add button for copy to clipboard
- Load more: run syntax highlighter
- Allow connecting to IPv6 (bug #1095)
- MySQL: Fix saving empty enum (bug #1152)
- MySQL 5.0-: Do not load partitioning info in alter table (bug #1099)
- MariaDB: Parse COLLATE in routine definition (bug #1104)
- PostgreSQL: Show structure of inherited tables
- PostgreSQL: Display index expressions
- PostgreSQL: Add SQL operator to select
- PostgreSQL: Hide only partitions, not all inherited tables from menu
- PostgreSQL: Allow comparing json columns (bug #1107)
- PostgreSQL: Shorten values in hstore columns
- PostgreSQL: Quote edit value with interval operator
- PostgreSQL: Fix calling functions with name-less parameters
- PostgreSQL: Fix calling functions returing table
- PostgreSQL: Don't treat user types containing 'file' as blobs (bug #1118)
- PostgreSQL: Export DROP and CREATE DATABASE (bug #1140)
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089, regression from 5.3.0)
- Elasticsearch: Support dropping aliases
- Plugins: Methods afterConnect(), processList() and killProcess()
- New plugin: Display row numbers in select (bug #1106)
- New plugin: Specify query timeout
## Adminer 5.3.0 (released 2025-05-04) ## Adminer 5.3.0 (released 2025-05-04)
- Align numeric functions right - Align numeric functions right
- Autocomplete: Support table aliases - Autocomplete: Support table aliases
@@ -116,8 +197,8 @@
- MariaDB: Fix creating and altering generated columns (bug #897) - MariaDB: Fix creating and altering generated columns (bug #897)
- PostgreSQL: Fix "where" and "order" privileges (bug #902, regression from 5.0.2) - PostgreSQL: Fix "where" and "order" privileges (bug #902, regression from 5.0.2)
- SQLite: Fix creating table in compiled version (bug #901, regression from 5.0.0) - SQLite: Fix creating table in compiled version (bug #901, regression from 5.0.0)
- Elastic: Do not pass null values on insert (PR #892) - Elasticsearch: Do not pass null values on insert (PR #892)
- Elastic: Fix displaying sparse rows (PR #893) - Elasticsearch: Fix displaying sparse rows (PR #893)
- Plugins: Add method dumpFooter() - Plugins: Add method dumpFooter()
## Adminer 5.0.2 (released 2025-03-10) ## Adminer 5.0.2 (released 2025-03-10)
@@ -198,6 +279,7 @@
- SQLite: Fix expressions in default values (bug SF-860) - SQLite: Fix expressions in default values (bug SF-860)
- MS SQL: Foreign keys in non-default schema (bug SF-833) - MS SQL: Foreign keys in non-default schema (bug SF-833)
- Oracle: Include tables granted by other user - Oracle: Include tables granted by other user
- Elasticsearch: Move to plugin
- MongoDB: Execute commands against the selected DB - MongoDB: Execute commands against the selected DB
## Adminer 4.15.0 ## Adminer 4.15.0

View File

@@ -36,7 +36,7 @@ if (!$error && $_POST) {
} }
} }
$query = (isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . table($PROCEDURE) . "(" . implode(", ", $call) . ")"; $query = (isset($_GET["callf"]) ? "SELECT " : "CALL ") . (idx($routine["returns"], "type") == "record" ? "* FROM " : "") . table($PROCEDURE) . "(" . implode(", ", $call) . ")";
$start = microtime(true); $start = microtime(true);
$result = connection()->multi_query($query); $result = connection()->multi_query($query);
$affected = connection()->affected_rows; // getting warnings overwrites this $affected = connection()->affected_rows; // getting warnings overwrites this

View File

@@ -3,7 +3,7 @@ namespace Adminer;
$TABLE = $_GET["create"]; $TABLE = $_GET["create"];
$partition_by = driver()->partitionBy; $partition_by = driver()->partitionBy;
$partitions_info = driver()->partitionsInfo($TABLE); $partitions_info = ($partition_by ? driver()->partitionsInfo($TABLE) : array());
$referencable_primary = referencable_primary($TABLE); $referencable_primary = referencable_primary($TABLE);
$foreign_keys = array(); $foreign_keys = array();

View File

@@ -38,7 +38,7 @@ if ($tables_views && !$error && !$_POST["search"]) {
} elseif (JUSH != "sql") { } elseif (JUSH != "sql") {
$result = (JUSH == "sqlite" $result = (JUSH == "sqlite"
? queries("VACUUM") ? queries("VACUUM")
: apply_queries("VACUUM" . ($_POST["optimize"] ? "" : " ANALYZE"), $_POST["tables"]) : apply_queries("VACUUM" . ($_POST["optimize"] ? " ANALYZE" : ""), $_POST["tables"])
); );
$message = lang('Tables have been optimized.'); $message = lang('Tables have been optimized.');
} elseif (!$_POST["tables"]) { } elseif (!$_POST["tables"]) {
@@ -49,27 +49,29 @@ if ($tables_views && !$error && !$_POST["search"]) {
} }
} }
queries_redirect(substr(ME, 0, -1), $message, $result); queries_redirect($_SERVER["REQUEST_URI"], $message, $result);
} }
page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true); page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true);
if (adminer()->homepage()) { if (adminer()->homepage()) {
if ($_GET["ns"] !== "") { if ($_GET["ns"] !== "") {
$order = $_GET["order"];
echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n"; echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n";
$tables_list = tables_list(); $tables_list = ($order ? table_status() : tables_list());
if (!$tables_list) { if (!$tables_list) {
echo "<p class='message'>" . lang('No tables.') . "\n"; echo "<p class='message'>" . lang('No tables.') . "\n";
} else { } else {
echo "<form action='' method='post'>\n"; echo "<form action='' method='post'>\n";
if (support("table")) { if (support("table")) {
echo "<fieldset><legend>" . lang('Search data in tables') . " <span id='selected2'></span></legend><div>"; 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 script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');", "");
echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n"; echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n";
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
if ($_POST["search"] && $_POST["query"] != "") { if ($_POST["search"] && $_POST["query"] != "") {
$_GET["where"][0]["op"] = driver()->convertOperator("LIKE %%"); $_GET["where"][0]["op"] = $_POST["op"];
search_tables(); search_tables();
} }
} }
@@ -78,94 +80,115 @@ if (adminer()->homepage()) {
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"); echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
echo '<thead><tr class="wrap">'; echo '<thead><tr class="wrap">';
echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", ""); echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", "");
echo '<th>' . lang('Table'); echo '<th><a href="' . h(substr(ME, 0, -1)) . '">' . lang('Table') . '</a>';
echo '<td>' . lang('Engine') . doc_link(array('sql' => 'storage-engines.html')); $columns = array("Engine" => array(lang('Engine') . doc_link(array('sql' => 'storage-engines.html'))));
echo '<td>' . lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/')); if (collations()) {
echo '<td>' . lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286')); $columns["Collation"] = array(lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/')));
echo '<td>' . lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')); }
echo '<td>' . lang('Data Free') . doc_link(array('sql' => 'show-table-status.html')); if (function_exists('Adminer\alter_table')) {
echo '<td>' . lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/')); $columns["Data_length"] = array(lang('Data Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT', 'oracle' => 'REFRN20286')), "create", lang('Alter table'));
echo '<td>' . lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286')); }
echo (support("comment") ? '<td>' . lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')) : ''); if (support('indexes')) {
$columns["Index_length"] = array(lang('Index Length') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')), "indexes", lang('Alter indexes'));
}
$columns["Data_free"] = array(lang('Data Free') . doc_link(array('sql' => 'show-table-status.html')), "edit", lang('New item'));
if (function_exists('Adminer\alter_table')) {
$columns["Auto_increment"] = array(lang('Auto Increment') . doc_link(array('sql' => 'example-auto-increment.html', 'mariadb' => 'auto_increment/')), "auto_increment=1&create", lang('Alter table'));
}
$columns["Rows"] = array(lang('Rows') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'catalog-pg-class.html#CATALOG-PG-CLASS', 'oracle' => 'REFRN20286')), "select", lang('Select data'));
if (support("comment")) {
$columns["Comment"] = array(lang('Comment') . doc_link(array('sql' => 'show-table-status.html', 'pgsql' => 'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')));
}
foreach ($columns as $key => $column) {
echo "<th><a href='" . h(ME) . "order=$key'>$column[0]</a>";
}
echo "</thead>\n"; echo "</thead>\n";
if ($order) {
uasort($tables_list, function ($a, $b) use ($order) {
$return = ($a[$order] < $b[$order] ? -1 : ($a[$order] > $b[$order] ? 1 : 0)); // <=> available since PHP 7.1
return (in_array($order, array('Engine', 'Collation', 'Comment')) ? $return : -$return);
});
}
$tables = 0; $tables = 0;
foreach ($tables_list as $name => $type) { foreach ($tables_list as $name => $status) {
$view = ($type !== null && !preg_match('~table|sequence~i', $type)); $view = ($order ? is_view($status) : $status !== null && !preg_match('~table|sequence~i', $status));
$status = ($order ? $status : array('Engine' => $status));
$id = h("Table-" . $name); $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 '<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)); 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) { if ($view && !preg_match('~materialized~i', $status['Engine'])) {
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>'; $title = lang('View');
echo '<td colspan="' . (count($columns) - 2) . '">' . (support("view") ? "<a href='" . h(ME) . "view=" . urlencode($name) . "' title='" . lang('Alter view') . "'>$title</a>" : $title);
echo '<td align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '" title="' . lang('Select data') . '">?</a>'; echo '<td align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '" title="' . lang('Select data') . '">?</a>';
echo '<td>' . h($status['Comment']);
} else { } else {
foreach ( foreach ($columns as $key => $column) {
array(
"Engine" => array(),
"Collation" => array(),
"Data_length" => array("create", lang('Alter table')),
"Index_length" => array("indexes", lang('Alter indexes')),
"Data_free" => array("edit", lang('New item')),
"Auto_increment" => array("auto_increment=1&create", lang('Alter table')),
"Rows" => array("select", lang('Select data')),
) as $key => $link
) {
$id = " id='$key-" . h($name) . "'"; $id = " id='$key-" . h($name) . "'";
echo ($link ? "<td align='right'>" . (support("table") || $key == "Rows" || (support("indexes") && $key != "Data_length") $val = idx($status, $key, '?');
? "<a href='" . h(ME . "$link[0]=") . urlencode($name) . "'$id title='$link[1]'>?</a>" echo ($column[1]
: "<span$id>?</span>" ? "<td align='right'><a href='" . h(ME . "$column[1]=") . urlencode($name) . "'$id title='$column[2]'>" . (is_numeric($val)
) : "<td id='$key-" . h($name) . "'>"); ? ($val < 0 ? '?' : ($key == "Rows" && $val && $status["Engine"] == (JUSH == "pgsql" ? "table" : "InnoDB") ? '~ ' : '') . format_number($val))
: $val
) . "</a>"
: "<td id='$key-" . h($name) . "'>" . h($val)
);
} }
$tables++; $tables++;
} }
echo (support("comment") ? "<td id='Comment-" . h($name) . "'>" : "");
echo "\n"; echo "\n";
} }
echo "<tr><td><th>" . lang('%d in total', count($tables_list)); echo "<tr><td><th>" . lang('%d in total', count($tables_list));
echo "<td>" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : ""); echo "<td>" . h(JUSH == "sql" ? get_val("SELECT @@default_storage_engine") : "");
echo "<td>" . h(db_collation(DB, collations())); echo (collations() ? "<td>" . h(db_collation(DB, collations())) : '');
foreach (array("Data_length", "Index_length", "Data_free") as $key) { foreach (array("Data_length", "Index_length", "Data_free") as $key) {
echo "<td align='right' id='sum-$key'>"; echo ($columns[$key] ? "<td align='right' id='sum-$key'>" : "");
} }
echo "\n"; echo "\n";
echo "</table>\n"; echo "</table>\n";
echo ($order ? '' : script("ajaxSetHtml('" . js_escape(ME) . "script=db');"));
echo "</div>\n"; echo "</div>\n";
if (!information_schema(DB)) { if (!information_schema(DB)) {
echo "<div class='footer'><div>\n";
$vacuum = "<input type='submit' value='" . lang('Vacuum') . "'> " . on_help("'VACUUM'"); $vacuum = "<input type='submit' value='" . lang('Vacuum') . "'> " . on_help("'VACUUM'");
$optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . on_help(JUSH == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM OPTIMIZE'"); $optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . on_help(JUSH == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM ANALYZE'");
echo "<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>" $print = (JUSH == "sqlite" ? $vacuum . "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'PRAGMA integrity_check'")
. (JUSH == "sqlite" ? $vacuum . "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'PRAGMA integrity_check'")
: (JUSH == "pgsql" ? $vacuum . $optimize : (JUSH == "pgsql" ? $vacuum . $optimize
: (JUSH == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> " . on_help("'ANALYZE TABLE'") : (JUSH == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> " . on_help("'ANALYZE TABLE'")
. $optimize . $optimize
. "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'CHECK TABLE'") . "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'CHECK TABLE'")
. "<input type='submit' name='repair' value='" . lang('Repair') . "'> " . on_help("'REPAIR TABLE'") . "<input type='submit' name='repair' value='" . lang('Repair') . "'> " . on_help("'REPAIR TABLE'")
: ""))) : "")))
. "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm() . (function_exists('Adminer\truncate_tables') ? "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm() : "")
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n"; . (function_exists('Adminer\drop_tables') ? "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() : "");
echo ($print ? "<div class='footer'><div>\n<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>$print\n</div></fieldset>\n" : "");
$databases = (support("scheme") ? adminer()->schemas() : adminer()->databases()); $databases = (support("scheme") ? adminer()->schemas() : adminer()->databases());
$script = "";
if (count($databases) != 1 && JUSH != "sqlite") { 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)); $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 ($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 "</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 (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 "<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 input_token();
echo "</div></fieldset>\n";
echo "</div></div>\n"; echo "</div></div>\n";
} }
echo "</form>\n"; echo "</form>\n";
echo script("tableCheck();"); echo script("tableCheck();");
} }
echo "<p class='links'><a href='" . h(ME) . "create='>" . lang('Create table') . "</a>\n"; echo (function_exists('Adminer\alter_table') ? "<p class='links'><a href='" . h(ME) . "create='>" . lang('Create table') . "</a>\n" : '');
echo (support("view") ? "<a href='" . h(ME) . "view='>" . lang('Create view') . "</a>\n" : ""); echo (support("view") ? "<a href='" . h(ME) . "view='>" . lang('Create view') . "</a>\n" : "");
if (support("routine")) { if (support("routine")) {
@@ -239,9 +262,5 @@ if (adminer()->homepage()) {
} }
echo '<p class="links"><a href="' . h(ME) . 'event=">' . lang('Create event') . "</a>\n"; 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

@@ -26,7 +26,7 @@ if (isset($_GET["mssql"])) {
$this->error = rtrim($this->error); $this->error = rtrim($this->error);
} }
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
$connection_info = array("UID" => $username, "PWD" => $password, "CharacterSet" => "UTF-8"); $connection_info = array("UID" => $username, "PWD" => $password, "CharacterSet" => "UTF-8");
$ssl = adminer()->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["Encrypt"])) { if (isset($ssl["Encrypt"])) {
@@ -39,7 +39,8 @@ if (isset($_GET["mssql"])) {
if ($db != "") { if ($db != "") {
$connection_info["Database"] = $db; $connection_info["Database"] = $db;
} }
$this->link = @sqlsrv_connect(preg_replace('~:~', ',', $server), $connection_info); list($host, $port) = host_port($server);
$this->link = @sqlsrv_connect($host . ($port ? ",$port" : ""), $connection_info);
if ($this->link) { if ($this->link) {
$info = sqlsrv_server_info($this->link); $info = sqlsrv_server_info($this->link);
$this->server_info = $info['SQLServerVersion']; $this->server_info = $info['SQLServerVersion'];
@@ -181,8 +182,9 @@ if (isset($_GET["mssql"])) {
class Db extends MssqlDb { class Db extends MssqlDb {
public $extension = "PDO_SQLSRV"; public $extension = "PDO_SQLSRV";
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
return $this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password); list($host, $port) = host_port($server);
return $this->dsn("sqlsrv:Server=$host" . ($port ? ",$port" : ""), $username, $password);
} }
} }
@@ -190,8 +192,9 @@ if (isset($_GET["mssql"])) {
class Db extends MssqlDb { class Db extends MssqlDb {
public $extension = "PDO_DBLIB"; public $extension = "PDO_DBLIB";
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
return $this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password); list($host, $port) = host_port($server);
return $this->dsn("dblib:charset=utf8;host=$host" . ($port ? (is_numeric($port) ? ";port=" : ";unix_socket=") . $port : ""), $username, $password);
} }
} }
} }
@@ -214,7 +217,7 @@ if (isset($_GET["mssql"])) {
public $generated = array("PERSISTED", "VIRTUAL"); public $generated = array("PERSISTED", "VIRTUAL");
public $onActions = "NO ACTION|CASCADE|SET NULL|SET DEFAULT"; public $onActions = "NO ACTION|CASCADE|SET NULL|SET DEFAULT";
static function connect(?string $server, string $username, string $password) { static function connect(string $server, string $username, string $password) {
if ($server == "") { if ($server == "") {
$server = "localhost:1433"; $server = "localhost:1433";
} }
@@ -678,7 +681,7 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)) as $row
return "TRUNCATE TABLE " . table($table); return "TRUNCATE TABLE " . table($table);
} }
function use_sql($database) { function use_sql($database, $style = "") {
return "USE " . idf_escape($database); return "USE " . idf_escape($database);
} }

View File

@@ -16,9 +16,9 @@ if (!defined('Adminer\DRIVER')) {
parent::init(); parent::init();
} }
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4 mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4
list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket list($host, $port) = host_port($server);
$ssl = adminer()->connectSsl(); $ssl = adminer()->connectSsl();
if ($ssl) { if ($ssl) {
$this->ssl_set($ssl['key'], $ssl['cert'], $ssl['ca'], '', ''); $this->ssl_set($ssl['key'], $ssl['cert'], $ssl['ca'], '', '');
@@ -32,7 +32,7 @@ if (!defined('Adminer\DRIVER')) {
(is_numeric($port) ? null : $port), (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) ($ssl ? ($ssl['verify'] !== false ? 2048 : 64) : 0) // 2048 - MYSQLI_CLIENT_SSL, 64 - MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT (not available before PHP 5.6.16)
); );
$this->options(MYSQLI_OPT_LOCAL_INFILE, false); $this->options(MYSQLI_OPT_LOCAL_INFILE, 0);
return ($return ? '' : $this->error); return ($return ? '' : $this->error);
} }
@@ -58,14 +58,14 @@ if (!defined('Adminer\DRIVER')) {
class Db extends SqlDb { class Db extends SqlDb {
/** @var resource */ private $link; /** @var resource */ private $link;
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
if (ini_bool("mysql.allow_local_infile")) { if (ini_bool("mysql.allow_local_infile")) {
return lang('Disable %s or enable %s or %s extensions.', "'mysql.allow_local_infile'", "MySQLi", "PDO_MySQL"); return lang('Disable %s or enable %s or %s extensions.', "'mysql.allow_local_infile'", "MySQLi", "PDO_MySQL");
} }
$this->link = @mysql_connect( $this->link = @mysql_connect(
($server != "" ? $server : ini_get("mysql.default_host")), ($server != "" ? $server : ini_get("mysql.default_host")),
("$server$username" != "" ? $username : ini_get("mysql.default_user")), ($server . $username != "" ? $username : ini_get("mysql.default_user")),
("$server$username$password" != "" ? $password : ini_get("mysql.default_password")), ($server . $username . $password != "" ? $password : ini_get("mysql.default_password")),
true, true,
131072 // CLIENT_MULTI_RESULTS for CALL 131072 // CLIENT_MULTI_RESULTS for CALL
); );
@@ -158,7 +158,7 @@ if (!defined('Adminer\DRIVER')) {
class Db extends PdoDb { class Db extends PdoDb {
public $extension = "PDO_MySQL"; public $extension = "PDO_MySQL";
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
$options = array(\PDO::MYSQL_ATTR_LOCAL_INFILE => false); $options = array(\PDO::MYSQL_ATTR_LOCAL_INFILE => false);
$ssl = adminer()->connectSsl(); $ssl = adminer()->connectSsl();
if ($ssl) { if ($ssl) {
@@ -175,8 +175,9 @@ if (!defined('Adminer\DRIVER')) {
$options[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $ssl['verify']; $options[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $ssl['verify'];
} }
} }
list($host, $port) = host_port($server);
return $this->dsn( return $this->dsn(
"mysql:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), "mysql:charset=utf8;host=$host" . ($port ? (is_numeric($port) ? ";port=" : ";unix_socket=") . $port : ""),
$username, $username,
$password, $password,
$options $options
@@ -211,7 +212,7 @@ if (!defined('Adminer\DRIVER')) {
public $functions = array("char_length", "date", "from_unixtime", "lower", "round", "floor", "ceil", "sec_to_time", "time_to_sec", "upper"); public $functions = array("char_length", "date", "from_unixtime", "lower", "round", "floor", "ceil", "sec_to_time", "time_to_sec", "upper");
public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
static function connect(?string $server, string $username, string $password) { static function connect(string $server, string $username, string $password) {
$connection = parent::connect($server, $username, $password); $connection = parent::connect($server, $username, $password);
if (is_string($connection)) { if (is_string($connection)) {
if (function_exists('iconv') && !is_utf8($connection) && strlen($s = iconv("windows-1250", "utf-8", $connection)) > strlen($connection)) { // windows-1250 - most common Windows encoding if (function_exists('iconv') && !is_utf8($connection) && strlen($s = iconv("windows-1250", "utf-8", $connection)) > strlen($connection)) { // windows-1250 - most common Windows encoding
@@ -340,7 +341,7 @@ if (!defined('Adminer\DRIVER')) {
function partitionsInfo(string $table): array { function partitionsInfo(string $table): array {
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table); $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"); $result = $this->conn->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
$return = array(); $return = array();
list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row(); 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"); $partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
@@ -457,7 +458,7 @@ if (!defined('Adminer\DRIVER')) {
/** Get table status /** Get table status
* @param bool $fast return only "Name", "Engine" and "Comment" fields * @param bool $fast return only "Name", "Engine" and "Comment" fields
* @return TableStatus[] * @return array<string, TableStatus>
*/ */
function table_status(string $name = "", bool $fast = false): array { function table_status(string $name = "", bool $fast = false): array {
$return = array(); $return = array();
@@ -695,7 +696,7 @@ if (!defined('Adminer\DRIVER')) {
* @param string $name new name * @param string $name new name
* @param list<array{string, list<string>, string}> $fields of [$orig, $process_field, $after] * @param list<array{string, list<string>, string}> $fields of [$orig, $process_field, $after]
* @param string[] $foreign * @param string[] $foreign
* @param numeric-string $auto_increment * @param numeric-string|'' $auto_increment
* @param ?Partitions $partitioning null means remove partitioning * @param ?Partitions $partitioning null means remove partitioning
* @return Result|bool * @return Result|bool
*/ */
@@ -892,43 +893,34 @@ if (!defined('Adminer\DRIVER')) {
* @return Routine * @return Routine
*/ */
function routine(string $name, string $type): array { function routine(string $name, string $type): array {
$aliases = array("bool", "boolean", "integer", "double precision", "real", "dec", "numeric", "fixed", "national char", "national varchar"); $fields = get_rows("SELECT
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)"; PARAMETER_NAME field,
$enum = driver()->enumLength; DATA_TYPE type,
$type_pattern = "((" . implode("|", array_merge(array_keys(driver()->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum)++)\\))?" CHARACTER_MAXIMUM_LENGTH length,
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?"; REGEXP_REPLACE(DTD_IDENTIFIER, '^[^ ]+ ', '') `unsigned`,
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : driver()->inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern"; 1 `null`,
$create = get_val("SHOW CREATE $type " . idf_escape($name), 2); DTD_IDENTIFIER full_type,
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match); PARAMETER_MODE `inout`,
$fields = array(); CHARACTER_SET_NAME collation
preg_match_all("~$pattern\\s*,?~is", $match[1], $matches, PREG_SET_ORDER); FROM information_schema.PARAMETERS
foreach ($matches as $param) { WHERE SPECIFIC_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$type' AND SPECIFIC_NAME = " . q($name) . "
$fields[] = array( ORDER BY ORDINAL_POSITION");
"field" => str_replace("``", "`", $param[2]) . $param[3], $return = connection()->query("SELECT ROUTINE_COMMENT comment, ROUTINE_DEFINITION definition, 'SQL' language
"type" => strtolower($param[5]), FROM information_schema.ROUTINES
"length" => preg_replace_callback("~$enum~s", 'Adminer\normalize_enum', $param[6]), WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_TYPE = '$type' AND ROUTINE_NAME = " . q($name))->fetch_assoc();
"unsigned" => strtolower(preg_replace('~\s+~', ' ', trim("$param[8] $param[7]"))), if ($fields && $fields[0]['field'] == '') {
"null" => true, $return['returns'] = array_shift($fields);
"full_type" => $param[4],
"inout" => strtoupper($param[1]),
"collation" => strtolower($param[9]),
);
} }
return array( $return['fields'] = $fields;
"fields" => $fields, /** @phpstan-var Routine */
"comment" => get_val("SELECT ROUTINE_COMMENT FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_NAME = " . q($name)), return $return;
) + ($type != "FUNCTION" ? array("definition" => $match[11]) : array(
"returns" => array("type" => $match[12], "length" => $match[13], "unsigned" => $match[15], "collation" => $match[16]),
"definition" => $match[17],
"language" => "SQL", // available in information_schema.ROUTINES.BODY_STYLE
));
} }
/** Get list of routines /** Get list of routines
* @return list<string[]> ["SPECIFIC_NAME" => , "ROUTINE_NAME" => , "ROUTINE_TYPE" => , "DTD_IDENTIFIER" => ] * @return list<string[]> ["SPECIFIC_NAME" => , "ROUTINE_NAME" => , "ROUTINE_TYPE" => , "DTD_IDENTIFIER" => ]
*/ */
function routines(): array { 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 /** Get list of available routine languages
@@ -983,8 +975,17 @@ if (!defined('Adminer\DRIVER')) {
} }
/** Get SQL command to change database */ /** Get SQL command to change database */
function use_sql(string $database): string { function use_sql(string $database, string $style = ""): string {
return "USE " . idf_escape($database); $name = idf_escape($database);
$return = "";
if (preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE $name", 1))) {
set_utf8mb4($create);
if ($style == "DROP+CREATE") {
$return = "DROP DATABASE IF EXISTS $name;\n";
}
$return .= "$create;\n";
}
return $return . "USE $name";
} }
/** Get SQL commands to create triggers */ /** Get SQL commands to create triggers */
@@ -1067,11 +1068,11 @@ if (!defined('Adminer\DRIVER')) {
} }
/** Kill a process /** Kill a process
* @param numeric-string $val * @param numeric-string $id
* @return Result|bool * @return Result|bool
*/ */
function kill_process(string $val) { function kill_process(string $id) {
return queries("KILL " . number($val)); return queries("KILL " . number($id));
} }
/** Return query to get connection ID */ /** Return query to get connection ID */

View File

@@ -20,7 +20,7 @@ if (isset($_GET["oracle"])) {
$this->error = $error; $this->error = $error;
} }
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
$this->link = @oci_new_connect($username, $password, $server, "AL32UTF8"); $this->link = @oci_new_connect($username, $password, $server, "AL32UTF8");
if ($this->link) { if ($this->link) {
$this->server_info = oci_server_version($this->link); $this->server_info = oci_server_version($this->link);
@@ -60,6 +60,10 @@ if (isset($_GET["oracle"])) {
} }
return $return; return $return;
} }
function timeout(int $ms): bool {
return oci_set_call_timeout($this->link, $ms);
}
} }
class Result { class Result {
@@ -106,7 +110,7 @@ if (isset($_GET["oracle"])) {
public $extension = "PDO_OCI"; public $extension = "PDO_OCI";
public $_current_db; public $_current_db;
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
return $this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password); return $this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password);
} }
@@ -165,7 +169,7 @@ if (isset($_GET["oracle"])) {
} }
} }
if ( if (
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows) !(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $this->conn->affected_rows)
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")")) || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
) { ) {
return false; return false;

View File

@@ -20,10 +20,11 @@ if (isset($_GET["pgsql"])) {
$this->error = $error; $this->error = $error;
} }
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
$db = adminer()->database(); $db = adminer()->database();
set_error_handler(array($this, '_error')); set_error_handler(array($this, '_error'));
$this->string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'"; list($host, $port) = host_port(addcslashes($server, "'\\"));
$this->string = "host='$host'" . ($port ? " port='$port'" : "") . " user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
$ssl = adminer()->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["mode"])) { if (isset($ssl["mode"])) {
$this->string .= " sslmode='" . $ssl["mode"] . "'"; $this->string .= " sslmode='" . $ssl["mode"] . "'";
@@ -87,7 +88,13 @@ if (isset($_GET["pgsql"])) {
} }
function warnings() { function warnings() {
return h(pg_last_notice($this->link)); // second parameter is available since PHP 7.1.0 if (PHP_VERSION_ID >= 70100) {
$return = implode("\n", pg_last_notice($this->link, 2)); // 2 - PGSQL_NOTICE_ALL
pg_last_notice($this->link, 3); // 3 - PGSQL_NOTICE_CLEAR
} else {
$return = pg_last_notice($this->link);
}
return nl_br(h($return));
} }
/** Copy from array into a table /** Copy from array into a table
@@ -143,10 +150,11 @@ if (isset($_GET["pgsql"])) {
public $extension = "PDO_PgSQL"; public $extension = "PDO_PgSQL";
public $timeout = 0; public $timeout = 0;
function attach(?string $server, string $username, string $password): string { function attach(string $server, string $username, string $password): string {
$db = adminer()->database(); $db = adminer()->database();
list($host, $port) = host_port(addcslashes($server, "'\\"));
//! client_encoding is supported since 9.1, but we can't yet use min_version here //! client_encoding is supported since 9.1, but we can't yet use min_version here
$dsn = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'"; $dsn = "pgsql:host='$host'" . ($port ? " port='$port'" : "") . " client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'";
$ssl = adminer()->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["mode"])) { if (isset($ssl["mode"])) {
$dsn .= " sslmode='" . $ssl["mode"] . "'"; $dsn .= " sslmode='" . $ssl["mode"] . "'";
@@ -204,13 +212,13 @@ if (isset($_GET["pgsql"])) {
static $extensions = array("PgSQL", "PDO_PgSQL"); static $extensions = array("PgSQL", "PDO_PgSQL");
static $jush = "pgsql"; static $jush = "pgsql";
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT ILIKE", "NOT IN", "IS NOT NULL"); // no "SQL" to avoid CSRF public $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 $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
public string $nsOid = "(SELECT oid FROM pg_namespace WHERE nspname = current_schema())"; public string $nsOid = "(SELECT oid FROM pg_namespace WHERE nspname = current_schema())";
static function connect(?string $server, string $username, string $password) { static function connect(string $server, string $username, string $password) {
$connection = parent::connect($server, $username, $password); $connection = parent::connect($server, $username, $password);
if (is_string($connection)) { if (is_string($connection)) {
return $connection; return $connection;
@@ -288,7 +296,7 @@ if (isset($_GET["pgsql"])) {
} }
} }
if ( if (
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows) !(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $this->conn->affected_rows)
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")")) || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
) { ) {
return false; return false;
@@ -332,15 +340,15 @@ if (isset($_GET["pgsql"])) {
} }
function inheritsFrom(string $table): array { function inheritsFrom(string $table): array {
return get_vals("SELECT relname FROM pg_class JOIN pg_inherits ON inhparent = oid WHERE inhrelid = " . $this->tableOid($table) . " ORDER BY 1"); return get_rows("SELECT relname AS table, nspname AS ns FROM pg_class JOIN pg_inherits ON inhparent = oid JOIN pg_namespace ON relnamespace = pg_namespace.oid WHERE inhrelid = " . $this->tableOid($table) . " ORDER BY 2, 1");
} }
function inheritedTables(string $table): array { function inheritedTables(string $table): array {
return get_vals("SELECT relname FROM pg_inherits JOIN pg_class ON inhrelid = oid WHERE inhparent = " . $this->tableOid($table) . " ORDER BY 1"); return get_rows("SELECT relname AS table, nspname AS ns FROM pg_inherits JOIN pg_class ON inhrelid = oid JOIN pg_namespace ON relnamespace = pg_namespace.oid WHERE inhparent = " . $this->tableOid($table) . " ORDER BY 2, 1");
} }
function partitionsInfo(string $table): array { function partitionsInfo(string $table): array {
$row = connection()->query("SELECT * FROM pg_partitioned_table WHERE partrelid = " . driver()->tableOid($table))->fetch_assoc(); $row = (min_version(10) ? $this->conn->query("SELECT * FROM pg_partitioned_table WHERE partrelid = " . $this->tableOid($table))->fetch_assoc() : null);
if ($row) { if ($row) {
$attrs = get_vals("SELECT attname FROM pg_attribute WHERE attrelid = $row[partrelid] AND attnum IN (" . str_replace(" ", ", ", $row["partattrs"]) . ")"); //! ordering $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'); $by = array('h' => 'HASH', 'l' => 'LIST', 'r' => 'RANGE');
@@ -359,7 +367,7 @@ if (isset($_GET["pgsql"])) {
function indexAlgorithms(array $tableStatus): array { function indexAlgorithms(array $tableStatus): array {
static $return = array(); static $return = array();
if (!$return) { if (!$return) {
$return = get_vals("SELECT amname FROM pg_am" . (min_version(9.6) ? " WHERE amtype = 'i'" : "") . " ORDER BY amname = 'btree' DESC, amname"); $return = get_vals("SELECT amname FROM pg_am" . (min_version(9.6) ? " WHERE amtype = 'i'" : "") . " ORDER BY amname = '" . ($this->conn->flavor == 'cockroach' ? "prefix" : "btree") . "' DESC, amname");
} }
return $return; return $return;
} }
@@ -448,15 +456,14 @@ ORDER BY 1";
get_rows("SELECT get_rows("SELECT
relname AS \"Name\", relname AS \"Name\",
CASE relkind WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' ELSE 'table' END AS \"Engine\"" . ($has_size ? ", CASE relkind WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' ELSE 'table' END AS \"Engine\"" . ($has_size ? ",
pg_table_size(oid) AS \"Data_length\", pg_table_size(c.oid) AS \"Data_length\",
pg_indexes_size(oid) AS \"Index_length\"" : "") . ", pg_indexes_size(c.oid) AS \"Index_length\"" : "") . ",
obj_description(oid, 'pg_class') AS \"Comment\", obj_description(c.oid, 'pg_class') AS \"Comment\",
" . (min_version(12) ? "''" : "CASE WHEN relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\", " . (min_version(12) ? "''" : "CASE WHEN relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\",
reltuples as \"Rows\", reltuples AS \"Rows\",
inhparent AS inherited, " . (min_version(10) ? "relispartition::int AS partition," : "") . "
current_schema() AS nspname current_schema() AS nspname
FROM pg_class FROM pg_class c
LEFT JOIN pg_inherits ON inhrelid = oid
WHERE relkind IN ('r', 'm', 'v', 'f', 'p') WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
AND relnamespace = " . driver()->nsOid . " AND relnamespace = " . driver()->nsOid . "
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Auto_increment " . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")) as $row //! Auto_increment
@@ -486,17 +493,19 @@ AND relnamespace = " . driver()->nsOid . "
format_type(a.atttypid, a.atttypmod) AS full_type, format_type(a.atttypid, a.atttypmod) AS full_type,
pg_get_expr(d.adbin, d.adrelid) AS default, pg_get_expr(d.adbin, d.adrelid) AS default,
a.attnotnull::int, a.attnotnull::int,
i.indrelid AS primary,
col_description(a.attrelid, a.attnum) AS comment" . (min_version(10) ? ", col_description(a.attrelid, a.attnum) AS comment" . (min_version(10) ? ",
a.attidentity" . (min_version(12) ? ", a.attidentity" . (min_version(12) ? ",
a.attgenerated" : "") : "") . " a.attgenerated" : "") : "") . "
FROM pg_attribute a FROM pg_attribute a
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
LEFT JOIN pg_index i ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) AND i.indisprimary
WHERE a.attrelid = " . driver()->tableOid($table) . " WHERE a.attrelid = " . driver()->tableOid($table) . "
AND NOT a.attisdropped AND NOT a.attisdropped
AND a.attnum > 0 AND a.attnum > 0
ORDER BY a.attnum") as $row ORDER BY a.attnum") as $row
) { ) {
//! collation, primary //! collation
preg_match('~([^([]+)(\((.*)\))?([a-z ]+)?((\[[0-9]*])*)$~', $row["full_type"], $match); preg_match('~([^([]+)(\((.*)\))?([a-z ]+)?((\[[0-9]*])*)$~', $row["full_type"], $match);
list(, $type, $length, $row["length"], $addon, $array) = $match; list(, $type, $length, $row["length"], $addon, $array) = $match;
$row["length"] .= $array; $row["length"] .= $array;
@@ -516,7 +525,7 @@ ORDER BY a.attnum") as $row
$row["auto_increment"] = $row['attidentity'] || preg_match('~^nextval\(~i', $row["default"]) $row["auto_increment"] = $row['attidentity'] || preg_match('~^nextval\(~i', $row["default"])
|| preg_match('~^unique_rowid\(~', $row["default"]); // CockroachDB || preg_match('~^unique_rowid\(~', $row["default"]); // CockroachDB
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1, "where" => 1, "order" => 1); $row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1, "where" => 1, "order" => 1);
if (preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) { if (!$row['generated'] && preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
$row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]); $row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]);
} }
$return[$row["field"]] = $row; $return[$row["field"]] = $row;
@@ -530,7 +539,7 @@ ORDER BY a.attnum") as $row
$table_oid = driver()->tableOid($table); $table_oid = driver()->tableOid($table);
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2); $columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
foreach ( foreach (
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial, pg_am.amname as algorithm, pg_get_expr(pg_index.indpred, pg_index.indrelid, true) AS partial 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 FROM pg_index
JOIN pg_class ON indexrelid = oid JOIN pg_class ON indexrelid = oid
JOIN pg_am ON pg_am.oid = pg_class.relam JOIN pg_am ON pg_am.oid = pg_class.relam
@@ -538,18 +547,17 @@ WHERE indrelid = $table_oid
ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
) { ) {
$relname = $row["relname"]; $relname = $row["relname"];
$return[$relname]["type"] = ($row["indispartial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX"))); $return[$relname]["type"] = ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX"));
$return[$relname]["columns"] = array(); $return[$relname]["columns"] = array();
$return[$relname]["descs"] = array(); $return[$relname]["descs"] = array();
$return[$relname]["algorithm"] = $row["algorithm"]; $return[$relname]["algorithm"] = $row["amname"];
$return[$relname]["partial"] = $row["partial"]; $return[$relname]["partial"] = $row["partial"];
if ($row["indkey"]) { $indexpr = preg_split('~(?<=\)), (?=\()~', $row["indexpr"]); //! '), (' used in expression
foreach (explode(" ", $row["indkey"]) as $indkey) { foreach (explode(" ", $row["indkey"]) as $indkey) {
$return[$relname]["columns"][] = $columns[$indkey]; $return[$relname]["columns"][] = ($indkey ? $columns[$indkey] : array_shift($indexpr));
} }
foreach (explode(" ", $row["indoption"]) as $indoption) { foreach (explode(" ", $row["indoption"]) as $indoption) {
$return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC $return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
}
} }
$return[$relname]["lengths"] = array(); $return[$relname]["lengths"] = array();
} }
@@ -559,12 +567,13 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
function foreign_keys($table) { function foreign_keys($table) {
$return = array(); $return = array();
foreach ( foreach (
get_rows("SELECT conname, condeferrable::int AS deferrable, pg_get_constraintdef(oid) AS definition get_rows("SELECT conname, condeferrable::int AS deferrable, condeferred::int AS deferred, pg_get_constraintdef(oid) AS definition
FROM pg_constraint FROM pg_constraint
WHERE conrelid = " . driver()->tableOid($table) . " WHERE conrelid = " . driver()->tableOid($table) . "
AND contype = 'f'::char AND contype = 'f'::char
ORDER BY conkey, conname") as $row ORDER BY conkey, conname") as $row
) { ) {
$row['deferrable'] = ($row['deferrable'] ? '' : 'NOT ') . 'DEFERRABLE' . ($row['deferred'] ? ' INITIALLY DEFERRED' : '');
if (preg_match('~FOREIGN KEY\s*\((.+)\)\s*REFERENCES (.+)\((.+)\)(.*)$~iA', $row['definition'], $match)) { if (preg_match('~FOREIGN KEY\s*\((.+)\)\s*REFERENCES (.+)\((.+)\)(.*)$~iA', $row['definition'], $match)) {
$row['source'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[1]))); $row['source'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[1])));
if (preg_match('~^(("([^"]|"")+"|[^"]+)\.)?"?("([^"]|"")+"|[^"]+)$~', $match[2], $match2)) { if (preg_match('~^(("([^"]|"")+"|[^"]+)\.)?"?("([^"]|"")+"|[^"]+)$~', $match[2], $match2)) {
@@ -824,7 +833,7 @@ FROM information_schema.routines
WHERE routine_schema = current_schema() AND specific_name = ' . q($name)); WHERE routine_schema = current_schema() AND specific_name = ' . q($name));
$return = idx($rows, 0, array()); $return = idx($rows, 0, array());
$return["returns"] = array("type" => $return["type_udt_name"]); $return["returns"] = array("type" => $return["type_udt_name"]);
$return["fields"] = get_rows('SELECT parameter_name AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout $return["fields"] = get_rows('SELECT COALESCE(parameter_name, ordinal_position::text) AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout
FROM information_schema.parameters FROM information_schema.parameters
WHERE specific_schema = current_schema() AND specific_name = ' . q($name) . ' WHERE specific_schema = current_schema() AND specific_name = ' . q($name) . '
ORDER BY ordinal_position'); ORDER BY ordinal_position');
@@ -906,11 +915,12 @@ AND typelem = 0"
$return = ""; $return = "";
$status = table_status1($table); $status = table_status1($table);
$ns = idf_escape($status['nspname']);
$fkeys = foreign_keys($table); $fkeys = foreign_keys($table);
ksort($fkeys); ksort($fkeys);
foreach ($fkeys as $fkey_name => $fkey) { foreach ($fkeys as $fkey_name => $fkey) {
$return .= "ALTER TABLE ONLY " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " $fkey[definition] " . ($fkey['deferrable'] ? 'DEFERRABLE' : 'NOT DEFERRABLE') . ";\n"; $return .= "ALTER TABLE ONLY $ns." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " " . preg_replace('~( REFERENCES )([^(.]+\()~', "\\1$ns.\\2", $fkey["definition"]) . ";\n";
} }
return ($return ? "$return\n" : $return); return ($return ? "$return\n" : $return);
@@ -921,9 +931,10 @@ AND typelem = 0"
$sequences = array(); $sequences = array();
$status = table_status1($table); $status = table_status1($table);
$ns = idf_escape($status['nspname']);
if (is_view($status)) { if (is_view($status)) {
$view = view($table); $view = view($table);
return rtrim("CREATE VIEW " . idf_escape($table) . " AS $view[select]", ";"); return rtrim("CREATE VIEW $ns." . idf_escape($table) . " AS $view[select]", ";");
} }
$fields = fields($table); $fields = fields($table);
@@ -931,12 +942,17 @@ AND typelem = 0"
return false; return false;
} }
$return = "CREATE TABLE " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " (\n "; $return = "CREATE TABLE $ns." . idf_escape($status['Name']) . " (\n ";
// fields' definitions // fields' definitions
foreach ($fields as $field) { foreach ($fields as $field) {
if ($field['default'] == "nextval('$status[Name]_$field[field]_seq')") {
$field['default'] = null;
$field['full_type'] = preg_replace('~int(eger)?~', 'serial', $field['full_type']);
}
$part = idf_escape($field['field']) . ' ' . $field['full_type'] $part = idf_escape($field['field']) . ' ' . $field['full_type']
. default_value($field) . preg_replace('~(nextval\(\')([^.\']+\')~', '\1' . str_replace("'", "''", $status['nspname']) . '.\2', default_value($field))
. ($field['null'] ? "" : " NOT NULL"); . ($field['null'] ? "" : " NOT NULL");
$return_parts[] = $part; $return_parts[] = $part;
@@ -947,8 +963,8 @@ AND typelem = 0"
? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q(idf_unescape($sequence_name)) ? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q(idf_unescape($sequence_name))
: "SELECT * FROM $sequence_name" : "SELECT * FROM $sequence_name"
), null, "-- ")); ), null, "-- "));
$sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $sequence_name;\n" : "") $sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $ns.$sequence_name;\n" : "")
. "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]" . "CREATE SEQUENCE $ns.$sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]"
. ($auto_increment && $sq['last_value'] ? " START " . ($sq["last_value"] + 1) : "") . ($auto_increment && $sq['last_value'] ? " START " . ($sq["last_value"] + 1) : "")
. " CACHE $sq[cache_value];" . " CACHE $sq[cache_value];"
; ;
@@ -969,7 +985,7 @@ AND typelem = 0"
} }
foreach (driver()->checkConstraints($table) as $conname => $consrc) { foreach (driver()->checkConstraints($table) as $conname => $consrc) {
$return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK $consrc"; $return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK ($consrc)";
} }
$return .= implode(",\n ", $return_parts) . "\n)"; $return .= implode(",\n ", $return_parts) . "\n)";
@@ -984,12 +1000,12 @@ AND typelem = 0"
// comments for table & fields // comments for table & fields
if ($status['Comment']) { if ($status['Comment']) {
$return .= "\n\nCOMMENT ON TABLE " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " IS " . q($status['Comment']) . ";"; $return .= "\n\nCOMMENT ON TABLE $ns." . idf_escape($status['Name']) . " IS " . q($status['Comment']) . ";";
} }
foreach ($fields as $field_name => $field) { foreach ($fields as $field_name => $field) {
if ($field['comment']) { if ($field['comment']) {
$return .= "\n\nCOMMENT ON COLUMN " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . "." . idf_escape($field_name) . " IS " . q($field['comment']) . ";"; $return .= "\n\nCOMMENT ON COLUMN $ns." . idf_escape($status['Name']) . "." . idf_escape($field_name) . " IS " . q($field['comment']) . ";";
} }
} }
@@ -1015,8 +1031,16 @@ AND typelem = 0"
} }
function use_sql($database) { function use_sql($database, $style = "") {
return "\connect " . idf_escape($database); $name = idf_escape($database);
$return = "";
if (preg_match('~CREATE~', $style)) {
if ($style == "DROP+CREATE") {
$return = "DROP DATABASE IF EXISTS $name;\n";
}
$return .= "CREATE DATABASE $name;\n"; //! get info from pg_database
}
return "$return\\connect $name";
} }
function show_variables() { function show_variables() {

View File

@@ -11,7 +11,7 @@ if (isset($_GET["sqlite"])) {
public $extension = "SQLite3"; public $extension = "SQLite3";
private $link; private $link;
function attach(?string $filename, string $username, string $password): string { function attach(string $filename, string $username, string $password): string {
$this->link = new \SQLite3($filename); $this->link = new \SQLite3($filename);
$version = $this->link->version(); $version = $this->link->version();
$this->server_info = $version["versionString"]; $this->server_info = $version["versionString"];
@@ -75,11 +75,8 @@ if (isset($_GET["sqlite"])) {
abstract class SqliteDb extends PdoDb { abstract class SqliteDb extends PdoDb {
public $extension = "PDO_SQLite"; public $extension = "PDO_SQLite";
function attach(?string $filename, string $username, string $password): string { function attach(string $filename, string $username, string $password): string {
$this->dsn(DRIVER . ":$filename", "", ""); return $this->dsn(DRIVER . ":$filename", "", "");
$this->query("PRAGMA foreign_keys = 1");
$this->query("PRAGMA busy_timeout = 500");
return '';
} }
} }
@@ -87,7 +84,7 @@ if (isset($_GET["sqlite"])) {
if (class_exists('Adminer\SqliteDb')) { if (class_exists('Adminer\SqliteDb')) {
class Db extends SqliteDb { class Db extends SqliteDb {
function attach(?string $filename, string $username, string $password): string { function attach(string $filename, string $username, string $password): string {
parent::attach($filename, $username, $password); parent::attach($filename, $username, $password);
$this->query("PRAGMA foreign_keys = 1"); $this->query("PRAGMA foreign_keys = 1");
$this->query("PRAGMA busy_timeout = 500"); $this->query("PRAGMA busy_timeout = 500");
@@ -122,7 +119,7 @@ if (isset($_GET["sqlite"])) {
public $functions = array("hex", "length", "lower", "round", "unixepoch", "upper"); public $functions = array("hex", "length", "lower", "round", "unixepoch", "upper");
public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
static function connect(?string $server, string $username, string $password) { static function connect(string $server, string $username, string $password) {
if ($password != "") { if ($password != "") {
return lang('Database does not support password.'); return lang('Database does not support password.');
} }
@@ -452,7 +449,7 @@ if (isset($_GET["sqlite"])) {
* @param list<list<string>> $fields [process_field()], empty to preserve * @param list<list<string>> $fields [process_field()], empty to preserve
* @param string[] $originals [$original => idf_escape($new_column)], empty to preserve * @param string[] $originals [$original => idf_escape($new_column)], empty to preserve
* @param string[] $foreign [format_foreign_key()], empty to preserve * @param string[] $foreign [format_foreign_key()], empty to preserve
* @param numeric-string $auto_increment set auto_increment to this value, "" to preserve * @param numeric-string|'' $auto_increment set auto_increment to this value, "" to preserve
* @param list<array{string, string, list<string>|'DROP'}> $indexes [[$type, $name, $columns]], empty to preserve * @param list<array{string, string, list<string>|'DROP'}> $indexes [[$type, $name, $columns]], empty to preserve
* @param string $drop_check CHECK constraint to drop * @param string $drop_check CHECK constraint to drop
* @param string $add_check CHECK constraint to add * @param string $add_check CHECK constraint to add
@@ -680,7 +677,7 @@ if (isset($_GET["sqlite"])) {
return "DELETE FROM " . table($table); return "DELETE FROM " . table($table);
} }
function use_sql($database) { function use_sql($database, $style = "") {
} }
function trigger_sql($table) { function trigger_sql($table) {

View File

@@ -4,8 +4,14 @@ namespace Adminer;
$TABLE = $_GET["dump"]; $TABLE = $_GET["dump"];
if ($_POST && !$error) { if ($_POST && !$error) {
$default = array("auto_increment" => '');
foreach (array("type", "routine", "event", "trigger") as $support) {
if (support($support)) {
$default[$support . "s"] = '';
}
}
save_settings( save_settings(
array_intersect_key($_POST, array_flip(array("output", "format", "db_style", "types", "routines", "events", "table_style", "auto_increment", "triggers", "data_style"))), array_intersect_key($_POST + $default, array_flip(array("output", "format", "db_style", "table_style", "data_style")) + $default),
"adminer_export" "adminer_export"
); );
$tables = array_flip((array) $_POST["tables"]) + array_flip((array) $_POST["data"]); $tables = array_flip((array) $_POST["tables"]) + array_flip((array) $_POST["data"]);
@@ -41,16 +47,9 @@ SET foreign_key_checks = 0;
foreach ((array) $databases as $db) { foreach ((array) $databases as $db) {
adminer()->dumpDatabase($db); adminer()->dumpDatabase($db);
if (connection()->select_db($db)) { if (connection()->select_db($db)) {
if ($is_sql && preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE " . idf_escape($db), 1))) {
set_utf8mb4($create);
if ($style == "DROP+CREATE") {
echo "DROP DATABASE IF EXISTS " . idf_escape($db) . ";\n";
}
echo "$create;\n";
}
if ($is_sql) { if ($is_sql) {
if ($style) { if ($style) {
echo use_sql($db) . ";\n\n"; echo use_sql($db, $style) . ";\n\n";
} }
$out = ""; $out = "";
@@ -160,10 +159,6 @@ $row = get_settings("adminer_export");
if (!$row) { if (!$row) {
$row = array("output" => "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT"); $row = array("output" => "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT");
} }
if (!isset($row["events"])) { // backwards compatibility
$row["routines"] = $row["events"] = ($_GET["dump"] == "");
$row["triggers"] = $row["table_style"];
}
echo "<tr><th>" . lang('Output') . "<td>" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n"; echo "<tr><th>" . lang('Output') . "<td>" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n";

View File

@@ -9,7 +9,7 @@ $where = (isset($_GET["select"])
); );
$update = (isset($_GET["select"]) ? $_POST["edit"] : $where); $update = (isset($_GET["select"]) ? $_POST["edit"] : $where);
foreach ($fields as $name => $field) { foreach ($fields as $name => $field) {
if (!isset($field["privileges"][$update ? "update" : "insert"]) || adminer()->fieldName($field) == "" || $field["generated"]) { if ((!$update && !isset($field["privileges"]["insert"])) || adminer()->fieldName($field) == "") {
unset($fields[$name]); unset($fields[$name]);
} }
} }
@@ -65,9 +65,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
} }
$row = null; $row = null;
if ($_POST["save"]) { if ($where) {
$row = (array) $_POST["fields"];
} elseif ($where) {
$select = array(); $select = array();
foreach ($fields as $name => $field) { foreach ($fields as $name => $field) {
if (isset($field["privileges"]["select"])) { if (isset($field["privileges"]["select"])) {
@@ -113,4 +111,8 @@ if (!support("table") && !$fields) { // used by Mongo and SimpleDB
} }
} }
if ($_POST["save"]) {
$row = (array) $_POST["fields"] + ($row ? $row : array());
}
edit_form($TABLE, $fields, $row, $update, $error); edit_form($TABLE, $fields, $row, $update, $error);

View File

@@ -34,7 +34,8 @@ if ($_GET["file"] == "default.css") {
../externals/jush/modules/jush-sqlite.js; ../externals/jush/modules/jush-sqlite.js;
../externals/jush/modules/jush-mssql.js; ../externals/jush/modules/jush-mssql.js;
../externals/jush/modules/jush-oracle.js; ../externals/jush/modules/jush-oracle.js;
../externals/jush/modules/jush-simpledb.js', 'minify_js')); ../externals/jush/modules/jush-simpledb.js;
../externals/jush/modules/jush-igdb.js', 'minify_js'));
} elseif ($_GET["file"] == "logo.png") { } elseif ($_GET["file"] == "logo.png") {
header("Content-Type: image/png"); header("Content-Type: image/png");
echo compile_file('../adminer/static/logo.png'); echo compile_file('../adminer/static/logo.png');

View File

@@ -39,10 +39,10 @@ page_header(lang('Foreign key'), $error, array("table" => $TABLE), h($TABLE));
if ($_POST) { if ($_POST) {
ksort($row["source"]); ksort($row["source"]);
if ($_POST["add"]) { if ($_POST["change"] || $_POST["change-js"]) {
$row["source"][] = "";
} elseif ($_POST["change"] || $_POST["change-js"]) {
$row["target"] = array(); $row["target"] = array();
} else {
$row["source"][] = "";
} }
} elseif ($name != "") { } elseif ($name != "") {
$foreign_keys = foreign_keys($TABLE); $foreign_keys = foreign_keys($TABLE);
@@ -103,10 +103,11 @@ foreach ($row["source"] as $key => $val) {
<p> <p>
<label><?php echo lang('ON DELETE'); ?>: <?php echo html_select("on_delete", array(-1 => "") + explode("|", driver()->onActions), $row["on_delete"]); ?></label> <label><?php echo lang('ON DELETE'); ?>: <?php echo html_select("on_delete", array(-1 => "") + explode("|", driver()->onActions), $row["on_delete"]); ?></label>
<label><?php echo lang('ON UPDATE'); ?>: <?php echo html_select("on_update", array(-1 => "") + explode("|", driver()->onActions), $row["on_update"]); ?></label> <label><?php echo lang('ON UPDATE'); ?>: <?php echo html_select("on_update", array(-1 => "") + explode("|", driver()->onActions), $row["on_update"]); ?></label>
<?php echo (DRIVER === 'pgsql' ? html_select("deferrable", array('NOT DEFERRABLE', 'DEFERRABLE', 'DEFERRABLE INITIALLY DEFERRED'), $row["deferrable"]) . ' ' : ''); ?>
<?php echo doc_link(array( <?php echo doc_link(array(
'sql' => "innodb-foreign-key-constraints.html", 'sql' => "innodb-foreign-key-constraints.html",
'mariadb' => "foreign-keys/", 'mariadb' => "foreign-keys/",
'pgsql' => "sql-createtable.html#SQL-CREATETABLE-REFERENCES", 'pgsql' => "sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES",
'mssql' => "t-sql/statements/create-table-transact-sql", 'mssql' => "t-sql/statements/create-table-transact-sql",
'oracle' => "SQLRF01111", 'oracle' => "SQLRF01111",
)); ?> )); ?>

View File

@@ -85,6 +85,10 @@ class Adminer {
return 2; return 2;
} }
/** Called after connecting and selecting a database */
function afterConnect(): void {
}
/** Headers to send before HTML output */ /** Headers to send before HTML output */
function headers(): void { function headers(): void {
} }
@@ -136,7 +140,7 @@ class Adminer {
echo "<table class='layout'>\n"; echo "<table class='layout'>\n";
// this is matched by compile.php // this is matched by compile.php
echo adminer()->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);")); echo adminer()->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", SqlDriver::$drivers, DRIVER, "loginDriver(this);"));
echo adminer()->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="hostname[:port]" placeholder="localhost" autocapitalize="off">'); echo adminer()->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="' . lang('hostname[:port] or :socket') . '" placeholder="localhost" autocapitalize="off">');
// this is matched by compile.php // this is matched by compile.php
echo adminer()->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" autofocus value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("const authDriver = qs('#username').form['auth[driver]']; authDriver && authDriver.onchange();")); echo adminer()->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" autofocus value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("const authDriver = qs('#username').form['auth[driver]']; authDriver && authDriver.onchange();"));
echo adminer()->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">'); echo adminer()->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">');
@@ -178,7 +182,7 @@ class Adminer {
* @return string HTML code, "" to ignore field * @return string HTML code, "" to ignore field
*/ */
function fieldName(array $field, int $order = 0): string { function fieldName(array $field, int $order = 0): string {
$type = $field["full_type"]; $type = $field["full_type"] . ($field["null"] ? " NULL" : "");
$comment = $field["comment"]; $comment = $field["comment"];
return '<span title="' . h($type . ($comment != "" ? ($type ? ": " : "") . $comment : '')) . '">' . h($field["field"]) . '</span>'; return '<span title="' . h($type . ($comment != "" ? ($type ? ": " : "") . $comment : '')) . '">' . h($field["field"]) . '</span>';
} }
@@ -198,8 +202,10 @@ class Adminer {
if (support("table")) { if (support("table")) {
$is_view = is_view($tableStatus); $is_view = is_view($tableStatus);
if ($is_view) { if ($is_view) {
$links["view"] = lang('Alter view'); if (support("view")) {
} else { $links["view"] = lang('Alter view');
}
} elseif (function_exists('Adminer\alter_table')) {
$links["create"] = lang('Alter table'); $links["create"] = lang('Alter table');
} }
} }
@@ -282,7 +288,7 @@ class Adminer {
/** Get a link to use in select table /** Get a link to use in select table
* @param string $val raw value of the field * @param string $val raw value of the field
* @param Field $field * @param array{type: string} $field
* @return string|void null to create the default link * @return string|void null to create the default link
*/ */
function selectLink(?string $val, array $field) { function selectLink(?string $val, array $field) {
@@ -291,7 +297,7 @@ class Adminer {
/** Value printed in select table /** Value printed in select table
* @param ?string $val HTML-escaped value to print * @param ?string $val HTML-escaped value to print
* @param ?string $link link to foreign key * @param ?string $link link to foreign key
* @param Field $field * @param array{type: string} $field
* @param string $original original value before applying editVal() and escaping * @param string $original original value before applying editVal() and escaping
*/ */
function selectVal(?string $val, ?string $link, array $field, ?string $original): string { function selectVal(?string $val, ?string $link, array $field, ?string $original): string {
@@ -300,14 +306,14 @@ class Adminer {
: (preg_match('~json~', $field["type"]) ? "<code class='jush-js'>$val</code>" : (preg_match('~json~', $field["type"]) ? "<code class='jush-js'>$val</code>"
: $val) : $val)
)); ));
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && !is_utf8($val)) { if (is_blob($field) && !is_utf8($val)) {
$return = "<i>" . lang('%d byte(s)', strlen($original)) . "</i>"; $return = "<i>" . lang('%d byte(s)', strlen($original)) . "</i>";
} }
return ($link ? "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : "") . ">$return</a>" : $return); return ($link ? "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : "") . ">$return</a>" : $return);
} }
/** Value conversion used in select and edit /** Value conversion used in select and edit
* @param Field $field * @param array{type: string} $field
*/ */
function editVal(?string $val, array $field): ?string { function editVal(?string $val, array $field): ?string {
return $val; return $val;
@@ -419,7 +425,7 @@ class Adminer {
echo "<div>(<i>" . implode("</i>, <i>", array_map('Adminer\h', $index["columns"])) . "</i>) AGAINST"; echo "<div>(<i>" . implode("</i>, <i>", array_map('Adminer\h', $index["columns"])) . "</i>) AGAINST";
echo " <input type='search' name='fulltext[$i]' value='" . h(idx($_GET["fulltext"], $i)) . "'>"; echo " <input type='search' name='fulltext[$i]' value='" . h(idx($_GET["fulltext"], $i)) . "'>";
echo script("qsl('input').oninput = selectFieldChange;", ""); echo script("qsl('input').oninput = selectFieldChange;", "");
echo checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL"); echo (JUSH == 'sql' ? checkbox("boolean[$i]", 1, isset($_GET["boolean"][$i]), "BOOL") : '');
echo "</div>\n"; echo "</div>\n";
} }
} }
@@ -608,7 +614,7 @@ class Adminer {
foreach ((array) $_GET["order"] as $key => $val) { foreach ((array) $_GET["order"] as $key => $val) {
if ($val != "") { if ($val != "") {
$return[] = (preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~', $val) ? $val : idf_escape($val)) //! MS SQL uses [] $return[] = (preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~', $val) ? $val : idf_escape($val)) //! MS SQL uses []
. (isset($_GET["desc"][$key]) ? " DESC" : "") . (isset($_GET["desc"][$key]) ? " DESC" . (JUSH == 'pgsql' && idx($fields[$val], "null") ? " NULLS LAST" : "") : "")
; ;
} }
} }
@@ -664,13 +670,13 @@ class Adminer {
} }
$history[$_GET["db"]][] = array($query, time(), $time); // not DB - $_GET["db"] is changed in database.inc.php //! respect $_GET["ns"] $history[$_GET["db"]][] = array($query, time(), $time); // not DB - $_GET["db"] is changed in database.inc.php //! respect $_GET["ns"]
$sql_id = "sql-" . count($history[$_GET["db"]]); $sql_id = "sql-" . count($history[$_GET["db"]]);
$return = "<a href='#$sql_id' class='toggle'>" . lang('SQL command') . "</a>\n"; $return = "<a href='#$sql_id' class='toggle'>" . lang('SQL command') . "</a> <a href='' class='jsonly copy'>🗐</a>\n";
if (!$failed && ($warnings = driver()->warnings())) { if (!$failed && ($warnings = driver()->warnings())) {
$id = "warnings-" . count($history[$_GET["db"]]); $id = "warnings-" . count($history[$_GET["db"]]);
$return = "<a href='#$id' class='toggle'>" . lang('Warnings') . "</a>, $return<div id='$id' class='hidden'>\n$warnings</div>\n"; $return = "<a href='#$id' class='toggle'>" . lang('Warnings') . "</a>, $return<div id='$id' class='hidden'>\n$warnings</div>\n";
} }
return " <span class='time'>" . @date("H:i:s") . "</span>" // @ - time zone may be not set return " <span class='time'>" . @date("H:i:s") . "</span>" // @ - time zone may be not set
. " $return<div id='$sql_id' class='hidden'><pre><code class='jush-" . JUSH . "'>" . shorten_utf8($query, 1000) . "</code></pre>" . " $return<div id='$sql_id' class='hidden'><pre><code class='jush-" . JUSH . "'>" . shorten_utf8($query, 1e4) . "</code></pre>"
. ($time ? " <span class='time'>($time)</span>" : '') . ($time ? " <span class='time'>($time)</span>" : '')
. (support("sql") ? '<p><a href="' . h(str_replace("db=" . urlencode(DB), "db=" . urlencode($_GET["db"]), ME) . 'sql=&history=' . (count($history[$_GET["db"]]) - 1)) . '">' . lang('Edit') . '</a>' : '') . (support("sql") ? '<p><a href="' . h(str_replace("db=" . urlencode(DB), "db=" . urlencode($_GET["db"]), ME) . 'sql=&history=' . (count($history[$_GET["db"]]) - 1)) . '">' . lang('Edit') . '</a>' : '')
. '</div>' . '</div>'
@@ -699,7 +705,7 @@ class Adminer {
} }
} }
} }
if ($key && $functions && !preg_match('~set|blob|bytea|raw|file|bool~', $field["type"])) { if ($key && $functions && !preg_match('~set|bool~', $field["type"]) && !is_blob($field)) {
$return .= "/SQL"; $return .= "/SQL";
} }
} }
@@ -713,13 +719,13 @@ class Adminer {
* @param ?string $table null in call.inc.php * @param ?string $table null in call.inc.php
* @param Field $field * @param Field $field
* @param string $attrs attributes to use inside the tag * @param string $attrs attributes to use inside the tag
* @param string|string[]|false|null $value false means original value
* @return string custom input field or empty string for default * @return string custom input field or empty string for default
*/ */
function editInput(?string $table, array $field, string $attrs, ?string $value): string { function editInput(?string $table, array $field, string $attrs, $value): string {
if ($field["type"] == "enum") { if ($field["type"] == "enum") {
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='-1' checked><i>" . lang('original') . "</i></label> " : "") return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='orig' checked><i>" . lang('original') . "</i></label> " : "")
. ($field["null"] ? "<label><input type='radio'$attrs value=''" . ($value !== null || isset($_GET["select"]) ? "" : " checked") . "><i>NULL</i></label> " : "") . enum_input("radio", $attrs, $field, $value, "NULL")
. enum_input("radio", $attrs, $field, $value, $value === 0 ? 0 : null) // 0 - empty value
; ;
} }
return ""; return "";
@@ -750,7 +756,7 @@ class Adminer {
} elseif (preg_match('~^([+-]|\|\|)$~', $function)) { } elseif (preg_match('~^([+-]|\|\|)$~', $function)) {
$return = idf_escape($name) . " $function $return"; $return = idf_escape($name) . " $function $return";
} elseif (preg_match('~^[+-] interval$~', $function)) { } 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)) { } elseif (preg_match('~^(addtime|subtime|concat)$~', $function)) {
$return = "$function(" . idf_escape($name) . ", $return)"; $return = "$function(" . idf_escape($name) . ", $return)";
} elseif (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) { } elseif (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) {
@@ -839,7 +845,7 @@ class Adminer {
} }
} }
} }
$result = connection()->query($query, 1); // 1 - MYSQLI_USE_RESULT //! enum and set as numbers $result = connection()->query($query, 1); // 1 - MYSQLI_USE_RESULT
if ($result) { if ($result) {
$insert = ""; $insert = "";
$buffer = ""; $buffer = "";
@@ -915,7 +921,7 @@ class Adminer {
* @return string filename without extension * @return string filename without extension
*/ */
function dumpFilename(string $identifier): string { function dumpFilename(string $identifier): string {
return friendly_url($identifier != "" ? $identifier : (SERVER != "" ? SERVER : "localhost")); return friendly_url($identifier != "" ? $identifier : (SERVER ?: "localhost"));
} }
/** Send headers for export /** Send headers for export
@@ -962,6 +968,12 @@ class Adminer {
echo (support("scheme") ? "<a href='" . h(ME) . "scheme='>" . ($_GET["ns"] != "" ? lang('Alter schema') : lang('Create schema')) . "</a>\n" : ""); 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 ($_GET["ns"] !== "" ? '<a href="' . h(ME) . 'schema=">' . lang('Database schema') . "</a>\n" : "");
echo (support("privileges") ? "<a href='" . h(ME) . "privileges='>" . lang('Privileges') . "</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; return true;
} }
@@ -984,7 +996,7 @@ class Adminer {
if ($password !== null) { if ($password !== null) {
$dbs = $_SESSION["db"][$vendor][$server][$username]; $dbs = $_SESSION["db"][$vendor][$server][$username];
foreach (($dbs ? array_keys($dbs) : array("")) as $db) { 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";
} }
} }
} }
@@ -1010,7 +1022,7 @@ class Adminer {
$actions[] = "<a href='" . h(ME) . "dump=" . urlencode(isset($_GET["table"]) ? $_GET["table"] : $_GET["select"]) . "' id='dump'" . bold(isset($_GET["dump"])) . ">" . lang('Export') . "</a>"; $actions[] = "<a href='" . h(ME) . "dump=" . urlencode(isset($_GET["table"]) ? $_GET["table"] : $_GET["select"]) . "' id='dump'" . bold(isset($_GET["dump"])) . ">" . lang('Export') . "</a>";
} }
$in_db = $_GET["ns"] !== "" && !$missing && DB != ""; $in_db = $_GET["ns"] !== "" && !$missing && DB != "";
if ($in_db) { if ($in_db && function_exists('Adminer\alter_table')) {
$actions[] = '<a href="' . h(ME) . 'create="' . bold($_GET["create"] === "") . ">" . lang('Create table') . "</a>"; $actions[] = '<a href="' . h(ME) . 'create="' . bold($_GET["create"] === "") . ">" . lang('Create table') . "</a>";
} }
echo ($actions ? "<p class='links'>\n" . implode("\n", $actions) . "\n" : ""); echo ($actions ? "<p class='links'>\n" . implode("\n", $actions) . "\n" : "");
@@ -1042,7 +1054,15 @@ class Adminer {
foreach ($tables as $table => $type) { foreach ($tables as $table => $type) {
$links[] = preg_quote($table, '/'); $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) { foreach (array("bac", "bra", "sqlite_quo", "mssql_bra") as $val) {
echo "jushLinks.$val = jushLinks." . JUSH . ";\n"; echo "jushLinks.$val = jushLinks." . JUSH . ";\n";
} }
@@ -1100,7 +1120,7 @@ class Adminer {
foreach ($tables as $table => $status) { foreach ($tables as $table => $status) {
$table = "$table"; // do not highlight "0" as active everywhere $table = "$table"; // do not highlight "0" as active everywhere
$name = adminer()->tableName($status); $name = adminer()->tableName($status);
if ($name != "" && !$status["inherited"]) { if ($name != "" && !$status["partition"]) {
echo '<li><a href="' . h(ME) . 'select=' . urlencode($table) . '"' echo '<li><a href="' . h(ME) . 'select=' . urlencode($table) . '"'
. bold($_GET["select"] == $table || $_GET["edit"] == $table, "select") . bold($_GET["select"] == $table || $_GET["edit"] == $table, "select")
. " title='" . lang('Select data') . "'>" . lang('select') . "</a> " . " title='" . lang('Select data') . "'>" . lang('select') . "</a> "
@@ -1115,4 +1135,33 @@ class Adminer {
} }
echo "</ul>\n"; echo "</ul>\n";
} }
/** Get server variables
* @return list<string[]> [[$name, $value]]
*/
function showVariables(): array {
return show_variables();
}
/** Get status variables
* @return list<string[]> [[$name, $value]]
*/
function showStatus(): array {
return show_status();
}
/** Get process list
* @return list<string[]> [$row]
*/
function processList(): array {
return process_list();
}
/** Kill a process
* @param numeric-string $id
* @return Result|bool
*/
function killProcess(string $id) {
return kill_process($id);
}
} }

View File

@@ -172,7 +172,7 @@ if (isset($_GET["username"]) && !class_exists('Adminer\Db')) {
$connection = ''; $connection = '';
if (isset($_GET["username"]) && is_string(get_password())) { if (isset($_GET["username"]) && is_string(get_password())) {
list($host, $port) = explode(":", SERVER, 2); list(, $port) = host_port(SERVER);
if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80 if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80
auth_error(lang('Connecting to privileged ports is not allowed.'), $permanent); auth_error(lang('Connecting to privileged ports is not allowed.'), $permanent);
} }

View File

@@ -29,16 +29,6 @@ if (isset($_GET["file"])) {
include "../adminer/file.inc.php"; include "../adminer/file.inc.php";
} }
if ($_GET["script"] == "version") {
$filename = get_temp_dir() . "/adminer.version";
@unlink($filename); // it may not be writable by us, @ - it may not exist
$fp = file_open_lock($filename);
if ($fp) {
file_write_unlock($fp, serialize(array("signature" => $_POST["signature"], "version" => $_POST["version"])));
}
exit;
}
// Adminer doesn't use any global variables; they used to be declared here // Adminer doesn't use any global variables; they used to be declared here
if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility
@@ -61,7 +51,11 @@ if (!defined("SID")) {
} }
// disable magic quotes to be able to use database escaping function // disable magic quotes to be able to use database escaping function
remove_slashes(array(&$_GET, &$_POST, &$_COOKIE), $filter); if (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) {
$_GET = remove_slashes($_GET, $filter);
$_POST = remove_slashes($_POST, $filter);
$_COOKIE = remove_slashes($_COOKIE, $filter);
}
if (function_exists("get_magic_quotes_runtime") && get_magic_quotes_runtime()) { if (function_exists("get_magic_quotes_runtime") && get_magic_quotes_runtime()) {
set_magic_quotes_runtime(false); set_magic_quotes_runtime(false);
} }
@@ -91,8 +85,8 @@ Adminer::$instance =
include "../adminer/drivers/mysql.inc.php"; // must be included as last driver include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
define('Adminer\JUSH', Driver::$jush); define('Adminer\JUSH', Driver::$jush);
define('Adminer\SERVER', $_GET[DRIVER]); // read from pgsql=localhost, '' means default server, null means no server define('Adminer\SERVER', "" . $_GET[DRIVER]); // read from pgsql=localhost, '' means default server
define('Adminer\DB', $_GET["db"]); // for the sake of speed and size define('Adminer\DB', "$_GET[db]"); // for the sake of speed and size
define( define(
'Adminer\ME', 'Adminer\ME',
preg_replace('~\?.*~', '', relative_uri()) . '?' preg_replace('~\?.*~', '', relative_uri()) . '?'
@@ -108,3 +102,5 @@ include "../adminer/include/xxtea.inc.php";
include "../adminer/include/auth.inc.php"; include "../adminer/include/auth.inc.php";
include "./include/editing.inc.php"; include "./include/editing.inc.php";
include "./include/connect.inc.php"; include "./include/connect.inc.php";
adminer()->afterConnect();

View File

@@ -18,7 +18,7 @@ abstract class SqlDb {
/** Connect to server /** Connect to server
* @return string error message * @return string error message
*/ */
abstract function attach(?string $server, string $username, string $password): string; abstract function attach(string $server, string $username, string $password): string;
/** Quote string to use in SQL /** Quote string to use in SQL
* @return string escaped string enclosed in ' * @return string escaped string enclosed in '

View File

@@ -62,24 +62,8 @@ function page_header(string $title, string $error = "", $breadcrumb = array(), s
adminer()->bodyClass(); adminer()->bodyClass();
echo "'>\n"; echo "'>\n";
$filename = get_temp_dir() . "/adminer.version"; $filename = get_temp_dir() . "/adminer.version";
if (!$_COOKIE["adminer_version"] && function_exists('openssl_verify') && file_exists($filename) && filemtime($filename) + 86400 > time()) { // 86400 - 1 day in seconds
$version = unserialize(file_get_contents($filename));
$public = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwqWOVuF5uw7/+Z70djoK
RlHIZFZPO0uYRezq90+7Amk+FDNd7KkL5eDve+vHRJBLAszF/7XKXe11xwliIsFs
DFWQlsABVZB3oisKCBEuI71J4kPH8dKGEWR9jDHFw3cWmoH3PmqImX6FISWbG3B8
h7FIx3jEaw5ckVPVTeo5JRm/1DZzJxjyDenXvBQ/6o9DgZKeNDgxwKzH+sw9/YCO
jHnq1cFpOIISzARlrHMa/43YfeNRAm/tsBXjSxembBPo7aQZLAWHmaj5+K19H10B
nCpz9Y++cipkVEiKRGih4ZEvjoFysEOdRLj6WiD/uUNky4xGeA6LaJqh5XpkFkcQ
fQIDAQAB
-----END PUBLIC KEY-----
";
if (openssl_verify($version["version"], base64_decode($version["signature"]), $public) == 1) {
$_COOKIE["adminer_version"] = $version["version"]; // doesn't need to send to the browser
}
}
echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick" echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick"
. (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '" . VERSION . "', '" . js_escape(ME) . "', '" . get_token() . "')") . (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '" . VERSION . "')")
. "}); . "});
document.body.classList.replace('nojs', 'js'); document.body.classList.replace('nojs', 'js');
const offlineMessage = '" . js_escape(lang('You are offline.')) . "'; const offlineMessage = '" . js_escape(lang('You are offline.')) . "';
@@ -153,8 +137,8 @@ function csp(): array {
return array( return array(
array( array(
"script-src" => "'self' 'unsafe-inline' 'nonce-" . get_nonce() . "' 'strict-dynamic'", // 'self' is a fallback for browsers not supporting 'strict-dynamic', 'unsafe-inline' is a fallback for browsers not supporting 'nonce-' "script-src" => "'self' 'unsafe-inline' 'nonce-" . get_nonce() . "' 'strict-dynamic'", // 'self' is a fallback for browsers not supporting 'strict-dynamic', 'unsafe-inline' is a fallback for browsers not supporting 'nonce-'
"connect-src" => "'self'", "connect-src" => "'self' https://www.adminer.org",
"frame-src" => "https://www.adminer.org", "frame-src" => "'none'",
"object-src" => "'none'", "object-src" => "'none'",
"base-uri" => "'none'", "base-uri" => "'none'",
"form-action" => "'self'", "form-action" => "'self'",

View File

@@ -19,6 +19,7 @@ abstract class SqlDriver {
/** @var Db */ protected $conn; /** @var Db */ protected $conn;
/** @var int[][] */ protected $types = array(); // [$group => [$type => $maximum_unsigned_length, ...], ...] /** @var int[][] */ protected $types = array(); // [$group => [$type => $maximum_unsigned_length, ...], ...]
/** @var string */ public $delimiter = ";";
/** @var string[] */ public $insertFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit and insert /** @var string[] */ public $insertFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit and insert
/** @var string[] */ public $editFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit only /** @var string[] */ public $editFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit only
/** @var list<string> */ public $unsigned = array(); // number variants /** @var list<string> */ public $unsigned = array(); // number variants
@@ -34,7 +35,7 @@ abstract class SqlDriver {
/** Connect to the database /** Connect to the database
* @return Db|string string for error * @return Db|string string for error
*/ */
static function connect(?string $server, string $username, string $password) { static function connect(string $server, string $username, string $password) {
$connection = new Db; $connection = new Db;
return ($connection->attach($server, $username, $password) ?: $connection); return ($connection->attach($server, $username, $password) ?: $connection);
} }
@@ -189,13 +190,8 @@ abstract class SqlDriver {
return $idf; 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 /** Convert value returned by database to actual value
* @param Field $field * @param array{type: string} $field
*/ */
function value(?string $val, array $field): ?string { function value(?string $val, array $field): ?string {
return (method_exists($this->conn, 'value') ? $this->conn->value($val, $field) : $val); return (method_exists($this->conn, 'value') ? $this->conn->value($val, $field) : $val);
@@ -219,14 +215,14 @@ abstract class SqlDriver {
} }
/** Get tables this table inherits from /** Get tables this table inherits from
* @return list<string> * @return list<array{table: string, ns: string}>
*/ */
function inheritsFrom(string $table): array { function inheritsFrom(string $table): array {
return array(); return array();
} }
/** Get inherited tables /** Get inherited tables
* @return list<string> * @return list<array{table: string, ns: string}>
*/ */
function inheritedTables(string $table): array { function inheritedTables(string $table): array {
return array(); return array();
@@ -258,8 +254,7 @@ abstract class SqlDriver {
return !is_view($table_status); return !is_view($table_status);
} }
/** /** Return list of supported index algorithms, first one is default
* Return list of supported index algorithms, first one is default
* @param TableStatus $tableStatus * @param TableStatus $tableStatus
* @return list<string> * @return list<string>
*/ */
@@ -274,14 +269,14 @@ abstract class SqlDriver {
// MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not // MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not
return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE
FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME" . ($this->conn->flavor == 'maria' ? " AND c.TABLE_NAME = t.TABLE_NAME" : "") . "
WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . " WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . "
AND t.TABLE_NAME = " . q($table) . " AND t.TABLE_NAME = " . q($table) . (JUSH == "pgsql" ? "
AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'", $this->conn); // ignore default IS NOT NULL checks in PostrgreSQL AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'" : ""), $this->conn); // ignore default IS NOT NULL checks in PostrgreSQL
} }
/** Get all fields in the current schema /** Get all fields in the current schema
* @return array<list<array{field:string, null:bool, type:string, length:?numeric-string, primary?:numeric-string}>> * @return array<list<array{field:string, null:bool, type:string, length:?numeric-string}>> optionally also 'primary'
*/ */
function allFields(): array { function allFields(): array {
$return = array(); $return = array();

View File

@@ -72,25 +72,18 @@ function print_select_result($result, ?Db $connection2 = null, array $orgtables
} else { } else {
$link = ME . "edit=" . urlencode($links[$key]); $link = ME . "edit=" . urlencode($links[$key]);
foreach ($indexes[$links[$key]] as $col => $j) { foreach ($indexes[$links[$key]] as $col => $j) {
if ($row[$j] === null) {
$link = "";
break;
}
$link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]); $link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]);
} }
} }
} elseif (is_url($val)) {
$link = $val;
}
if ($val === null) {
$val = "<i>NULL</i>";
} elseif ($blobs[$key] && !is_utf8($val)) {
$val = "<i>" . lang('%d byte(s)', strlen($val)) . "</i>"; //! link to download
} else {
$val = h($val);
if ($types[$key] == 254) { // 254 - char
$val = "<code>$val</code>";
}
}
if ($link) {
$val = "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : '') . ">$val</a>";
} }
$field = array(
'type' => ($blobs[$key] ? 'blob' : ($types[$key] == 254 ? 'char' : '')),
);
$val = select_value($val, $link, $field, null);
// https://dev.mysql.com/doc/dev/mysql-server/latest/field__types_8h.html // https://dev.mysql.com/doc/dev/mysql-server/latest/field__types_8h.html
echo "<td" . ($types[$key] <= 9 || $types[$key] == 246 ? " class='number'" : "") . ">$val"; echo "<td" . ($types[$key] <= 9 || $types[$key] == 246 ? " class='number'" : "") . ">$val";
} }
@@ -150,13 +143,13 @@ function select_input(string $attrs, array $options, ?string $value = "", string
* @param string $key or "" to close the object * @param string $key or "" to close the object
* @param string|int $val * @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; static $first = true;
if ($first) { if ($first) {
echo "{"; echo "{";
} }
if ($key != "") { 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; $first = false;
} else { } else {
echo "\n}\n"; echo "\n}\n";
@@ -246,15 +239,18 @@ function process_field(array $field, array $type_field): array {
* @param Field $field * @param Field $field
*/ */
function default_value(array $field): string { function default_value(array $field): string {
$default = $field["default"]; if ($field["default"] === null) {
return "";
}
$default = str_replace("\r", "", $field["default"]);
$generated = $field["generated"]; $generated = $field["generated"];
return ($default === null ? "" : (in_array($generated, driver()->generated) return (in_array($generated, driver()->generated)
? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") . "" : " GENERATED ALWAYS AS ($default) $generated") ? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") : " GENERATED ALWAYS AS ($default) $generated")
: " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default)) : " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default))
? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column ? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column
: str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", (JUSH == "sqlite" ? "($default)" : $default)) : str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", (JUSH == "sqlite" ? "($default)" : $default))
) )
)); );
} }
/** Get type class to use in CSS /** Get type class to use in CSS
@@ -314,7 +310,7 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", array $f
echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n"; echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n";
echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", driver()->inout), $field["inout"]) : "") . "<th>"; echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", driver()->inout), $field["inout"]) : "") . "<th>";
if ($display) { if ($display) {
echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'>"; echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'" . (isset($_POST["add"][$i-1]) ? " autofocus" : "") . ">";
} }
echo input_hidden("fields[$i][orig]", $orig); echo input_hidden("fields[$i][orig]", $orig);
edit_type("fields[$i]", $field, $collations, $foreign_keys); edit_type("fields[$i]", $field, $collations, $foreign_keys);
@@ -325,7 +321,9 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", array $f
? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), driver()->generated), $field["generated"]) . " " ? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), driver()->generated), $field["generated"]) . " "
: checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default") : checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default")
); );
echo "<input name='fields[$i][default]' value='" . h($field["default"]) . "' aria-labelledby='label-default'>"; $attrs = " name='fields[$i][default]' aria-labelledby='label-default'";
$value = h($field["default"]);
echo (preg_match('~\n~', $field["default"]) ? "<textarea$attrs rows='2' cols='30' style='vertical-align: bottom;'>\n$value</textarea>" : "<input$attrs value='$value'>"); // \n to preserve the leading newline
echo (support("comment") ? "<td$comment_class><input name='fields[$i][comment]' value='" . h($field["comment"]) . "' data-maxlength='" . (min_version(5.5) ? 1024 : 255) . "' aria-labelledby='label-comment'>" : ""); echo (support("comment") ? "<td$comment_class><input name='fields[$i][comment]' value='" . h($field["comment"]) . "' data-maxlength='" . (min_version(5.5) ? 1024 : 255) . "' aria-labelledby='label-comment'>" : "");
} }
echo "<td>"; echo "<td>";
@@ -487,6 +485,7 @@ function format_foreign_key(array $foreign_key): string {
. " (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions . " (" . 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_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_update"]) ? " ON UPDATE $foreign_key[on_update]" : "")
. ($foreign_key["deferrable"] ? " $foreign_key[deferrable]" : "")
; ;
} }
@@ -507,20 +506,6 @@ function tar_file(string $filename, $tmp_file): void {
echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512); echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512);
} }
/** Get INI bytes value */
function ini_bytes(string $ini): int {
$val = ini_get($ini);
switch (strtolower(substr($val, -1))) {
case 'g':
$val = (int) $val * 1024; // no break
case 'm':
$val = (int) $val * 1024; // no break
case 'k':
$val = (int) $val * 1024;
}
return $val;
}
/** Create link to database documentation /** Create link to database documentation
* @param string[] $paths JUSH => $path * @param string[] $paths JUSH => $path
* @param string $text HTML code * @param string $text HTML code

View File

@@ -76,24 +76,19 @@ function number_type(): string {
} }
/** Disable magic_quotes_gpc /** Disable magic_quotes_gpc
* @param list<array> $process e.g. [&$_GET, &$_POST, &$_COOKIE] * @param mixed[] $values
* @param bool $filter whether to leave values as is * @param bool $filter whether to leave values as is
* @return void modified in place * @return mixed[]
*/ */
function remove_slashes(array $process, bool $filter = false): void { function remove_slashes(array $values, bool $filter = false): array {
if (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) { $return = array();
while (list($key, $val) = each($process)) { foreach ($values as $key => $val) {
foreach ($val as $k => $v) { $return[stripslashes($key)] = (is_array($val)
unset($process[$key][$k]); ? remove_slashes($val, $filter)
if (is_array($v)) { : ($filter ? $val : stripslashes($val))
$process[$key][stripslashes($k)] = $v; );
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = ($filter ? $v : stripslashes($v));
}
}
}
} }
return $return;
} }
/** Escape or unescape string to use inside form [] */ /** Escape or unescape string to use inside form [] */
@@ -128,6 +123,20 @@ function ini_bool(string $ini): bool {
return (preg_match('~^(on|true|yes)$~i', $val) || (int) $val); // boolean values set by php_value are strings return (preg_match('~^(on|true|yes)$~i', $val) || (int) $val); // boolean values set by php_value are strings
} }
/** Get INI bytes value */
function ini_bytes(string $ini): int {
$val = ini_get($ini);
switch (strtolower(substr($val, -1))) {
case 'g':
$val = (int) $val * 1024; // no break
case 'm':
$val = (int) $val * 1024; // no break
case 'k':
$val = (int) $val * 1024;
}
return $val;
}
/** Check if SID is necessary */ /** Check if SID is necessary */
function sid(): bool { function sid(): bool {
static $return; static $return;
@@ -230,7 +239,7 @@ function get_rows(string $query, ?Db $connection2 = null, string $error = "<p cl
*/ */
function unique_array(?array $row, array $indexes) { function unique_array(?array $row, array $indexes) {
foreach ($indexes as $index) { foreach ($indexes as $index) {
if (preg_match("~PRIMARY|UNIQUE~", $index["type"])) { if (preg_match("~PRIMARY|UNIQUE~", $index["type"]) && !$index["partial"]) {
$return = array(); $return = array();
foreach ($index["columns"] as $key) { foreach ($index["columns"] as $key) {
if (!isset($row[$key])) { // NULL is ambiguous if (!isset($row[$key])) { // NULL is ambiguous
@@ -264,9 +273,10 @@ function where(array $where, array $fields = array()): string {
$field_type = $field["type"]; $field_type = $field["type"];
$return[] = $column $return[] = $column
. (JUSH == "sql" && $field_type == "json" ? " = CAST(" . q($val) . " AS JSON)" . (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 == "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 : (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 ; //! enum and set
if (JUSH == "sql" && preg_match('~char|text~', $field_type) && preg_match("~[^ -@]~", $val)) { // not just [a-z] to catch non-ASCII characters 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"; $return[] = "$column = " . q($val) . " COLLATE " . charset(connection()) . "_bin";
@@ -291,7 +301,7 @@ function where_check(string $val, array $fields = array()): string {
* @param int $i condition order * @param int $i condition order
* @param string $column column identifier * @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); return "&where%5B$i%5D%5Bcol%5D=" . urlencode($column) . "&where%5B$i%5D%5Bop%5D=" . urlencode(($value !== null ? $operator : "IS NULL")) . "&where%5B$i%5D%5Bval%5D=" . urlencode($value);
} }
@@ -319,7 +329,7 @@ function convert_fields(array $columns, array $fields, array $select = array()):
*/ */
function cookie(string $name, ?string $value, int $lifetime = 2592000): void { function cookie(string $name, ?string $value, int $lifetime = 2592000): void {
header( header(
"Set-Cookie: $name=" . urlencode($value) "Set-Cookie: $name=" . rawurlencode($value)
. ($lifetime ? "; expires=" . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT" : "") . ($lifetime ? "; expires=" . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT" : "")
. "; path=" . preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]) . "; path=" . preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"])
. (HTTPS ? "; secure" : "") . (HTTPS ? "; secure" : "")
@@ -337,11 +347,11 @@ function get_settings(string $cookie): array {
} }
/** Get setting stored in a cookie /** Get setting stored in a cookie
* @param mixed $default
* @return mixed * @return mixed
*/ */
function get_setting(string $key, string $cookie = "adminer_settings") { function get_setting(string $key, string $cookie = "adminer_settings", $default = null) {
$settings = get_settings($cookie); return idx(get_settings($cookie), $key, $default);
return $settings[$key];
} }
/** Store settings to a cookie /** Store settings to a cookie
@@ -460,7 +470,7 @@ function queries(string $query) {
if (!Queries::$start) { if (!Queries::$start) {
Queries::$start = microtime(true); Queries::$start = microtime(true);
} }
Queries::$queries[] = (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";"; Queries::$queries[] = (driver()->delimiter != ';' ? $query : (preg_match('~;$~', $query) ? "DELIMITER ;;\n$query;\nDELIMITER " : $query) . ";");
return connection()->query($query); return connection()->query($query);
} }
@@ -641,12 +651,13 @@ function dump_headers(string $identifier, bool $multi_table = false): string {
* @param string[] $row * @param string[] $row
*/ */
function dump_csv(array $row): void { function dump_csv(array $row): void {
$tsv = $_POST["format"] == "tsv";
foreach ($row as $key => $val) { foreach ($row as $key => $val) {
if (preg_match('~["\n,;\t]|^0|\.\d*0$~', $val) || $val === "") { if (preg_match('~["\n]|^0[^.]|\.\d*0$|' . ($tsv ? '\t' : '[,;]|^$') . '~', $val)) {
$row[$key] = '"' . str_replace('"', '""', $val) . '"'; $row[$key] = '"' . str_replace('"', '""', $val) . '"';
} }
} }
echo implode(($_POST["format"] == "csv" ? "," : ($_POST["format"] == "tsv" ? "\t" : ";")), $row) . "\r\n"; echo implode(($_POST["format"] == "csv" ? "," : ($tsv ? "\t" : ";")), $row) . "\r\n";
} }
/** Apply SQL function /** Apply SQL function
@@ -685,7 +696,7 @@ function file_open_lock(string $filename) {
if (!$fp) { if (!$fp) {
return; return;
} }
chmod($filename, 0660); @chmod($filename, 0660); // @ - may not be permitted
if (!flock($fp, LOCK_EX)) { if (!flock($fp, LOCK_EX)) {
fclose($fp); fclose($fp);
return; return;
@@ -750,19 +761,35 @@ function rand_string(): string {
} }
/** Format value to use in select /** Format value to use in select
* @param string|string[] $val * @param string|string[]|list<string[]> $val
* @param Field $field * @param array{type: string} $field
* @param ?numeric-string $text_length * @param ?numeric-string $text_length
* @return string HTML * @return string HTML
*/ */
function select_value($val, string $link, array $field, ?string $text_length): string { function select_value($val, string $link, array $field, ?string $text_length): string {
if (is_array($val)) { if (is_array($val)) {
$return = ""; $return = "";
foreach ($val as $k => $v) { if (array_filter($val, 'is_array') == array_values($val)) { // list of arrays
$return .= "<tr>" $keys = array();
. ($val != array_values($val) ? "<th>" . h($k) : "") foreach ($val as $v) {
. "<td>" . select_value($v, $link, $field, $text_length) $keys += array_fill_keys(array_keys($v), null);
; }
foreach (array_keys($keys) as $k) {
$return .= "<th>" . h($k);
}
foreach ($val as $v) {
$return .= "<tr>";
foreach (array_merge($keys, $v) as $v2) {
$return .= "<td>" . select_value($v2, $link, $field, $text_length);
}
}
} else {
foreach ($val as $k => $v) {
$return .= "<tr>"
. ($val != array_values($val) ? "<th>" . h($k) : "")
. "<td>" . select_value($v, $link, $field, $text_length)
;
}
} }
return "<table>$return</table>"; return "<table>$return</table>";
} }
@@ -777,7 +804,7 @@ function select_value($val, string $link, array $field, ?string $text_length): s
$link = $val; // IE 11 and all modern browsers hide referrer $link = $val; // IE 11 and all modern browsers hide referrer
} }
} }
$return = adminer()->editVal($val, $field); $return = adminer()->editVal(driver()->value($val, $field), $field);
if ($return !== null) { if ($return !== null) {
if (!is_utf8($return)) { if (!is_utf8($return)) {
$return = "\0"; // htmlspecialchars of binary data returns an empty string $return = "\0"; // htmlspecialchars of binary data returns an empty string
@@ -790,6 +817,13 @@ function select_value($val, string $link, array $field, ?string $text_length): s
return adminer()->selectVal($return, $link, $field, $val); return adminer()->selectVal($return, $link, $field, $val);
} }
/** Check whether the field type is blob or equivalent
* @param array{type: string} $field
*/
function is_blob(array $field): bool {
return preg_match('~blob|bytea|raw|file~', $field["type"]) && !in_array($field["type"], idx(driver()->structuredTypes(), lang('User types'), array()));
}
/** Check whether the string is e-mail address */ /** Check whether the string is e-mail address */
function is_mail(?string $email): bool { function is_mail(?string $email): bool {
$atom = '[-a-z0-9!#$%&\'*+/=?^_`{|}~]'; // characters of local-name $atom = '[-a-z0-9!#$%&\'*+/=?^_`{|}~]'; // characters of local-name
@@ -801,14 +835,24 @@ function is_mail(?string $email): bool {
/** Check whether the string is URL address */ /** Check whether the string is URL address */
function is_url(?string $string): bool { function is_url(?string $string): bool {
$domain = '[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])'; // one domain component //! IDN $domain = '[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])'; // one domain component //! IDN
return preg_match("~^(https?)://($domain?\\.)+$domain(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i", $string); //! restrict path, query and fragment characters return preg_match("~^((https?):)?//($domain?\\.)+$domain(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i", $string); //! restrict path, query and fragment characters
} }
/** Check if field should be shortened /** Check if field should be shortened
* @param Field $field * @param array{type: string} $field
*/ */
function is_shortable(array $field): bool { function is_shortable(array $field): bool {
return preg_match('~char|text|json|lob|geometry|point|linestring|polygon|string|bytea~', $field["type"]); return !preg_match('~' . number_type() . '|date|time|year~', $field["type"]);
}
/** Split server into host and (port or socket)
* @return array{0: string, 1: string}
*/
function host_port(string $server) {
return (preg_match('~^(\[(.+)]|([^:]+)):([^:]+)$~', $server, $match) // [a:b] - IPv6
? array($match[2] . $match[3], $match[4])
: array($server, '')
);
} }
/** Get query to compute number of found rows /** Get query to compute number of found rows

View File

@@ -176,18 +176,34 @@ function hidden_fields_get(): void {
echo input_hidden("username", $_GET["username"]); echo input_hidden("username", $_GET["username"]);
} }
/** Get <input type='file'> */
function file_input(string $input): string {
$max_file_uploads = "max_file_uploads";
$max_file_uploads_value = ini_get($max_file_uploads);
$upload_max_filesize = "upload_max_filesize";
$upload_max_filesize_value = ini_get($upload_max_filesize);
return (ini_bool("file_uploads")
? $input . script("qsl('input[type=\"file\"]').onchange = partialArg(fileChange, "
. "$max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "', " // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
. ini_bytes("upload_max_filesize") . ", '" . lang('Increase %s.', "$upload_max_filesize = $upload_max_filesize_value") . "')")
: lang('File uploads are disabled.')
);
}
/** Print enum or set input field /** Print enum or set input field
* @param 'radio'|'checkbox' $type * @param 'radio'|'checkbox' $type
* @param Field $field * @param Field $field
* @param mixed $value string|array * @param string|string[]|false|null $value false means original value
*/ */
function enum_input(string $type, string $attrs, array $field, $value, ?string $empty = null): string { function enum_input(string $type, string $attrs, array $field, $value, string $empty = ""): string {
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches); preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === $empty) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : ""); $prefix = ($field["type"] == "enum" ? "val-" : "");
foreach ($matches[1] as $i => $val) { $checked = (is_array($value) ? in_array("null", $value) : $value === null);
$return = ($field["null"] && $prefix ? "<label><input type='$type'$attrs value='null'" . ($checked ? " checked" : "") . "><i>$empty</i></label>" : "");
foreach ($matches[1] as $val) {
$val = stripcslashes(str_replace("''", "'", $val)); $val = stripcslashes(str_replace("''", "'", $val));
$checked = (is_array($value) ? in_array($val, $value) : $value === $val); $checked = (is_array($value) ? in_array($prefix . $val, $value) : $value === $val);
$return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h(adminer()->editVal($val, $field)) . '</label>'; $return .= " <label><input type='$type'$attrs value='" . h($prefix . $val) . "'" . ($checked ? ' checked' : '') . '>' . h(adminer()->editVal($val, $field)) . '</label>';
} }
return $return; return $return;
} }
@@ -200,21 +216,23 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
$name = h(bracket_escape($field["field"])); $name = h(bracket_escape($field["field"]));
echo "<td class='function'>"; echo "<td class='function'>";
if (is_array($value) && !$function) { if (is_array($value) && !$function) {
$value = json_encode($value, 128 | 64 | 256); // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4
$function = "json"; $function = "json";
} }
$json = ($function == "json" || preg_match('~^jsonb?$~', $field["type"]));
if ($json && $value != '' && (JUSH != "pgsql" || $field["type"] != "json")) {
$value = json_encode(is_array($value) ? $value : json_decode($value), 128 | 64 | 256); // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4
}
$reset = (JUSH == "mssql" && $field["auto_increment"]); $reset = (JUSH == "mssql" && $field["auto_increment"]);
if ($reset && !$_POST["save"]) { if ($reset && !$_POST["save"]) {
$function = null; $function = null;
} }
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + adminer()->editFunctions($field); $functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + adminer()->editFunctions($field);
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
$attrs = " name='fields[$name]'$disabled" . ($autofocus ? " autofocus" : "");
$enums = driver()->enumLength($field); $enums = driver()->enumLength($field);
if ($enums) { if ($enums) {
$field["type"] = "enum"; $field["type"] = "enum";
$field["length"] = $enums; $field["length"] = $enums;
} }
$attrs = " name='fields[$name]" . ($field["type"] == "enum" || $field["type"] == "set" ? "[]" : "") . "'" . ($autofocus ? " autofocus" : "");
echo driver()->unconvertFunction($field) . " "; echo driver()->unconvertFunction($field) . " ";
$table = $_GET["edit"] ?: $_GET["select"]; $table = $_GET["edit"] ?: $_GET["select"];
if ($field["type"] == "enum") { if ($field["type"] == "enum") {
@@ -222,7 +240,7 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
} else { } else {
$has_function = (in_array($function, $functions) || isset($functions[$function])); $has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1 echo (count($functions) > 1
? "<select name='function[$name]'$disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>" ? "<select name='function[$name]'>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
. on_help("event.target.value.replace(/^SQL\$/, '')", 1) . on_help("event.target.value.replace(/^SQL\$/, '')", 1)
. script("qsl('select').onchange = functionChange;", "") . script("qsl('select').onchange = functionChange;", "")
: h(reset($functions)) : h(reset($functions))
@@ -234,15 +252,10 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
echo "<input type='hidden'$attrs value='0'>" echo "<input type='hidden'$attrs value='0'>"
. "<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>"; . "<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>";
} elseif ($field["type"] == "set") { } elseif ($field["type"] == "set") {
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches); echo enum_input("checkbox", $attrs, $field, (is_string($value) ? explode(",", $value) : $value));
foreach ($matches[1] as $i => $val) { } elseif (is_blob($field) && ini_bool("file_uploads")) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = in_array($val, explode(",", $value), true);
echo " <label><input type='checkbox' name='fields[$name][$i]' value='" . h($val) . "'" . ($checked ? ' checked' : '') . ">" . h(adminer()->editVal($val, $field)) . '</label>';
}
} elseif (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
echo "<input type='file' name='fields-$name'>"; echo "<input type='file' name='fields-$name'>";
} elseif ($function == "json" || preg_match('~^jsonb?$~', $field["type"])) { } elseif ($json) {
echo "<textarea$attrs cols='50' rows='12' class='jush-js'>" . h($value) . '</textarea>'; echo "<textarea$attrs cols='50' rows='12' class='jush-js'>" . h($value) . '</textarea>';
} elseif (($text = preg_match('~text|lob|memo~i', $field["type"])) || preg_match("~\n~", $value)) { } elseif (($text = preg_match('~text|lob|memo~i', $field["type"])) || preg_match("~\n~", $value)) {
if ($text && JUSH != "sqlite") { if ($text && JUSH != "sqlite") {
@@ -290,19 +303,21 @@ function input(array $field, $value, ?string $function, ?bool $autofocus = false
* @return mixed false to leave the original value * @return mixed false to leave the original value
*/ */
function process_input(array $field) { function process_input(array $field) {
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
return;
}
$idf = bracket_escape($field["field"]); $idf = bracket_escape($field["field"]);
$function = idx($_POST["function"], $idf); $function = idx($_POST["function"], $idf);
$value = $_POST["fields"][$idf]; $value = idx($_POST["fields"], $idf);
if ($value === null) {
return false;
}
if ($field["type"] == "enum" || driver()->enumLength($field)) { if ($field["type"] == "enum" || driver()->enumLength($field)) {
if ($value == -1) { $value = idx($value, 0);
if ($value == "orig" || !$value) {
return false; return false;
} }
if ($value == "") { if ($value == "null") {
return "NULL"; return "NULL";
} }
$value = substr($value, 4); // 4 - strlen("val-")
} }
if ($field["auto_increment"] && $value == "") { if ($field["auto_increment"] && $value == "") {
return null; return null;
@@ -324,7 +339,7 @@ function process_input(array $field) {
} }
return $value; return $value;
} }
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) { if (is_blob($field) && ini_bool("file_uploads")) {
$file = get_file("fields-$idf"); $file = get_file("fields-$idf");
if (!is_string($file)) { if (!is_string($file)) {
return false; //! report errors return false; //! report errors
@@ -381,6 +396,7 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
return; return;
} }
echo "<form action='' method='post' enctype='multipart/form-data' id='form'>\n"; echo "<form action='' method='post' enctype='multipart/form-data' id='form'>\n";
$editable = false;
if (!$fields) { if (!$fields) {
echo "<p class='error'>" . lang('You have no privileges to update this table.') . "\n"; echo "<p class='error'>" . lang('You have no privileges to update this table.') . "\n";
} else { } else {
@@ -411,30 +427,35 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
if (!$_POST["save"] && is_string($value)) { if (!$_POST["save"] && is_string($value)) {
$value = adminer()->editVal($value, $field); $value = adminer()->editVal($value, $field);
} }
$function = ($_POST["save"] if (($update && !isset($field["privileges"]["update"])) || $field["generated"]) {
? idx($_POST["function"], $name, "") echo "<td class='function'><td>" . select_value($value, '', $field, null);
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) } else {
? "now" $editable = true;
: ($value === false ? null : ($value !== null ? '' : 'NULL')) $function = ($_POST["save"]
) ? idx($_POST["function"], $name, "")
); : ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) { ? "now"
$function = "SQL"; : ($value === false ? null : ($value !== null ? '' : 'NULL'))
} )
if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) { );
$value = ""; if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) {
$function = "now"; $function = "SQL";
} }
if ($field["type"] == "uuid" && $value == "uuid()") { if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) {
$value = ""; $value = "";
$function = "uuid"; $function = "now";
} }
if ($autofocus !== false) { if ($field["type"] == "uuid" && $value == "uuid()") {
$autofocus = ($field["auto_increment"] || $function == "now" || $function == "uuid" ? null : true); // null - don't autofocus this input but check the next one $value = "";
} $function = "uuid";
input($field, $value, $function, $autofocus); }
if ($autofocus) { if ($autofocus !== false) {
$autofocus = false; $autofocus = ($field["auto_increment"] || $function == "now" || $function == "uuid" ? null : true); // null - don't autofocus this input but check the next one
}
input($field, $value, $function, $autofocus);
if ($autofocus) {
$autofocus = false;
}
} }
echo "\n"; echo "\n";
} }
@@ -450,7 +471,7 @@ function edit_form(string $table, array $fields, $row, ?bool $update, string $er
echo "</table>\n"; echo "</table>\n";
} }
echo "<p>\n"; echo "<p>\n";
if ($fields) { if ($editable) {
echo "<input type='submit' value='" . lang('Save') . "'>\n"; echo "<input type='submit' value='" . lang('Save') . "'>\n";
if (!isset($_GET["select"])) { if (!isset($_GET["select"])) {
echo "<input type='submit' name='insert' value='" . ($update echo "<input type='submit' name='insert' value='" . ($update

View File

@@ -26,7 +26,7 @@ function lang_format($translation, $number = null): string {
: (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other : (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other
: (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other : (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other
: (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0 : (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0
: (in_array(LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other : (in_array(LANG, array('bs', 'hr', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other
: 1)))))))) // different forms for 1, other : 1)))))))) // different forms for 1, other
; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html ; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
$translation = $translation[$pos]; $translation = $translation[$pos];
@@ -67,6 +67,7 @@ function langs(): array {
'gl' => 'Galego', // Eduardo Penabad Ramos 'gl' => 'Galego', // Eduardo Penabad Ramos
'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/ 'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/
'hi' => 'हिन्दी', // Joshi yogesh 'hi' => 'हिन्दी', // Joshi yogesh
'hr' => 'Hrvatski', // Nikola Paradžik
'hu' => 'Magyar', // Borsos Szilárd (Borsosfi) - http://www.borsosfi.hu, info@borsosfi.hu 'hu' => 'Magyar', // Borsos Szilárd (Borsosfi) - http://www.borsosfi.hu, info@borsosfi.hu
'id' => 'Bahasa Indonesia', // Ivan Lanin - http://ivan.lanin.org 'id' => 'Bahasa Indonesia', // Ivan Lanin - http://ivan.lanin.org
'it' => 'Italiano', // Alessandro Fiorotto, Paolo Asperti 'it' => 'Italiano', // Alessandro Fiorotto, Paolo Asperti

View File

@@ -17,12 +17,12 @@ class Plugins {
$basename = "adminer-plugins"; $basename = "adminer-plugins";
if (is_dir($basename)) { if (is_dir($basename)) {
foreach (glob("$basename/*.php") as $filename) { foreach (glob("$basename/*.php") as $filename) {
$include = include_once "./$filename"; $this->includeOnce($filename);
} }
} }
$help = " href='https://www.adminer.org/plugins/#use'" . target_blank(); $help = " href='https://www.adminer.org/plugins/#use'" . target_blank();
if (file_exists("$basename.php")) { if (file_exists("$basename.php")) {
$include = include_once "./$basename.php"; // example: return array(new AdminerLoginOtp($secret)) $include = $this->includeOnce("$basename.php"); // example: return array(new AdminerLoginOtp($secret));
if (is_array($include)) { if (is_array($include)) {
foreach ($include as $plugin) { foreach ($include as $plugin) {
$plugins[get_class($plugin)] = $plugin; $plugins[get_class($plugin)] = $plugin;
@@ -32,7 +32,7 @@ class Plugins {
} }
} }
foreach (get_declared_classes() as $class) { foreach (get_declared_classes() as $class) {
if (!$plugins[$class] && preg_match('~^Adminer\w~i', $class)) { if (!$plugins[$class] && (preg_match('~^Adminer\w~i', $class) || is_subclass_of($class, 'Adminer\Plugin'))) {
// we need to use reflection because PHP 7.1 throws ArgumentCountError for missing arguments but older versions issue a warning // we need to use reflection because PHP 7.1 throws ArgumentCountError for missing arguments but older versions issue a warning
$reflection = new \ReflectionClass($class); $reflection = new \ReflectionClass($class);
$constructor = $reflection->getConstructor(); $constructor = $reflection->getConstructor();
@@ -59,6 +59,13 @@ class Plugins {
} }
} }
/** Separate function to not overwrite local variables
* @return array<object>|true
*/
function includeOnce(string $filename) {
return include_once "./$filename";
}
/** /**
* @param literal-string $name * @param literal-string $name
* @param mixed[] $params * @param mixed[] $params
@@ -67,7 +74,7 @@ class Plugins {
function __call(string $name, array $params) { function __call(string $name, array $params) {
$args = array(); $args = array();
foreach ($params as $key => $val) { 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]; $args[] = &$params[$key];
} }
$return = null; $return = null;

View File

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

View File

@@ -12,6 +12,7 @@ if (preg_match('~MyISAM|M?aria' . (min_version(5.7, '10.2.2') ? '|InnoDB' : '')
$index_types[] = "SPATIAL"; $index_types[] = "SPATIAL";
} }
$indexes = indexes($TABLE); $indexes = indexes($TABLE);
$fields = fields($TABLE);
$primary = array(); $primary = array();
if (JUSH == "mongo") { // doesn't support primary key if (JUSH == "mongo") { // doesn't support primary key
$primary = $indexes["_id_"]; $primary = $indexes["_id_"];
@@ -38,7 +39,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
if ($column != "") { if ($column != "") {
$length = idx($index["lengths"], $key); $length = idx($index["lengths"], $key);
$desc = idx($index["descs"], $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; $columns[] = $column;
$lengths[] = ($length ?: null); $lengths[] = ($length ?: null);
$descs[] = $desc; $descs[] = $desc;
@@ -81,7 +82,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
page_header(lang('Indexes'), $error, array("table" => $TABLE), h($TABLE)); page_header(lang('Indexes'), $error, array("table" => $TABLE), h($TABLE));
$fields = array_keys(fields($TABLE)); $fields_keys = array_keys($fields);
if ($_POST["add"]) { if ($_POST["add"]) {
foreach ($row["indexes"] as $key => $index) { foreach ($row["indexes"] as $key => $index) {
if ($index["columns"][count($index["columns"])] != "") { if ($index["columns"][count($index["columns"])] != "") {
@@ -138,7 +139,7 @@ if (support("partial_indexes")) {
if ($primary) { if ($primary) {
echo "<tr><td>PRIMARY<td>"; echo "<tr><td>PRIMARY<td>";
foreach ($primary["columns"] as $key => $column) { 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 "<label><input disabled type='checkbox'>" . lang('descending') . "</label> ";
} }
echo "<td><td>\n"; echo "<td><td>\n";
@@ -158,7 +159,7 @@ foreach ($row["indexes"] as $index) {
foreach ($index["columns"] as $key => $column) { foreach ($index["columns"] as $key => $column) {
echo "<span>" . select_input( echo "<span>" . select_input(
" name='indexes[$j][columns][$i]' title='" . lang('Column') . "'", " 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, $column,
"partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')" "partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')"
); );

View File

@@ -13,6 +13,7 @@ Lang::$translations = array(
'Logged as: %s' => 'Přihlášen jako: %s', 'Logged as: %s' => 'Přihlášen jako: %s',
'Logout successful.' => 'Odhlášení proběhlo v pořádku.', '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.', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Díky za použití Admineru, <a href="https://www.adminer.org/cs/donation/">přispějte</a> na vývoj.',
'hostname[:port] or :socket' => 'hostname[:port] nebo :socket',
'Invalid credentials.' => 'Neplatné přihlašovací údaje.', '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.', '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>.', '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>.',
@@ -27,7 +28,7 @@ Lang::$translations = array(
'Connecting to privileged ports is not allowed.' => 'Připojování k privilegovaným portům není povoleno.', '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.', '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 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.', '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', '%s version: %s through PHP extension %s' => 'Verze %s: %s přes PHP rozšíření %s',
'Refresh' => 'Obnovit', 'Refresh' => 'Obnovit',
@@ -76,6 +77,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Soubor %s na webovém serveru', 'Webserver file %s' => 'Soubor %s na webovém serveru',
'Run file' => 'Spustit soubor', 'Run file' => 'Spustit soubor',
'File does not exist.' => 'Soubor neexistuje.', 'File does not exist.' => 'Soubor neexistuje.',
'Increase %s.' => 'Zvyšte %s.',
'File uploads are disabled.' => 'Nahrávání souborů není povoleno.', 'File uploads are disabled.' => 'Nahrávání souborů není povoleno.',
'Unable to upload a file.' => 'Nepodařilo se nahrát soubor.', 'Unable to upload a file.' => 'Nepodařilo se nahrát soubor.',
'Maximum allowed file size is %sB.' => 'Maximální povolená velikost souboru je %sB.', 'Maximum allowed file size is %sB.' => 'Maximální povolená velikost souboru je %sB.',
@@ -195,6 +197,7 @@ Lang::$translations = array(
'Partition name' => 'Název oddílu', 'Partition name' => 'Název oddílu',
'Values' => 'Hodnoty', 'Values' => 'Hodnoty',
'Inherits from' => 'Zděděná z', 'Inherits from' => 'Zděděná z',
'Inherited by' => 'Zděděné',
'View' => 'Pohled', 'View' => 'Pohled',
'Materialized view' => 'Materializovaný pohled', 'Materialized view' => 'Materializovaný pohled',

364
adminer/lang/hr.inc.php Normal file
View File

@@ -0,0 +1,364 @@
<?php
namespace Adminer;
Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...)
'System' => 'Sustav',
'Server' => 'Poslužitelj',
'Username' => 'Korisničko ime',
'Password' => 'Lozinka',
'Permanent login' => 'Trajna prijava',
'Login' => 'Prijava',
'Logout' => 'Odjava',
'Logged as: %s' => 'Prijavljen kao: %s',
'Logout successful.' => 'Uspješna odjava.',
'Invalid credentials.' => 'Neispravni podaci za prijavu.',
'Language' => 'Jezik',
'Invalid CSRF token. Send the form again.' => 'Nevažeći CSRF token. Pošaljite obrazac ponovo.',
'No extension' => 'Nema proširenja',
'None of the supported PHP extensions (%s) are available.' => 'Nijedno od podržanih PHP proširenja (%s) nije dostupno.',
'Session support must be enabled.' => 'Podrška za sesije mora biti uključena.',
'Session expired, please login again.' => 'Sesija je istekla, molimo prijavite se ponovo.',
'%s version: %s through PHP extension %s' => '%s verzija: %s putem PHP proširenja %s',
'Refresh' => 'Osvježi',
// text direction - 'ltr' or 'rtl'
'ltr' => 'ltr',
'Privileges' => 'Ovlasti',
'Create user' => 'Novi korisnik',
'User has been dropped.' => 'Korisnik je izbrisan.',
'User has been altered.' => 'Korisnik je izmijenjen.',
'User has been created.' => 'Korisnik je kreiran.',
'Hashed' => 'Hashirano',
'Column' => 'Stupac',
'Routine' => 'Rutina',
'Grant' => 'Dodijeli',
'Revoke' => 'Opozovi',
'Process list' => 'Popis procesa',
'%d process(es) have been killed.' => array('%d proces je zaustavljen.', '%d procesa su zaustavljena.', '%d procesa je zaustavljeno.'),
'Kill' => 'Zaustavi',
'Variables' => 'Varijable',
'Status' => 'Status',
'SQL command' => 'SQL naredba',
'%d query(s) executed OK.' => array('%d upit je uspješno izvršen.', '%d upita su uspješno izvršena.', '%d upita je uspješno izvršeno.'),
'Query executed OK, %d row(s) affected.' => array('Upit je uspješno izvršen, %d redak je ažuriran.', 'Upit je uspješno izvršen, %d retka su ažurirana.', 'Upit je uspješno izvršen, %d redaka je ažurirano.'),
'No commands to execute.' => 'Nema naredbi za izvršavanje.',
'Error in query' => 'Greška u upitu',
'Execute' => 'Izvrši',
'Stop on error' => 'Zaustavi pri grešci',
'Show only errors' => 'Prikaži samo greške',
// sprintf() format for time of the command
'%.3f s' => '%.3f s',
'History' => 'Povijest',
'Clear' => 'Očisti',
'Edit all' => 'Uredi sve',
'File upload' => 'Prijenos datoteke',
'From server' => 'S poslužitelja',
'Webserver file %s' => 'Datoteka %s s web poslužitelja',
'Run file' => 'Pokreni datoteku',
'File does not exist.' => 'Datoteka ne postoji.',
'File uploads are disabled.' => 'Prijenos datoteka je onemogućen.',
'Unable to upload a file.' => 'Prijenos datoteke nije uspio.',
'Maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veličina datoteke je %sB.',
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'Preveliki POST podaci. Smanjite podatke ili povećajte vrijednost konfiguracijske direktive %s.',
'Export' => 'Izvoz',
'Output' => 'Ispis',
'open' => 'otvori',
'save' => 'spremi',
'Format' => 'Format',
'Data' => 'Podaci',
'Database' => 'Baza podataka',
'Use' => 'Koristi',
'Select database' => 'Odaberite bazu',
'Invalid database.' => 'Neispravna baza podataka.',
'Database has been dropped.' => 'Baza podataka je izbrisana.',
'Databases have been dropped.' => 'Baze podataka su izbrisane.',
'Database has been created.' => 'Baza podataka je kreirana.',
'Database has been renamed.' => 'Baza podataka je preimenovana.',
'Database has been altered.' => 'Baza podataka je izmijenjena.',
'Alter database' => 'Izmijeni bazu podataka',
'Create database' => 'Kreiraj bazu podataka',
'Database schema' => 'Shema baze podataka',
// link to current database schema layout
'Permanent link' => 'Trajna veza',
// thousands separator - must contain single byte
',' => '.',
'0123456789' => '0123456789',
'Engine' => 'Motor',
'Collation' => 'Uspoređivanje',
'Data Length' => 'Duljina podataka',
'Index Length' => 'Duljina indeksa',
'Data Free' => 'Slobodan prostor',
'Rows' => 'Redaka',
'%d in total' => 'ukupno %d',
'Analyze' => 'Analiziraj',
'Optimize' => 'Optimiziraj',
'Check' => 'Provjeri',
'Repair' => 'Popravi',
'Truncate' => 'Isprazni',
'Tables have been truncated.' => 'Tablice su ispražnjene.',
'Move to other database' => 'Premjesti u drugu bazu podataka',
'Move' => 'Premjesti',
'Tables have been moved.' => 'Tablice su premještene.',
'Copy' => 'Kopiraj',
'Tables have been copied.' => 'Tablice su kopirane.',
'Routines' => 'Rutine',
'Routine has been called, %d row(s) affected.' => array('Rutina je pozvana, %d redak je ažuriran.', 'Rutina je pozvana, %d retka su ažurirana.', 'Rutina je pozvana, %d redaka je ažurirano.'),
'Call' => 'Pozovi',
'Parameter name' => 'Naziv parametra',
'Create procedure' => 'Kreiraj proceduru',
'Create function' => 'Kreiraj funkciju',
'Routine has been dropped.' => 'Rutina je izbrisana.',
'Routine has been altered.' => 'Rutina je izmijenjena.',
'Routine has been created.' => 'Rutina je kreirana.',
'Alter function' => 'Izmijeni funkciju',
'Alter procedure' => 'Izmijeni proceduru',
'Return type' => 'Tip povratne vrijednosti',
'Events' => 'Događaji',
'Event has been dropped.' => 'Događaj je izbrisan.',
'Event has been altered.' => 'Događaj je izmijenjen.',
'Event has been created.' => 'Događaj je kreiran.',
'Alter event' => 'Izmijeni događaj',
'Create event' => 'Kreiraj događaj',
'At given time' => 'U zadano vrijeme',
'Every' => 'Svako',
'Schedule' => 'Raspored',
'Start' => 'Početak',
'End' => 'Kraj',
'On completion preserve' => 'Zadrži po završetku',
'Tables' => 'Tablice',
'Tables and views' => 'Tablice i pogledi',
'Table' => 'Tablica',
'No tables.' => 'Nema tablica.',
'Alter table' => 'Izmijeni tablicu',
'Create table' => 'Kreiraj tablicu',
'Table has been dropped.' => 'Tablica je izbrisana.',
'Tables have been dropped.' => 'Tablice su izbrisane.',
'Tables have been optimized.' => 'Tablice su optimizirane.',
'Table has been altered.' => 'Tablica je izmijenjena.',
'Table has been created.' => 'Tablica je kreirana.',
'Table name' => 'Naziv tablice',
'Show structure' => 'Prikaži strukturu',
'engine' => 'motor',
'collation' => 'uspoređivanje',
'Column name' => 'Naziv stupca',
'Type' => 'Tip',
'Length' => 'Duljina',
'Auto Increment' => 'Auto-inkrement',
'Options' => 'Opcije',
'Comment' => 'Komentar',
'Default values' => 'Zadane vrijednosti',
'Drop' => 'Izbriši',
'Are you sure?' => 'Jeste li sigurni?',
'Move up' => 'Pomakni gore',
'Move down' => 'Pomakni dolje',
'Remove' => 'Ukloni',
'Maximum number of allowed fields exceeded. Please increase %s.' => 'Premašen je maksimalni broj dozvoljenih polja. Molimo povećajte %s.',
'Partition by' => 'Particioniraj po',
'Partitions' => 'Particije',
'Partition name' => 'Naziv particije',
'Values' => 'Vrijednosti',
'View' => 'Pogled',
'View has been dropped.' => 'Pogled je izbrisan.',
'View has been altered.' => 'Pogled je izmijenjen.',
'View has been created.' => 'Pogled je kreiran.',
'Alter view' => 'Izmijeni pogled',
'Create view' => 'Kreiraj pogled',
'Indexes' => 'Indeksi',
'Indexes have been altered.' => 'Indeksi su izmijenjeni.',
'Alter indexes' => 'Izmijeni indekse',
'Add next' => 'Dodaj sljedeći',
'Index Type' => 'Tip indeksa',
'length' => 'duljina',
'Foreign keys' => 'Strani ključevi',
'Foreign key' => 'Strani ključ',
'Foreign key has been dropped.' => 'Strani ključ je izbrisan.',
'Foreign key has been altered.' => 'Strani ključ je izmijenjen.',
'Foreign key has been created.' => 'Strani ključ je kreiran.',
'Target table' => 'Ciljna tablica',
'Change' => 'Izmijeni',
'Source' => 'Izvor',
'Target' => 'Cilj',
'Add column' => 'Dodaj stupac',
'Alter' => 'Izmijeni',
'Add foreign key' => 'Dodaj strani ključ',
'ON DELETE' => 'ON DELETE (pri brisanju)',
'ON UPDATE' => 'ON UPDATE (pri ažuriranju)',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Izvorni i ciljni stupci moraju biti istog tipa podataka, ciljni stupci moraju biti indeksirani i referencirani podaci moraju postojati.',
'Triggers' => 'Okidači',
'Add trigger' => 'Dodaj okidač',
'Trigger has been dropped.' => 'Okidač je izbrisan.',
'Trigger has been altered.' => 'Okidač je izmijenjen.',
'Trigger has been created.' => 'Okidač je kreiran.',
'Alter trigger' => 'Izmijeni okidač',
'Create trigger' => 'Kreiraj okidač',
'Time' => 'Vrijeme',
'Event' => 'Događaj',
'Name' => 'Naziv',
'select' => 'odaberi',
'Select' => 'Odaberi',
'Selected' => 'Odabrano',
'Select data' => 'Odaberi podatke',
'Functions' => 'Funkcije',
'Aggregation' => 'Agregacija',
'Search' => 'Pretraži',
'anywhere' => 'bilo gdje',
'Search data in tables' => 'Pretraži podatke u tablicama',
'Sort' => 'Sortiraj',
'descending' => 'silazno',
'Limit' => 'Ograničenje',
'Text length' => 'Duljina teksta',
'Action' => 'Radnja',
'Full table scan' => 'Puno pretraživanje tablice',
'Unable to select the table' => 'Nije moguće odabrati tablicu',
'No rows.' => 'Nema redaka.',
'%d row(s)' => array('%d redak', '%d retka', '%d redaka'),
'Page' => 'Stranica',
'last' => 'posljednja',
'Loading' => 'Učitavanje',
'Load more data' => 'Učitaj više podataka',
'Whole result' => 'Cijeli skup rezultata',
'%d byte(s)' => array('%d bajt', '%d bajta', '%d bajtova'),
'Import' => 'Uvoz',
'%d row(s) have been imported.' => array('%d redak je uvezen.', '%d retka su uvezena.', '%d redaka je uvezeno.'),
// in-place editing in select
'Ctrl+click on a value to modify it.' => 'Ctrl+klik na vrijednost za izmjenu.',
'Use edit link to modify this value.' => 'Koristite vezu za uređivanje ove vrijednosti.',
// %s can contain auto-increment value
'Item%s has been inserted.' => 'Stavka %s je unesena.',
'Item has been deleted.' => 'Stavka je izbrisana.',
'Item has been updated.' => 'Stavka je ažurirana.',
'%d item(s) have been affected.' => array('%d stavka je zahvaćena.', '%d stavke su zahvaćene.', '%d stavki je zahvaćeno.'),
'New item' => 'Nova stavka',
'original' => 'original',
// label for value '' in enum data type
'empty' => 'prazno',
'edit' => 'uredi',
'Edit' => 'Uredi',
'Insert' => 'Unesi',
'Save' => 'Spremi',
'Save and continue edit' => 'Spremi i nastavi uređivanje',
'Save and insert next' => 'Spremi i unesi sljedeće',
'Clone' => 'Kloniraj',
'Delete' => 'Izbriši',
'Modify' => 'Izmijeni',
// data type descriptions
'Numbers' => 'Brojevi',
'Date and time' => 'Datum i vrijeme',
'Strings' => 'Tekst',
'Binary' => 'Binarno',
'Lists' => 'Liste',
'Network' => 'Mreža',
'Geometry' => 'Geometrija',
'Relations' => 'Odnosi',
'Editor' => 'Uređivač',
// date format in Editor: $1 yyyy, $2 yy, $3 mm, $4 m, $5 dd, $6 d
'$1-$3-$5' => '$5.$3.$1',
// hint for date format - use language equivalents for day, month and year shortcuts
'[yyyy]-mm-dd' => 'dd.mm.[yyyy]',
// hint for time format - use language equivalents for hour, minute and second shortcuts
'HH:MM:SS' => 'HH:MM:SS',
'now' => 'sada',
'yes' => 'da',
'no' => 'ne',
// general SQLite error in create, drop or rename database
'File exists.' => 'Datoteka već postoji.',
'Please use one of the extensions %s.' => 'Molimo koristite jedan od nastavaka %s.',
// PostgreSQL and MS SQL schema support
'Alter schema' => 'Izmijeni shemu',
'Create schema' => 'Kreiraj shemu',
'Schema has been dropped.' => 'Shema je izbrisana.',
'Schema has been created.' => 'Shema je kreirana.',
'Schema has been altered.' => 'Shema je izmijenjena.',
'Schema' => 'Shema',
'Invalid schema.' => 'Neispravna shema.',
// PostgreSQL sequences support
'Sequences' => 'Nizovi',
'Create sequence' => 'Kreiraj niz',
'Sequence has been dropped.' => 'Niz je izbrisan.',
'Sequence has been created.' => 'Niz je kreiran.',
'Sequence has been altered.' => 'Niz je izmijenjen.',
'Alter sequence' => 'Izmijeni niz',
// PostgreSQL user types support
'User types' => 'Korisnički tipovi',
'Create type' => 'Kreiraj tip',
'Type has been dropped.' => 'Tip je izbrisan.',
'Type has been created.' => 'Tip je kreiran.',
'Alter type' => 'Izmijeni tip',
// MS SQL login
'Too many unsuccessful logins, try again in %d minute(s).' => array('Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minutu.', 'Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minute.', 'Previše neuspješnih pokušaja prijave, pokušajte ponovo za %d minuta.'),
'Check has been dropped.' => 'Provjera je izbrisana.',
'Check has been altered.' => 'Provjera je izmijenjena.',
'Check has been created.' => 'Provjera je kreirana.',
'Alter check' => 'Izmijeni provjeru',
'Create check' => 'Kreiraj provjeru',
'Drop %s?' => 'Izbrisati %s?',
'Vacuum' => 'Vakuumiranje',
'overwrite' => 'prepiši',
'Disable %s or enable %s or %s extensions.' => 'Onemogućite %s ili omogućite %s ili %s proširenja.',
'Database does not support password.' => 'Baza podataka ne podržava lozinku.',
'DB' => 'BP',
'hostname[:port] or :socket' => 'hostname[:port] ili :socket',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer ne podržava pristup bazi podataka bez lozinke, <a href="https://www.adminer.org/en/password/"%s>više informacija</a>.',
'Warnings' => 'Upozorenja',
'Default value' => 'Zadana vrijednost',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Hvala što koristite Adminer, razmislite o <a href="https://www.adminer.org/en/donation/">donaciji</a>.',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Glavna lozinka je istekla. <a href="https://www.adminer.org/en/extension/"%s>Implementirajte</a> metodu %s kako biste je učinili trajnom.',
'The action will be performed after successful login with the same credentials.' => 'Radnja će biti izvršena nakon uspješne prijave s istim podacima.',
'Connecting to privileged ports is not allowed.' => 'Spajanje na privilegirane portove nije dopušteno.',
'There is a space in the input password which might be the cause.' => 'U unesenoj lozinci postoji razmak koji bi mogao biti uzrok problema.',
'If you did not send this request from Adminer then close this page.' => 'Ako ovaj zahtjev niste poslali iz Adminera, zatvorite ovu stranicu.',
'You can upload a big SQL file via FTP and import it from server.' => 'Veliku SQL datoteku možete prenijeti putem FTP-a i uvesti je s poslužitelja.',
'Size' => 'Veličina',
'Compute' => 'Izračunaj',
'Loaded plugins' => 'Učitani dodaci',
'screenshot' => 'snimka zaslona',
'You are offline.' => 'Niste povezani s mrežom.',
'Increase %s.' => 'Povećajte %s.',
'You have no privileges to update this table.' => 'Nemate ovlasti za ažuriranje ove tablice.',
'Saving' => 'Spremanje',
'Unknown error.' => 'Nepoznata greška.',
'%s must <a%s>return an array</a>.' => '%s mora <a%s>vratiti niz</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Konfigurirajte</a> %s u %s.',
'Algorithm' => 'Algoritam',
'Columns' => 'Stupci',
'Condition' => 'Uvjet',
'File must be in UTF-8 encoding.' => 'Datoteka mora biti u UTF-8 kodiranju.',
'ATTACH queries are not supported.' => 'ATTACH upiti nisu podržani.',
'%d / ' => '%d / ',
'Limit rows' => 'Ograniči retke',
'Materialized view' => 'Materijaliziran pogled',
'Inherits from' => 'Nasljeđuje od',
'Checks' => 'Provjere',
'Inherited by' => 'Nasljeđeno od',
);
// run `php ../../lang.php hr` to update this file

View File

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

View File

@@ -76,6 +76,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Plik %s na serwerze', 'Webserver file %s' => 'Plik %s na serwerze',
'Run file' => 'Uruchom z pliku', 'Run file' => 'Uruchom z pliku',
'File does not exist.' => 'Plik nie istnieje.', 'File does not exist.' => 'Plik nie istnieje.',
'Increase %s.' => 'Zwiększ %s.',
'File uploads are disabled.' => 'Wgrywanie plików jest wyłączone.', 'File uploads are disabled.' => 'Wgrywanie plików jest wyłączone.',
'Unable to upload a file.' => 'Wgranie pliku było niemożliwe.', 'Unable to upload a file.' => 'Wgranie pliku było niemożliwe.',
'Maximum allowed file size is %sB.' => 'Maksymalna wielkość pliku to %sB.', 'Maximum allowed file size is %sB.' => 'Maksymalna wielkość pliku to %sB.',
@@ -196,6 +197,7 @@ Lang::$translations = array(
'Partition name' => 'Nazwa partycji', 'Partition name' => 'Nazwa partycji',
'Values' => 'Wartości', 'Values' => 'Wartości',
'Inherits from' => 'Dziedziczy po', 'Inherits from' => 'Dziedziczy po',
'Inherited by' => 'Odziedziczone przez',
'View' => 'Perspektywa', 'View' => 'Perspektywa',
'Materialized view' => 'Zmaterializowana perspektywa', 'Materialized view' => 'Zmaterializowana perspektywa',

View File

@@ -13,6 +13,7 @@ Lang::$translations = array(
'Logged as: %s' => 'Xx: %s', 'Logged as: %s' => 'Xx: %s',
'Logout successful.' => 'Xx.', '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>.', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Xx <a href="https://www.adminer.org/en/donation/">xx</a>.',
'hostname[:port] or :socket' => 'xx',
'Invalid credentials.' => 'Xx.', 'Invalid credentials.' => 'Xx.',
'There is a space in the input password which might be the cause.' => '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>.', '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>.',
@@ -77,6 +78,7 @@ Lang::$translations = array(
'Webserver file %s' => 'Xx %s', 'Webserver file %s' => 'Xx %s',
'Run file' => 'Xx', 'Run file' => 'Xx',
'File does not exist.' => 'Xx.', 'File does not exist.' => 'Xx.',
'Increase %s.' => 'Xx %s.',
'File uploads are disabled.' => 'Xx.', 'File uploads are disabled.' => 'Xx.',
'Unable to upload a file.' => 'Xx.', 'Unable to upload a file.' => 'Xx.',
'Maximum allowed file size is %sB.' => 'Xx %sB.', 'Maximum allowed file size is %sB.' => 'Xx %sB.',
@@ -197,6 +199,7 @@ Lang::$translations = array(
'Partition name' => 'Xx', 'Partition name' => 'Xx',
'Values' => 'Xx', 'Values' => 'Xx',
'Inherits from' => 'Xx', 'Inherits from' => 'Xx',
'Inherited by' => 'Xx',
'View' => 'Xx', 'View' => 'Xx',
'Materialized view' => 'Xx', 'Materialized view' => 'Xx',

View File

@@ -62,7 +62,7 @@ if (isset($_GET["function"])) {
</table> </table>
<?php echo script("editFields();"); ?> <?php echo script("editFields();"); ?>
</div> </div>
<p><?php textarea("definition", $row["definition"]); ?> <p><?php textarea("definition", $row["definition"], 20); ?>
<p> <p>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<?php if ($PROCEDURE != "") { ?> <?php if ($PROCEDURE != "") { ?>

View File

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

View File

@@ -18,7 +18,8 @@ $base_left = -1;
/** @var array{fields:Field[], pos:array{float, float}, references:string[][][]}[] */ /** @var array{fields:Field[], pos:array{float, float}, references:string[][][]}[] */
$schema = array(); // table => array("fields" => array(name => field), "pos" => array(top, left), "references" => array(table => array(left => array(source, target)))) $schema = array(); // table => array("fields" => array(name => field), "pos" => array(top, left), "references" => array(table => array(left => array(source, target))))
$referenced = array(); // target_table => array(table => array(left => target_column)) $referenced = array(); // target_table => array(table => array(left => target_column))
$lefts = array(); // float => bool /** @var array<numeric-string, bool> */
$lefts = array();
$all_fields = driver()->allFields(); $all_fields = driver()->allFields();
foreach (table_status('', true) as $table => $table_status) { foreach (table_status('', true) as $table => $table_status) {
if (is_view($table_status)) { if (is_view($table_status)) {
@@ -102,16 +103,18 @@ foreach ($schema as $name => $table) {
foreach ($schema as $name => $table) { foreach ($schema as $name => $table) {
foreach ((array) $table["references"] as $target_name => $refs) { foreach ((array) $table["references"] as $target_name => $refs) {
foreach ($refs as $left => $ref) { if ($schema[$target_name]) { // otherwise table in another schema
$min_pos = $top; foreach ($refs as $left => $ref) {
$max_pos = -10; $min_pos = $top;
foreach ($ref[0] as $key => $source) { $max_pos = -10;
$pos1 = $table["pos"][0] + $table["fields"][$source]["pos"]; foreach ($ref[0] as $key => $source) {
$pos2 = $schema[$target_name]["pos"][0] + $schema[$target_name]["fields"][$ref[1][$key]]["pos"]; $pos1 = $table["pos"][0] + $table["fields"][$source]["pos"];
$min_pos = min($min_pos, $pos1, $pos2); $pos2 = $schema[$target_name]["pos"][0] + $schema[$target_name]["fields"][$ref[1][$key]]["pos"];
$max_pos = max($max_pos, $pos1, $pos2); $min_pos = min($min_pos, $pos1, $pos2);
$max_pos = max($max_pos, $pos1, $pos2);
}
echo "<div class='references' id='refl$left' style='left: $left" . "em; top: $min_pos" . "em; padding: .5em 0;'><div style='border-right: 1px solid gray; margin-top: 1px; height: " . ($max_pos - $min_pos) . "em;'></div></div>\n";
} }
echo "<div class='references' id='refl$left' style='left: $left" . "em; top: $min_pos" . "em; padding: .5em 0;'><div style='border-right: 1px solid gray; margin-top: 1px; height: " . ($max_pos - $min_pos) . "em;'></div></div>\n";
} }
} }
} }

View File

@@ -7,7 +7,7 @@ if ($_GET["script"] == "db") {
$sums = array("Data_length" => 0, "Index_length" => 0, "Data_free" => 0); $sums = array("Data_length" => 0, "Index_length" => 0, "Data_free" => 0);
foreach (table_status() as $name => $table_status) { foreach (table_status() as $name => $table_status) {
json_row("Comment-$name", h($table_status["Comment"])); 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) { foreach (array("Engine", "Collation") as $key) {
json_row("$key-$name", h($table_status[$key])); json_row("$key-$name", h($table_status[$key]));
} }

View File

@@ -353,17 +353,14 @@ if (!$columns && support("table")) {
$desc = "&desc%5B0%5D=1"; $desc = "&desc%5B0%5D=1";
echo "<th id='th[" . h(bracket_escape($key)) . "]'>" . script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});", ""); 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 $fun = apply_sql_function($val["fun"], $name); //! columns looking like functions
$sortable = isset($field["privileges"]["order"]) || $fun; $sortable = isset($field["privileges"]["order"]) || $fun != $name;
echo ($sortable ? "<a href='" . h($href . ($order[0] == $column || $order[0] == $key || (!$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'>"; $menu = ($sortable ? "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>" : '');
if ($sortable) {
echo "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>";
}
if (!$val["fun"] && isset($field["privileges"]["where"])) { if (!$val["fun"] && isset($field["privileges"]["where"])) {
echo '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>'; $menu .= '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>';
echo script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');"); $menu .= script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');");
} }
echo "</span>"; echo ($menu ? "<span class='column hidden'>$menu</span>" : "");
} }
$functions[$key] = $val["fun"]; $functions[$key] = $val["fun"];
next($select); next($select);
@@ -417,13 +414,12 @@ if (!$columns && support("table")) {
if (isset($names[$key])) { if (isset($names[$key])) {
$column = current($select); $column = current($select);
$field = (array) $fields[$key]; $field = (array) $fields[$key];
$val = driver()->value($val, $field);
if ($val != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) { if ($val != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
$email_fields[$key] = (is_mail($val) ? $names[$key] : ""); //! filled e-mails can be contained on other pages $email_fields[$key] = (is_mail($val) ? $names[$key] : ""); //! filled e-mails can be contained on other pages
} }
$link = ""; $link = "";
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && $val != "") { if (is_blob($field) && $val != "") {
$link = ME . 'download=' . urlencode($TABLE) . '&field=' . urlencode($key) . $unique_idf; $link = ME . 'download=' . urlencode($TABLE) . '&field=' . urlencode($key) . $unique_idf;
} }
if (!$link && $val !== null) { // link related items if (!$link && $val !== null) { // link related items
@@ -459,7 +455,8 @@ if (!$columns && support("table")) {
$html = select_value($val, $link, $field, $text_length); $html = select_value($val, $link, $field, $text_length);
$id = h("val[$unique_idf][" . bracket_escape($key) . "]"); $id = h("val[$unique_idf][" . bracket_escape($key) . "]");
$posted = idx(idx($_POST["val"], $unique_idf), bracket_escape($key)); $posted = idx(idx($_POST["val"], $unique_idf), bracket_escape($key));
$editable = !is_array($row[$key]) && is_utf8($html) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"]; $update = idx($field["privileges"], "update");
$editable = !is_array($row[$key]) && is_utf8($html) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"] && $update;
$type = (preg_match('~^(AVG|MIN|MAX)\((.+)\)~', $column, $match) ? $fields[idf_unescape($match[2])]["type"] : $field["type"]); $type = (preg_match('~^(AVG|MIN|MAX)\((.+)\)~', $column, $match) ? $fields[idf_unescape($match[2])]["type"] : $field["type"]);
$text = preg_match('~text|json|lob~', $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); $is_number = preg_match(number_type(), $type) || preg_match('~^(CHAR_LENGTH|ROUND|FLOOR|CEIL|TIME_TO_SEC|COUNT|SUM)\(~', $column);
@@ -469,10 +466,11 @@ if (!$columns && support("table")) {
echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>"); echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>");
} else { } else {
$long = strpos($html, "<i>…</i>"); $long = strpos($html, "<i>…</i>");
echo " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'" echo ($update
. ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'") ? " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'"
. ">$html" . ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'")
; : ""
) . ">$html";
} }
} }
next($select); next($select);
@@ -503,7 +501,7 @@ if (!$columns && support("table")) {
if (intval($found_rows) < max(1e4, 2 * ($page + 1) * $limit)) { if (intval($found_rows) < max(1e4, 2 * ($page + 1) * $limit)) {
// slow with big tables // slow with big tables
$found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group))); $found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group)));
} else { } elseif (JUSH == 'sql' || JUSH == 'pgsql') {
$exact_count = false; $exact_count = false;
} }
} }
@@ -595,9 +593,10 @@ if (!$columns && support("table")) {
echo "<a href='#import'>" . lang('Import') . "</a>"; echo "<a href='#import'>" . lang('Import') . "</a>";
echo script("qsl('a').onclick = partial(toggle, 'import');", ""); echo script("qsl('a').onclick = partial(toggle, 'import');", "");
echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: "; echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: ";
echo "<input type='file' name='csv_file'> "; echo file_input("<input type='file' name='csv_file'> "
echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]); . html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"])
echo " <input type='submit' name='import' value='" . lang('Import') . "'>"; . " <input type='submit' name='import' value='" . lang('Import') . "'>")
;
echo "</span>"; echo "</span>";
} }

View File

@@ -4,9 +4,13 @@ namespace Adminer;
if (!$error && $_POST["export"]) { if (!$error && $_POST["export"]) {
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import"); save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers("sql"); dump_headers("sql");
adminer()->dumpTable("", ""); if ($_POST["format"] == "sql") {
adminer()->dumpData("", "table", $_POST["query"]); echo "$_POST[query]\n";
adminer()->dumpFooter(); } else {
adminer()->dumpTable("", "");
adminer()->dumpData("", "table", $_POST["query"]);
adminer()->dumpFooter();
}
exit; exit;
} }
@@ -53,7 +57,7 @@ if (!$error && $_POST) {
} }
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)"; $space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)";
$delimiter = ";"; $delimiter = driver()->delimiter;
$offset = 0; $offset = 0;
$empty = true; $empty = true;
$connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error $connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
@@ -65,11 +69,9 @@ if (!$error && $_POST) {
} }
$commands = 0; $commands = 0;
$errors = array(); $errors = array();
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|' . $line_comment . '|$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : ''); $parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|' . $line_comment . '|$' . (JUSH == "pgsql" ? '|\$([a-zA-Z]\w*)?\$' : '');
$total_start = microtime(true); $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 $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 != "") { while ($query != "") {
if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) { if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) {
@@ -174,7 +176,7 @@ if (!$error && $_POST) {
$id = "export-$commands"; $id = "export-$commands";
echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: " 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("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_hidden("query", $q)
. "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n" . "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n"
. "</form>\n" . "</form>\n"
@@ -248,12 +250,9 @@ if (!isset($_GET["import"])) {
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n"; echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";
} else { } else {
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
$gz = (extension_loaded("zlib") ? "[.gz]" : ""); $gz = (extension_loaded("zlib") ? "[.gz]" : "");
echo (ini_bool("file_uploads") echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
? "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 echo file_input("SQL$gz: <input type='file' name='sql_file[]' multiple>\n$execute");
: lang('File uploads are disabled.')
);
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
$importServerPath = adminer()->importServerPath(); $importServerPath = adminer()->importServerPath();
if ($importServerPath) { if ($importServerPath) {

View File

@@ -57,6 +57,7 @@ input.wayoff { left: -1000px; position: absolute; }
.date { color: #7F007F; } .date { color: #7F007F; }
.enum { color: #007F7F; } .enum { color: #007F7F; }
.binary { color: red; } .binary { color: red; }
.odds tbody tr { background: var(--bg); }
.odds tbody tr:nth-child(2n) { background: #F5F5F5; } .odds tbody tr:nth-child(2n) { background: #F5F5F5; }
.js .checkable .checked td, .js .checkable .checked th { background: var(--lit); } .js .checkable .checked td, .js .checkable .checked th { background: var(--lit); }
.time { color: silver; font-size: 70%; } .time { color: silver; font-size: 70%; }
@@ -128,14 +129,18 @@ input.wayoff { left: -1000px; position: absolute; }
.js .foot { display: none; } .js .foot { display: none; }
.js #menuopen { display: block; position: absolute; top: 3px; left: 6px; } .js #menuopen { display: block; position: absolute; top: 3px; left: 6px; }
.nojs #menu { position: static; } .nojs #menu { position: static; }
.rtl.js #foot { left: auto; right: 0; }
.rtl .pages { right: auto; } .rtl .pages { right: auto; }
.rtl #content { margin-right: 10px; } .rtl.js #menuopen { left: auto; right: 6px; }
.rtl #breadcrumb { right: auto; } .rtl #content { margin-left: 0 !important; margin-right: 10px; }
.rtl #breadcrumb { left: auto !important; right: 48px; }
} }
@media print { @media print {
#lang, #menu { display: none; } #lang, #menu, .logout { display: none; }
#content { margin-left: 1em; } #content { margin-left: 1em; }
#breadcrumb { left: 1em; } #breadcrumb { left: 1em; }
.rtl #content { margin-left: auto; margin-right: 1em; }
.rtl #breadcrumb { left: auto; right: 1em; }
.nowrap td, .nowrap th, td.nowrap { white-space: normal; } .nowrap td, .nowrap th, td.nowrap { white-space: normal; }
} }

View File

@@ -93,6 +93,13 @@ function messagesPrint(parent) {
for (const el of qsa('.toggle', parent)) { for (const el of qsa('.toggle', parent)) {
el.onclick = partial(toggle, el.getAttribute('href').substr(1)); el.onclick = partial(toggle, el.getAttribute('href').substr(1));
} }
for (const el of qsa('.copy', parent)) {
el.onclick = () => {
navigator.clipboard.writeText(qs('code', el.parentElement).innerText).then(() => el.textContent = '✓');
setTimeout(() => el.textContent = '🗐', 1000);
return false;
};
}
} }
@@ -384,7 +391,7 @@ function editingAddRow(focus) {
*/ */
function editingRemoveRow(name) { function editingRemoveRow(name) {
const field = formField(this.form, this.name.replace(/[^[]+(.+)/, name)); const field = formField(this.form, this.name.replace(/[^[]+(.+)/, name));
field.parentNode.removeChild(field); field.remove();
parentTag(this, 'tr').style.display = 'none'; parentTag(this, 'tr').style.display = 'none';
return false; return false;
} }
@@ -456,7 +463,7 @@ function editingLengthChange() {
*/ */
function editingLengthFocus() { function editingLengthFocus() {
const td = this.parentNode; const td = this.parentNode;
if (/(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) { if (/^(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) {
const edit = qs('#enum-edit'); const edit = qs('#enum-edit');
edit.value = enumValues(this.value); edit.value = enumValues(this.value);
td.appendChild(edit); td.appendChild(edit);
@@ -655,13 +662,29 @@ function indexesAddColumn(prefix) {
* @param string * @param string
*/ */
function sqlSubmit(form, root) { function sqlSubmit(form, root) {
if (encodeURIComponent(form['query'].value).length < 500) { const action = root
form.action = root + '&sql=' + encodeURIComponent(form['query'].value)
+ '&sql=' + encodeURIComponent(form['query'].value) + (form['limit'].value ? '&limit=' + +form['limit'].value : '')
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '') + (form['error_stops'].checked ? '&error_stops=1' : '')
+ (form['error_stops'].checked ? '&error_stops=1' : '') + (form['only_errors'].checked ? '&only_errors=1' : '')
+ (form['only_errors'].checked ? '&only_errors=1' : '') ;
; if ((document.location.origin + document.location.pathname + action).length < 2000) { // reasonable minimum is 2048
form.action = action;
}
}
/** Check if PHP can handle the uploaded files
* @param Event
* @param number
* @param string
* @param number
* @param string
*/
function fileChange(event, count, countMessage, size, sizeMessage) {
if (event.target.files.length > count) {
alert(countMessage);
} else if (Array.from(event.target.files).reduce((sum, file) => sum + file.size, 0) > size) {
alert(sizeMessage);
} }
} }

View File

@@ -96,29 +96,15 @@ function cookie(assign, days) {
/** Verify current Adminer version /** Verify current Adminer version
* @param string * @param string
* @param string own URL base
* @param string
*/ */
function verifyVersion(current, url, token) { function verifyVersion(current) {
cookie('adminer_version=0', 1); cookie('adminer_version=0', 1);
const iframe = document.createElement('iframe'); // do not send X-Requested-With to avoid preflight
iframe.src = 'https://www.adminer.org/version/?current=' + current; fetch('https://www.adminer.org/version/?current=' + current).then(async response => {
iframe.frameBorder = 0; const json = await response.json();
iframe.marginHeight = 0; cookie('adminer_version=' + (json.version || current), 7); // empty if there's no newer version
iframe.scrolling = 'no'; qs('#version').textContent = json.version;
iframe.style.width = '7ex'; });
iframe.style.height = '1.25em';
iframe.style.display = 'none';
addEventListener('message', event => {
if (event.origin == 'https://www.adminer.org') {
const match = /version=(.+)/.exec(event.data);
if (match) {
cookie('adminer_version=' + match[1], 1);
ajax(url + 'script=version', () => { }, event.data + '&token=' + token);
}
}
}, false);
qs('#version').appendChild(iframe);
} }
/** Get value of select /** Get value of select
@@ -235,8 +221,8 @@ function formChecked(input, name) {
function tableClick(event, click) { function tableClick(event, click) {
const td = parentTag(event.target, 'td'); const td = parentTag(event.target, 'td');
let text; let text;
if (td && (text = td.getAttribute('data-text'))) { if (td && (text = td.dataset.text)) {
if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) { if (selectClick.call(td, event, +text, td.dataset.warning)) {
return; return;
} }
} }
@@ -525,14 +511,14 @@ function functionChange() {
if (selectValue(this)) { if (selectValue(this)) {
if (input.origType === undefined) { if (input.origType === undefined) {
input.origType = input.type; input.origType = input.type;
input.origMaxLength = input.getAttribute('data-maxlength'); input.origMaxLength = input.dataset.maxlength;
} }
input.removeAttribute('data-maxlength'); delete input.dataset.maxlength;
input.type = 'text'; input.type = 'text';
} else if (input.origType) { } else if (input.origType) {
input.type = input.origType; input.type = input.origType;
if (input.origMaxLength >= 0) { if (input.origMaxLength >= 0) {
input.setAttribute('data-maxlength', input.origMaxLength); input.dataset.maxlength = input.origMaxLength;
} }
} }
oninput({target: input}); oninput({target: input});
@@ -736,9 +722,10 @@ function selectLoadMore(limit, loading) {
return !ajax(href, request => { return !ajax(href, request => {
const tbody = document.createElement('tbody'); const tbody = document.createElement('tbody');
tbody.innerHTML = request.responseText; tbody.innerHTML = request.responseText;
adminerHighlighter(qsa('code', tbody));
qs('#table').appendChild(tbody); qs('#table').appendChild(tbody);
if (tbody.children.length < limit) { if (tbody.children.length < limit) {
a.parentNode.removeChild(a); a.remove();
} else { } else {
a.href = href.replace(/\d+$/, page => +page + 1); a.href = href.replace(/\d+$/, page => +page + 1);
a.innerHTML = title; a.innerHTML = title;
@@ -843,7 +830,7 @@ function cloneNode(el) {
oninput = event => { oninput = event => {
const target = event.target; const target = event.target;
const maxLength = target.getAttribute('data-maxlength'); const maxLength = target.dataset.maxlength;
alterClass(target, 'maxlength', target.value && maxLength != null && target.value.length > maxLength); // maxLength could be 0 alterClass(target, 'maxlength', target.value && maxLength != null && target.value.length > maxLength); // maxLength could be 0
}; };

View File

@@ -22,10 +22,18 @@ if ($comment != "") {
echo "<p class='nowrap'>" . lang('Comment') . ": " . h($comment) . "\n"; echo "<p class='nowrap'>" . lang('Comment') . ": " . h($comment) . "\n";
} }
function tables_links($tables) { if ($fields) {
adminer()->tableStructurePrint($fields, $table_status);
}
/** Print links to tables
* @param list<array{table: string, ns: string}> $tables
*/
function tables_links(array $tables): void {
echo "<ul>\n"; echo "<ul>\n";
foreach ($tables as $table) { foreach ($tables as $row) {
echo "<li><a href='" . h(ME . "table=" . urlencode($table)) . "'>" . h($table) . "</a>"; $link = preg_replace('~ns=[^&]*~', "ns=" . urlencode($row["ns"]), ME);
echo "<li><a href='" . h($link . "table=" . urlencode($row["table"])) . "'>" . ($row["ns"] != $_GET["ns"] ? "<b>" . h($row["ns"]) . "</b>." : "") . h($row["table"]) . "</a>";
} }
echo "</ul>\n"; echo "</ul>\n";
} }
@@ -34,8 +42,6 @@ $inherits = driver()->inheritsFrom($TABLE);
if ($inherits) { if ($inherits) {
echo "<h3>" . lang('Inherits from') . "</h3>\n"; echo "<h3>" . lang('Inherits from') . "</h3>\n";
tables_links($inherits); tables_links($inherits);
} elseif ($fields) {
adminer()->tableStructurePrint($fields, $table_status);
} }
if (support("indexes") && driver()->supportsIndex($table_status)) { if (support("indexes") && driver()->supportsIndex($table_status)) {
@@ -110,7 +116,7 @@ if (support(is_view($table_status) ? "view_trigger" : "trigger")) {
$inherited = driver()->inheritedTables($TABLE); $inherited = driver()->inheritedTables($TABLE);
if ($inherited) { if ($inherited) {
echo "<h3 id='partitions'>" . lang('Partitions') . "</h3>\n"; echo "<h3 id='partitions'>" . lang('Inherited by') . "</h3>\n";
$partition = driver()->partitionsInfo($TABLE); $partition = driver()->partitionsInfo($TABLE);
if ($partition) { if ($partition) {
echo "<p><code class='jush-" . JUSH . "'>BY " . h("$partition[partition_by]($partition[partition])") . "</code>\n"; echo "<p><code class='jush-" . JUSH . "'>BY " . h("$partition[partition_by]($partition[partition])") . "</code>\n";

View File

@@ -4,7 +4,7 @@ namespace Adminer;
$status = isset($_GET["status"]); $status = isset($_GET["status"]);
page_header($status ? lang('Status') : lang('Variables')); page_header($status ? lang('Status') : lang('Variables'));
$variables = ($status ? show_status() : show_variables()); $variables = ($status ? adminer()->showStatus() : adminer()->showVariables());
if (!$variables) { if (!$variables) {
echo "<p class='message'>" . lang('No rows.') . "\n"; echo "<p class='message'>" . lang('No rows.') . "\n";
} else { } else {

View File

@@ -47,7 +47,7 @@ function put_file($match) {
// check function definition in drivers // check function definition in drivers
if ($vendor != "mysql") { if ($vendor != "mysql") {
preg_match_all( preg_match_all(
'~\bfunction ([^(]+)~', '~\bfunction (?!alter_table|drop_tables|truncate_tables)([^(]+)~', // used for feature detection
preg_replace('~class Driver.*\n\t}~sU', '', file_get_contents(__DIR__ . "/adminer/drivers/mysql.inc.php")), preg_replace('~class Driver.*\n\t}~sU', '', file_get_contents(__DIR__ . "/adminer/drivers/mysql.inc.php")),
$matches $matches
); //! respect context (extension, class) ); //! respect context (extension, class)
@@ -98,7 +98,7 @@ function put_file($match) {
echo "lang() not found\n"; echo "lang() not found\n";
} }
} else { } else {
$return = preg_replace('~// not used in a single language version from here\n.*~s', '', $return); $return = preg_replace('~// not used in a single language version from here.*~s', '', $return);
$return = preg_replace_callback('~(\$pos = (.+\n).+;)~sU', function ($match) { $return = preg_replace_callback('~(\$pos = (.+\n).+;)~sU', function ($match) {
return "\$pos = $match[2]\t\t\t: " . (preg_match("~'$_SESSION[lang]'.* \\? (.+)\n~U", $match[1], $match2) ? $match2[1] : "1") . "\n\t\t);"; return "\$pos = $match[2]\t\t\t: " . (preg_match("~'$_SESSION[lang]'.* \\? (.+)\n~U", $match[1], $match2) ? $match2[1] : "1") . "\n\t\t);";
}, $return); }, $return);

View File

@@ -33,7 +33,11 @@
"php": ">=7.4" "php": ">=7.4"
}, },
"scripts": { "scripts": {
"clean": "rm -f adminer*.php editor*.php", "check": [
"compile": "@php compile.php" "phpcs",
"phpstan analyse -c phpstan.neon"
],
"compile": "@php compile.php",
"clean": "rm -f adminer*.php editor*.php"
} }
} }

View File

@@ -185,7 +185,7 @@ table#table thead .checked th {
padding: 3rem 0 1.5rem; padding: 3rem 0 1.5rem;
} }
#menu p, #logins, #tables { #menu p, #tables {
padding: .8em 0em 1.2rem; padding: .8em 0em 1.2rem;
} }
@@ -237,6 +237,14 @@ table#table thead .checked th {
background: initial; background: initial;
} }
#logins ul {
padding: 3rem 0 1rem;
}
#logins ul li {
margin-bottom: .5rem;
}
/* /*
* Elements * Elements
*/ */

View File

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

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

@@ -59,6 +59,9 @@ class Adminer {
return 5; return 5;
} }
function afterConnect() {
}
function headers() { function headers() {
} }
@@ -214,9 +217,9 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
} }
function selectVal($val, $link, $field, $original) { function selectVal($val, $link, $field, $original) {
$return = $val; $return = "$val";
$link = h($link); $link = h($link);
if (preg_match('~blob|bytea~', $field["type"]) && !is_utf8($val)) { if (is_blob($field) && !is_utf8($val)) {
$return = lang('%d byte(s)', strlen($original)); $return = lang('%d byte(s)', strlen($original));
if (preg_match("~^(GIF|\xFF\xD8\xFF|\x89PNG\x0D\x0A\x1A\x0A)~", $original)) { // GIF|JPG|PNG, getimagetype() works with filename if (preg_match("~^(GIF|\xFF\xD8\xFF|\x89PNG\x0D\x0A\x1A\x0A)~", $original)) { // GIF|JPG|PNG, getimagetype() works with filename
$return = "<img src='$link' alt='$return'>"; $return = "<img src='$link' alt='$return'>";
@@ -261,14 +264,14 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
$fields = fields($_GET["select"]); $fields = fields($_GET["select"]);
foreach ($columns as $name => $desc) { foreach ($columns as $name => $desc) {
$field = $fields[$name]; $field = $fields[$name];
if (preg_match("~enum~", $field["type"]) || like_bool($field)) { //! set - uses 1 << $i and FIND_IN_SET() if ($field["type"] == "enum" || like_bool($field)) { //! set - uses 1 << $i and FIND_IN_SET()
$key = $keys[$name]; $key = $keys[$name];
$i--; $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"); $val = idx($where[$key], "val");
echo (like_bool($field) echo (like_bool($field)
? "<select name='where[$i][val]'>" . optionlist(array("" => "", lang('no'), lang('yes')), $val, true) . "</select>" ? "<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)) : enum_input("checkbox", " name='where[$i][val][]'", $field, (array) $val, lang('empty'))
); );
echo "</div>\n"; echo "</div>\n";
unset($columns[$name]); unset($columns[$name]);
@@ -366,7 +369,13 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
if ($col != "" || is_numeric($val) || !preg_match(number_type(), $field["type"])) { if ($col != "" || is_numeric($val) || !preg_match(number_type(), $field["type"])) {
$name = idf_escape($name); $name = idf_escape($name);
if ($col != "" && $field["type"] == "enum") { if ($col != "" && $field["type"] == "enum") {
$conds[] = (in_array(0, $val) ? "$name IS NULL OR " : "") . "$name IN (" . implode(", ", array_map('Adminer\q', $val)) . ")"; $in = array();
foreach ($val as $val1) {
if (preg_match('~val-~', $val1)) {
$in[] = q(substr($val1, 4));
}
}
$conds[] = (in_array("null", $val) ? "$name IS NULL OR " : "") . ($in ? "$name IN (" . implode(", ", $in) . ")" : "0");
} else { } else {
$text_type = preg_match('~char|text|enum|set~', $field["type"]); $text_type = preg_match('~char|text|enum|set~', $field["type"]);
$value = adminer()->processInput($field, (!$op && $text_type && preg_match('~^[^%]+$~', $val) ? "%$val%" : $val)); $value = adminer()->processInput($field, (!$op && $text_type && preg_match('~^[^%]+$~', $val) ? "%$val%" : $val));
@@ -456,8 +465,8 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
function editInput($table, $field, $attrs, $value) { function editInput($table, $field, $attrs, $value) {
if ($field["type"] == "enum") { if ($field["type"] == "enum") {
return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='-1' checked><i>" . lang('original') . "</i></label> " : "") return (isset($_GET["select"]) ? "<label><input type='radio'$attrs value='orig' checked><i>" . lang('original') . "</i></label> " : "")
. enum_input("radio", $attrs, $field, ($value || isset($_GET["select"]) ? $value : ""), ($field["null"] ? "" : null)) . enum_input("radio", $attrs, $field, $value, lang('empty'))
; ;
} }
$options = $this->foreignKeyOptions($table, $field["field"], $value); $options = $this->foreignKeyOptions($table, $field["field"], $value);

View File

@@ -43,8 +43,8 @@ function send_mail(string $email, string $subject, string $message, string $from
} }
/** Check whether the column looks like boolean /** Check whether the column looks like boolean
* @param Field $field single field returned from fields() * @param array{type: string, length?: string} $field single field returned from fields()
*/ */
function like_bool(array $field): bool { function like_bool(array $field): bool {
return preg_match("~bool|(tinyint|bit)\\(1\\)~", $field["full_type"]); return $field["type"] == "bool" || (preg_match('~bit|tinyint~', $field["type"]) && $field["length"] == 1);
} }

View File

@@ -53,13 +53,3 @@ function whisperClick(event) {
return false; return false;
} }
} }
/** Add new attachment field
* @this HTMLInputElement
*/
function emailFileChange() {
const el = this.cloneNode(true);
this.onchange = () => { };
el.value = '';
this.parentNode.appendChild(el);
}

2
externals/jush vendored

View File

@@ -116,7 +116,6 @@
<rule ref="Squiz.PHP.NonExecutableCode"/> <rule ref="Squiz.PHP.NonExecutableCode"/>
<rule ref="Squiz.Scope.StaticThisUsage"/> <rule ref="Squiz.Scope.StaticThisUsage"/>
<rule ref="Squiz.WhiteSpace.FunctionOpeningBraceSpace"/> <rule ref="Squiz.WhiteSpace.FunctionOpeningBraceSpace"/>
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing"/> <rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing"/>

View File

@@ -10,18 +10,19 @@ parameters:
# not real problems # not real problems
- identifier: include.fileNotFound # includes in include/ relative from index.php - identifier: include.fileNotFound # includes in include/ relative from index.php
- identifier: includeOnce.fileNotFound # ./adminer-plugins.php
- "~^Function (set_magic_quotes_runtime|mysql_)~" # PHP < 7 functions - "~^Function (set_magic_quotes_runtime|mysql_)~" # PHP < 7 functions
- "~an unknown class OCI-?Lob~" # this looks like PHPStan bug - "~an unknown class OCI-?Lob~" # this looks like PHPStan bug
- "~^Variable \\$error might not be defined~" # declared in bootstrap.inc.php - "~^Variable \\$error might not be defined~" # declared in bootstrap.inc.php
- "~^Constant LANG not found~" # defined in lang.inc.php - "~^Constant LANG not found~" # defined in lang.inc.php
- "~ an undefined \\w+ Adminer\\\\Db::~" # defined in that versions of Db - "~ an undefined \\w+ Adminer\\\\Db::~" # defined in that versions of Db
- "~^Call to an undefined method Adminer\\\\Result::seek~" # defined in MS SQL - "~^Call to an undefined method Adminer\\\\Result::seek~" # defined in MS SQL
- "~^Call to an undefined method Adminer\\\\Driver::setUserTypes~" # defined in PostgreSQL - "~^Call to an undefined method Adminer\\\\Driver::(setUserTypes|tableOid)~" # defined in PostgreSQL
- "~^Access to an undefined property Adminer\\\\Driver::\\$nsOid~" # defined in PostgreSQL
- "~expects int, float given~" # this will work - "~expects int, float given~" # this will work
- "~expects bool~" # truthy values - "~expects bool~" # truthy values
- "~fread expects int<1, max>, 100000~" # 1e6 - "~fread expects int<1, max>, 100000~" # 1e6
- "~'strlen' given~" # used as a bool callback - "~'strlen' given~" # used as a bool callback
- "~between int<70100~" # PHP_VERSION_ID check
- -
message: "~ type specified~" # duplicate functions and methods message: "~ type specified~" # duplicate functions and methods
@@ -39,11 +40,13 @@ parameters:
- identifier: booleanAnd.leftAlwaysTrue - identifier: booleanAnd.leftAlwaysTrue
- identifier: booleanAnd.rightAlwaysTrue - identifier: booleanAnd.rightAlwaysTrue
- identifier: booleanAnd.rightAlwaysFalse - identifier: booleanAnd.rightAlwaysFalse
- identifier: booleanOr.rightAlwaysTrue
- identifier: ternary.alwaysTrue - identifier: ternary.alwaysTrue
- identifier: if.alwaysTrue - identifier: if.alwaysTrue
- identifier: while.alwaysTrue
- identifier: isset.offset - identifier: isset.offset
- identifier: deadCode.unreachable - identifier: deadCode.unreachable
- "~Function Adminer\\\\get_driver\\(\\) never returns null~"
- "~on array\\{}~"
paths: paths:
- adminer/drivers/mysql.inc.php # other drivers inherit the annotations so we take them from here - adminer/drivers/mysql.inc.php # other drivers inherit the annotations so we take them from here
@@ -61,8 +64,8 @@ parameters:
max: 80499 max: 80499
typeAliases: 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, inherited?:numeric-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}" 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 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}" 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>, algorithm?:string, partial?:string}" Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>, algorithm?:string, partial?:string}"

View File

@@ -23,5 +23,6 @@ class AdminerDotJs extends Adminer\Plugin {
'pl' => array('' => 'Wczytuj adminer.js'), 'pl' => array('' => 'Wczytuj adminer.js'),
'ro' => array('' => 'Încarcă adminer.js'), 'ro' => array('' => 'Încarcă adminer.js'),
'ja' => array('' => 'adminer.js を読込み'), 'ja' => array('' => 'adminer.js を読込み'),
'hr' => array('' => 'Učitava adminer.js'),
); );
} }

View File

@@ -11,15 +11,26 @@ class AdminerBackwardKeys extends Adminer\Plugin {
function backwardKeys($table, $tableName) { function backwardKeys($table, $tableName) {
$return = array(); $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 ( foreach (
Adminer\get_rows($q = "SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME 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 FROM information_schema.key_column_usage s" . (Adminer\JUSH == "sql" ? "
WHERE TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . " WHERE table_schema = " . Adminer\q(Adminer\DB) . "
AND REFERENCED_TABLE_SCHEMA = " . Adminer\q(Adminer\DB) . " AND referenced_table_schema = " . Adminer\q(Adminer\DB) . "
AND REFERENCED_TABLE_NAME = " . Adminer\q($table) . " AND referenced_table_name" : "
ORDER BY ORDINAL_POSITION", null, "") as $row 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) { foreach ($return as $key => $val) {
$name = Adminer\adminer()->tableName(Adminer\table_status1($key, true)); $name = Adminer\adminer()->tableName(Adminer\table_status1($key, true));
@@ -40,9 +51,12 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
$link = Adminer\ME . 'select=' . urlencode($table); $link = Adminer\ME . 'select=' . urlencode($table);
$i = 0; $i = 0;
foreach ($cols as $column => $val) { foreach ($cols as $column => $val) {
if (!isset($row[$val])) {
continue 2;
}
$link .= Adminer\where_link($i++, $column, $row[$val]); $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); $link = Adminer\ME . 'edit=' . urlencode($table);
foreach ($cols as $column => $val) { foreach ($cols as $column => $val) {
$link .= "&set" . urlencode("[" . Adminer\bracket_escape($column) . "]") . "=" . urlencode($row[$val]); $link .= "&set" . urlencode("[" . Adminer\bracket_escape($column) . "]") . "=" . urlencode($row[$val]);
@@ -61,5 +75,6 @@ ORDER BY ORDINAL_POSITION", null, "") as $row
'de' => array('' => 'Links zu Tabellen anzeigen die auf die aktuelle Zeile verweisen, wie im Adminer Editor'), 'de' => array('' => 'Links zu Tabellen anzeigen die auf die aktuelle Zeile verweisen, wie im Adminer Editor'),
'ja' => array('' => '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'), 'pl' => array('' => 'Wyświetlaj linki do tabel odnoszących się do bieżącego wiersza, tak samo jak w Edytorze administratora'),
'hr' => array('' => 'Prikazuje veze na tablice koje referenciraju trenutni redak, kao u Adminer Editoru'),
); );
} }

View File

@@ -36,5 +36,6 @@ onbeforeunload = () => editChanged;
'de' => array('' => 'Zeigt eine Bestätigung an bevor die Seite neu geladen wird, wenn ein Formularfeld geändert wurde'), 'de' => array('' => 'Zeigt eine Bestätigung an bevor die Seite neu geladen wird, wenn ein Formularfeld geändert wurde'),
'ja' => array('' => 'フォームの列が変更された時、ページを再読込みする前に確認を表示'), 'ja' => array('' => 'フォームの列が変更された時、ページを再読込みする前に確認を表示'),
'pl' => array('' => 'Wyświetlaj potwierdzenie przed rozładowaniem strony, jeśli pole formularza zostało zmienione'), 'pl' => array('' => 'Wyświetlaj potwierdzenie przed rozładowaniem strony, jeśli pole formularza zostało zmienione'),
'hr' => array('' => 'Prikazuje potvrdu prije napuštanja stranice ako je polje obrasca promijenjeno'),
); );
} }

View File

@@ -96,5 +96,14 @@ class AdminerConfig extends Adminer\Plugin {
'Use %s if exists' => 'あれば %s を使う', 'Use %s if exists' => 'あれば %s を使う',
'Use builtin design' => '組込みのデザインを使う', 'Use builtin design' => '組込みのデザインを使う',
), ),
'hr' => array(
'' => 'Nikola Radovanović - cobisimo@gmail.com',
'Configuration saved.' => 'Konfiguracija je spremljena.',
'Configuration' => 'Konfiguracija',
'Only some plugins support configuration, e.g. %s.' => 'Samo neki dodaci podržavaju konfiguraciju, npr. %s.',
'Use %s if exists' => 'Koristi %s ako postoji',
'Use builtin design' => 'Koristi ugrađeni dizajn',
'Design' => 'Dizajn',
),
); );
} }

View File

@@ -50,5 +50,6 @@ if (saved) {
'de' => array('' => 'Umschalten zwischen hellem und dunklem Design erlauben'), 'de' => array('' => 'Umschalten zwischen hellem und dunklem Design erlauben'),
'ja' => array('' => 'ダークモードへの切替え'), 'ja' => array('' => 'ダークモードへの切替え'),
'pl' => array('' => 'Zezwalaj na przełączanie trybu jasnego i ciemnego'), 'pl' => array('' => 'Zezwalaj na przełączanie trybu jasnego i ciemnego'),
'hr' => array('' => 'Omogućuje prebacivanje između svijetlog i tamnog izgleda'),
); );
} }

View File

@@ -32,5 +32,6 @@ class AdminerDatabaseHide extends Adminer\Plugin {
'pl' => array('' => 'Ukryj niektóre bazy danych w interfejsie tylko po to, aby ulepszyć motyw, a nie wtyczkę zabezpieczającą'), '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'), '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 上で表示禁止 (デザイン的な効果のみでセキュリティ的には効果なし)'), 'ja' => array('' => '一部データベースを UI 上で表示禁止 (デザイン的な効果のみでセキュリティ的には効果なし)'),
'hr' => array('' => 'Sakriva neke baze podataka iz sučelja samo radi poboljšanja izgleda, nije sigurnosni dodatak'),
); );
} }

View File

@@ -16,7 +16,7 @@ class AdminerDesigns extends Adminer\Plugin {
$this->designs = $designs; $this->designs = $designs;
} }
function headers() { function afterConnect() {
if (isset($_POST["design"]) && Adminer\verify_token()) { if (isset($_POST["design"]) && Adminer\verify_token()) {
Adminer\restart_session(); Adminer\restart_session();
$_SESSION["design"] = $_POST["design"]; $_SESSION["design"] = $_POST["design"];
@@ -49,5 +49,6 @@ class AdminerDesigns extends Adminer\Plugin {
'pl' => array('' => 'Zezwalaj na przełączanie motywów'), 'pl' => array('' => 'Zezwalaj na przełączanie motywów'),
'ro' => array('' => 'Permiteți comutarea designurilor'), 'ro' => array('' => 'Permiteți comutarea designurilor'),
'ja' => array('' => 'テーマ設定を有効化'), 'ja' => array('' => 'テーマ設定を有効化'),
'hr' => array('' => 'Omogućuje promjenu dizajna'),
); );
} }

View File

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

View File

@@ -15,14 +15,17 @@ if (isset($_GET["clickhouse"])) {
function rootQuery($db, $query) { function rootQuery($db, $query) {
$file = @file_get_contents("$this->url/?database=$db", false, stream_context_create(array('http' => array( $file = @file_get_contents("$this->url/?database=$db", false, stream_context_create(array('http' => array(
'method' => 'POST', 'method' => 'POST',
'content' => $this->isQuerySelectLike($query) ? "$query FORMAT JSONCompact" : $query, 'content' => $query,
'header' => 'Content-type: application/x-www-form-urlencoded', 'header' => array(
'Content-Type: application/x-www-form-urlencoded',
'X-ClickHouse-Format: JSONCompact',
),
'ignore_errors' => 1, 'ignore_errors' => 1,
'follow_location' => 0, 'follow_location' => 0,
'max_redirects' => 0, 'max_redirects' => 0,
)))); ))));
if ($file === false || !preg_match('~^HTTP/[0-9.]+ 2~i', $http_response_header[0])) { if ($file === false || preg_match('~^HTTP/[0-9.]+ 403~i', $http_response_header[0])) {
$this->error = lang('Invalid credentials.'); $this->error = lang('Invalid credentials.');
return false; return false;
} }
@@ -45,11 +48,18 @@ if (isset($_GET["clickhouse"])) {
} }
} }
} }
// 400 == Syntax error
// 404 == Unknown expression identifier
// 500 == Column 'x' is not under aggregate function and not in GROUP BY keys
if (preg_match('~^HTTP/[0-9.]+ [45]~i', $http_response_header[0])) {
$this->error = $return['exception'];
return false;
}
return new Result($return); return new Result($return);
} }
function isQuerySelectLike($query) { function isQuerySelectLike($query) {
return (bool) preg_match('~^(select|show)~i', $query); return (bool) preg_match('~^\s*(select|show|with)~i', $query);
} }
function query($query, $unbuffered = false) { function query($query, $unbuffered = false) {
@@ -75,7 +85,7 @@ if (isset($_GET["clickhouse"])) {
class Result { class Result {
public $num_rows, $columns, $meta; public $num_rows, $columns, $meta;
private $rows, $offset = 0; private $rows = array(), $offset = 0;
function __construct($result) { function __construct($result) {
foreach ($result['data'] as $item) { foreach ($result['data'] as $item) {
@@ -243,7 +253,7 @@ if (isset($_GET["clickhouse"])) {
} }
function limit($query, $where, $limit, $offset = 0, $separator = " ") { function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return " $query$where" . ($limit ? $separator . "LIMIT $limit" . ($offset ? ", $offset" : "") : ""); return " $query$where" . ($limit ? $separator . "LIMIT " . ($offset ? "$offset, " : "") . $limit : "");
} }
function limit1($table, $query, $where, $separator = "\n") { function limit1($table, $query, $where, $separator = "\n") {
@@ -274,7 +284,7 @@ if (isset($_GET["clickhouse"])) {
function table_status($name = "", $fast = false) { function table_status($name = "", $fast = false) {
$return = array(); $return = array();
$tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q(connection()->_db)); $tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q(connection()->_db) . ($name != "" ? " AND name = " . q($name) : ""));
foreach ($tables as $table) { foreach ($tables as $table) {
$return[$table['name']] = array( $return[$table['name']] = array(
'Name' => $table['name'], 'Name' => $table['name'],

View File

@@ -287,10 +287,6 @@ if (isset($_GET["elastic"])) {
return !!$this->conn->affected_rows; return !!$this->conn->affected_rows;
} }
function convertOperator($operator): string {
return $operator == "LIKE %%" ? "should" : $operator;
}
} }
function support($feature) { function support($feature) {
@@ -412,6 +408,11 @@ if (isset($_GET["elastic"])) {
return $table_status["Engine"] == "view"; 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() { function error() {
return h(connection()->error); return h(connection()->error);
} }
@@ -533,15 +534,18 @@ if (isset($_GET["elastic"])) {
} }
} }
/** Drop types function drop_views(array $tables): bool {
* @param list<string> $tables $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 { function drop_tables(array $tables): bool {
$return = true; $return = true;
foreach ($tables as $table) { //! convert to bulk api foreach ($tables as $table) { //! convert to bulk api
$return = $return && connection()->rootQuery(urlencode($table), null, 'DELETE'); $return = $return && connection()->rootQuery(urlencode($table), null, 'DELETE');
} }
return $return; return $return;
} }

459
plugins/drivers/igdb.php Normal file
View File

@@ -0,0 +1,459 @@
<?php
/** Driver for https://api-docs.igdb.com/
* @link https://demo.adminer.org/igdb/?igdb=IGDB&db=api
* username: your Client-ID
* password: your access token from https://id.twitch.tv/oauth2/token
* @link https://www.adminer.org/static/plugins/igdb.png
*/
namespace Adminer;
add_driver("igdb", "APICalypse");
if (isset($_GET["igdb"])) {
define('Adminer\DRIVER', "igdb");
class Db extends SqlDb {
public $extension = "json";
public $server_info = "v4";
private $username;
private $password;
function attach($server, $username, $password): string {
$this->username = $username;
$this->password = $password;
return '';
}
function select_db($database) {
return ($database == "api");
}
function request($endpoint, $query, $method = 'POST') {
$context = stream_context_create(array('http' => array(
'method' => $method,
'header' => array(
"Content-Type: text/plain",
"Client-ID: $this->username",
"Authorization: Bearer $this->password",
),
'content' => $query,
'ignore_errors' => true,
)));
$response = file_get_contents("https://api.igdb.com/v4/$endpoint", false, $context);
$return = json_decode($response, true);
if ($http_response_header[0] != 'HTTP/1.1 200 OK') {
if (is_array($return)) {
foreach (is_array($return[0]) ? $return : array($return) as $rows) {
foreach ($rows as $key => $val) {
$this->error .= '<b>' . h($key) . ':</b> ' . (is_url($val) ? '<a href="' . h($val) . '"' . target_blank() . '>' . h($val) . '</a>' : h($val)) . '<br>';
}
}
} else {
$this->error = htmlspecialchars(strip_tags($response), 0, null, false);
}
return false;
}
return $return;
}
function query($query, $unbuffered = false) {
if (preg_match('~^SELECT COUNT\(\*\) FROM (\w+)( WHERE ((MATCH \(search\) AGAINST \((.+)\))|.+))?$~', $query, $match)) {
return new Result(array($match[1] == 'dumps' ? array('count' => 50) : $this->request("$match[1]/count", ($match[5] ? 'search "' . addcslashes($match[5], '\\"') . '";'
: ($match[3] ? 'where ' . str_replace(' AND ', ' & ', $match[3]) . ';'
: ''
)))));
}
if (preg_match('~^\s*(GET|POST|DELETE)\s+([\w/?=]+)\s*;\s*(.*)$~s', $query, $match)) {
$endpoint = $match[2];
$response = $this->request($endpoint, $match[3], $match[1]);
if ($response === false) {
return $response;
}
$return = new Result(is_array($response[0]) ? $response : array($response));
$return->table = $endpoint;
if ($endpoint == 'multiquery') {
$return->results = $response;
}
return $return;
}
$this->error = "Syntax:<br>POST &lt;endpoint>; fields ...;";
return false;
}
function store_result() {
if ($this->multi && ($result = current($this->multi->results))) {
echo "<h3>" . h($result['name']) . "</h3>\n";
$this->multi->__construct($result['count'] ? array(array('count' => $result['count'])) : $result['result']);
}
return $this->multi;
}
function next_result(): bool {
return $this->multi && next($this->multi->results);
}
function quote($string): string {
return $string;
}
}
class Result {
public $num_rows;
public $table;
public $results = array();
private $result;
private $fields;
function __construct($result) {
$keys = array();
foreach ($result as $i => $row) {
foreach ($row as $key => $val) {
$keys[$key] = null;
if (is_array($val) && is_int($val[0])) {
$result[$i][$key] = "(" . implode(",", $val) . ")";
}
}
}
foreach ($result as $i => $row) {
$result[$i] = array_merge($keys, $row);
}
$this->result = $result;
$this->num_rows = count($result);
$this->fields = array_keys(idx($result, 0, array()));
}
function fetch_assoc() {
$row = current($this->result);
next($this->result);
return $row;
}
function fetch_row() {
$row = $this->fetch_assoc();
return ($row ? array_values($row) : false);
}
function fetch_field(): \stdClass {
$field = current($this->fields);
next($this->fields);
return ($field != '' ? (object) array('name' => $field, 'type' => 15, 'charsetnr' => 0, 'orgtable' => $this->table) : false);
}
}
class Driver extends SqlDriver {
static $extensions = array("json");
static $jush = "igdb";
private static $docsFilename = __DIR__ . DIRECTORY_SEPARATOR . 'igdb-api.html';
public $delimiter = ";;";
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~");
public $tables = array();
public $links = array();
public $fields = array();
public $foreignKeys = array();
public $foundRows = null;
static function connect(string $server, string $username, string $password) {
if (!file_exists(self::$docsFilename)) {
return "Download https://api-docs.igdb.com/ and save it as " . self::$docsFilename; // copy() doesn't work - bot protection
}
return parent::connect($server, $username, $password);
}
function __construct($connection) {
parent::__construct($connection);
libxml_use_internal_errors(true);
$dom = new \DOMDocument();
$dom->loadHTMLFile(self::$docsFilename);
$xpath = new \DOMXPath($dom);
$els = $xpath->query('//div[@class="content"]/*');
$link = '';
foreach ($els as $i => $el) {
if ($el->tagName == 'h2') {
$link = $el->getAttribute('id');
}
if ($el->nodeValue == 'Request Path') {
$table = preg_replace('~^https://api.igdb.com/v4/~', '', $els[$i+1]->firstElementChild->nodeValue);
$comment = $els[$i-1]->tagName == 'p' ? $els[$i-1]->nodeValue : '';
if (preg_match('~^DEPRECATED!~', $comment)) {
continue;
}
$this->fields[$table]['id'] = array('full_type' => 'bigint', 'comment' => '');
$this->links[$link] = $table;
$this->tables[$table] = array('Name' => $table, 'Engine' => 'endpoint', 'Comment' => $comment);
foreach ($xpath->query('tbody/tr', $els[$i+2]) as $tr) {
$tds = $xpath->query('td', $tr);
$field = $tds[0]->nodeValue;
$comment = $tds[2]->nodeValue;
if ($field != 'checksum' && $field != 'content_descriptions' && !preg_match('~^DEPRECATED!~', $comment)) {
$this->fields[$table][$field] = array(
'full_type' => str_replace(' ', ' ', $tds[1]->nodeValue),
'comment' => str_replace(' ', ' ', $comment),
);
$ref = $xpath->query('a/@href', $tds[1]);
if (count($ref) && !in_array($ref[0]->value, array('#game-version-feature-enums', '#tag-numbers'))) {
$this->foreignKeys[$table][$field] = substr($ref[0]->value, 1);
} elseif ($field === 'game_id') { // game_time_to_beats, popularity_primitives
$this->foreignKeys[$table][$field] = 'game';
}
}
}
uksort($this->fields[$table], function ($a, $b) use ($table) {
return (($b == 'id') - ($a == 'id'))
?: (($b == 'name') - ($a == 'name'))
?: (($a == 'updated_at') - ($b == 'updated_at'))
?: (($a == 'created_at') - ($b == 'created_at'))
?: (!idx($this->foreignKeys[$table], $b) - !idx($this->foreignKeys[$table], $a))
?: ($a < $b ? -1 : 1)
;
});
}
}
$this->tables['dumps'] = array(
'Name' => 'dumps',
'Engine' => 'dumps',
'Comment' => 'Daily updated CSV Data Dumps which can be used to kick start your projects or keep your data up to date (within 24 hours)',
);
$this->links['data-dumps'] = 'dumps';
$this->fields['dumps'] = array(
's3_url' => array('comment' => 'The download Url is a presigned S3 url that is valid for 5 minutes'),
'endpoint' => array(),
'file_name' => array(),
'size_bytes' => array(),
'updated_at' => array('full_type' => 'datetime'),
'schema_version' => array('full_type' => 'datetime', 'comment' => 'Will change when the schema changes'),
'schema' => array('comment' => 'Reflects the current data structure and data type that the Dump is using'),
);
$this->tables['webhooks'] = array(
'Name' => 'webhooks',
'Engine' => 'webhooks',
'Comment' => 'Webhooks allow us to push data to you when it is added, updated, or deleted',
);
$this->links['webhooks'] = 'webhooks';
$this->fields['webhooks'] = array(
'endpoint' => array(
'full_type' => 'String',
'comment' => 'Specify what type of data you want from your webhook',
'privileges' => array('insert' => 1),
),
'id' => array('comment' => 'A unique ID for the webhook'),
'url' => array(
'full_type' => 'String',
'length' => '100',
'comment' => 'Your prepared url that is ready to accept data from us',
'privileges' => array('select' => 1, 'insert' => 1),
),
'method' => array(
'full_type' => 'enum',
'length' => "('create','delete','update')",
'comment' => 'The type of data you are expecting to your url, there are three types of methods',
'privileges' => array('insert' => 1),
),
'category' => array('comment' => 'Based on the endpoint you chose'),
'sub_category' => array('comment' => 'Based on your method (can be 0, 1, 2)'),
'active' => array('comment' => 'Is the webhook currently active'),
'api_key' => array('comment' => 'Displays the api key the webhook is connected to'),
'secret' => array(
'full_type' => 'String',
'comment' => 'Your “secret” password for your webhook',
'privileges' => array('select' => 1, 'insert' => 1),
),
'created_at' => array('comment' => 'Created at date'),
'updated_at' => array('comment' => 'Updated at date'),
);
}
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
$query = '';
$search = preg_match('~^MATCH \(search\) AGAINST \((.+)\)$~', $where[0], $match);
if ($search) {
$query = 'search "' . addcslashes($match[1], '\\"') . "\";\n";
unset($where[0]);
}
foreach ($where as $i => $val) {
$where[$i] = str_replace(' OR ', ' | ', $val);
}
$columns = ($select != array('*') ? $select : array_keys($this->fields[$table]));
$common = ($where ? "\nwhere " . implode(" & ", $where) . ";" : "");
$method = ($table == 'webhooks' || $table == 'dumps' ? 'GET' : 'POST');
if ($method == 'POST') {
$query .= "fields " . implode(",", $select) . ";"
. ($select == array('*') ? "\nexclude checksum;" : "")
. $common
. ($order ? "\nsort " . strtolower(implode(",", $order)) . ";" : "")
. "\nlimit $limit;"
. ($page ? "\noffset " . ($page * $limit) . ";" : "")
;
}
$start = microtime(true);
$multi = (!$search && $method == 'POST' && array_key_exists($table, driver()->tables));
$realQuery = str_replace("*;\nexclude checksum", implode(',', $columns), $query); // exclude deprecated columns
$return = ($multi
? $this->conn->request('multiquery', "query $table \"result\" { $realQuery };\nquery $table/count \"count\" { $common };")
: $this->conn->request(($method == 'GET' && $where ? "$table/" . reset($_GET["where"]) : $table), $realQuery, $method)
);
if ($print) {
echo adminer()->selectQuery("$method $table;\n$query", $start);
}
if ($return === false) {
return $return;
}
$this->foundRows = ($multi ? $return[1]['count'] : null);
$return = ($multi ? $return[0]['result'] : $return);
if ($table == 'dumps' && $where) {
$return = array($return);
} elseif ($return && $method == 'POST') {
$return[0] = array_merge(array_fill_keys($columns, null), $return[0]);
}
return new Result($return);
}
function insert($table, $set) {
$content = array();
foreach ($set as $key => $val) {
if ($key != 'endpoint') {
$content[] = urlencode($key) . '=' . urlencode($val);
}
}
return queries("POST $set[endpoint]/$table; " . implode('&', $content));
}
function delete($table, $queryWhere, $limit = 0) {
preg_match_all('~\bid = (\d+)~', $queryWhere, $matches);
$this->conn->affected_rows = 0;
foreach ($matches[1] as $id) {
$result = queries("DELETE $table/$id;");
if (!$result) {
return false;
}
$row = $result->fetch_row();
if (!$row[0]) {
$this->conn->error = "ID $id not found.";
return false;
}
$this->conn->affected_rows++;
}
return true;
}
function value($val, $field): ?string {
return ($val && in_array($field['full_type'], array('Unix Time Stamp', 'datetime')) ? str_replace(' 00:00:00', '', gmdate('Y-m-d H:i:s', $val)) : $val);
}
function tableHelp($name, $is_view = false) {
return strtolower("https://api-docs.igdb.com/#" . array_search($name, $this->links));
}
}
function logged_user() {
return $_GET["username"];
}
function get_databases($flush) {
return array("api");
}
function collations() {
return array();
}
function db_collation($db, $collations) {
}
function information_schema($db) {
}
function indexes($table, $connection2 = null) {
$return = array(array('type' => 'PRIMARY', 'columns' => array($table == 'dumps' ? 'endpoint' : 'id')));
if (in_array($table, array('characters', 'collections', 'games', 'platforms', 'themes'))) { // https://api-docs.igdb.com/#search-1
$return[] = array("type" => "FULLTEXT", "columns" => array("search"));
}
return $return;
}
function fields($table) {
$return = array();
foreach (driver()->fields[$table] ?: array() as $key => $val) {
$type = strtolower(preg_replace('~ .*~', '', $val['full_type']));
$return[$key] = $val + array(
"field" => $key,
"type" => ($type == 'reference' ? 'int' : $type), // align right reference columns
"privileges" => array("select" => 1) + ($table == 'webhooks' || $table == 'dumps' ? array() : array("where" => 1, "order" => 1)),
);
}
return $return;
}
function convert_field($field) {
}
function unconvert_field($field, $return) {
return $return;
}
function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return $query;
}
function idf_escape($idf) {
return $idf;
}
function table($idf) {
return idf_escape($idf);
}
function foreign_keys($table) {
$return = array();
foreach (driver()->foreignKeys[$table] ?: array() as $key => $val) {
$return[] = array(
'table' => driver()->links[$val],
'source' => array($key),
'target' => array('id'),
);
}
return $return;
}
function tables_list() {
return array_fill_keys(array_keys(table_status()), 'table');
}
function table_status($name = "", $fast = false) {
$tables = driver()->tables;
return ($name != '' ? ($tables[$name] ? array($name => $tables[$name]) : array()) : $tables);
}
function count_tables($databases) {
return array(reset($databases) => count(tables_list()));
}
function error() {
return connection()->error;
}
function is_view($table_status) {
return false;
}
function found_rows($table_status, $where) {
return driver()->foundRows;
}
function fk_support($table_status) {
return true;
}
function last_id($result): string {
$row = $result->fetch_assoc();
return (string) $row['id'];
}
function support($feature) {
return in_array($feature, array('columns', 'comment', 'sql', 'table'));
}
}

View File

@@ -25,7 +25,8 @@ if (isset($_GET["imap"])) {
private $imap; private $imap;
function attach($server, $username, $password): string { function attach($server, $username, $password): string {
$this->mailbox = "{" . "$server:993/ssl}"; // Adminer disallows specifying privileged port in server name list($host, $port) = host_port($server);
$this->mailbox = "{" . "$host:" . ($port ?: 993) . "/ssl}"; // Adminer disallows specifying privileged port in server name
$this->imap = @imap_open($this->mailbox, $username, $password, OP_HALFOPEN, 1); $this->imap = @imap_open($this->mailbox, $username, $password, OP_HALFOPEN, 1);
return ($this->imap ? '' : imap_last_error()); return ($this->imap ? '' : imap_last_error());
} }

View File

@@ -174,5 +174,6 @@ DROP PROCEDURE adminer_alter;
'pl' => array('' => 'Eksportuje jedną bazę danych (np. programistyczną), aby można ją było zsynchronizować z inną bazą danych (np. produkcyjną)'), '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)'), '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('' => 'データベース (開発用など) をエクスポートし、別のデータベース (本番用など) と同期'), 'ja' => array('' => 'データベース (開発用など) をエクスポートし、別のデータベース (本番用など) と同期'),
'hr' => array('' => 'Izvozi bazu podataka (npr. razvojnu) tako da se može sinkronizirati s drugom bazom (npr. produkcijskom)'),
); );
} }

View File

@@ -43,5 +43,6 @@ class AdminerDumpBz2 extends Adminer\Plugin {
'pl' => array('' => 'Zrzuć do formatu Bzip2'), 'pl' => array('' => 'Zrzuć do formatu Bzip2'),
'ro' => array('' => 'Dump în format Bzip2'), 'ro' => array('' => 'Dump în format Bzip2'),
'ja' => array('' => 'Bzip2 形式でエクスポート'), 'ja' => array('' => 'Bzip2 形式でエクスポート'),
'hr' => array('' => 'Izvoz u Bzip2 format'),
); );
} }

View File

@@ -9,7 +9,7 @@
class AdminerDumpDate extends Adminer\Plugin { class AdminerDumpDate extends Adminer\Plugin {
function dumpFilename($identifier) { function dumpFilename($identifier) {
return Adminer\friendly_url(($identifier != "" ? $identifier : (Adminer\SERVER != "" ? Adminer\SERVER : "localhost")) . "-" . Adminer\get_val("SELECT NOW()")); return Adminer\friendly_url(($identifier != "" ? $identifier : (Adminer\SERVER ?: "localhost")) . "-" . Adminer\get_val("SELECT NOW()"));
} }
protected $translations = array( protected $translations = array(
@@ -18,5 +18,6 @@ class AdminerDumpDate extends Adminer\Plugin {
'pl' => array('' => 'Dołącz bieżącą datę i godzinę do nazwy pliku eksportu'), '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'), 'ro' => array('' => 'Includeți data și ora curentă în numele fișierului de export'),
'ja' => array('' => 'エクスポートファイル名に現在日時を含める'), 'ja' => array('' => 'エクスポートファイル名に現在日時を含める'),
'hr' => array('' => 'Dodaje trenutni datum i vrijeme u naziv datoteke izvoza'),
); );
} }

View File

@@ -64,5 +64,6 @@ class AdminerDumpJson extends Adminer\Plugin {
'pl' => array('' => 'Zrzuć do formatu JSON'), 'pl' => array('' => 'Zrzuć do formatu JSON'),
'ro' => array('' => 'Dump în format JSON'), 'ro' => array('' => 'Dump în format JSON'),
'ja' => array('' => 'JSON 形式でエクスポート'), 'ja' => array('' => 'JSON 形式でエクスポート'),
'hr' => array('' => 'Izvoz u JSON format'),
); );
} }

View File

@@ -52,5 +52,6 @@ class AdminerDumpPhp extends Adminer\Plugin {
'pl' => array('' => 'Zrzucaj do formatu PHP'), 'pl' => array('' => 'Zrzucaj do formatu PHP'),
'ro' => array('' => 'Dump în format PHP'), 'ro' => array('' => 'Dump în format PHP'),
'ja' => array('' => 'PHP 形式でエクスポート'), 'ja' => array('' => 'PHP 形式でエクスポート'),
'hr' => array('' => 'Izvoz u PHP format'),
); );
} }

View File

@@ -58,5 +58,6 @@ class AdminerDumpXml extends Adminer\Plugin {
'pl' => array('' => 'Zrzut do formatu XML w strukturze <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'), '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'), 'ja' => array('' => '構造化 XML 形式でエクスポート <database name=""><table name=""><column name="">value'),
'hr' => array('' => 'Izvoz u XML format u strukturi <database name=""><table name=""><column name="">vrijednost'),
); );
} }

View File

@@ -47,5 +47,6 @@ class AdminerDumpZip extends Adminer\Plugin {
'pl' => array('' => 'Zrzuć do formatu ZIP'), 'pl' => array('' => 'Zrzuć do formatu ZIP'),
'ro' => array('' => 'Dump în format ZIP'), 'ro' => array('' => 'Dump în format ZIP'),
'ja' => array('' => 'ZIP 形式でエクスポート'), 'ja' => array('' => 'ZIP 形式でエクスポート'),
'hr' => array('' => 'Izvoz u ZIP format'),
); );
} }

View File

@@ -57,5 +57,6 @@ class AdminerEditCalendar extends Adminer\Plugin {
'pl' => array('' => 'Wyświetl interfejs jQuery Timepicker dla każdego pola daty i godziny'), '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'), 'ro' => array('' => 'Afișați jQuery UI Timepicker pentru fiecare câmp de dată și dată-timp'),
'ja' => array('' => '各日時列に jQuery UI の Timepicker を表示'), 'ja' => array('' => '各日時列に jQuery UI の Timepicker を表示'),
'hr' => array('' => 'Prikazuje jQuery UI Timepicker za svako polje datuma i datuma-vremena'),
); );
} }

View File

@@ -46,5 +46,6 @@ class AdminerEditForeign extends Adminer\Plugin {
'pl' => array('' => 'Wybierz klucz obcy w formularzu edycji'), 'pl' => array('' => 'Wybierz klucz obcy w formularzu edycji'),
'ro' => array('' => 'Selectați cheia străină în formularul de editare'), 'ro' => array('' => 'Selectați cheia străină în formularul de editare'),
'ja' => array('' => '外部キーを編集フォームで選択'), 'ja' => array('' => '外部キーを編集フォームで選択'),
'hr' => array('' => 'Odabir stranog ključa u obrascu za uređivanje'),
); );
} }

View File

@@ -20,5 +20,6 @@ class AdminerEditTextarea extends Adminer\Plugin {
'pl' => array('' => 'Użyj <textarea> dla char i varchar'), 'pl' => array('' => 'Użyj <textarea> dla char i varchar'),
'ro' => array('' => 'Utilizați <textarea> pentru char și varchar'), 'ro' => array('' => 'Utilizați <textarea> pentru char și varchar'),
'ja' => array('' => 'char や varchar に <textarea> を使用'), 'ja' => array('' => 'char や varchar に <textarea> を使用'),
'hr' => array('' => 'Koristi <textarea> za char i varchar polja'),
); );
} }

View File

@@ -43,5 +43,6 @@ class AdminerEditorSetup extends Adminer\Plugin {
'de' => array('' => 'Treiber, Server und Datenbank für die Verwendung mit Adminer Editor einrichten'), 'de' => array('' => 'Treiber, Server und Datenbank für die Verwendung mit Adminer Editor einrichten'),
'ja' => array('' => 'Adminer Editor で使用するドライバ、サーバ、データベースを設定'), 'ja' => array('' => 'Adminer Editor で使用するドライバ、サーバ、データベースを設定'),
'pl' => array('' => 'Konfiguruj sterownik, serwer i bazę danych do użycia z Adminer Editorem'), 'pl' => array('' => 'Konfiguruj sterownik, serwer i bazę danych do użycia z Adminer Editorem'),
'hr' => array('' => 'Postavlja upravljački program, poslužitelj i bazu podataka za korištenje s Adminer Editorom'),
); );
} }

View File

@@ -18,5 +18,6 @@ class AdminerEditorViews extends Adminer\Plugin {
'pl' => array('' => 'Wyświetlaj widoki w Adminer Editorze'), 'pl' => array('' => 'Wyświetlaj widoki w Adminer Editorze'),
'ro' => array('' => 'Afișează vizualizări în Adminer Editor'), 'ro' => array('' => 'Afișează vizualizări în Adminer Editor'),
'ja' => array('' => 'Adminer Editor にビューを表示'), 'ja' => array('' => 'Adminer Editor にビューを表示'),
'hr' => array('' => 'Prikazuje poglede u Adminer Editoru'),
); );
} }

View File

@@ -61,5 +61,6 @@ class AdminerEmailTable extends Adminer\Plugin {
'pl' => array('' => 'Pobieraj temat i wiadomość e-mail z bazy danych (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)'), 'ro' => array('' => 'Obțineți subiectul e-mailului și mesajul din baza de date (Adminer Editor)'),
'ja' => array('' => 'メールの件名と本文をデータベースから取得 (Adminer Editor)'), 'ja' => array('' => 'メールの件名と本文をデータベースから取得 (Adminer Editor)'),
'hr' => array('' => 'Dohvaća predmet i poruku e-pošte iz baze podataka (Adminer Editor)'),
); );
} }

View File

@@ -11,23 +11,23 @@ class AdminerEnumOption extends Adminer\Plugin {
function editInput($table, $field, $attrs, $value) { function editInput($table, $field, $attrs, $value) {
if ($field["type"] == "enum") { if ($field["type"] == "enum") {
$options = array(); $options = array();
$selected = $value; $selected = "val-$value";
if (isset($_GET["select"])) { if (isset($_GET["select"])) {
$options[-1] = Adminer\lang('original'); $options["orig"] = Adminer\lang('original');
if ($selected === null) { if ($value === null) {
$selected = -1; $selected = "orig";
} }
} }
if ($field["null"]) { if ($field["null"]) {
$options[""] = "NULL"; $options["null"] = "NULL";
if ($selected === null) { if ($value === null) {
$selected = ""; $selected = "null";
} }
} }
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches); preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
foreach ($matches[1] as $val) { foreach ($matches[1] as $val) {
$val = stripcslashes(str_replace("''", "'", $val)); $val = stripcslashes(str_replace("''", "'", $val));
$options[$val] = $val; $options["val-$val"] = $val;
} }
return "<select$attrs>" . Adminer\optionlist($options, $selected, 1) . "</select>"; // 1 - use keys return "<select$attrs>" . Adminer\optionlist($options, $selected, 1) . "</select>"; // 1 - use keys
} }
@@ -39,5 +39,6 @@ class AdminerEnumOption extends Adminer\Plugin {
'pl' => array('' => 'Użyj <select><option> do edycji enum zamiast <input type="radio">'), '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">'), 'ro' => array('' => 'Utilizați <select><option> pentru editarea enum în loc de <input type="radio">'),
'ja' => array('' => '列挙型の編集に <input type="radio"> ではなく <select><option> を使用'), 'ja' => array('' => '列挙型の編集に <input type="radio"> ではなく <select><option> を使用'),
'hr' => array('' => 'Koristi <select><option> za uređivanje enum polja umjesto <input type="radio">'),
); );
} }

View File

@@ -1,5 +1,5 @@
<?php <?php
//! delete //! handle delete
/** Edit fields ending with "_path" by <input type="file"> and link to the uploaded files from select /** Edit fields ending with "_path" by <input type="file"> and link to the uploaded files from select
* @link https://www.adminer.org/plugins/#use * @link https://www.adminer.org/plugins/#use
@@ -30,13 +30,13 @@ class AdminerFileUpload extends Adminer\Plugin {
function processInput($field, $value, $function = "") { function processInput($field, $value, $function = "") {
if (preg_match('~(.*)_path$~', $field["field"], $regs)) { if (preg_match('~(.*)_path$~', $field["field"], $regs)) {
$table = ($_GET["edit"] != "" ? $_GET["edit"] : $_GET["select"]); $table = ($_GET["edit"] != "" ? $_GET["edit"] : $_GET["select"]);
$name = "fields-$field[field]"; $name = $field["field"];
if ($_FILES[$name]["error"] || !preg_match("~(\\.($this->extensions))?\$~", $_FILES[$name]["name"], $regs2)) { if ($_FILES["fields"]["error"][$name] || !preg_match("~(\\.($this->extensions))?\$~", $_FILES["fields"]["name"][$name], $regs2)) {
return false; return false;
} }
//! unlink old //! unlink old
$filename = (function_exists('random_bytes') ? bin2hex(random_bytes(8)) : uniqid("", true)) . $regs2[0]; $filename = (function_exists('random_bytes') ? bin2hex(random_bytes(8)) : uniqid("", true)) . $regs2[0];
if (!move_uploaded_file($_FILES[$name]["tmp_name"], "$this->uploadPath$table/$regs[1]-$filename")) { if (!move_uploaded_file($_FILES["fields"]["tmp_name"][$name], $this->uploadPath . Adminer\friendly_url($table) . "/$regs[1]-$filename")) {
return false; return false;
} }
return Adminer\q($filename); return Adminer\q($filename);
@@ -45,7 +45,7 @@ class AdminerFileUpload extends Adminer\Plugin {
function selectVal($val, &$link, $field, $original) { function selectVal($val, &$link, $field, $original) {
if ($val != "" && preg_match('~(.*)_path$~', $field["field"], $regs)) { if ($val != "" && preg_match('~(.*)_path$~', $field["field"], $regs)) {
$link = "$this->displayPath$_GET[select]/$regs[1]-$val"; $link = $this->displayPath . Adminer\friendly_url($_GET["select"]) . "/$regs[1]-$val";
} }
} }
@@ -55,5 +55,6 @@ class AdminerFileUpload extends Adminer\Plugin {
'pl' => array('' => 'Edytuj pola kończące się na "_path" za pomocą <input type="file"> i link do przesłanych plików z wybierz'), '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'), '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"> で変更し、"選択" からアップロードされたファイルにリンク'), 'ja' => array('' => '列名が "_path" で終わる列を <input type="file"> で変更し、"選択" からアップロードされたファイルにリンク'),
'hr' => array('' => 'Uređuje polja koja završavaju s "_path" putem <input type="file"> i povezuje ih s učitanim datotekama'),
); );
} }

View File

@@ -191,5 +191,6 @@ class AdminerForeignSystem extends Adminer\Plugin {
'pl' => array('' => 'Połącz tabele systemowe (w bazach danych "mysql" i "information_schema") za pomocą kluczy obcych'), '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'), 'ro' => array('' => 'Conectați tabelele de sistem (în bazele de date "mysql" și "information_schema") prin chei străine'),
'ja' => array('' => 'システムテーブル ("mysql" と "information_schema") を外部キーを用いて接続'), 'ja' => array('' => 'システムテーブル ("mysql" と "information_schema") を外部キーを用いて接続'),
'hr' => array('' => 'Povezuje sistemske tablice (u bazama "mysql" i "information_schema") stranim ključevima'),
); );
} }

View File

@@ -30,5 +30,6 @@ class AdminerFrames extends Adminer\Plugin {
'pl' => array('' => 'Zezwalaj na używanie Adminera wewnątrz ramki'), 'pl' => array('' => 'Zezwalaj na używanie Adminera wewnątrz ramki'),
'ro' => array('' => 'Permiteți utilizarea Adminer în interiorul unui cadru'), 'ro' => array('' => 'Permiteți utilizarea Adminer în interiorul unui cadru'),
'ja' => array('' => 'フレーム内での Adminer 利用を許可'), 'ja' => array('' => 'フレーム内での Adminer 利用を許可'),
'hr' => array('' => 'Dopušta korištenje Adminera unutar framea'),
); );
} }

View File

@@ -7,7 +7,7 @@
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @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) * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/ */
class AdminerCodemirror extends Adminer\Plugin { class AdminerHighlightCodemirror extends Adminer\Plugin {
private $root; private $root;
private $minified; private $minified;
@@ -107,5 +107,6 @@ addEventListener('DOMContentLoaded', () => {
'de' => array('' => 'CodeMirror 5 verwenden für die Syntaxhervorhebung und <textarea> einschließlich der Überschrift von Schlüsselwörtern und Tabellen'), '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> を強調表示'), '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'), 'pl' => array('' => 'Użyj CodeMirror 5 do podświetlania składni i <textarea>, uwzględniając wcześniejsze wpisywanie słów kluczowych i tabel'),
'hr' => array('' => 'Koristi CodeMirror 5 za isticanje sintakse i <textarea>, uključujući dovršavanje ključnih riječi i tablica'),
); );
} }

View File

@@ -7,7 +7,7 @@
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @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) * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/ */
class AdminerMonaco extends Adminer\Plugin { class AdminerHighlightMonaco extends Adminer\Plugin {
private $root; private $root;
function __construct($root = "https://cdn.jsdelivr.net/npm/monaco-editor@0.52/min/vs") { function __construct($root = "https://cdn.jsdelivr.net/npm/monaco-editor@0.52/min/vs") {
@@ -77,5 +77,6 @@ addEventListener('DOMContentLoaded', () => {
'de' => array('' => 'Monaco-Editor von VS Code verwenden, für die Syntaxhervorhebung und SQL <textarea>'), 'de' => array('' => 'Monaco-Editor von VS Code verwenden, für die Syntaxhervorhebung und SQL <textarea>'),
'ja' => array('' => '構文や <textarea> の強調表示に VS Code の Monaco Editor を使用'), 'ja' => array('' => '構文や <textarea> の強調表示に VS Code の Monaco Editor を使用'),
'pl' => array('' => 'Użyj Monaco Editora programu VS Code do podświetlania składni i <textarea> SQL'), 'pl' => array('' => 'Użyj Monaco Editora programu VS Code do podświetlania składni i <textarea> SQL'),
'hr' => array('' => 'Koristi Monaco Editor iz VS Code za isticanje sintakse i SQL <textarea>'),
); );
} }

View File

@@ -7,7 +7,7 @@
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @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) * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/ */
class AdminerPrism extends Adminer\Plugin { class AdminerHighlightPrism extends Adminer\Plugin {
private $editorRoot; private $editorRoot;
private $minified; private $minified;
private $theme; private $theme;
@@ -65,5 +65,6 @@ if (el) {
'de' => array('' => 'Prism Code Editor verwenden, für die Syntaxhervorhebung und <textarea>'), 'de' => array('' => 'Prism Code Editor verwenden, für die Syntaxhervorhebung und <textarea>'),
'ja' => array('' => '構文や <textarea> の強調表示に Prism Code Editor を使用'), 'ja' => array('' => '構文や <textarea> の強調表示に Prism Code Editor を使用'),
'pl' => array('' => 'Użyj Prism Code Editora do podświetlania składni i <textarea>'), 'pl' => array('' => 'Użyj Prism Code Editora do podświetlania składni i <textarea>'),
'hr' => array('' => 'Koristi Prism Code Editor za isticanje sintakse i SQL <textarea>'),
); );
} }

View File

@@ -52,5 +52,6 @@ class AdminerJsonColumn extends Adminer\Plugin {
'pl' => array('' => 'Wyświetl wartości JSON jako tabelę w edycji'), 'pl' => array('' => 'Wyświetl wartości JSON jako tabelę w edycji'),
'ro' => array('' => 'Afișează valorile JSON sub formă de tabel în editare'), 'ro' => array('' => 'Afișează valorile JSON sub formă de tabel în editare'),
'ja' => array('' => 'JSON 値をテーブルとして編集画面に表示'), 'ja' => array('' => 'JSON 値をテーブルとして編集画面に表示'),
'hr' => array('' => 'Prikazuje JSON vrijednosti kao tablicu u uređivanju'),
); );
} }

View File

@@ -42,5 +42,6 @@ class AdminerLoginIp extends Adminer\Plugin {
'pl' => array('' => 'Sprawdzaj adres IP i zezwakaj na puste hasło'), 'pl' => array('' => 'Sprawdzaj adres IP i zezwakaj na puste hasło'),
'ro' => array('' => 'Verificați adresa IP și permiteți parola goală'), 'ro' => array('' => 'Verificați adresa IP și permiteți parola goală'),
'ja' => array('' => 'IP アドレスの確認、及び空パスワードの許可'), 'ja' => array('' => 'IP アドレスの確認、及び空パスワードの許可'),
'hr' => array('' => 'Provjerava IP adresu i dopušta praznu lozinku'),
); );
} }

View File

@@ -12,7 +12,7 @@ class AdminerLoginOtp extends Adminer\Plugin {
/** /**
* @param string $secret decoded secret, e.g. base64_decode("SECRET") * @param string $secret decoded secret, e.g. base64_decode("SECRET")
*/ */
function __construct(string $secret) { function __construct($secret) {
$this->secret = $secret; $this->secret = $secret;
if ($_POST["auth"]) { if ($_POST["auth"]) {
$_SESSION["otp"] = (string) $_POST["auth"]["otp"]; $_SESSION["otp"] = (string) $_POST["auth"]["otp"];
@@ -75,5 +75,10 @@ class AdminerLoginOtp extends Adminer\Plugin {
'ja' => array( 'ja' => array(
'' => 'ログイン時にワンタイムパスワード (二要素認証) が必要', '' => 'ログイン時にワンタイムパスワード (二要素認証) が必要',
), ),
'hr' => array(
'' => 'Jednokratna lozinka (dvofaktorska autentifikacija) potrebna pri prijavi',
'One Time Password' => 'Jednokratna lozinka',
'Invalid OTP.' => 'Nevažeća jednokratna lozinka.',
),
); );
} }

View File

@@ -12,7 +12,7 @@ class AdminerLoginPasswordLess extends Adminer\Plugin {
/** Set allowed password /** Set allowed password
* @param string $password_hash result of password_hash() * @param string $password_hash result of password_hash()
*/ */
function __construct(string $password_hash) { function __construct($password_hash) {
$this->password_hash = $password_hash; $this->password_hash = $password_hash;
} }
@@ -33,5 +33,6 @@ class AdminerLoginPasswordLess extends Adminer\Plugin {
'pl' => array('' => 'Włącz logowanie bez hasła'), 'pl' => array('' => 'Włącz logowanie bez hasła'),
'ro' => array('' => 'Activați autentificarea fără parolă'), 'ro' => array('' => 'Activați autentificarea fără parolă'),
'ja' => array('' => 'パスワードなしのログインを許可'), 'ja' => array('' => 'パスワードなしのログインを許可'),
'hr' => array('' => 'Omogućuje prijavu bez lozinke'),
); );
} }

View File

@@ -44,5 +44,6 @@ class AdminerLoginServers extends Adminer\Plugin {
'pl' => array('' => 'Wyświetlaj stałą listę serwerów w formularzu logowania'), 'pl' => array('' => 'Wyświetlaj stałą listę serwerów w formularzu logowania'),
'ro' => array('' => 'Afișarea unei liste constante de servere în formularul de conectare'), 'ro' => array('' => 'Afișarea unei liste constante de servere în formularul de conectare'),
'ja' => array('' => 'ログイン画面に定義済のサーバリストを表示'), 'ja' => array('' => 'ログイン画面に定義済のサーバリストを表示'),
'hr' => array('' => 'Prikazuje konstantan popis poslužitelja u obrascu za prijavu'),
); );
} }

View File

@@ -28,5 +28,6 @@ class AdminerLoginSsl extends Adminer\Plugin {
'pl' => array('' => 'Połącz się z MySQL, PostgreSQL, MS SQL za pomocą protokołu SSL'), 'pl' => array('' => 'Połącz się z MySQL, PostgreSQL, MS SQL za pomocą protokołu SSL'),
'ro' => array('' => 'Conectați-vă la MySQL, PostgreSQL, MS SQL utilizând SSL'), 'ro' => array('' => 'Conectați-vă la MySQL, PostgreSQL, MS SQL utilizând SSL'),
'ja' => array('' => 'MySQL, PostgreSQL, MS SQL への接続時に SSL を利用'), 'ja' => array('' => 'MySQL, PostgreSQL, MS SQL への接続時に SSL を利用'),
'hr' => array('' => 'Spajanje na MySQL, PostgreSQL i MS SQL putem SSL-a'),
); );
} }

View File

@@ -19,7 +19,7 @@ class AdminerLoginTable extends Adminer\Plugin {
protected $database; protected $database;
/** Set database of login table */ /** Set database of login table */
function __construct(string $database) { function __construct($database) {
$this->database = $database; $this->database = $database;
} }
@@ -33,5 +33,6 @@ class AdminerLoginTable extends Adminer\Plugin {
'pl' => array('' => 'Uwierzytelnij użytkownika z tabeli "login"'), 'pl' => array('' => 'Uwierzytelnij użytkownika z tabeli "login"'),
'ro' => array('' => 'Autentificați un utilizator din tabelul "login"'), 'ro' => array('' => 'Autentificați un utilizator din tabelul "login"'),
'ja' => array('' => '"login" テーブルによるユーザ認証'), 'ja' => array('' => '"login" テーブルによるユーザ認証'),
'hr' => array('' => 'Autentificira korisnika prema tablici "login"'),
); );
} }

View File

@@ -44,5 +44,6 @@ class AdminerMasterSlave extends Adminer\Plugin {
'pl' => array('' => 'Wykonuje zapisy na komputerze głównym i odczyty na komputerze podrzędnym'), 'pl' => array('' => 'Wykonuje zapisy na komputerze głównym i odczyty na komputerze podrzędnym'),
'ro' => array('' => 'Executarea scrierilor pe master și a citirilor pe slave'), 'ro' => array('' => 'Executarea scrierilor pe master și a citirilor pe slave'),
'ja' => array('' => 'マスタ書込みとスレーブ読込みの有効化'), 'ja' => array('' => 'マスタ書込みとスレーブ読込みの有効化'),
'hr' => array('' => 'Izvodi pisanje na masteru i čitanje na slaveu'),
); );
} }

View File

@@ -21,12 +21,12 @@ class AdminerMenuLinks extends Adminer\Plugin {
'' => $this->lang('Both'), '' => $this->lang('Both'),
'auto' => $this->lang('Auto (select on select page, structure otherwise)'), 'auto' => $this->lang('Auto (select on select page, structure otherwise)'),
); );
$menu = Adminer\get_setting("menu", "adminer_config") ?: $this->menu; $menu = Adminer\get_setting("menu", "adminer_config", $this->menu);
return array($this->lang('Menu table links') => Adminer\html_radios('config[menu]', $options, $menu, "<br>")); return array($this->lang('Menu table links') => Adminer\html_radios('config[menu]', $options, $menu, "<br>"));
} }
function tablesPrint(array $tables) { function tablesPrint(array $tables) {
$menu = Adminer\get_setting("menu", "adminer_config") ?: $this->menu; $menu = Adminer\get_setting("menu", "adminer_config", $this->menu);
$titles = array( $titles = array(
'select' => $this->lang('Select data'), 'select' => $this->lang('Select data'),
'table' => $this->lang('Show structure'), 'table' => $this->lang('Show structure'),
@@ -36,7 +36,7 @@ class AdminerMenuLinks extends Adminer\Plugin {
foreach ($tables as $table => $status) { foreach ($tables as $table => $status) {
$table = "$table"; // do not highlight "0" as active everywhere $table = "$table"; // do not highlight "0" as active everywhere
$name = Adminer\adminer()->tableName($status); $name = Adminer\adminer()->tableName($status);
if ($name != "" && !$status["inherited"]) { if ($name != "" && !$status["partition"]) {
echo '<li>'; echo '<li>';
if (!$menu) { if (!$menu) {
echo '<a href="' . Adminer\h(Adminer\ME) . 'select=' . urlencode($table) . '"' echo '<a href="' . Adminer\h(Adminer\ME) . 'select=' . urlencode($table) . '"'
@@ -112,5 +112,14 @@ class AdminerMenuLinks extends Adminer\Plugin {
'Select data' => 'データ', 'Select data' => 'データ',
'Show structure' => '構造', 'Show structure' => '構造',
), ),
'hr' => array(
'' => 'Prikazuje veze na odabir podataka ili strukturu tablice u izborniku',
'Select data' => 'Odaberi podatke',
'Show structure' => 'Prikaži strukturu',
'Both' => 'Oboje',
'Auto (select on select page, structure otherwise)' => 'Automatski (odabir na stranici odabira, inače struktura)',
'Menu table links' => 'Veze tablice u izborniku',
'select' => 'odaberi',
),
); );
} }

View File

@@ -26,7 +26,7 @@ class AdminerPrettyJsonColumn extends Adminer\Plugin {
if ($function === '') { if ($function === '') {
$json = $this->testJson($value); $json = $this->testJson($value);
if ($json !== $value) { if ($json !== $value) {
$value = json_encode($json); return Adminer\q(json_encode($json));
} }
} }
} }
@@ -37,5 +37,6 @@ class AdminerPrettyJsonColumn extends Adminer\Plugin {
'pl' => array('' => 'Ładnie drukuj wartości JSON w edycji'), 'pl' => array('' => 'Ładnie drukuj wartości JSON w edycji'),
'ro' => array('' => 'Afisare frumoasa a valorilor JSON în editare'), 'ro' => array('' => 'Afisare frumoasa a valorilor JSON în editare'),
'ja' => array('' => '編集時に JSON 文字列を見易く表示'), 'ja' => array('' => '編集時に JSON 文字列を見易く表示'),
'hr' => array('' => 'Lijepo prikazuje JSON vrijednosti u uređivanju'),
); );
} }

28
plugins/row-numbers.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/** Display row numbers in select
* @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 AdminerRowNumbers extends Adminer\Plugin {
function backwardKeys($table, $tableName) {
return array(1);
}
function backwardKeysPrint($backwardKeys, $row) {
static $n;
if (!$n) {
$n = $_GET["page"] * Adminer\adminer()->selectLimitProcess();
}
$n++;
echo "$n.\n";
}
protected $translations = array(
'cs' => array('' => 'Zobrazí čísla řádek ve výpisu'),
'hr' => array('' => 'Prikazuje brojeve redaka u ispisu'),
);
}

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