Compare commits

..

291 Commits

Author SHA1 Message Date
Jakub Vrana
99a0949d2a Release 5.1.0 2025-03-24 17:35:22 +01:00
Jakub Vrana
1cf5a91d98 Export: Use output buffering 2025-03-24 17:28:53 +01:00
Jakub Vrana
dcde78eef2 Document //! 2025-03-24 17:21:44 +01:00
Jakub Vrana
88821a5780 Move beforeunload to plugin 2025-03-24 16:55:40 +01:00
Jakub Vrana
078a8b3d6b Improve handling of translations with ' 2025-03-24 15:57:46 +01:00
Inoyatulloh
4df205d4ec Added Uzbek language 2025-03-24 15:44:30 +01:00
Karel Uhlík
a50395c275 AdminerTablesFilter: Fix JS variable visibility
fix error: Uncaught ReferenceError: reg is not defined
2025-03-24 15:28:02 +01:00
Jakub Vrana
8b93d51c68 MS SQL PDO: Hide unsupported EXPLAIN 2025-03-24 14:47:55 +01:00
Jakub Vrana
50cdbbe415 Non-MySQL: Better field types in SQL command 2025-03-24 14:43:15 +01:00
Jakub Vrana
f5d23a8cad MS SQL PDO: Display last insert ID 2025-03-24 14:25:38 +01:00
Jakub Vrana
3bc675b0fc MS SQL: Common parent for PDO Db 2025-03-24 14:11:25 +01:00
Jakub Vrana
b86a9cb5fb Turn on output buffering only after sending headers
Otherwise output sent before Location (usually errors) is lost.
2025-03-24 14:04:17 +01:00
Jakub Vrana
cd53c54b6a Tests MS SQL: Connect to default server 2025-03-24 13:53:11 +01:00
Jakub Vrana
99163feefb SQLite PDO: Display results in SQL query 2025-03-24 13:53:11 +01:00
Jakub Vrana
84e4971b1e Tests: Check loaded plugin 2025-03-24 13:53:11 +01:00
Jakub Vrana
d22b1091f1 Notices: fetch_field in PDO 2025-03-24 13:53:10 +01:00
Jakub Vrana
b349830758 PostgreSQL: Fix number of affected rows after clone 2025-03-24 12:18:24 +01:00
Jakub Vrana
b8eb0ad8f5 Notices: Declare properties in fetch_field() 2025-03-24 12:18:24 +01:00
Jakub Vrana
c47590bb0d Fix JS error on AI column input 2025-03-24 12:18:24 +01:00
Jakub Vrana
486f164247 Notices: Declare Db::$info at least in MySQL 2025-03-24 12:18:24 +01:00
Jakub Vrana
edce93ca80 Tests: Fix AI 2025-03-24 12:18:20 +01:00
Jakub Vrana
858c914a50 AdminerDarkSwitcher: Icon 2025-03-24 07:38:33 +01:00
Jakub Vrana
9fd9a1fc8d Mute the same error in PHP 5 2025-03-24 07:30:22 +01:00
Jakub Vrana
60555c5736 Editor: Fix pre-selecting empty enum 2025-03-24 07:30:22 +01:00
Jakub Vrana
8cec1bd611 Notices: Declare Adminer::$error 2025-03-24 07:30:22 +01:00
Jakub Vrana
9b84908b99 Notices: Require declared properties 2025-03-24 07:30:22 +01:00
Jakub Vrana
3dd1b41472 Notices: Store maria into a declared variable 2025-03-24 07:30:22 +01:00
Jakub Vrana
b89f628e40 Reset onbeforeupload after Save and continue edit 2025-03-24 07:30:22 +01:00
Jakub Vrana
708646156a Notices: Enable E_NOTICE and E_STRICT 2025-03-24 07:30:20 +01:00
Jakub Vrana
9e52f0fa2e Notices: Check session status before restarting 2025-03-23 22:09:46 +01:00
Jakub Vrana
3cd50c9a02 Notices: Silent unavoidable error 2025-03-23 21:47:32 +01:00
Jakub Vrana
f5ecb18896 Notices: Add helper for reset() with expression 2025-03-23 21:39:25 +01:00
Jakub Vrana
0501846743 AdminerSqlGemini: Support multiple instructions
Example:
Cities speaking Czech.
Then create indexes so it's fast.
2025-03-23 20:24:25 +01:00
Jakub Vrana
8db8b7a10d Plugins: Use call_user_func_array 2025-03-23 20:22:41 +01:00
Jakub Vrana
d6a417fa58 Add comment 2025-03-23 20:22:41 +01:00
Jakub Vrana
4477f95426 AdminerEditCalendar: Avoid work in constructor 2025-03-23 20:22:41 +01:00
Jakub Vrana
6a64c7771b Plugins: Display doc-comments at server overview 2025-03-23 20:22:41 +01:00
Jakub Vrana
c32e4f82fe AdminerSqlGemini: Display progress 2025-03-23 20:22:41 +01:00
Jakub Vrana
19bb320e7a New plugin: Verify new versions from GitHub
Thanks to @adrianbj in 441e7f0.
2025-03-23 20:22:40 +01:00
Jakub Vrana
fb47ba6bbb Add instructions to translation files 2025-03-23 15:35:30 +01:00
Jakub Vrana
03ff0bbc04 JS: Use classList instead of className 2025-03-23 15:10:18 +01:00
Jakub Vrana
e8055329a6 CSS: Move Logout down on narrow screens (fix #938) 2025-03-23 14:06:11 +01:00
Jakub Vrana
9142e7e8a5 AdminerDumpAlter: Rename colliding method (fix #943) 2025-03-23 13:56:40 +01:00
Jakub Vrana
e975e546e1 Docs: improve style thanks to ChatGPT 2025-03-23 13:52:38 +01:00
Jakub Vrana
1b59b10f62 MySQLi: Avoid using PHP 8.1 method
This reverts 359fdf3.
2025-03-23 12:40:12 +01:00
Jakub Vrana
f1a8bd9ef8 Chrome 134 on iOS: Allow zooming out (bug #940) 2025-03-23 12:40:07 +01:00
Jakub Vrana
204176b33b New plugin: AI prompt in SQL command creating the queries with Google Gemini 2025-03-23 08:02:48 +01:00
Jakub Vrana
cf436d7317 Add CONTRIBUTING 2025-03-23 00:25:49 +01:00
Jakub Vrana
7eb92d9601 Add notes for developers 2025-03-23 00:25:48 +01:00
Jakub Vrana
8da9239279 Plugins: Remove obsolete instructions 2025-03-22 22:34:52 +01:00
Jakub Vrana
e0dde9034f Doc-comments: Use consistent style 2025-03-22 21:04:58 +01:00
Jakub Vrana
9b162e8291 PostgreSQL: Fix clone after e395fe5 2025-03-22 20:55:51 +01:00
Jakub Vrana
359fdf3d07 MySQLi: Use fetch_column 2025-03-22 08:50:47 +01:00
Jakub Vrana
00b7056147 MySQLi: Add comment 2025-03-22 08:47:21 +01:00
Jakub Vrana
db829fd463 PDO: Handle PHP warnings for internal queries 2025-03-22 08:47:21 +01:00
Jakub Vrana
359d153130 Tests PostgreSQL: Fix after e395fe5 2025-03-22 08:47:21 +01:00
Jakub Vrana
ab44927442 Tests: Run also in PDO 2025-03-22 08:47:03 +01:00
Jakub Vrana
01a6af8b70 Tests: Remove localhost:8080 2025-03-22 08:20:30 +01:00
Jakub Vrana
3f2eec9b92 URL parameter ?ext=pdo to force using PDO 2025-03-22 08:18:39 +01:00
Jakub Vrana
aaca1eee8e JS: Unsupport event.srcElement 2025-03-21 23:58:45 +01:00
Jakub Vrana
26db8596aa JS: Simplify hiding ajaxstatus 2025-03-21 23:58:45 +01:00
Jakub Vrana
5aedb33fb6 JS: Simplify onbeforeunload 2025-03-21 23:58:45 +01:00
Jakub Vrana
7fca87dfd7 JS: Unsupport old IE 2025-03-21 23:58:42 +01:00
Jakub Vrana
711f41d05b Ctrl+click in select moves the cursor in modern browsers 2025-03-21 23:58:15 +01:00
Jakub Vrana
f7b20b5f6f Increase space after SQL result (fix #937) 2025-03-21 22:32:49 +01:00
Jakub Vrana
719c1e7067 Plugins autoloading: Link help 2025-03-21 22:23:45 +01:00
John F. Arroyave Gutiérrez
83a8b18c85 Spanish translation upgrade
Just some edditions and new translations
2025-03-21 22:23:44 +01:00
Matthaiks
4d082d4fcd Update Polish translation 2025-03-21 07:33:40 +01:00
Jakub Vrana
a705be7a94 Confirm before exiting edited edit form 2025-03-20 17:15:18 +01:00
Jakub Vrana
a2586e7265 Update comment: Chrome doesn't propagate Ctrl+Up 2025-03-20 16:00:25 +01:00
Jakub Vrana
f38bbff5bf Increase maximum width of string edit (bug #930) 2025-03-20 15:39:55 +01:00
Peter Knut
09720b31f1 Remove hardcoded textarea height 2025-03-20 15:26:06 +01:00
Jakub Vrana
0e28daac55 Multi-edit: Increase size of textarea 2025-03-20 15:19:32 +01:00
Jakub Vrana
3eb478b65a CSS: Fix adminer.css recognition (includes version) 2025-03-20 14:50:58 +01:00
Jakub Vrana
192ce41bd3 Tests: Run on compiled version 2025-03-20 14:33:17 +01:00
Jakub Vrana
bcd5164e2f Plugins: Handle autoloading errors 2025-03-20 14:33:17 +01:00
Jakub Vrana
cd686c7a1e CSS: Light background in dark bold error 2025-03-20 14:33:17 +01:00
Jakub Vrána
e9feb1587a Add plugins/README.md 2025-03-20 14:32:56 +01:00
Jakub Vrana
1f5a7fa717 JS: Use arrow functions
All function () {} left in the code reference `this` and thus couldn't use arrow functions.
2025-03-20 10:35:08 +01:00
Jakub Vrana
3c310ddfaf Designs: Link gallery 2025-03-20 09:54:45 +01:00
Jakub Vrana
6d4896e03c JS: Add ESLint config
To use this: Create package.json and run:
npm install --save-dev eslint @eslint/js
2025-03-20 09:54:44 +01:00
Jakub Vrana
5ef77ec399 JS: Fix typo after def9e07 2025-03-20 09:17:04 +01:00
Jakub Vrana
62c18efbd5 JS: Use let/const for globals 2025-03-20 08:59:07 +01:00
Jakub Vrana
101229043e JS: Use let/const in spaghetti code 2025-03-20 08:58:48 +01:00
Jakub Vrana
0e21106e48 JS: Change var to let/const
Created by `eslint . --fix` with rules no-var and then prefer-const.
2025-03-20 08:32:23 +01:00
Jakub Vrana
f16a6d415a JS: Style issues 2025-03-20 08:03:54 +01:00
Jakub Vrana
11699223df JS: Remove outdated check 2025-03-20 07:39:21 +01:00
Jakub Vrana
7c75d3f86c JS: Remove unnecessary escape 2025-03-20 07:26:17 +01:00
Jakub Vrana
287667d631 Designs: Link screenshots 2025-03-20 06:53:52 +01:00
Jakub Vrana
ef4c340cc2 PostgreSQL 11: Support PROCEDURE 2025-03-19 22:43:40 +01:00
Jakub Vrana
f2a5d5127a Display collation at table structure if different from table 2025-03-19 22:42:02 +01:00
Jakub Vrana
889ffe28c7 JS: Add missing function 2025-03-19 22:04:30 +01:00
Jakub Vrana
5f6aa1e267 JS: Depend on JSON 2025-03-19 21:51:25 +01:00
Jakub Vrana
def9e07831 JS: Use for..of 2025-03-19 21:51:23 +01:00
Jakub Vrana
c73bd1233e Changelog for new plugins 2025-03-19 20:29:20 +01:00
Jakub Vrana
e6ead2d12c New plugin: Allow switching light and dark mode (fix #926) 2025-03-19 20:20:43 +01:00
Jakub Vrana
4802389ce6 Delete stale comment after 3eba731 2025-03-19 19:21:22 +01:00
Takashi SHIRAI
7845b70d7d Modify the Japanese messages.
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-03-19 18:38:54 +01:00
Takashi SHIRAI
9f8344e53c Fix the uncaught exception because of namespace.
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-03-19 18:35:32 +01:00
Jakub Vrana
2dd32cf534 Plugin pretty-json-column: fix with enum (fix #928)
Also add syntax highlighting.
2025-03-19 18:22:57 +01:00
Jakub Vrana
0b063262ee Remove ns= from auth_url 2025-03-19 17:53:26 +01:00
Peter Knut
458d07122f Bigger font size for code blocks 2025-03-19 17:45:31 +01:00
Jakub Vrana
740a4b8b1c Issue template: Use italics 2025-03-19 17:33:46 +01:00
Jakub Vrana
6be1e1c069 Plugins: Fix displaying with legacy AdminerPlugin 2025-03-19 17:29:42 +01:00
Jakub Vrana
b861500910 Make Adminer\Plugins visible for adminer_object 2025-03-19 17:29:42 +01:00
Jakub Vrana
eca176f362 MS SQL: Fix collation issues when retrieving default values
I was getting this error:
Error in query (4189): Cannot convert to text/ntext or collate to 'Czech_100_CI_AI_SC_UTF8' because these legacy LOB types do not support UTF-8 or UTF-16 encodings. Use types varchar(max), nvarchar(max) or a collation which does not have the _SC or _UTF8 flags.

This reverts part of a2adb67.
2025-03-19 17:29:42 +01:00
Jakub Vrana
8f489da2bd Tests MS SQL: Use default server 2025-03-19 17:29:42 +01:00
Jakub Vrana
3138b1de88 CockroachDB: not actually fixed 2025-03-19 17:29:42 +01:00
Matthaiks
e723158bbf Update Polish translation 2025-03-19 17:29:31 +01:00
Jakub Vrana
feed6e4488 PostgreSQL: Use language sql when creating routines by default 2025-03-19 06:49:48 +01:00
Jakub Vrana
9b345fbafb CockroachDB: Do not use version number when linking docs 2025-03-19 06:49:48 +01:00
Jakub Vrana
ca30d04575 PostgreSQL: Display shorter version (regression from 5.0.5) 2025-03-19 06:49:48 +01:00
Jakub Vrana
b4e41f5e4b CockroachDB: Pass param length to routine drop (cockroachdb/cockroach#142886) 2025-03-19 06:49:30 +01:00
Jakub Vrana
8979baca28 Remove forgotten newline 2025-03-19 05:45:22 +01:00
Jakub Vrana
8019b9ae4d Autoloaded plugins: Drivers work too 2025-03-19 05:14:41 +01:00
Jakub Vrana
7fec2992d9 Autoloaded plugins: Use in Editor 2025-03-19 05:08:24 +01:00
Jakub Vrana
df98f2453d Plugins: Load config from adminer-plugins.php 2025-03-19 05:05:42 +01:00
Jakub Vrana
eb1d8d5468 Display loaded plugins 2025-03-18 22:30:51 +01:00
Jakub Vrana
ea3da56b6d Add plugins/plugin.php for backwards compatibility 2025-03-18 22:30:51 +01:00
Jakub Vrana
41dabfb4f2 Plugins: Autoload plugins in adminer-plugins/ 2025-03-18 22:30:49 +01:00
Jakub Vrana
ff0fe92c00 Add forgotten space 2025-03-18 19:51:25 +01:00
Jakub Vrana
06c15aa263 IMAP: Use db mail 2025-03-18 19:46:40 +01:00
Jakub Vrana
8a64fb16cd IMAP: Use imap_num_msg() 2025-03-18 19:46:08 +01:00
Jakub Vrana
bda53f0734 CSS: Allow more custom styles with dark mode (fix #925) 2025-03-18 17:49:33 +01:00
Jakub Vrana
bc9de24d77 Add helper for <input type=hidden> 2025-03-18 17:15:10 +01:00
Jakub Vrana
9cfea02e19 Avoid excessive PHP and HTML mixing 2025-03-18 16:45:15 +01:00
Jakub Vrana
eeb78d7e48 Vendor display: Do not overwrite PostgreSQL by MySQL at the same server 2025-03-18 16:30:23 +01:00
Jakub Vrana
751405e006 - PostgreSQL: Avoid warning about crdb_version (fix #924, regression from 5.0.5) 2025-03-18 16:20:44 +01:00
Jakub Vrana
3fb6cac361 PostgreSQL: Display description of system variables 2025-03-18 16:00:21 +01:00
Matrixman
8613f97948 New version of design rmSOFT 2025-03-18 15:43:50 +01:00
Jakub Vrana
249807fc48 IMAP: Simplify expunge 2025-03-18 14:56:37 +01:00
Jakub Vrana
6e76454f59 Update changes 2025-03-18 14:45:10 +01:00
Jakub Vrana
655cca0872 IMAP: Export 2025-03-18 14:05:51 +01:00
Jakub Vrana
16989a736c IMAP: Use halfopen 2025-03-18 13:58:05 +01:00
Jakub Vrana
a3d1ab34bb Move engines() to Driver 2025-03-18 13:41:24 +01:00
Jakub Vrana
b2f904c64f IMAP: Add Data_length 2025-03-18 13:36:49 +01:00
Jakub Vrana
05d27a132d Compile: Do not require unsupported functions 2025-03-18 13:29:48 +01:00
Jakub Vrana
4861c88cc6 IMAP: Create, drop, truncate 2025-03-18 13:10:45 +01:00
Jakub Vrana
1424a42b1f IMAP: Avoid globals 2025-03-18 13:00:40 +01:00
Jakub Vrana
1c5a192cb5 Add forgotten colon 2025-03-18 12:57:29 +01:00
Jakub Vrana
336fdaf09c Revert "Table-less drivers: Hide Create table link"
This reverts commit 841f76fb00.
This reverts commit 8fed1523ec.
2025-03-18 12:55:30 +01:00
Jakub Vrana
57e6c2651c IMAP: Number of rows 2025-03-18 12:52:17 +01:00
Jakub Vrana
693dc5b8a7 IMAP: Delete message
Also support mailboxes with spaces
2025-03-18 12:35:36 +01:00
Jakub Vrana
753642630b IMAP: Select columns 2025-03-18 12:15:31 +01:00
Jakub Vrana
8fed1523ec Table-less drivers: Hide Create table link 2025-03-18 11:35:15 +01:00
Jakub Vrana
ebb31bdac8 IMAP: New driver created just for fun 2025-03-18 11:35:14 +01:00
Jakub Vrana
5069e5a400 JSON: Remove unnecessary escape 2025-03-18 11:15:03 +01:00
Jakub Vrana
33b69ed82b Edit: prefer JSON over text 2025-03-18 11:12:07 +01:00
Jakub Vrana
4e4f280061 Table-less drivers: display empty field only without fields() 2025-03-18 11:00:57 +01:00
Jakub Vrana
841f76fb00 Table-less drivers: Hide Create table link 2025-03-18 11:00:28 +01:00
Jakub Vrana
73e7e00c77 fieldName: Display : before comment only with type 2025-03-18 09:26:16 +01:00
Jakub Vrana
29f954069d CSS: Sticky table headers (fix #918) 2025-03-18 02:10:57 +01:00
Jakub Vrana
c1eccc9cdd Use CHANGELOG.md 2025-03-17 21:13:15 +01:00
Jakub Vrana
430cdaeb3a Use README.md 2025-03-17 20:29:50 +01:00
Jakub Vrana
e395fe583a PostgreSQL: Display auto_increment of inserted rows 2025-03-17 19:58:22 +01:00
Jakub Vrana
19313a8dab Pass $result to last_id 2025-03-17 19:58:21 +01:00
Jakub Vrana
17c0960e7f Consider plugins autoloading 2025-03-17 19:58:21 +01:00
Jakub Vrana
a9b020452e Plugins: Add backward-keys 2025-03-17 19:58:20 +01:00
Jakub Vrana
e1f0fded69 Add helper for <input type=hidden name=token> 2025-03-17 18:45:08 +01:00
Jakub Vrana
91f9980267 Bug reports: Add screenshot 2025-03-17 17:37:29 +01:00
Jakub Vrana
5f3fac4b48 Develop 2025-03-17 17:15:17 +01:00
Jakub Vrana
b7679701ce Release 5.0.6 2025-03-17 17:13:46 +01:00
Jakub Vrana
b7258d2e95 Shorten queries saved from SQL command to URL (fix #917) 2025-03-17 15:05:56 +01:00
Jakub Vrana
c51d9919fe Update tests 2025-03-17 14:29:07 +01:00
Jakub Vrana
78c431ab20 Fix typos after 22a3efe 2025-03-17 14:28:04 +01:00
Jakub Vrana
916889bb8e Compile: Fix joining conditional echos 2025-03-17 14:25:57 +01:00
Jakub Vrana
e8b15c99f4 Add back function to get driver name (fix #919)
This reverts fe88f83.
2025-03-17 08:07:24 +01:00
Jakub Vrana
a460019535 Compile PostgreSQL: Address warnings (fix #916) 2025-03-17 07:43:10 +01:00
Jakub Vrana
ea5a7453fb CSS: Set also light color-scheme 2025-03-17 07:14:26 +01:00
Jakub Vrana
587f6a5375 CSS: Dark input fields in dark mode 2025-03-17 07:04:21 +01:00
Jakub Vrana
2bb74e7467 Compile: fetch_column() is used only internally 2025-03-17 06:38:16 +01:00
Jakub Vrana
a684044bb3 Compile SQLite: Remove server login field 2025-03-17 06:13:48 +01:00
Jakub Vrana
36a3465a64 Compile: Rename variable with other meaning in Adminer 2025-03-17 05:54:24 +01:00
Jakub Vrana
223aee70d5 Mark bugs at https://sourceforge.net/p/adminer/bugs-and-features/ 2025-03-17 05:38:14 +01:00
Jakub Vrana
c02a7d6abe Add missing namespace 2025-03-17 00:49:12 +01:00
Jakub Vrana
22a3efe4ed Code style: avoid excesive mixing of PHP and HTML 2025-03-17 00:02:41 +01:00
Jakub Vrana
dd47df9b9c Display comment in title of field 2025-03-16 22:49:50 +01:00
Jakub Vrana
0b0e8940e0 Update externals 2025-03-16 22:42:45 +01:00
Jakub Vrana
fa8339c8c2 Plugins: Remove fragile autoloader 2025-03-16 22:09:43 +01:00
Jakub Vrana
f0ee812b29 Add todo 2025-03-16 21:55:36 +01:00
Jakub Vrana
f093cb6db2 Rename file 2025-03-16 21:49:39 +01:00
Jakub Vrana
68d4a5a650 Delete adminer.version before writing (bug #855) 2025-03-16 21:43:09 +01:00
Jakub Vrana
6576fa6a73 Security: Disallow writing temporary files to symlinks (bug #855)
Cc @peterpp
2025-03-16 21:43:08 +01:00
Peter Knut
28535bf384 Refactor generating of private key
Generating of private key is atomic now.
2025-03-16 20:54:28 +01:00
Peter Knut
43e3fe375d Refactor working with a locked file 2025-03-16 20:54:24 +01:00
Jakub Vrana
d8a9a3db8d CockroachDB: Display CockroachDB instead of PostgreSQL 2025-03-16 19:07:42 +01:00
Jakub Vrana
777d5dca0e Store information about vendor 2025-03-16 19:03:12 +01:00
Jakub Vrana
6d71cd678e Separate HTML functions 2025-03-16 19:03:02 +01:00
Jakub Vrana
30714d98f9 Save bytes 2025-03-16 18:05:52 +01:00
Jakub Vrana
8353bd48de MariaDB: Display MariaDB instead of MySQL 2025-03-16 18:02:34 +01:00
Peter Knut
0762c761ac Rename functions for settings stored in a cookie 2025-03-16 17:24:28 +01:00
Jakub Vrana
529197f403 Compile: Move caching headers to file.inc.php 2025-03-16 17:16:23 +01:00
Jakub Vrana
d20cbf14e7 Remember export setting at SQL command 2025-03-16 15:13:10 +01:00
Jakub Vrana
517f63835d Plugins: add syntaxHighlighting() and CodeMirror 2025-03-16 14:00:39 +01:00
Jakub Vrana
4fee062b73 Simplify autofocus 2025-03-16 11:08:01 +01:00
Jakub Vrana
26769b2357 Do not align right non-numbers, e.g. NULL or foreign keys 2025-03-16 09:10:43 +01:00
Jakub Vrana
42bf7b9ca0 Align numbers right (bug #912) 2025-03-15 15:13:41 +01:00
Jakub Vrana
d8755c903d Save bytes 2025-03-15 13:57:29 +01:00
Jakub Vrana
a391fcb6c4 Compile: Use external PhpShrink 2025-03-15 09:38:06 +01:00
Jakub Vrana
c99ca863ce php_shrink: Move add_apo_slashes to compile.php 2025-03-15 09:10:43 +01:00
Jakub Vrana
e0283543f3 Support adminer.css + adminer-dark.css together 2025-03-15 08:51:51 +01:00
Jakub Vrana
a72e053520 php_shrink: Use foreach 2025-03-15 07:35:37 +01:00
Jakub Vrana
de654712d5 php_shrink: Preprocess ?>HTML<?php 2025-03-15 07:33:47 +01:00
Jakub Vrana
9c1d5484a2 php_shrink: Join echos interleaved with comments 2025-03-15 07:21:37 +01:00
Jakub Vrana
1fd8aa885b Fix bs,ru,sr,uk single lang version after f2ce6c0 2025-03-15 02:32:37 +01:00
Jakub Vrana
08882c6a8e Add comments to places processed by compile.php 2025-03-15 02:26:16 +01:00
Jakub Vrana
fd199ec156 php_shrink: Document bug 2025-03-15 02:26:07 +01:00
Jakub Vrana
e3515fd63f CSS: Dark mode syntax highlighting and adminer-dark.css 2025-03-15 02:00:21 +01:00
Jakub Vrana
be0a485b14 JUSH: Revert to original colors 2025-03-14 12:35:06 +01:00
Jakub Vrana
94c04712d6 SQL textarea: Open help on Ctrl+click 2025-03-14 11:01:20 +01:00
Jakub Vrana
caea0b7f68 JUSH: Use dark mode 2025-03-14 09:55:34 +01:00
Jakub Vrana
134301b3ff Tests CockroachDB: Link bug 2025-03-14 08:02:11 +01:00
Jakub Vrana
d14f3dd2d8 Tests: Check status variables 2025-03-14 07:31:57 +01:00
Jakub Vrana
67c313c86d Tests SQLite: Add more 2025-03-14 07:27:58 +01:00
Jakub Vrana
5eaa73583a Tests PostgreSQL: Materialized view 2025-03-14 06:44:02 +01:00
Jakub Vrana
68b6af6fce Tests: Check that check constraints work 2025-03-14 06:38:26 +01:00
Jakub Vrana
e65fdba5d1 Test PostgreSQL: enum 2025-03-14 06:18:42 +01:00
Jakub Vrana
8f336cd0b3 Tests: Remove useless xpath= 2025-03-14 05:58:45 +01:00
Jakub Vrana
5f3bfe8451 JUSH textarea: Use oninput 2025-03-14 05:56:30 +01:00
Jakub Vrana
1a3d58e74e Tests: Add newlines 2025-03-13 23:04:22 +01:00
Jakub Vrana
af9a851c5e Tests: generated columns 2025-03-13 23:00:14 +01:00
Jakub Vrana
3a40a855ea Develop 2025-03-13 18:29:28 +01:00
Jakub Vrana
967647759e Release 5.0.5 2025-03-13 18:28:43 +01:00
Jakub Vrana
e4323ced55 Tests: Fix CockroachDB after 48308f3 2025-03-13 18:22:06 +01:00
Jakub Vrana
85c6af6f87 Compile: strip space after CSS comment 2025-03-13 18:00:54 +01:00
Jakub Vrana
9226804aa2 CSS: Move media selector to <link> 2025-03-13 17:55:42 +01:00
Jakub Vrana
041e7064ca php_shrink: Simplify test 2025-03-13 17:45:16 +01:00
Jakub Vrana
4c2a8b0050 Compile: Move ?><? removal to php_shrink 2025-03-13 17:43:20 +01:00
Jakub Vrana
9afbf1a465 Compile: dark.css 2025-03-13 17:33:19 +01:00
Jakub Vrana
352ef9c778 CSS: Don't use dark theme with adminer.css 2025-03-13 17:11:40 +01:00
Jakub Vrana
45107dc46e Don't highlight NULL as JSON 2025-03-13 16:55:17 +01:00
Jakub Vrana
21f3426adb Design: Update dark mode (by rmsoft) 2025-03-13 16:48:56 +01:00
Jakub Vrana
95dccfe9fb MongoDB: Rename file 2025-03-13 15:21:19 +01:00
Jakub Vrana
fe88f83c95 Elastic: Fix number of rows in group queries 2025-03-13 14:34:26 +01:00
Jakub Vrana
078957fe32 MongoDB: Move to plugin 2025-03-13 14:27:54 +01:00
Jakub Vrana
61f07867f9 Update readme 2025-03-13 14:09:45 +01:00
Jakub Vrana
0135dd5b81 Tests: Add tests for MS SQL 2025-03-13 14:07:31 +01:00
Jakub Vrana
773a2253d3 Export: Display unknown number of rows as ? 2025-03-13 13:29:10 +01:00
Jakub Vrana
272042a30e Fix skipOriginal after unconvertFunction 2025-03-13 13:13:06 +01:00
Jakub Vrana
48308f3357 CockroachDB: Recognize unique_rowid() as auto_increment 2025-03-13 13:09:05 +01:00
Jakub Vrana
c90033a962 Test PostgreSQL: SQL command 2025-03-13 13:03:24 +01:00
Jakub Vrana
8bd022f974 CockroachDB: Fix test 2025-03-13 13:02:53 +01:00
Jakub Vrana
b1550b052d MySQL: Simplify checking for MariaDB 2025-03-13 12:24:35 +01:00
Jakub Vrana
9862846a7c CockroachDB: Declare support 2025-03-13 12:15:37 +01:00
Jakub Vrana
f14e3e38f6 CockroachDB: Disable processlist 2025-03-13 12:12:44 +01:00
Jakub Vrana
95262c4215 CSS: Fix style 2025-03-13 12:08:01 +01:00
Jakub Vrana
dacfdc4608 Tests: Work with JUSH textarea 2025-03-13 12:07:49 +01:00
Jakub Vrana
7636c253fb CockroachDB: Display version 2025-03-13 11:51:03 +01:00
Jakub Vrana
44d26a9dd3 Tests: Add tests for CockroachDB 2025-03-13 11:08:42 +01:00
Jakub Vrana
b229e7b583 Tests: Add tests for MariaDB and PostgreSQL 2025-03-13 10:45:53 +01:00
Jakub Vrana
8e91417be1 MySQL: Display converting function for binary, bit or geometry fields 2025-03-13 07:50:20 +01:00
Peter Knut
db7202fcf0 MySQL: Fix saving string default value of json field 2025-03-13 07:19:27 +01:00
Jakub Vrana
260487fbc2 Designs: Update rmsoft (fix #911) 2025-03-13 07:10:23 +01:00
Jakub Vrana
3ae964c915 Designs: Fix price 2025-03-13 07:06:43 +01:00
Jakub Vrana
d56c8cbaae CSS: Simplify .odds 2025-03-13 07:03:42 +01:00
Jakub Vrana
d347f88c54 Design: Update rmsoft (fix #911) 2025-03-13 06:38:32 +01:00
Jakub Vrana
b9b4db0c8e Compile: Add more php_shrink tests 2025-03-13 06:10:58 +01:00
Jakub Vrana
818b9ad903 Compile: Strip public 2025-03-12 23:15:24 +01:00
Jakub Vrana
4a6436773f Compile: Add tests for php_shrink 2025-03-12 23:15:22 +01:00
Jakub Vrana
81594e4a2d Issues: Prefer MySQLi 2025-03-12 18:28:56 +01:00
Jakub Vrana
f0bdb0e6ca Designs: Update .odds 2025-03-12 17:50:24 +01:00
Jakub Vrana
b9e4806d3c CSS: lowercase color names 2025-03-12 17:43:58 +01:00
Jakub Vrana
7e708dae57 CSS: Add dark theme 2025-03-12 17:42:53 +01:00
Jakub Vrana
a0fe44ec18 CSS: Expand <code> in <pre> 2025-03-12 17:35:44 +01:00
Jakub Vrana
eb0f280776 CSS: Fix highlighting checked odd rows 2025-03-12 17:35:34 +01:00
Jakub Vrana
3b1189cd3c MySQL: Allow setting default values of json column 2025-03-12 14:25:19 +01:00
Jakub Vrana
434a8f7705 CSS: Disable odd on hover 2025-03-12 13:58:20 +01:00
Jakub Vrana
f9478c67d2 CSS: Merge rules 2025-03-12 12:57:59 +01:00
Jakub Vrana
f2ce6c0a71 Wrap long lines 2025-03-12 11:43:21 +01:00
Jakub Vrana
a50b3d6385 MS SQL: Fix editing rows with datetime column in primary key
Inspired by adminneo-org@b4afc08.
This fixes https://sourceforge.net/p/adminer/discussion/960418/thread/a547e79622/.
2025-03-12 09:28:22 +01:00
Jakub Vrana
e39deca4f1 Wrap Schema: in navigation to <span> (fix #907) 2025-03-12 09:26:04 +01:00
Jakub Vrana
ce69970f54 Update German translation (by @wintstar) 2025-03-12 09:09:09 +01:00
Jakub Vrana
51ac1312a1 MariaDB: Don't display NULL as default value 2025-03-12 08:51:25 +01:00
Jakub Vrana
4505544953 Code style: exclude removed extensions 2025-03-12 08:12:28 +01:00
Jakub Vrana
fa75213ff6 Coverage: Fix newlines 2025-03-12 06:15:47 +01:00
Jakub Vrana
63acb37ea6 Coverage: Display drivers 2025-03-12 06:09:18 +01:00
Jakub Vrana
3ad6c16f59 Coverage: Modernize HTML 2025-03-12 06:06:28 +01:00
Jakub Vrana
22d08b4a50 Add comment 2025-03-12 06:01:29 +01:00
Jakub Vrana
dd3cc4e683 Add plugin AdminerEditorViews (fix #905) 2025-03-12 05:22:57 +01:00
Jakub Vrana
5504a617d0 Compile: Support private static 2025-03-12 05:03:42 +01:00
Jakub Vrana
2fdebfda29 Ensure PHP 5.3 compatibility 2025-03-11 21:43:59 +01:00
Jakub Vrana
dc2e945aef MySQL: Convert binary default value to hex when editing 2025-03-11 20:25:52 +01:00
Jakub Vrana
43d86287c4 Fix focusing first field 2025-03-11 20:16:49 +01:00
Jakub Vrana
a94a727af7 PostgreSQL PDO: Escape bytea values (bug #218) 2025-03-11 19:42:10 +01:00
Jakub Vrana
c2d29a6937 MySQL: Display default values of binary columns 2025-03-11 19:25:42 +01:00
Jakub Vrana
c082136558 Rename variable 2025-03-11 18:40:34 +01:00
Jakub Vrana
7b1ea5fa2c AdminerDumpAlter: Use dumpFooter 2025-03-11 18:20:11 +01:00
Jakub Vrana
156839142e Fix plugin AdminerPrettyJsonColumn 2025-03-11 18:16:44 +01:00
Jakub Vrana
2ee4e3b2e1 Compile: Fix shortening in protected functions 2025-03-11 17:56:21 +01:00
Jakub Vrana
8b4c8b0156 Fix coverage 2025-03-11 14:37:08 +01:00
Jakub Vrana
9702878297 Develop 2025-03-11 14:00:37 +01:00
205 changed files with 7002 additions and 3991 deletions

View File

@@ -8,7 +8,8 @@ assignees: ''
---
**Adminer version:** please use latest published or Git
**Driver:** e.g. MySQL
**Database version:** e.g. 10.1.48-MariaDB
**Driver:** e.g. MySQLi
**Database version:** e.g. 10.2.12-MariaDB
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._

6
.gitignore vendored
View File

@@ -1,4 +1,10 @@
/adminer/adminer.css
/adminer/adminer-dark.css
/editor/adminer.css
/editor/adminer-dark.css
/adminer*.php
/editor*.php
/tests/pdo-*.html
/vendor/
adminer-plugins/
adminer-plugins.php

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "JsShrink"]
path = externals/JsShrink
url = https://github.com/vrana/JsShrink
[submodule "PhpShrink"]
path = externals/PhpShrink
url = https://github.com/vrana/PhpShrink

1158
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

3
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,3 @@
- Reproducible [bug reports](https://github.com/vrana/adminer/issues/new?template=bug_report.md) are warmly welcomed.
- [Feature requests](https://github.com/vrana/adminer/issues/new?template=BLANK_ISSUE) are also fine, but I'm quite picky about what to accept into Adminer. Please don't be offended if I close the issue as "Not Planned," especially if it can be achieved with a plugin.
- [Pull requests](https://github.com/vrana/adminer/pulls) for both bug fixes and simple features are welcome. Before working on anything more complicated, get familiar with the [Adminer philosophy](https://github.com/vrana/adminer/blob/master/developing.md).

50
README.md Normal file
View File

@@ -0,0 +1,50 @@
# Adminer
**Adminer** is a full-featured database management tool written in PHP. It consists of a single file ready to deploy to the target server.
**Adminer Editor** offers data manipulation for end-users.
[Official Website](https://www.adminer.org/)
## Features
- **Supports:** MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MS SQL, Oracle
- **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse, IMAP
- **Requirements:** PHP 5.3+
## Screenshot
![Screenshot](https://www.adminer.org/static/screenshots/table.png)
## Installation
If downloaded from Git then run: `git submodule update --init`
- `adminer/index.php` - Run development version of Adminer
- `editor/index.php` - Run development version of Adminer Editor
- `editor/example.php` - Example customization
- `adminer/sqlite.php` - Development version of Adminer with SQLite allowed
- `editor/sqlite.php` - Development version of Editor with SQLite allowed
- `adminer/designs.php` - Development version of Adminer with `adminer.css` switcher
- `compile.php` - Create a single file version
- `lang.php` - Update translations
- `tests/*.html` - Katalon Recorder test suites
## Plugins
There are [several plugins](/plugins/) distributed with Adminer, as well as many user-contributed plugins linked on the [Adminer Plugins page](https://www.adminer.org/plugins/).
To use a plugin, simply upload it to the `adminer-plugins/` directory next to `adminer.php`. You can also upload plugins for drivers (e.g., `elastic.php`) in this directory.
```
- adminer.php
- adminer-plugins/
- dump-xml.php
- login-password-less.php
- elastic.php
- ...
- adminer-plugins.php
```
Some plugins require configuration. To use them, create a file named `adminer-plugins.php`. You can also specify the loading order in this file.
```php
<?php // adminer-plugins.php
return array(
new AdminerLoginPasswordLess('$2y$07$Czp9G/aLi3AnaUqpvkF05OHO1LMizrAgMLvnaOdvQovHaRv28XDhG'),
// You can specify all plugins here or just the ones needing configuration.
);
```

View File

@@ -2,10 +2,10 @@
## Supported Versions
I support only the last published version and the last development version (last commit).
Only the latest published version and the latest development version (last commit) are supported.
## Reporting a Vulnerability
To report a vulnerability, add a new draft security advisory at https://github.com/vrana/adminer/security/advisories/new.
To report a vulnerability, create a new draft security advisory at [GitHub Security Advisories](https://github.com/vrana/adminer/security/advisories/new).
I handle security issues with top priority. If you don't hear from me in a week then please ping the bug. Once I accept the bug, the fix should be available and new version released within days. I will mark the bug as public after releasing a new version or declining the bug.
Security issues are handled with top priority. If you don't receive a response within a week, please follow up on the report. Once a vulnerability is acknowledged, a fix should be available and a new version released within a few days. The issue will be made public after the fix is released or if the report is declined.

View File

@@ -85,7 +85,7 @@ if ($in) {
?>
<p>
<input type="submit" value="<?php echo lang('Call'); ?>">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>
<pre>

View File

@@ -47,5 +47,5 @@ echo doc_link(array(
<?php if ($name != "") { ?>
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $name)); ?>
<?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>

View File

@@ -30,7 +30,7 @@ if ($row["auto_increment_col"]) {
}
if ($_POST) {
set_adminer_settings(array("comments" => $_POST["comments"], "defaults" => $_POST["defaults"]));
save_settings(array("comments" => $_POST["comments"], "defaults" => $_POST["defaults"]));
}
if ($_POST && !process_fields($row["fields"]) && !$error) {
@@ -83,17 +83,18 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
$partitioning = "";
if (support("partitioning")) {
if (isset($partition_by[$row["partition_by"]])) {
$params = array_filter($row, function ($key) {
return preg_match('~^partition~', $key);
}, ARRAY_FILTER_USE_KEY);
$params = array();
foreach ($row as $key => $val) {
if (preg_match('~^partition~', $key)) {
$params[$key] = $val;
}
}
foreach ($params["partition_names"] as $key => $name) {
if ($name == "") {
unset($params["partition_names"][$key]);
unset($params["partition_values"][$key]);
}
}
if ($params != get_partitions_info($TABLE)) {
$partitions = array();
if ($params["partition_by"] == 'RANGE' || $params["partition_by"] == 'LIST') {
@@ -102,7 +103,6 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
$partitions[] = "\n PARTITION " . idf_escape($name) . " VALUES " . ($params["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
}
}
// $params["partition"] can be expression, not only column
$partitioning .= "\nPARTITION BY $params[partition_by]($params[partition])";
if ($partitions) {
@@ -168,7 +168,7 @@ if (!$_POST) {
}
$collations = collations();
$engines = engines();
$engines = $driver->engines();
// case of engine may differ
foreach ($engines as $engine) {
if (!strcasecmp($engine, $row["Engine"])) {
@@ -180,32 +180,27 @@ foreach ($engines as $engine) {
<form action="" method="post" id="form">
<p>
<?php if (support("columns") || $TABLE == "") { ?>
<?php echo lang('Table name'); ?>: <input name="name"<?php echo ($TABLE == "" && !$_POST ? " autofocus" : ""); ?> data-maxlength="64" value="<?php echo h($row["name"]); ?>" autocapitalize="off">
<?php echo ($engines ? html_select("Engine", array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . on_help("getTarget(event).value", 1) . script("qsl('select').onchange = helpClose;") : ""); ?>
<?php
<?php
if (support("columns") || $TABLE == "") {
echo lang('Table name') . ": <input name='name'" . ($TABLE == "" && !$_POST ? " autofocus" : "") . " data-maxlength='64' value='" . h($row["name"]) . "' autocapitalize='off'>\n";
echo ($engines ? html_select("Engine", array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . on_help("event.target.value", 1) . script("qsl('select').onchange = helpClose;") . "\n" : "");
if ($collations) {
echo "<datalist id='collations'>" . optionlist($collations) . "</datalist>";
echo (preg_match("~sqlite|mssql~", JUSH) ? "" : "<input list='collations' name='Collation' value='" . h($row["Collation"]) . "' placeholder='(" . lang('collation') . ")'>");
}
?>
<input type="submit" value="<?php echo lang('Save'); ?>">
<?php } ?>
echo "<input type='submit' value='" . lang('Save') . "'>\n";
}
<?php if (support("columns")) { ?>
<div class="scrollable">
<table id="edit-fields" class="nowrap">
<?php
if (support("columns")) {
echo "<div class='scrollable'>\n";
echo "<table id='edit-fields' class='nowrap'>\n";
edit_fields($row["fields"], $collations, "TABLE", $foreign_keys);
?>
</table>
<?php echo script("editFields();"); ?>
</div>
<p>
<?php echo lang('Auto Increment'); ?>: <input type="number" name="Auto_increment" class="size" value="<?php echo h($row["Auto_increment"]); ?>">
<?php echo checkbox("defaults", 1, ($_POST ? $_POST["defaults"] : adminer_setting("defaults")), lang('Default values'), "columnShow(this.checked, 5)", "jsonly"); ?>
<?php
$comments = ($_POST ? $_POST["comments"] : adminer_setting("comments"));
echo "</table>\n";
echo script("editFields();");
echo "</div>\n<p>\n";
echo lang('Auto Increment') . ": <input type='number' name='Auto_increment' class='size' value='" . h($row["Auto_increment"]) . "'>\n";
echo checkbox("defaults", 1, ($_POST ? $_POST["defaults"] : get_setting("defaults")), lang('Default values'), "columnShow(this.checked, 5)", "jsonly");
$comments = ($_POST ? $_POST["comments"] : get_setting("comments"));
echo (support("comment")
? checkbox("comments", 1, $comments, lang('Comment'), "editingCommentsClick(this, true);", "jsonly")
. ' ' . (preg_match('~\n~', $row["Comment"])
@@ -226,25 +221,19 @@ foreach ($engines as $engine) {
if (support("partitioning")) {
$partition_table = preg_match('~RANGE|LIST~', $row["partition_by"]);
print_fieldset("partition", lang('Partition by'), $row["partition_by"]);
?>
<p>
<?php echo html_select("partition_by", array("" => "") + $partition_by, $row["partition_by"]) . on_help("getTarget(event).value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;"); ?>
(<input name="partition" value="<?php echo h($row["partition"]); ?>">)
<?php echo lang('Partitions'); ?>: <input type="number" name="partitions" class="size<?php echo ($partition_table || !$row["partition_by"] ? " hidden" : ""); ?>" value="<?php echo h($row["partitions"]); ?>">
<table id="partition-table"<?php echo ($partition_table ? "" : " class='hidden'"); ?>>
<thead><tr><th><?php echo lang('Partition name'); ?><th><?php echo lang('Values'); ?></thead>
<?php
echo "<p>" . html_select("partition_by", array("" => "") + $partition_by, $row["partition_by"]) . on_help("event.target.value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;");
echo "(<input name='partition' value='" . h($row["partition"]) . "'>)\n";
echo lang('Partitions') . ": <input type='number' name='partitions' class='size" . ($partition_table || !$row["partition_by"] ? " hidden" : "") . "' value='" . h($row["partitions"]) . "'>\n";
echo "<table id='partition-table'" . ($partition_table ? "" : " class='hidden'") . ">\n";
echo "<thead><tr><th>" . lang('Partition name') . "<th>" . lang('Values') . "</thead>\n";
foreach ($row["partition_names"] as $key => $val) {
echo '<tr>';
echo '<td><input name="partition_names[]" value="' . h($val) . '" autocapitalize="off">';
echo ($key == count($row["partition_names"]) - 1 ? script("qsl('input').oninput = partitionNameChange;") : '');
echo '<td><input name="partition_values[]" value="' . h($row["partition_values"][$key]) . '">';
}
?>
</table>
</div></fieldset>
<?php
echo "</table>\n</div></fieldset>\n";
}
echo input_token();
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -76,6 +76,6 @@ if (DB != "") {
} elseif (!$_POST["add_x"] && $_GET["db"] == "") {
echo "<input type='image' class='icon' name='add' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>\n";
}
echo input_token();
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -157,7 +157,7 @@ if ($adminer->homepage()) {
}
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 "<input type='hidden' name='token' value='$token'>\n";
echo input_token();
echo "</div></fieldset>\n";
echo "</div></div>\n";
}
@@ -165,8 +165,8 @@ if ($adminer->homepage()) {
echo script("tableCheck();");
}
echo '<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 "<p class='links'><a href='" . h(ME) . "create='>" . lang('Create table') . "</a>\n";
echo (support("view") ? "<a href='" . h(ME) . "view='>" . lang('Create view') . "</a>\n" : "");
if (support("routine")) {
echo "<h3 id='routines'>" . lang('Routines') . "</h3>\n";

View File

@@ -3,8 +3,13 @@ function adminer_object() {
include_once "../plugins/plugin.php";
include_once "../plugins/designs.php";
$designs = array();
foreach (glob("../designs/*", GLOB_ONLYDIR) as $filename) {
$designs["$filename/adminer.css"] = basename($filename);
foreach (glob("../designs/*", GLOB_ONLYDIR) as $dirname) {
foreach (array("", "-dark") as $mode) {
$filename = "$dirname/adminer$mode.css";
if (file_exists($filename)) {
$designs[$filename] = basename($dirname);
}
}
}
return new AdminerPlugin(array(
new AdminerDesigns($designs),

View File

@@ -11,9 +11,9 @@ $drivers["mssql"] = "MS SQL";
if (isset($_GET["mssql"])) {
define('Adminer\DRIVER', "mssql");
if (extension_loaded("sqlsrv")) {
if (extension_loaded("sqlsrv") && $_GET["ext"] != "pdo") {
class Db {
public $extension = "sqlsrv", $server_info, $affected_rows, $errno, $error;
public $extension = "sqlsrv", $flavor = '', $server_info, $affected_rows, $errno, $error;
private $link, $result;
private function get_error() {
@@ -140,8 +140,8 @@ if (isset($_GET["mssql"])) {
$field = $this->fields[$this->offset++];
$return = new \stdClass;
$return->name = $field["Name"];
$return->orgname = $field["Name"];
$return->type = ($field["Type"] == 1 ? 254 : 0);
$return->type = ($field["Type"] == 1 ? 254 : 15);
$return->charsetnr = 0;
return $return;
}
@@ -156,32 +156,55 @@ if (isset($_GET["mssql"])) {
}
}
} elseif (extension_loaded("pdo_sqlsrv")) {
class Db extends PdoDb {
public $extension = "PDO_SQLSRV";
function last_id($result) {
return get_val("SELECT SCOPE_IDENTITY()"); // @@IDENTITY can return trigger INSERT
}
function connect($server, $username, $password) {
$this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password);
return true;
}
function explain($connection, $query) {
$connection->query("SET SHOWPLAN_ALL ON");
$return = $connection->query($query);
$connection->query("SET SHOWPLAN_ALL OFF"); // connection is used also for indexes
return $return;
}
} else {
class MssqlDb extends PdoDb {
function select_db($database) {
// database selection is separated from the connection so dbname in DSN can't be used
return $this->query(use_sql($database));
}
function lastInsertId() {
return $this->pdo->lastInsertId();
}
}
} elseif (extension_loaded("pdo_dblib")) {
class Db extends PdoDb {
public $extension = "PDO_DBLIB";
function last_id($result) {
global $connection;
return $connection->lastInsertId();
}
function connect($server, $username, $password) {
$this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password);
return true;
function explain($connection, $query) {
}
if (extension_loaded("pdo_sqlsrv")) {
class Db extends MssqlDb {
public $extension = "PDO_SQLSRV";
function connect($server, $username, $password) {
$this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password);
return true;
}
}
function select_db($database) {
return $this->query(use_sql($database));
} elseif (extension_loaded("pdo_dblib")) {
class Db extends MssqlDb {
public $extension = "PDO_DBLIB";
function connect($server, $username, $password) {
$this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password);
return true;
}
}
}
}
@@ -309,10 +332,6 @@ if (isset($_GET["mssql"])) {
return get_val("SELECT collation_name FROM sys.databases WHERE name = " . q($db));
}
function engines() {
return array();
}
function logged_user() {
return get_val("SELECT SUSER_NAME()");
}
@@ -359,7 +378,7 @@ WHERE schema_id = SCHEMA_ID(" . q(get_schema()) . ") AND type IN ('S', 'U', 'V')
$return = array();
$table_id = get_val("SELECT object_id FROM sys.all_objects WHERE schema_id = SCHEMA_ID(" . q(get_schema()) . ") AND type IN ('S', 'U', 'V') AND name = " . q($table));
foreach (
get_rows("SELECT c.max_length, c.precision, c.scale, c.name, c.is_nullable, c.is_identity, c.collation_name, t.name type, CAST(d.definition as text) [default], d.name default_constraint, i.is_primary_key
get_rows("SELECT c.max_length, c.precision, c.scale, c.name, c.is_nullable, c.is_identity, c.collation_name, t.name type, d.definition [default], d.name default_constraint, i.is_primary_key
FROM sys.all_columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
LEFT JOIN sys.default_constraints d ON c.default_object_id = d.object_id
@@ -509,7 +528,16 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table), $connection2) as $row
foreach ($comments as $key => $val) {
$comment = substr($val, 9); // 9 - strlen(" COMMENT ")
queries("EXEC sp_dropextendedproperty @name = N'MS_Description', @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
queries("EXEC sp_addextendedproperty @name = N'MS_Description', @value = " . $comment . ", @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
queries("EXEC sp_addextendedproperty
@name = N'MS_Description',
@value = $comment,
@level0type = N'Schema',
@level0name = " . q(get_schema()) . ",
@level1type = N'Table',
@level1name = " . q($name) . ",
@level2type = N'Column',
@level2name = " . q($key))
;
}
return true;
}
@@ -538,17 +566,6 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table), $connection2) as $row
;
}
function last_id() {
return get_val("SELECT SCOPE_IDENTITY()"); // @@IDENTITY can return trigger INSERT
}
function explain($connection, $query) {
$connection->query("SET SHOWPLAN_ALL ON");
$return = $connection->query($query);
$connection->query("SET SHOWPLAN_ALL OFF"); // connection is used also for indexes
return $return;
}
function found_rows($table_status, $where) {
}

View File

@@ -1,19 +1,20 @@
<?php
namespace Adminer;
$drivers = array("server" => "MySQL") + $drivers;
$drivers = array("server" => "MySQL / MariaDB") + $drivers;
if (!defined('Adminer\DRIVER')) {
define('Adminer\DRIVER', "server"); // server - backwards compatibility
// MySQLi supports everything, MySQL doesn't support multiple result sets, PDO_MySQL doesn't support orgtable
if (extension_loaded("mysqli")) {
if (extension_loaded("mysqli") && $_GET["ext"] != "pdo") {
class Db extends \MySQLi {
public $extension = "MySQLi";
public $extension = "MySQLi", $flavor = '';
function __construct() {
parent::init();
}
/** @see https://php.net/mysqli.construct */
function connect($server = "", $username = "", $password = "", $database = null, $port = null, $socket = null) {
global $adminer;
mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4
@@ -50,7 +51,7 @@ if (!defined('Adminer\DRIVER')) {
return false;
}
$row = $result->fetch_array();
return $row[$field];
return ($row ? $row[$field] : false);
}
function quote($string) {
@@ -62,8 +63,10 @@ if (!defined('Adminer\DRIVER')) {
class Db {
public
$extension = "MySQL", ///< @var string extension name
$flavor = '', ///< @var string different vendor with the same API, e.g. MariaDB, usually stays empty
$server_info, ///< @var string server version
$affected_rows, ///< @var int number of affected rows
$info, ///< @var string see https://php.net/mysql_info
$errno, ///< @var int last error code
$error ///< @var string last error message
;
@@ -95,7 +98,7 @@ if (!defined('Adminer\DRIVER')) {
return (bool) $this->link;
}
/** Sets the client character set
/** Set the client character set
* @param string
* @return bool
*/
@@ -216,12 +219,11 @@ if (!defined('Adminer\DRIVER')) {
}
/** Fetch next field
* @return object properties: name, type, orgtable, orgname, charsetnr
* @return object properties: name, type (0 number, 15 varchar, 254 char), charsetnr (63 binary); optionally: table, orgtable, orgname
*/
function fetch_field() {
$return = mysql_fetch_field($this->result, $this->offset++); // offset required under certain conditions
$return->orgtable = $return->table;
$return->orgname = $return->name;
$return->charsetnr = ($return->blob ? 63 : 0);
return $return;
}
@@ -330,6 +332,13 @@ if (!defined('Adminer\DRIVER')) {
}
}
function unconvertFunction($field) {
return (preg_match("~binary~", $field["type"]) ? "<code class='jush-sql'>UNHEX</code>"
: ($field["type"] == "bit" ? doc_link(array('sql' => 'bit-value-literals.html'), "<code>b''</code>")
: (preg_match("~geometry|point|linestring|polygon~", $field["type"]) ? "<code class='jush-sql'>GeomFromText</code>"
: "")));
}
function insert($table, $set) {
return ($set ? parent::insert($table, $set) : queries("INSERT INTO " . table($table) . " ()\nVALUES ()"));
}
@@ -361,7 +370,7 @@ if (!defined('Adminer\DRIVER')) {
function slowQuery($query, $timeout) {
if (min_version('5.7.8', '10.1.2')) {
if (preg_match('~MariaDB~', $this->conn->server_info)) {
if ($this->conn->flavor == 'maria') {
return "SET STATEMENT max_statement_time=$timeout FOR $query";
} elseif (preg_match('~^(SELECT\b)(.+)~is', $query, $match)) {
return "$match[1] /*+ MAX_EXECUTION_TIME(" . ($timeout * 1000) . ") */ $match[2]";
@@ -386,7 +395,7 @@ if (!defined('Adminer\DRIVER')) {
}
function tableHelp($name, $is_view = false) {
$maria = preg_match('~MariaDB~', $this->conn->server_info);
$maria = ($this->conn->flavor == 'maria');
if (information_schema(DB)) {
return strtolower("information-schema-" . ($maria ? "$name-table/" : str_replace("_", "-", $name) . "-table.html"));
}
@@ -403,6 +412,16 @@ if (!defined('Adminer\DRIVER')) {
}
return $c_style;
}
function engines() {
$return = array();
foreach (get_rows("SHOW ENGINES") as $row) {
if (preg_match("~YES|DEFAULT~", $row["Support"])) {
$return[] = $row["Engine"];
}
}
return $return;
}
}
@@ -428,10 +447,13 @@ if (!defined('Adminer\DRIVER')) {
* @return mixed Db or string for error
*/
function connect($credentials) {
global $drivers;
$connection = new Db;
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
$connection->set_charset(charset($connection)); // available in MySQLi since PHP 5.0.5
$connection->set_charset(charset($connection));
$connection->query("SET sql_quote_show_create = 1, autocommit = 1");
$connection->flavor = (preg_match('~MariaDB~', $connection->server_info) ? 'maria' : '');
$drivers[DRIVER] = ($connection->flavor == 'maria' ? "MariaDB" : "MySQL");
return $connection;
}
$return = $connection->error;
@@ -498,19 +520,6 @@ if (!defined('Adminer\DRIVER')) {
return $return;
}
/** Get supported engines
* @return array
*/
function engines() {
$return = array();
foreach (get_rows("SHOW ENGINES") as $row) {
if (preg_match("~YES|DEFAULT~", $row["Support"])) {
$return[] = $row["Engine"];
}
}
return $return;
}
/** Get logged user
* @return string
*/
@@ -587,11 +596,11 @@ if (!defined('Adminer\DRIVER')) {
/** Get information about fields
* @param string
* @return array [$name => ["field" => , "full_type" => , "type" => , "length" => , "unsigned" => , "default" => , "null" => , "auto_increment" => , "on_update" => , "collation" => , "privileges" => , "comment" => , "primary" => , "generated" => ]]
* @return array [$name => ["field" =>, "full_type" =>, "type" =>, "length" =>, "unsigned" =>, "default" =>, "null" =>, "auto_increment" =>, "on_update" =>, "collation" =>, "privileges" =>, "comment" =>, "primary" =>, "generated" =>]]
*/
function fields($table) {
global $connection;
$maria = preg_match('~MariaDB~', $connection->server_info);
$maria = ($connection->flavor == 'maria');
$return = array();
foreach (get_rows("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = " . q($table) . " ORDER BY ORDINAL_POSITION") as $row) {
$field = $row["COLUMN_NAME"];
@@ -600,27 +609,32 @@ if (!defined('Adminer\DRIVER')) {
$extra = $row["EXTRA"];
// https://mariadb.com/kb/en/library/show-columns/, https://github.com/vrana/adminer/pull/359#pullrequestreview-276677186
preg_match('~^(VIRTUAL|PERSISTENT|STORED)~', $extra, $generated);
preg_match('~^([^( ]+)(?:\((.+)\))?( unsigned)?( zerofill)?$~', $type, $match);
preg_match('~^([^( ]+)(?:\((.+)\))?( unsigned)?( zerofill)?$~', $type, $match_type);
$default = $row["COLUMN_DEFAULT"];
$is_text = preg_match('~text~', $match[1]);
if (!$maria && $is_text) {
// default value a'b of text column is stored as _utf8mb4\'a\\\'b\' in MySQL
$default = preg_replace("~^(_\w+)?('.*')$~", '\2', stripslashes($default));
}
if ($maria || $is_text) {
$default = preg_replace_callback("~^'(.*)'$~", function ($match) {
return stripslashes(str_replace("''", "'", $match[1]));
}, $default);
if ($default != "") {
$is_text = preg_match('~text|json~', $match_type[1]);
if (!$maria && $is_text) {
// default value a'b of text column is stored as _utf8mb4\'a\\\'b\' in MySQL
$default = preg_replace("~^(_\w+)?('.*')$~", '\2', stripslashes($default));
}
if ($maria || $is_text) {
$default = ($default == "NULL" ? null : preg_replace_callback("~^'(.*)'$~", function ($match) {
return stripslashes(str_replace("''", "'", $match[1]));
}, $default));
}
if (!$maria && preg_match('~binary~', $match_type[1]) && preg_match('~^0x(\w*)$~', $default, $match)) {
$default = pack("H*", $match[1]);
}
}
$return[$field] = array(
"field" => $field,
"full_type" => $type,
"type" => $match[1],
"length" => $match[2],
"unsigned" => ltrim($match[3] . $match[4]),
"type" => $match_type[1],
"length" => $match_type[2],
"unsigned" => ltrim($match_type[3] . $match_type[4]),
"default" => ($generated
? ($maria ? $generation : stripslashes($generation))
: ($default != "" || preg_match("~char|set~", $match[1]) ? $default : null)
: $default
),
"null" => ($row["IS_NULLABLE"] == "YES"),
"auto_increment" => ($extra == "auto_increment"),
@@ -662,7 +676,12 @@ if (!defined('Adminer\DRIVER')) {
$return = array();
$create_table = get_val("SHOW CREATE TABLE " . table($table), 1);
if ($create_table) {
preg_match_all("~CONSTRAINT ($pattern) FOREIGN KEY ?\\(((?:$pattern,? ?)+)\\) REFERENCES ($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE ($driver->onActions))?(?: ON UPDATE ($driver->onActions))?~", $create_table, $matches, PREG_SET_ORDER);
preg_match_all(
"~CONSTRAINT ($pattern) FOREIGN KEY ?\\(((?:$pattern,? ?)+)\\) REFERENCES ($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE ($driver->onActions))?(?: ON UPDATE ($driver->onActions))?~",
$create_table,
$matches,
PREG_SET_ORDER
);
foreach ($matches as $match) {
preg_match_all("~$pattern~", $match[2], $source);
preg_match_all("~$pattern~", $match[5], $target);
@@ -806,7 +825,7 @@ if (!defined('Adminer\DRIVER')) {
$default = $field[1][3];
if (preg_match('~ GENERATED~', $default)) {
// swap default and null
$field[1][3] = (preg_match('~MariaDB~', $connection->server_info) ? "" : $field[1][2]); // MariaDB doesn't support NULL on virtual columns
$field[1][3] = ($connection->flavor == 'maria' ? "" : $field[1][2]); // MariaDB doesn't support NULL on virtual columns
$field[1][2] = $default;
}
$alter[] = ($table != "" ? ($field[0] != "" ? "CHANGE " . idf_escape($field[0]) : "ADD") : " ") . " " . implode($field[1]) . ($table != "" ? $field[2] : "");
@@ -982,7 +1001,9 @@ if (!defined('Adminer\DRIVER')) {
global $driver;
$aliases = array("bool", "boolean", "integer", "double precision", "real", "dec", "numeric", "fixed", "national char", "national varchar");
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
$type_pattern = "((" . implode("|", array_merge(array_keys($driver->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$driver->enumLength)++)\\))?\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";
$enum = $driver->enumLength;
$type_pattern = "((" . implode("|", array_merge(array_keys($driver->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum)++)\\))?"
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : $driver->inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
$create = get_val("SHOW CREATE $type " . idf_escape($name), 2);
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match);
@@ -992,7 +1013,7 @@ if (!defined('Adminer\DRIVER')) {
$fields[] = array(
"field" => str_replace("``", "`", $param[2]) . $param[3],
"type" => strtolower($param[5]),
"length" => preg_replace_callback("~$driver->enumLength~s", 'Adminer\normalize_enum', $param[6]),
"length" => preg_replace_callback("~$enum~s", 'Adminer\normalize_enum', $param[6]),
"unsigned" => strtolower(preg_replace('~\s+~', ' ', trim("$param[8] $param[7]"))),
"null" => 1,
"full_type" => $param[4],
@@ -1034,9 +1055,10 @@ if (!defined('Adminer\DRIVER')) {
}
/** Get last auto increment ID
* @param Result or true
* @return string
*/
function last_id() {
function last_id($result) {
return get_val("SELECT LAST_INSERT_ID()"); // mysql_insert_id() truncates bigint
}
@@ -1136,10 +1158,17 @@ if (!defined('Adminer\DRIVER')) {
}
/** Get server variables
* @return array [$name => $value]
* @return array [[$name, $value]]
*/
function show_variables() {
return get_key_vals("SHOW VARIABLES");
return get_rows("SHOW VARIABLES");
}
/** Get status variables
* @return array [[$name, $value]]
*/
function show_status() {
return get_rows("SHOW STATUS");
}
/** Get process list
@@ -1149,13 +1178,6 @@ if (!defined('Adminer\DRIVER')) {
return get_rows("SHOW FULL PROCESSLIST");
}
/** Get status variables
* @return array [$name => $value]
*/
function show_status() {
return get_key_vals("SHOW STATUS");
}
/** Convert field in select and edit
* @param array one element from fields()
* @return string
@@ -1192,7 +1214,7 @@ if (!defined('Adminer\DRIVER')) {
}
/** Check whether a feature is supported
* @param string "check", "comment", "copy", "database", "descidx", "drop_col", "dump", "event", "indexes", "kill", "materializedview", "partitioning", "privileges", "procedure", "processlist", "routine", "scheme", "sequence", "status", "table", "trigger", "type", "variables", "view", "view_trigger"
* @param string "check|comment|copy|database|descidx|drop_col|dump|event|indexes|kill|materializedview|partitioning|privileges|procedure|processlist|routine|scheme|sequence|status|table|trigger|type|variables|view|view_trigger"
* @return bool
*/
function support($feature) {

View File

@@ -5,9 +5,9 @@ $drivers["oracle"] = "Oracle (beta)";
if (isset($_GET["oracle"])) {
define('Adminer\DRIVER', "oracle");
if (extension_loaded("oci8")) {
if (extension_loaded("oci8") && $_GET["ext"] != "pdo") {
class Db {
public $extension = "oci8", $server_info, $affected_rows, $errno, $error;
public $extension = "oci8", $flavor = '', $server_info, $affected_rows, $errno, $error;
public $_current_db;
private $link, $result;
@@ -112,8 +112,7 @@ if (isset($_GET["oracle"])) {
$column = $this->offset++;
$return = new \stdClass;
$return->name = oci_field_name($this->result, $column);
$return->orgname = $return->name;
$return->type = oci_field_type($this->result, $column);
$return->type = oci_field_type($this->result, $column); //! map to MySQL numbers
$return->charsetnr = (preg_match("~raw|blob|bfile~", $return->type) ? 63 : 0); // 63 - binary
return $return;
}
@@ -247,10 +246,6 @@ ORDER BY 1"
return get_val("SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET'"); //! respect $db
}
function engines() {
return array();
}
function logged_user() {
return get_val("SELECT USER FROM DUAL");
}
@@ -495,7 +490,7 @@ AND c_src.TABLE_NAME = " . q($table);
return apply_queries("DROP TABLE", $tables);
}
function last_id() {
function last_id($result) {
return 0; //!
}
@@ -517,11 +512,29 @@ AND c_src.TABLE_NAME = " . q($table);
}
function show_variables() {
return get_key_vals('SELECT name, display_value FROM v$parameter');
return get_rows('SELECT name, display_value FROM v$parameter');
}
function show_status() {
$return = array();
$rows = get_rows('SELECT * FROM v$instance');
foreach (reset($rows) as $key => $val) {
$return[] = array($key, $val);
}
return $return;
}
function process_list() {
return get_rows('SELECT sess.process AS "process", sess.username AS "user", sess.schemaname AS "schema", sess.status AS "status", sess.wait_class AS "wait_class", sess.seconds_in_wait AS "seconds_in_wait", sql.sql_text AS "sql_text", sess.machine AS "machine", sess.port AS "port"
return get_rows('SELECT
sess.process AS "process",
sess.username AS "user",
sess.schemaname AS "schema",
sess.status AS "status",
sess.wait_class AS "wait_class",
sess.seconds_in_wait AS "seconds_in_wait",
sql.sql_text AS "sql_text",
sess.machine AS "machine",
sess.port AS "port"
FROM v$session sess LEFT OUTER JOIN v$sql sql
ON sql.sql_id = sess.sql_id
WHERE sess.type = \'USER\'
@@ -529,11 +542,6 @@ ORDER BY PROCESS
');
}
function show_status() {
$rows = get_rows('SELECT * FROM v$instance');
return reset($rows);
}
function convert_field($field) {
}

View File

@@ -5,9 +5,9 @@ $drivers["pgsql"] = "PostgreSQL";
if (isset($_GET["pgsql"])) {
define('Adminer\DRIVER', "pgsql");
if (extension_loaded("pgsql")) {
if (extension_loaded("pgsql") && $_GET["ext"] != "pdo") {
class Db {
public $extension = "PgSQL", $server_info, $affected_rows, $error, $timeout;
public $extension = "PgSQL", $flavor = '', $server_info, $affected_rows, $error, $timeout;
private $link, $result, $string, $database = true;
function _error($errno, $error) {
@@ -35,25 +35,22 @@ if (isset($_GET["pgsql"])) {
}
restore_error_handler();
if ($this->link) {
$version = pg_version($this->link);
$this->server_info = $version["server"];
pg_set_client_encoding($this->link, "UTF8");
}
return (bool) $this->link;
}
function quote($string) {
return pg_escape_literal($this->link, $string);
return (function_exists('pg_escape_literal')
? pg_escape_literal($this->link, $string) // available since PHP 5.4.4
: "'" . pg_escape_string($this->link, $string) . "'"
);
}
function value($val, $field) {
return ($field["type"] == "bytea" && $val !== null ? pg_unescape_bytea($val) : $val);
}
function quoteBinary($string) {
return "'" . pg_escape_bytea($this->link, $string) . "'";
}
function select_db($database) {
global $adminer;
if ($database == $adminer->database()) {
@@ -136,12 +133,9 @@ if (isset($_GET["pgsql"])) {
function fetch_field() {
$column = $this->offset++;
$return = new \stdClass;
if (function_exists('pg_field_table')) {
$return->orgtable = pg_field_table($this->result, $column);
}
$return->orgtable = pg_field_table($this->result, $column);
$return->name = pg_field_name($this->result, $column);
$return->orgname = $return->name;
$return->type = pg_field_type($this->result, $column);
$return->type = pg_field_type($this->result, $column); //! map to MySQL numbers
$return->charsetnr = ($return->type == "bytea" ? 63 : 0); // 63 - binary
return $return;
}
@@ -173,10 +167,6 @@ if (isset($_GET["pgsql"])) {
return ($adminer->database() == $database);
}
function quoteBinary($s) {
return q($s);
}
function query($query, $unbuffered = false) {
$return = parent::query($query, $unbuffered);
if ($this->timeout) {
@@ -246,6 +236,13 @@ if (isset($_GET["pgsql"])) {
$this->types[lang('User types')] = array_flip($types);
}
function insertReturning($table) {
$auto_increment = array_filter(fields($table), function ($field) {
return $field['auto_increment'];
});
return (count($auto_increment) == 1 ? " RETURNING " . idf_escape(key($auto_increment)) : "");
}
function insertUpdate($table, $rows, $primary) {
global $connection;
foreach ($rows as $set) {
@@ -283,7 +280,7 @@ if (isset($_GET["pgsql"])) {
}
function quoteBinary($s) {
return $this->conn->quoteBinary($s);
return "'\\x" . bin2hex($s) . "'"; // available since PostgreSQL 8.1
}
function warnings() {
@@ -326,11 +323,18 @@ if (isset($_GET["pgsql"])) {
}
function connect($credentials) {
global $drivers;
$connection = new Db;
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
if (min_version(9, 0, $connection)) {
$connection->query("SET application_name = 'Adminer'");
}
$version = $connection->result("SELECT version()");
$connection->flavor = (preg_match('~CockroachDB~', $version) ? 'cockroach' : '');
$connection->server_info = preg_replace('~^\D*([\d.]+[-\w]*).*~', '\1', $version);
if ($connection->flavor == 'cockroach') { // we don't use "PostgreSQL / CockroachDB" by default because it's too long
$drivers[DRIVER] = "CockroachDB";
}
return $connection;
}
return $connection->error;
@@ -357,10 +361,6 @@ ORDER BY datname");
return get_val("SELECT datcollate FROM pg_database WHERE datname = " . q($db));
}
function engines() {
return array();
}
function logged_user() {
return get_val("SELECT user");
}
@@ -432,7 +432,14 @@ WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
'timestamp with time zone' => 'timestamptz',
);
foreach (
get_rows("SELECT a.attname AS field, format_type(a.atttypid, a.atttypmod) AS full_type, pg_get_expr(d.adbin, d.adrelid) AS default, a.attnotnull::int, col_description(c.oid, a.attnum) AS comment" . (min_version(10) ? ", a.attidentity" . (min_version(12) ? ", a.attgenerated" : "") : "") . "
get_rows("SELECT
a.attname AS field,
format_type(a.atttypid, a.atttypmod) AS full_type,
pg_get_expr(d.adbin, d.adrelid) AS default,
a.attnotnull::int,
col_description(c.oid, a.attnum) AS comment" . (min_version(10) ? ",
a.attidentity" . (min_version(12) ? ",
a.attgenerated" : "") : "") . "
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
JOIN pg_attribute a ON c.oid = a.attrelid
@@ -460,7 +467,8 @@ ORDER BY a.attnum") as $row
}
$row["generated"] = ($row["attgenerated"] == "s" ? "STORED" : "");
$row["null"] = !$row["attnotnull"];
$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
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1, "where" => 1, "order" => 1);
if (preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
$row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]);
@@ -478,7 +486,12 @@ ORDER BY a.attnum") as $row
$return = array();
$table_oid = $connection2->result("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($table));
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
foreach (get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial FROM pg_index i, pg_class ci WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row) {
foreach (
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial
FROM pg_index i, pg_class ci
WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid
ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
) {
$relname = $row["relname"];
$return[$relname]["type"] = ($row["indispartial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX")));
$return[$relname]["columns"] = array();
@@ -698,7 +711,12 @@ ORDER BY conkey, conname") as $row
$columns[] = $row["event_object_column"];
}
$return = array();
foreach (get_rows('SELECT trigger_name AS "Trigger", action_timing AS "Timing", event_manipulation AS "Event", \'FOR EACH \' || action_orientation AS "Type", action_statement AS "Statement" FROM information_schema.triggers ' . "$where ORDER BY event_manipulation DESC") as $row) {
foreach (
get_rows('SELECT trigger_name AS "Trigger", action_timing AS "Timing", event_manipulation AS "Event", \'FOR EACH \' || action_orientation AS "Type", action_statement AS "Statement"
FROM information_schema.triggers' . "
$where
ORDER BY event_manipulation DESC") as $row
) {
if ($columns && $row["Event"] == "UPDATE") {
$row["Event"] .= " OF";
}
@@ -755,13 +773,14 @@ ORDER BY SPECIFIC_NAME');
function routine_id($name, $row) {
$return = array();
foreach ($row["fields"] as $field) {
$return[] = $field["type"];
$length = $field["length"];
$return[] = $field["type"] . ($length ? "($length)" : "");
}
return idf_escape($name) . "(" . implode(", ", $return) . ")";
}
function last_id() {
return 0; // there can be several sequences
function last_id($result) {
return (is_object($result) && $result->num_rows ? $result->fetch_column(0) : 0);
}
function explain($connection, $query) {
@@ -854,7 +873,7 @@ AND typelem = 0"
// sequences for fields
if (preg_match('~nextval\(\'([^\']+)\'\)~', $field['default'], $matches)) {
$sequence_name = $matches[1];
$sq = reset(get_rows((min_version(10)
$sq = first(get_rows((min_version(10)
? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q(idf_unescape($sequence_name))
: "SELECT * FROM $sequence_name"
), null, "-- "));
@@ -923,7 +942,7 @@ AND typelem = 0"
}
function show_variables() {
return get_key_vals("SHOW ALL");
return get_rows("SHOW ALL");
}
function process_list() {
@@ -938,7 +957,11 @@ AND typelem = 0"
}
function support($feature) {
return preg_match('~^(check|database|table|columns|sql|indexes|descidx|comment|view|' . (min_version(9.3) ? 'materializedview|' : '') . 'scheme|routine|processlist|sequence|trigger|type|variables|drop_col|kill|dump)$~', $feature);
global $connection;
return preg_match('~^(check|database|table|columns|sql|indexes|descidx|comment|view|' . (min_version(9.3) ? 'materializedview|' : '') . 'scheme|' . (min_version(11) ? 'procedure|' : '') . 'routine|sequence|trigger|type|variables|drop_col'
. ($connection->flavor == 'cockroach' ? '' : '|processlist') // https://github.com/cockroachdb/cockroach/issues/24745
. '|kill|dump)$~', $feature)
;
}
function kill_process($val) {

View File

@@ -5,7 +5,7 @@ $drivers["sqlite"] = "SQLite";
if (isset($_GET["sqlite"])) {
define('Adminer\DRIVER', "sqlite");
if (class_exists("SQLite3")) {
if (class_exists("SQLite3") && $_GET["ext"] != "pdo") {
class SqliteDb {
public $extension = "SQLite3", $server_info, $affected_rows, $errno, $error;
@@ -34,7 +34,7 @@ if (isset($_GET["sqlite"])) {
function quote($string) {
return (is_utf8($string)
? "'" . $this->link->escapeString($string) . "'"
: "x'" . reset(unpack('H*', $string)) . "'"
: "x'" . first(unpack('H*', $string)) . "'"
);
}
@@ -73,12 +73,12 @@ if (isset($_GET["sqlite"])) {
$type = $this->result->columnType($column);
return (object) array(
"name" => $this->result->columnName($column),
"type" => $type,
"type" => ($type == SQLITE3_TEXT ? 15 : 0),
"charsetnr" => ($type == SQLITE3_BLOB ? 63 : 0), // 63 - binary
);
}
function __desctruct() {
function __destruct() {
return $this->result->finalize();
}
}
@@ -100,6 +100,7 @@ if (isset($_GET["sqlite"])) {
if (class_exists('Adminer\SqliteDb')) {
class Db extends SqliteDb {
public $flavor = '';
function __construct() {
parent::__construct(":memory:");
@@ -115,14 +116,6 @@ if (isset($_GET["sqlite"])) {
}
return false;
}
function multi_query($query) {
return $this->result = $this->query($query);
}
function next_result() {
return false;
}
}
}
@@ -219,10 +212,6 @@ if (isset($_GET["sqlite"])) {
return get_val("PRAGMA encoding"); // there is no database list so $db == DB
}
function engines() {
return array();
}
function logged_user() {
return get_current_user(); // should return effective user
}
@@ -397,7 +386,7 @@ if (isset($_GET["sqlite"])) {
}
try {
$link = new SqliteDb($db);
} catch (Exception $ex) {
} catch (\Exception $ex) {
$connection->error = $ex->getMessage();
return false;
}
@@ -680,7 +669,7 @@ if (isset($_GET["sqlite"])) {
return queries("BEGIN");
}
function last_id() {
function last_id($result) {
return get_val("SELECT LAST_INSERT_ROWID()");
}
@@ -722,8 +711,9 @@ if (isset($_GET["sqlite"])) {
foreach (get_rows("PRAGMA pragma_list") as $row) {
$name = $row["name"];
if ($name != "pragma_list" && $name != "compile_options") {
$return[$name] = array($name, '');
foreach (get_rows("PRAGMA $name") as $row) {
$return[$name] .= implode(", ", $row) . "\n";
$return[$name][1] .= implode(", ", $row) . "\n";
}
}
}
@@ -733,8 +723,7 @@ if (isset($_GET["sqlite"])) {
function show_status() {
$return = array();
foreach (get_vals("PRAGMA compile_options") as $option) {
list($key, $val) = explode("=", $option, 2);
$return[$key] = $val;
$return[] = explode("=", $option, 2);
}
return $return;
}

View File

@@ -4,11 +4,10 @@ namespace Adminer;
$TABLE = $_GET["dump"];
if ($_POST && !$error) {
$cookie = "";
foreach (array("output", "format", "db_style", "types", "routines", "events", "table_style", "auto_increment", "triggers", "data_style") as $key) {
$cookie .= "&$key=" . urlencode($_POST[$key]);
}
cookie("adminer_export", substr($cookie, 1));
save_settings(
array_intersect_key($_POST, array_flip(array("output", "format", "db_style", "types", "routines", "events", "table_style", "auto_increment", "triggers", "data_style"))),
"adminer_export"
);
$tables = array_flip((array) $_POST["tables"]) + array_flip((array) $_POST["data"]);
$ext = dump_headers(
(count($tables) == 1 ? key($tables) : DB),
@@ -156,7 +155,7 @@ $data_style = array('', 'TRUNCATE+INSERT', 'INSERT');
if (JUSH == "sql") { //! use insertUpdate() in all drivers
$data_style[] = 'INSERT+UPDATE';
}
parse_str($_COOKIE["adminer_export"], $row);
$row = get_settings("adminer_export");
if (!$row) {
$row = array("output" => "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT");
}
@@ -184,7 +183,7 @@ echo "<tr><th>" . lang('Data') . "<td>" . html_select('data_style', $data_style,
?>
</table>
<p><input type="submit" value="<?php echo lang('Export'); ?>">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
<table>
<?php

View File

@@ -55,7 +55,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
}
} else {
$result = $driver->insert($TABLE, $set);
$last_id = ($result ? last_id() : 0);
$last_id = ($result ? last_id($result) : 0);
queries_redirect($location, lang('Item%s has been inserted.', ($last_id ? " $last_id" : "")), $result); //! link
}
}

View File

@@ -56,5 +56,5 @@ if (!$row && $EVENT != "") {
<?php if ($EVENT != "") { ?>
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $EVENT)); ?>
<?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>

View File

@@ -1,7 +1,15 @@
<?php
namespace Adminer;
// caching headers added in compile.php
if (substr($VERSION, -4) != '-dev') {
if ($_SERVER["HTTP_IF_MODIFIED_SINCE"]) {
header("HTTP/1.1 304 Not Modified");
exit;
}
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 365*24*60*60) . " GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: immutable");
}
if ($_GET["file"] == "favicon.ico") {
header("Content-Type: image/x-icon");
@@ -9,6 +17,9 @@ if ($_GET["file"] == "favicon.ico") {
} elseif ($_GET["file"] == "default.css") {
header("Content-Type: text/css; charset=utf-8");
echo lzw_decompress(compile_file('../adminer/static/default.css;../externals/jush/jush.css', 'minify_css'));
} elseif ($_GET["file"] == "dark.css") {
header("Content-Type: text/css; charset=utf-8");
echo lzw_decompress(compile_file('../adminer/static/dark.css;../externals/jush/jush-dark.css', 'minify_css'));
} elseif ($_GET["file"] == "functions.js") {
header("Content-Type: text/javascript; charset=utf-8");
echo lzw_decompress(compile_file('../adminer/static/functions.js;static/editing.js', 'minify_js'));

View File

@@ -85,8 +85,8 @@ if (support("scheme")) {
}
echo lang('DB') . ": " . html_select("db", $dbs, $row["db"] != "" ? $row["db"] : $_GET["db"], $onchange);
}
echo input_hidden("change-js");
?>
<input type="hidden" name="change-js" value="">
<noscript><p><input type="submit" name="change" value="<?php echo lang('Change'); ?>"></noscript>
<table>
<thead><tr><th id="label-source"><?php echo lang('Source'); ?><th id="label-target"><?php echo lang('Target'); ?></thead>
@@ -116,5 +116,5 @@ foreach ($row["source"] as $key => $val) {
<?php if ($name != "") { ?>
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $name)); ?>
<?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>

View File

@@ -4,8 +4,8 @@ namespace Adminer;
// any method change in this file should be transferred to editor/include/adminer.inc.php and plugins/plugin.php
class Adminer {
/** @var array operators used in select, null for all operators */
public $operators;
public $operators; ///< @var array operators used in select, null for all operators
public $error = ''; ///< @var protected(set) string HTML
/** Name in title and navigation
* @return string HTML code
@@ -94,12 +94,13 @@ class Adminer {
}
/** Print HTML code inside <head>
* @return bool true to link favicon.ico and adminer.css if exists
* @param bool dark CSS: false to disable, true to force, null to base on user preferences
* @return bool true to link favicon.ico
*/
function head() {
?>
<link rel="stylesheet" type="text/css" href="../externals/jush/jush.css">
<?php
function head($dark = null) {
// this is matched by compile.php
echo "<link rel='stylesheet' href='../externals/jush/jush.css'>\n";
echo ($dark !== false ? "<link rel='stylesheet'" . ($dark ? "" : " media='(prefers-color-scheme: dark)'") . " href='../externals/jush/jush-dark.css'>\n" : "");
return true;
}
@@ -108,9 +109,11 @@ class Adminer {
*/
function css() {
$return = array();
$filename = "adminer.css";
if (file_exists($filename)) {
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
foreach (array("", "-dark") as $mode) {
$filename = "adminer$mode.css";
if (file_exists($filename)) {
$return[] = "$filename?v=" . crc32(file_get_contents($filename));
}
}
return $return;
}
@@ -121,8 +124,10 @@ class Adminer {
function loginForm() {
global $drivers;
echo "<table class='layout'>\n";
// this is matched by compile.php
echo $this->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", $drivers, DRIVER, "loginDriver(this);"));
echo $this->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="hostname[:port]" placeholder="localhost" autocapitalize="off">');
// this is matched by compile.php
echo $this->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" autofocus value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("qs('#username').form['auth[driver]'].onchange();"));
echo $this->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">');
echo $this->loginFormField('db', '<tr><th>' . lang('Database') . '<td>', '<input name="auth[db]" value="' . h($_GET["db"]) . '" autocapitalize="off">');
@@ -167,7 +172,9 @@ class Adminer {
* @return string HTML code, "" to ignore field
*/
function fieldName($field, $order = 0) {
return '<span title="' . h($field["full_type"]) . '">' . h($field["field"]) . '</span>';
$type = $field["full_type"];
$comment = $field["comment"];
return '<span title="' . h($type . ($comment != "" ? ($type ? ": " : "") . $comment : '')) . '">' . h($field["field"]) . '</span>';
}
/** Print links after select heading
@@ -256,6 +263,11 @@ class Adminer {
return shorten_utf8(trim($query), 1000);
}
/** Print HTML code just before the Execute button in SQL command
*/
function sqlPrintAfter() {
}
/** Description of a row in a table
* @param string
* @return string SQL expression, empty string for no description
@@ -289,13 +301,14 @@ class Adminer {
* @return string
*/
function selectVal($val, $link, $field, $original) {
$return = ($val === null ? "<i>NULL</i>" : (preg_match("~char|binary|boolean~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "<code>$val</code>" : $val));
$return = ($val === null ? "<i>NULL</i>"
: (preg_match("~char|binary|boolean~", $field["type"]) && !preg_match("~var~", $field["type"]) ? "<code>$val</code>"
: (preg_match('~json~', $field["type"]) ? "<code class='jush-js'>$val</code>"
: $val)
));
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && !is_utf8($val)) {
$return = "<i>" . lang('%d byte(s)', strlen($original)) . "</i>";
}
if (preg_match('~json~', $field["type"])) {
$return = "<code class='jush-js'>$return</code>";
}
return ($link ? "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : "") . ">$return</a>" : $return);
}
@@ -310,9 +323,10 @@ class Adminer {
/** Print table structure in tabular format
* @param array data about individual fields
* @param array
* @return null
*/
function tableStructurePrint($fields) {
function tableStructurePrint($fields, $tableStatus = null) {
global $driver;
echo "<div class='scrollable'>\n";
echo "<table class='nowrap odds'>\n";
@@ -321,8 +335,11 @@ class Adminer {
foreach ($fields as $field) {
echo "<tr><th>" . h($field["field"]);
$type = h($field["full_type"]);
echo "<td><span title='" . h($field["collation"]) . "'>"
. (in_array($type, (array) $structured_types[lang('User types')]) ? "<a href='" . h(ME . 'type=' . urlencode($type)) . "'>$type</a>" : $type)
$collation = h($field["collation"]);
echo "<td><span title='$collation'>"
. (in_array($type, (array) $structured_types[lang('User types')])
? "<a href='" . h(ME . 'type=' . urlencode($type)) . "'>$type</a>"
: $type . ($collation && isset($tableStatus["Collation"]) && $collation != $tableStatus["Collation"] ? " $collation" : ""))
. "</span>"
;
echo ($field["null"] ? " <i>NULL</i>" : "");
@@ -375,7 +392,7 @@ class Adminer {
($key !== "" ? "selectFieldChange" : "selectAddRow")
);
echo "<div>" . ($driver->functions || $driver->grouping ? html_select("columns[$i][fun]", array(-1 => "") + array_filter(array(lang('Functions') => $driver->functions, lang('Aggregation') => $driver->grouping)), $val["fun"])
. on_help("getTarget(event).value && getTarget(event).value.replace(/ |\$/, '(') + ')'", 1)
. on_help("event.target.value && event.target.value.replace(/ |\$/, '(') + ')'", 1)
. script("qsl('select').onchange = function () { helpClose();" . ($key !== "" ? "" : " qsl('select, input', this.parentNode).onchange();") . " };", "")
. "($column)" : $column) . "</div>\n";
$i++;
@@ -472,7 +489,7 @@ class Adminer {
echo "<input type='submit' value='" . lang('Select') . "'>";
echo " <span id='noindex' title='" . lang('Full table scan') . "'></span>";
echo "<script" . nonce() . ">\n";
echo "var indexColumns = ";
echo "const indexColumns = ";
$columns = array();
foreach ($indexes as $index) {
$current_key = reset($index["columns"]);
@@ -761,7 +778,7 @@ class Adminer {
return unconvert_field($field, $return);
}
/** Returns export output options
/** Return export output options
* @return array
*/
function dumpOutput() {
@@ -772,7 +789,7 @@ class Adminer {
return $return;
}
/** Returns export format options
/** Return export format options
* @return array empty to disable export
*/
function dumpFormat() {
@@ -972,31 +989,28 @@ class Adminer {
return true;
}
/** Prints navigation after Adminer title
/** Print navigation after Adminer title
* @param string can be "auth" if there is no database connection, "db" if there is no database selected, "ns" with invalid schema
* @return null
*/
function navigation($missing) {
global $VERSION, $drivers, $connection;
?>
<h1>
<?php echo $this->name(); ?>
<span class="version">
<?php echo $VERSION; ?>
<a href="https://www.adminer.org/#download"<?php echo target_blank(); ?> id="version"><?php echo (version_compare($VERSION, $_COOKIE["adminer_version"]) < 0 ? h($_COOKIE["adminer_version"]) : ""); ?></a>
</span>
</h1>
<?php
echo "<h1>" . $this->name() . " <span class='version'>$VERSION";
$new_version = $_COOKIE["adminer_version"];
echo " <a href='https://www.adminer.org/#download'" . target_blank() . " id='version'>" . (version_compare($VERSION, $new_version) < 0 ? h($new_version) : "") . "</a>";
echo "</span></h1>\n";
// this is matched by compile.php
switch_lang();
if ($missing == "auth") {
$output = "";
foreach ((array) $_SESSION["pwds"] as $vendor => $servers) {
foreach ($servers as $server => $usernames) {
$name = h(get_setting("vendor-$vendor-$server") ?: $drivers[$vendor]);
foreach ($usernames as $username => $password) {
if ($password !== null) {
$dbs = $_SESSION["db"][$vendor][$server][$username];
foreach (($dbs ? array_keys($dbs) : array("")) as $db) {
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($drivers[$vendor]) " . h($username . ($server != "" ? "@" . $this->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
$output .= "<li><a href='" . h(auth_url($vendor, $server, $username, $db)) . "'>($name) " . h($username . ($server != "" ? "@" . $this->serverName($server) : "") . ($db != "" ? " - $db" : "")) . "</a>\n";
}
}
}
@@ -1011,31 +1025,7 @@ class Adminer {
$connection->select_db(DB);
$tables = table_status('', true);
}
echo script_src("../externals/jush/modules/jush.js");
echo script_src("../externals/jush/modules/jush-textarea.js");
echo script_src("../externals/jush/modules/jush-txt.js");
echo script_src("../externals/jush/modules/jush-js.js");
if (support("sql")) {
echo script_src("../externals/jush/modules/jush-" . JUSH . ".js");
?>
<script<?php echo nonce(); ?>>
<?php
if ($tables) {
$links = array();
foreach ($tables as $table => $type) {
$links[] = preg_quote($table, '/');
}
echo "var jushLinks = { " . JUSH . ": [ '" . js_escape(ME) . (support("table") ? "table=" : "select=") . "\$&', /\\b(" . implode("|", $links) . ")\\b/g ] };\n";
foreach (array("bac", "bra", "sqlite_quo", "mssql_bra") as $val) {
echo "jushLinks.$val = jushLinks." . JUSH . ";\n";
}
}
$server_info = $connection->server_info;
?>
bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '\1', $server_info) : ""); ?>'<?php echo (preg_match('~MariaDB~', $server_info) ? ", true" : ""); ?>);
</script>
<?php
}
$this->syntaxHighlighting($tables);
$this->databasesPrint($missing);
$actions = array();
if (DB == "" || !$missing) {
@@ -1060,7 +1050,37 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
}
}
/** Prints databases list in menu
/** Set up syntax highlight for code and <textarea>
* @param array result of table_status()
*/
function syntaxHighlighting($tables) {
global $connection;
// this is matched by compile.php
echo script_src("../externals/jush/modules/jush.js");
echo script_src("../externals/jush/modules/jush-textarea.js");
echo script_src("../externals/jush/modules/jush-txt.js");
echo script_src("../externals/jush/modules/jush-js.js");
if (support("sql")) {
echo script_src("../externals/jush/modules/jush-" . JUSH . ".js");
echo "<script" . nonce() . ">\n";
if ($tables) {
$links = array();
foreach ($tables as $table => $type) {
$links[] = preg_quote($table, '/');
}
echo "var jushLinks = { " . JUSH . ": [ '" . js_escape(ME) . (support("table") ? "table=" : "select=") . "\$&', /\\b(" . implode("|", $links) . ")\\b/g ] };\n";
foreach (array("bac", "bra", "sqlite_quo", "mssql_bra") as $val) {
echo "jushLinks.$val = jushLinks." . JUSH . ";\n";
}
}
echo "</script>\n";
}
echo script("syntaxHighlighting('" . (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '\1', $connection->server_info) : "") . "'"
. ($connection->flavor == 'maria' ? ", 'maria'" : ($connection->flavor == 'cockroach' ? ", 'cockroach'" : "")) . ");"
);
}
/** Print databases list in menu
* @param string
* @return null
*/
@@ -1070,20 +1090,17 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
if (DB && $databases && !in_array(DB, $databases)) {
array_unshift($databases, DB);
}
?>
<form action="">
<p id="dbs">
<?php
echo "<form action=''>\n<p id='dbs'>\n";
hidden_fields_get();
$db_events = script("mixin(qsl('select'), {onmousedown: dbMouseDown, onchange: dbChange});");
echo "<span title='" . lang('Database') . "'>" . lang('DB') . "</span>: " . ($databases
echo "<span title='" . lang('Database') . "'>" . lang('DB') . ":</span> " . ($databases
? html_select("db", array("" => "") + $databases, DB) . $db_events
: "<input name='db' value='" . h(DB) . "' autocapitalize='off' size='19'>\n"
);
echo "<input type='submit' value='" . lang('Use') . "'" . ($databases ? " class='hidden'" : "") . ">\n";
if (support("scheme")) {
if ($missing != "db" && DB != "" && $connection->select_db(DB)) {
echo "<br>" . lang('Schema') . ": " . html_select("ns", array("" => "") + $adminer->schemas(), $_GET["ns"]) . $db_events;
echo "<br><span>" . lang('Schema') . ":</span> " . html_select("ns", array("" => "") + $adminer->schemas(), $_GET["ns"]) . $db_events;
if ($_GET["ns"] != "") {
set_schema($_GET["ns"]);
}
@@ -1091,14 +1108,14 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
}
foreach (array("import", "sql", "schema", "dump", "privileges") as $val) {
if (isset($_GET[$val])) {
echo "<input type='hidden' name='$val' value=''>";
echo input_hidden($val);
break;
}
}
echo "</p></form>\n";
}
/** Prints table list in menu
/** Print table list in menu
* @param array result of table_status('', true)
* @return null
*/

View File

@@ -19,7 +19,17 @@ if ($_COOKIE["adminer_permanent"]) {
function add_invalid_login() {
global $adminer;
$fp = file_open_lock(get_temp_dir() . "/adminer.invalid");
$base = get_temp_dir() . "/adminer.invalid";
// adminer.invalid may not be writable by us, try the files with random suffixes
foreach (glob("$base*") ?: array($base) as $filename) {
$fp = file_open_lock($filename);
if ($fp) {
break;
}
}
if (!$fp) {
$fp = file_open_lock("$base-" . rand_string());
}
if (!$fp) {
return;
}
@@ -42,7 +52,15 @@ function add_invalid_login() {
function check_invalid_login() {
global $adminer;
$invalids = unserialize(@file_get_contents(get_temp_dir() . "/adminer.invalid")); // @ - may not exist
$invalids = array();
foreach (glob(get_temp_dir() . "/adminer.invalid*") as $filename) {
$fp = file_open_lock($filename);
if ($fp) {
$invalids = unserialize(stream_get_contents($fp));
file_unlock($fp);
break;
}
}
$invalid = ($invalids ? $invalids[$adminer->bruteForceKey()] : array());
$next_attempt = ($invalid[1] > 29 ? $invalid[0] - time() : 0); // allow 30 invalid attempts
if ($next_attempt > 0) { //! do the same with permanent login
@@ -61,7 +79,7 @@ if ($auth) {
set_password($vendor, $server, $username, $password);
$_SESSION["db"][$vendor][$server][$username][$db] = true;
if ($auth["permanent"]) {
$key = base64_encode($vendor) . "-" . base64_encode($server) . "-" . base64_encode($username) . "-" . base64_encode($db);
$key = implode("-", array_map('base64_encode', array($vendor, $server, $username, $db)));
$private = $adminer->permanentLogin(true);
$permanent[$key] = "$key:" . base64_encode($private ? encrypt_string($password, $private) : "");
cookie("adminer_permanent", implode(" ", $permanent));
@@ -105,7 +123,7 @@ function unset_permanent() {
cookie("adminer_permanent", implode(" ", $permanent));
}
/** Renders an error message and a login form
/** Render an error message and a login form
* @param string plain text
* @return null exits
*/
@@ -169,6 +187,9 @@ if (isset($_GET["username"]) && is_string(get_password())) {
if ($adminer->operators === null) {
$adminer->operators = $driver->operators;
}
if (Driver::$jush == 'sql' || $connection->flavor == 'cockroach') {
save_settings(array("vendor-" . DRIVER . "-" . SERVER => $drivers[DRIVER]));
}
}
}

View File

@@ -3,6 +3,7 @@ namespace Adminer;
include "../adminer/include/version.inc.php";
include "../adminer/include/errors.inc.php";
// this is matched by compile.php
include "../adminer/include/coverage.inc.php";
// disable filter.default
@@ -21,6 +22,7 @@ if (function_exists("mb_internal_encoding")) {
}
include "../adminer/include/functions.inc.php";
include "../adminer/include/html.inc.php";
// used only in compiled file
if (isset($_GET["file"])) {
@@ -28,7 +30,9 @@ if (isset($_GET["file"])) {
}
if ($_GET["script"] == "version") {
$fp = file_open_lock(get_temp_dir() . "/adminer.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"])));
}
@@ -62,7 +66,6 @@ if (function_exists("get_magic_quotes_runtime") && get_magic_quotes_runtime()) {
set_magic_quotes_runtime(false);
}
@set_time_limit(0); // @ - can be disabled
@ini_set("zend.ze1_compatibility_mode", false); // @ - deprecated
@ini_set("precision", 15); // @ - can be disabled, 15 - internal PHP precision
include "../adminer/include/lang.inc.php";
@@ -73,9 +76,18 @@ include "../adminer/drivers/sqlite.inc.php";
include "../adminer/drivers/pgsql.inc.php";
include "../adminer/drivers/oracle.inc.php";
include "../adminer/drivers/mssql.inc.php";
include "../adminer/drivers/mongo.inc.php";
include "./include/adminer.inc.php";
$adminer = (function_exists('adminer_object') ? adminer_object() : new Adminer);
include "../adminer/include/plugins.inc.php";
if (function_exists('adminer_object')) {
$adminer = adminer_object();
} elseif (is_dir("adminer-plugins") || file_exists("adminer-plugins.php")) {
$adminer = new Plugins(null);
} else {
$adminer = new Adminer;
}
// this is matched by compile.php
include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
define('Adminer\JUSH', Driver::$jush);
@@ -86,6 +98,7 @@ define(
preg_replace('~\?.*~', '', relative_uri()) . '?'
. (sid() ? SID . '&' : '')
. (SERVER !== null ? DRIVER . "=" . urlencode(SERVER) . '&' : '')
. ($_GET["ext"] ? "ext=" . urlencode($_GET["ext"]) . '&' : '')
. (isset($_GET["username"]) ? "username=" . urlencode($_GET["username"]) . '&' : '')
. (DB != "" ? 'db=' . urlencode(DB) . '&' . (isset($_GET["ns"]) ? "ns=" . urlencode($_GET["ns"]) . "&" : "") : '')
);

View File

@@ -8,7 +8,13 @@ if (isset($_GET["import"])) {
$_GET["sql"] = $_GET["import"];
}
if (!(DB != "" ? $connection->select_db(DB) : isset($_GET["sql"]) || isset($_GET["dump"]) || isset($_GET["database"]) || isset($_GET["processlist"]) || isset($_GET["privileges"]) || isset($_GET["user"]) || isset($_GET["variables"]) || $_GET["script"] == "connect" || $_GET["script"] == "kill")) {
if (
!(DB != ""
? $connection->select_db(DB)
: isset($_GET["sql"]) || isset($_GET["dump"]) || isset($_GET["database"]) || isset($_GET["processlist"]) || isset($_GET["privileges"]) || isset($_GET["user"]) || isset($_GET["variables"])
|| $_GET["script"] == "connect" || $_GET["script"] == "kill"
)
) {
if (DB != "" || $_GET["refresh"]) {
restart_session();
set_session("dbs", null);
@@ -38,6 +44,14 @@ if (!(DB != "" ? $connection->select_db(DB) : isset($_GET["sql"]) || isset($_GET
}
echo "<p>" . lang('%s version: %s through PHP extension %s', $drivers[DRIVER], "<b>" . h($connection->server_info) . "</b>", "<b>$connection->extension</b>") . "\n";
echo "<p>" . lang('Logged as: %s', "<b>" . h(logged_user()) . "</b>") . "\n";
if (isset($adminer->plugins) && is_array($adminer->plugins)) {
echo "<p>" . lang('Loaded plugins') . ":\n<ul>\n";
foreach ($adminer->plugins as $plugin) {
$reflection = new \ReflectionObject($plugin);
echo "<li><b>" . get_class($plugin) . "</b>" . h(preg_match('~^/[\s*]+(.+)~', $reflection->getDocComment(), $match) ? ": $match[1]" : "") . "\n";
}
echo "</ul>\n";
}
$databases = $adminer->databases();
if ($databases) {
$scheme = support("scheme");
@@ -72,13 +86,13 @@ if (!(DB != "" ? $connection->select_db(DB) : isset($_GET["sql"]) || isset($_GET
echo (support("database")
? "<div class='footer'><div>\n"
. "<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>\n"
. "<input type='hidden' name='all' value=''>" . script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^db/)); };") // used by trCheck()
. input_hidden("all") . script("qsl('input').onclick = function () { selectCount('selected', formChecked(this, /^db/)); };") // used by trCheck()
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm() . "\n"
. "</div></fieldset>\n"
. "</div></div>\n"
: ""
);
echo "<input type='hidden' name='token' value='$token'>\n";
echo input_token();
echo "</form>\n";
echo script("tableCheck();");
}

View File

@@ -2,9 +2,9 @@
namespace Adminer;
// coverage is used in tests and removed in compilation
if (extension_loaded("xdebug") && file_exists(sys_get_temp_dir() . "/adminer_coverage.ser")) {
if (extension_loaded("xdebug") && file_exists(sys_get_temp_dir() . "/adminer.coverage")) {
function save_coverage() {
$coverage_filename = sys_get_temp_dir() . "/adminer_coverage.ser";
$coverage_filename = sys_get_temp_dir() . "/adminer.coverage";
$coverage = unserialize(file_get_contents($coverage_filename));
foreach (xdebug_get_code_coverage() as $filename => $lines) {
foreach ($lines as $l => $val) {

View File

@@ -1,10 +1,6 @@
<?php
namespace Adminer;
if (!ob_get_level()) {
ob_start(null, 4096);
}
/** Print HTML header
* @param string used in title, breadcrumb and heading, should be HTML escaped
* @param string
@@ -19,28 +15,52 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
page_messages($error);
exit;
}
if (!ob_get_level()) {
ob_start(null, 4096);
}
$title_all = $title . ($title2 != "" ? ": $title2" : "");
$title_page = strip_tags($title_all . (SERVER != "" && SERVER != "localhost" ? h(" - " . SERVER) : "") . " - " . $adminer->name());
// initial-scale=1 is the default but Chrome 134 on iOS is not able to zoom out without it
?>
<!DOCTYPE html>
<html lang="<?php echo $LANG; ?>" dir="<?php echo lang('ltr'); ?>">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?php echo $title_page; ?></title>
<link rel="stylesheet" type="text/css" href="../adminer/static/default.css">
<?php echo script_src("../adminer/static/functions.js"); ?>
<?php echo script_src("static/editing.js"); ?>
<?php if ($adminer->head()) { ?>
<link rel="shortcut icon" type="image/x-icon" href="../adminer/static/favicon.ico">
<link rel="apple-touch-icon" href="../adminer/static/favicon.ico">
<?php foreach ($adminer->css() as $css) { ?>
<link rel="stylesheet" type="text/css" href="<?php echo h($css); ?>">
<?php } ?>
<?php } ?>
<body class="<?php echo lang('ltr'); ?> nojs">
<link rel="stylesheet" href="../adminer/static/default.css">
<?php
$css = $adminer->css();
$has_light = false;
$has_dark = false;
foreach ($css as $filename) {
if (strpos($filename, "adminer.css") !== false) {
$has_light = true;
}
if (strpos($filename, "adminer-dark.css") !== false) {
$has_dark = true;
}
}
$dark = ($has_light
? ($has_dark ? null : false) // both styles - autoswitching, only adminer.css - light
: ($has_dark ?: null) // only adminer-dark.css - dark, neither - autoswitching
);
$media = " media='(prefers-color-scheme: dark)'";
if ($dark !== false) {
echo "<link rel='stylesheet'" . ($dark ? "" : $media) . " href='../adminer/static/dark.css'>\n";
}
echo "<meta name='color-scheme' content='" . ($dark === null ? "light dark" : ($dark ? "dark" : "light")) . "'>\n";
// this is matched by compile.php
echo script_src("../adminer/static/functions.js");
echo script_src("static/editing.js");
if ($adminer->head($dark)) {
echo "<link rel='shortcut icon' type='image/x-icon' href='../adminer/static/favicon.ico'>\n";
echo "<link rel='apple-touch-icon' href='../adminer/static/favicon.ico'>\n";
}
foreach ($css as $val) {
echo "<link rel='stylesheet'" . (preg_match('~-dark~', $val) && !$dark ? $media : "") . " href='" . h($val) . "'>\n";
}
echo "\n<body class='" . lang('ltr') . " nojs'>\n";
$filename = get_temp_dir() . "/adminer.version";
if (!$_COOKIE["adminer_version"] && function_exists('openssl_verify') && file_exists($filename) && filemtime($filename) + 86400 > time()) { // 86400 - 1 day in seconds
$version = unserialize(file_get_contents($filename));
@@ -58,21 +78,16 @@ fQIDAQAB
$_COOKIE["adminer_version"] = $version["version"]; // doesn't need to send to the browser
}
}
?>
<script<?php echo nonce(); ?>>
mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick<?php
echo (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '$VERSION', '" . js_escape(ME) . "', '" . get_token() . "')"); // $token may be empty in auth.inc.php
?>});
document.body.className = document.body.className.replace(/ nojs/, ' js');
var offlineMessage = '<?php echo js_escape(lang('You are offline.')); ?>';
var thousandsSeparator = '<?php echo js_escape(lang(',')); ?>';
</script>
<div id="help" class="jush-<?php echo JUSH; ?> jsonly hidden"></div>
<?php echo script("mixin(qs('#help'), {onmouseover: function () { helpOpen = 1; }, onmouseout: helpMouseout});"); ?>
<div id="content">
<?php
echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick"
. (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '$VERSION', '" . js_escape(ME) . "', '" . get_token() . "')") // $token may be empty in auth.inc.php
. "});
document.body.classList.replace('nojs', 'js');
const offlineMessage = '" . js_escape(lang('You are offline.')) . "';
const thousandsSeparator = '" . js_escape(lang(',')) . "';")
;
echo "<div id='help' class='jush-" . JUSH . " jsonly hidden'></div>\n";
echo script("mixin(qs('#help'), {onmouseover: () => { helpOpen = 1; }, onmouseout: helpMouseout});");
echo "<div id='content'>\n";
if ($breadcrumb !== null) {
$link = substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1);
echo '<p id="breadcrumb"><a href="' . h($link ?: ".") . '">' . $drivers[DRIVER] . '</a> » ';
@@ -165,6 +180,7 @@ function get_nonce() {
* @return null
*/
function page_messages($error) {
global $adminer;
$uri = preg_replace('~^[^?]*~', '', $_SERVER["REQUEST_URI"]);
$messages = $_SESSION["messages"][$uri];
if ($messages) {
@@ -174,6 +190,9 @@ function page_messages($error) {
if ($error) {
echo "<div class='error'>$error</div>\n";
}
if ($adminer->error) { // separate <div>
echo "<div class='error'>$adminer->error</div>\n";
}
}
/** Print HTML footer
@@ -181,23 +200,20 @@ function page_messages($error) {
* @return null
*/
function page_footer($missing = "") {
global $adminer, $token;
?>
</div>
<div id="menu">
<?php $adminer->navigation($missing); ?>
</div>
<?php if ($missing != "auth") { ?>
global $adminer;
echo "</div>\n\n<div id='menu'>\n";
$adminer->navigation($missing);
echo "</div>\n\n";
if ($missing != "auth") {
?>
<form action="" method="post">
<p class="logout">
<span><?php echo h($_GET["username"]) . "\n"; ?></span>
<input type="submit" name="logout" value="<?php echo lang('Logout'); ?>" id="logout">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</p>
</form>
<?php } ?>
<?php
}
echo script("setupSubmitHighlight(document);");
}

View File

@@ -66,6 +66,13 @@ abstract class SqlDriver {
function enumLength($field) {
}
/** Function used to convert the value inputted by user
* @param array
* @return string or null
*/
function unconvertFunction($field) {
}
/** Select data from table
* @param string
* @param array result of $adminer->selectColumnsProcess()[0]
@@ -135,7 +142,15 @@ abstract class SqlDriver {
return queries("INSERT INTO " . table($table) . ($set
? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")"
: " DEFAULT VALUES"
));
) . $this->insertReturning($table));
}
/** Get RETURNING clause for INSERT queries, PostgreSQL specific
* @param string
* @return string
*/
function insertReturning($table) {
return "";
}
/** Insert or update data in table
@@ -237,6 +252,13 @@ abstract class SqlDriver {
return false;
}
/** Get supported engines
* @return array
*/
function engines() {
return array();
}
/** Check whether table supports indexes
* @param array result of table_status()
* @return bool

View File

@@ -25,12 +25,14 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
for ($j=0; $j < count($row); $j++) {
$field = $result->fetch_field();
$name = $field->name;
$orgtable = $field->orgtable;
$orgname = $field->orgname;
$return[$field->table] = $orgtable;
$orgtable = (isset($field->orgtable) ? $field->orgtable : "");
$orgname = (isset($field->orgname) ? $field->orgname : $name);
if ($orgtables && JUSH == "sql") { // MySQL EXPLAIN
$links[$j] = ($name == "table" ? "table=" : ($name == "possible_keys" ? "indexes=" : null));
} elseif ($orgtable != "") {
if (isset($field->table)) {
$return[$field->table] = $orgtable;
}
if (!isset($indexes[$orgtable])) {
// find primary key in each table
$indexes[$orgtable] = array();
@@ -90,7 +92,8 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
if ($link) {
$val = "<a href='" . h($link) . "'" . (is_url($link) ? target_blank() : '') . ">$val</a>";
}
echo "<td>$val";
// 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 ($i ? "</table>\n</div>" : "<p class='message'>" . lang('No rows.')) . "\n";
@@ -119,31 +122,6 @@ function referencable_primary($self) {
return $return;
}
/** Get settings stored in a cookie
* @return array
*/
function adminer_settings() {
parse_str($_COOKIE["adminer_settings"], $settings);
return $settings;
}
/** Get setting stored in a cookie
* @param string
* @return array
*/
function adminer_setting($key) {
$settings = adminer_settings();
return $settings[$key];
}
/** Store settings to a cookie
* @param array
* @return bool
*/
function set_adminer_settings($settings) {
return cookie("adminer_settings", http_build_query($settings + adminer_settings()));
}
/** Print SQL <textarea> tag
* @param string
* @param string or array in which case [0] of every element is used
@@ -209,7 +187,7 @@ function json_row($key, $val = null) {
function edit_type($key, $field, $collations, $foreign_keys = array(), $extra_types = array()) {
global $driver;
$type = $field["type"];
?><td><select name="<?php echo h($key); ?>[type]" class="type" aria-labelledby="label-type"><?php
echo "<td><select name='" . h($key) . "[type]' class='type' aria-labelledby='label-type'>";
if ($type && !array_key_exists($type, $driver->types()) && !isset($foreign_keys[$type]) && !in_array($type, $extra_types)) {
$extra_types[] = $type;
}
@@ -218,19 +196,24 @@ function edit_type($key, $field, $collations, $foreign_keys = array(), $extra_ty
$structured_types[lang('Foreign keys')] = $foreign_keys;
}
echo optionlist(array_merge($extra_types, $structured_types), $type);
?></select><td><input
name="<?php echo h($key); ?>[length]"
value="<?php echo h($field["length"]); ?>"
size="3"
<?php echo (!$field["length"] && preg_match('~var(char|binary)$~', $type) ? " class='required'" : ""); //! type="number" with enabled JavaScript ?>
aria-labelledby="label-length"><td class="options"><?php
echo ($collations ? "<input list='collations' name='" . h($key) . "[collation]'" . (preg_match('~(char|text|enum|set)$~', $type) ? "" : " class='hidden'") . " value='" . h($field["collation"]) . "' placeholder='(" . lang('collation') . ")'>" : '');
echo "</select><td>";
echo "<input name='" . h($key) . "[length]' value='" . h($field["length"]) . "' size='3'"
. (!$field["length"] && preg_match('~var(char|binary)$~', $type) ? " class='required'" : "") //! type="number" with enabled JavaScript
. " aria-labelledby='label-length'>";
echo "<td class='options'>";
echo ($collations
? "<input list='collations' name='" . h($key) . "[collation]'" . (preg_match('~(char|text|enum|set)$~', $type) ? "" : " class='hidden'") . " value='" . h($field["collation"]) . "' placeholder='(" . lang('collation') . ")'>"
: ''
);
echo ($driver->unsigned ? "<select name='" . h($key) . "[unsigned]'" . (!$type || preg_match(number_type(), $type) ? "" : " class='hidden'") . '><option>' . optionlist($driver->unsigned, $field["unsigned"]) . '</select>' : '');
echo (isset($field['on_update']) ? "<select name='" . h($key) . "[on_update]'" . (preg_match('~timestamp|datetime~', $type) ? "" : " class='hidden'") . '>'
. optionlist(array("" => "(" . lang('ON UPDATE') . ")", "CURRENT_TIMESTAMP"), (preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) ? "CURRENT_TIMESTAMP" : $field["on_update"]))
. '</select>' : ''
);
echo ($foreign_keys ? "<select name='" . h($key) . "[on_delete]'" . (preg_match("~`~", $type) ? "" : " class='hidden'") . "><option value=''>(" . lang('ON DELETE') . ")" . optionlist(explode("|", $driver->onActions), $field["on_delete"]) . "</select> " : " "); // space for IE
echo ($foreign_keys
? "<select name='" . h($key) . "[on_delete]'" . (preg_match("~`~", $type) ? "" : " class='hidden'") . "><option value=''>(" . lang('ON DELETE') . ")" . optionlist(explode("|", $driver->onActions), $field["on_delete"]) . "</select> "
: " " // space for IE
);
}
/** Get partition info
@@ -307,8 +290,8 @@ function default_value($field) {
$generated = $field["generated"];
return ($default === null ? "" : (in_array($generated, $driver->generated)
? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") . "" : " GENERATED ALWAYS AS ($default) $generated")
: " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default))
? (JUSH == "sql" && preg_match('~text~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column
: " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default))
? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column
: str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", (JUSH == "sqlite" ? "($default)" : $default))
)
));
@@ -343,54 +326,49 @@ function type_class($type) {
function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = array()) {
global $driver;
$fields = array_values($fields);
$default_class = (($_POST ? $_POST["defaults"] : adminer_setting("defaults")) ? "" : " class='hidden'");
$comment_class = (($_POST ? $_POST["comments"] : adminer_setting("comments")) ? "" : " class='hidden'");
?>
<thead><tr>
<?php echo ($type == "PROCEDURE" ? "<td>" : ""); ?>
<th id="label-name"><?php echo ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); ?>
<td id="label-type"><?php echo lang('Type'); ?><textarea id="enum-edit" rows="4" cols="12" wrap="off" style="display: none;"></textarea><?php echo script("qs('#enum-edit').onblur = editingLengthBlur;"); ?>
<td id="label-length"><?php echo lang('Length'); ?>
<td><?php echo lang('Options'); /* no label required, options have their own label */ ?>
<?php if ($type == "TABLE") { ?>
<td id="label-null">NULL
<td><input type="radio" name="auto_increment_col" value=""><abbr id="label-ai" title="<?php echo lang('Auto Increment'); ?>">AI</abbr><?php echo doc_link(array(
'sql' => "example-auto-increment.html",
'mariadb' => "auto_increment/",
'sqlite' => "autoinc.html",
'pgsql' => "datatype-numeric.html#DATATYPE-SERIAL",
'mssql' => "t-sql/statements/create-table-transact-sql-identity-property",
)); ?>
<td id="label-default"<?php echo $default_class; ?>><?php echo lang('Default value'); ?>
<?php echo (support("comment") ? "<td id='label-comment'$comment_class>" . lang('Comment') : ""); ?>
<?php } ?>
<td><?php echo "<input type='image' class='icon' name='add[" . (support("move_col") ? 0 : count($fields)) . "]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>" . script("row_count = " . count($fields) . ";"); ?>
</thead>
<tbody>
<?php
$default_class = (($_POST ? $_POST["defaults"] : get_setting("defaults")) ? "" : " class='hidden'");
$comment_class = (($_POST ? $_POST["comments"] : get_setting("comments")) ? "" : " class='hidden'");
echo "<thead><tr>\n";
echo ($type == "PROCEDURE" ? "<td>" : "");
echo "<th id='label-name'>" . ($type == "TABLE" ? lang('Column name') : lang('Parameter name'));
echo "<td id='label-type'>" . lang('Type') . "<textarea id='enum-edit' rows='4' cols='12' wrap='off' style='display: none;'></textarea>" . script("qs('#enum-edit').onblur = editingLengthBlur;");
echo "<td id='label-length'>" . lang('Length');
echo "<td>" . lang('Options'); // no label required, options have their own label
if ($type == "TABLE") {
echo "<td id='label-null'>NULL\n";
echo "<td><input type='radio' name='auto_increment_col' value=''><abbr id='label-ai' title='" . lang('Auto Increment') . "'>AI</abbr>";
echo doc_link(array(
'sql' => "example-auto-increment.html",
'mariadb' => "auto_increment/",
'sqlite' => "autoinc.html",
'pgsql' => "datatype-numeric.html#DATATYPE-SERIAL",
'mssql' => "t-sql/statements/create-table-transact-sql-identity-property",
));
echo "<td id='label-default'$default_class>" . lang('Default value');
echo (support("comment") ? "<td id='label-comment'$comment_class>" . lang('Comment') : "");
}
echo "<td><input type='image' class='icon' name='add[" . (support("move_col") ? 0 : count($fields)) . "]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>" . script("row_count = " . count($fields) . ";");
echo "</thead>\n<tbody>\n";
echo script("mixin(qsl('tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput});");
foreach ($fields as $i => $field) {
$i++;
$orig = $field[($_POST ? "orig" : "field")];
$display = (isset($_POST["add"][$i-1]) || (isset($field["field"]) && !$_POST["drop_col"][$i])) && (support("drop_col") || $orig == "");
?>
<tr<?php echo ($display ? "" : " style='display: none;'"); ?>>
<?php echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", $driver->inout), $field["inout"]) : "") . "<th>"; ?>
<?php if ($display) { ?>
<input name="fields[<?php echo $i; ?>][field]" value="<?php echo h($field["field"]); ?>" data-maxlength="64" autocapitalize="off" aria-labelledby="label-name">
<?php } ?>
<input type="hidden" name="fields[<?php echo $i; ?>][orig]" value="<?php echo h($orig); ?>"><?php edit_type("fields[$i]", $field, $collations, $foreign_keys); ?>
<?php
echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n";
echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", $driver->inout), $field["inout"]) : "") . "<th>";
if ($display) {
echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'>";
}
echo input_hidden("fields[$i][orig]", $orig);
edit_type("fields[$i]", $field, $collations, $foreign_keys);
if ($type == "TABLE") {
?>
<td><?php echo checkbox("fields[$i][null]", 1, $field["null"], "", "", "block", "label-null"); ?>
<td><label class="block"><input type="radio" name="auto_increment_col" value="<?php echo $i; ?>"<?php echo ($field["auto_increment"] ? " checked" : ""); ?> aria-labelledby="label-ai"></label><td<?php echo $default_class; ?>><?php
echo ($driver->generated
echo "<td>" . checkbox("fields[$i][null]", 1, $field["null"], "", "", "block", "label-null");
echo "<td><label class='block'><input type='radio' name='auto_increment_col' value='$i'" . ($field["auto_increment"] ? " checked" : "") . " aria-labelledby='label-ai'></label>";
echo "<td$default_class>" . ($driver->generated
? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), $driver->generated), $field["generated"]) . " "
: checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default")
);
?>
<input name="fields[<?php echo $i; ?>][default]" value="<?php echo h($field["default"]); ?>" aria-labelledby="label-default"><?php
echo "<input name='fields[$i][default]' value='" . h($field["default"]) . "' aria-labelledby='label-default'>";
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>";
@@ -620,11 +598,11 @@ function doc_link($paths, $text = "<sup>?</sup>") {
$urls = array(
'sql' => "https://dev.mysql.com/doc/refman/$version/en/",
'sqlite' => "https://www.sqlite.org/",
'pgsql' => "https://www.postgresql.org/docs/$version/",
'pgsql' => "https://www.postgresql.org/docs/" . ($connection->flavor == 'cockroach' ? "current" : $version) . "/",
'mssql' => "https://learn.microsoft.com/en-us/sql/",
'oracle' => "https://www.oracle.com/pls/topic/lookup?ctx=db" . preg_replace('~^.* (\d+)\.(\d+)\.\d+\.\d+\.\d+.*~s', '\1\2', $server_info) . "&id=",
);
if (preg_match('~MariaDB~', $server_info)) {
if ($connection->flavor == 'maria') {
$urls['sql'] = "https://mariadb.com/kb/en/";
$paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql']));
}

View File

@@ -1,7 +1,10 @@
<?php
namespace Adminer;
error_reporting(6135); // errors and warnings
error_reporting(24575); // all but E_DEPRECATED (overriding mysqli methods without types is deprecated)
set_error_handler(function ($errno, $errstr) {
return !!preg_match('~^(Trying to access array offset on( value of type)? null|Undefined (array key|property))~', $errstr);
}, E_WARNING);
// "offset on null" mutes $_GET["fields"][0] if there's no ?fields[]= (62017e3 is a wrong fix for this)
// "Undefined array key" mutes $_GET["q"] if there's no ?q=
// "Undefined offset" and "Undefined index" are older messages for the same thing
return !!preg_match('~^(Trying to access array offset on( value of type)? null|Undefined (array key|offset|index))~', $errstr);
}, E_WARNING | E_NOTICE); // warning since PHP 8.0

View File

@@ -20,6 +20,14 @@ function adminer() {
return $adminer;
}
/** Get Driver object
* @return Driver
*/
function driver() {
global $driver;
return $driver;
}
/** Get Adminer version
* @return string
*/
@@ -40,6 +48,15 @@ function idf_unescape($idf) {
return str_replace($last . $last, $last, substr($idf, 1, -1));
}
/** Shortcut for $connection->quote($string)
* @param string
* @return string
*/
function q($string) {
global $connection;
return $connection->quote($string);
}
/** Escape string to use inside ''
* @param string
* @return string
@@ -122,171 +139,6 @@ function charset($connection) {
return (min_version("5.5.3", 0, $connection) ? "utf8mb4" : "utf8"); // SHOW CHARSET would require an extra query
}
/** Return <script> element
* @param string
* @param string
* @return string
*/
function script($source, $trailing = "\n") {
return "<script" . nonce() . ">$source</script>$trailing";
}
/** Return <script src> element
* @param string
* @return string
*/
function script_src($url) {
return "<script src='" . h($url) . "'" . nonce() . "></script>\n";
}
/** Get a nonce="" attribute with CSP nonce
* @return string
*/
function nonce() {
return ' nonce="' . get_nonce() . '"';
}
/** Get a target="_blank" attribute
* @return string
*/
function target_blank() {
return ' target="_blank" rel="noreferrer noopener"';
}
/** Escape for HTML
* @param string
* @return string
*/
function h($string) {
return str_replace("\0", "&#0;", htmlspecialchars($string, ENT_QUOTES, 'utf-8'));
}
/** Convert \n to <br>
* @param string
* @return string
*/
function nl_br($string) {
return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3
}
/** Generate HTML checkbox
* @param string
* @param string
* @param bool
* @param string
* @param string
* @param string
* @param string
* @return string
*/
function checkbox($name, $value, $checked, $label = "", $onclick = "", $class = "", $labelled_by = "") {
$return = "<input type='checkbox' name='$name' value='" . h($value) . "'"
. ($checked ? " checked" : "")
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
. ">"
. ($onclick ? script("qsl('input').onclick = function () { $onclick };", "") : "")
;
return ($label != "" || $class ? "<label" . ($class ? " class='$class'" : "") . ">$return" . h($label) . "</label>" : $return);
}
/** Generate list of HTML options
* @param array array of strings or arrays (creates optgroup)
* @param mixed
* @param bool always use array keys for value="", otherwise only string keys are used
* @return string
*/
function optionlist($options, $selected = null, $use_keys = false) {
$return = "";
foreach ($options as $k => $v) {
$opts = array($k => $v);
if (is_array($v)) {
$return .= '<optgroup label="' . h($k) . '">';
$opts = $v;
}
foreach ($opts as $key => $val) {
$return .= '<option'
. ($use_keys || is_string($key) ? ' value="' . h($key) . '"' : '')
. ($selected !== null && ($use_keys || is_string($key) ? (string) $key : $val) === $selected ? ' selected' : '')
. '>' . h($val)
;
}
if (is_array($v)) {
$return .= '</optgroup>';
}
}
return $return;
}
/** Generate HTML <select>
* @param string
* @param array
* @param string
* @param string
* @param string
* @return string
*/
function html_select($name, $options, $value = "", $onchange = "", $labelled_by = "") {
return "<select name='" . h($name) . "'"
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
. ">" . optionlist($options, $value) . "</select>"
. ($onchange ? script("qsl('select').onchange = function () { $onchange };", "") : "")
;
}
/** Generate HTML radio list
* @param string
* @param array
* @param string
* @return string
*/
function html_radios($name, $options, $value = "") {
$return = "";
foreach ($options as $key => $val) {
$return .= "<label><input type='radio' name='" . h($name) . "' value='" . h($key) . "'" . ($key == $value ? " checked" : "") . ">" . h($val) . "</label>";
}
return $return;
}
/** Get onclick confirmation
* @param string
* @param string
* @return string
*/
function confirm($message = "", $selector = "qsl('input')") {
return script("$selector.onclick = function () { return confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "'); };", "");
}
/** Print header for hidden fieldset (close by </div></fieldset>)
* @param string
* @param string
* @param bool
* @return null
*/
function print_fieldset($id, $legend, $visible = false) {
echo "<fieldset><legend>";
echo "<a href='#fieldset-$id'>$legend</a>";
echo script("qsl('a').onclick = partial(toggle, 'fieldset-$id');", "");
echo "</legend>";
echo "<div id='fieldset-$id'" . ($visible ? "" : " class='hidden'") . ">\n";
}
/** Return class='active' if $bold is true
* @param bool
* @param string
* @return string
*/
function bold($bold, $class = "") {
return ($bold ? " class='active $class'" : ($class ? " class='$class'" : ""));
}
/** Escape string for JavaScript apostrophes
* @param string
* @return string
*/
function js_escape($string) {
return addcslashes($string, "\r\n'\\/"); // slash for <script>
}
/** Get INI boolean value
* @param string
* @return bool
@@ -335,15 +187,6 @@ function get_password() {
return $return;
}
/** Shortcut for $connection->quote($string)
* @param string
* @return string
*/
function q($string) {
global $connection;
return $connection->quote($string);
}
/** Get single value from database
* @param string
* @param int
@@ -459,14 +302,14 @@ function where($where, $fields = array()) {
foreach ((array) $where["where"] as $key => $val) {
$key = bracket_escape($key, 1); // 1 - back
$column = escape_key($key);
$field_type = $fields[$key]["type"];
$return[] = $column
. (JUSH == "sql" && $fields[$key]["type"] == "json" ? " = CAST(" . q($val) . " AS JSON)"
. (JUSH == "sql" && $field_type == "json" ? " = CAST(" . q($val) . " AS JSON)"
: (JUSH == "sql" && is_numeric($val) && preg_match('~\.~', $val) ? " LIKE " . q($val) // LIKE because of floats but slow with ints
: (JUSH == "mssql" ? " LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val)) // LIKE because of text
: " = " . unconvert_field($fields[$key], q($val))
)))
: (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($fields[$key], q($val)))))
; //! enum and set
if (JUSH == "sql" && preg_match('~char|text~', $fields[$key]["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";
}
}
@@ -536,11 +379,39 @@ function cookie($name, $value, $lifetime = 2592000) {
);
}
/** Get settings stored in a cookie
* @param string
* @return array
*/
function get_settings($cookie) {
parse_str($_COOKIE[$cookie], $settings);
return $settings;
}
/** Get setting stored in a cookie
* @param string
* @param string
* @return mixed
*/
function get_setting($key, $cookie = "adminer_settings") {
$settings = get_settings($cookie);
return $settings[$key];
}
/** Store settings to a cookie
* @param array
* @param string
* @return bool
*/
function save_settings($settings, $cookie = "adminer_settings") {
return cookie($cookie, http_build_query($settings + get_settings($cookie)));
}
/** Restart stopped session
* @return null
*/
function restart_session() {
if (!ini_bool("session.use_cookies")) {
if (!ini_bool("session.use_cookies") && (!function_exists('session_status') || session_status() == 1)) { // 1 - PHP_SESSION_NONE, session_status() available since PHP 5.4
session_start();
}
}
@@ -585,10 +456,17 @@ function set_session($key, $val) {
*/
function auth_url($vendor, $server, $username, $db = null) {
global $drivers;
preg_match('~([^?]*)\??(.*)~', remove_from_uri(implode("|", array_keys($drivers)) . "|username|" . ($db !== null ? "db|" : "") . session_name()), $match);
$uri = remove_from_uri(implode("|", array_keys($drivers))
. "|username|ext|"
. ($db !== null ? "db|" : "")
. ($vendor == 'mssql' || $vendor == 'pgsql' ? "" : "ns|") // we don't have access to support() here
. session_name())
;
preg_match('~([^?]*)\??(.*)~', $uri, $match);
return "$match[1]?"
. (sid() ? SID . "&" : "")
. ($vendor != "server" || $server != "" ? urlencode($vendor) . "=" . urlencode($server) . "&" : "")
. ($_GET["ext"] ? "ext=" . urlencode($_GET["ext"]) . "&" : "")
. "username=" . urlencode($username)
. ($db != "" ? "&db=" . urlencode($db) : "")
. ($match[2] ? "&$match[2]" : "")
@@ -720,18 +598,6 @@ function remove_from_uri($param = "") {
return substr(preg_replace("~(?<=[?&])($param" . (SID ? "" : "|" . session_name()) . ")=[^&]*&~", '', relative_uri() . "&"), 0, -1);
}
/** Generate page number for pagination
* @param int
* @param int
* @return string
*/
function pagination($page, $current) {
return " " . ($page == $current
? $page + 1
: '<a href="' . h(remove_from_uri("page") . ($page ? "&page=$page" . ($_GET["next"] ? "&next=" . urlencode($_GET["next"]) : "") : "")) . '">' . ($page + 1) . "</a>"
);
}
/** Get file contents from $_FILES
* @param string
* @param bool
@@ -832,36 +698,6 @@ function friendly_url($val) {
return preg_replace('~\W~i', '-', $val);
}
/** Print hidden fields
* @param array
* @param array
* @param string
* @return bool
*/
function hidden_fields($process, $ignore = array(), $prefix = '') {
$return = false;
foreach ($process as $key => $val) {
if (!in_array($key, $ignore)) {
if (is_array($val)) {
hidden_fields($val, array(), $key);
} else {
$return = true;
echo '<input type="hidden" name="' . h($prefix ? $prefix . "[$key]" : $key) . '" value="' . h($val) . '">';
}
}
}
return $return;
}
/** Print hidden fields for GET forms
* @return null
*/
function hidden_fields_get() {
echo (sid() ? '<input type="hidden" name="' . session_name() . '" value="' . h(session_id()) . '">' : '');
echo (SERVER !== null ? '<input type="hidden" name="' . DRIVER . '" value="' . h(SERVER) . '">' : "");
echo '<input type="hidden" name="username" value="' . h($_GET["username"]) . '">';
}
/** Get status of a single table and fall back to name on error
* @param string
* @param bool
@@ -887,172 +723,6 @@ function column_foreign_keys($table) {
return $return;
}
/** Print enum input field
* @param string "radio"|"checkbox"
* @param string
* @param array
* @param mixed string|array
* @param string
* @return null
*/
function enum_input($type, $attrs, $field, $value, $empty = null) {
global $adminer;
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === $empty) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = (is_array($value) ? in_array($val, $value) : $value === $val);
$return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h($adminer->editVal($val, $field)) . '</label>';
}
return $return;
}
/** Print edit input field
* @param array one field from fields()
* @param mixed
* @param string
* @return null
*/
function input($field, $value, $function) {
global $driver, $adminer;
$name = h(bracket_escape($field["field"]));
echo "<td class='function'>";
if (is_array($value) && !$function) {
$value = json_encode($value, 128); // 128 - JSON_PRETTY_PRINT available since PHP 5.4
$function = "json";
}
$reset = (JUSH == "mssql" && $field["auto_increment"]);
if ($reset && !$_POST["save"]) {
$function = null;
}
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field);
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
$attrs = " name='fields[$name]'$disabled";
$enums = $driver->enumLength($field);
if ($enums) {
$field["type"] = "enum";
$field["length"] = $enums;
}
if ($field["type"] == "enum") {
echo h($functions[""]) . "<td>" . $adminer->editInput($_GET["edit"], $field, $attrs, $value);
} else {
$has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1
? "<select name='function[$name]'$disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
. on_help("getTarget(event).value.replace(/^SQL\$/, '')", 1)
. script("qsl('select').onchange = functionChange;", "")
: h(reset($functions))
) . '<td>';
$input = $adminer->editInput($_GET["edit"], $field, $attrs, $value); // usage in call is without a table
if ($input != "") {
echo $input;
} elseif (preg_match('~bool~', $field["type"])) {
echo "<input type='hidden'$attrs value='0'>"
. "<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>";
} elseif ($field["type"] == "set") {
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = in_array($val, explode(",", $value), true);
echo " <label><input type='checkbox' name='fields[$name][$i]' value='" . h($val) . "'" . ($checked ? ' checked' : '') . ">" . h($adminer->editVal($val, $field)) . '</label>';
}
} elseif (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
echo "<input type='file' name='fields-$name'>";
} elseif (($text = preg_match('~text|lob|memo~i', $field["type"])) || preg_match("~\n~", $value)) {
if ($text && JUSH != "sqlite") {
$attrs .= " cols='50' rows='12'";
} else {
$rows = min(12, substr_count($value, "\n") + 1);
$attrs .= " cols='30' rows='$rows'" . ($rows == 1 ? " style='height: 1.2em;'" : ""); // 1.2em - line-height
}
echo "<textarea$attrs>" . h($value) . '</textarea>';
} elseif ($function == "json" || preg_match('~^jsonb?$~', $field["type"])) {
echo "<textarea$attrs cols='50' rows='12' class='jush-js'>" . h($value) . '</textarea>';
} else {
// int(3) is only a display hint
$types = $driver->types();
$maxlength = (!preg_match('~int~', $field["type"]) && preg_match('~^(\d+)(,(\d+))?$~', $field["length"], $match)
? ((preg_match("~binary~", $field["type"]) ? 2 : 1) * $match[1] + ($match[3] ? 1 : 0) + ($match[2] && !$field["unsigned"] ? 1 : 0))
: ($types[$field["type"]] ? $types[$field["type"]] + ($field["unsigned"] ? 0 : 1) : 0)
);
if (JUSH == 'sql' && min_version(5.6) && preg_match('~time~', $field["type"])) {
$maxlength += 7; // microtime
}
// type='date' and type='time' display localized value which may be confusing, type='datetime' uses 'T' as date and time separator
echo "<input"
. ((!$has_function || $function === "") && preg_match('~(?<!o)int(?!er)~', $field["type"]) && !preg_match('~\[\]~', $field["full_type"]) ? " type='number'" : "")
. " value='" . h($value) . "'" . ($maxlength ? " data-maxlength='$maxlength'" : "")
. (preg_match('~char|binary~', $field["type"]) && $maxlength > 20 ? " size='40'" : "")
. "$attrs>"
;
}
echo $adminer->editHint($_GET["edit"], $field, $value);
// skip 'original'
$first = 0;
foreach ($functions as $key => $val) {
if ($key === "" || !$val) {
break;
}
$first++;
}
if ($first) {
echo script("mixin(qsl('td'), {onchange: partial(skipOriginal, $first), oninput: function () { this.onchange(); }});");
}
}
}
/** Process edit input field
* @param one field from fields()
* @return string or false to leave the original value
*/
function process_input($field) {
global $adminer, $driver;
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
return null;
}
$idf = bracket_escape($field["field"]);
$function = $_POST["function"][$idf];
$value = $_POST["fields"][$idf];
if ($field["type"] == "enum" || $driver->enumLength($field)) {
if ($value == -1) {
return false;
}
if ($value == "") {
return "NULL";
}
}
if ($field["auto_increment"] && $value == "") {
return null;
}
if ($function == "orig") {
return (preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) ? idf_escape($field["field"]) : false);
}
if ($function == "NULL") {
return "NULL";
}
if ($field["type"] == "set") {
$value = implode(",", (array) $value);
}
if ($function == "json") {
$function = "";
$value = json_decode($value, true);
if (!is_array($value)) {
return false; //! report errors
}
return $value;
}
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
$file = get_file("fields-$idf");
if (!is_string($file)) {
return false; //! report errors
}
return $driver->quoteBinary($file);
}
return $adminer->processInput($field, $value, $function);
}
/** Compute fields() from $_POST edit data
* @return array
*/
@@ -1078,29 +748,6 @@ function fields_from_edit() {
return $return;
}
/** Print results of search in all tables
* @uses $_GET["where"][0]
* @uses $_POST["tables"]
* @return null
*/
function search_tables() {
global $adminer, $connection;
$_GET["where"][0]["val"] = $_POST["query"];
$sep = "<ul>\n";
foreach (table_status('', true) as $table => $table_status) {
$name = $adminer->tableName($table_status);
if (isset($table_status["Engine"]) && $name != "" && (!$_POST["tables"] || in_array($table, $_POST["tables"]))) {
$result = $connection->query("SELECT" . limit("1 FROM " . table($table), " WHERE " . implode(" AND ", $adminer->selectSearchProcess(fields($table), array())), 1));
if (!$result || $result->fetch_row()) {
$print = "<a href='" . h(ME . "select=" . urlencode($table) . "&where[0][op]=" . urlencode($_GET["where"][0]["op"]) . "&where[0][val]=" . urlencode($_GET["where"][0]["val"])) . "'>$name</a>";
echo "$sep<li>" . ($result ? $print : "<p class='error'>$print: " . error()) . "\n";
$sep = "";
}
}
}
echo ($sep ? "<p class='message'>" . lang('No tables.') : "</ul>") . "\n";
}
/** Send headers for export
* @param string
* @param bool
@@ -1114,6 +761,9 @@ function dump_headers($identifier, $multi_table = false) {
header("Content-Disposition: attachment; filename=" . $adminer->dumpFilename($identifier) . ".$return" . ($output != "file" && preg_match('~^[0-9a-z]+$~', $output) ? ".$output" : ""));
}
session_write_close();
if (!ob_get_level()) {
ob_start(null, 4096);
}
ob_flush();
flush();
return $return;
@@ -1166,15 +816,18 @@ function get_temp_dir() {
* @return resource or null for error
*/
function file_open_lock($filename) {
$fp = @fopen($filename, "r+"); // @ - may not exist
if (!$fp) { // c+ is available since PHP 5.2.6
$fp = @fopen($filename, "w"); // @ - may not be writable
if (!$fp) {
return;
}
chmod($filename, 0660);
if (is_link($filename)) {
return; // https://cwe.mitre.org/data/definitions/61.html
}
$fp = @fopen($filename, "c+"); // @ - may not be writable
if (!$fp) {
return;
}
chmod($filename, 0660);
if (!flock($fp, LOCK_EX)) {
fclose($fp);
return;
}
flock($fp, LOCK_EX);
return $fp;
}
@@ -1186,26 +839,45 @@ function file_write_unlock($fp, $data) {
rewind($fp);
fwrite($fp, $data);
ftruncate($fp, strlen($data));
file_unlock($fp);
}
/** Unlock and close a file
* @param resource
*/
function file_unlock($fp) {
flock($fp, LOCK_UN);
fclose($fp);
}
/** Get first element of an array
* @param array
* @return mixed or false if not found
*/
function first($array) {
// reset(f()) triggers a notice
return reset($array);
}
/** Read password from file adminer.key in temporary directory or create one
* @param bool
* @return string or false if the file can not be created
*/
function password_file($create) {
$filename = get_temp_dir() . "/adminer.key";
$return = @file_get_contents($filename); // @ - may not exist
if ($return || !$create) {
return $return;
if (!$create && !file_exists($filename)) {
return false;
}
$fp = @fopen($filename, "w"); // @ - can have insufficient rights //! is not atomic
if ($fp) {
chmod($filename, 0660);
$fp = file_open_lock($filename);
if (!$fp) {
return false;
}
$return = stream_get_contents($fp);
if (!$return) {
$return = rand_string();
fwrite($fp, $return);
fclose($fp);
file_write_unlock($fp, $return);
} else {
file_unlock($fp);
}
return $return;
}
@@ -1315,14 +987,7 @@ function slow_query($query) {
$connection2 = null;
if (!$slow_query && support("kill") && is_object($connection2 = connect($adminer->credentials())) && ($db == "" || $connection2->select_db($db))) {
$kill = $connection2->result(connection_id()); // MySQL and MySQLi can use thread_id but it's not in PDO_MySQL
?>
<script<?php echo nonce(); ?>>
var timeout = setTimeout(function () {
ajax('<?php echo js_escape(ME); ?>script=kill', function () {
}, 'kill=<?php echo $kill; ?>&token=<?php echo $token; ?>');
}, <?php echo 1000 * $timeout; ?>);
</script>
<?php
echo script("const timeout = setTimeout(() => { ajax('" . js_escape(ME) . "script=kill', function () {}, 'kill=$kill&token=$token'); }, 1000 * $timeout);");
}
ob_flush();
flush();
@@ -1388,123 +1053,3 @@ function lzw_decompress($binary) {
}
return $return;
}
/** Return events to display help on mouse over
* @param string JS expression
* @param bool JS expression
* @return string
*/
function on_help($command, $side = 0) {
return script("mixin(qsl('select, input'), {onmouseover: function (event) { helpMouseover.call(this, event, $command, $side) }, onmouseout: helpMouseout});", "");
}
/** Print edit data form
* @param string
* @param array
* @param mixed
* @param bool
* @return null
*/
function edit_form($table, $fields, $row, $update) {
global $adminer, $token, $error;
$table_name = $adminer->tableName(table_status1($table, true));
page_header(
($update ? lang('Edit') : lang('Insert')),
$error,
array("select" => array($table, $table_name)),
$table_name
);
$adminer->editRowPrint($table, $fields, $row, $update);
if ($row === false) {
echo "<p class='error'>" . lang('No rows.') . "\n";
return;
}
?>
<form action="" method="post" enctype="multipart/form-data" id="form">
<?php
$first = 0;
if (!$fields) {
echo "<p class='error'>" . lang('You have no privileges to update this table.') . "\n";
} else {
echo "<table class='layout'>" . script("qsl('table').onkeydown = editingKeydown;");
foreach ($fields as $name => $field) {
echo "<tr><th>" . $adminer->fieldName($field);
$default = $_GET["set"][bracket_escape($name)];
if ($default === null) {
$default = $field["default"];
if ($field["type"] == "bit" && preg_match("~^b'([01]*)'\$~", $default, $regs)) {
$default = $regs[1];
}
}
$value = ($row !== null
? ($row[$name] != "" && JUSH == "sql" && preg_match("~enum|set~", $field["type"]) && is_array($row[$name])
? implode(",", $row[$name])
: (is_bool($row[$name]) ? +$row[$name] : $row[$name])
)
: (!$update && $field["auto_increment"]
? ""
: (isset($_GET["select"]) ? false : $default)
)
);
if (!$_POST["save"] && is_string($value)) {
$value = $adminer->editVal($value, $field);
}
$function = ($_POST["save"]
? (string) $_POST["function"][$name]
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
? "now"
: ($value === false ? null : ($value !== null ? '' : 'NULL'))
)
);
if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) {
$function = "SQL";
}
if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) {
$value = "";
$function = "now";
}
if ($field["type"] == "uuid" && $value == "uuid()") {
$value = "";
$function = "uuid";
}
if ($field["auto_increment"] || $function == "now" || $function == "uuid") {
$first++;
}
input($field, $value, $function);
echo "\n";
}
if (!support("table")) {
echo "<tr>"
. "<th><input name='field_keys[]'>"
. script("qsl('input').oninput = fieldChange;")
. "<td class='function'>" . html_select("field_funs[]", $adminer->editFunctions(array("null" => isset($_GET["select"]))))
. "<td><input name='field_vals[]'>"
. "\n"
;
}
echo "</table>\n";
}
echo "<p>\n";
if ($fields) {
echo "<input type='submit' value='" . lang('Save') . "'>\n";
if (!isset($_GET["select"])) {
echo "<input type='submit' name='insert' value='" . ($update
? lang('Save and continue edit')
: lang('Save and insert next')
) . "' title='Ctrl+Shift+Enter'>\n";
echo ($update ? script("qsl('input').onclick = function () { return !ajaxForm(this.form, '" . lang('Saving') . "…', this); };") : "");
}
}
echo ($update ? "<input type='submit' name='delete' value='" . lang('Delete') . "'>" . confirm() . "\n"
: ($_POST || !$fields ? "" : script("focus(qsa('td', qs('#form'))[2*$first+1].firstChild);"))
);
if (isset($_GET["select"])) {
hidden_fields(array("check" => (array) $_POST["check"], "clone" => $_POST["clone"], "all" => $_POST["all"]));
}
?>
<input type="hidden" name="referer" value="<?php echo h(isset($_POST["referer"]) ? $_POST["referer"] : $_SERVER["HTTP_REFERER"]); ?>">
<input type="hidden" name="save" value="1">
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>
<?php
}

View File

@@ -0,0 +1,536 @@
<?php
namespace Adminer;
/** Return <script> element
* @param string
* @param string
* @return string
*/
function script($source, $trailing = "\n") {
return "<script" . nonce() . ">$source</script>$trailing";
}
/** Return <script src> element
* @param string
* @return string
*/
function script_src($url) {
return "<script src='" . h($url) . "'" . nonce() . "></script>\n";
}
/** Get a nonce="" attribute with CSP nonce
* @return string
*/
function nonce() {
return ' nonce="' . get_nonce() . '"';
}
/** Get <input type="hidden">
* @param string
* @param string
* @return string HTML
*/
function input_hidden($name, $value = "") {
return "<input type='hidden' name='" . h($name) . "' value='" . h($value) . "'>\n";
}
/** Get <input type="hidden" name="token">
* @param string token to use instead of global $token
* @return string HTML
*/
function input_token($special = "") {
global $token;
return input_hidden("token", ($special ?: $token));
}
/** Get a target="_blank" attribute
* @return string
*/
function target_blank() {
return ' target="_blank" rel="noreferrer noopener"';
}
/** Escape for HTML
* @param string
* @return string
*/
function h($string) {
return str_replace("\0", "&#0;", htmlspecialchars($string, ENT_QUOTES, 'utf-8'));
}
/** Convert \n to <br>
* @param string
* @return string
*/
function nl_br($string) {
return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3
}
/** Generate HTML checkbox
* @param string
* @param string
* @param bool
* @param string
* @param string
* @param string
* @param string
* @return string
*/
function checkbox($name, $value, $checked, $label = "", $onclick = "", $class = "", $labelled_by = "") {
$return = "<input type='checkbox' name='$name' value='" . h($value) . "'"
. ($checked ? " checked" : "")
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
. ">"
. ($onclick ? script("qsl('input').onclick = function () { $onclick };", "") : "")
;
return ($label != "" || $class ? "<label" . ($class ? " class='$class'" : "") . ">$return" . h($label) . "</label>" : $return);
}
/** Generate list of HTML options
* @param array array of strings or arrays (creates optgroup)
* @param mixed
* @param bool always use array keys for value="", otherwise only string keys are used
* @return string
*/
function optionlist($options, $selected = null, $use_keys = false) {
$return = "";
foreach ($options as $k => $v) {
$opts = array($k => $v);
if (is_array($v)) {
$return .= '<optgroup label="' . h($k) . '">';
$opts = $v;
}
foreach ($opts as $key => $val) {
$return .= '<option'
. ($use_keys || is_string($key) ? ' value="' . h($key) . '"' : '')
. ($selected !== null && ($use_keys || is_string($key) ? (string) $key : $val) === $selected ? ' selected' : '')
. '>' . h($val)
;
}
if (is_array($v)) {
$return .= '</optgroup>';
}
}
return $return;
}
/** Generate HTML <select>
* @param string
* @param array
* @param string
* @param string
* @param string
* @return string
*/
function html_select($name, $options, $value = "", $onchange = "", $labelled_by = "") {
return "<select name='" . h($name) . "'"
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
. ">" . optionlist($options, $value) . "</select>"
. ($onchange ? script("qsl('select').onchange = function () { $onchange };", "") : "")
;
}
/** Generate HTML radio list
* @param string
* @param array
* @param string
* @return string
*/
function html_radios($name, $options, $value = "") {
$return = "";
foreach ($options as $key => $val) {
$return .= "<label><input type='radio' name='" . h($name) . "' value='" . h($key) . "'" . ($key == $value ? " checked" : "") . ">" . h($val) . "</label>";
}
return $return;
}
/** Get onclick confirmation
* @param string
* @param string
* @return string
*/
function confirm($message = "", $selector = "qsl('input')") {
return script("$selector.onclick = () => confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "');", "");
}
/** Print header for hidden fieldset (close by </div></fieldset>)
* @param string
* @param string
* @param bool
* @return null
*/
function print_fieldset($id, $legend, $visible = false) {
echo "<fieldset><legend>";
echo "<a href='#fieldset-$id'>$legend</a>";
echo script("qsl('a').onclick = partial(toggle, 'fieldset-$id');", "");
echo "</legend>";
echo "<div id='fieldset-$id'" . ($visible ? "" : " class='hidden'") . ">\n";
}
/** Return class='active' if $bold is true
* @param bool
* @param string
* @return string
*/
function bold($bold, $class = "") {
return ($bold ? " class='active $class'" : ($class ? " class='$class'" : ""));
}
/** Escape string for JavaScript apostrophes
* @param string
* @return string
*/
function js_escape($string) {
return addcslashes($string, "\r\n'\\/"); // slash for <script>
}
/** Generate page number for pagination
* @param int
* @param int
* @return string
*/
function pagination($page, $current) {
return " " . ($page == $current
? $page + 1
: '<a href="' . h(remove_from_uri("page") . ($page ? "&page=$page" . ($_GET["next"] ? "&next=" . urlencode($_GET["next"]) : "") : "")) . '">' . ($page + 1) . "</a>"
);
}
/** Print hidden fields
* @param array
* @param array
* @param string
* @return bool
*/
function hidden_fields($process, $ignore = array(), $prefix = '') {
$return = false;
foreach ($process as $key => $val) {
if (!in_array($key, $ignore)) {
if (is_array($val)) {
hidden_fields($val, array(), $key);
} else {
$return = true;
echo input_hidden(($prefix ? $prefix . "[$key]" : $key), $val);
}
}
}
return $return;
}
/** Print hidden fields for GET forms
* @return null
*/
function hidden_fields_get() {
echo (sid() ? input_hidden(session_name(), session_id()) : '');
echo (SERVER !== null ? input_hidden(DRIVER, SERVER) : "");
echo input_hidden("username", $_GET["username"]);
}
/** Print enum or set input field
* @param string "radio"|"checkbox"
* @param string
* @param array
* @param mixed string|array
* @param string
* @return null
*/
function enum_input($type, $attrs, $field, $value, $empty = null) {
global $adminer;
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === $empty) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = (is_array($value) ? in_array($val, $value) : $value === $val);
$return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h($adminer->editVal($val, $field)) . '</label>';
}
return $return;
}
/** Print edit input field
* @param array one field from fields()
* @param mixed
* @param string
* @param bool
* @return null
*/
function input($field, $value, $function, $autofocus = false) {
global $driver, $adminer;
$name = h(bracket_escape($field["field"]));
echo "<td class='function'>";
if (is_array($value) && !$function) {
$value = json_encode($value, 128 | 64 | 256); // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4
$function = "json";
}
$reset = (JUSH == "mssql" && $field["auto_increment"]);
if ($reset && !$_POST["save"]) {
$function = null;
}
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field);
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
$attrs = " name='fields[$name]'$disabled" . ($autofocus ? " autofocus" : "");
$enums = $driver->enumLength($field);
if ($enums) {
$field["type"] = "enum";
$field["length"] = $enums;
}
echo $driver->unconvertFunction($field) . " ";
if ($field["type"] == "enum") {
echo h($functions[""]) . "<td>" . $adminer->editInput($_GET["edit"], $field, $attrs, $value);
} else {
$has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1
? "<select name='function[$name]'$disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
. on_help("event.target.value.replace(/^SQL\$/, '')", 1)
. script("qsl('select').onchange = functionChange;", "")
: h(reset($functions))
) . '<td>';
$input = $adminer->editInput($_GET["edit"], $field, $attrs, $value); // usage in call is without a table
if ($input != "") {
echo $input;
} elseif (preg_match('~bool~', $field["type"])) {
echo "<input type='hidden'$attrs value='0'>"
. "<input type='checkbox'" . (preg_match('~^(1|t|true|y|yes|on)$~i', $value) ? " checked='checked'" : "") . "$attrs value='1'>";
} elseif ($field["type"] == "set") {
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = in_array($val, explode(",", $value), true);
echo " <label><input type='checkbox' name='fields[$name][$i]' value='" . h($val) . "'" . ($checked ? ' checked' : '') . ">" . h($adminer->editVal($val, $field)) . '</label>';
}
} elseif (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
echo "<input type='file' name='fields-$name'>";
} elseif ($function == "json" || preg_match('~^jsonb?$~', $field["type"])) {
echo "<textarea$attrs cols='50' rows='12' class='jush-js'>" . h($value) . '</textarea>';
} elseif (($text = preg_match('~text|lob|memo~i', $field["type"])) || preg_match("~\n~", $value)) {
if ($text && JUSH != "sqlite") {
$attrs .= " cols='50' rows='12'";
} else {
$rows = min(12, substr_count($value, "\n") + 1);
$attrs .= " cols='30' rows='$rows'";
}
echo "<textarea$attrs>" . h($value) . '</textarea>';
} else {
// int(3) is only a display hint
$types = $driver->types();
$maxlength = (!preg_match('~int~', $field["type"]) && preg_match('~^(\d+)(,(\d+))?$~', $field["length"], $match)
? ((preg_match("~binary~", $field["type"]) ? 2 : 1) * $match[1] + ($match[3] ? 1 : 0) + ($match[2] && !$field["unsigned"] ? 1 : 0))
: ($types[$field["type"]] ? $types[$field["type"]] + ($field["unsigned"] ? 0 : 1) : 0)
);
if (JUSH == 'sql' && min_version(5.6) && preg_match('~time~', $field["type"])) {
$maxlength += 7; // microtime
}
// type='date' and type='time' display localized value which may be confusing, type='datetime' uses 'T' as date and time separator
echo "<input"
. ((!$has_function || $function === "") && preg_match('~(?<!o)int(?!er)~', $field["type"]) && !preg_match('~\[\]~', $field["full_type"]) ? " type='number'" : "")
. " value='" . h($value) . "'" . ($maxlength ? " data-maxlength='$maxlength'" : "")
. (preg_match('~char|binary~', $field["type"]) && $maxlength > 20 ? " size='" . ($maxlength > 99 ? 60 : 40) . "'" : "")
. "$attrs>"
;
}
echo $adminer->editHint($_GET["edit"], $field, $value);
// skip 'original'
$first = 0;
foreach ($functions as $key => $val) {
if ($key === "" || !$val) {
break;
}
$first++;
}
if ($first && count($functions) > 1) {
echo script("qsl('td').oninput = partial(skipOriginal, $first);");
}
}
}
/** Process edit input field
* @param one field from fields()
* @return string or false to leave the original value
*/
function process_input($field) {
global $adminer, $driver;
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
return null;
}
$idf = bracket_escape($field["field"]);
$function = $_POST["function"][$idf];
$value = $_POST["fields"][$idf];
if ($field["type"] == "enum" || $driver->enumLength($field)) {
if ($value == -1) {
return false;
}
if ($value == "") {
return "NULL";
}
}
if ($field["auto_increment"] && $value == "") {
return null;
}
if ($function == "orig") {
return (preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) ? idf_escape($field["field"]) : false);
}
if ($function == "NULL") {
return "NULL";
}
if ($field["type"] == "set") {
$value = implode(",", (array) $value);
}
if ($function == "json") {
$function = "";
$value = json_decode($value, true);
if (!is_array($value)) {
return false; //! report errors
}
return $value;
}
if (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
$file = get_file("fields-$idf");
if (!is_string($file)) {
return false; //! report errors
}
return $driver->quoteBinary($file);
}
return $adminer->processInput($field, $value, $function);
}
/** Print results of search in all tables
* @uses $_GET["where"][0]
* @uses $_POST["tables"]
* @return null
*/
function search_tables() {
global $adminer, $connection;
$_GET["where"][0]["val"] = $_POST["query"];
$sep = "<ul>\n";
foreach (table_status('', true) as $table => $table_status) {
$name = $adminer->tableName($table_status);
if (isset($table_status["Engine"]) && $name != "" && (!$_POST["tables"] || in_array($table, $_POST["tables"]))) {
$result = $connection->query("SELECT" . limit("1 FROM " . table($table), " WHERE " . implode(" AND ", $adminer->selectSearchProcess(fields($table), array())), 1));
if (!$result || $result->fetch_row()) {
$print = "<a href='" . h(ME . "select=" . urlencode($table) . "&where[0][op]=" . urlencode($_GET["where"][0]["op"]) . "&where[0][val]=" . urlencode($_GET["where"][0]["val"])) . "'>$name</a>";
echo "$sep<li>" . ($result ? $print : "<p class='error'>$print: " . error()) . "\n";
$sep = "";
}
}
}
echo ($sep ? "<p class='message'>" . lang('No tables.') : "</ul>") . "\n";
}
/** Return events to display help on mouse over
* @param string JS expression
* @param bool JS expression
* @return string
*/
function on_help($command, $side = 0) {
return script("mixin(qsl('select, input'), {onmouseover: function (event) { helpMouseover.call(this, event, $command, $side) }, onmouseout: helpMouseout});", "");
}
/** Print edit data form
* @param string
* @param array
* @param mixed
* @param bool
* @return null
*/
function edit_form($table, $fields, $row, $update) {
global $adminer, $error;
$table_name = $adminer->tableName(table_status1($table, true));
page_header(
($update ? lang('Edit') : lang('Insert')),
$error,
array("select" => array($table, $table_name)),
$table_name
);
$adminer->editRowPrint($table, $fields, $row, $update);
if ($row === false) {
echo "<p class='error'>" . lang('No rows.') . "\n";
return;
}
echo "<form action='' method='post' enctype='multipart/form-data' id='form'>\n";
if (!$fields) {
echo "<p class='error'>" . lang('You have no privileges to update this table.') . "\n";
} else {
echo "<table class='layout'>" . script("qsl('table').onkeydown = editingKeydown;");
$autofocus = !$_POST;
foreach ($fields as $name => $field) {
echo "<tr><th>" . $adminer->fieldName($field);
$default = $_GET["set"][bracket_escape($name)];
if ($default === null) {
$default = $field["default"];
if ($field["type"] == "bit" && preg_match("~^b'([01]*)'\$~", $default, $regs)) {
$default = $regs[1];
}
if (JUSH == "sql" && preg_match('~binary~', $field["type"])) {
$default = bin2hex($default); // same as UNHEX
}
}
$value = ($row !== null
? ($row[$name] != "" && JUSH == "sql" && preg_match("~enum|set~", $field["type"]) && is_array($row[$name])
? implode(",", $row[$name])
: (is_bool($row[$name]) ? +$row[$name] : $row[$name])
)
: (!$update && $field["auto_increment"]
? ""
: (isset($_GET["select"]) ? false : $default)
)
);
if (!$_POST["save"] && is_string($value)) {
$value = $adminer->editVal($value, $field);
}
$function = ($_POST["save"]
? (string) $_POST["function"][$name]
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
? "now"
: ($value === false ? null : ($value !== null ? '' : 'NULL'))
)
);
if (!$_POST && !$update && $value == $field["default"] && preg_match('~^[\w.]+\(~', $value)) {
$function = "SQL";
}
if (preg_match("~time~", $field["type"]) && preg_match('~^CURRENT_TIMESTAMP~i', $value)) {
$value = "";
$function = "now";
}
if ($field["type"] == "uuid" && $value == "uuid()") {
$value = "";
$function = "uuid";
}
if ($autofocus !== false) {
$autofocus = ($field["auto_increment"] || $function == "now" || $function == "uuid" ? null : true); // null - don't autofocus this input but check the next one
}
input($field, $value, $function, $autofocus);
if ($autofocus) {
$autofocus = false;
}
echo "\n";
}
if (!support("table") && !fields($table)) {
echo "<tr>"
. "<th><input name='field_keys[]'>"
. script("qsl('input').oninput = fieldChange;")
. "<td class='function'>" . html_select("field_funs[]", $adminer->editFunctions(array("null" => isset($_GET["select"]))))
. "<td><input name='field_vals[]'>"
. "\n"
;
}
echo "</table>\n";
}
echo "<p>\n";
if ($fields) {
echo "<input type='submit' value='" . lang('Save') . "'>\n";
if (!isset($_GET["select"])) {
echo "<input type='submit' name='insert' value='" . ($update
? lang('Save and continue edit')
: lang('Save and insert next')
) . "' title='Ctrl+Shift+Enter'>\n";
echo ($update ? script("qsl('input').onclick = function () { return !ajaxForm(this.form, '" . lang('Saving') . "…', this); };") : "");
}
}
echo ($update ? "<input type='submit' name='delete' value='" . lang('Delete') . "'>" . confirm() . "\n" : "");
if (isset($_GET["select"])) {
hidden_fields(array("check" => (array) $_POST["check"], "clone" => $_POST["clone"], "all" => $_POST["all"]));
}
echo input_hidden("referer", (isset($_POST["referer"]) ? $_POST["referer"] : $_SERVER["HTTP_REFERER"]));
echo input_hidden("save", 1);
echo input_token();
echo "</form>\n";
}

View File

@@ -45,6 +45,7 @@ $langs = array(
'th' => 'ภาษาไทย', // Panya Saraphi, elect.tu@gmail.com - http://www.opencart2u.com/
'tr' => 'Türkçe', // Bilgehan Korkmaz - turktron.com
'uk' => 'Українська', // Valerii Kryzhov
'uz' => 'Oʻzbekcha', // Junaydullaev Inoyatullokhon - https://av.uz/
'vi' => 'Tiếng Việt', // Giang Manh @ manhgd google mail
'zh' => '简体中文', // Mr. Lodar, vea - urn2.net - vea.urn2@gmail.com
'zh-tw' => '繁體中文', // http://tzangms.com
@@ -63,10 +64,12 @@ function get_lang() {
* @param int
* @return string
*/
// this is matched by compile.php
function lang($idf, $number = null) {
global $LANG, $translations;
$translation = ($translations[$idf] ?: $idf);
if (is_array($translation)) {
// this is matched by compile.php
$pos = ($number == 1 ? 0
: ($LANG == 'cs' || $LANG == 'sk' ? ($number && $number < 5 ? 1 : 2) // different forms for 1, 2-4, other
: ($LANG == 'fr' ? (!$number ? 0 : 1) // different forms for 0-1, other
@@ -74,11 +77,12 @@ function lang($idf, $number = null) {
: ($LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other
: ($LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other
: ($LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0
: ($LANG == 'bs' || $LANG == 'ru' || $LANG == 'sr' || $LANG == 'uk' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other
: 1 // different forms for 1, other
)))))))); // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
: (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
: 1)))))))) // different forms for 1, other
; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
$translation = $translation[$pos];
}
$translation = str_replace("'", '', $translation); // translations can contain HTML or be used in optionlist (we couldn't escape them here) but they can also be used e.g. in title='' //! escape plaintext translations
$args = func_get_args();
array_shift($args);
$format = str_replace("%d", "%s", $translation);
@@ -93,7 +97,7 @@ function switch_lang() {
echo "<form action='' method='post'>\n<div id='lang'>";
echo lang('Language') . ": " . html_select("lang", $langs, $LANG, "this.form.submit();");
echo " <input type='submit' value='" . lang('Use') . "' class='hidden'>\n";
echo "<input type='hidden' name='token' value='" . get_token() . "'>\n"; // $token may be empty in auth.inc.php
echo input_token(get_token()); // $token may be empty in auth.inc.php
echo "</div>\n</form>\n";
}

View File

@@ -4,7 +4,7 @@ namespace Adminer;
// PDO can be used in several database drivers
if (extension_loaded('pdo')) {
abstract class PdoDb {
public $server_info, $affected_rows, $errno, $error;
public $flavor = '', $server_info, $affected_rows, $errno, $error;
protected $pdo;
private $result;
@@ -13,7 +13,7 @@ if (extension_loaded('pdo')) {
$options[\PDO::ATTR_STATEMENT_CLASS] = array('Adminer\PdoDbStatement');
try {
$this->pdo = new \PDO($dsn, $username, $password, $options);
} catch (Exception $ex) {
} catch (\Exception $ex) {
auth_error(h($ex->getMessage()));
}
$this->server_info = @$this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION);
@@ -72,7 +72,7 @@ if (extension_loaded('pdo')) {
return false;
}
$row = $result->fetch();
return $row[$field];
return $row ? $row[$field] : false;
}
}
@@ -87,11 +87,15 @@ if (extension_loaded('pdo')) {
return $this->fetch(\PDO::FETCH_NUM);
}
function fetch_column($field) {
return $this->fetchColumn($field);
}
function fetch_field() {
$row = (object) $this->getColumnMeta($this->_offset++);
$row->orgtable = $row->table;
$row->orgname = $row->name;
$row->charsetnr = (in_array("blob", (array) $row->flags) ? 63 : 0);
$type = $row->pdo_type;
$row->type = ($type == \PDO::PARAM_INT ? 0 : 15);
$row->charsetnr = ($type == \PDO::PARAM_LOB || (isset($row->flags) && in_array("blob", (array) $row->flags)) ? 63 : 0);
return $row;
}

View File

@@ -0,0 +1,427 @@
<?php
namespace Adminer;
class Plugins extends Adminer {
public $plugins; ///< @var protected(set) array
/** Register plugins
* @param array object instances or null to autoload plugins from adminer-plugins/
*/
function __construct($plugins) {
if ($plugins === null) {
$plugins = array();
$basename = "adminer-plugins";
if (is_dir($basename)) {
foreach (glob("$basename/*.php") as $filename) {
$include = include_once "./$filename";
}
}
$help = " href='https://www.adminer.org/plugins/#use'" . target_blank();
if (file_exists("$basename.php")) {
$include = include_once "./$basename.php"; // example: return array(new AdminerLoginOtp($secret))
if (is_array($include)) {
foreach ($include as $plugin) {
$plugins[get_class($plugin)] = $plugin;
}
} else {
$this->error .= lang('%s must <a%s>return an array</a>.', "<b>$basename.php</b>", $help) . "<br>";
}
}
foreach (get_declared_classes() as $class) {
if (!$plugins[$class] && preg_match('~^Adminer\w~i', $class)) {
$reflection = new \ReflectionClass($class);
$constructor = $reflection->getConstructor();
if ($constructor && $constructor->getNumberOfRequiredParameters()) {
$this->error .= lang('<a%s>Configure</a> %s in %s.', $help, "<b>$class</b>", "<b>$basename.php</b>") . "<br>";
} else {
$plugins[$class] = new $class;
}
}
}
}
$this->plugins = $plugins;
}
private function callParent($function, $args) {
return call_user_func_array(array('parent', $function), $args);
}
private function applyPlugin($function, $params) {
$args = array();
foreach ($params as $key => $val) {
// some plugins accept params by reference - we don't need to propage it outside, just to the other plugins
$args[] = &$params[$key];
}
foreach ($this->plugins as $plugin) {
if (method_exists($plugin, $function)) {
$return = call_user_func_array(array($plugin, $function), $args);
if ($return !== null) {
return $return;
}
}
}
return $this->callParent($function, $args);
}
private function appendPlugin($function, $args) {
$return = $this->callParent($function, $args);
foreach ($this->plugins as $plugin) {
if (method_exists($plugin, $function)) {
$value = call_user_func_array(array($plugin, $function), $args);
if ($value) {
$return += $value;
}
}
}
return $return;
}
// appendPlugin
function dumpFormat() {
$args = func_get_args();
return $this->appendPlugin(__FUNCTION__, $args);
}
function dumpOutput() {
$args = func_get_args();
return $this->appendPlugin(__FUNCTION__, $args);
}
function editRowPrint($table, $fields, $row, $update) {
$args = func_get_args();
return $this->appendPlugin(__FUNCTION__, $args);
}
function editFunctions($field) {
$args = func_get_args();
return $this->appendPlugin(__FUNCTION__, $args);
}
// applyPlugin
function name() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function credentials() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function connectSsl() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function permanentLogin($create = false) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function bruteForceKey() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function serverName($server) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function database() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function schemas() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function databases($flush = true) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function queryTimeout() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function headers() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function csp() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function head($dark = null) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function css() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function loginForm() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function loginFormField($name, $heading, $value) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function login($login, $password) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function tableName($tableStatus) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function fieldName($field, $order = 0) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLinks($tableStatus, $set = "") {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function foreignKeys($table) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function backwardKeys($table, $tableName) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function backwardKeysPrint($backwardKeys, $row) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectQuery($query, $start, $failed = false) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function sqlCommandQuery($query) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function sqlPrintAfter() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function rowDescription($table) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function rowDescriptions($rows, $foreignKeys) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLink($val, $field) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectVal($val, $link, $field, $original) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function editVal($val, $field) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function tableStructurePrint($fields, $tableStatus = null) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function tableIndexesPrint($indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectColumnsPrint($select, $columns) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectSearchPrint($where, $columns, $indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectOrderPrint($order, $columns, $indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLimitPrint($limit) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLengthPrint($text_length) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectActionPrint($indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectCommandPrint() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectImportPrint() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectEmailPrint($emailFields, $columns) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectColumnsProcess($columns, $indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectSearchProcess($fields, $indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectOrderProcess($fields, $indexes) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLimitProcess() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectLengthProcess() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectEmailProcess($where, $foreignKeys) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function selectQueryBuild($select, $where, $group, $order, $limit, $page) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function messageQuery($query, $time, $failed = false) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function editInput($table, $field, $attrs, $value) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function editHint($table, $field, $value) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function processInput($field, $value, $function = "") {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpDatabase($db) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpTable($table, $style, $is_view = 0) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpData($table, $style, $query) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpFilename($identifier) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpHeaders($identifier, $multi_table = false) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function dumpFooter() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function importServerPath() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function homepage() {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function navigation($missing) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function syntaxHighlighting($tables) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function databasesPrint($missing) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
function tablesPrint($tables) {
$args = func_get_args();
return $this->applyPlugin(__FUNCTION__, $args);
}
}

View File

@@ -1,4 +1,4 @@
<?php
namespace Adminer;
$VERSION = "5.0.4";
$VERSION = "5.1.0";

View File

@@ -6,6 +6,7 @@
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
// this is matched by compile.php
namespace Adminer;
@@ -15,6 +16,7 @@ include "./include/tmpfile.inc.php";
if (isset($_GET["select"]) && ($_POST["edit"] || $_POST["clone"]) && !$_POST["save"]) {
$_GET["edit"] = $_GET["select"];
}
// this is matched by compile.php
if (isset($_GET["callf"])) {
$_GET["call"] = $_GET["callf"];
}

View File

@@ -19,7 +19,7 @@ if (JUSH == "mongo") { // doesn't support primary key
}
$row = $_POST;
if ($row) {
set_adminer_settings(array("index_options" => $row["options"]));
save_settings(array("index_options" => $row["options"]));
}
if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
$alter = array();
@@ -97,7 +97,7 @@ if (!$row) {
$row["indexes"] = $indexes;
}
$lengths = (JUSH == "sql" || JUSH == "mssql");
$show_options = ($_POST ? $_POST["options"] : adminer_setting("index_options"));
$show_options = ($_POST ? $_POST["options"] : get_setting("index_options"));
?>
<form action="" method="post">
@@ -155,5 +155,5 @@ foreach ($row["indexes"] as $index) {
</div>
<p>
<input type="submit" value="<?php echo lang('Save'); ?>">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>

View File

@@ -266,3 +266,5 @@ $translations = array(
'Edit all' => 'تعديل الكل',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php ar` to update this file

View File

@@ -256,7 +256,7 @@ $translations = array(
// in-place editing in select
'Modify' => 'Промяна',
'Ctrl+click on a value to modify it.' => 'Ctrl+щракване в стойността, за да я промените.',
'Use edit link to modify this value.' => 'Използвайте "редакция" за промяна на данните.',
'Use edit link to modify this value.' => 'Използвайте \'редакция\' за промяна на данните.',
// %s can contain auto-increment value
'Item%s has been inserted.' => 'Елементи%s бяха вмъкнати.',
@@ -335,3 +335,5 @@ $translations = array(
'Type has been created.' => 'Вида беше създаден.',
'Alter type' => 'Промяна на вид',
);
// run `php ../../lang.php bg` to update this file

View File

@@ -266,3 +266,5 @@ $translations = array(
'Edit all' => 'সবগুলো সম্পাদনা করুন',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php bn` to update this file

View File

@@ -320,3 +320,5 @@ $translations = array(
'Type has been created.' => 'tip je spašen.',
'Alter type' => 'Ažuriraj tip',
);
// run `php ../../lang.php bs` to update this file

View File

@@ -267,3 +267,5 @@ $translations = array(
'Edit all' => 'Edita-ho tot',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php ca` to update this file

View File

@@ -13,6 +13,9 @@ $translations = array(
'Logged as: %s' => 'Přihlášen jako: %s',
'Logout successful.' => 'Odhlášení proběhlo v pořádku.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Díky za použití Admineru, <a href="https://www.adminer.org/cs/donation/">přispějte</a> na vývoj.',
'Loaded plugins' => 'Nahrané pluginy',
'%s must <a%s>return an array</a>.' => '%s musí <a%s>vracet pole</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Nakonfigurujte</a> %s v %s.',
'Invalid credentials.' => 'Neplatné přihlašovací údaje.',
'There is a space in the input password which might be the cause.' => 'Problém může být, že je v zadaném hesle mezera.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje přístup k databázi bez hesla, <a href="https://www.adminer.org/cs/password/"%s>více informací</a>.',
@@ -356,3 +359,5 @@ $translations = array(
'Check has been altered.' => 'Kontrola byla změněna.',
'Check has been dropped.' => 'Kontrola byla odstraněna.',
);
// run `php ../../lang.php cs` to update this file

View File

@@ -281,3 +281,5 @@ $translations = array(
'Alter type' => 'Ændre type',
'Saving' => 'Gemmer',
);
// run `php ../../lang.php da` to update this file

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => 'Unbekannter Fehler.',
'Database does not support password.' => 'Die Datenbank unterstützt kein Passwort.',
'Disable %s or enable %s or %s extensions.' => 'Deaktivieren Sie %s oder aktivieren Sie die Erweiterungen %s oder %s.',
'Check has been dropped.' => 'Check wurde abgebrochen.',
'Check has been altered.' => 'Check wurde geändert.',
'Check has been created.' => 'Check wurde erstellt.',
'Alter check' => 'Check ändern',
'Create check' => 'Check erstellen',
'Checks' => 'Checks',
);
// run `php ../../lang.php de` to update this file

View File

@@ -335,3 +335,5 @@ $translations = array(
'Type has been created.' => 'Ο τύπος δημιουργήθηκε.',
'Alter type' => 'Τροποποίηση τύπου',
);
// run `php ../../lang.php el` to update this file

View File

@@ -15,3 +15,5 @@ $translations = array(
'%d in total' => '%d in total',
'%d query(s) executed OK.' => array('%d query executed OK.', '%d queries executed OK.'),
);
// run `php ../../lang.php en` to update this file

View File

@@ -44,6 +44,7 @@ $translations = array(
'Save and insert next' => 'Guardar e insertar siguiente',
'Delete' => 'Eliminar',
'Database' => 'Base de datos',
'DB' => 'BD',
'Routines' => 'Procedimientos',
'Indexes have been altered.' => 'Índices actualizados.',
'Indexes' => 'Índices',
@@ -62,18 +63,22 @@ $translations = array(
'Page' => 'Página',
'Query executed OK, %d row(s) affected.' => array('Consulta ejecutada, %d registro afectado.', 'Consulta ejecutada, %d registros afectados.'),
'Error in query' => 'Error al ejecutar consulta',
'Unknown error.' => 'Error desconocido.',
'Warnings' => 'Advertencias',
'ATTACH queries are not supported.' => 'Consultas tipo ATTACH no soportadas.',
'Execute' => 'Ejecutar',
'Table' => 'Tabla',
'Foreign keys' => 'Claves externas',
'Triggers' => 'Disparadores',
'View' => 'Vista',
'Materialized view' => 'Vista materializada',
'Unable to select the table' => 'No es posible seleccionar la tabla',
'Invalid CSRF token. Send the form again.' => 'Token CSRF inválido. Vuelva a enviar los datos del formulario.',
'Comment' => 'Comentario',
'Default values' => 'Valores predeterminados',
'%d byte(s)' => array('%d byte', '%d bytes'),
'No commands to execute.' => 'No es posible ejecutar ningún comando.',
'Unable to upload a file.' => 'No es posible importar el archivo.',
'No commands to execute.' => 'Ningún comando para ejecutar.',
'Unable to upload a file.' => 'No es posible cargar el archivo.',
'File upload' => 'Importar archivo',
'File uploads are disabled.' => 'Importación de archivos deshablilitada.',
'Routine has been called, %d row(s) affected.' => array('Consulta ejecutada, %d registro afectado.', 'Consulta ejecutada, %d registros afectados.'),
@@ -83,9 +88,9 @@ $translations = array(
'Session support must be enabled.' => 'Deben estar habilitadas las sesiones.',
'Session expired, please login again.' => 'Sesión caducada, por favor escriba su clave de nuevo.',
'Text length' => 'Longitud de texto',
'Foreign key has been dropped.' => 'Clave externa eliminada.',
'Foreign key has been altered.' => 'Clave externa modificada.',
'Foreign key has been created.' => 'Clave externa creada.',
'Foreign key has been dropped.' => 'Clave foranea eliminada.',
'Foreign key has been altered.' => 'Clave foranea modificada.',
'Foreign key has been created.' => 'Clave foranea creada.',
'Foreign key' => 'Clave externa',
'Target table' => 'Tabla de destino',
'Change' => 'Modificar',
@@ -140,6 +145,8 @@ $translations = array(
'Grant' => 'Conceder',
'Revoke' => 'Impedir',
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'POST data demasiado grande. Reduzca el tamaño o aumente la directiva de configuración %s.',
'You can upload a big SQL file via FTP and import it from server.' => 'Usted puede cargar un SQL grande mediante FTP e importarlo desde el servidor.',
'You are offline.' => 'Usted no esta en linea.',
'Logged as: %s' => 'Logueado como: %s',
'Move up' => 'Mover arriba',
'Move down' => 'Mover abajo',
@@ -147,8 +154,8 @@ $translations = array(
'Aggregation' => 'Agregados',
'Export' => 'Exportar',
'Output' => 'Salida',
'open' => 'mostrar',
'save' => 'archivo',
'open' => 'abrir',
'save' => 'guardar',
'Format' => 'Formato',
'Tables' => 'Tablas',
'Data' => 'Datos',
@@ -195,6 +202,7 @@ $translations = array(
'Partition name' => 'Nombre de partición',
'Values' => 'Valores',
'%d row(s) have been imported.' => array('%d registro importado.', '%d registros importados.'),
'File must be in UTF-8 encoding.' => 'El archivo tiene que ser codificacion UTF-8.',
'anywhere' => 'donde sea',
'Import' => 'Importar',
'Stop on error' => 'Parar en caso de error',
@@ -256,7 +264,7 @@ $translations = array(
'Attachments' => 'Adjuntos',
'%d query(s) executed OK.' => array('%d sentencia SQL ejecutada correctamente.', '%d sentencias SQL ejecutadas correctamente.'),
'Show only errors' => 'Mostrar solamente errores',
'Refresh' => 'Refrescar',
'Refresh' => 'Actualizar',
'Invalid schema.' => 'Esquema inválido.',
'Please use one of the extensions %s.' => 'Por favor, use una de las extensiones %s.',
'now' => 'ahora',
@@ -266,4 +274,28 @@ $translations = array(
'Permanent link' => 'Enlace permanente',
'Edit all' => 'Editar todos',
'HH:MM:SS' => 'HH:MM:SS',
'Loaded plugins' => 'Plugins cargados',
// '<b>%s</b> must return an array.' => '<b>%s</b> tiene que retornar un arreglo.',
// 'Configure <b>%s</b> in <b>%s</b>.' => 'Configurar <b>%s</b> en <b>%s</b>.',
'There is a space in the input password which might be the cause.' => 'Hay un espacio en el password, lo cual puede ser la causa.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer no soporta accesar una base de datos sin clave, <a href="https://www.adminer.org/en/password/"%s>Ver detalles</a>.',
'Database does not support password.' => 'La base de datos no soporta password.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Muchos intentos de acceso Intente en %d minutos.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Password maestro expirado. <a href="https://www.adminer.org/en/extension/"%s>Implemente</a> %s metodo para hacerlo permanente.',
'If you did not send this request from Adminer then close this page.' => 'Si no puede enviar la solicitud por Adminer entonces cierre esta pagina.',
'Connecting to privileged ports is not allowed.' => 'Conexiones a puertos privilegiados no son permitidas.',
'Disable %s or enable %s or %s extensions.' => 'Desactivar %s o activar %s o %s extensiones.',
'The action will be performed after successful login with the same credentials.' => 'La operacion sera ejecutada despues de ingresar nuevamente con las mismas credenciales.',
'You have no privileges to update this table.' => 'Usted no tiene privilegios para actualizar esta tabla.',
// Table check constraints
'Checks' => 'Chequeos',
'Create check' => 'Crear chequeo',
'Alter check' => 'Cambiar chequeo',
'Check has been created.' => 'Chequeo creado.',
'Check has been altered.' => 'Chequeo cambiado.',
'Check has been dropped.' => 'Chequeo eliminado.',
);
// run `php ../../lang.php es` to update this file

View File

@@ -267,3 +267,5 @@ $translations = array(
'Edit all' => 'Muuda kõiki',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php et` to update this file

View File

@@ -333,3 +333,5 @@ $translations = array(
'Type has been created.' => 'نوع ایجاد شد.',
'Alter type' => 'ویرایش نوع',
);
// run `php ../../lang.php fa` to update this file

View File

@@ -349,3 +349,5 @@ $translations = array(
'Database does not support password.' => 'Tietokanta ei tue salasanaa.',
'Disable %s or enable %s or %s extensions.' => 'Poista käytöstä %s tai ota käyttöön laajennus %s tai %s.',
);
// run `php ../../lang.php fi` to update this file

View File

@@ -242,7 +242,7 @@ $translations = array(
'Type has been dropped.' => 'Le type a été supprimé.',
'Type has been created.' => 'Le type a été créé.',
'Ctrl+click on a value to modify it.' => 'Ctrl+cliquez sur une valeur pour la modifier.',
'Use edit link to modify this value.' => 'Utilisez le lien "modifier" pour modifier cette valeur.',
'Use edit link to modify this value.' => 'Utilisez le lien \'modifier\' pour modifier cette valeur.',
'last' => 'dernière',
'From server' => 'Depuis le serveur',
'System' => 'Système',
@@ -302,3 +302,5 @@ $translations = array(
'Database does not support password.' => 'La base de données ne support pas les mots de passe.',
'Disable %s or enable %s or %s extensions.' => 'Désactiver %s ou activer %s or %s extensions.',
);
// run `php ../../lang.php fr` to update this file

View File

@@ -290,3 +290,5 @@ $translations = array(
'yes' => 'si',
'no' => 'non',
);
// run `php ../../lang.php gl` to update this file

View File

@@ -292,3 +292,5 @@ $translations = array(
'yes' => 'כן',
'no' => 'לא',
);
// run `php ../../lang.php he` to update this file

View File

@@ -266,3 +266,5 @@ $translations = array(
'Edit all' => 'Összes szerkesztése',
'HH:MM:SS' => 'óó:pp:mm',
);
// run `php ../../lang.php hu` to update this file

View File

@@ -315,3 +315,5 @@ $translations = array(
'Type has been created.' => 'Jenis berhasil dibuat.',
'Alter type' => 'Ubah jenis',
);
// run `php ../../lang.php id` to update this file

View File

@@ -302,3 +302,5 @@ $translations = array(
'yes' => 'si',
'no' => 'no',
);
// run `php ../../lang.php it` to update this file

View File

@@ -3,17 +3,24 @@ namespace Adminer;
$translations = array(
'Login' => 'ログイン',
'Logout successful.' => 'ログアウト',
'Invalid credentials.' => '不正なログイン',
'Logout successful.' => 'ログアウトしました。',
'Invalid credentials.' => '不正なログインです。',
'Server' => 'サーバ',
'Username' => 'ユーザ名',
'Password' => 'パスワード',
'Loaded plugins' => '読込済プラグイン',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminerのご利用ありがとうございました。(寄付は<a href="https://www.adminer.org/en/donation/">こちら</a>)',
'There is a space in the input password which might be the cause.' => '入力されたパスワードに空白が含まれているので、それが原因かもしれません。',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer はパスワードのないデータベースへの接続には対応していません。(<a href="https://www.adminer.org/en/password/"%s>詳細</a>)',
'Database does not support password.' => 'データベースがパスワードに対応していません。',
'Too many unsuccessful logins, try again in %d minute(s).' => 'ログインの失敗数が多すぎます。%d分後に再試行してください。',
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'マスタパスワードが期限切れになりました。<a href="https://www.adminer.org/en/extension/"%s>(実装例)</a> 無期限にするには %s 関数を用います。',
'Select database' => 'データベースを選択してください',
'Invalid database.' => '不正なデータベース',
'Table has been dropped.' => 'テーブルを削除しました',
'Table has been altered.' => 'テーブルを変更しました',
'Table has been created.' => 'テーブルを作成しました',
'Alter table' => 'テーブル変更',
'Invalid database.' => '不正なデータベースです。',
'Table has been dropped.' => 'テーブルを削除しました',
'Table has been altered.' => 'テーブルを変更しました',
'Table has been created.' => 'テーブルを作成しました',
'Alter table' => 'テーブル変更',
'Create table' => 'テーブルを作成',
'Table name' => 'テーブル名',
'engine' => 'エンジン',
@@ -25,10 +32,11 @@ $translations = array(
'Options' => '設定',
'Save' => '保存',
'Drop' => '削除',
'Database has been dropped.' => 'データベースを削除しました',
'Database has been created.' => 'データベースを作成しました',
'Database has been renamed.' => 'データベースの名前を変えました',
'Database has been altered.' => 'データベースを変更しました',
'Drop %s?' => '%s を削除しますか?',
'Database has been dropped.' => 'データベースを削除しました',
'Database has been created.' => 'データベースを作成しました',
'Database has been renamed.' => 'データベースの名前を変えました',
'Database has been altered.' => 'データベースを変更しました。',
'Alter database' => 'データベースを変更',
'Create database' => 'データベースを作成',
'SQL command' => 'SQLコマンド',
@@ -36,15 +44,17 @@ $translations = array(
'Use' => '使用',
'No tables.' => 'テーブルがありません。',
'select' => '選択',
'Item has been deleted.' => '項目を削除しました',
'Item has been updated.' => '項目を更新しました',
'Item has been deleted.' => '項目を削除しました',
'Item has been updated.' => '項目を更新しました',
'Edit' => '編集',
'Insert' => '挿入',
'Save and insert next' => '保存/追加',
'Delete' => '削除',
'You have no privileges to update this table.' => 'このテーブルを更新する権限がありません。',
'Database' => 'データベース',
'DB' => 'DB',
'Routines' => 'ルーチン',
'Indexes have been altered.' => '索引を変更しました',
'Indexes have been altered.' => '索引を変更しました',
'Indexes' => '索引',
'Alter indexes' => '索引の変更',
'Add next' => '追加',
@@ -55,36 +65,46 @@ $translations = array(
'Sort' => 'ソート',
'descending' => '降順',
'Limit' => '制約',
'No rows.' => '行がありません',
'Limit rows' => '行数の制約',
'No rows.' => '行がありません。',
'Action' => '動作',
'edit' => '編集',
'Page' => 'ページ',
'Query executed OK, %d row(s) affected.' => 'クエリーを実行しました。%d 行を変更しました',
'Query executed OK, %d row(s) affected.' => 'クエリーを実行しました。%d 行を変更しました',
'Error in query' => 'クエリーのエラー',
'Unknown error.' => '不明なエラーです。',
'Warnings' => '警告',
'ATTACH queries are not supported.' => 'ATTACH クエリーは対応していません。',
'Execute' => '実行',
'Table' => 'テーブル',
'Foreign keys' => '外部キー',
'Triggers' => 'トリガー',
'View' => 'ビュー',
'Materialized view' => 'マテビュー',
'Full table scan' => 'テーブルの全スキャン',
'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からのリクエストを送信しない場合はこのページを閉じてください。',
'Comment' => 'コメント',
'Default values' => '規定値',
'%d byte(s)' => '%d バイト',
'No commands to execute.' => '実行するコマンドがありません',
'Unable to upload a file.' => 'ファイルをアップロードできません',
'No commands to execute.' => '実行するコマンドがありません',
'Unable to upload a file.' => 'ファイルをアップロードできません',
'File upload' => 'ファイルをアップロード',
'File uploads are disabled.' => 'ファイルのアップロードが無効です',
'Routine has been called, %d row(s) affected.' => 'ルーチンを呼びました。%d 行を変更しました',
'File uploads are disabled.' => 'ファイルのアップロードが無効です',
'Routine has been called, %d row(s) affected.' => 'ルーチンを呼びました。%d 行を変更しました',
'Call' => '呼出し',
'No extension' => '拡張機能がありません',
'None of the supported PHP extensions (%s) are available.' => 'PHPの拡張機能%sがセットアップされていません',
'Session support must be enabled.' => 'セッションを有効にしてください',
'Session expired, please login again.' => 'セッションの期限切れ。ログインし直してください',
'None of the supported PHP extensions (%s) are available.' => 'PHPの拡張機能%sがセットアップされていません',
'Connecting to privileged ports is not allowed.' => '特権ポートへの接続は許可されていません。',
'Disable %s or enable %s or %s extensions.' => '%s を無効にするか、拡張機能 %s または %s を有効にしてください',
'Session support must be enabled.' => 'セッションを有効にしてください。',
'Session expired, please login again.' => 'セッションの期限切れ。ログインし直してください。',
'The action will be performed after successful login with the same credentials.' => '同じアカウントで正しくログインすると作業を実行します。',
'Text length' => '文字列の長さ',
'Foreign key has been dropped.' => '外部キーを削除しました',
'Foreign key has been altered.' => '外部キーを変更しました',
'Foreign key has been created.' => '外部キーを作成しました',
'Foreign key has been dropped.' => '外部キーを削除しました',
'Foreign key has been altered.' => '外部キーを変更しました',
'Foreign key has been created.' => '外部キーを作成しました',
'Foreign key' => '外キー',
'Target table' => 'テーブル',
'Change' => '変更',
@@ -97,49 +117,52 @@ $translations = array(
'ON UPDATE' => 'ON UPDATE',
'Index Type' => '索引の型',
'length' => '長さ',
'View has been dropped.' => 'ビューを削除しました',
'View has been altered.' => 'ビューを変更しました',
'View has been created.' => 'ビューを作成しました',
'View has been dropped.' => 'ビューを削除しました',
'View has been altered.' => 'ビューを変更しました',
'View has been created.' => 'ビューを作成しました',
'Alter view' => 'ビューを変更',
'Create view' => 'ビューを作成',
'Name' => '名称',
'Process list' => 'プロセス一覧',
'%d process(es) have been killed.' => '%d プロセスを強制終了しました',
'%d process(es) have been killed.' => '%d プロセスを強制終了しました',
'Kill' => '強制終了',
'Parameter name' => '参数名',
'Database schema' => '構造',
'Create procedure' => 'プロシージャの作成',
'Create function' => '関数の作成',
'Routine has been dropped.' => 'ルーチンを作成',
'Routine has been altered.' => 'ルーチンを変更',
'Routine has been created.' => 'ルーチンを作成',
'Routine has been dropped.' => 'ルーチンを作成しました。',
'Routine has been altered.' => 'ルーチンを変更しました。',
'Routine has been created.' => 'ルーチンを作成しました。',
'Alter function' => '関数の変更',
'Alter procedure' => 'プロシージャの変更',
'Return type' => '戻り値の型',
'Add trigger' => 'トリガーの追加',
'Trigger has been dropped.' => 'トリガーを削除しました',
'Trigger has been altered.' => 'トリガーを変更しました',
'Trigger has been created.' => 'トリガーを追加しました',
'Trigger has been dropped.' => 'トリガーを削除しました',
'Trigger has been altered.' => 'トリガーを変更しました',
'Trigger has been created.' => 'トリガーを追加しました',
'Alter trigger' => 'トリガーの変更',
'Create trigger' => 'トリガーの作成',
'Time' => '時間',
'Event' => 'イベント',
'%s version: %s through PHP extension %s' => '%sバージョン%s、 PHP拡張機能 %s',
'%d / ' => '%d / ',
'%d row(s)' => '%d 行',
'Remove' => '除外',
'Are you sure?' => '実行しますか?',
'Privileges' => '権限',
'Create user' => 'ユーザを作成',
'User has been dropped.' => 'ユーザを削除',
'User has been altered.' => 'ユーザを変更',
'User has been created.' => 'ユーザを作成',
'User has been dropped.' => 'ユーザを削除しました。',
'User has been altered.' => 'ユーザを変更しました。',
'User has been created.' => 'ユーザを作成しました。',
'Hashed' => 'Hashed',
'Column' => '列',
'Routine' => 'ルーチン',
'Grant' => '権限の付与',
'Revoke' => '権限の取消し',
'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 are offline.' => 'オフライン状態です。',
'Move up' => '上',
'Move down' => '下',
'Export' => 'エクスポート',
@@ -151,9 +174,9 @@ $translations = array(
'Format' => '形式',
'Functions' => '関数',
'Aggregation' => '集合',
'Event has been dropped.' => '削除しました',
'Event has been altered.' => '変更しました',
'Event has been created.' => '作成しました',
'Event has been dropped.' => 'イベントを削除しました',
'Event has been altered.' => 'イベントを変更しました',
'Event has been created.' => 'イベントを作成しました',
'Alter event' => '変更',
'Create event' => '作成',
'Start' => '開始',
@@ -164,8 +187,8 @@ $translations = array(
'Events' => 'イベント',
'Schedule' => 'スケジュール',
'At given time' => '指定時刻',
'Tables have been truncated.' => 'テーブルをtruncateしました',
'Tables have been moved.' => 'テーブルを移動しました',
'Tables have been truncated.' => 'テーブルを空にしました',
'Tables have been moved.' => 'テーブルを移動しました',
'Tables and views' => 'テーブルとビュー',
'Engine' => 'エンジン',
'Collation' => '照合順序',
@@ -177,6 +200,7 @@ $translations = array(
'0123456789' => '0123456789',
'Analyze' => '分析',
'Optimize' => '最適化',
'Vacuum' => '不要領域の回収',
'Check' => 'チェック',
'Repair' => '修復',
'Truncate' => '空にする',
@@ -184,16 +208,18 @@ $translations = array(
'Move' => '移動',
'Save and continue edit' => '保存して継続',
'original' => '元',
'%d item(s) have been affected.' => '%d を更新しました',
'%d item(s) have been affected.' => '%d を更新しました',
'Whole result' => '全結果',
'Tables have been dropped.' => 'テーブルを削除しました',
'Tables have been dropped.' => 'テーブルを削除しました',
'Tables have been optimized.' => 'テーブルを最適化しました。',
'Clone' => 'クローン',
'Maximum number of allowed fields exceeded. Please increase %s.' => '定義可能な最大フィールド数を越えました。%s を増やしてください。',
'Partition by' => 'パーティション',
'Partitions' => 'パーティション',
'Partition name' => 'パーティション名',
'Values' => '値',
'%d row(s) have been imported.' => '%d 行をインポートしました',
'%d row(s) have been imported.' => '%d 行をインポートしました',
'File must be in UTF-8 encoding.' => 'ファイルをUTF-8で保存してください。',
'Show structure' => '構造',
'anywhere' => '任意',
'Import' => 'インポート',
@@ -208,7 +234,7 @@ $translations = array(
'Relations' => '関係',
'Run file' => 'ファイルを実行',
'Clear' => '消去',
'Maximum allowed file size is %sB.' => '最大ファイルサイズ %sB',
'Maximum allowed file size is %sB.' => '最大ファイルサイズ %sB です。',
'Numbers' => '数字',
'Date and time' => '日時',
'Strings' => '文字列',
@@ -219,49 +245,51 @@ $translations = array(
'From' => '差出人',
'Subject' => '題名',
'Send' => '送信',
'%d e-mail(s) have been sent.' => '%d メールを送信しました',
'%d e-mail(s) have been sent.' => '%d メールを送信しました',
'Webserver file %s' => 'Webサーバファイル %s',
'File does not exist.' => 'ファイルは存在しません',
'File does not exist.' => 'ファイルは存在しません',
'%d in total' => '合計 %d',
'Permanent login' => '永続的にログイン',
'Databases have been dropped.' => 'データベースを削除しました',
'Databases have been dropped.' => 'データベースを削除しました',
'Search data in tables' => 'データを検索する',
'Schema' => 'スキーマ',
'Alter schema' => 'スキーマ変更',
'Create schema' => 'スキーマ追加',
'Schema has been dropped.' => 'スキーマを削除しました',
'Schema has been created.' => 'スキーマを追加しました',
'Schema has been altered.' => 'スキーマを変更しました',
'Schema has been dropped.' => 'スキーマを削除しました',
'Schema has been created.' => 'スキーマを追加しました',
'Schema has been altered.' => 'スキーマを変更しました',
'Sequences' => 'シーケンス',
'Create sequence' => 'シーケンス作成',
'Alter sequence' => 'シーケンス変更',
'Sequence has been dropped.' => 'シーケンスを削除しました',
'Sequence has been created.' => 'シーケンスを追加しました',
'Sequence has been altered.' => 'シーケンスを変更しました',
'Sequence has been dropped.' => 'シーケンスを削除しました',
'Sequence has been created.' => 'シーケンスを追加しました',
'Sequence has been altered.' => 'シーケンスを変更しました',
'User types' => 'ユーザー定義型',
'Create type' => 'ユーザー定義型作成',
'Alter type' => 'ユーザー定義型変更',
'Type has been dropped.' => 'ユーザー定義型を削除しました',
'Type has been created.' => 'ユーザー定義型を追加しました',
'Use edit link to modify this value.' => 'リンクを編集する',
'Type has been dropped.' => 'ユーザー定義型を削除しました',
'Type has been created.' => 'ユーザー定義型を追加しました',
'Ctrl+click on a value to modify it.' => 'Ctrl+クリックで値を修正します。',
'Use edit link to modify this value.' => 'この値を修正するとリンクを編集します。',
'last' => '最終',
'From server' => 'サーバーから実行',
'System' => 'データベース種類',
'empty' => '空',
'Network' => 'ネットワーク型',
'Geometry' => 'ジオメトリ型',
'File exists.' => 'ファイルが既に存在します',
'File exists.' => 'ファイルが既に存在します',
'Attachments' => '添付ファイル',
'Item%s has been inserted.' => '%s項目を挿入しました',
'Item%s has been inserted.' => '%s項目を挿入しました',
'now' => '現在の日時',
'%d query(s) executed OK.' => '%d クエリーを実行しました',
'%d query(s) executed OK.' => '%d クエリーを実行しました',
'Show only errors' => 'エラーのみ表示',
'Refresh' => 'リフレッシュ',
'Invalid schema.' => '無効なスキーマ',
'Please use one of the extensions %s.' => 'いずれかの拡張機能を使ってください %s',
'Invalid schema.' => '無効なスキーマです。',
'Please use one of the extensions %s.' => '%s のいずれかの拡張機能を使ってください',
'ltr' => 'ltr',
'Tables have been copied.' => 'テーブルをコピーしました',
'Tables have been copied.' => 'テーブルをコピーしました',
'Copy' => 'コピー',
'overwrite' => '上書き',
'Permanent link' => 'パーマネントリンク',
'Edit all' => 'すべて編集',
'HH:MM:SS' => '時:分:秒',
@@ -275,4 +303,14 @@ $translations = array(
'yes' => 'はい',
'no' => 'いいえ',
'Default value' => '既定値',
// Table check constraints
'Checks' => 'チェック',
'Create check' => 'チェックを作成',
'Alter check' => 'チェックを変更',
'Check has been created.' => 'チェックを作成しました。',
'Check has been altered.' => 'チェックを変更しました。',
'Check has been dropped.' => 'チェックを削除しました。',
);
// run `php ../../lang.php ja` to update this file

View File

@@ -302,3 +302,5 @@ $translations = array(
'Database does not support password.' => 'ბაზაში არაა მხარდაჭერილი პაროლი.',
'Disable %s or enable %s or %s extensions.' => 'გათიშეთ %s ან ჩართეთ %s ან %s გაფართოება.',
);
// run `php ../../lang.php ka` to update this file

View File

@@ -279,3 +279,5 @@ $translations = array(
'You can upload a big SQL file via FTP and import it from server.' => '큰 SQL 파일은 FTP를 통하여 업로드하여 서버에서 가져올 수 있습니다.',
'You have no privileges to update this table.' => '이 테이블을 업데이트할 권한이 없습니다.',
);
// run `php ../../lang.php ko` to update this file

View File

@@ -311,3 +311,5 @@ $translations = array(
'Type has been created.' => 'Tipas sukurtas.',
'Alter type' => 'Keisti tipą',
);
// run `php ../../lang.php lt` to update this file

View File

@@ -243,7 +243,7 @@ $translations = array(
'Type has been dropped.' => 'Tips dzēsts.',
'Type has been created.' => 'Tips izveidots.',
'Ctrl+click on a value to modify it.' => 'Lai izmainītu vērtību, izmanto Ctrl + peles klikšķi.',
'Use edit link to modify this value.' => 'Izmainīt vērtību var tikai ar saiti "Izmainīt".',
'Use edit link to modify this value.' => 'Izmainīt vērtību var tikai ar saiti \'Izmainīt\'.',
'last' => 'pēdējā',
'From server' => 'No servera',
'System' => 'Sistēma',
@@ -302,3 +302,5 @@ $translations = array(
'Unknown error.' => 'Nezināma kļūda.',
'Database does not support password.' => 'Datubāze neatbalsta paroli.',
);
// run `php ../../lang.php lv` to update this file

View File

@@ -339,3 +339,5 @@ $translations = array(
'Type has been created.' => 'Jenis telah dibuat.',
'Alter type' => 'Ubah jenis',
);
// run `php ../../lang.php ms` to update this file

View File

@@ -243,7 +243,7 @@ $translations = array(
'Type has been dropped.' => 'Type verwijderd.',
'Type has been created.' => 'Type aangemaakt.',
'Ctrl+click on a value to modify it.' => 'Ctrl+klik op een waarde om deze te bewerken.',
'Use edit link to modify this value.' => 'Gebruik de link "bewerk" om deze waarde te wijzigen.',
'Use edit link to modify this value.' => 'Gebruik de link \'bewerk\' om deze waarde te wijzigen.',
'last' => 'laatste',
'From server' => 'Van server',
'System' => 'Databasesysteem',
@@ -302,3 +302,5 @@ $translations = array(
'yes' => 'ja',
'no' => 'neen',
);
// run `php ../../lang.php nl` to update this file

View File

@@ -281,3 +281,5 @@ $translations = array(
'Alter type' => 'Endre type',
'Saving' => 'Lagrer',
);
// run `php ../../lang.php no` to update this file

View File

@@ -13,6 +13,9 @@ $translations = array(
'Logged as: %s' => 'Zalogowany jako: %s',
'Logout successful.' => 'Wylogowano pomyślnie.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Dziękujemy za używanie Adminera, rozważ <a href="https://www.adminer.org/pl/donation/">dotację</a>.',
'Loaded plugins' => 'Wczytane wtyczki',
'%s must <a%s>return an array</a>.' => '%s musi <a%s>zwrócić tablicę</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Skonfiguruj</a> %s w %s.',
'Invalid credentials.' => 'Nieprawidłowe dane logowania.',
'There is a space in the input password which might be the cause.' => 'W haśle wejściowym znajduje się spacja, która może być przyczyną.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nie obsługuje dostępu do bazy danych bez hasła, <a href="https://www.adminer.org/pl/password/"%s>więcej informacji</a>.',
@@ -356,3 +359,5 @@ $translations = array(
'Check has been altered.' => 'Kontrola została zmieniona.',
'Check has been dropped.' => 'Kontrola została usunięta.',
);
// run `php ../../lang.php pl` to update this file

View File

@@ -262,3 +262,5 @@ $translations = array(
'now' => 'agora',
'ltr' => 'ltr',
);
// run `php ../../lang.php pt-br` to update this file

View File

@@ -262,3 +262,5 @@ $translations = array(
'now' => 'agora',
'ltr' => 'ltr',
);
// run `php ../../lang.php pt` to update this file

View File

@@ -267,3 +267,5 @@ $translations = array(
'Edit all' => 'Editează tot',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php ro` to update this file

View File

@@ -303,3 +303,5 @@ $translations = array(
'Database does not support password.' => 'База данных не поддерживает пароль.',
'Disable %s or enable %s or %s extensions.' => 'Отключите %s или включите расширения %s или %s.',
);
// run `php ../../lang.php ru` to update this file

View File

@@ -309,3 +309,5 @@ $translations = array(
'Check has been altered.' => 'Kontrola bola zmenená.',
'Check has been dropped.' => 'Kontrola bola odstránená.',
);
// run `php ../../lang.php sk` to update this file

View File

@@ -306,3 +306,5 @@ $translations = array(
'Type has been created.' => 'Tip je ustvarjen.',
'Alter type' => 'Spremeni tip',
);
// run `php ../../lang.php sl` to update this file

View File

@@ -318,3 +318,5 @@ $translations = array(
'Type has been created.' => 'тип је креиран.',
'Alter type' => 'Уреди тип',
);
// run `php ../../lang.php sr` to update this file

View File

@@ -348,3 +348,5 @@ $translations = array(
'Type has been created.' => 'Typ har skapats.',
'Alter type' => 'Ändra typ',
);
// run `php ../../lang.php sv` to update this file

View File

@@ -266,3 +266,5 @@ $translations = array(
'Edit all' => 'அனைத்தையும் தொகு',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php ta` to update this file

View File

@@ -267,3 +267,5 @@ $translations = array(
'Edit all' => 'แก้ไขทั้งหมด',
'HH:MM:SS' => 'HH:MM:SS',
);
// run `php ../../lang.php th` to update this file

View File

@@ -342,3 +342,5 @@ $translations = array(
'Type has been created.' => 'Tür oluşturuldu.',
'Alter type' => 'Türü değiştir',
);
// run `php ../../lang.php tr` to update this file

View File

@@ -345,3 +345,5 @@ $translations = array(
'Unknown error.' => 'Невідома помилка.',
'Database does not support password.' => 'База даних не підтримує пароль.',
);
// run `php ../../lang.php uk` to update this file

365
adminer/lang/uz.inc.php Normal file
View File

@@ -0,0 +1,365 @@
<?php
namespace Adminer;
$translations = array(
// label for database system selection (MySQL, SQLite, ...)
'System' => 'Tizim',
'Server' => 'Server',
'Username' => 'Foydalanuvchi nomi',
'Password' => 'Parol',
'Permanent login' => 'Doimiy kirish',
'Login' => 'Kirish',
'Logout' => 'Chiqish',
'Logged as: %s' => 'Siz kirgansiz: %s',
'Logout successful.' => 'Muvaffaqiyatli chiqdingiz.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminer dasturidan foydalanganingiz uchun rahmat, <a href="https://www.adminer.org/en/donation/">xayriya qilishni</a> o\'ylab ko\'ring.',
'Loaded plugins' => 'Yuklangan plaginlar',
'%s must <a%s>return an array</a>.' => '%s <a%s>massiv qaytarishi</a> kerak.',
'<a%s>Configure</a> %s in %s.' => '%s ni %s ichida <a%s>sozlang</a>.',
'Invalid credentials.' => 'Noto\'g\'ri ma\'lumotlar.',
'There is a space in the input password which might be the cause.' => 'Kiritilgan parolda bo\'sh joy bor, bu sabab bo\'lishi mumkin.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer parolsiz ma\'lumotlar bazasiga kirishni qo\'llab-quvvatlamaydi, <a href="https://www.adminer.org/en/password/"%s>ko\'proq ma\'lumot</a>.',
'Database does not support password.' => 'Ma\'lumotlar bazasi parolni qo\'llab-quvvatlamaydi.',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Juda ko\'p muvaffaqiyatsiz urinishlar, %d daqiqadan so\'ng qayta urining.', 'Juda ko\'p muvaffaqiyatsiz urinishlar, %d daqiqadan so\'ng qayta urining.'),
'Master password expired. <a href="https://www.adminer.org/en/extension/"%s>Implement</a> %s method to make it permanent.' => 'Asosiy parol muddati tugadi. Uni doimiy qilish uchun %s usulini <a href="https://www.adminer.org/en/extension/"%s>amalga oshiring</a>.',
'Language' => 'Til',
'Invalid CSRF token. Send the form again.' => 'Noto\'g\'ri CSRF belgisi. Shaklni qayta yuboring.',
'If you did not send this request from Adminer then close this page.' => 'Agar bu so\'rovni Adminerdan yuborgan bo\'lmasangiz, ushbu sahifani yoping.',
'No extension' => 'Kengaytma yo\'q',
// %s contains the list of the extensions, e.g. 'mysqli, PDO_MySQL'
'None of the supported PHP extensions (%s) are available.' => 'Qo\'llab-quvvatlanadigan PHP kengaytmalarining (%s) hech biri mavjud emas.',
'Connecting to privileged ports is not allowed.' => 'Imtiyozli portlarga ulanishga ruxsat berilmagan.',
'Disable %s or enable %s or %s extensions.' => '%s ni o\'chiring yoki %s yoki %s kengaytmalarini yoqing.',
'Session support must be enabled.' => 'Sessiya qo\'llab-quvvatlashi yoqilgan bo\'lishi kerak.',
'Session expired, please login again.' => 'Sessiya muddati tugadi, iltimos, qayta kiring.',
'The action will be performed after successful login with the same credentials.' => 'Amal bir xil ma\'lumotlar bilan muvaffaqiyatli kirishdan so\'ng amalga oshiriladi.',
'%s version: %s through PHP extension %s' => '%s versiyasi: %s PHP kengaytmasi %s orqali',
'Refresh' => 'Yangilash',
// text direction - 'ltr' or 'rtl'
'ltr' => 'ltr',
'Privileges' => 'Imtiyozlar',
'Create user' => 'Foydalanuvchi yaratish',
'User has been dropped.' => 'Foydalanuvchi o\'chirildi.',
'User has been altered.' => 'Foydalanuvchi o\'zgartirildi.',
'User has been created.' => 'Foydalanuvchi yaratildi.',
'Hashed' => 'Xeshlangan',
'Column' => 'Ustun',
'Routine' => 'Protsedura',
'Grant' => 'Berish',
'Revoke' => 'Bekor qilish',
'Process list' => 'Jarayonlar ro\'yxati',
'%d process(es) have been killed.' => array('%d jarayon to\'xtatildi.', '%d jarayonlar to\'xtatildi.'),
'Kill' => 'To\'xtatish',
'Variables' => 'O\'zgaruvchilar',
'Status' => 'Holat',
'SQL command' => 'SQL buyrug\'i',
'%d query(s) executed OK.' => array('%d so\'rov muvaffaqiyatli bajarildi.', '%d so\'rovlar muvaffaqiyatli bajarildi.'),
'Query executed OK, %d row(s) affected.' => array('So\'rov muvaffaqiyatli bajarildi, %d qator o\'zgartirildi.', 'So\'rov muvaffaqiyatli bajarildi, %d qatorlar o\'zgartirildi.'),
'No commands to execute.' => 'Bajariladigan buyruqlar yo\'q.',
'Error in query' => 'So\'rovda xatolik',
'Unknown error.' => 'Noma\'lum xatolik.',
'Warnings' => 'Ogohlantirishlar',
'ATTACH queries are not supported.' => 'ATTACH so\'rovlari qo\'llab-quvvatlanmaydi.',
'Execute' => 'Bajarish',
'Stop on error' => 'Xatoda to\'xtash',
'Show only errors' => 'Faqat xatolarni ko\'rsatish',
// sprintf() format for time of the command
'%.3f s' => '%.3f s',
'History' => 'Tarix',
'Clear' => 'Tozalash',
'Edit all' => 'Hammasini tahrirlash',
'File upload' => 'Fayl yuklash',
'From server' => 'Serverdan',
'Webserver file %s' => 'Veb-server fayli %s',
'Run file' => 'Faylni ishga tushirish',
'File does not exist.' => 'Fayl mavjud emas.',
'File uploads are disabled.' => 'Fayl yuklash o\'chirilgan.',
'Unable to upload a file.' => 'Faylni yuklab bo\'lmadi.',
'Maximum allowed file size is %sB.' => 'Maksimal ruxsat etilgan fayl hajmi %sB.',
'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'Juda katta POST ma\'lumotlari. Ma\'lumotlarni kamaytiring yoki %s konfiguratsiya direktivasini oshiring.',
'You can upload a big SQL file via FTP and import it from server.' => 'Katta SQL faylini FTP orqali yuklab, uni serverdan import qilishingiz mumkin.',
'You are offline.' => 'Siz oflayndasiz.',
'Export' => 'Eksport',
'Output' => 'Natija',
'open' => 'ochish',
'save' => 'saqlash',
'Saving' => 'Saqlanmoqda',
'Format' => 'Format',
'Data' => 'Ma\'lumotlar',
'Database' => 'Ma\'lumotlar bazasi',
'DB' => 'MB',
'Use' => 'Foydalanish',
'Select database' => 'Ma\'lumotlar bazasini tanlash',
'Invalid database.' => 'Noto\'g\'ri ma\'lumotlar bazasi.',
'Database has been dropped.' => 'Ma\'lumotlar bazasi o\'chirildi.',
'Databases have been dropped.' => 'Ma\'lumotlar bazalari o\'chirildi.',
'Database has been created.' => 'Ma\'lumotlar bazasi yaratildi.',
'Database has been renamed.' => 'Ma\'lumotlar bazasi qayta nomlandi.',
'Database has been altered.' => 'Ma\'lumotlar bazasi o\'zgartirildi.',
'Alter database' => 'Ma\'lumotlar bazasini o\'zgartirish',
'Create database' => 'Ma\'lumotlar bazasini yaratish',
'Database schema' => 'Ma\'lumotlar bazasi sxemasi',
// link to current database schema layout
'Permanent link' => 'Doimiy havola',
// thousands separator - must contain single byte
',' => ' ',
'0123456789' => '0123456789',
'Engine' => 'Dvigatel',
'Collation' => 'Kodlash',
'Data Length' => 'Ma\'lumotlar hajmi',
'Index Length' => 'Indeks hajmi',
'Data Free' => 'Bo\'sh ma\'lumotlar',
'Rows' => 'Qatorlar',
'%d in total' => 'Jami %d',
'Analyze' => 'Tahlil qilish',
'Optimize' => 'Optimallash',
'Vacuum' => 'Tozalash',
'Check' => 'Tekshirish',
'Repair' => 'Ta\'mirlash',
'Truncate' => 'Bo\'shatish',
'Tables have been truncated.' => 'Jadvallar bo\'shatildi.',
'Move to other database' => 'Boshqa ma\'lumotlar bazasiga ko\'chirish',
'Move' => 'Ko\'chirish',
'Tables have been moved.' => 'Jadvallar ko\'chirildi.',
'Copy' => 'Nusxalash',
'Tables have been copied.' => 'Jadvallar nusxalandi.',
'overwrite' => 'qayta yozish',
'Routines' => 'Protseduralar',
'Routine has been called, %d row(s) affected.' => array('Protsedura chaqirildi, %d qator o\'zgartirildi.', 'Protsedura chaqirildi, %d qatorlar o\'zgartirildi.'),
'Call' => 'Chaqirish',
'Parameter name' => 'Parametr nomi',
'Create procedure' => 'Protsedura yaratish',
'Create function' => 'Funksiya yaratish',
'Routine has been dropped.' => 'Protsedura o\'chirildi.',
'Routine has been altered.' => 'Protsedura o\'zgartirildi.',
'Routine has been created.' => 'Protsedura yaratildi.',
'Alter function' => 'Funksiyani o\'zgartirish',
'Alter procedure' => 'Protseduranni o\'zgartirish',
'Return type' => 'Qaytarish turi',
'Events' => 'Hodisalar',
'Event has been dropped.' => 'Hodisa o\'chirildi.',
'Event has been altered.' => 'Hodisa o\'zgartirildi.',
'Event has been created.' => 'Hodisa yaratildi.',
'Alter event' => 'Hodisani o\'zgartirish',
'Create event' => 'Hodisa yaratish',
'At given time' => 'Belgilangan vaqtda',
'Every' => 'Har bir',
'Schedule' => 'Jadval',
'Start' => 'Boshlash',
'End' => 'Tugatish',
'On completion preserve' => 'Yakunlangandan so\'ng saqlash',
'Tables' => 'Jadvallar',
'Tables and views' => 'Jadvallar va ko\'rinishlar',
'Table' => 'Jadval',
'No tables.' => 'Jadvallar yo\'q.',
'Alter table' => 'Jadvalni o\'zgartirish',
'Create table' => 'Jadval yaratish',
'Table has been dropped.' => 'Jadval o\'chirildi.',
'Tables have been dropped.' => 'Jadvallar o\'chirildi.',
'Tables have been optimized.' => 'Jadvallar optimallashtirildi.',
'Table has been altered.' => 'Jadval o\'zgartirildi.',
'Table has been created.' => 'Jadval yaratildi.',
'Table name' => 'Jadval nomi',
'Show structure' => 'Tuzilishni ko\'rsatish',
'engine' => 'dvigatel',
'collation' => 'kodlash',
'Column name' => 'Ustun nomi',
'Type' => 'Tur',
'Length' => 'Uzunlik',
'Auto Increment' => 'Avto ko\'payish',
'Options' => 'Variantlar',
'Comment' => 'Izoh',
'Default value' => 'Standart qiymat',
'Default values' => 'Standart qiymatlar',
'Drop' => 'O\'chirish',
'Drop %s?' => '%s ni o\'chirasizmi?',
'Are you sure?' => 'Ishonchingiz komilmi?',
'Size' => 'Hajm',
'Compute' => 'Hisoblash',
'Move up' => 'Yuqoriga ko\'chirish',
'Move down' => 'Pastga ko\'chirish',
'Remove' => 'Olib tashlash',
'Maximum number of allowed fields exceeded. Please increase %s.' => 'Ruxsat etilgan maydonlar soni oshib ketdi. Iltimos, %s ni oshiring.',
'Partition by' => 'Bo\'lish mezon',
'Partitions' => 'Bo\'limlar',
'Partition name' => 'Bo\'lim nomi',
'Values' => 'Qiymatlar',
'View' => 'Ko\'rinish',
'Materialized view' => 'Moddiy ko\'rinish',
'View has been dropped.' => 'Ko\'rinish o\'chirildi.',
'View has been altered.' => 'Ko\'rinish o\'zgartirildi.',
'View has been created.' => 'Ko\'rinish yaratildi.',
'Alter view' => 'Ko\'rinishni o\'zgartirish',
'Create view' => 'Ko\'rinish yaratish',
'Indexes' => 'Indekslar',
'Indexes have been altered.' => 'Indekslar o\'zgartirildi.',
'Alter indexes' => 'Indekslarni o\'zgartirish',
'Add next' => 'Keyingisini qo\'shish',
'Index Type' => 'Indeks turi',
'length' => 'uzunlik',
'Foreign keys' => 'Tashqi kalitlar',
'Foreign key' => 'Tashqi kalit',
'Foreign key has been dropped.' => 'Tashqi kalit o\'chirildi.',
'Foreign key has been altered.' => 'Tashqi kalit o\'zgartirildi.',
'Foreign key has been created.' => 'Tashqi kalit yaratildi.',
'Target table' => 'Maqsad jadvali',
'Change' => 'O\'zgartirish',
'Source' => 'Manba',
'Target' => 'Maqsad',
'Add column' => 'Ustun qo\'shish',
'Alter' => 'O\'zgartirish',
'Add foreign key' => 'Tashqi kalit qo\'shish',
'ON DELETE' => 'O\'CHIRILGANDA',
'ON UPDATE' => 'YANGILANGANDA',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Manba va maqsad ustunlari bir xil ma\'lumot turiga ega bo\'lishi kerak, maqsad ustunlarda indeks bo\'lishi kerak va havolalar qilingan ma\'lumotlar mavjud bo\'lishi kerak.',
'Triggers' => 'Triggerlar',
'Add trigger' => 'Trigger qo\'shish',
'Trigger has been dropped.' => 'Trigger o\'chirildi.',
'Trigger has been altered.' => 'Trigger o\'zgartirildi.',
'Trigger has been created.' => 'Trigger yaratildi.',
'Alter trigger' => 'Triggerni o\'zgartirish',
'Create trigger' => 'Trigger yaratish',
'Time' => 'Vaqt',
'Event' => 'Hodisa',
'Name' => 'Nomi',
'select' => 'tanlash',
'Select' => 'Tanlash',
'Select data' => 'Ma\'lumotlarni tanlash',
'Functions' => 'Funksiyalar',
'Aggregation' => 'Agregatsiya',
'Search' => 'Qidirish',
'anywhere' => 'hamma joyda',
'Search data in tables' => 'Jadvallarda ma\'lumotlarni qidirish',
'Sort' => 'Saralash',
'descending' => 'kamayish bo\'yicha',
'Limit' => 'Cheklov',
'Limit rows' => 'Qatorlarni cheklash',
'Text length' => 'Matn uzunligi',
'Action' => 'Amal',
'Full table scan' => 'To\'liq jadval skanerlash',
'Unable to select the table' => 'Jadvalni tanlab bo\'lmadi',
'No rows.' => 'Qatorlar yo\'q.',
// used in SQL query limit and it is followed by another number, e.g. '10 / 50 rows' meaning 10 of 50 rows
'%d / ' => '%d / ',
'%d row(s)' => array('%d qator', '%d qatorlar'),
'Page' => 'Sahifa',
'last' => 'oxirgi',
'Load more data' => 'Ko\'proq ma\'lumot yuklash',
'Loading' => 'Yuklanmoqda',
'Whole result' => 'Butun natija',
'%d byte(s)' => array('%d bayt', '%d baytlar'),
'Import' => 'Import',
'%d row(s) have been imported.' => array('%d qator import qilindi.', '%d qatorlar import qilindi.'),
'File must be in UTF-8 encoding.' => 'Fayl UTF-8 kodlashda bo\'lishi kerak.',
// in-place editing in select
'Modify' => 'O\'zgartirish',
'Ctrl+click on a value to modify it.' => 'Qiymatni o\'zgartirish uchun Ctrl+bosing.',
'Use edit link to modify this value.' => 'Bu qiymatni o\'zgartirish uchun tahrir havolasidan foydalaning.',
// %s can contain auto-increment value, e.g. ' 123'
'Item%s has been inserted.' => 'Element%s kiritildi.',
'Item has been deleted.' => 'Element o\'chirildi.',
'Item has been updated.' => 'Element yangilandi.',
'%d item(s) have been affected.' => array('%d element o\'zgartirildi.', '%d elementlar o\'zgartirildi.'),
'New item' => 'Yangi element',
'original' => 'asl',
// label for value '' in enum data type
'empty' => 'bo\'sh',
'edit' => 'tahrirlash',
'Edit' => 'Tahrirlash',
'Insert' => 'Kiritish',
'Save' => 'Saqlash',
'Save and continue edit' => 'Saqlash va tahrirlashni davom ettirish',
'Save and insert next' => 'Saqlash va keyingisini kiritish',
'Selected' => 'Tanlangan',
'Clone' => 'Klonlash',
'Delete' => 'O\'chirish',
'You have no privileges to update this table.' => 'Bu jadvalni yangilash uchun sizda huquqlar yo\'q.',
'E-mail' => 'E-pochta',
'From' => 'Kimdan',
'Subject' => 'Mavzu',
'Attachments' => 'Ilovalar',
'Send' => 'Yuborish',
'%d e-mail(s) have been sent.' => array('%d e-pochta yuborildi.', '%d e-pochtalar yuborildi.'),
// data type descriptions
'Numbers' => 'Raqamlar',
'Date and time' => 'Sana va vaqt',
'Strings' => 'Matnlar',
'Binary' => 'Ikkilik',
'Lists' => 'Ro\'yxatlar',
'Network' => 'Tarmoq',
'Geometry' => 'Geometriya',
'Relations' => 'Munosabatlar',
'Editor' => 'Muharrir',
// 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' => 'SS:MM:SS',
'now' => 'hozir',
'yes' => 'ha',
'no' => 'yo\'q',
// general SQLite error in create, drop or rename database
'File exists.' => 'Fayl mavjud.',
'Please use one of the extensions %s.' => 'Iltimos, kengaytmalardan birini %s foydalaning.',
// PostgreSQL and MS SQL schema support
'Alter schema' => 'Sxemani o\'zgartirish',
'Create schema' => 'Sxema yaratish',
'Schema has been dropped.' => 'Sxema o\'chirildi.',
'Schema has been created.' => 'Sxema yaratildi.',
'Schema has been altered.' => 'Sxema o\'zgartirildi.',
'Schema' => 'Sxema',
'Invalid schema.' => 'Noto\'g\'ri sxema.',
// PostgreSQL sequences support
'Sequences' => 'Ketma-ketliklar',
'Create sequence' => 'Ketma-ketlik yaratish',
'Sequence has been dropped.' => 'Ketma-ketlik o\'chirildi.',
'Sequence has been created.' => 'Ketma-ketlik yaratildi.',
'Sequence has been altered.' => 'Ketma-ketlik o\'zgartirildi.',
'Alter sequence' => 'Ketma-ketlikni o\'zgartirish',
// PostgreSQL user types support
'User types' => 'Foydalanuvchi turlari',
'Create type' => 'Tur yaratish',
'Type has been dropped.' => 'Tur o\'chirildi.',
'Type has been created.' => 'Tur yaratildi.',
'Alter type' => 'Turni o\'zgartirish',
// Table check constraints
'Checks' => 'Tekshirishlar',
'Create check' => 'Tekshirish yaratish',
'Alter check' => 'Tekshirishni o\'zgartirish',
'Check has been created.' => 'Tekshirish yaratildi.',
'Check has been altered.' => 'Tekshirish o\'zgartirildi.',
'Check has been dropped.' => 'Tekshirish o\'chirildi.',
);
// run `php ../../lang.php uz` to update this file

View File

@@ -327,3 +327,5 @@ $translations = array(
'Type has been created.' => 'Đã tạo kiểu.',
'Alter type' => 'Sửa kiểu dữ liệu',
);
// run `php ../../lang.php vi` to update this file

View File

@@ -13,6 +13,9 @@ $translations = array(
'Logged as: %s' => 'Xx: %s',
'Logout successful.' => 'Xx.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Xx <a href="https://www.adminer.org/en/donation/">xx</a>.',
'Loaded plugins' => 'Xx',
'%s must <a%s>return an array</a>.' => '%s xx <a%s>xx</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Xx</a> %s xx %s.',
'Invalid credentials.' => 'Xx.',
'There is a space in the input password which might be the cause.' => 'Xx.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Xx, <a href="https://www.adminer.org/en/password/"%s>xx</a>.',
@@ -358,3 +361,5 @@ $translations = array(
'Check has been altered.' => 'Xx.',
'Check has been dropped.' => 'Xx.',
);
// run `php ../../lang.php xx` to update this file

View File

@@ -348,3 +348,5 @@ $translations = array(
'Type has been created.' => '已建立類型。',
'Alter type' => '修改類型',
);
// run `php ../../lang.php zh-tw` to update this file

View File

@@ -348,3 +348,5 @@ $translations = array(
'Type has been created.' => '已创建类型。',
'Alter type' => '修改类型',
);
// run `php ../../lang.php zh` to update this file

View File

@@ -1,44 +0,0 @@
<?php
function adminer_object() {
// required to run any plugin
include_once "../plugins/plugin.php";
// autoloader
foreach (glob("../plugins/*.php") as $filename) {
include_once $filename;
}
// enable extra drivers just by including them
//~ include "../plugins/drivers/simpledb.php";
$plugins = array(
// specify enabled plugins here
new AdminerDatabaseHide(array('information_schema')),
new AdminerDumpJson,
new AdminerDumpBz2,
new AdminerDumpZip,
new AdminerDumpXml,
new AdminerDumpAlter,
//~ new AdminerSqlLog("past-" . rtrim(`git describe --tags --abbrev=0`) . ".sql"),
//~ new AdminerTinymce("../externals/tinymce/jscripts/tiny_mce/tiny_mce_dev.js"),
new AdminerFileUpload(""),
new AdminerJsonColumn,
new AdminerSlugify,
new AdminerTranslation,
new AdminerForeignSystem,
new AdminerEnumOption,
new AdminerTablesFilter,
new AdminerEditForeign,
);
/* It is possible to combine customization and plugins:
class AdminerCustomization extends AdminerPlugin {
}
return new AdminerCustomization($plugins);
*/
return new AdminerPlugin($plugins);
}
// include original Adminer or Adminer Editor (usually named adminer.php)
include "./index.php";

View File

@@ -14,8 +14,8 @@ if (!$result) {
echo "<form action=''><p>\n";
hidden_fields_get();
echo "<input type='hidden' name='db' value='" . h(DB) . "'>\n";
echo ($grant ? "" : "<input type='hidden' name='grant' value=''>\n");
echo input_hidden("db", DB);
echo ($grant ? "" : input_hidden("grant"));
echo "<table class='odds'>\n";
echo "<thead><tr><th>" . lang('Username') . "<th>" . lang('Server') . "<th></thead>\n";

View File

@@ -9,6 +9,11 @@ $row["fields"] = (array) $row["fields"];
if ($_POST && !process_fields($row["fields"]) && !$error) {
$orig = routine($_GET["procedure"], $routine);
$temp_name = "$row[name]_adminer_" . uniqid();
foreach ($row["fields"] as $key => $field) {
if ($field["field"] == "") {
unset($row["fields"][$key]);
}
}
drop_create(
"DROP $routine " . routine_id($PROCEDURE, $orig),
create_routine($routine, $row),
@@ -26,9 +31,13 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
page_header(($PROCEDURE != "" ? (isset($_GET["function"]) ? lang('Alter function') : lang('Alter procedure')) . ": " . h($PROCEDURE) : (isset($_GET["function"]) ? lang('Create function') : lang('Create procedure'))), $error);
if (!$_POST && $PROCEDURE != "") {
$row = routine($_GET["procedure"], $routine);
$row["name"] = $PROCEDURE;
if (!$_POST) {
if ($PROCEDURE == "") {
$row["language"] = "sql";
} else {
$row = routine($_GET["procedure"], $routine);
$row["name"] = $PROCEDURE;
}
}
$collations = get_vals("SHOW CHARACTER SET");
@@ -59,5 +68,5 @@ if (isset($_GET["function"])) {
<?php if ($PROCEDURE != "") { ?>
<input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"><?php echo confirm(lang('Drop %s?', $PROCEDURE)); ?>
<?php } ?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
<?php echo input_token(); ?>
</form>

View File

@@ -56,7 +56,7 @@ if (support("kill")) {
echo ($i + 1) . "/" . lang('%d in total', max_connections());
echo "<p><input type='submit' value='" . lang('Kill') . "'>\n";
}
echo input_token();
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>
<?php echo script("tableCheck();"); ?>

View File

@@ -52,9 +52,9 @@ foreach (table_status('', true) as $table => $table_status) {
?>
<div id="schema" style="height: <?php echo $top; ?>em;">
<script<?php echo nonce(); ?>>
qs('#schema').onselectstart = function () { return false; };
var tablePos = {<?php echo implode(",", $table_pos_js) . "\n"; ?>};
var em = qs('#schema').offsetHeight / <?php echo $top; ?>;
qs('#schema').onselectstart = () => false;
const tablePos = {<?php echo implode(",", $table_pos_js) . "\n"; ?>};
const em = qs('#schema').offsetHeight / <?php echo $top; ?>;
document.onmousemove = schemaMousemove;
document.onmouseup = partialArg(schemaMouseup, '<?php echo js_escape(DB); ?>');
</script>
@@ -74,7 +74,9 @@ foreach ($schema as $name => $table) {
$left1 = $left - $table_pos[$name][1];
$i = 0;
foreach ($ref[0] as $source) {
echo "\n<div class='references' title='" . h($target_name) . "' id='refs$left-" . ($i++) . "' style='left: $left1" . "em; top: " . $table["fields"][$source]["pos"] . "em; padding-top: .5em;'><div style='border-top: 1px solid Gray; width: " . (-$left1) . "em;'></div></div>";
echo "\n<div class='references' title='" . h($target_name) . "' id='refs$left-" . ($i++) . "' style='left: $left1" . "em; top: " . $table["fields"][$source]["pos"] . "em; padding-top: .5em;'>"
. "<div style='border-top: 1px solid gray; width: " . (-$left1) . "em;'></div></div>"
;
}
}
}
@@ -84,8 +86,9 @@ foreach ($schema as $name => $table) {
$left1 = $left - $table_pos[$name][1];
$i = 0;
foreach ($columns as $target) {
echo "\n<div class='references' title='" . h($target_name) . "' id='refd$left-" . ($i++) . "' style='left: $left1" . "em; top: " . $table["fields"][$target]["pos"] . "em; height: 1.25em; background: url(../adminer/static/arrow.gif) no-repeat right center;'>"
. "<div style='height: .5em; border-bottom: 1px solid Gray; width: " . (-$left1) . "em;'></div>"
echo "\n<div class='references' title='" . h($target_name) . "' id='refd$left-" . ($i++) . "'"
. " style='left: $left1" . "em; top: " . $table["fields"][$target]["pos"] . "em; height: 1.25em; background: url(../adminer/static/arrow.gif) no-repeat right center;'>"
. "<div style='height: .5em; border-bottom: 1px solid gray; width: " . (-$left1) . "em;'></div>"
. "</div>"
;
}
@@ -106,7 +109,7 @@ foreach ($schema as $name => $table) {
$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

@@ -34,6 +34,6 @@ if (!$row) {
if ($_GET["ns"] != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', $_GET["ns"])) . "\n";
}
echo input_token();
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -25,7 +25,7 @@ if ($_GET["script"] == "db") {
$sums[$key] += ($table_status["Engine"] != "InnoDB" || $key != "Data_free" ? $table_status[$key] : 0);
}
} elseif (array_key_exists($key, $table_status)) {
json_row("$key-$name");
json_row("$key-$name", "?");
}
}
}

View File

@@ -7,7 +7,7 @@ $indexes = indexes($TABLE);
$fields = fields($TABLE);
$foreign_keys = column_foreign_keys($TABLE);
$oid = $table_status["Oid"];
parse_str($_COOKIE["adminer_import"], $adminer_import);
$adminer_import = get_settings("adminer_import");
$rights = array(); // privilege => 0
$columns = array(); // selectable columns
@@ -83,7 +83,7 @@ if ($_POST && !$error) {
}
$where_check = ($where_check ? "\nWHERE " . implode(" AND ", $where_check) : "");
if ($_POST["export"]) {
cookie("adminer_import", "output=" . urlencode($_POST["output"]) . "&format=" . urlencode($_POST["format"]));
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers($TABLE);
$adminer->dumpTable($TABLE, "");
$from = ($select ? implode(", ", $select) : "*")
@@ -125,11 +125,11 @@ if ($_POST && !$error) {
$result = ($_POST["delete"]
? $driver->delete($TABLE, $where_check)
: ($_POST["clone"]
? queries("INSERT $query$where_check")
? queries("INSERT $query$where_check" . $driver->insertReturning($TABLE))
: $driver->update($TABLE, $set, $where_check)
)
);
$affected = $connection->affected_rows;
$affected = $connection->affected_rows + (is_object($result) ? $result->num_rows : 0); // PostgreSQL with RETURNING fills num_rows
} else {
foreach ((array) $_POST["check"] as $val) {
// where is not unique so OR can't be used
@@ -150,7 +150,7 @@ if ($_POST && !$error) {
}
$message = lang('%d item(s) have been affected.', $affected);
if ($_POST["clone"] && $result && $affected == 1) {
$last_id = last_id();
$last_id = last_id($result);
if ($last_id) {
$message = lang('Item%s has been inserted.', " $last_id");
}
@@ -195,7 +195,7 @@ if ($_POST && !$error) {
} elseif (!preg_match('~~u', $file)) {
$error = lang('File must be in UTF-8 encoding.');
} else {
cookie("adminer_import", "output=" . urlencode($adminer_import["output"]) . "&format=" . urlencode($_POST["separator"]));
save_settings(array("output" => $adminer_import["output"], "format" => $_POST["separator"]), "adminer_import");
$result = true;
$cols = array_keys($fields);
preg_match_all('~(?>"[^"]*"|[^"\r\n]+)+~', $file, $matches);
@@ -258,8 +258,8 @@ if (!$columns && support("table")) {
echo "<form action='' id='form'>\n";
echo "<div style='display: none;'>";
hidden_fields_get();
echo (DB != "" ? '<input type="hidden" name="db" value="' . h(DB) . '">' . (isset($_GET["ns"]) ? '<input type="hidden" name="ns" value="' . h($_GET["ns"]) . '">' : "") : ""); // not used in Editor
echo '<input type="hidden" name="select" value="' . h($TABLE) . '">';
echo (DB != "" ? input_hidden("db", DB) . (isset($_GET["ns"]) ? input_hidden("ns", $_GET["ns"]) : "") : ""); // not used in Editor
echo input_hidden("select", $TABLE);
echo "</div>\n";
$adminer->selectColumnsPrint($select, $columns);
$adminer->selectSearchPrint($where, $search_columns, $indexes);
@@ -452,8 +452,8 @@ if (!$columns && support("table")) {
$id = h("val[$unique_idf][" . bracket_escape($key) . "]");
$value = $_POST["val"][$unique_idf][bracket_escape($key)];
$editable = !is_array($row[$key]) && is_utf8($val) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"];
$text = preg_match('~text|lob~', $field["type"]);
echo "<td id='$id'";
$text = preg_match('~text|json|lob~', $field["type"]);
echo "<td id='$id'" . (preg_match(number_type(), $field["type"]) && is_numeric(strip_tags($val)) ? " class='number'" : "");
if (($_GET["modify"] && $editable) || $value !== null) {
$h_value = h($value !== null ? $value : $row[$key]);
echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>");
@@ -491,7 +491,7 @@ if (!$columns && support("table")) {
$found_rows = ($is_group ? false : found_rows($table_status, $where));
if ($found_rows < max(1e4, 2 * ($page + 1) * $limit)) {
// slow with big tables
$found_rows = reset(slow_query(count_rows($TABLE, $where, $is_group, $group)));
$found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group)));
} else {
$exact_count = false;
}
@@ -544,7 +544,7 @@ if (!$columns && support("table")) {
echo "<fieldset>";
echo "<legend>" . lang('Whole result') . "</legend>";
$display_rows = ($exact_count ? "" : "~ ") . $found_rows;
$onclick = "var checked = formChecked(this, /check/); selectCount('selected', this.checked ? '$display_rows' : checked); selectCount('selected2', this.checked || !checked ? '$display_rows' : checked);";
$onclick = "const checked = formChecked(this, /check/); selectCount('selected', this.checked ? '$display_rows' : checked); selectCount('selected2', this.checked || !checked ? '$display_rows' : checked);";
echo checkbox("all", 1, 0, ($found_rows !== false ? ($exact_count ? "" : "~ ") . lang('%d row(s)', $found_rows) : ""), $onclick) . "\n";
echo "</fieldset>\n";
@@ -594,7 +594,7 @@ if (!$columns && support("table")) {
echo "</div>";
}
echo "<input type='hidden' name='token' value='$token'>\n";
echo input_token();
echo "</form>\n";
echo (!$group && $select ? "" : script("tableCheck();"));
}

View File

@@ -32,6 +32,6 @@ if (!$row) {
if ($SEQUENCE != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', $SEQUENCE)) . "\n";
}
echo input_token();
?>
<input type="hidden" name="token" value="<?php echo $token; ?>">
</form>

View File

@@ -2,6 +2,7 @@
namespace Adminer;
if (!$error && $_POST["export"]) {
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers("sql");
$adminer->dumpTable("", "");
$adminer->dumpData("", "table", $_POST["query"]);
@@ -41,7 +42,7 @@ if (!$error && $_POST) {
if ($query != "" && strlen($query) < 1e6) { // don't add big queries
$q = $query . (preg_match("~;[ \t\r\n]*\$~", $query) ? "" : ";"); //! doesn't work with DELIMITER |
if (!$history || reset(end($history)) != $q) { // no repeated queries
if (!$history || first(end($history)) != $q) { // no repeated queries
restart_session();
$history[] = array($q, time()); //! add elapsed time
set_session("queries", $history_all); // required because reference is unlinked by stop_session()
@@ -64,7 +65,7 @@ if (!$error && $_POST) {
$errors = array();
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|-- |$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : '');
$total_start = microtime(true);
parse_str($_COOKIE["adminer_export"], $adminer_export);
$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"]);
@@ -89,8 +90,8 @@ if (!$error && $_POST) {
$pattern = ($found == '/*' ? '\*/'
: ($found == '[' ? ']'
: (preg_match('~^-- |^#~', $found) ? "\n"
: preg_quote($found) . ($c_style_escapes ? "|\\\\." : "")
)));
: preg_quote($found) . ($c_style_escapes ? "|\\\\." : "")))
);
while (preg_match("($pattern|\$)s", $query, $match, PREG_OFFSET_CAPTURE, $offset)) {
$s = $match[0][0];
@@ -158,7 +159,7 @@ if (!$error && $_POST) {
if (!$_POST["only_errors"]) {
echo "<form action='' method='post'>\n";
$num_rows = $result->num_rows;
echo "<p>" . ($num_rows ? ($limit && $num_rows > $limit ? lang('%d / ', $limit) : "") . lang('%d row(s)', $num_rows) : "");
echo "<p class='sql-footer'>" . ($num_rows ? ($limit && $num_rows > $limit ? lang('%d / ', $limit) : "") . lang('%d row(s)', $num_rows) : "");
echo $time;
if ($connection2 && preg_match("~^($space|\\()*+SELECT\\b~i", $q) && ($explain = explain($connection2, $q))) {
echo ", <a href='#$explain_id'>Explain</a>" . script("qsl('a').onclick = partial(toggle, '$explain_id');", "");
@@ -167,8 +168,8 @@ if (!$error && $_POST) {
echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: "
. html_select("output", $adminer->dumpOutput(), $adminer_export["output"]) . " "
. html_select("format", $dump_format, $adminer_export["format"])
. "<input type='hidden' name='query' value='" . h($q) . "'>"
. " <input type='submit' name='export' value='" . lang('Export') . "'><input type='hidden' name='token' value='$token'></span>\n"
. input_hidden("query", $q)
. "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n"
. "</form>\n"
;
}
@@ -180,7 +181,7 @@ if (!$error && $_POST) {
stop_session();
}
if (!$_POST["only_errors"]) {
echo "<p class='message' title='" . h($connection->info) . "'>" . lang('Query executed OK, %d row(s) affected.', $affected) . "$time\n";
echo "<p class='message' title='" . h(isset($connection->info) ? $connection->info : "") . "'>" . lang('Query executed OK, %d row(s) affected.', $affected) . "$time\n";
}
}
echo ($warnings ? "<div id='$warnings_id' class='hidden'>\n$warnings</div>\n" : "");
@@ -234,7 +235,9 @@ if (!isset($_GET["import"])) {
echo "<p>";
textarea("query", $q, 20);
echo script(($_POST ? "" : "qs('textarea').focus();\n") . "qs('#form').onsubmit = partial(sqlSubmit, qs('#form'), '" . js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history")) . "');");
echo "<p>$execute\n";
echo "<p>";
$adminer->sqlPrintAfter();
echo "$execute\n";
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";
} else {
@@ -257,7 +260,7 @@ if (!isset($_GET["import"])) {
echo checkbox("error_stops", 1, ($_POST ? $_POST["error_stops"] : isset($_GET["import"]) || $_GET["error_stops"]), lang('Stop on error')) . "\n";
echo checkbox("only_errors", 1, ($_POST ? $_POST["only_errors"] : isset($_GET["import"]) || $_GET["only_errors"]), lang('Show only errors')) . "\n";
echo "<input type='hidden' name='token' value='$token'>\n";
echo input_token();
if (!isset($_GET["import"]) && $history) {
print_fieldset("history", lang('History'), $_GET["history"] != "");

46
adminer/static/dark.css Normal file
View File

@@ -0,0 +1,46 @@
/** @author Robert Mesaros, https://www.rmsoft.sk */
body { color: #829bb0; background: #002240; }
a { color: #517fa8; }
a:visited { color: #517fa8; }
a:link:hover, a:visited:hover { color: #9bc0e1; }
h1 { border-color: #5e94c1; color: #ffddbf; background: #154269; }
h2 { border-color: #a3bdd3; color: #000; background: #3c678d; }
table, td, th { border-color: #0e416d; }
th { background: #11385a; }
thead td, thead th { color: #a8b05f; background: #011d35; }
thead th a { color: #a8b05f; }
fieldset { border-color: #16548a; }
code { background: #11385a; }
tbody tr:hover td, tbody tr:hover th { background: #133553; }
pre.jush { background: #11385a; }
input.default { box-shadow: 1px 1px 1px #888; }
input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
.version { color: #888; }
.js .column { background: #011d35; }
.error { color: red; background: #efdada; border: 1px solid #e76f6f; }
.error b { background: #efeaea; }
.message { color: #0b860b; background: #efe; border: 1px solid #7fbd7f; }
.message table { color: #829bb0; background: #002240; }
.char { color: #a949a9; }
.date { color: #59c159; }
.enum { color: #d55c5c; }
.binary { color: #9bc0e1; }
.odds tbody tr:nth-child(2n) { background: #042541; }
.js .checkable .checked td, .js .checkable .checked th { background: #10395c; color: #67a4a5; }
.js .checkable .checked:hover td, .js .checkable .checked:hover th { background: #133553; }
.js .checkable .checked a { color: #67a4a5; }
.icon { background-color: #062642; }
.icon:hover { background-color: #d1394e; }
.footer { border-top-color: rgba(0, 34, 64, .7); border-image-source: linear-gradient(rgba(0, 34, 64, 0.2), #002240); }
.footer > div { background: #002240; }
#menu p, #logins, #tables { border-color: #326b9c; }
#logins a, #tables a, #tables span { background: #002240; }
#breadcrumb { background: #154269; }
#h1 { color: #ffddbf; }
#version { color: #d2b397; }
#schema .table { border-color: #093459; }
#help { border-color: #666; background: #c7e4fe; }
#schema div.table a { color: #3c7bb3; }
#menu .active { color: #398c8d; }
#edit-fields tbody tr:hover td, #edit-fields tbody tr:hover th { background: #3b6f9d; }

View File

@@ -1,4 +1,5 @@
/** @author Ondrej Valka, http://valka.info */
body { color: #000; background: #fff; font: 90%/1.25 Verdana, Arial, Helvetica, sans-serif; margin: 0; min-width: fit-content; }
a { color: blue; text-decoration: none; }
a:visited { color: navy; }
@@ -10,27 +11,29 @@ h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom:
h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; }
form { margin: 0; }
td table { width: 100%; margin: 0; }
table { margin: 1em 20px 0 0; border-collapse: collapse; font-size: 90%; }
td, th { border: 1px solid #999; padding: .2em .3em; }
table { margin: 1em 20px 0 0; font-size: 90%; border-spacing: 0; border-width: 1px 0 0 1px; }
table, td, th { border-color: #999; border-style: solid; }
td, th { border-width: 0 1px 1px 0; padding: .2em .3em; margin: 0; }
th { background: #eee; text-align: left; }
thead { position: sticky; top: 0; }
thead th { text-align: center; padding: .2em .5em; }
thead td, thead th { background: #ddf; } /* position: sticky; causes Firefox to lose borders */
thead td, thead th { background: #ddf; }
fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8em .5em 0 0; border: 1px solid #999; }
p { margin: .8em 20px 0 0; }
img { vertical-align: middle; border: 0; }
td img { max-width: 200px; max-height: 200px; }
code { background: #eee; }
tbody tr:hover td, tbody tr:hover th { background: #eee; }
code { font-size: 110%; padding: 1px 2px; background: #eee; }
pre { margin: 1em 0 0; }
td pre { margin: 0; }
pre, textarea { font: 100%/1.25 monospace; }
pre, textarea { font: 110%/1.25 monospace; }
pre.jush { background: #fff; }
pre code { display: block; font-size: 100%; }
input, textarea { box-sizing: border-box; }
input, select { vertical-align: middle; }
input[type="radio"] { vertical-align: text-bottom; }
input.default { box-shadow: 1px 1px 1px #777; }
input.required { box-shadow: 1px 1px 1px red; }
input.maxlength { box-shadow: 1px 1px 1px red; }
input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
input.wayoff { left: -1000px; position: absolute; }
.block { display: block; }
.version { color: #777; font-size: 62%; }
@@ -47,17 +50,16 @@ input.wayoff { left: -1000px; position: absolute; }
.date { color: #7F007F; }
.enum { color: #007F7F; }
.binary { color: red; }
.odds tbody tr:nth-child(2n) td { background: #F5F5F5; }
.odds tbody tr:nth-child(2n) { background: #F5F5F5; }
.js .checkable .checked td, .js .checkable .checked th { background: #ddf; }
.time { color: silver; font-size: 70%; }
.function { text-align: right; }
.number { text-align: right; }
.datetime { text-align: right; }
.function, .number, .datetime { text-align: right; }
.type { width: 15ex; }
.options select, .options input { width: 20ex; }
.view { font-style: italic; }
.active { font-weight: bold; }
.sqlarea { width: 98%; }
.sql-footer { margin-bottom: 2.5em; }
.explain table { white-space: pre; }
.icon { width: 18px; height: 18px; background-color: navy; }
.icon:hover { background-color: red; }
@@ -98,7 +100,7 @@ input.wayoff { left: -1000px; position: absolute; }
@media all and (max-width: 880px) {
.pages { left: auto; }
.logout { position: static; padding: 0 1em 1em; }
.logout { position: static; padding: 1em; }
#menu { position: static; width: auto; }
#content { margin-left: 10px; }
#lang { position: static; }

View File

@@ -2,19 +2,19 @@
/** Load syntax highlighting
* @param string first three characters of database system version
* @param [boolean]
* @param [string]
*/
function bodyLoad(version, maria) {
function syntaxHighlighting(version, vendor) {
if (window.jush) {
jush.create_links = ' target="_blank" rel="noreferrer noopener"';
if (version) {
for (var key in jush.urls) {
var obj = jush.urls;
for (let key in jush.urls) {
let obj = jush.urls;
if (typeof obj[key] != 'string') {
obj = obj[key];
key = 0;
if (maria) {
for (var i = 1; i < obj.length; i++) {
if (vendor == 'maria') {
for (let i = 1; i < obj.length; i++) {
obj[i] = obj[i]
.replace('.html', '/')
.replace('-type-syntax', '-data-types')
@@ -29,20 +29,21 @@ function bodyLoad(version, maria) {
}
}
obj[key] = (maria ? obj[key].replace('dev.mysql.com/doc/mysql', 'mariadb.com/kb') : obj[key]) // MariaDB
obj[key] = (vendor == 'maria' ? obj[key].replace('dev.mysql.com/doc/mysql', 'mariadb.com/kb') : obj[key]) // MariaDB
.replace('/doc/mysql', '/doc/refman/' + version) // MySQL
.replace('/docs/current', '/docs/' + version) // PostgreSQL
;
if (vendor != 'cockroach') {
obj[key] = obj[key].replace('/docs/current', '/docs/' + version); // PostgreSQL
}
}
}
if (window.jushLinks) {
jush.custom_links = jushLinks;
}
jush.highlight_tag('code', 0);
var tags = qsa('textarea');
for (var i = 0; i < tags.length; i++) {
if (/(^|\s)jush-/.test(tags[i].className)) {
var pre = jush.textarea(tags[i]);
for (const tag of qsa('textarea')) {
if (/(^|\s)jush-/.test(tag.className)) {
const pre = jush.textarea(tag);
if (pre) {
setupSubmitHighlightInput(pre);
}
@@ -58,7 +59,7 @@ function bodyLoad(version, maria) {
*/
function formField(form, name) {
// required in IE < 8, form.elements[name] doesn't work
for (var i=0; i < form.length; i++) {
for (let i=0; i < form.length; i++) {
if (form[i].name == name) {
return form[i];
}
@@ -72,17 +73,16 @@ function formField(form, name) {
function typePassword(el, disable) {
try {
el.type = (disable ? 'text' : 'password');
} catch (e) {
} catch (e) { // empty
}
}
/** Install toggle handler
* @param [HTMLElement]
*/
function messagesPrint(el) {
var els = qsa('.toggle', el);
for (var i = 0; i < els.length; i++) {
els[i].onclick = partial(toggle, els[i].getAttribute('href').substr(1));
function messagesPrint(parent) {
for (const el of qsa('.toggle', parent)) {
el.onclick = partial(toggle, el.getAttribute('href').substr(1));
}
}
@@ -92,16 +92,16 @@ function messagesPrint(el) {
* @param HTMLSelectElement
*/
function loginDriver(driver) {
var trs = parentTag(driver, 'table').rows;
var disabled = /sqlite/.test(selectValue(driver));
const trs = parentTag(driver, 'table').rows;
const disabled = /sqlite/.test(selectValue(driver));
alterClass(trs[1], 'hidden', disabled); // 1 - row with server
trs[1].getElementsByTagName('input')[0].disabled = disabled;
}
var dbCtrl;
var dbPrevious = {};
let dbCtrl;
const dbPrevious = {};
/** Check if database should be opened to a new window
* @param MouseEvent
@@ -142,25 +142,22 @@ function dbChange() {
* @this HTMLElement
*/
function selectFieldChange() {
var form = this.form;
var ok = (function () {
var inputs = qsa('input', form);
for (var i=0; i < inputs.length; i++) {
if (inputs[i].value && /^fulltext/.test(inputs[i].name)) {
const form = this.form;
const ok = (() => {
for (const input of qsa('input', form)) {
if (input.value && /^fulltext/.test(input.name)) {
return true;
}
}
var ok = form.limit.value;
var selects = qsa('select', form);
var group = false;
var columns = {};
for (var i=0; i < selects.length; i++) {
var select = selects[i];
var col = selectValue(select);
var match = /^(where.+)col]/.exec(select.name);
let ok = form.limit.value;
let group = false;
const columns = {};
for (const select of qsa('select', form)) {
const col = selectValue(select);
let match = /^(where.+)col]/.exec(select.name);
if (match) {
var op = selectValue(form[match[1] + 'op]']);
var val = form[match[1] + 'val]'].value;
const op = selectValue(form[match[1] + 'op]']);
const val = form[match[1] + 'val]'].value;
if (col in indexColumns && (!/LIKE|REGEXP/.test(op) || (op == 'LIKE' && val.charAt(0) != '%'))) {
return true;
} else if (col || val) {
@@ -171,7 +168,7 @@ function selectFieldChange() {
if (/^(avg|count|count distinct|group_concat|max|min|sum)$/.test(col)) {
group = true;
}
var val = selectValue(form[match[1] + 'col]']);
const val = selectValue(form[match[1] + 'col]']);
if (val) {
columns[col && col != 'count' ? '' : val] = 1;
}
@@ -184,7 +181,7 @@ function selectFieldChange() {
}
}
if (group) {
for (var col in columns) {
for (const col in columns) {
if (!(col in indexColumns)) {
ok = false;
}
@@ -197,7 +194,7 @@ function selectFieldChange() {
var added = '.', rowCount;
let added = '.', rowCount;
/** Check if val is equal to a-delimiter-b where delimiter is '_', '' or big letter
* @param string
@@ -222,28 +219,25 @@ function idfEscape(s) {
/** Set up event handlers for edit_fields().
*/
function editFields() {
var els = qsa('[name$="[field]"]');
for (var i = 0; i < els.length; i++) {
els[i].oninput = function () {
for (const el of qsa('[name$="[field]"]')) {
el.oninput = function () {
editingNameChange.call(this);
if (!this.defaultValue) {
editingAddRow.call(this);
}
}
};
}
els = qsa('[name$="[length]"]');
for (var i = 0; i < els.length; i++) {
mixin(els[i], {onfocus: editingLengthFocus, oninput: editingLengthChange});
for (const el of qsa('[name$="[length]"]')) {
mixin(el, {onfocus: editingLengthFocus, oninput: editingLengthChange});
}
els = qsa('[name$="[type]"]');
for (var i = 0; i < els.length; i++) {
mixin(els[i], {
for (const el of qsa('[name$="[type]"]')) {
mixin(el, {
onfocus: function () {
lastType = selectValue(this);
},
onchange: editingTypeChange,
onmouseover: function (event) {
helpMouseover.call(this, event, getTarget(event).value, 1);
helpMouseover.call(this, event, event.target.value, 1);
},
onmouseout: helpMouseout
});
@@ -255,13 +249,13 @@ function editFields() {
* @return boolean false to cancel action
*/
function editingClick(event) {
var el = getTarget(event);
let el = event.target;
if (!isTag(el, 'input')) {
el = parentTag(el, 'label');
el = el && qs('input', el);
}
if (el) {
var name = el.name;
const name = el.name;
if (/^add\[/.test(name)) {
editingAddRow.call(el, 1);
} else if (/^up\[/.test(name)) {
@@ -269,10 +263,10 @@ function editingClick(event) {
} else if (/^down\[/.test(name)) {
editingMoveRow.call(el);
} else if (/^drop_col\[/.test(name)) {
editingRemoveRow.call(el, 'fields\$1[field]');
editingRemoveRow.call(el, 'fields$1[field]');
} else {
if (name == 'auto_increment_col') {
var field = el.form['fields[' + el.value + '][field]'];
const field = el.form['fields[' + el.value + '][field]'];
if (!field.value) {
field.value = 'id';
field.oninput();
@@ -288,7 +282,7 @@ function editingClick(event) {
* @param InputEvent
*/
function editingInput(event) {
var el = getTarget(event);
const el = event.target;
if (/\[default]$/.test(el.name)) {
el.previousElementSibling.checked = true;
el.previousElementSibling.selectedIndex = Math.max(el.previousElementSibling.selectedIndex, 1);
@@ -299,24 +293,22 @@ function editingInput(event) {
* @this HTMLInputElement
*/
function editingNameChange() {
var name = this.name.substr(0, this.name.length - 7);
var type = formField(this.form, name + '[type]');
var opts = type.options;
var candidate; // don't select anything with ambiguous match (like column `id`)
var val = this.value;
for (var i = opts.length; i--; ) {
var match = /(.+)`(.+)/.exec(opts[i].value);
const name = this.name.substr(0, this.name.length - 7);
const type = formField(this.form, name + '[type]');
const opts = type.options;
let candidate; // don't select anything with ambiguous match (like column `id`)
const val = this.value;
for (let i = opts.length; i--; ) {
const match = /(.+)`(.+)/.exec(opts[i].value);
if (!match) { // common type
if (candidate && i == opts.length - 2 && val == opts[candidate].value.replace(/.+`/, '') && name == 'fields[1]') { // single target table, link to column, first field - probably `id`
return;
}
break;
}
var table = match[1];
var column = match[2];
var tables = [ table, table.replace(/s$/, ''), table.replace(/es$/, '') ];
for (var j=0; j < tables.length; j++) {
table = tables[j];
const base = match[1];
const column = match[2];
for (const table of [ base, base.replace(/s$/, ''), base.replace(/es$/, '') ]) {
if (val == column || val == table || delimiterEqual(val, table, column) || delimiterEqual(val, column, table)) {
if (candidate) {
return;
@@ -338,20 +330,20 @@ function editingNameChange() {
* @this HTMLInputElement
*/
function editingAddRow(focus) {
var match = /(\d+)(\.\d+)?/.exec(this.name);
var x = match[0] + (match[2] ? added.substr(match[2].length) : added) + '1';
var row = parentTag(this, 'tr');
var row2 = cloneNode(row);
var tags = qsa('select', row);
var tags2 = qsa('select', row2);
for (var i=0; i < tags.length; i++) {
const match = /(\d+)(\.\d+)?/.exec(this.name);
const x = match[0] + (match[2] ? added.substr(match[2].length) : added) + '1';
const row = parentTag(this, 'tr');
const row2 = cloneNode(row);
let tags = qsa('select', row);
let tags2 = qsa('select', row2);
for (let i=0; i < tags.length; i++) {
tags2[i].name = tags[i].name.replace(/[0-9.]+/, x);
tags2[i].selectedIndex = tags[i].selectedIndex;
}
tags = qsa('input', row);
tags2 = qsa('input', row2);
var input = tags2[0]; // IE loose tags2 after insertBefore()
for (var i=0; i < tags.length; i++) {
const input = tags2[0]; // IE loose tags2 after insertBefore()
for (let i=0; i < tags.length; i++) {
if (tags[i].name == 'auto_increment_col') {
tags2[i].value = x;
tags2[i].checked = false;
@@ -382,7 +374,7 @@ function editingAddRow(focus) {
* @this HTMLInputElement
*/
function editingRemoveRow(name) {
var field = formField(this.form, this.name.replace(/[^\[]+(.+)/, name));
const field = formField(this.form, this.name.replace(/[^[]+(.+)/, name));
field.parentNode.removeChild(field);
parentTag(this, 'tr').style.display = 'none';
return false;
@@ -394,7 +386,7 @@ function editingRemoveRow(name) {
* @this HTMLInputElement
*/
function editingMoveRow(up){
var row = parentTag(this, 'tr');
const row = parentTag(this, 'tr');
if (!('nextElementSibling' in row)) {
return true;
}
@@ -404,17 +396,16 @@ function editingMoveRow(up){
return false;
}
var lastType = '';
let lastType = '';
/** Clear length and hide collation or unsigned
* @this HTMLSelectElement
*/
function editingTypeChange() {
var type = this;
var name = type.name.substr(0, type.name.length - 6);
var text = selectValue(type);
for (var i=0; i < type.form.elements.length; i++) {
var el = type.form.elements[i];
const type = this;
const name = type.name.substr(0, type.name.length - 6);
const text = selectValue(type);
for (const el of type.form.elements) {
if (el.name == name + '[length]') {
if (!(
(/(char|binary)$/.test(lastType) && /(char|binary)$/.test(text))
@@ -455,9 +446,9 @@ function editingLengthChange() {
* @this HTMLInputElement
*/
function editingLengthFocus() {
var td = this.parentNode;
const td = this.parentNode;
if (/(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) {
var edit = qs('#enum-edit');
const edit = qs('#enum-edit');
edit.value = enumValues(this.value);
td.appendChild(edit);
this.style.display = 'none';
@@ -471,11 +462,11 @@ function editingLengthFocus() {
* @return string values separated by newlines
*/
function enumValues(s) {
var re = /(^|,)\s*'(([^\\']|\\.|'')*)'\s*/g;
var result = [];
var offset = 0;
var match;
while (match = re.exec(s)) {
const re = /(^|,)\s*'(([^\\']|\\.|'')*)'\s*/g;
const result = [];
let offset = 0;
let match;
while ((match = re.exec(s))) {
if (offset != match.index) {
break;
}
@@ -489,8 +480,8 @@ function enumValues(s) {
* @this HTMLTextAreaElement
*/
function editingLengthBlur() {
var field = this.parentNode.firstChild;
var val = this.value;
const field = this.parentNode.firstChild;
const val = this.value;
field.value = (/^'[^\n]+'$/.test(val) ? val : val && "'" + val.replace(/\n+$/, '').replace(/'/g, "''").replace(/\\/g, '\\\\').replace(/\n/g, "','") + "'");
field.style.display = 'inline';
this.style.display = 'none';
@@ -501,9 +492,8 @@ function editingLengthBlur() {
* @param number
*/
function columnShow(checked, column) {
var trs = qsa('tr', qs('#edit-fields'));
for (var i=0; i < trs.length; i++) {
alterClass(qsa('td', trs[i])[column], 'hidden', !checked);
for (const tr of qsa('tr', qs('#edit-fields'))) {
alterClass(qsa('td', tr)[column], 'hidden', !checked);
}
}
@@ -511,9 +501,8 @@ function columnShow(checked, column) {
* @param boolean
*/
function indexOptionsShow(checked) {
var options = qsa('.idxopts');
for (var i=0; i < options.length; i++) {
alterClass(options[i], 'hidden', !checked);
for (const option of qsa('.idxopts')) {
alterClass(option, 'hidden', !checked);
}
}
@@ -521,7 +510,7 @@ function indexOptionsShow(checked) {
* @this HTMLSelectElement
*/
function partitionByChange() {
var partitionTable = /RANGE|LIST/.test(selectValue(this));
const partitionTable = /RANGE|LIST/.test(selectValue(this));
alterClass(this.form['partitions'], 'hidden', partitionTable || !this.selectedIndex);
alterClass(qs('#partition-table'), 'hidden', !partitionTable);
helpClose();
@@ -531,10 +520,10 @@ function partitionByChange() {
* @this HTMLInputElement
*/
function partitionNameChange() {
var row = cloneNode(parentTag(this, 'tr'));
const row = cloneNode(parentTag(this, 'tr'));
row.firstChild.firstChild.value = '';
parentTag(this, 'table').appendChild(row);
this.oninput = function () {};
this.oninput = () => { };
}
/** Show or hide comment fields
@@ -542,7 +531,7 @@ function partitionNameChange() {
* @param [boolean] whether to focus Comment if checked
*/
function editingCommentsClick(el, focus) {
var comment = el.form['Comment'];
const comment = el.form['Comment'];
columnShow(el.checked, 6);
alterClass(comment, 'hidden', !el.checked);
if (focus && el.checked) {
@@ -557,10 +546,10 @@ function editingCommentsClick(el, focus) {
* @this HTMLTableElement
*/
function dumpClick(event) {
var el = parentTag(getTarget(event), 'label');
let el = parentTag(event.target, 'label');
if (el) {
el = qs('input', el);
var match = /(.+)\[]$/.exec(el.name);
const match = /(.+)\[]$/.exec(el.name);
if (match) {
checkboxClick.call(el, event);
formUncheck('check-' + match[1]);
@@ -574,12 +563,11 @@ function dumpClick(event) {
* @this HTMLSelectElement
*/
function foreignAddRow() {
var row = cloneNode(parentTag(this, 'tr'));
this.onchange = function () { };
var selects = qsa('select', row);
for (var i=0; i < selects.length; i++) {
selects[i].name = selects[i].name.replace(/\d+]/, '1$&');
selects[i].selectedIndex = 0;
const row = cloneNode(parentTag(this, 'tr'));
this.onchange = () => { };
for (const select of qsa('select', row)) {
select.name = select.name.replace(/\d+]/, '1$&');
select.selectedIndex = 0;
}
parentTag(this, 'table').appendChild(row);
}
@@ -590,17 +578,15 @@ function foreignAddRow() {
* @this HTMLSelectElement
*/
function indexesAddRow() {
var row = cloneNode(parentTag(this, 'tr'));
this.onchange = function () { };
var selects = qsa('select', row);
for (var i=0; i < selects.length; i++) {
selects[i].name = selects[i].name.replace(/indexes\[\d+/, '$&1');
selects[i].selectedIndex = 0;
const row = cloneNode(parentTag(this, 'tr'));
this.onchange = () => { };
for (const select of qsa('select', row)) {
select.name = select.name.replace(/indexes\[\d+/, '$&1');
select.selectedIndex = 0;
}
var inputs = qsa('input', row);
for (var i=0; i < inputs.length; i++) {
inputs[i].name = inputs[i].name.replace(/indexes\[\d+/, '$&1');
inputs[i].value = '';
for (const input of qsa('input', row)) {
input.name = input.name.replace(/indexes\[\d+/, '$&1');
input.value = '';
}
parentTag(this, 'table').appendChild(row);
}
@@ -610,12 +596,11 @@ function indexesAddRow() {
* @this HTMLSelectElement
*/
function indexesChangeColumn(prefix) {
var names = [];
for (var tag in { 'select': 1, 'input': 1 }) {
var columns = qsa(tag, parentTag(this, 'td'));
for (var i=0; i < columns.length; i++) {
if (/\[columns]/.test(columns[i].name)) {
var value = selectValue(columns[i]);
const names = [];
for (const tag in { 'select': 1, 'input': 1 }) {
for (const column of qsa(tag, parentTag(this, 'td'))) {
if (/\[columns]/.test(column.name)) {
const value = selectValue(column);
if (value) {
names.push(value);
}
@@ -630,25 +615,21 @@ function indexesChangeColumn(prefix) {
* @this HTMLSelectElement
*/
function indexesAddColumn(prefix) {
var field = this;
var select = field.form[field.name.replace(/].*/, '][type]')];
const field = this;
const select = field.form[field.name.replace(/].*/, '][type]')];
if (!select.selectedIndex) {
while (selectValue(select) != "INDEX" && select.selectedIndex < select.options.length) {
select.selectedIndex++;
}
select.onchange();
}
var column = cloneNode(field.parentNode);
var selects = qsa('select', column);
for (var i = 0; i < selects.length; i++) {
select = selects[i];
const column = cloneNode(field.parentNode);
for (const select of qsa('select', column)) {
select.name = select.name.replace(/]\[\d+/, '$&1');
select.selectedIndex = 0;
}
field.onchange = partial(indexesChangeColumn, prefix);
var inputs = qsa('input', column);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
for (const input of qsa('input', column)) {
input.name = input.name.replace(/]\[\d+/, '$&1');
if (input.type != 'checkbox') {
input.value = '';
@@ -660,12 +641,12 @@ function indexesAddColumn(prefix) {
/** Updates the form action
/** Update the form action
* @param HTMLFormElement
* @param string
*/
function sqlSubmit(form, root) {
if (encodeURIComponent(form['query'].value).length < 2e3) {
if (encodeURIComponent(form['query'].value).length < 500) {
form.action = root
+ '&sql=' + encodeURIComponent(form['query'].value)
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '')
@@ -683,7 +664,7 @@ function sqlSubmit(form, root) {
* @param HTMLFormElement
*/
function triggerChange(tableRe, table, form) {
var formEvent = selectValue(form['Event']);
const formEvent = selectValue(form['Event']);
if (tableRe.test(form['Trigger'].value)) {
form['Trigger'].value = table + '_' + (selectValue(form['Timing']).charAt(0) + formEvent.charAt(0)).toLowerCase();
}
@@ -692,7 +673,7 @@ function triggerChange(tableRe, table, form) {
var that, x, y; // em and tablePos defined in schema.inc.php
let that, x, y; // em and tablePos defined in schema.inc.php
/** Get mouse position
* @param MouseEvent
@@ -711,29 +692,28 @@ function schemaMousedown(event) {
*/
function schemaMousemove(event) {
if (that !== undefined) {
var left = (event.clientX - x) / em;
var top = (event.clientY - y) / em;
var divs = qsa('div', that);
var lineSet = { };
for (var i=0; i < divs.length; i++) {
if (divs[i].className == 'references') {
var div2 = qs('[id="' + (/^refs/.test(divs[i].id) ? 'refd' : 'refs') + divs[i].id.substr(4) + '"]');
var ref = (tablePos[divs[i].title] || [ div2.parentNode.offsetTop / em, 0 ]);
var left1 = -1;
var id = divs[i].id.replace(/^ref.(.+)-.+/, '$1');
if (divs[i].parentNode != div2.parentNode) {
const left = (event.clientX - x) / em;
const top = (event.clientY - y) / em;
const lineSet = { };
for (const div of qsa('div', that)) {
if (div.classList.contains('references')) {
const div2 = qs('[id="' + (/^refs/.test(div.id) ? 'refd' : 'refs') + div.id.substr(4) + '"]');
const ref = (tablePos[div.title] || [ div2.parentNode.offsetTop / em, 0 ]);
let left1 = -1;
const id = div.id.replace(/^ref.(.+)-.+/, '$1');
if (div.parentNode != div2.parentNode) {
left1 = Math.min(0, ref[1] - left) - 1;
divs[i].style.left = left1 + 'em';
divs[i].querySelector('div').style.width = -left1 + 'em';
var left2 = Math.min(0, left - ref[1]) - 1;
div.style.left = left1 + 'em';
div.querySelector('div').style.width = -left1 + 'em';
const left2 = Math.min(0, left - ref[1]) - 1;
div2.style.left = left2 + 'em';
div2.querySelector('div').style.width = -left2 + 'em';
}
if (!lineSet[id]) {
var line = qs('[id="' + divs[i].id.replace(/^....(.+)-.+$/, 'refl$1') + '"]');
var top1 = top + divs[i].offsetTop / em;
var top2 = top + div2.offsetTop / em;
if (divs[i].parentNode != div2.parentNode) {
const line = qs('[id="' + div.id.replace(/^....(.+)-.+$/, 'refl$1') + '"]');
const top1 = top + div.offsetTop / em;
let top2 = top + div2.offsetTop / em;
if (div.parentNode != div2.parentNode) {
top2 += ref[0] - top;
line.querySelector('div').style.height = Math.abs(top1 - top2) + 'em';
}
@@ -756,12 +736,12 @@ function schemaMouseup(event, db) {
if (that !== undefined) {
tablePos[that.firstChild.firstChild.firstChild.data] = [ (event.clientY - y) / em, (event.clientX - x) / em ];
that = undefined;
var s = '';
for (var key in tablePos) {
let s = '';
for (const key in tablePos) {
s += '_' + key + ':' + Math.round(tablePos[key][0] * 10000) / 10000 + 'x' + Math.round(tablePos[key][1] * 10000) / 10000;
}
s = encodeURIComponent(s.substr(1));
var link = qs('#schema-link');
const link = qs('#schema-link');
link.href = link.href.replace(/[^=]+$/, '') + s;
cookie('adminer_schema-' + db + '=' + s, 30); //! special chars in db
}
@@ -769,7 +749,7 @@ function schemaMouseup(event, db) {
var helpOpen, helpIgnore; // when mouse outs <option> then it mouse overs border of <select> - ignore it
let helpOpen, helpIgnore; // when mouse outs <option> then it mouse overs border of <select> - ignore it
/** Display help
* @param MouseEvent
@@ -778,17 +758,17 @@ var helpOpen, helpIgnore; // when mouse outs <option> then it mouse overs border
* @this HTMLElement
*/
function helpMouseover(event, text, side) {
var target = getTarget(event);
const target = event.target;
if (!text) {
helpClose();
} else if (window.jush && (!helpIgnore || this != target)) {
helpOpen = 1;
var help = qs('#help');
const help = qs('#help');
help.innerHTML = text;
jush.highlight_tag([ help ]);
alterClass(help, 'hidden');
var rect = target.getBoundingClientRect();
var body = document.documentElement;
const rect = target.getBoundingClientRect();
const body = document.documentElement;
help.style.top = (body.scrollTop + rect.top - (side ? (help.offsetHeight - target.offsetHeight) / 2 : help.offsetHeight)) + 'px';
help.style.left = (body.scrollLeft + rect.left - (side ? help.offsetWidth : (help.offsetWidth - target.offsetWidth) / 2)) + 'px';
}
@@ -800,8 +780,8 @@ function helpMouseover(event, text, side) {
*/
function helpMouseout(event) {
helpOpen = 0;
helpIgnore = (this != getTarget(event));
setTimeout(function () {
helpIgnore = (this != event.target);
setTimeout(() => {
if (!helpOpen) {
helpClose();
}

View File

@@ -14,7 +14,7 @@ function qs(selector, context) {
* @return HTMLElement
*/
function qsl(selector, context) {
var els = qsa(selector, context);
const els = qsa(selector, context);
return els[els.length - 1];
}
@@ -33,7 +33,7 @@ function qsa(selector, context) {
* @return function with preserved this
*/
function partial(fn) {
var args = Array.apply(null, arguments).slice(1);
const args = Array.apply(null, arguments).slice(1);
return function () {
return fn.apply(this, args);
};
@@ -45,7 +45,7 @@ function partial(fn) {
* @return function with preserved this
*/
function partialArg(fn) {
var args = Array.apply(null, arguments);
const args = Array.apply(null, arguments);
return function (arg) {
args[0] = arg;
return fn.apply(this, args);
@@ -57,7 +57,7 @@ function partialArg(fn) {
* @param Object
*/
function mixin(target, source) {
for (var key in source) {
for (const key in source) {
target[key] = source[key];
}
}
@@ -69,7 +69,7 @@ function mixin(target, source) {
*/
function alterClass(el, className, enable) {
if (el) {
el.className = el.className.replace(RegExp('(^|\\s)' + className + '(\\s|$)'), '$2') + (enable ? ' ' + className : '');
el.classList[enable ? 'add' : 'remove'](className);
}
}
@@ -78,18 +78,17 @@ function alterClass(el, className, enable) {
* @return boolean false
*/
function toggle(id) {
var el = qs('#' + id);
alterClass(el, 'hidden', !/(^|\s)hidden(\s|$)/.test(el.className));
const el = qs('#' + id);
el && el.classList.toggle('hidden');
return false;
}
/** Set permanent cookie
* @param string
* @param number
* @param string optional
*/
function cookie(assign, days) {
var date = new Date();
const date = new Date();
date.setDate(date.getDate() + days);
document.cookie = assign + '; expires=' + date;
}
@@ -101,26 +100,23 @@ function cookie(assign, days) {
*/
function verifyVersion(current, url, token) {
cookie('adminer_version=0', 1);
var iframe = document.createElement('iframe');
const iframe = document.createElement('iframe');
iframe.src = 'https://www.adminer.org/version/?current=' + current;
iframe.frameBorder = 0;
iframe.marginHeight = 0;
iframe.scrolling = 'no';
iframe.style.width = '7ex';
iframe.style.height = '1.25em';
if (window.postMessage && window.addEventListener) {
iframe.style.display = 'none';
addEventListener('message', function (event) {
if (event.origin == 'https://www.adminer.org') {
var match = /version=(.+)/.exec(event.data);
if (match) {
cookie('adminer_version=' + match[1], 1);
ajax(url + 'script=version', function () {
}, event.data + '&token=' + token);
}
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);
}
}
}, false);
qs('#version').appendChild(iframe);
}
@@ -132,7 +128,7 @@ function selectValue(select) {
if (!select.selectedIndex) {
return select.value;
}
var selected = select.options[select.selectedIndex];
const selected = select.options[select.selectedIndex];
return ((selected.attributes.value || {}).specified ? selected.value : selected.text);
}
@@ -142,7 +138,7 @@ function selectValue(select) {
* @return boolean
*/
function isTag(el, tag) {
var re = new RegExp('^(' + tag + ')$', 'i');
const re = new RegExp('^(' + tag + ')$', 'i');
return el && re.test(el.tagName);
}
@@ -162,7 +158,7 @@ function parentTag(el, tag) {
* @param HTMLInputElement
*/
function trCheck(el) {
var tr = parentTag(el, 'tr');
const tr = parentTag(el, 'tr');
alterClass(tr, 'checked', el.checked);
if (el.form && el.form['all'] && el.form['all'].onclick) { // Opera treats form.all as document.all
el.form['all'].onclick();
@@ -176,11 +172,9 @@ function trCheck(el) {
*/
function selectCount(id, count) {
setHtml(id, (count === '' ? '' : '(' + (count + '').replace(/\B(?=(\d{3})+$)/g, thousandsSeparator) + ')'));
var el = qs('#' + id);
const el = qs('#' + id);
if (el) {
var inputs = qsa('input', el.parentNode.parentNode);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
for (const input of qsa('input', el.parentNode.parentNode)) {
if (input.type == 'submit') {
input.disabled = (count == '0');
}
@@ -193,11 +187,10 @@ function selectCount(id, count) {
* @this HTMLInputElement
*/
function formCheck(name) {
var elems = this.form.elements;
for (var i=0; i < elems.length; i++) {
if (name.test(elems[i].name)) {
elems[i].checked = this.checked;
trCheck(elems[i]);
for (const elem of this.form.elements) {
if (name.test(elem.name)) {
elem.checked = this.checked;
trCheck(elem);
}
}
}
@@ -205,9 +198,8 @@ function formCheck(name) {
/** Check all rows in <table class="checkable">
*/
function tableCheck() {
var inputs = qsa('table.checkable td:first-child input');
for (var i=0; i < inputs.length; i++) {
trCheck(inputs[i]);
for (const input of qsa('table.checkable td:first-child input')) {
trCheck(input);
}
}
@@ -215,7 +207,7 @@ function tableCheck() {
* @param string
*/
function formUncheck(id) {
var el = qs('#' + id);
const el = qs('#' + id);
el.checked = false;
trCheck(el);
}
@@ -225,11 +217,10 @@ function formUncheck(id) {
* @param RegExp
* @return number
*/
function formChecked(el, name) {
var checked = 0;
var elems = el.form.elements;
for (var i=0; i < elems.length; i++) {
if (name.test(elems[i].name) && elems[i].checked) {
function formChecked(input, name) {
let checked = 0;
for (const el of input.form.elements) {
if (name.test(el.name) && el.checked) {
checked++;
}
}
@@ -241,15 +232,15 @@ function formChecked(el, name) {
* @param [boolean] force click
*/
function tableClick(event, click) {
var td = parentTag(getTarget(event), 'td');
var text;
const td = parentTag(event.target, 'td');
let text;
if (td && (text = td.getAttribute('data-text'))) {
if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) {
return;
}
}
click = (click || !window.getSelection || getSelection().isCollapsed);
var el = getTarget(event);
let el = event.target;
while (!isTag(el, 'tr')) {
if (isTag(el, 'table|a|input|textarea')) {
if (el.type != 'checkbox') {
@@ -278,7 +269,7 @@ function tableClick(event, click) {
trCheck(el);
}
var lastChecked;
let lastChecked;
/** Shift-click on checkbox for multiple selection.
* @param MouseEvent
@@ -289,11 +280,9 @@ function checkboxClick(event) {
return;
}
if (event.shiftKey && (!lastChecked || lastChecked.name == this.name)) {
var checked = (lastChecked ? lastChecked.checked : true);
var inputs = qsa('input', parentTag(this, 'table'));
var checking = !lastChecked;
for (var i=0; i < inputs.length; i++) {
var input = inputs[i];
const checked = (lastChecked ? lastChecked.checked : true);
let checking = !lastChecked;
for (const input of qsa('input', parentTag(this, 'table'))) {
if (input.name === this.name) {
if (checking) {
input.checked = checked;
@@ -317,7 +306,7 @@ function checkboxClick(event) {
* @param string undefined to set parentNode to empty string
*/
function setHtml(id, html) {
var el = qs('[id="' + id.replace(/[\\"]/g, '\\$&') + '"]'); // database name is used as ID
const el = qs('[id="' + id.replace(/[\\"]/g, '\\$&') + '"]'); // database name is used as ID
if (el) {
if (html == null) {
el.parentNode.innerHTML = '';
@@ -332,7 +321,7 @@ function setHtml(id, html) {
* @return number
*/
function nodePosition(el) {
var pos = 0;
let pos = 0;
while ((el = el.previousSibling)) {
pos++;
}
@@ -356,7 +345,7 @@ function pageClick(href, page) {
* @this HTMLElement
*/
function menuOver(event) {
var a = getTarget(event);
const a = event.target;
if (isTag(a, 'a|span') && a.offsetLeft + a.offsetWidth > a.parentNode.offsetWidth - 15) { // 15 - ellipsis
this.style.overflow = 'visible';
}
@@ -375,23 +364,21 @@ function menuOut() {
* @this HTMLSelectElement
*/
function selectAddRow() {
var field = this;
var row = cloneNode(field.parentNode);
const field = this;
const row = cloneNode(field.parentNode);
field.onchange = selectFieldChange;
field.onchange();
var selects = qsa('select', row);
for (var i=0; i < selects.length; i++) {
selects[i].name = selects[i].name.replace(/[a-z]\[\d+/, '$&1');
selects[i].selectedIndex = 0;
for (const select of qsa('select', row)) {
select.name = select.name.replace(/[a-z]\[\d+/, '$&1');
select.selectedIndex = 0;
}
var inputs = qsa('input', row);
for (var i=0; i < inputs.length; i++) {
inputs[i].name = inputs[i].name.replace(/[a-z]\[\d+/, '$&1');
inputs[i].className = '';
if (inputs[i].type == 'checkbox') {
inputs[i].checked = false;
for (const input of qsa('input', row)) {
input.name = input.name.replace(/[a-z]\[\d+/, '$&1');
input.className = '';
if (input.type == 'checkbox') {
input.checked = false;
} else {
inputs[i].value = '';
input.value = '';
}
}
field.parentNode.parentNode.appendChild(row);
@@ -403,8 +390,7 @@ function selectAddRow() {
*/
function selectSearchKeydown(event) {
if (event.keyCode == 13 || event.keyCode == 10) {
this.onsearch = function () {
};
this.onsearch = () => { };
}
}
@@ -419,15 +405,14 @@ function selectSearchSearch() {
/** Toggles column context menu
/** Toggle column context menu
* @param [string] extra class name
* @this HTMLElement
*/
function columnMouse(className) {
var spans = qsa('span', this);
for (var i=0; i < spans.length; i++) {
if (/column/.test(spans[i].className)) {
spans[i].className = 'column' + (className || '');
for (const span of qsa('span', this)) {
if (/column/.test(span.className)) {
span.className = 'column' + (className || '');
}
}
}
@@ -439,12 +424,13 @@ function columnMouse(className) {
* @return boolean false
*/
function selectSearch(name) {
var el = qs('#fieldset-search');
let el = qs('#fieldset-search');
el.className = '';
var divs = qsa('div', el);
for (var i=0; i < divs.length; i++) {
var div = divs[i];
var el = qs('[name$="[col]"]', div);
const divs = qsa('div', el);
let i, div;
for (i=0; i < divs.length; i++) {
div = divs[i];
el = qs('[name$="[col]"]', div);
if (el && selectValue(el) == name) {
break;
}
@@ -466,14 +452,6 @@ function isCtrl(event) {
return (event.ctrlKey || event.metaKey) && !event.altKey; // shiftKey allowed
}
/** Return event target
* @param Event
* @return HTMLElement
*/
function getTarget(event) {
return event.target || event.srcElement;
}
/** Send form by Ctrl+Enter on <select> and <textarea>
@@ -483,7 +461,7 @@ function getTarget(event) {
*/
function bodyKeydown(event, button) {
eventStop(event);
var target = getTarget(event);
let target = event.target;
if (target.jushTextarea) {
target = target.jushTextarea;
}
@@ -507,10 +485,10 @@ function bodyKeydown(event, button) {
* @param MouseEvent
*/
function bodyClick(event) {
var target = getTarget(event);
const target = event.target;
if ((isCtrl(event) || event.shiftKey) && target.type == 'submit' && isTag(target, 'input')) {
target.form.target = '_blank';
setTimeout(function () {
setTimeout(() => {
// if (isCtrl(event)) { focus(); } doesn't work
target.form.target = '';
}, 0);
@@ -519,15 +497,15 @@ function bodyClick(event) {
/** Change focus by Ctrl+Up or Ctrl+Down
/** Change focus by Ctrl+Shift+Up or Ctrl+Shift+Down
* @param KeyboardEvent
* @return boolean
*/
function editingKeydown(event) {
if ((event.keyCode == 40 || event.keyCode == 38) && isCtrl(event)) { // 40 - Down, 38 - Up
var target = getTarget(event);
var sibling = (event.keyCode == 40 ? 'nextSibling' : 'previousSibling');
var el = target.parentNode.parentNode[sibling];
const target = event.target;
const sibling = (event.keyCode == 40 ? 'nextSibling' : 'previousSibling');
let el = target.parentNode.parentNode[sibling];
if (el && (isTag(el, 'tr') || (el = el[sibling])) && isTag(el, 'tr') && (el = el.childNodes[nodePosition(target.parentNode)]) && (el = el.childNodes[nodePosition(target)])) {
el.focus();
}
@@ -543,7 +521,7 @@ function editingKeydown(event) {
* @this HTMLSelectElement
*/
function functionChange() {
var input = this.form[this.name.replace(/^function/, 'fields')];
const input = this.form[this.name.replace(/^function/, 'fields')];
if (input) { // undefined with the set data type
if (selectValue(this)) {
if (input.origType === undefined) {
@@ -568,7 +546,7 @@ function functionChange() {
* @this HTMLTableCellElement
*/
function skipOriginal(first) {
var fnSelect = this.previousSibling.firstChild;
const fnSelect = qs('select', this.previousSibling);
if (fnSelect.selectedIndex < first) {
fnSelect.selectedIndex = first;
}
@@ -578,14 +556,13 @@ function skipOriginal(first) {
* @this HTMLInputElement
*/
function fieldChange() {
var row = cloneNode(parentTag(this, 'tr'));
var inputs = qsa('input', row);
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = '';
const row = cloneNode(parentTag(this, 'tr'));
for (const input of qsa('input', row)) {
input.value = '';
}
// keep value in <select> (function)
parentTag(this, 'table').appendChild(row);
this.oninput = function () { };
this.oninput = () => { };
}
@@ -599,27 +576,25 @@ function fieldChange() {
* @uses offlineMessage
*/
function ajax(url, callback, data, message) {
var request = (window.XMLHttpRequest ? new XMLHttpRequest() : (window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : false));
const request = new XMLHttpRequest();
if (request) {
var ajaxStatus = qs('#ajaxstatus');
const ajaxStatus = qs('#ajaxstatus');
if (message) {
ajaxStatus.innerHTML = '<div class="message">' + message + '</div>';
ajaxStatus.className = ajaxStatus.className.replace(/ hidden/g, '');
} else {
ajaxStatus.className += ' hidden';
}
alterClass(ajaxStatus, 'hidden', !message);
request.open((data ? 'POST' : 'GET'), url);
if (data) {
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.onreadystatechange = function () {
request.onreadystatechange = () => {
if (request.readyState == 4) {
if (/^2/.test(request.status)) {
callback(request);
} else {
} else if (message !== null) {
ajaxStatus.innerHTML = (request.status ? request.responseText : '<div class="error">' + offlineMessage + '</div>');
ajaxStatus.className = ajaxStatus.className.replace(/ hidden/g, '');
alterClass(ajaxStatus, 'hidden');
}
}
};
@@ -633,14 +608,16 @@ function ajax(url, callback, data, message) {
* @return boolean false for success
*/
function ajaxSetHtml(url) {
return !ajax(url, function (request) {
var data = window.JSON ? JSON.parse(request.responseText) : eval('(' + request.responseText + ')');
for (var key in data) {
return !ajax(url, request => {
const data = JSON.parse(request.responseText);
for (const key in data) {
setHtml(key, data[key]);
}
});
}
let editChanged; // used by plugins
/** Save form contents through AJAX
* @param HTMLFormElement
* @param string
@@ -648,10 +625,8 @@ function ajaxSetHtml(url) {
* @return boolean
*/
function ajaxForm(form, message, button) {
var data = [];
var els = form.elements;
for (var i = 0; i < els.length; i++) {
var el = els[i];
let data = [];
for (const el of form.elements) {
if (el.name && !el.disabled) {
if (/^file$/i.test(el.type) && el.value) {
return false;
@@ -663,17 +638,21 @@ function ajaxForm(form, message, button) {
}
data = data.join('&');
var url = form.action;
let url = form.action;
if (!/post/i.test(form.method)) {
url = url.replace(/\?.*/, '') + '?' + data;
data = '';
}
return ajax(url, function (request) {
return ajax(url, request => {
const ajaxstatus = qs('#ajaxstatus');
setHtml('ajaxstatus', request.responseText);
if (window.jush) {
jush.highlight_tag(qsa('code', qs('#ajaxstatus')), 0);
if (qs('.message', ajaxstatus)) { // success
editChanged = null;
}
messagesPrint(qs('#ajaxstatus'));
if (window.jush) {
jush.highlight_tag(qsa('code', ajaxstatus), 0);
}
messagesPrint(ajaxstatus);
}, data, message);
}
@@ -687,8 +666,8 @@ function ajaxForm(form, message, button) {
* @this HTMLElement
*/
function selectClick(event, text, warning) {
var td = this;
var target = getTarget(event);
const td = this;
const target = event.target;
if (!isCtrl(event) || isTag(td.firstChild, 'input|textarea') || isTag(target, 'a')) {
return;
}
@@ -696,28 +675,25 @@ function selectClick(event, text, warning) {
alert(warning);
return true;
}
var original = td.innerHTML;
const original = td.innerHTML;
text = text || /\n/.test(original);
var input = document.createElement(text ? 'textarea' : 'input');
input.onkeydown = function (event) {
if (!event) {
event = window.event;
}
const input = document.createElement(text ? 'textarea' : 'input');
input.onkeydown = event => {
if (event.keyCode == 27 && !event.shiftKey && !event.altKey && !isCtrl(event)) { // 27 - Esc
inputBlur.apply(input);
td.innerHTML = original;
}
};
let pos = event.rangeOffset;
let value = (td.firstChild && td.firstChild.alt) || td.textContent || td.innerText;
const pos = getSelection().anchorOffset;
let value = (td.firstChild && td.firstChild.alt) || td.textContent;
const tdStyle = window.getComputedStyle(td, null);
input.style.width = Math.max(td.clientWidth - parseFloat(tdStyle.paddingLeft) - parseFloat(tdStyle.paddingRight), 20) + 'px';
input.style.width = Math.max(td.clientWidth - parseFloat(tdStyle.paddingLeft) - parseFloat(tdStyle.paddingRight), (text ? 200 : 20)) + 'px';
if (text) {
var rows = 1;
value.replace(/\n/g, function () {
let rows = 1;
value.replace(/\n/g, () => {
rows++;
});
input.rows = rows;
@@ -725,20 +701,12 @@ function selectClick(event, text, warning) {
if (qsa('i', td).length) { // <i> - NULL
value = '';
}
if (document.selection) {
var range = document.selection.createRange();
range.moveToPoint(event.clientX, event.clientY);
var range2 = range.duplicate();
range2.moveToElementText(td);
range2.setEndPoint('EndToEnd', range);
pos = range2.text.length;
}
td.innerHTML = '';
td.appendChild(input);
setupSubmitHighlight(td);
input.focus();
if (text == 2) { // long text
return ajax(location.href + '&' + encodeURIComponent(td.id) + '=', function (request) {
return ajax(location.href + '&' + encodeURIComponent(td.id) + '=', request => {
if (request.responseText) {
input.value = request.responseText;
input.name = td.id;
@@ -749,11 +717,6 @@ function selectClick(event, text, warning) {
input.name = td.id;
input.selectionStart = pos;
input.selectionEnd = pos;
if (document.selection) {
var range = document.selection.createRange();
range.moveEnd('character', -input.value.length + pos);
range.select();
}
return true;
}
@@ -766,22 +729,20 @@ function selectClick(event, text, warning) {
* @this HTMLLinkElement
*/
function selectLoadMore(limit, loading) {
var a = this;
var title = a.innerHTML;
var href = a.href;
const a = this;
const title = a.innerHTML;
const href = a.href;
a.innerHTML = loading;
if (href) {
a.removeAttribute('href');
return !ajax(href, function (request) {
var tbody = document.createElement('tbody');
return !ajax(href, request => {
const tbody = document.createElement('tbody');
tbody.innerHTML = request.responseText;
qs('#table').appendChild(tbody);
if (tbody.children.length < limit) {
a.parentNode.removeChild(a);
} else {
a.href = href.replace(/\d+$/, function (page) {
return +page + 1;
});
a.href = href.replace(/\d+$/, page => +page + 1);
a.innerHTML = title;
}
});
@@ -794,11 +755,7 @@ function selectLoadMore(limit, loading) {
* @param Event
*/
function eventStop(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
event.stopPropagation();
}
@@ -807,10 +764,9 @@ function eventStop(event) {
* @param HTMLElement
*/
function setupSubmitHighlight(parent) {
for (var key in { input: 1, select: 1, textarea: 1 }) {
var inputs = qsa(key, parent);
for (var i = 0; i < inputs.length; i++) {
setupSubmitHighlightInput(inputs[i])
for (const key in { input: 1, select: 1, textarea: 1 }) {
for (const input of qsa(key, parent)) {
setupSubmitHighlightInput(input);
}
}
}
@@ -829,7 +785,7 @@ function setupSubmitHighlightInput(input) {
* @this HTMLInputElement
*/
function inputFocus() {
var submit = findDefaultSubmit(this);
const submit = findDefaultSubmit(this);
if (submit) {
alterClass(submit, 'default', true);
}
@@ -839,7 +795,7 @@ function inputFocus() {
* @this HTMLInputElement
*/
function inputBlur() {
var submit = findDefaultSubmit(this);
const submit = findDefaultSubmit(this);
if (submit) {
alterClass(submit, 'default');
}
@@ -856,9 +812,7 @@ function findDefaultSubmit(el) {
if (!el.form) {
return null;
}
var inputs = qsa('input', el.form);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
for (const input of qsa('input', el.form)) {
if (input.type == 'submit' && !input.style.zIndex) {
return input;
}
@@ -873,21 +827,7 @@ function findDefaultSubmit(el) {
* @param function
*/
function addEvent(el, action, handler) {
if (el.addEventListener) {
el.addEventListener(action, handler, false);
} else {
el.attachEvent('on' + action, handler);
}
}
/** Defer focusing element
* @param HTMLElement
*/
function focus(el) {
setTimeout(function () {
// this has to be an anonymous function because Firefox passes some arguments to setTimeout callback
el.focus();
}, 0);
el.addEventListener(action, handler, false);
}
/** Clone node and setup submit highlighting
@@ -895,13 +835,13 @@ function focus(el) {
* @return HTMLElement
*/
function cloneNode(el) {
var el2 = el.cloneNode(true);
var selector = 'input, select';
var origEls = qsa(selector, el);
var cloneEls = qsa(selector, el2);
for (var i=0; i < origEls.length; i++) {
var origEl = origEls[i];
for (var key in origEl) {
const el2 = el.cloneNode(true);
const selector = 'input, select';
const origEls = qsa(selector, el);
const cloneEls = qsa(selector, el2);
for (let i=0; i < origEls.length; i++) {
const origEl = origEls[i];
for (const key in origEl) {
if (/^on/.test(key) && origEl[key]) {
cloneEls[i][key] = origEl[key];
}
@@ -911,8 +851,8 @@ function cloneNode(el) {
return el2;
}
oninput = function (event) {
var target = event.target;
var maxLength = target.getAttribute('data-maxlength');
oninput = event => {
const target = event.target;
const maxLength = target.getAttribute('data-maxlength');
alterClass(target, 'maxlength', target.value && maxLength != null && target.value.length > maxLength); // maxLength could be 0
};

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