1
0
mirror of https://github.com/vrana/adminer.git synced 2025-09-03 19:32:36 +02:00

Compare commits

...

300 Commits

Author SHA1 Message Date
Jakub Vrana
eb43ea3025 Release 5.2.1 2025-04-11 22:26:41 +02:00
Jakub Vrana
2ba833409a non-MySQL: Parse '--' without trailing space as comment in SQL command (fix #1025, regression from 5.2.0) 2025-04-11 22:15:14 +02:00
Jakub Vrana
5eaaa498d3 PostgreSQL PDO: Fix bytea without primary key (fix #1021) 2025-04-11 15:29:59 +02:00
Jakub Vrana
746c0a7b0b Update JUSH 2025-04-11 14:53:33 +02:00
Jakub Vrana
b30526213d Lang: Display line of the update 2025-04-10 19:04:06 +02:00
Jakub Vrana
6819815b88 Don't display Loaded plugins with only driver plugins 2025-04-10 18:56:38 +02:00
Jakub Vrana
a83626c8af Fix import without primary key (fix #1017, regression from 5.1.1) 2025-04-10 17:58:22 +02:00
Jakub Vrana
d4ddbc0639 Import: Add margin (fix #1012) 2025-04-10 17:32:03 +02:00
Jakub Vrana
a6cb91f0d2 AdminerLoginOtp: Translate 2025-04-09 08:05:06 +02:00
Jakub Vrana
78d3ce830d Do not highlight table 0 as active 2025-04-09 06:27:09 +02:00
Jakub Vrana
88099b7dd7 Code style: Fix 2025-04-08 21:26:08 +02:00
Jakub Vrana
8bce359fae Fix search anywhere (fix #1004, regression from 5.1.1) 2025-04-08 20:41:44 +02:00
Jakub Vrana
8ca7066625 AdminerMenuLinks: Translation in single language version (fix #1001) 2025-04-08 20:11:47 +02:00
Jakub Vrana
51bcc2a064 Develop 2025-04-08 18:29:13 +02:00
Jakub Vrana
588af652d4 Release 5.2.0 2025-04-08 18:28:50 +02:00
Jakub Vrana
b0c345f9be Update Poslish translations 2025-04-08 17:03:02 +02:00
Jakub Vrana
b2677187f1 Autocomplete: Populate only on pages where useful 2025-04-08 16:57:25 +02:00
Jakub Vrana
52ee085ca7 Update German translation (bug #1001) 2025-04-08 14:33:58 +02:00
Jakub Vrana
00459b302a Translations: Remove trailing fullstops 2025-04-08 13:49:21 +02:00
Jakub Vrana
17598c7ab3 Fix typo in translation 2025-04-08 13:47:36 +02:00
Jakub Vrana
b489cec651 Remove doubled spaces 2025-04-08 13:36:26 +02:00
Jakub Vrana
6da8bb670a Plugins: Compatibility with PHP 5 2025-04-08 13:14:32 +02:00
Takashi SHIRAI
e601a3d8ce Update Japanese translation
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-04-08 13:10:05 +02:00
Jakub Vrana
efde7fcc6c Lang: Non-static $translations 2025-04-08 13:06:27 +02:00
Jakub Vrana
91b3526e8d Plugins: non-static $translations (fix #1000)
The main reason is that static properties are minified.
2025-04-08 12:57:03 +02:00
Jakub Vrana
a3ddd59015 Update externals 2025-04-08 12:54:06 +02:00
Jakub Vrana
7908d86c9f Doc: Plugin translations 2025-04-08 11:00:31 +02:00
Jakub Vrana
0cb41c63c7 Compile: Ignore $this->lang 2025-04-07 21:35:25 +02:00
Jakub Vrana
50de50571d AdminerSqlGemini: Version in User-Agent 2025-04-07 21:27:33 +02:00
Jakub Vrana
de38cb65b6 Designs: Fix logout background 2025-04-07 21:16:30 +02:00
Jakub Vrana
e6cc8bf91e AdminerConfig: Move link (fix #995) 2025-04-07 20:55:01 +02:00
Jakub Vrana
be6cf07d26 Foreign key: Avoid extra newline in error 2025-04-07 19:59:53 +02:00
Jakub Vrana
3f979793f7 Fix foreign key actions (regression from 5.1.1)
https://github.com/vrana/adminer/discussions/969#discussioncomment-12752117
2025-04-07 19:51:43 +02:00
Jakub Vrana
c8878d1652 CSS: Avoid footer shadow over text 2025-04-07 19:24:33 +02:00
Jakub Vrana
bf24198e68 Plugins: Link screenshot 2025-04-07 19:18:43 +02:00
Jakub Vrana
e33ead15e5 AdminerPlugin: Delete 2025-04-07 18:55:14 +02:00
Jakub Vrana
1087d55913 AdminerDotJs: Translate description 2025-04-07 18:53:38 +02:00
Jakub Vrana
38bdd0a961 lang.php: Add translation of plugin description 2025-04-07 18:26:31 +02:00
Jakub Vrana
3dd040abd1 Plugins: Translate descriptions (fix #994) 2025-04-07 18:26:28 +02:00
Jakub Vrana
3e455a4787 Plugins: Extend Adminer\Plugin 2025-04-07 17:02:16 +02:00
Jakub Vrana
95f14bca56 Plugins: Allow providing description 2025-04-07 15:54:31 +02:00
Jakub Vrana
121b77e866 Update German translation (bug #994) 2025-04-07 15:37:08 +02:00
Jakub Vrana
41c71f8854 Plugins: Defer syntax highlighting 2025-04-07 14:58:19 +02:00
Jakub Vrana
b0e5c1d6e4 Defer syntax highlighting 2025-04-07 14:58:19 +02:00
Jakub Vrana
7d3d46e509 Add driver plugins readme 2025-04-07 12:51:10 +02:00
Jakub Vrana
9bd23b1395 Compress served static files 2025-04-07 12:46:15 +02:00
Jakub Vrana
93f8a0e7ed Compress HTML by default 2025-04-07 12:33:51 +02:00
Jakub Vrana
e6810258bd Accessibility: Use <option>(label) as label 2025-04-07 11:43:52 +02:00
Jakub Vrana
aac223e279 Accessibility: Add <label> to <select> 2025-04-07 11:32:22 +02:00
Jakub Vrana
75f9aa9e7d Compile: JUSH autocomplete 2025-04-07 11:13:28 +02:00
Jakub Vrána
05170899dc Update PayPal link 2025-04-07 09:10:40 +02:00
Jakub Vrana
69890ffc48 Driver plugins: Compatibility with compiled version 2025-04-07 07:53:42 +02:00
Jakub Vrana
025d77c7c6 Move comment 2025-04-07 06:48:49 +02:00
Jakub Vrana
de95807eaf CSS: Fix typo 2025-04-06 17:12:36 +02:00
Jakub Vrana
833fa22e3f Update comment 2025-04-06 16:41:25 +02:00
Jakub Vrana
9e0aa1b91a Update translations in plugins 2025-04-06 16:36:10 +02:00
Jakub Vrana
f2e1243fb7 Generalize updating translations 2025-04-06 16:36:10 +02:00
Jakub Vrana
0d683fd57c CSS: Add --dim variable 2025-04-06 16:36:10 +02:00
Matthaiks
9683342792 Add Polish translation 2025-04-06 16:36:03 +02:00
Matthaiks
cdd8448908 Update Polish translation 2025-04-06 14:51:56 +02:00
Jakub Vrana
3578517e15 CSS: Add background to logout (fix #987)
Also simplify shadows which might fix #986.
2025-04-06 14:39:03 +02:00
Jakub Vrana
2d853b633a AdminerMenuLinks: Use more descriptive labels 2025-04-06 08:24:18 +02:00
Jakub Vrana
089093d4b1 AdminerConfig: Support permalinks 2025-04-06 08:15:20 +02:00
Jakub Vrana
39b977bd80 AdminerConfig: Configure using adminer.css 2025-04-06 08:15:18 +02:00
Jakub Vrana
0aff6e06a6 Highlight current table in menu when editing Check 2025-04-06 07:29:23 +02:00
Jakub Vrana
b36bd12291 Use plural for index columns 2025-04-06 07:29:23 +02:00
Jakub Vrana
fb32c10f94 Plugins: Move lang() to Adminer\Plugin 2025-04-06 07:29:23 +02:00
Jakub Vrana
7ff1d82903 New plugin: Configure menu table links 2025-04-06 07:29:23 +02:00
Jakub Vrana
cca943015f New plugin: Configure options by end-users and store them to a cookie 2025-04-06 07:29:16 +02:00
Jakub Vrana
48f82b3454 Move loaded plugins down (dg/adminer#49) 2025-04-05 23:03:42 +02:00
Jakub Vrana
91d43b574b Designs: Add default dark 2025-04-05 07:28:57 +02:00
Jakub Vrana
6258c975c1 Designs: Adapt to iPad 2025-04-05 07:04:00 +02:00
Jakub Vrana
e12d12524f CSS: Display menu on modern iPads (fix #985)
https://www.ios-resolution.com/
2025-04-04 22:23:40 +02:00
Jakub Vrana
a43a9fbd52 Designs: Fix on mobile (fix #985) 2025-04-04 20:54:26 +02:00
Jakub Vrana
79ae6d8541 Designs: Fix logo 2025-04-04 19:54:12 +02:00
Richard Kapička
60ea595cdc Wrap Loaded plugins into div 2025-04-04 19:38:46 +02:00
Jakub Vrana
ebd2e4f5b4 MS SQL: Limit one INSERT in export to 1000 rows (fix #983) 2025-04-04 19:13:03 +02:00
Jakub Vrana
806efe5e2d AdminerSqlGemini: Add Czech translation 2025-04-04 18:52:18 +02:00
Jakub Vrana
54d3239cfb Editor: Do not remove .icon-move 2025-04-04 18:31:17 +02:00
Jakub Vrana
7198ad5229 Anchor Logout button to body, not to viewport (bug #979)
This reverts commit e277d05162.
2025-04-04 18:27:45 +02:00
Jakub Vrana
01d8fe112c Do not include compiled logo twice 2025-04-04 17:44:58 +02:00
Jakub Vrana
510cd2e068 Move comment 2025-04-04 17:34:18 +02:00
Jakub Vrana
7dbd929600 CSS: Link logo 2025-04-04 17:29:14 +02:00
Jakub Vrana
6c9ac63508 Editor: Split sending e-mails to a plugin 2025-04-04 17:26:56 +02:00
Jakub Vrana
ab0dc19c9f Plugins: Allow formatting translations using Adminer\lang_format() 2025-04-04 17:18:34 +02:00
Jakub Vrana
4aa7647a55 Use LANG instead of get_lang() 2025-04-04 17:18:34 +02:00
Jakub Vrana
db2709d15a Update Spanish translation 2025-04-04 16:14:54 +02:00
Jakub Vrana
5f5d114dac Add missing space 2025-04-04 09:32:54 +02:00
Jakub Vrana
dadaa02b52 Update German translation (fix #981) 2025-04-04 09:30:56 +02:00
Jakub Vrana
e277d05162 CSS: Move Logout on mobile (fix #979) 2025-04-04 09:28:28 +02:00
Jakub Vrana
ef3946ee52 CSS: Align logo with menu 2025-04-04 08:41:55 +02:00
Jakub Vrana
57f6d296fb CSS: Match -dark only in filename 2025-04-04 08:39:40 +02:00
Jakub Vrana
bd35de5a6b Designs: Remove duplicate logo 2025-04-04 08:34:55 +02:00
Jakub Vrana
64816a3d7a CSS: Inlline icon 2025-04-04 08:02:36 +02:00
Jakub Vrana
6e8c89ee71 CSS: Add logo 2025-04-04 07:59:17 +02:00
Jakub Vrana
3f6136205d CSS: Add border-radius to fieldset (fix #980) 2025-04-03 23:17:02 +02:00
Jakub Vrana
1500a3f2c8 Mobile: Move menu 1px up (fix #976) 2025-04-03 22:11:22 +02:00
Jakub Vrana
63236ea5d1 Mobile: Add shadow to menu (fix #975) 2025-04-03 22:07:42 +02:00
Jakub Vrana
c6398736ac Update JUSH 2025-04-03 22:01:19 +02:00
Jakub Vrana
dc25ccec0d Tests: Search in tables 2025-04-03 18:46:10 +02:00
Jakub Vrana
364d18f166 AdminerSqlGemini: Display errors 2025-04-03 17:43:27 +02:00
Matrixman
e7c2d09fa8 New version of design rmSOFT 2025-04-03 16:24:43 +02:00
Jakub Vrana
646af54e7b Fix type for search anywhere (fix #973) 2025-04-03 16:05:52 +02:00
Jakub Vrana
96191587cc New plugin: Set up driver, server and database in Adminer Editor 2025-04-03 15:50:30 +02:00
Jakub Vrana
dea16493ff Editor: Fix SQLite example 2025-04-03 15:20:39 +02:00
Jakub Vrana
d7c14b16b1 Autocomplete SQL commands 2025-04-03 15:10:22 +02:00
Jakub Vrana
986433dd3a Changes: Improve message 2025-04-03 12:07:46 +02:00
Jakub Vrana
a70089f8ce PostgreSQL: Support COPY FROM stdin in SQL query (fix #942) 2025-04-03 11:41:43 +02:00
Jakub Vrana
49eefa2585 Call credentials() from connect() 2025-04-03 10:23:31 +02:00
Jakub Vrana
3693992650 non-MySQL: Parse '--' as comment in SQL command (bug SF-842) 2025-04-03 09:17:55 +02:00
Jakub Vrana
30847c97eb Do not edit NULL values by Modify (fix #967) 2025-04-03 08:57:49 +02:00
Jakub Vrana
9b179bca21 AdminerSqlGemini: Add default key 2025-04-03 08:28:02 +02:00
Jakub Vrana
ce03585210 MySQL: Display number of found rows in group queries (regression from 5.1.1) 2025-04-02 21:32:28 +02:00
Jakub Vrana
22dc4ff444 SQLite: Add missing border to Status 2025-04-02 21:12:48 +02:00
Jakub Vrana
2b6a262d56 Develop 2025-04-02 19:56:59 +02:00
Jakub Vrana
5fdcfd0978 Release 5.1.1 2025-04-02 19:56:11 +02:00
Jakub Vrana
5b7dfbec11 Select: Allow ordering by COUNT(*) (fix #966, regression from 5.0.2) 2025-04-02 18:01:14 +02:00
Jakub Vrana
76dd19b69f Japanese: Update message 2025-04-02 17:41:40 +02:00
Takashi SHIRAI
104edc75fa Plugins autoloading: Modify the Japanese message.
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-04-02 17:35:12 +02:00
Jakub Vrana
bd85f19a44 AdminerBeforeUnload: No error for Ctrl+Enter 2025-04-01 20:56:35 +02:00
Jakub Vrana
2db83e9b8f Fix JS error on Ctrl+Shift+Enter on select= 2025-04-01 20:45:39 +02:00
Jakub Vrana
5e3990e473 Turkish: Fix date hint (thanks Shirai Takashi) 2025-04-01 20:27:37 +02:00
Takashi SHIRAI
3e9d47ad08 Modify the Japanese messages.
Signed-off-by: Takashi SHIRAI <shirai@nintendo.co.jp>
2025-04-01 20:25:11 +02:00
Jakub Vrana
04ed73be26 Explicitly mark nullable params (thanks to @dg) 2025-04-01 19:14:44 +02:00
Jakub Vrana
01ea001f22 Explicitly mark nullable params (thanks to @dg) 2025-04-01 19:09:46 +02:00
Jakub Vrana
634b0aaacf AdminerLoginServers: Add comment (bug #965) 2025-04-01 18:55:34 +02:00
Jakub Vrana
06469660e8 Login: Fix hiding server with AdminerLoginServers 2025-04-01 18:27:04 +02:00
Jakub Vrana
e0629c6445 Fix lang.php 2025-04-01 17:21:32 +02:00
makss
1f58f664ae Update Ukrainian and Russian translation 2025-04-01 17:21:23 +02:00
Jakub Vrana
d9956c8a5c New plugin: Use Monaco Editor for syntax highlighting 2025-04-01 16:37:12 +02:00
Jakub Vrana
a03b05ceb4 AdminerPrism: Add border and resize 2025-04-01 12:31:45 +02:00
Jakub Vrana
b386463dcf AdminerPrism: Use Code Editor for highlighting 2025-04-01 12:10:44 +02:00
Jakub Vrana
954cc17312 AdminerPrism: Add Code Editor 2025-04-01 11:57:50 +02:00
Jakub Vrana
45a68bd6f7 New plugin: Use Prism for syntax highlighting 2025-04-01 10:00:59 +02:00
Jakub Vrana
9ec24b9244 Syntax highlighting: Hook AJAX 2025-04-01 09:52:04 +02:00
Jakub Vrana
717f0b0e10 AdminerCodemirror: Use latest version 2025-04-01 07:33:46 +02:00
Jakub Vrana
a27f0953a6 AdminerCodemirror: Use allFields (bug #962) 2025-03-31 21:45:06 +02:00
Jakub Vrana
177429d59f Optimize retrieving columns for schema 2025-03-31 21:45:06 +02:00
Jakub Vrana
9f3f3b9515 Plugins: Allow changing CSP by more plugins 2025-03-31 20:20:26 +02:00
Jakub Vrana
595c228175 AdminerCodemirror: Use jsDelivr by default 2025-03-31 20:17:30 +02:00
Jakub Vrana
058a9ec2ce Tests: Add comment 2025-03-31 19:42:34 +02:00
Jakub Vrana
7f6ae00b0d AdminerSqlGemini: Empty query 2025-03-31 19:37:27 +02:00
Jakub Vrana
cfde891ea4 Unify textarea highlighting 2025-03-31 19:30:23 +02:00
Jakub Vrana
63ab8561be Auth: Set token after unsuccessful login
Broken by d59830c
2025-03-31 18:40:18 +02:00
Jakub Vrana
5b095e9f4e SQL: Stop session 2025-03-31 18:39:02 +02:00
Jakub Vrana
b2fb3587fd AdminerForeignSystem: Add more views 2025-03-31 18:38:46 +02:00
Jakub Vrana
5cfd3f422c Select: Align numeric null right 2025-03-31 17:14:02 +02:00
Jakub Vrana
eb6b23e014 Fix type when missing $field
This happens e.g. for INNODB_BUFFER_PAGE.IS_STALE which is undeclared but returned.
2025-03-31 17:07:27 +02:00
Jakub Vrana
10bc856ebe Call static method 2025-03-31 12:35:45 +02:00
Jakub Vrana
7dd214a03c Use a helper 2025-03-31 12:24:27 +02:00
Jakub Vrana
2504ea23c4 Tests: Fix MySQL PDO 2025-03-31 11:23:43 +02:00
Jakub Vrana
e6c0c8ab6b Fix type of select_db() 2025-03-31 11:08:37 +02:00
Jakub Vrana
366342985d Tests: Fix generating PDO after c7140c2 2025-03-31 10:50:17 +02:00
Jakub Vrana
27c688b902 SQLite: Do not return unrelated auto_increment 2025-03-31 10:42:53 +02:00
Jakub Vrana
41964badb0 SQLite: Fix type of $auto_increment 2025-03-31 10:42:30 +02:00
Jakub Vrana
c76b4f1805 Update PhpShrink 2025-03-31 10:23:03 +02:00
Jakub Vrana
1b52d3a975 Localize help links 2025-03-31 10:18:17 +02:00
Jakub Vrana
695ce8c4da Use Lang::$translations instead of $translations 2025-03-31 10:18:14 +02:00
Jakub Vrana
30a8c4caca MS SQL: Fix type 2025-03-31 10:09:31 +02:00
Jakub Vrana
21347e6ef5 Initialize optional variable 2025-03-31 10:09:31 +02:00
Jakub Vrana
f2871266ad Tests: Fix 2025-03-31 10:09:31 +02:00
Jakub Vrana
79bebe77ba AdminerForeignSystem: Support new tables 2025-03-31 10:09:30 +02:00
Jakub Vrana
007c97d0d2 Fix converting long values to unique_idf
Found by PHP error: Trying to access array offset on null
2025-03-31 10:09:30 +02:00
Jakub Vrana
b50d19629f PHPStan: Use int for $limit 2025-03-31 10:09:30 +02:00
Jakub Vrana
016c1b2357 PHPStan: Fix types 2025-03-31 10:09:30 +02:00
Jakub Vrana
c05b1ac048 Tests: Add screenshots 2025-03-31 10:09:30 +02:00
Jakub Vrana
c64ee3d907 PostgreSQL: Fix login 2025-03-31 10:09:30 +02:00
Jakub Vrana
b64c80acc9 Schema: Reduce precision to pixels 2025-03-31 10:09:30 +02:00
Matrixman
fa22df0d7f New version of design rmSOFT
New version of design rmSOFT
2025-03-31 10:09:30 +02:00
Jakub Vrana
a93e4cb694 Return Db from connection()
It's not a real type declaration because compile.php passes stdClass here.
2025-03-31 10:09:30 +02:00
Jakub Vrana
7ee6f4f7ac Move connect() to Driver 2025-03-31 10:09:30 +02:00
Jakub Vrana
992561f75e Add comment 2025-03-31 10:09:30 +02:00
Jakub Vrana
65fd673d05 Remove ignored errors 2025-03-31 10:09:30 +02:00
Jakub Vrana
4b262ededa Remove unnecessary braces 2025-03-31 10:09:29 +02:00
Jakub Vrana
0d67bd9eb8 Update docs 2025-03-31 10:09:29 +02:00
Jakub Vrana
9b6943d5af Add helper for $connection2 2025-03-29 22:21:18 +01:00
Jakub Vrana
291ae7f1ac Fix types of $connection2 2025-03-29 22:17:16 +01:00
Jakub Vrana
712d96b22c Use connection() instead of $connection 2025-03-29 22:10:20 +01:00
Jakub Vrana
168ea5ae6d Use driver() instead of $driver 2025-03-29 22:05:31 +01:00
Jakub Vrana
845445baad Use adminer() instead of $adminer 2025-03-29 21:43:29 +01:00
Jakub Vrana
87f149ce1d Move HTML function, fix types 2025-03-29 18:10:32 +01:00
Jakub Vrana
79f5280f3d AdminerCodemirror: Simplify code 2025-03-29 17:50:43 +01:00
Jakub Vrana
82450b1ad2 Changelog: Use bullets 2025-03-29 17:00:15 +01:00
Jakub Vrana
eeb13253a8 Add comment 2025-03-29 16:25:35 +01:00
Jakub Vrana
6a3161cd49 Fix type of permanentLogin() 2025-03-29 15:21:25 +01:00
Jakub Vrana
141db3cb8d Add comment 2025-03-29 15:17:39 +01:00
Jakub Vrana
fd1661d811 Doc: Move type stripping 2025-03-29 15:01:51 +01:00
Jakub Vrana
6ea34e3b9a CSS: Add --fg color 2025-03-29 14:51:25 +01:00
Jakub Vrana
e4ed78ff7a Designs: Define --bg 2025-03-29 13:15:14 +01:00
Jakub Vrana
262366b120 CSS: Use --bg 2025-03-29 11:31:45 +01:00
Jakub Vrana
79fbf9c58a CSS: Hide menu on mobile 2025-03-29 11:11:57 +01:00
Jakub Vrana
6cf3d5d2b8 Inline GIF to CSS 2025-03-29 08:49:51 +01:00
Jakub Vrana
5b329ae720 JUSH: Fix opening help to new window 2025-03-28 23:29:14 +01:00
Jakub Vrana
225b6671c7 Delete function moved to get_val 2025-03-28 23:00:47 +01:00
Jakub Vrana
cec6db144f Compile: Fix single driver 2025-03-28 22:59:58 +01:00
Jakub Vrana
aceb4ce7a5 Move $drivers to SqlDriver 2025-03-28 22:58:32 +01:00
Jakub Vrana
1f88485a3c Rename variable 2025-03-28 22:58:03 +01:00
Jakub Vrana
4e1e638f98 MySQLi: Use default credentials
Accidentally removed by c96894e.
2025-03-28 22:29:00 +01:00
Jakub Vrana
03d0daff5c Remove global $permanent 2025-03-28 22:29:00 +01:00
Jakub Vrana
1eb7538e8c PHPStan: Mute LANG not found 2025-03-28 22:29:00 +01:00
Jakub Vrana
29339c5223 Db: Unify connection error handling 2025-03-28 22:28:52 +01:00
Jakub Vrana
d5bba383ea Check numeric table names after error 2025-03-28 20:45:27 +01:00
Jakub Vrana
d59830c7b2 Delete $has_token 2025-03-28 20:40:28 +01:00
Jakub Vrana
ff37ac1d35 MySQLi: Check for more results (fix #955) 2025-03-28 20:13:06 +01:00
Jakub Vrana
75c94cec6b Fix types 2025-03-28 19:48:25 +01:00
Jakub Vrana
65d97caeb9 Move $error to Adminer::$error 2025-03-28 19:48:23 +01:00
Jakub Vrana
3cfae4b8f4 Pass $error as param 2025-03-28 19:04:13 +01:00
Jakub Vrana
e219ef9ad1 Move $token to get_token() 2025-03-28 19:04:11 +01:00
Jakub Vrana
74457f0895 Move $HTTPS to HTTPS 2025-03-28 18:39:30 +01:00
Jakub Vrana
f6d311457e Move $langs to langs() 2025-03-28 18:39:30 +01:00
Jakub Vrana
adab18da78 Bump Composer PHP version 2025-03-28 18:39:30 +01:00
Jakub Vrana
508baa8c6b Move $LANG and get_lang() to LANG 2025-03-28 18:39:30 +01:00
Jakub Vrana
f0920af6b7 Move $VERSION and version() to VERSION 2025-03-28 18:39:30 +01:00
Jakub Vrana
06f0a926dd Docs: update 2025-03-28 18:39:28 +01:00
Jakub Vrana
81c5ae33ab Docs: wrap 2025-03-28 17:43:13 +01:00
Jakub Vrana
4cbe50fd49 Tests: select with where and order 2025-03-28 17:43:13 +01:00
Jakub Vrana
2396397b75 Elasticsearch: Make it work with Elasticsearch 8 2025-03-28 17:43:11 +01:00
Jakub Vrana
bd823716fc Elastic: Fix types 2025-03-28 16:17:26 +01:00
Jakub Vrana
7a19fa67fd Integrate Db::result in get_val 2025-03-28 15:41:38 +01:00
Jakub Vrana
195341d075 Split editFunctions 2025-03-28 15:41:38 +01:00
Jakub Vrana
46f6a96c95 Doc-comments: Fix type errors 2025-03-28 15:41:36 +01:00
Jakub Vrana
dc38a7ded3 Plugins: Move operators to a method 2025-03-28 14:30:00 +01:00
Jakub Vrana
c7140c2158 Tests: Run from /adminer/
To run them on the compiled version, rename adminer.php to index.php and start a web server one directory up.
2025-03-28 12:51:45 +01:00
Jakub Vrana
c2c8992dd0 PHPStan: Fix errors in Plugins 2025-03-28 12:47:09 +01:00
Jakub Vrana
a691bcbf15 Rename function with the same name as Driver::select 2025-03-28 12:47:09 +01:00
Jakub Vrana
e3a4a214e6 Doc-comments: Remove redundant info 2025-03-28 12:47:09 +01:00
Jakub Vrana
a9143ccbdc Doc-comments: Fix type errors 2025-03-28 12:47:09 +01:00
Jakub Vrana
c169c55d70 Call Plugins from Adminer class 2025-03-28 12:47:09 +01:00
Jakub Vrana
54f3437a6a Plugins: Simplify calling
The class Plugins don't extend Adminer anymore. Adminer is now treated like any other plugin.
Hooks are now registered in the constructor and they are called dynamically.
Apart from being much more pleasant to work with, it shaves 7 kB from the compiled file.
2025-03-28 12:47:09 +01:00
Jakub Vrana
96178b83ad Compile: Strip types 2025-03-28 12:47:08 +01:00
Jakub Vrana
b948f77af4 Doc-comments: Fix type errors 2025-03-28 12:47:06 +01:00
Jakub Vrana
54f8d731b3 Doc-comments: Sync method signatures 2025-03-28 12:45:02 +01:00
Jakub Vrana
ab4208dcb8 Doc-comments: Declare type properties 2025-03-28 12:45:02 +01:00
Jakub Vrana
5e88dae4e2 Doc-comments: Format 2025-03-28 12:45:02 +01:00
Jakub Vrana
45c045382a Doc-comments: Move return types to declaration 2025-03-28 12:45:02 +01:00
Jakub Vrana
641ee4ff26 Doc-comments: Move param types to declaration 2025-03-28 12:45:02 +01:00
Jakub Vrana
69073d9d54 AdminerLoginSsl: Document type 2025-03-28 12:45:02 +01:00
Jakub Vrana
911f3b71b7 Doc-comments: Add param names 2025-03-28 12:45:02 +01:00
Jakub Vrana
3bde36b68e Tests: Invalid table 2025-03-28 12:45:01 +01:00
Jakub Vrana
d47d3cb4c5 Document error revealed by PHPStan 2025-03-27 21:05:32 +01:00
Jakub Vrana
0cdc18d22c Tests PostgreSQL: Sequence and schema 2025-03-27 21:05:32 +01:00
Jakub Vrana
47c533db4d Test wrong password 2025-03-27 21:05:32 +01:00
Jakub Vrana
feaed0497a Tests: Add test adder 2025-03-27 21:05:32 +01:00
Jakub Vrana
104132de36 Fix errors discovered by tests 2025-03-27 21:05:31 +01:00
Jakub Vrana
4d22e8fd4e PostgreSQL: Fix PHP warning when creating new routine 2025-03-27 18:56:10 +01:00
Jakub Vrana
a2ff6a7fb1 PostgreSQL: Unuse deleted fetch_field 2025-03-27 18:47:05 +01:00
Jakub Vrana
b23bf6c055 PHPStan: Fix more errors 2025-03-27 18:39:48 +01:00
Jakub Vrana
e2deed9a02 Use common parent for Db 2025-03-27 18:39:47 +01:00
Jakub Vrana
0578b5c490 JS: Add 'use strict' 2025-03-27 10:27:46 +01:00
Jakub Vrana
81ae16bce1 JS: Remove forgotten log 2025-03-27 07:23:34 +01:00
Jakub Vrana
806aa51f48 AdminerSqlGemini: Make work with CodeMirror 2025-03-27 07:22:51 +01:00
Jakub Vrana
8f2a829b2e WYMeditor not updated since 2014 2025-03-27 07:13:17 +01:00
Jakub Vrana
36b44248aa Travis is not free anymore 2025-03-27 07:00:00 +01:00
Jakub Vrana
23f5d64d75 Compile: Fix pgsql (fix #956) 2025-03-26 22:23:58 +01:00
Jakub Vrana
584d04b5b3 PHPStan: Check level 8 without level 7 2025-03-26 21:54:17 +01:00
Jakub Vrana
d3b53d9d9c PHPStan: Fix level 6 errors 2025-03-26 21:54:00 +01:00
Jakub Vrana
c96894ecd4 PHPStan: Fix level 5 errors 2025-03-26 19:29:50 +01:00
Jakub Vrana
c78299a3f6 PHPStan: Fix level 4 errors 2025-03-26 18:32:45 +01:00
Jakub Vrana
53d5e7b60a PHPStan: Check only one driver 2025-03-26 18:21:02 +01:00
Jakub Vrana
d77ed18842 Separate queries(null) 2025-03-26 17:04:30 +01:00
Jakub Vrana
309fdb0d86 PHPStan: Fix level 3 errors 2025-03-26 16:57:58 +01:00
Jakub Vrana
7e5757f8b4 PHPStan: Fix level 2 errors 2025-03-26 16:22:15 +01:00
Jakub Vrana
d39cc24c61 PHPStan: Fix level 1 errors 2025-03-26 13:49:11 +01:00
Jakub Vrana
3de9b23156 PHPStan: Use @return void
PHPStan then warns abouts using the return value
2025-03-26 13:14:12 +01:00
Jakub Vrana
63c258a7f9 PHPStan: Fix level 0 errors 2025-03-26 13:14:10 +01:00
Jakub Vrana
f75f0aacfe SQLite: Fix non-PDO driver after 99163fe 2025-03-26 11:39:06 +01:00
Jakub Vrana
a60e00bf72 Use Adminer\Plugins 2025-03-26 11:10:37 +01:00
Jakub Vrana
109b0df6de Readme: Remove duplicite information 2025-03-26 10:34:11 +01:00
Jakub Vrana
6e7158537f Add comment 2025-03-26 10:25:13 +01:00
Jakub Vrana
c5f87110ff Notices: Use idx() 2025-03-26 10:21:36 +01:00
Jakub Vrana
1a2ae0e29e AdminerDarkSwitcher: Work with compiled version (bug #926) 2025-03-26 07:28:47 +01:00
Jakub Vrana
1b8a428d2f Notices: Avoid accessing offset on null
Thanks to @peterpp at 62017e3.
2025-03-26 07:20:10 +01:00
Jakub Vrana
d3be21e000 Tests: Add schema 2025-03-26 07:18:46 +01:00
Jakub Vrana
012562571a MySQL: Simplify condition in fk_support 2025-03-26 03:29:22 +01:00
Jakub Vrana
41aad5bc37 Doc-comment: Use type aliases for arrays
Type aliases could be defined either globally (https://phpstan.org/writing-php-code/phpdoc-types#global-type-aliases) or just for a class.
I prefer having them at the place where they are created.
2025-03-26 02:43:08 +01:00
Jakub Vrana
cd5ccddd22 Display error for invalid table 2025-03-26 01:37:53 +01:00
Jakub Vrana
cccc784da4 Always return array from table_status() 2025-03-26 01:34:48 +01:00
Jakub Vrana
cd207238b7 Move icons to CSS 2025-03-26 00:08:16 +01:00
Jakub Vrana
67fa4c2a6f Schema: Move style to CSS 2025-03-25 22:35:31 +01:00
Jakub Vrana
eac7d042ed Avoid <optgroup> in <datalist> 2025-03-25 22:04:19 +01:00
Jakub Vrana
a2077070af CSS: Invert icons in dark mode 2025-03-25 21:47:25 +01:00
Jakub Vrana
26adca1003 Simplify designs.php 2025-03-25 21:10:02 +01:00
Jakub Vrana
76d810faca lucas-sandery design: Icons with uncompiled version (fix #954) 2025-03-25 21:07:34 +01:00
Jakub Vrana
db0e44221b Doc-comments: Use special PHPStan types 2025-03-25 15:18:47 +01:00
Jakub Vrana
01e2fe4234 Doc-comments: Use array shapes in @return
This uses https://phpstan.org/writing-php-code/phpdoc-types#array-shapes
I'm not going to do this in @param, it would be better to use https://phpstan.org/writing-php-code/phpdoc-types#global-type-aliases
2025-03-25 15:08:13 +01:00
Jakub Vrana
4d2b5144b1 Doc-comments: Improve array @var 2025-03-25 14:41:26 +01:00
Jakub Vrana
2ee325183b Doc-comment: Improve array @param
This uses syntax from https://phpstan.org/writing-php-code/phpdoc-types#general-arrays.

int[] means an array of ints with arbitrary keys (usually strings)
list<string> means an array of strings with sequential integer keys starting at 0
list<string>[] means an arbitrary array of string lists
list<string[]> means list of arbitrary string arrays
string[][] means two dimensional array with arbitrary keys in both dimensions
array was left in the comments for https://phpstan.org/writing-php-code/phpdoc-types#array-shapes
2025-03-25 14:31:27 +01:00
Jakub Vrana
26aa48122f Doc-comments: Improve array @return 2025-03-25 13:27:54 +01:00
Jakub Vrana
19b7358452 AdminerSqlGemini: Highlight button 2025-03-25 07:33:30 +01:00
Jakub Vrana
a1080ea8dc JS: Simplify SubmitHighlight 2025-03-25 07:03:42 +01:00
Jakub Vrana
9b1b779dbd AdminerSqlGemini: Handle Ctrl+Enter 2025-03-25 06:56:00 +01:00
Jakub Vrana
4bbbea2fbe AdminerSqlGemini: Wrap returned text to comment 2025-03-25 06:49:34 +01:00
Jakub Vrana
16e49d27cb AdminerSqlGemini: Return more columns by default 2025-03-25 06:29:41 +01:00
Jakub Vrana
190d91a0f9 AdminerSqlGemini: Send vendor 2025-03-25 06:17:27 +01:00
Jakub Vrana
2c72b879e9 Simplify saving flavor 2025-03-25 06:15:09 +01:00
Jakub Vrana
001f5ac21a AdminerSqlGemini: Avoid button jumping 2025-03-25 06:09:00 +01:00
Jakub Vrana
b13c76149f Add comment 2025-03-25 06:08:54 +01:00
Jakub Vrana
27a5aeea86 AdminerSqlLog: Update comment 2025-03-24 23:49:30 +01:00
Jakub Vrana
5a1be8ae65 More developer notes 2025-03-24 19:17:18 +01:00
Jakub Vrana
5dea23a07a Develop 2025-03-24 17:36:47 +01:00
220 changed files with 5021 additions and 4570 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
github: vrana github: vrana
patreon: jakubvrana patreon: jakubvrana
custom: ["https://www.paypal.com/donate/?item_name=Donation+to+Adminer&business=jakub%40vrana.cz"] custom: ["https://www.paypal.com/donate/?hosted_button_id=6PK5VNUCFT3FG"]

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@
/adminer*.php /adminer*.php
/editor*.php /editor*.php
/tests/pdo-*.html /tests/pdo-*.html
/tests/screenshots/
/tests/cropped/
/vendor/ /vendor/
adminer-plugins/ adminer-plugins/
adminer-plugins.php adminer-plugins.php

View File

@@ -1,13 +0,0 @@
language: php
php:
- 5.6
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
- 8.1
- 8.2
- 8.3
- 8.4
script: git diff --name-only $TRAVIS_COMMIT_RANGE | grep '\.php$' | xargs -n1 -P8 php -l | grep -v 'No syntax errors'; test $? -eq 1

View File

@@ -1,3 +1,35 @@
## Adminer 5.2.1 (released 2025-04-11)
- Fix search anywhere (bug #1004, regression from 5.1.1)
- Fix import without primary key (bug #1017, regression from 5.1.1)
- PostgreSQL PDO: Fix bytea without primary key (bug #1021)
- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug #1025, regression from 5.2.0)
## Adminer 5.2.0 (released 2025-04-08)
- Autocomplete SQL commands
- Do not edit NULL values by Modify (bug #967)
- Fix foreign key actions (regression from 5.1.1)
- MySQL: Display number of found rows in group queries (regression from 5.1.1)
- PostgreSQL: Support COPY FROM stdin in SQL query (bug #942)
- non-MySQL: Parse '--' without trailing space as comment in SQL command (bug SF-842)
- MS SQL: Limit one INSERT in export to 1000 rows (bug #983)
- CSS: Add logo
- Editor: Move mass sending e-mails to a plugin
- Plugins: Support translations by extending Adminer\Plugin
- New plugin: Configure options by end-users and store them to a cookie
- New plugin: Configure menu table links
- New plugin: Set up driver, server and database in Adminer Editor
## Adminer 5.1.1 (released 2025-04-02)
- Export: Fix tar (regression from 5.0.3)
- Select: Allow ordering by COUNT(*) (bug #966, regression from 5.0.2)
- Optimize retrieving columns for schema
- Elasticsearch: Make it work with Elasticsearch 8
- CSS: Hide menu on mobile
- CSS: Invert icons in dark mode
- Plugins: Allow changing CSP by more plugins
- New plugin: Use Monaco Editor for syntax highlighting
- New plugin: Use Prism for syntax highlighting
## Adminer 5.1.0 (released 2025-03-24) ## Adminer 5.1.0 (released 2025-03-24)
- Display collation at table structure if different from table - Display collation at table structure if different from table
- Ctrl+click in select moves the cursor in modern browsers - Ctrl+click in select moves the cursor in modern browsers

View File

@@ -7,10 +7,10 @@
## Features ## Features
- **Supports:** MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MS SQL, Oracle - **Supports:** MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MS SQL, Oracle
- **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse, IMAP - **Plugins for:** Elasticsearch, SimpleDB, MongoDB, Firebird, ClickHouse, IMAP
- **Requirements:** PHP 5.3+ - **Requirements:** PHP 5.3+ (compiled file), PHP 7.4+ (source codes)
## Screenshot ## Screenshot
![Screenshot](https://www.adminer.org/static/screenshots/table.png) ![Table structure](https://www.adminer.org/static/screenshots/table.png)
## Installation ## Installation
If downloaded from Git then run: `git submodule update --init` If downloaded from Git then run: `git submodule update --init`
@@ -18,33 +18,9 @@ If downloaded from Git then run: `git submodule update --init`
- `adminer/index.php` - Run development version of Adminer - `adminer/index.php` - Run development version of Adminer
- `editor/index.php` - Run development version of Adminer Editor - `editor/index.php` - Run development version of Adminer Editor
- `editor/example.php` - Example customization - `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 - `compile.php` - Create a single file version
- `lang.php` - Update translations - `lang.php` - Update translations
- `tests/*.html` - Katalon Recorder test suites - `tests/*.html` - Katalon Recorder test suites
## Plugins ## 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/). There are several plugins distributed with Adminer, as well as many user-contributed plugins listed 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

@@ -19,13 +19,14 @@ foreach ($routine["fields"] as $i => $field) {
if (!$error && $_POST) { if (!$error && $_POST) {
$call = array(); $call = array();
foreach ($routine["fields"] as $key => $field) { foreach ($routine["fields"] as $key => $field) {
$val = "";
if (in_array($key, $in)) { if (in_array($key, $in)) {
$val = process_input($field); $val = process_input($field);
if ($val === false) { if ($val === false) {
$val = "''"; $val = "''";
} }
if (isset($out[$key])) { if (isset($out[$key])) {
$connection->query("SET @" . idf_escape($field["field"]) . " = $val"); connection()->query("SET @" . idf_escape($field["field"]) . " = $val");
} }
} }
$call[] = (isset($out[$key]) ? "@" . idf_escape($field["field"]) : $val); $call[] = (isset($out[$key]) ? "@" . idf_escape($field["field"]) : $val);
@@ -33,31 +34,31 @@ if (!$error && $_POST) {
$query = (isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . table($PROCEDURE) . "(" . implode(", ", $call) . ")"; $query = (isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . table($PROCEDURE) . "(" . implode(", ", $call) . ")";
$start = microtime(true); $start = microtime(true);
$result = $connection->multi_query($query); $result = connection()->multi_query($query);
$affected = $connection->affected_rows; // getting warnings overwrites this $affected = connection()->affected_rows; // getting warnings overwrites this
echo $adminer->selectQuery($query, $start, !$result); echo adminer()->selectQuery($query, $start, !$result);
if (!$result) { if (!$result) {
echo "<p class='error'>" . error() . "\n"; echo "<p class='error'>" . error() . "\n";
} else { } else {
$connection2 = connect($adminer->credentials()); $connection2 = connect();
if (is_object($connection2)) { if ($connection2) {
$connection2->select_db(DB); $connection2->select_db(DB);
} }
do { do {
$result = $connection->store_result(); $result = connection()->store_result();
if (is_object($result)) { if (is_object($result)) {
select($result, $connection2); print_select_result($result, $connection2);
} else { } else {
echo "<p class='message'>" . lang('Routine has been called, %d row(s) affected.', $affected) echo "<p class='message'>" . lang('Routine has been called, %d row(s) affected.', $affected)
. " <span class='time'>" . @date("H:i:s") . "</span>\n" // @ - time zone may be not set . " <span class='time'>" . @date("H:i:s") . "</span>\n" // @ - time zone may be not set
; ;
} }
} while ($connection->next_result()); } while (connection()->next_result());
if ($out) { if ($out) {
select($connection->query("SELECT " . implode(", ", $out))); print_select_result(connection()->query("SELECT " . implode(", ", $out)));
} }
} }
} }
@@ -70,14 +71,14 @@ if ($in) {
foreach ($in as $key) { foreach ($in as $key) {
$field = $routine["fields"][$key]; $field = $routine["fields"][$key];
$name = $field["field"]; $name = $field["field"];
echo "<tr><th>" . $adminer->fieldName($field); echo "<tr><th>" . adminer()->fieldName($field);
$value = $_POST["fields"][$name]; $value = idx($_POST["fields"], $name);
if ($value != "") { if ($value != "") {
if ($field["type"] == "set") { if ($field["type"] == "set") {
$value = implode(",", $value); $value = implode(",", $value);
} }
} }
input($field, $value, (string) $_POST["function"][$name]); // param name can be empty input($field, $value, idx($_POST["function"], $name, "")); // param name can be empty
echo "\n"; echo "\n";
} }
echo "</table>\n"; echo "</table>\n";
@@ -90,9 +91,13 @@ if ($in) {
<pre> <pre>
<?php <?php
function pre_tr($s) { /** Format string as table row
* @return string HTML
*/
function pre_tr(string $s): string {
return preg_replace('~^~m', '<tr>', preg_replace('~\|~', '<td>', preg_replace('~\|$~m', "", rtrim($s)))); return preg_replace('~^~m', '<tr>', preg_replace('~\|~', '<td>', preg_replace('~\|$~m', "", rtrim($s))));
} }
$table = '(\+--[-+]+\+\n)'; $table = '(\+--[-+]+\+\n)';
$row = '(\| .* \|\n)'; $row = '(\| .* \|\n)';
echo preg_replace_callback( echo preg_replace_callback(

View File

@@ -7,7 +7,7 @@ $row = $_POST;
if ($row && !$error) { if ($row && !$error) {
if (JUSH == "sqlite") { if (JUSH == "sqlite") {
$result = recreate_table($TABLE, $TABLE, array(), array(), array(), 0, array(), $name, ($row["drop"] ? "" : $row["clause"])); $result = recreate_table($TABLE, $TABLE, array(), array(), array(), "", array(), "$name", ($row["drop"] ? "" : $row["clause"]));
} else { } else {
$result = ($name == "" || queries("ALTER TABLE " . table($TABLE) . " DROP CONSTRAINT " . idf_escape($name))); $result = ($name == "" || queries("ALTER TABLE " . table($TABLE) . " DROP CONSTRAINT " . idf_escape($name)));
if (!$row["drop"]) { if (!$row["drop"]) {
@@ -24,7 +24,7 @@ if ($row && !$error) {
page_header(($name != "" ? lang('Alter check') . ": " . h($name) : lang('Create check')), $error, array("table" => $TABLE)); page_header(($name != "" ? lang('Alter check') . ": " . h($name) : lang('Create check')), $error, array("table" => $TABLE));
if (!$row) { if (!$row) {
$checks = $driver->checkConstraints($TABLE); $checks = driver()->checkConstraints($TABLE);
$row = array("name" => $name, "clause" => $checks[$name]); $row = array("name" => $name, "clause" => $checks[$name]);
} }
?> ?>

View File

@@ -17,8 +17,8 @@ $orig_fields = array();
$table_status = array(); $table_status = array();
if ($TABLE != "") { if ($TABLE != "") {
$orig_fields = fields($TABLE); $orig_fields = fields($TABLE);
$table_status = table_status($TABLE); $table_status = table_status1($TABLE);
if (!$table_status) { if (count($table_status) < 2) { // there's only the Name field
$error = lang('No tables.'); $error = lang('No tables.');
} }
} }
@@ -140,7 +140,7 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
page_header(($TABLE != "" ? lang('Alter table') : lang('Create table')), $error, array("table" => $TABLE), h($TABLE)); page_header(($TABLE != "" ? lang('Alter table') : lang('Create table')), $error, array("table" => $TABLE), h($TABLE));
if (!$_POST) { if (!$_POST) {
$types = $driver->types(); $types = driver()->types();
$row = array( $row = array(
"Engine" => $_COOKIE["adminer_engine"], "Engine" => $_COOKIE["adminer_engine"],
"fields" => array(array("field" => "", "type" => (isset($types["int"]) ? "int" : (isset($types["integer"]) ? "integer" : "")), "on_update" => "")), "fields" => array(array("field" => "", "type" => (isset($types["int"]) ? "int" : (isset($types["integer"]) ? "integer" : "")), "on_update" => "")),
@@ -168,7 +168,10 @@ if (!$_POST) {
} }
$collations = collations(); $collations = collations();
$engines = $driver->engines(); if (is_array(reset($collations))) {
$collations = call_user_func_array('array_merge', array_values($collations));
}
$engines = driver()->engines();
// case of engine may differ // case of engine may differ
foreach ($engines as $engine) { foreach ($engines as $engine) {
if (!strcasecmp($engine, $row["Engine"])) { if (!strcasecmp($engine, $row["Engine"])) {
@@ -185,8 +188,8 @@ 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 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" : ""); 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) { if ($collations) {
echo "<datalist id='collations'>" . optionlist($collations) . "</datalist>"; echo "<datalist id='collations'>" . optionlist($collations) . "</datalist>\n";
echo (preg_match("~sqlite|mssql~", JUSH) ? "" : "<input list='collations' name='Collation' value='" . h($row["Collation"]) . "' placeholder='(" . lang('collation') . ")'>"); echo (preg_match("~sqlite|mssql~", JUSH) ? "" : "<input list='collations' name='Collation' value='" . h($row["Collation"]) . "' placeholder='(" . lang('collation') . ")'>\n");
} }
echo "<input type='submit' value='" . lang('Save') . "'>\n"; echo "<input type='submit' value='" . lang('Save') . "'>\n";
} }
@@ -230,7 +233,7 @@ if (support("partitioning")) {
echo '<tr>'; echo '<tr>';
echo '<td><input name="partition_names[]" value="' . h($val) . '" autocapitalize="off">'; echo '<td><input name="partition_names[]" value="' . h($val) . '" autocapitalize="off">';
echo ($key == count($row["partition_names"]) - 1 ? script("qsl('input').oninput = partitionNameChange;") : ''); echo ($key == count($row["partition_names"]) - 1 ? script("qsl('input').oninput = partitionNameChange;") : '');
echo '<td><input name="partition_values[]" value="' . h($row["partition_values"][$key]) . '">'; echo '<td><input name="partition_values[]" value="' . h(idx($row["partition_values"], $key)) . '">';
} }
echo "</table>\n</div></fieldset>\n"; echo "</table>\n</div></fieldset>\n";
} }

View File

@@ -3,7 +3,7 @@ namespace Adminer;
$row = $_POST; $row = $_POST;
if ($_POST && !$error && !isset($_POST["add_x"])) { // add is an image and PHP changes add.x to add_x if ($_POST && !$error && !$_POST["add"]) {
$name = trim($row["name"]); $name = trim($row["name"]);
if ($_POST["drop"]) { if ($_POST["drop"]) {
$_GET["db"] = ""; // to save in global history $_GET["db"] = ""; // to save in global history
@@ -60,7 +60,7 @@ if ($_POST) {
<form action="" method="post"> <form action="" method="post">
<p> <p>
<?php <?php
echo ($_POST["add_x"] || strpos($name, "\n") echo ($_POST["add"] || strpos($name, "\n")
? '<textarea autofocus name="name" rows="10" cols="40">' . h($name) . '</textarea><br>' ? '<textarea autofocus name="name" rows="10" cols="40">' . h($name) . '</textarea><br>'
: '<input name="name" autofocus value="' . h($name) . '" data-maxlength="64" autocapitalize="off">' : '<input name="name" autofocus value="' . h($name) . '" data-maxlength="64" autocapitalize="off">'
) . "\n" . ($collations ? html_select("collation", array("" => "(" . lang('collation') . ")") + $collations, $row["collation"]) . doc_link(array( ) . "\n" . ($collations ? html_select("collation", array("" => "(" . lang('collation') . ")") + $collations, $row["collation"]) . doc_link(array(
@@ -73,8 +73,8 @@ echo ($_POST["add_x"] || strpos($name, "\n")
<?php <?php
if (DB != "") { if (DB != "") {
echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', DB)) . "\n"; echo "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . confirm(lang('Drop %s?', DB)) . "\n";
} elseif (!$_POST["add_x"] && $_GET["db"] == "") { } elseif (!$_POST["add"] && $_GET["db"] == "") {
echo "<input type='image' class='icon' name='add' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>\n"; echo icon("plus", "add[0]", "+", lang('Add next')) . "\n";
} }
echo input_token(); echo input_token();
?> ?>

View File

@@ -54,7 +54,7 @@ if ($tables_views && !$error && !$_POST["search"]) {
page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true); page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true);
if ($adminer->homepage()) { if (adminer()->homepage()) {
if ($_GET["ns"] !== "") { if ($_GET["ns"] !== "") {
echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n"; echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n";
$tables_list = tables_list(); $tables_list = tables_list();
@@ -69,7 +69,7 @@ if ($adminer->homepage()) {
echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n"; echo " <input type='submit' name='search' value='" . lang('Search') . "'>\n";
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
if ($_POST["search"] && $_POST["query"] != "") { if ($_POST["search"] && $_POST["query"] != "") {
$_GET["where"][0]["op"] = $driver->convertOperator("LIKE %%"); $_GET["where"][0]["op"] = driver()->convertOperator("LIKE %%");
search_tables(); search_tables();
} }
} }
@@ -93,7 +93,7 @@ if ($adminer->homepage()) {
foreach ($tables_list as $name => $type) { foreach ($tables_list as $name => $type) {
$view = ($type !== null && !preg_match('~table|sequence~i', $type)); $view = ($type !== null && !preg_match('~table|sequence~i', $type));
$id = h("Table-" . $name); $id = h("Table-" . $name);
echo '<tr><td>' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array($name, $tables_views, true), "", "", "", $id); echo '<tr><td>' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array("$name", $tables_views, true), "", "", "", $id); // "$name" to check numeric table names
echo '<th>' . (support("table") || support("indexes") ? "<a href='" . h(ME) . "table=" . urlencode($name) . "' title='" . lang('Show structure') . "' id='$id'>" . h($name) . '</a>' : h($name)); echo '<th>' . (support("table") || support("indexes") ? "<a href='" . h(ME) . "table=" . urlencode($name) . "' title='" . lang('Show structure') . "' id='$id'>" . h($name) . '</a>' : h($name));
if ($view) { if ($view) {
echo '<td colspan="6"><a href="' . h(ME) . "view=" . urlencode($name) . '" title="' . lang('Alter view') . '">' . (preg_match('~materialized~i', $type) ? lang('Materialized view') : lang('View')) . '</a>'; echo '<td colspan="6"><a href="' . h(ME) . "view=" . urlencode($name) . '" title="' . lang('Alter view') . '">' . (preg_match('~materialized~i', $type) ? lang('Materialized view') : lang('View')) . '</a>';
@@ -146,12 +146,12 @@ if ($adminer->homepage()) {
: ""))) : "")))
. "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm() . "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help(JUSH == "sqlite" ? "'DELETE'" : "'TRUNCATE" . (JUSH == "pgsql" ? "'" : " TABLE'")) . confirm()
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n"; . "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n";
$databases = (support("scheme") ? $adminer->schemas() : $adminer->databases()); $databases = (support("scheme") ? adminer()->schemas() : adminer()->databases());
if (count($databases) != 1 && JUSH != "sqlite") { if (count($databases) != 1 && JUSH != "sqlite") {
$db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB)); $db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB));
echo "<p>" . lang('Move to other database') . ": "; echo "<p><label>" . lang('Move to other database') . ": ";
echo ($databases ? html_select("target", $databases, $db) : '<input name="target" value="' . h($db) . '" autocapitalize="off">'); echo ($databases ? html_select("target", $databases, $db) : '<input name="target" value="' . h($db) . '" autocapitalize="off">');
echo " <input type='submit' name='move' value='" . lang('Move') . "'>"; echo "</label> <input type='submit' name='move' value='" . lang('Move') . "'>";
echo (support("copy") ? " <input type='submit' name='copy' value='" . lang('Copy') . "'> " . checkbox("overwrite", 1, $_POST["overwrite"], lang('overwrite')) : ""); echo (support("copy") ? " <input type='submit' name='copy' value='" . lang('Copy') . "'> " . checkbox("overwrite", 1, $_POST["overwrite"], lang('overwrite')) : "");
echo "\n"; echo "\n";
} }

View File

@@ -1,17 +1,11 @@
<?php <?php
function adminer_object() { function adminer_object() {
include_once "../plugins/plugin.php";
include_once "../plugins/designs.php"; include_once "../plugins/designs.php";
$designs = array(); $designs = array();
foreach (glob("../designs/*", GLOB_ONLYDIR) as $dirname) { foreach (glob("../designs/*/*.css") as $filename) {
foreach (array("", "-dark") as $mode) { $designs[$filename] = basename(dirname($filename));
$filename = "$dirname/adminer$mode.css";
if (file_exists($filename)) {
$designs[$filename] = basename($dirname);
}
}
} }
return new AdminerPlugin(array( return new Adminer\Plugins(array(
new AdminerDesigns($designs), new AdminerDesigns($designs),
)); ));
} }

View File

@@ -6,7 +6,7 @@ $fields = fields($TABLE);
header("Content-Type: application/octet-stream"); header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=" . friendly_url("$TABLE-" . implode("_", $_GET["where"])) . "." . friendly_url($_GET["field"])); header("Content-Disposition: attachment; filename=" . friendly_url("$TABLE-" . implode("_", $_GET["where"])) . "." . friendly_url($_GET["field"]));
$select = array(idf_escape($_GET["field"])); $select = array(idf_escape($_GET["field"]));
$result = $driver->select($TABLE, $select, array(where($_GET, $fields)), $select); $result = driver()->select($TABLE, $select, array(where($_GET, $fields)), $select);
$row = ($result ? $result->fetch_row() : array()); $row = ($result ? $result->fetch_row() : array());
echo $driver->value($row[0], $fields[$_GET["field"]]); echo driver()->value($row[0], $fields[$_GET["field"]]);
exit; // don't output footer exit; // don't output footer

View File

@@ -7,13 +7,14 @@
namespace Adminer; namespace Adminer;
$drivers["mssql"] = "MS SQL"; add_driver("mssql", "MS SQL");
if (isset($_GET["mssql"])) { if (isset($_GET["mssql"])) {
define('Adminer\DRIVER', "mssql"); define('Adminer\DRIVER', "mssql");
if (extension_loaded("sqlsrv") && $_GET["ext"] != "pdo") { if (extension_loaded("sqlsrv") && $_GET["ext"] != "pdo") {
class Db { class Db extends SqlDb {
public $extension = "sqlsrv", $flavor = '', $server_info, $affected_rows, $errno, $error; public $extension = "sqlsrv";
private $link, $result; private $link, $result;
private function get_error() { private function get_error() {
@@ -25,17 +26,16 @@ if (isset($_GET["mssql"])) {
$this->error = rtrim($this->error); $this->error = rtrim($this->error);
} }
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
global $adminer;
$connection_info = array("UID" => $username, "PWD" => $password, "CharacterSet" => "UTF-8"); $connection_info = array("UID" => $username, "PWD" => $password, "CharacterSet" => "UTF-8");
$ssl = $adminer->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["Encrypt"])) { if (isset($ssl["Encrypt"])) {
$connection_info["Encrypt"] = $ssl["Encrypt"]; $connection_info["Encrypt"] = $ssl["Encrypt"];
} }
if (isset($ssl["TrustServerCertificate"])) { if (isset($ssl["TrustServerCertificate"])) {
$connection_info["TrustServerCertificate"] = $ssl["TrustServerCertificate"]; $connection_info["TrustServerCertificate"] = $ssl["TrustServerCertificate"];
} }
$db = $adminer->database(); $db = adminer()->database();
if ($db != "") { if ($db != "") {
$connection_info["Database"] = $db; $connection_info["Database"] = $db;
} }
@@ -46,19 +46,19 @@ if (isset($_GET["mssql"])) {
} else { } else {
$this->get_error(); $this->get_error();
} }
return (bool) $this->link; return ($this->link ? '' : $this->error);
} }
function quote($string) { function quote(string $string): string {
$unicode = strlen($string) != strlen(utf8_decode($string)); $unicode = strlen($string) != strlen(utf8_decode($string));
return ($unicode ? "N" : "") . "'" . str_replace("'", "''", $string) . "'"; return ($unicode ? "N" : "") . "'" . str_replace("'", "''", $string) . "'";
} }
function select_db($database) { function select_db(string $database) {
return $this->query(use_sql($database)); return $this->query(use_sql($database));
} }
function query($query, $unbuffered = false) { function query(string $query, bool $unbuffered = false) {
$result = sqlsrv_query($this->link, $query); //! , array(), ($unbuffered ? array() : array("Scrollable" => "keyset")) $result = sqlsrv_query($this->link, $query); //! , array(), ($unbuffered ? array() : array("Scrollable" => "keyset"))
$this->error = ""; $this->error = "";
if (!$result) { if (!$result) {
@@ -68,7 +68,7 @@ if (isset($_GET["mssql"])) {
return $this->store_result($result); return $this->store_result($result);
} }
function multi_query($query) { function multi_query(string $query) {
$this->result = sqlsrv_query($this->link, $query); $this->result = sqlsrv_query($this->link, $query);
$this->error = ""; $this->error = "";
if (!$this->result) { if (!$this->result) {
@@ -92,17 +92,8 @@ if (isset($_GET["mssql"])) {
return true; return true;
} }
function next_result() { function next_result(): bool {
return $this->result ? sqlsrv_next_result($this->result) : null; return $this->result ? !!sqlsrv_next_result($this->result) : false;
}
function result($query, $field = 0) {
$result = $this->query($query);
if (!is_object($result)) {
return false;
}
$row = $result->fetch_row();
return $row[$field];
} }
} }
@@ -133,7 +124,7 @@ if (isset($_GET["mssql"])) {
return $this->convert(sqlsrv_fetch_array($this->result, SQLSRV_FETCH_NUMERIC)); return $this->convert(sqlsrv_fetch_array($this->result, SQLSRV_FETCH_NUMERIC));
} }
function fetch_field() { function fetch_field(): \stdClass {
if (!$this->fields) { if (!$this->fields) {
$this->fields = sqlsrv_field_metadata($this->result); $this->fields = sqlsrv_field_metadata($this->result);
} }
@@ -168,8 +159,8 @@ if (isset($_GET["mssql"])) {
} }
} else { } else {
class MssqlDb extends PdoDb { abstract class MssqlDb extends PdoDb {
function select_db($database) { function select_db(string $database) {
// database selection is separated from the connection so dbname in DSN can't be used // database selection is separated from the connection so dbname in DSN can't be used
return $this->query(use_sql($database)); return $this->query(use_sql($database));
} }
@@ -180,8 +171,7 @@ if (isset($_GET["mssql"])) {
} }
function last_id($result) { function last_id($result) {
global $connection; return connection()->lastInsertId();
return $connection->lastInsertId();
} }
function explain($connection, $query) { function explain($connection, $query) {
@@ -191,9 +181,8 @@ if (isset($_GET["mssql"])) {
class Db extends MssqlDb { class Db extends MssqlDb {
public $extension = "PDO_SQLSRV"; public $extension = "PDO_SQLSRV";
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
$this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password); return $this->dsn("sqlsrv:Server=" . str_replace(":", ",", $server), $username, $password);
return true;
} }
} }
@@ -201,9 +190,8 @@ if (isset($_GET["mssql"])) {
class Db extends MssqlDb { class Db extends MssqlDb {
public $extension = "PDO_DBLIB"; public $extension = "PDO_DBLIB";
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
$this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password); return $this->dsn("dblib:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)), $username, $password);
return true;
} }
} }
} }
@@ -211,25 +199,29 @@ if (isset($_GET["mssql"])) {
class Driver extends SqlDriver { class Driver extends SqlDriver {
static $possibleDrivers = array("SQLSRV", "PDO_SQLSRV", "PDO_DBLIB"); static $extensions = array("SQLSRV", "PDO_SQLSRV", "PDO_DBLIB");
static $jush = "mssql"; static $jush = "mssql";
public $insertFunctions = array("date|time" => "getdate");
public $editFunctions = array( public $editFunctions = array(
array( "int|decimal|real|float|money|datetime" => "+/-",
"date|time" => "getdate", "char|text" => "+",
), array(
"int|decimal|real|float|money|datetime" => "+/-",
"char|text" => "+",
)
); );
public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL");
public $functions = array("len", "lower", "round", "upper"); public $functions = array("len", "lower", "round", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
public $onActions = "NO ACTION|CASCADE|SET NULL|SET DEFAULT";
public $generated = array("PERSISTED", "VIRTUAL"); public $generated = array("PERSISTED", "VIRTUAL");
public $onActions = "NO ACTION|CASCADE|SET NULL|SET DEFAULT";
function __construct($connection) { static function connect(?string $server, string $username, string $password) {
if ($server == "") {
$server = "localhost:1433";
}
return parent::connect($server, $username, $password);
}
function __construct(Db $connection) {
parent::__construct($connection); parent::__construct($connection);
$this->types = array( //! use sys.types $this->types = array( //! use sys.types
lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "int" => 10, "bigint" => 20, "bit" => 1, "decimal" => 0, "real" => 12, "float" => 53, "smallmoney" => 10, "money" => 20), lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "int" => 10, "bigint" => 20, "bit" => 1, "decimal" => 0, "real" => 12, "float" => 53, "smallmoney" => 10, "money" => 20),
@@ -239,7 +231,7 @@ if (isset($_GET["mssql"])) {
); );
} }
function insertUpdate($table, $rows, $primary) { function insertUpdate(string $table, array $rows, array $primary) {
$fields = fields($table); $fields = fields($table);
$update = array(); $update = array();
$where = array(); $where = array();
@@ -283,7 +275,7 @@ if (isset($_GET["mssql"])) {
return queries("BEGIN TRANSACTION"); return queries("BEGIN TRANSACTION");
} }
function tableHelp($name, $is_view = false) { function tableHelp(string $name, bool $is_view = false) {
$links = array( $links = array(
"sys" => "catalog-views/sys-", "sys" => "catalog-views/sys-",
"INFORMATION_SCHEMA" => "information-schema-views/", "INFORMATION_SCHEMA" => "information-schema-views/",
@@ -305,23 +297,12 @@ if (isset($_GET["mssql"])) {
return ($_GET["ns"] != "" ? idf_escape($_GET["ns"]) . "." : "") . idf_escape($idf); return ($_GET["ns"] != "" ? idf_escape($_GET["ns"]) . "." : "") . idf_escape($idf);
} }
function connect($credentials) { function get_databases($flush) {
$connection = new Db;
if ($credentials[0] == "") {
$credentials[0] = "localhost:1433";
}
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
return $connection;
}
return $connection->error;
}
function get_databases() {
return get_vals("SELECT name FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb')"); return get_vals("SELECT name FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb')");
} }
function limit($query, $where, $limit, $offset = 0, $separator = " ") { function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return ($limit !== null ? " TOP (" . ($limit + $offset) . ")" : "") . " $query$where"; // seek later return ($limit ? " TOP (" . ($limit + $offset) . ")" : "") . " $query$where"; // seek later
} }
function limit1($table, $query, $where, $separator = "\n") { function limit1($table, $query, $where, $separator = "\n") {
@@ -341,10 +322,9 @@ if (isset($_GET["mssql"])) {
} }
function count_tables($databases) { function count_tables($databases) {
global $connection;
$return = array(); $return = array();
foreach ($databases as $db) { foreach ($databases as $db) {
$connection->select_db($db); connection()->select_db($db);
$return[$db] = get_val("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES"); $return[$db] = get_val("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES");
} }
return $return; return $return;
@@ -357,9 +337,6 @@ if (isset($_GET["mssql"])) {
FROM sys.all_objects AS ao FROM sys.all_objects AS ao
WHERE schema_id = SCHEMA_ID(" . q(get_schema()) . ") AND type IN ('S', 'U', 'V') " . ($name != "" ? "AND name = " . q($name) : "ORDER BY name")) as $row WHERE schema_id = SCHEMA_ID(" . q(get_schema()) . ") AND type IN ('S', 'U', 'V') " . ($name != "" ? "AND name = " . q($name) : "ORDER BY name")) as $row
) { ) {
if ($name != "") {
return $row;
}
$return[$row["Name"]] = $row; $return[$row["Name"]] = $row;
} }
return $return; return $return;
@@ -388,7 +365,7 @@ WHERE c.object_id = " . q($table_id)) as $row
) { ) {
$type = $row["type"]; $type = $row["type"];
$length = (preg_match("~char|binary~", $type) $length = (preg_match("~char|binary~", $type)
? $row["max_length"] / ($type[0] == 'n' ? 2 : 1) ? intval($row["max_length"]) / ($type[0] == 'n' ? 2 : 1)
: ($type == "decimal" ? "$row[precision],$row[scale]" : "") : ($type == "decimal" ? "$row[precision],$row[scale]" : "")
); );
$return[$row["name"]] = array( $return[$row["name"]] = array(
@@ -449,8 +426,7 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table), $connection2) as $row
} }
function error() { function error() {
global $connection; return nl_br(h(preg_replace('~^(\[[^]]*])+~m', '', connection()->error)));
return nl_br(h(preg_replace('~^(\[[^]]*])+~m', '', $connection->error)));
} }
function create_database($db, $collation) { function create_database($db, $collation) {
@@ -601,7 +577,7 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table), $connection2) as $row
return apply_queries("ALTER SCHEMA " . idf_escape($target) . " TRANSFER", array_merge($tables, $views)); return apply_queries("ALTER SCHEMA " . idf_escape($target) . " TRANSFER", array_merge($tables, $views));
} }
function trigger($name) { function trigger($name, $table) {
if ($name == "") { if ($name == "") {
return array(); return array();
} }
@@ -661,8 +637,7 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)) as $row
} }
function create_sql($table, $auto_increment, $style) { function create_sql($table, $auto_increment, $style) {
global $driver; if (is_view(table_status1($table))) {
if (is_view(table_status($table))) {
$view = view($table); $view = view($table);
return "CREATE VIEW " . table($table) . " AS $view[select]"; return "CREATE VIEW " . table($table) . " AS $view[select]";
} }
@@ -685,7 +660,7 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)) as $row
$fields[] = ($index["type"] == "INDEX" ? "INDEX $name" : "CONSTRAINT $name " . ($index["type"] == "UNIQUE" ? "UNIQUE" : "PRIMARY KEY")) . " (" . implode(", ", $columns) . ")"; $fields[] = ($index["type"] == "INDEX" ? "INDEX $name" : "CONSTRAINT $name " . ($index["type"] == "UNIQUE" ? "UNIQUE" : "PRIMARY KEY")) . " (" . implode(", ", $columns) . ")";
} }
} }
foreach ($driver->checkConstraints($table) as $name => $check) { foreach (driver()->checkConstraints($table) as $name => $check) {
$fields[] = "CONSTRAINT " . idf_escape($name) . " CHECK ($check)"; $fields[] = "CONSTRAINT " . idf_escape($name) . " CHECK ($check)";
} }
return "CREATE TABLE " . table($table) . " (\n\t" . implode(",\n\t", $fields) . "\n)"; return "CREATE TABLE " . table($table) . " (\n\t" . implode(",\n\t", $fields) . "\n)";
@@ -710,7 +685,7 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)) as $row
function trigger_sql($table) { function trigger_sql($table) {
$return = ""; $return = "";
foreach (triggers($table) as $name => $trigger) { foreach (triggers($table) as $name => $trigger) {
$return .= create_trigger(" ON " . table($table), trigger($name)) . ";"; $return .= create_trigger(" ON " . table($table), trigger($name, $table)) . ";";
} }
return $return; return $return;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,16 @@
<?php <?php
namespace Adminer; namespace Adminer;
$drivers["oracle"] = "Oracle (beta)"; add_driver("oracle", "Oracle (beta)");
if (isset($_GET["oracle"])) { if (isset($_GET["oracle"])) {
define('Adminer\DRIVER', "oracle"); define('Adminer\DRIVER', "oracle");
if (extension_loaded("oci8") && $_GET["ext"] != "pdo") { if (extension_loaded("oci8") && $_GET["ext"] != "pdo") {
class Db { class Db extends SqlDb {
public $extension = "oci8", $flavor = '', $server_info, $affected_rows, $errno, $error; public $extension = "oci8";
public $_current_db; public $_current_db;
private $link, $result; private $link;
function _error($errno, $error) { function _error($errno, $error) {
if (ini_bool("html_errors")) { if (ini_bool("html_errors")) {
@@ -19,27 +20,26 @@ if (isset($_GET["oracle"])) {
$this->error = $error; $this->error = $error;
} }
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
$this->link = @oci_new_connect($username, $password, $server, "AL32UTF8"); $this->link = @oci_new_connect($username, $password, $server, "AL32UTF8");
if ($this->link) { if ($this->link) {
$this->server_info = oci_server_version($this->link); $this->server_info = oci_server_version($this->link);
return true; return '';
} }
$error = oci_error(); $error = oci_error();
$this->error = $error["message"]; return $error["message"];
return false;
} }
function quote($string) { function quote(string $string): string {
return "'" . str_replace("'", "''", $string) . "'"; return "'" . str_replace("'", "''", $string) . "'";
} }
function select_db($database) { function select_db(string $database) {
$this->_current_db = $database; $this->_current_db = $database;
return true; return true;
} }
function query($query, $unbuffered = false) { function query(string $query, bool $unbuffered = false) {
$result = oci_parse($this->link, $query); $result = oci_parse($this->link, $query);
$this->error = ""; $this->error = "";
if (!$result) { if (!$result) {
@@ -60,23 +60,6 @@ if (isset($_GET["oracle"])) {
} }
return $return; return $return;
} }
function multi_query($query) {
return $this->result = $this->query($query);
}
function store_result() {
return $this->result;
}
function next_result() {
return false;
}
function result($query, $field = 0) {
$result = $this->query($query);
return (is_object($result) ? $result->fetch_column($field) : false);
}
} }
class Result { class Result {
@@ -89,7 +72,7 @@ if (isset($_GET["oracle"])) {
private function convert($row) { private function convert($row) {
foreach ((array) $row as $key => $val) { foreach ((array) $row as $key => $val) {
if (is_a($val, 'OCI-Lob')) { if (is_a($val, 'OCILob') || is_a($val, 'OCI-Lob')) {
$row[$key] = $val->load(); $row[$key] = $val->load();
} }
} }
@@ -104,11 +87,7 @@ if (isset($_GET["oracle"])) {
return $this->convert(oci_fetch_row($this->result)); return $this->convert(oci_fetch_row($this->result));
} }
function fetch_column($field) { function fetch_field(): \stdClass {
return (oci_fetch($this->result) ? oci_result($this->result, $field + 1) : false);
}
function fetch_field() {
$column = $this->offset++; $column = $this->offset++;
$return = new \stdClass; $return = new \stdClass;
$return->name = oci_field_name($this->result, $column); $return->name = oci_field_name($this->result, $column);
@@ -127,12 +106,11 @@ if (isset($_GET["oracle"])) {
public $extension = "PDO_OCI"; public $extension = "PDO_OCI";
public $_current_db; public $_current_db;
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
$this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password); return $this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password);
return true;
} }
function select_db($database) { function select_db(string $database) {
$this->_current_db = $database; $this->_current_db = $database;
return true; return true;
} }
@@ -143,25 +121,24 @@ if (isset($_GET["oracle"])) {
class Driver extends SqlDriver { class Driver extends SqlDriver {
static $possibleDrivers = array("OCI8", "PDO_OCI"); static $extensions = array("OCI8", "PDO_OCI");
static $jush = "oracle"; static $jush = "oracle";
public $insertFunctions = array( //! no parentheses
"date" => "current_date",
"timestamp" => "current_timestamp",
);
public $editFunctions = array( public $editFunctions = array(
array( //! no parentheses "number|float|double" => "+/-",
"date" => "current_date", "date|timestamp" => "+ interval/- interval",
"timestamp" => "current_timestamp", "char|clob" => "||",
), array(
"number|float|double" => "+/-",
"date|timestamp" => "+ interval/- interval",
"char|clob" => "||",
)
); );
public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"); public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL");
public $functions = array("length", "lower", "round", "upper"); public $functions = array("length", "lower", "round", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
function __construct($connection) { function __construct(Db $connection) {
parent::__construct($connection); parent::__construct($connection);
$this->types = array( $this->types = array(
lang('Numbers') => array("number" => 38, "binary_float" => 12, "binary_double" => 21), lang('Numbers') => array("number" => 38, "binary_float" => 12, "binary_double" => 21),
@@ -177,8 +154,7 @@ if (isset($_GET["oracle"])) {
return true; // automatic start return true; // automatic start
} }
function insertUpdate($table, $rows, $primary) { function insertUpdate(string $table, array $rows, array $primary) {
global $connection;
foreach ($rows as $set) { foreach ($rows as $set) {
$update = array(); $update = array();
$where = array(); $where = array();
@@ -189,7 +165,7 @@ if (isset($_GET["oracle"])) {
} }
} }
if ( if (
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $connection->affected_rows) !(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows)
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")")) || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
) { ) {
return false; return false;
@@ -198,7 +174,7 @@ if (isset($_GET["oracle"])) {
return true; return true;
} }
function hasCStyleEscapes() { function hasCStyleEscapes(): bool {
return true; return true;
} }
} }
@@ -213,15 +189,7 @@ if (isset($_GET["oracle"])) {
return idf_escape($idf); return idf_escape($idf);
} }
function connect($credentials) { function get_databases($flush) {
$connection = new Db;
if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
return $connection;
}
return $connection->error;
}
function get_databases() {
return get_vals( return get_vals(
"SELECT DISTINCT tablespace_name FROM ( "SELECT DISTINCT tablespace_name FROM (
SELECT tablespace_name FROM user_tablespaces SELECT tablespace_name FROM user_tablespaces
@@ -233,7 +201,7 @@ ORDER BY 1"
function limit($query, $where, $limit, $offset = 0, $separator = " ") { function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return ($offset ? " * FROM (SELECT t.*, rownum AS rnum FROM (SELECT $query$where) t WHERE rownum <= " . ($limit + $offset) . ") WHERE rnum > $offset" return ($offset ? " * FROM (SELECT t.*, rownum AS rnum FROM (SELECT $query$where) t WHERE rownum <= " . ($limit + $offset) . ") WHERE rnum > $offset"
: ($limit !== null ? " * FROM (SELECT $query$where) WHERE rownum <= " . ($limit + $offset) : ($limit ? " * FROM (SELECT $query$where) WHERE rownum <= " . ($limit + $offset)
: " $query$where" : " $query$where"
)); ));
} }
@@ -251,9 +219,8 @@ ORDER BY 1"
} }
function get_current_db() { function get_current_db() {
global $connection; $db = connection()->_current_db ?: DB;
$db = $connection->_current_db ?: DB; unset(connection()->_current_db);
unset($connection->_current_db);
return $db; return $db;
} }
@@ -298,9 +265,6 @@ ORDER BY 1"
UNION SELECT view_name, 'view', 0, 0 FROM $view" . ($name != "" ? " WHERE view_name = $search" : "") . " UNION SELECT view_name, 'view', 0, 0 FROM $view" . ($name != "" ? " WHERE view_name = $search" : "") . "
ORDER BY 1") as $row ORDER BY 1") as $row
) { ) {
if ($name != "") {
return $row;
}
$return[$row["Name"]] = $row; $return[$row["Name"]] = $row;
} }
return $return; return $return;
@@ -377,8 +341,7 @@ ORDER BY ac.constraint_type, aic.column_position", $connection2) as $row
} }
function error() { function error() {
global $connection; return h(connection()->error); //! highlight sqltext from offset
return h($connection->error); //! highlight sqltext from offset
} }
function explain($connection, $query) { function explain($connection, $query) {
@@ -504,9 +467,8 @@ AND c_src.TABLE_NAME = " . q($table);
} }
function set_schema($scheme, $connection2 = null) { function set_schema($scheme, $connection2 = null) {
global $connection;
if (!$connection2) { if (!$connection2) {
$connection2 = $connection; $connection2 = connection();
} }
return $connection2->query("ALTER SESSION SET CURRENT_SCHEMA = " . idf_escape($scheme)); return $connection2->query("ALTER SESSION SET CURRENT_SCHEMA = " . idf_escape($scheme));
} }

View File

@@ -1,14 +1,16 @@
<?php <?php
namespace Adminer; namespace Adminer;
$drivers["pgsql"] = "PostgreSQL"; add_driver("pgsql", "PostgreSQL");
if (isset($_GET["pgsql"])) { if (isset($_GET["pgsql"])) {
define('Adminer\DRIVER', "pgsql"); define('Adminer\DRIVER', "pgsql");
if (extension_loaded("pgsql") && $_GET["ext"] != "pdo") { if (extension_loaded("pgsql") && $_GET["ext"] != "pdo") {
class Db { class PgsqlDb extends SqlDb {
public $extension = "PgSQL", $flavor = '', $server_info, $affected_rows, $error, $timeout; public $extension = "PgSQL";
private $link, $result, $string, $database = true; public $timeout = 0;
private $link, $string, $database = true;
function _error($errno, $error) { function _error($errno, $error) {
if (ini_bool("html_errors")) { if (ini_bool("html_errors")) {
@@ -18,12 +20,11 @@ if (isset($_GET["pgsql"])) {
$this->error = $error; $this->error = $error;
} }
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
global $adminer; $db = adminer()->database();
$db = $adminer->database();
set_error_handler(array($this, '_error')); set_error_handler(array($this, '_error'));
$this->string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'"; $this->string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
$ssl = $adminer->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["mode"])) { if (isset($ssl["mode"])) {
$this->string .= " sslmode='" . $ssl["mode"] . "'"; $this->string .= " sslmode='" . $ssl["mode"] . "'";
} }
@@ -37,23 +38,22 @@ if (isset($_GET["pgsql"])) {
if ($this->link) { if ($this->link) {
pg_set_client_encoding($this->link, "UTF8"); pg_set_client_encoding($this->link, "UTF8");
} }
return (bool) $this->link; return ($this->link ? '' : $this->error);
} }
function quote($string) { function quote(string $string): string {
return (function_exists('pg_escape_literal') return (function_exists('pg_escape_literal')
? pg_escape_literal($this->link, $string) // available since PHP 5.4.4 ? pg_escape_literal($this->link, $string) // available since PHP 5.4.4
: "'" . pg_escape_string($this->link, $string) . "'" : "'" . pg_escape_string($this->link, $string) . "'"
); );
} }
function value($val, $field) { function value(?string $val, array $field): ?string {
return ($field["type"] == "bytea" && $val !== null ? pg_unescape_bytea($val) : $val); return ($field["type"] == "bytea" && $val !== null ? pg_unescape_bytea($val) : $val);
} }
function select_db($database) { function select_db(string $database) {
global $adminer; if ($database == adminer()->database()) {
if ($database == $adminer->database()) {
return $this->database; return $this->database;
} }
$return = @pg_connect("$this->string dbname='" . addcslashes($database, "'\\") . "'", PGSQL_CONNECT_FORCE_NEW); $return = @pg_connect("$this->string dbname='" . addcslashes($database, "'\\") . "'", PGSQL_CONNECT_FORCE_NEW);
@@ -67,7 +67,7 @@ if (isset($_GET["pgsql"])) {
$this->link = @pg_connect("$this->string dbname='postgres'"); $this->link = @pg_connect("$this->string dbname='postgres'");
} }
function query($query, $unbuffered = false) { function query(string $query, bool $unbuffered = false) {
$result = @pg_query($this->link, $query); $result = @pg_query($this->link, $query);
$this->error = ""; $this->error = "";
if (!$result) { if (!$result) {
@@ -86,27 +86,23 @@ if (isset($_GET["pgsql"])) {
return $return; return $return;
} }
function multi_query($query) {
return $this->result = $this->query($query);
}
function store_result() {
return $this->result;
}
function next_result() {
// PgSQL extension doesn't support multiple results
return false;
}
function result($query, $field = 0) {
$result = $this->query($query);
return ($result ? $result->fetch_column($field) : false);
}
function warnings() { function warnings() {
return h(pg_last_notice($this->link)); // second parameter is available since PHP 7.1.0 return h(pg_last_notice($this->link)); // second parameter is available since PHP 7.1.0
} }
/** Copy from array into a table
* @param list<string> $rows
*/
function copyFrom(string $table, array $rows): bool {
$this->error = '';
set_error_handler(function (int $errno, string $error): bool {
$this->error = (ini_bool('html_errors') ? html_entity_decode($error) : $error);
return true;
});
$return = pg_copy_from($this->link, $table, $rows);
restore_error_handler();
return $return;
}
} }
class Result { class Result {
@@ -126,11 +122,7 @@ if (isset($_GET["pgsql"])) {
return pg_fetch_row($this->result); return pg_fetch_row($this->result);
} }
function fetch_column($field) { function fetch_field(): \stdClass {
return ($this->num_rows ? pg_fetch_result($this->result, 0, $field) : false);
}
function fetch_field() {
$column = $this->offset++; $column = $this->offset++;
$return = new \stdClass; $return = new \stdClass;
$return->orgtable = pg_field_table($this->result, $column); $return->orgtable = pg_field_table($this->result, $column);
@@ -146,28 +138,26 @@ if (isset($_GET["pgsql"])) {
} }
} elseif (extension_loaded("pdo_pgsql")) { } elseif (extension_loaded("pdo_pgsql")) {
class Db extends PdoDb { class PgsqlDb extends PdoDb {
public $extension = "PDO_PgSQL", $timeout; public $extension = "PDO_PgSQL";
public $timeout = 0;
function connect($server, $username, $password) { function attach(?string $server, string $username, string $password): string {
global $adminer; $db = adminer()->database();
$db = $adminer->database();
//! client_encoding is supported since 9.1, but we can't yet use min_version here //! client_encoding is supported since 9.1, but we can't yet use min_version here
$dsn = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'"; $dsn = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'";
$ssl = $adminer->connectSsl(); $ssl = adminer()->connectSsl();
if (isset($ssl["mode"])) { if (isset($ssl["mode"])) {
$dsn .= " sslmode='" . $ssl["mode"] . "'"; $dsn .= " sslmode='" . $ssl["mode"] . "'";
} }
$this->dsn($dsn, $username, $password); return $this->dsn($dsn, $username, $password);
return true;
} }
function select_db($database) { function select_db(string $database) {
global $adminer; return (adminer()->database() == $database);
return ($adminer->database() == $database);
} }
function query($query, $unbuffered = false) { function query(string $query, bool $unbuffered = false) {
$return = parent::query($query, $unbuffered); $return = parent::query($query, $unbuffered);
if ($this->timeout) { if ($this->timeout) {
$this->timeout = 0; $this->timeout = 0;
@@ -177,7 +167,13 @@ if (isset($_GET["pgsql"])) {
} }
function warnings() { function warnings() {
return ''; // not implemented in PDO_PgSQL as of PHP 7.2.1 // not implemented in PDO_PgSQL as of PHP 7.2.1
}
function copyFrom(string $table, array $rows): bool {
$return = $this->pdo->pgsqlCopyFromArray($table, $rows);
$this->error = idx($this->pdo->errorInfo(), 2) ?: '';
return $return;
} }
function close() { function close() {
@@ -188,15 +184,47 @@ if (isset($_GET["pgsql"])) {
if (class_exists('Adminer\PgsqlDb')) {
class Db extends PgsqlDb {
function multi_query(string $query) {
if (preg_match('~\bCOPY\s+(.+?)\s+FROM\s+stdin;\n?(.*)\n\\\\\.$~is', str_replace("\r\n", "\n", $query), $match)) { // no ^ to allow leading comments
$rows = explode("\n", $match[2]);
$this->affected_rows = count($rows);
return $this->copyFrom($match[1], $rows);
}
return parent::multi_query($query);
}
}
}
class Driver extends SqlDriver { class Driver extends SqlDriver {
static $possibleDrivers = array("PgSQL", "PDO_PgSQL"); static $extensions = array("PgSQL", "PDO_PgSQL");
static $jush = "pgsql"; static $jush = "pgsql";
public $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); // no "SQL" to avoid CSRF public $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); // no "SQL" to avoid CSRF
public $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper"); public $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
public $grouping = array("avg", "count", "count distinct", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
function __construct($connection) { static function connect(?string $server, string $username, string $password) {
$connection = parent::connect($server, $username, $password);
if (is_string($connection)) {
return $connection;
}
$version = get_val("SELECT version()", 0, $connection);
$connection->flavor = (preg_match('~CockroachDB~', $version) ? 'cockroach' : '');
$connection->server_info = preg_replace('~^\D*([\d.]+[-\w]*).*~', '\1', $version);
if (min_version(9, 0, $connection)) {
$connection->query("SET application_name = 'Adminer'");
}
if ($connection->flavor == 'cockroach') { // we don't use "PostgreSQL / CockroachDB" by default because it's too long
add_driver(DRIVER, "CockroachDB");
}
return $connection;
}
function __construct(Db $connection) {
parent::__construct($connection); parent::__construct($connection);
$this->types = array( //! arrays $this->types = array( //! arrays
lang('Numbers') => array("smallint" => 5, "integer" => 10, "bigint" => 19, "boolean" => 1, "numeric" => 0, "real" => 7, "double precision" => 16, "money" => 20), lang('Numbers') => array("smallint" => 5, "integer" => 10, "bigint" => 19, "boolean" => 1, "numeric" => 0, "real" => 7, "double precision" => 16, "money" => 20),
@@ -212,22 +240,21 @@ if (isset($_GET["pgsql"])) {
$this->types[lang('Strings')]["jsonb"] = 4294967295; $this->types[lang('Strings')]["jsonb"] = 4294967295;
} }
} }
$this->insertFunctions = array(
"char" => "md5",
"date|time" => "now",
);
$this->editFunctions = array( $this->editFunctions = array(
array( number_type() => "+/-",
"char" => "md5", "date|time" => "+ interval/- interval", //! escape
"date|time" => "now", "char|text" => "||",
), array(
number_type() => "+/-",
"date|time" => "+ interval/- interval", //! escape
"char|text" => "||",
)
); );
if (min_version(12, 0, $connection)) { if (min_version(12, 0, $connection)) {
$this->generated = array("STORED"); $this->generated = array("STORED");
} }
} }
function enumLength($field) { function enumLength(array $field) {
$enum = $this->types[lang('User types')][$field["type"]]; $enum = $this->types[lang('User types')][$field["type"]];
return ($enum ? type_values($enum) : ""); return ($enum ? type_values($enum) : "");
} }
@@ -236,15 +263,14 @@ if (isset($_GET["pgsql"])) {
$this->types[lang('User types')] = array_flip($types); $this->types[lang('User types')] = array_flip($types);
} }
function insertReturning($table) { function insertReturning(string $table): string {
$auto_increment = array_filter(fields($table), function ($field) { $auto_increment = array_filter(fields($table), function ($field) {
return $field['auto_increment']; return $field['auto_increment'];
}); });
return (count($auto_increment) == 1 ? " RETURNING " . idf_escape(key($auto_increment)) : ""); return (count($auto_increment) == 1 ? " RETURNING " . idf_escape(key($auto_increment)) : "");
} }
function insertUpdate($table, $rows, $primary) { function insertUpdate(string $table, array $rows, array $primary) {
global $connection;
foreach ($rows as $set) { foreach ($rows as $set) {
$update = array(); $update = array();
$where = array(); $where = array();
@@ -255,7 +281,7 @@ if (isset($_GET["pgsql"])) {
} }
} }
if ( if (
!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $connection->affected_rows) !(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && connection()->affected_rows)
|| queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")")) || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"))
) { ) {
return false; return false;
@@ -264,13 +290,13 @@ if (isset($_GET["pgsql"])) {
return true; return true;
} }
function slowQuery($query, $timeout) { function slowQuery(string $query, int $timeout) {
$this->conn->query("SET statement_timeout = " . (1000 * $timeout)); $this->conn->query("SET statement_timeout = " . (1000 * $timeout));
$this->conn->timeout = 1000 * $timeout; $this->conn->timeout = 1000 * $timeout;
return $query; return $query;
} }
function convertSearch($idf, $val, $field) { function convertSearch(string $idf, array $val, array $field): string {
$textTypes = "char|text"; $textTypes = "char|text";
if (strpos($val["op"], "LIKE") === false) { if (strpos($val["op"], "LIKE") === false) {
$textTypes .= "|date|time(stamp)?|boolean|uuid|inet|cidr|macaddr|" . number_type(); $textTypes .= "|date|time(stamp)?|boolean|uuid|inet|cidr|macaddr|" . number_type();
@@ -279,7 +305,7 @@ if (isset($_GET["pgsql"])) {
return (preg_match("~$textTypes~", $field["type"]) ? $idf : "CAST($idf AS text)"); return (preg_match("~$textTypes~", $field["type"]) ? $idf : "CAST($idf AS text)");
} }
function quoteBinary($s) { function quoteBinary(string $s): string {
return "'\\x" . bin2hex($s) . "'"; // available since PostgreSQL 8.1 return "'\\x" . bin2hex($s) . "'"; // available since PostgreSQL 8.1
} }
@@ -287,7 +313,7 @@ if (isset($_GET["pgsql"])) {
return $this->conn->warnings(); return $this->conn->warnings();
} }
function tableHelp($name, $is_view = false) { function tableHelp(string $name, bool $is_view = false) {
$links = array( $links = array(
"information_schema" => "infoschema", "information_schema" => "infoschema",
"pg_catalog" => ($is_view ? "view" : "catalog"), "pg_catalog" => ($is_view ? "view" : "catalog"),
@@ -298,15 +324,15 @@ if (isset($_GET["pgsql"])) {
} }
} }
function supportsIndex($table_status) { function supportsIndex(array $table_status): bool {
// returns true for "materialized view" // returns true for "materialized view"
return $table_status["Engine"] != "view"; return $table_status["Engine"] != "view";
} }
function hasCStyleEscapes() { function hasCStyleEscapes(): bool {
static $c_style; static $c_style;
if ($c_style === null) { if ($c_style === null) {
$c_style = ($this->conn->result("SHOW standard_conforming_strings") == "off"); $c_style = (get_val("SHOW standard_conforming_strings", 0, $this->conn) == "off");
} }
return $c_style; return $c_style;
} }
@@ -322,32 +348,14 @@ if (isset($_GET["pgsql"])) {
return idf_escape($idf); return idf_escape($idf);
} }
function connect($credentials) { function get_databases($flush) {
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;
}
function get_databases() {
return get_vals("SELECT datname FROM pg_database return get_vals("SELECT datname FROM pg_database
WHERE datallowconn = TRUE AND has_database_privilege(datname, 'CONNECT') WHERE datallowconn = TRUE AND has_database_privilege(datname, 'CONNECT')
ORDER BY datname"); ORDER BY datname");
} }
function limit($query, $where, $limit, $offset = 0, $separator = " ") { function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : ""); return " $query$where" . ($limit ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
} }
function limit1($table, $query, $where, $separator = "\n") { function limit1($table, $query, $where, $separator = "\n") {
@@ -380,10 +388,9 @@ ORDER BY 1";
} }
function count_tables($databases) { function count_tables($databases) {
global $connection;
$return = array(); $return = array();
foreach ($databases as $db) { foreach ($databases as $db) {
if ($connection->select_db($db)) { if (connection()->select_db($db)) {
$return[$db] = count(tables_list()); $return[$db] = count(tables_list());
} }
} }
@@ -414,7 +421,7 @@ WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
) { ) {
$return[$row["Name"]] = $row; $return[$row["Name"]] = $row;
} }
return ($name != "" ? $return[$name] : $return); return $return;
} }
function is_view($table_status) { function is_view($table_status) {
@@ -479,12 +486,9 @@ ORDER BY a.attnum") as $row
} }
function indexes($table, $connection2 = null) { function indexes($table, $connection2 = null) {
global $connection; $connection2 = connection($connection2);
if (!is_object($connection2)) {
$connection2 = $connection;
}
$return = array(); $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)); $table_oid = get_val("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($table), 0, $connection2);
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2); $columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
foreach ( foreach (
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial
@@ -501,7 +505,7 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
$return[$relname]["columns"][] = $columns[$indkey]; $return[$relname]["columns"][] = $columns[$indkey];
} }
foreach (explode(" ", $row["indoption"]) as $indoption) { foreach (explode(" ", $row["indoption"]) as $indoption) {
$return[$relname]["descs"][] = ($indoption & 1 ? '1' : null); // 1 - INDOPTION_DESC $return[$relname]["descs"][] = (intval($indoption) & 1 ? '1' : null); // 1 - INDOPTION_DESC
} }
} }
$return[$relname]["lengths"] = array(); $return[$relname]["lengths"] = array();
@@ -510,7 +514,6 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
} }
function foreign_keys($table) { function foreign_keys($table) {
global $driver;
$return = array(); $return = array();
foreach ( foreach (
get_rows("SELECT conname, condeferrable::int AS deferrable, pg_get_constraintdef(oid) AS definition get_rows("SELECT conname, condeferrable::int AS deferrable, pg_get_constraintdef(oid) AS definition
@@ -526,8 +529,8 @@ ORDER BY conkey, conname") as $row
$row['table'] = idf_unescape($match2[4]); $row['table'] = idf_unescape($match2[4]);
} }
$row['target'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[3]))); $row['target'] = array_map('Adminer\idf_unescape', array_map('trim', explode(',', $match[3])));
$row['on_delete'] = (preg_match("~ON DELETE ($driver->onActions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION'); $row['on_delete'] = (preg_match("~ON DELETE (" . driver()->onActions . ")~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
$row['on_update'] = (preg_match("~ON UPDATE ($driver->onActions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION'); $row['on_update'] = (preg_match("~ON UPDATE (" . driver()->onActions . ")~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
$return[$row['conname']] = $row; $return[$row['conname']] = $row;
} }
} }
@@ -548,8 +551,7 @@ ORDER BY conkey, conname") as $row
} }
function error() { function error() {
global $connection; $return = h(connection()->error);
$return = h($connection->error);
if (preg_match('~^(.*\n)?([^\n]*)\n( *)\^(\n.*)?$~s', $return, $match)) { if (preg_match('~^(.*\n)?([^\n]*)\n( *)\^(\n.*)?$~s', $return, $match)) {
$return = $match[1] . preg_replace('~((?:[^&]|&[^;]*;){' . strlen($match[3]) . '})(.*)~', '\1<b>\2</b>', $match[2]) . $match[4]; $return = $match[1] . preg_replace('~((?:[^&]|&[^;]*;){' . strlen($match[3]) . '})(.*)~', '\1<b>\2</b>', $match[2]) . $match[4];
} }
@@ -561,14 +563,12 @@ ORDER BY conkey, conname") as $row
} }
function drop_databases($databases) { function drop_databases($databases) {
global $connection; connection()->close();
$connection->close();
return apply_queries("DROP DATABASE", $databases, 'Adminer\idf_escape'); return apply_queries("DROP DATABASE", $databases, 'Adminer\idf_escape');
} }
function rename_database($name, $collation) { function rename_database($name, $collation) {
global $connection; connection()->close();
$connection->close();
return queries("ALTER DATABASE " . idf_escape(DB) . " RENAME TO " . idf_escape($name)); return queries("ALTER DATABASE " . idf_escape(DB) . " RENAME TO " . idf_escape($name));
} }
@@ -683,7 +683,7 @@ ORDER BY conkey, conname") as $row
function drop_tables($tables) { function drop_tables($tables) {
foreach ($tables as $table) { foreach ($tables as $table) {
$status = table_status($table); $status = table_status1($table);
if (!queries("DROP " . strtoupper($status["Engine"]) . " " . table($table))) { if (!queries("DROP " . strtoupper($status["Engine"]) . " " . table($table))) {
return false; return false;
} }
@@ -693,7 +693,7 @@ ORDER BY conkey, conname") as $row
function move_tables($tables, $views, $target) { function move_tables($tables, $views, $target) {
foreach (array_merge($tables, $views) as $table) { foreach (array_merge($tables, $views) as $table) {
$status = table_status($table); $status = table_status1($table);
if (!queries("ALTER " . strtoupper($status["Engine"]) . " " . table($table) . " SET SCHEMA " . idf_escape($target))) { if (!queries("ALTER " . strtoupper($status["Engine"]) . " " . table($table) . " SET SCHEMA " . idf_escape($target))) {
return false; return false;
} }
@@ -750,7 +750,7 @@ ORDER BY event_manipulation DESC") as $row
$rows = get_rows('SELECT routine_definition AS definition, LOWER(external_language) AS language, * $rows = get_rows('SELECT routine_definition AS definition, LOWER(external_language) AS language, *
FROM information_schema.routines FROM information_schema.routines
WHERE routine_schema = current_schema() AND specific_name = ' . q($name)); WHERE routine_schema = current_schema() AND specific_name = ' . q($name));
$return = $rows[0]; $return = idx($rows, 0, array());
$return["returns"] = array("type" => $return["type_udt_name"]); $return["returns"] = array("type" => $return["type_udt_name"]);
$return["fields"] = get_rows('SELECT parameter_name AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout $return["fields"] = get_rows('SELECT parameter_name AS field, data_type AS type, character_maximum_length AS length, parameter_mode AS inout
FROM information_schema.parameters FROM information_schema.parameters
@@ -780,7 +780,8 @@ ORDER BY SPECIFIC_NAME');
} }
function last_id($result) { function last_id($result) {
return (is_object($result) && $result->num_rows ? $result->fetch_column(0) : 0); $row = (is_object($result) ? $result->fetch_row() : array());
return ($row ? $row[0] : 0);
} }
function explain($connection, $query) { function explain($connection, $query) {
@@ -791,10 +792,9 @@ ORDER BY SPECIFIC_NAME');
if (preg_match("~ rows=([0-9]+)~", get_val("EXPLAIN SELECT * FROM " . idf_escape($table_status["Name"]) . ($where ? " WHERE " . implode(" AND ", $where) : "")), $regs)) { if (preg_match("~ rows=([0-9]+)~", get_val("EXPLAIN SELECT * FROM " . idf_escape($table_status["Name"]) . ($where ? " WHERE " . implode(" AND ", $where) : "")), $regs)) {
return $regs[1]; return $regs[1];
} }
return false;
} }
function types() { function types(): array {
return get_key_vals( return get_key_vals(
"SELECT oid, typname "SELECT oid, typname
FROM pg_type FROM pg_type
@@ -819,12 +819,11 @@ AND typelem = 0"
} }
function set_schema($schema, $connection2 = null) { function set_schema($schema, $connection2 = null) {
global $connection, $driver;
if (!$connection2) { if (!$connection2) {
$connection2 = $connection; $connection2 = connection();
} }
$return = $connection2->query("SET search_path TO " . idf_escape($schema)); $return = $connection2->query("SET search_path TO " . idf_escape($schema));
$driver->setUserTypes(types()); //! get types from current_schemas('t') driver()->setUserTypes(types()); //! get types from current_schemas('t')
return $return; return $return;
} }
@@ -834,7 +833,7 @@ AND typelem = 0"
function foreign_keys_sql($table) { function foreign_keys_sql($table) {
$return = ""; $return = "";
$status = table_status($table); $status = table_status1($table);
$fkeys = foreign_keys($table); $fkeys = foreign_keys($table);
ksort($fkeys); ksort($fkeys);
@@ -846,18 +845,17 @@ AND typelem = 0"
} }
function create_sql($table, $auto_increment, $style) { function create_sql($table, $auto_increment, $style) {
global $driver;
$return_parts = array(); $return_parts = array();
$sequences = array(); $sequences = array();
$status = table_status($table); $status = table_status1($table);
if (is_view($status)) { if (is_view($status)) {
$view = view($table); $view = view($table);
return rtrim("CREATE VIEW " . idf_escape($table) . " AS $view[select]", ";"); return rtrim("CREATE VIEW " . idf_escape($table) . " AS $view[select]", ";");
} }
$fields = fields($table); $fields = fields($table);
if (!$status || empty($fields)) { if (count($status) < 2 || empty($fields)) {
return false; return false;
} }
@@ -867,7 +865,7 @@ AND typelem = 0"
foreach ($fields as $field) { foreach ($fields as $field) {
$part = idf_escape($field['field']) . ' ' . $field['full_type'] $part = idf_escape($field['field']) . ' ' . $field['full_type']
. default_value($field) . default_value($field)
. ($field['attnotnull'] ? " NOT NULL" : ""); . ($field['null'] ? "" : " NOT NULL");
$return_parts[] = $part; $return_parts[] = $part;
// sequences for fields // sequences for fields
@@ -898,7 +896,7 @@ AND typelem = 0"
} }
} }
foreach ($driver->checkConstraints($table) as $conname => $consrc) { foreach (driver()->checkConstraints($table) as $conname => $consrc) {
$return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK $consrc"; $return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK $consrc";
} }
@@ -927,7 +925,7 @@ AND typelem = 0"
} }
function trigger_sql($table) { function trigger_sql($table) {
$status = table_status($table); $status = table_status1($table);
$return = ""; $return = "";
foreach (triggers($table) as $trg_id => $trg) { foreach (triggers($table) as $trg_id => $trg) {
$trigger = trigger($trg_id, $status['Name']); $trigger = trigger($trg_id, $status['Name']);
@@ -957,9 +955,8 @@ AND typelem = 0"
} }
function support($feature) { function support($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' 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 . (connection()->flavor == 'cockroach' ? '' : '|processlist') // https://github.com/cockroachdb/cockroach/issues/24745
. '|kill|dump)$~', $feature) . '|kill|dump)$~', $feature)
; ;
} }

View File

@@ -1,23 +1,24 @@
<?php <?php
namespace Adminer; namespace Adminer;
$drivers["sqlite"] = "SQLite"; add_driver("sqlite", "SQLite");
if (isset($_GET["sqlite"])) { if (isset($_GET["sqlite"])) {
define('Adminer\DRIVER', "sqlite"); define('Adminer\DRIVER', "sqlite");
if (class_exists("SQLite3") && $_GET["ext"] != "pdo") {
class SqliteDb { if (class_exists("SQLite3") && $_GET["ext"] != "pdo") {
public $extension = "SQLite3", $server_info, $affected_rows, $errno, $error; abstract class SqliteDb extends SqlDb {
public $extension = "SQLite3";
private $link; private $link;
function __construct($filename) { function attach(?string $filename, string $username, string $password): string {
$this->link = new \SQLite3($filename); $this->link = new \SQLite3($filename);
$version = $this->link->version(); $version = $this->link->version();
$this->server_info = $version["versionString"]; $this->server_info = $version["versionString"];
return '';
} }
function query($query) { function query(string $query, bool $unbuffered = false) {
$result = @$this->link->query($query); $result = @$this->link->query($query);
$this->error = ""; $this->error = "";
if (!$result) { if (!$result) {
@@ -31,25 +32,12 @@ if (isset($_GET["sqlite"])) {
return true; return true;
} }
function quote($string) { function quote(string $string): string {
return (is_utf8($string) return (is_utf8($string)
? "'" . $this->link->escapeString($string) . "'" ? "'" . $this->link->escapeString($string) . "'"
: "x'" . first(unpack('H*', $string)) . "'" : "x'" . first(unpack('H*', $string)) . "'"
); );
} }
function store_result() {
return $this->result;
}
function result($query, $field = 0) {
$result = $this->query($query);
if (!is_object($result)) {
return false;
}
$row = $result->fetch_row();
return $row ? $row[$field] : false;
}
} }
class Result { class Result {
@@ -68,7 +56,7 @@ if (isset($_GET["sqlite"])) {
return $this->result->fetchArray(SQLITE3_NUM); return $this->result->fetchArray(SQLITE3_NUM);
} }
function fetch_field() { function fetch_field(): \stdClass {
$column = $this->offset++; $column = $this->offset++;
$type = $this->result->columnType($column); $type = $this->result->columnType($column);
return (object) array( return (object) array(
@@ -79,20 +67,19 @@ if (isset($_GET["sqlite"])) {
} }
function __destruct() { function __destruct() {
return $this->result->finalize(); $this->result->finalize();
} }
} }
} elseif (extension_loaded("pdo_sqlite")) { } elseif (extension_loaded("pdo_sqlite")) {
class SqliteDb extends PdoDb { abstract class SqliteDb extends PdoDb {
public $extension = "PDO_SQLite"; public $extension = "PDO_SQLite";
function __construct($filename) { function attach(?string $filename, string $username, string $password): string {
$this->dsn(DRIVER . ":$filename", "", ""); $this->dsn(DRIVER . ":$filename", "", "");
} $this->query("PRAGMA foreign_keys = 1");
$this->query("PRAGMA busy_timeout = 500");
function select_db($db) { return '';
return false;
} }
} }
@@ -100,19 +87,16 @@ if (isset($_GET["sqlite"])) {
if (class_exists('Adminer\SqliteDb')) { if (class_exists('Adminer\SqliteDb')) {
class Db extends SqliteDb { class Db extends SqliteDb {
public $flavor = ''; function attach(?string $filename, string $username, string $password): string {
parent::attach($filename, $username, $password);
function __construct() {
parent::__construct(":memory:");
$this->query("PRAGMA foreign_keys = 1"); $this->query("PRAGMA foreign_keys = 1");
$this->query("PRAGMA busy_timeout = 500");
return '';
} }
function select_db($filename) { function select_db(string $filename): bool {
if (is_readable($filename) && $this->query("ATTACH " . $this->quote(preg_match("~(^[/\\\\]|:)~", $filename) ? $filename : dirname($_SERVER["SCRIPT_FILENAME"]) . "/$filename") . " AS a")) { // is_readable - SQLite 3 if (is_readable($filename) && $this->query("ATTACH " . $this->quote(preg_match("~(^[/\\\\]|:)~", $filename) ? $filename : dirname($_SERVER["SCRIPT_FILENAME"]) . "/$filename") . " AS a")) {
parent::__construct($filename); return !self::attach($filename, '', '');
$this->query("PRAGMA foreign_keys = 1");
$this->query("PRAGMA busy_timeout = 500");
return true;
} }
return false; return false;
} }
@@ -122,37 +106,41 @@ if (isset($_GET["sqlite"])) {
class Driver extends SqlDriver { class Driver extends SqlDriver {
static $possibleDrivers = array("SQLite3", "PDO_SQLite"); static $extensions = array("SQLite3", "PDO_SQLite");
static $jush = "sqlite"; static $jush = "sqlite";
protected $types = array(array("integer" => 0, "real" => 0, "numeric" => 0, "text" => 0, "blob" => 0)); protected $types = array(array("integer" => 0, "real" => 0, "numeric" => 0, "text" => 0, "blob" => 0));
public $insertFunctions = array(); // "text" => "date('now')/time('now')/datetime('now')",
public $editFunctions = array( public $editFunctions = array(
array( "integer|real|numeric" => "+/-",
// "text" => "date('now')/time('now')/datetime('now')", // "text" => "date/time/datetime",
), array( "text" => "||",
"integer|real|numeric" => "+/-",
// "text" => "date/time/datetime",
"text" => "||",
)
); );
public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"); // REGEXP can be user defined function public $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"); // REGEXP can be user defined function
public $functions = array("hex", "length", "lower", "round", "unixepoch", "upper"); public $functions = array("hex", "length", "lower", "round", "unixepoch", "upper");
public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"); public $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
function __construct($connection) { static function connect(?string $server, string $username, string $password) {
if ($password != "") {
return lang('Database does not support password.');
}
return parent::connect(":memory:", "", "");
}
function __construct(Db $connection) {
parent::__construct($connection); parent::__construct($connection);
if (min_version(3.31, 0, $connection)) { if (min_version(3.31, 0, $connection)) {
$this->generated = array("STORED", "VIRTUAL"); $this->generated = array("STORED", "VIRTUAL");
} }
} }
function structuredTypes() { function structuredTypes(): array {
return array_keys($this->types[0]); return array_keys($this->types[0]);
} }
function insertUpdate($table, $rows, $primary) { function insertUpdate(string $table, array $rows, array $primary) {
$values = array(); $values = array();
foreach ($rows as $set) { foreach ($rows as $set) {
$values[] = "(" . implode(", ", $set) . ")"; $values[] = "(" . implode(", ", $set) . ")";
@@ -160,7 +148,7 @@ if (isset($_GET["sqlite"])) {
return queries("REPLACE INTO " . table($table) . " (" . implode(", ", array_keys(reset($rows))) . ") VALUES\n" . implode(",\n", $values)); return queries("REPLACE INTO " . table($table) . " (" . implode(", ", array_keys(reset($rows))) . ") VALUES\n" . implode(",\n", $values));
} }
function tableHelp($name, $is_view = false) { function tableHelp(string $name, bool $is_view = false) {
if ($name == "sqlite_sequence") { if ($name == "sqlite_sequence") {
return "fileformat2.html#seqtab"; return "fileformat2.html#seqtab";
} }
@@ -169,10 +157,20 @@ if (isset($_GET["sqlite"])) {
} }
} }
function checkConstraints($table) { function checkConstraints(string $table): array {
preg_match_all('~ CHECK *(\( *(((?>[^()]*[^() ])|(?1))*) *\))~', $this->conn->result("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . q($table)), $matches); //! could be inside a comment preg_match_all('~ CHECK *(\( *(((?>[^()]*[^() ])|(?1))*) *\))~', get_val("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . q($table), 0, $this->conn), $matches); //! could be inside a comment
return array_combine($matches[2], $matches[2]); return array_combine($matches[2], $matches[2]);
} }
function allFields(): array {
$return = array();
foreach (tables_list() as $table => $type) {
foreach (fields($table) as $field) {
$return[$table][] = $field;
}
}
return $return;
}
} }
@@ -185,20 +183,12 @@ if (isset($_GET["sqlite"])) {
return idf_escape($idf); return idf_escape($idf);
} }
function connect($credentials) { function get_databases($flush) {
list(, , $password) = $credentials;
if ($password != "") {
return lang('Database does not support password.');
}
return new Db;
}
function get_databases() {
return array(); return array();
} }
function limit($query, $where, $limit, $offset = 0, $separator = " ") { function limit($query, $where, $limit, $offset = 0, $separator = " ") {
return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : ""); return " $query$where" . ($limit ? $separator . "LIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
} }
function limit1($table, $query, $where, $separator = "\n") { function limit1($table, $query, $where, $separator = "\n") {
@@ -230,10 +220,10 @@ if (isset($_GET["sqlite"])) {
$row["Rows"] = get_val("SELECT COUNT(*) FROM " . idf_escape($row["Name"])); $row["Rows"] = get_val("SELECT COUNT(*) FROM " . idf_escape($row["Name"]));
$return[$row["Name"]] = $row; $return[$row["Name"]] = $row;
} }
foreach (get_rows("SELECT * FROM sqlite_sequence", null, "") as $row) { foreach (get_rows("SELECT * FROM sqlite_sequence" . ($name != "" ? " WHERE name = " . q($name) : ""), null, "") as $row) {
$return[$row["name"]]["Auto_increment"] = $row["seq"]; $return[$row["name"]]["Auto_increment"] = $row["seq"];
} }
return ($name != "" ? $return[$name] : $return); return $return;
} }
function is_view($table_status) { function is_view($table_status) {
@@ -288,12 +278,9 @@ if (isset($_GET["sqlite"])) {
} }
function indexes($table, $connection2 = null) { function indexes($table, $connection2 = null) {
global $connection; $connection2 = connection($connection2);
if (!is_object($connection2)) {
$connection2 = $connection;
}
$return = array(); $return = array();
$sql = $connection2->result("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . q($table)); $sql = get_val("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . q($table), 0, $connection2);
if (preg_match('~\bPRIMARY\s+KEY\s*\((([^)"]+|"[^"]*"|`[^`]*`)++)~i', $sql, $match)) { if (preg_match('~\bPRIMARY\s+KEY\s*\((([^)"]+|"[^"]*"|`[^`]*`)++)~i', $sql, $match)) {
$return[""] = array("type" => "PRIMARY", "columns" => array(), "lengths" => array(), "descs" => array()); $return[""] = array("type" => "PRIMARY", "columns" => array(), "lengths" => array(), "descs" => array());
preg_match_all('~((("[^"]*+")+|(?:`[^`]*+`)+)|(\S+))(\s+(ASC|DESC))?(,\s*|$)~i', $match[1], $matches, PREG_SET_ORDER); preg_match_all('~((("[^"]*+")+|(?:`[^`]*+`)+)|(\S+))(\s+(ASC|DESC))?(,\s*|$)~i', $match[1], $matches, PREG_SET_ORDER);
@@ -360,34 +347,32 @@ if (isset($_GET["sqlite"])) {
} }
function error() { function error() {
global $connection; return h(connection()->error);
return h($connection->error);
} }
function check_sqlite_name($name) { function check_sqlite_name($name) {
// avoid creating PHP files on unsecured servers // avoid creating PHP files on unsecured servers
global $connection;
$extensions = "db|sdb|sqlite"; $extensions = "db|sdb|sqlite";
if (!preg_match("~^[^\\0]*\\.($extensions)\$~", $name)) { if (!preg_match("~^[^\\0]*\\.($extensions)\$~", $name)) {
$connection->error = lang('Please use one of the extensions %s.', str_replace("|", ", ", $extensions)); connection()->error = lang('Please use one of the extensions %s.', str_replace("|", ", ", $extensions));
return false; return false;
} }
return true; return true;
} }
function create_database($db, $collation) { function create_database($db, $collation) {
global $connection;
if (file_exists($db)) { if (file_exists($db)) {
$connection->error = lang('File exists.'); connection()->error = lang('File exists.');
return false; return false;
} }
if (!check_sqlite_name($db)) { if (!check_sqlite_name($db)) {
return false; return false;
} }
try { try {
$link = new SqliteDb($db); $link = new Db();
$link->attach($db, '', '');
} catch (\Exception $ex) { } catch (\Exception $ex) {
$connection->error = $ex->getMessage(); connection()->error = $ex->getMessage();
return false; return false;
} }
$link->query('PRAGMA encoding = "UTF-8"'); $link->query('PRAGMA encoding = "UTF-8"');
@@ -397,11 +382,10 @@ if (isset($_GET["sqlite"])) {
} }
function drop_databases($databases) { function drop_databases($databases) {
global $connection; connection()->attach(":memory:", '', ''); // to unlock file, doesn't work in PDO on Windows
$connection->__construct(":memory:"); // to unlock file, doesn't work in PDO on Windows
foreach ($databases as $db) { foreach ($databases as $db) {
if (!@unlink($db)) { if (!@unlink($db)) {
$connection->error = lang('File exists.'); connection()->error = lang('File exists.');
return false; return false;
} }
} }
@@ -409,12 +393,11 @@ if (isset($_GET["sqlite"])) {
} }
function rename_database($name, $collation) { function rename_database($name, $collation) {
global $connection;
if (!check_sqlite_name($name)) { if (!check_sqlite_name($name)) {
return false; return false;
} }
$connection->__construct(":memory:"); connection()->attach(":memory:", '', '');
$connection->error = lang('File exists.'); connection()->error = lang('File exists.');
return @rename(DB, $name); return @rename(DB, $name);
} }
@@ -423,7 +406,6 @@ if (isset($_GET["sqlite"])) {
} }
function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) { function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
global $connection;
$use_all_fields = ($table == "" || $foreign); $use_all_fields = ($table == "" || $foreign);
foreach ($fields as $field) { foreach ($fields as $field) {
if ($field[0] != "" || !$field[1] || $field[2]) { if ($field[0] != "" || !$field[1] || $field[2]) {
@@ -456,7 +438,7 @@ if (isset($_GET["sqlite"])) {
if ($auto_increment) { if ($auto_increment) {
queries("BEGIN"); queries("BEGIN");
queries("UPDATE sqlite_sequence SET seq = $auto_increment WHERE name = " . q($name)); // ignores error queries("UPDATE sqlite_sequence SET seq = $auto_increment WHERE name = " . q($name)); // ignores error
if (!$connection->affected_rows) { if (!connection()->affected_rows) {
queries("INSERT INTO sqlite_sequence (name, seq) VALUES (" . q($name) . ", $auto_increment)"); queries("INSERT INTO sqlite_sequence (name, seq) VALUES (" . q($name) . ", $auto_increment)");
} }
queries("COMMIT"); queries("COMMIT");
@@ -465,19 +447,17 @@ if (isset($_GET["sqlite"])) {
} }
/** Recreate table /** Recreate table
* @param string original name * @param string $table original name
* @param string new name * @param string $name new name
* @param array [process_field()], empty to preserve * @param list<list<string>> $fields [process_field()], empty to preserve
* @param array [$original => idf_escape($new_column)], empty to preserve * @param string[] $originals [$original => idf_escape($new_column)], empty to preserve
* @param string [format_foreign_key()], empty to preserve * @param string[] $foreign [format_foreign_key()], empty to preserve
* @param int set auto_increment to this value, 0 to preserve * @param numeric-string $auto_increment set auto_increment to this value, "" to preserve
* @param array [[$type, $name, $columns]], empty to preserve * @param list<array{string, string, list<string>|'DROP'}> $indexes [[$type, $name, $columns]], empty to preserve
* @param string CHECK constraint to drop * @param string $drop_check CHECK constraint to drop
* @param string CHECK constraint to add * @param string $add_check CHECK constraint to add
* @return bool
*/ */
function recreate_table($table, $name, $fields, $originals, $foreign, $auto_increment = 0, $indexes = array(), $drop_check = "", $add_check = "") { function recreate_table(string $table, string $name, array $fields, array $originals, array $foreign, string $auto_increment = "", $indexes = array(), string $drop_check = "", string $add_check = ""): bool {
global $driver;
if ($table != "") { if ($table != "") {
if (!$fields) { if (!$fields) {
foreach (fields($table) as $key => $field) { foreach (fields($table) as $key => $field) {
@@ -534,24 +514,25 @@ if (isset($_GET["sqlite"])) {
} }
queries("BEGIN"); queries("BEGIN");
} }
foreach ($fields as $key => $field) { $changes = array();
foreach ($fields as $field) {
if (preg_match('~GENERATED~', $field[3])) { if (preg_match('~GENERATED~', $field[3])) {
unset($originals[array_search($field[0], $originals)]); unset($originals[array_search($field[0], $originals)]);
} }
$fields[$key] = " " . implode($field); $changes[] = " " . implode($field);
} }
$fields = array_merge($fields, array_filter($foreign)); $changes = array_merge($changes, array_filter($foreign));
foreach ($driver->checkConstraints($table) as $check) { foreach (driver()->checkConstraints($table) as $check) {
if ($check != $drop_check) { if ($check != $drop_check) {
$fields[] = " CHECK ($check)"; $changes[] = " CHECK ($check)";
} }
} }
if ($add_check) { if ($add_check) {
$fields[] = " CHECK ($add_check)"; $changes[] = " CHECK ($add_check)";
} }
$temp_name = ($table == $name ? "adminer_$name" : $name); $temp_name = ($table == $name ? "adminer_$name" : $name);
if (!queries("CREATE TABLE " . table($temp_name) . " (\n" . implode(",\n", $fields) . "\n)")) { if (!queries("CREATE TABLE " . table($temp_name) . " (\n" . implode(",\n", $changes) . "\n)")) {
// implicit ROLLBACK to not overwrite $connection->error // implicit ROLLBACK to not overwrite connection()->error
return false; return false;
} }
if ($table != "") { if ($table != "") {
@@ -560,10 +541,10 @@ if (isset($_GET["sqlite"])) {
} }
$triggers = array(); $triggers = array();
foreach (triggers($table) as $trigger_name => $timing_event) { foreach (triggers($table) as $trigger_name => $timing_event) {
$trigger = trigger($trigger_name); $trigger = trigger($trigger_name, $table);
$triggers[] = "CREATE TRIGGER " . idf_escape($trigger_name) . " " . implode(" ", $timing_event) . " ON " . table($name) . "\n$trigger[Statement]"; $triggers[] = "CREATE TRIGGER " . idf_escape($trigger_name) . " " . implode(" ", $timing_event) . " ON " . table($name) . "\n$trigger[Statement]";
} }
$auto_increment = $auto_increment ? 0 : get_val("SELECT seq FROM sqlite_sequence WHERE name = " . q($table)); // if $auto_increment is set then it will be updated later $auto_increment = $auto_increment ? "" : get_val("SELECT seq FROM sqlite_sequence WHERE name = " . q($table)); // if $auto_increment is set then it will be updated later
if ( if (
!queries("DROP TABLE " . table($table)) // drop before creating indexes and triggers to allow using old names !queries("DROP TABLE " . table($table)) // drop before creating indexes and triggers to allow using old names
|| ($table == $name && !queries("ALTER TABLE " . table($temp_name) . " RENAME TO " . table($name))) || ($table == $name && !queries("ALTER TABLE " . table($temp_name) . " RENAME TO " . table($name)))
@@ -595,7 +576,7 @@ if (isset($_GET["sqlite"])) {
function alter_indexes($table, $alter) { function alter_indexes($table, $alter) {
foreach ($alter as $primary) { foreach ($alter as $primary) {
if ($primary[0] == "PRIMARY") { if ($primary[0] == "PRIMARY") {
return recreate_table($table, $table, array(), array(), array(), 0, $alter); return recreate_table($table, $table, array(), array(), array(), "", $alter);
} }
} }
foreach (array_reverse($alter) as $val) { foreach (array_reverse($alter) as $val) {
@@ -626,7 +607,7 @@ if (isset($_GET["sqlite"])) {
return false; return false;
} }
function trigger($name) { function trigger($name, $table) {
if ($name == "") { if ($name == "") {
return array("Statement" => "BEGIN\n\t;\nEND"); return array("Statement" => "BEGIN\n\t;\nEND");
} }
@@ -680,7 +661,7 @@ if (isset($_GET["sqlite"])) {
function found_rows($table_status, $where) { function found_rows($table_status, $where) {
} }
function types() { function types(): array {
return array(); return array();
} }
@@ -723,7 +704,7 @@ if (isset($_GET["sqlite"])) {
function show_status() { function show_status() {
$return = array(); $return = array();
foreach (get_vals("PRAGMA compile_options") as $option) { foreach (get_vals("PRAGMA compile_options") as $option) {
$return[] = explode("=", $option, 2); $return[] = explode("=", $option, 2) + array('', '');
} }
return $return; return $return;
} }

View File

@@ -16,7 +16,7 @@ if ($_POST && !$error) {
$is_sql = preg_match('~sql~', $_POST["format"]); $is_sql = preg_match('~sql~', $_POST["format"]);
if ($is_sql) { if ($is_sql) {
echo "-- Adminer $VERSION " . $drivers[DRIVER] . " " . str_replace("\n", " ", $connection->server_info) . " dump\n\n"; echo "-- Adminer " . VERSION . " " . get_driver(DRIVER) . " " . str_replace("\n", " ", connection()->server_info) . " dump\n\n";
if (JUSH == "sql") { if (JUSH == "sql") {
echo "SET NAMES utf8; echo "SET NAMES utf8;
SET time_zone = '+00:00'; SET time_zone = '+00:00';
@@ -24,8 +24,8 @@ SET foreign_key_checks = 0;
" . ($_POST["data_style"] ? "SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; " . ($_POST["data_style"] ? "SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
" : "") . " " : "") . "
"; ";
$connection->query("SET time_zone = '+00:00'"); connection()->query("SET time_zone = '+00:00'");
$connection->query("SET sql_mode = ''"); connection()->query("SET sql_mode = ''");
} }
} }
@@ -39,8 +39,8 @@ SET foreign_key_checks = 0;
} }
foreach ((array) $databases as $db) { foreach ((array) $databases as $db) {
$adminer->dumpDatabase($db); adminer()->dumpDatabase($db);
if ($connection->select_db($db)) { if (connection()->select_db($db)) {
if ($is_sql && preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE " . idf_escape($db), 1))) { if ($is_sql && preg_match('~CREATE~', $style) && ($create = get_val("SHOW CREATE DATABASE " . idf_escape($db), 1))) {
set_utf8mb4($create); set_utf8mb4($create);
if ($style == "DROP+CREATE") { if ($style == "DROP+CREATE") {
@@ -93,17 +93,18 @@ SET foreign_key_checks = 0;
$table = (DB == "" || in_array($name, (array) $_POST["tables"])); $table = (DB == "" || in_array($name, (array) $_POST["tables"]));
$data = (DB == "" || in_array($name, (array) $_POST["data"])); $data = (DB == "" || in_array($name, (array) $_POST["data"]));
if ($table || $data) { if ($table || $data) {
$tmp_file = null;
if ($ext == "tar") { if ($ext == "tar") {
$tmp_file = new TmpFile; $tmp_file = new TmpFile;
ob_start(array($tmp_file, 'write'), 1e5); ob_start(array($tmp_file, 'write'), 1e5);
} }
$adminer->dumpTable($name, ($table ? $_POST["table_style"] : ""), (is_view($table_status) ? 2 : 0)); adminer()->dumpTable($name, ($table ? $_POST["table_style"] : ""), (is_view($table_status) ? 2 : 0));
if (is_view($table_status)) { if (is_view($table_status)) {
$views[] = $name; $views[] = $name;
} elseif ($data) { } elseif ($data) {
$fields = fields($name); $fields = fields($name);
$adminer->dumpData($name, $_POST["data_style"], "SELECT *" . convert_fields($fields, $fields) . " FROM " . table($name)); adminer()->dumpData($name, $_POST["data_style"], "SELECT *" . convert_fields($fields, $fields) . " FROM " . table($name));
} }
if ($is_sql && $_POST["triggers"] && $table && ($triggers = trigger_sql($name))) { if ($is_sql && $_POST["triggers"] && $table && ($triggers = trigger_sql($name))) {
echo "\nDELIMITER ;;\n$triggers\nDELIMITER ;\n"; echo "\nDELIMITER ;;\n$triggers\nDELIMITER ;\n";
@@ -129,7 +130,7 @@ SET foreign_key_checks = 0;
} }
foreach ($views as $view) { foreach ($views as $view) {
$adminer->dumpTable($view, $_POST["table_style"], 1); adminer()->dumpTable($view, $_POST["table_style"], 1);
} }
if ($ext == "tar") { if ($ext == "tar") {
@@ -139,7 +140,7 @@ SET foreign_key_checks = 0;
} }
} }
$adminer->dumpFooter(); adminer()->dumpFooter();
exit; exit;
} }
@@ -164,9 +165,9 @@ if (!isset($row["events"])) { // backwards compatibility
$row["triggers"] = $row["table_style"]; $row["triggers"] = $row["table_style"];
} }
echo "<tr><th>" . lang('Output') . "<td>" . html_radios("output", $adminer->dumpOutput(), $row["output"]) . "\n"; echo "<tr><th>" . lang('Output') . "<td>" . html_radios("output", adminer()->dumpOutput(), $row["output"]) . "\n";
echo "<tr><th>" . lang('Format') . "<td>" . html_radios("format", $adminer->dumpFormat(), $row["format"]) . "\n"; echo "<tr><th>" . lang('Format') . "<td>" . html_radios("format", adminer()->dumpFormat(), $row["format"]) . "\n";
echo (JUSH == "sqlite" ? "" : "<tr><th>" . lang('Database') . "<td>" . html_select('db_style', $db_style, $row["db_style"]) echo (JUSH == "sqlite" ? "" : "<tr><th>" . lang('Database') . "<td>" . html_select('db_style', $db_style, $row["db_style"])
. (support("type") ? checkbox("types", 1, $row["types"], lang('User types')) : "") . (support("type") ? checkbox("types", 1, $row["types"], lang('User types')) : "")
@@ -220,7 +221,7 @@ if (DB != "") {
echo "<label class='block'><input type='checkbox' id='check-databases'" . ($TABLE == "" ? " checked" : "") . ">" . lang('Database') . "</label>"; echo "<label class='block'><input type='checkbox' id='check-databases'" . ($TABLE == "" ? " checked" : "") . ">" . lang('Database') . "</label>";
echo script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);", ""); echo script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);", "");
echo "</thead>\n"; echo "</thead>\n";
$databases = $adminer->databases(); $databases = adminer()->databases();
if ($databases) { if ($databases) {
foreach ($databases as $db) { foreach ($databases as $db) {
if (!information_schema($db)) { if (!information_schema($db)) {

View File

@@ -3,10 +3,13 @@ namespace Adminer;
$TABLE = $_GET["edit"]; $TABLE = $_GET["edit"];
$fields = fields($TABLE); $fields = fields($TABLE);
$where = (isset($_GET["select"]) ? ($_POST["check"] && count($_POST["check"]) == 1 ? where_check($_POST["check"][0], $fields) : "") : where($_GET, $fields)); $where = (isset($_GET["select"])
? ($_POST["check"] && count($_POST["check"]) == 1 ? where_check($_POST["check"][0], $fields) : "")
: where($_GET, $fields)
);
$update = (isset($_GET["select"]) ? $_POST["edit"] : $where); $update = (isset($_GET["select"]) ? $_POST["edit"] : $where);
foreach ($fields as $name => $field) { foreach ($fields as $name => $field) {
if (!isset($field["privileges"][$update ? "update" : "insert"]) || $adminer->fieldName($field) == "" || $field["generated"]) { if (!isset($field["privileges"][$update ? "update" : "insert"]) || adminer()->fieldName($field) == "" || $field["generated"]) {
unset($fields[$name]); unset($fields[$name]);
} }
} }
@@ -27,7 +30,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
queries_redirect( queries_redirect(
$location, $location,
lang('Item has been deleted.'), lang('Item has been deleted.'),
$driver->delete($TABLE, $query_where, !$unique_array) driver()->delete($TABLE, $query_where, $unique_array ? 0 : 1)
); );
} else { } else {
@@ -46,7 +49,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
queries_redirect( queries_redirect(
$location, $location,
lang('Item has been updated.'), lang('Item has been updated.'),
$driver->update($TABLE, $set, $query_where, !$unique_array) driver()->update($TABLE, $set, $query_where, $unique_array ? 0 : 1)
); );
if (is_ajax()) { if (is_ajax()) {
page_headers(); page_headers();
@@ -54,7 +57,7 @@ if ($_POST && !$error && !isset($_GET["select"])) {
exit; exit;
} }
} else { } else {
$result = $driver->insert($TABLE, $set); $result = driver()->insert($TABLE, $set);
$last_id = ($result ? last_id($result) : 0); $last_id = ($result ? last_id($result) : 0);
queries_redirect($location, lang('Item%s has been inserted.', ($last_id ? " $last_id" : "")), $result); //! link queries_redirect($location, lang('Item%s has been inserted.', ($last_id ? " $last_id" : "")), $result); //! link
} }
@@ -77,7 +80,7 @@ if ($_POST["save"]) {
$select = array("*"); $select = array("*");
} }
if ($select) { if ($select) {
$result = $driver->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1)); $result = driver()->select($TABLE, $select, array($where), $select, array(), (isset($_GET["select"]) ? 2 : 1));
if (!$result) { if (!$result) {
$error = error(); $error = error();
} else { } else {
@@ -92,12 +95,12 @@ if ($_POST["save"]) {
} }
} }
if (!support("table") && !$fields) { if (!support("table") && !$fields) { // used by Mongo and SimpleDB
if (!$where) { // insert if (!$where) { // insert
$result = $driver->select($TABLE, array("*"), $where, array("*")); $result = driver()->select($TABLE, array("*"), array(), array("*"));
$row = ($result ? $result->fetch_assoc() : false); $row = ($result ? $result->fetch_assoc() : false);
if (!$row) { if (!$row) {
$row = array($driver->primary => ""); $row = array(driver()->primary => "");
} }
} }
if ($row) { if ($row) {
@@ -105,9 +108,9 @@ if (!support("table") && !$fields) {
if (!$where) { if (!$where) {
$row[$key] = null; $row[$key] = null;
} }
$fields[$key] = array("field" => $key, "null" => ($key != $driver->primary), "auto_increment" => ($key == $driver->primary)); $fields[$key] = array("field" => $key, "null" => ($key != driver()->primary), "auto_increment" => ($key == driver()->primary));
} }
} }
} }
edit_form($TABLE, $fields, $row, $update); edit_form($TABLE, $fields, $row, $update, $error);

View File

@@ -2,10 +2,9 @@
// To create Adminer just for Elasticsearch, run `../compile.php elastic`. // To create Adminer just for Elasticsearch, run `../compile.php elastic`.
function adminer_object() { function adminer_object() {
include_once "../plugins/plugin.php";
include_once "../plugins/login-password-less.php"; include_once "../plugins/login-password-less.php";
include_once "../plugins/drivers/elastic.php"; include_once "../plugins/drivers/elastic.php";
return new AdminerPlugin(array( return new Adminer\Plugins(array(
// TODO: inline the result of password_hash() so that the password is not visible in source codes // TODO: inline the result of password_hash() so that the password is not visible in source codes
new AdminerLoginPasswordLess(password_hash("YOUR_PASSWORD_HERE", PASSWORD_DEFAULT)), new AdminerLoginPasswordLess(password_hash("YOUR_PASSWORD_HERE", PASSWORD_DEFAULT)),
)); ));

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
if (substr($VERSION, -4) != '-dev') { if (substr(VERSION, -4) != '-dev') {
if ($_SERVER["HTTP_IF_MODIFIED_SINCE"]) { if ($_SERVER["HTTP_IF_MODIFIED_SINCE"]) {
header("HTTP/1.1 304 Not Modified"); header("HTTP/1.1 304 Not Modified");
exit; exit;
@@ -11,10 +11,9 @@ if (substr($VERSION, -4) != '-dev') {
header("Cache-Control: immutable"); header("Cache-Control: immutable");
} }
if ($_GET["file"] == "favicon.ico") { @ini_set("zlib.output_compression", '1'); // @ - may be disabled
header("Content-Type: image/x-icon");
echo lzw_decompress(compile_file('../adminer/static/favicon.ico', 'lzw_compress')); if ($_GET["file"] == "default.css") {
} elseif ($_GET["file"] == "default.css") {
header("Content-Type: text/css; charset=utf-8"); header("Content-Type: text/css; charset=utf-8");
echo lzw_decompress(compile_file('../adminer/static/default.css;../externals/jush/jush.css', 'minify_css')); echo lzw_decompress(compile_file('../adminer/static/default.css;../externals/jush/jush.css', 'minify_css'));
} elseif ($_GET["file"] == "dark.css") { } elseif ($_GET["file"] == "dark.css") {
@@ -26,6 +25,7 @@ if ($_GET["file"] == "favicon.ico") {
} elseif ($_GET["file"] == "jush.js") { } elseif ($_GET["file"] == "jush.js") {
header("Content-Type: text/javascript; charset=utf-8"); header("Content-Type: text/javascript; charset=utf-8");
echo lzw_decompress(compile_file('../externals/jush/modules/jush.js; echo lzw_decompress(compile_file('../externals/jush/modules/jush.js;
../externals/jush/modules/jush-autocomplete-sql.js;
../externals/jush/modules/jush-textarea.js; ../externals/jush/modules/jush-textarea.js;
../externals/jush/modules/jush-txt.js; ../externals/jush/modules/jush-txt.js;
../externals/jush/modules/jush-js.js; ../externals/jush/modules/jush-js.js;
@@ -35,24 +35,8 @@ if ($_GET["file"] == "favicon.ico") {
../externals/jush/modules/jush-mssql.js; ../externals/jush/modules/jush-mssql.js;
../externals/jush/modules/jush-oracle.js; ../externals/jush/modules/jush-oracle.js;
../externals/jush/modules/jush-simpledb.js', 'minify_js')); ../externals/jush/modules/jush-simpledb.js', 'minify_js'));
} else { } elseif ($_GET["file"] == "logo.png") {
header("Content-Type: image/gif"); header("Content-Type: image/png");
switch ($_GET["file"]) { echo compile_file('../adminer/static/logo.png');
case "plus.gif":
echo compile_file('../adminer/static/plus.gif');
break;
case "cross.gif":
echo compile_file('../adminer/static/cross.gif');
break;
case "up.gif":
echo compile_file('../adminer/static/up.gif');
break;
case "down.gif":
echo compile_file('../adminer/static/down.gif');
break;
case "arrow.gif":
echo compile_file('../adminer/static/arrow.gif');
break;
}
} }
exit; exit;

View File

@@ -31,7 +31,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-
$result $result
); );
if (!$row["drop"]) { if (!$row["drop"]) {
$error = "$error<br>" . lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.'); //! no partitioning $error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.'); //! no partitioning
} }
} }
@@ -58,7 +58,7 @@ if ($_POST) {
<?php <?php
$source = array_keys(fields($TABLE)); //! no text and blob $source = array_keys(fields($TABLE)); //! no text and blob
if ($row["db"] != "") { if ($row["db"] != "") {
$connection->select_db($row["db"]); connection()->select_db($row["db"]);
} }
if ($row["ns"] != "") { if ($row["ns"] != "") {
$orig_schema = get_schema(); $orig_schema = get_schema();
@@ -67,23 +67,23 @@ if ($row["ns"] != "") {
$referencable = array_keys(array_filter(table_status('', true), 'Adminer\fk_support')); $referencable = array_keys(array_filter(table_status('', true), 'Adminer\fk_support'));
$target = array_keys(fields(in_array($row["table"], $referencable) ? $row["table"] : reset($referencable))); $target = array_keys(fields(in_array($row["table"], $referencable) ? $row["table"] : reset($referencable)));
$onchange = "this.form['change-js'].value = '1'; this.form.submit();"; $onchange = "this.form['change-js'].value = '1'; this.form.submit();";
echo "<p>" . lang('Target table') . ": " . html_select("table", $referencable, $row["table"], $onchange) . "\n"; echo "<p><label>" . lang('Target table') . ": " . html_select("table", $referencable, $row["table"], $onchange) . "</label>\n";
if (support("scheme")) { if (support("scheme")) {
$schemas = array_filter($adminer->schemas(), function ($schema) { $schemas = array_filter(adminer()->schemas(), function ($schema) {
return !preg_match('~^information_schema$~i', $schema); return !preg_match('~^information_schema$~i', $schema);
}); });
echo lang('Schema') . ": " . html_select("ns", $schemas, $row["ns"] != "" ? $row["ns"] : $_GET["ns"], $onchange); echo "<label>" . lang('Schema') . ": " . html_select("ns", $schemas, $row["ns"] != "" ? $row["ns"] : $_GET["ns"], $onchange) . "</label>";
if ($row["ns"] != "") { if ($row["ns"] != "") {
set_schema($orig_schema); set_schema($orig_schema);
} }
} elseif (JUSH != "sqlite") { } elseif (JUSH != "sqlite") {
$dbs = array(); $dbs = array();
foreach ($adminer->databases() as $db) { foreach (adminer()->databases() as $db) {
if (!information_schema($db)) { if (!information_schema($db)) {
$dbs[] = $db; $dbs[] = $db;
} }
} }
echo lang('DB') . ": " . html_select("db", $dbs, $row["db"] != "" ? $row["db"] : $_GET["db"], $onchange); echo "<label>" . lang('DB') . ": " . html_select("db", $dbs, $row["db"] != "" ? $row["db"] : $_GET["db"], $onchange) . "</label>";
} }
echo input_hidden("change-js"); echo input_hidden("change-js");
?> ?>
@@ -95,14 +95,14 @@ $j = 0;
foreach ($row["source"] as $key => $val) { foreach ($row["source"] as $key => $val) {
echo "<tr>"; echo "<tr>";
echo "<td>" . html_select("source[" . (+$key) . "]", array(-1 => "") + $source, $val, ($j == count($row["source"]) - 1 ? "foreignAddRow.call(this);" : ""), "label-source"); echo "<td>" . html_select("source[" . (+$key) . "]", array(-1 => "") + $source, $val, ($j == count($row["source"]) - 1 ? "foreignAddRow.call(this);" : ""), "label-source");
echo "<td>" . html_select("target[" . (+$key) . "]", $target, $row["target"][$key], "", "label-target"); echo "<td>" . html_select("target[" . (+$key) . "]", $target, idx($row["target"], $key), "", "label-target");
$j++; $j++;
} }
?> ?>
</table> </table>
<p> <p>
<?php echo lang('ON DELETE'); ?>: <?php echo html_select("on_delete", array(-1 => "") + explode("|", $driver->onActions), $row["on_delete"]); ?> <label><?php echo lang('ON DELETE'); ?>: <?php echo html_select("on_delete", array(-1 => "") + explode("|", driver()->onActions), $row["on_delete"]); ?></label>
<?php echo lang('ON UPDATE'); ?>: <?php echo html_select("on_update", array(-1 => "") + explode("|", $driver->onActions), $row["on_update"]); ?> <label><?php echo lang('ON UPDATE'); ?>: <?php echo html_select("on_update", array(-1 => "") + explode("|", driver()->onActions), $row["on_update"]); ?></label>
<?php echo doc_link(array( <?php echo doc_link(array(
'sql' => "innodb-foreign-key-constraints.html", 'sql' => "innodb-foreign-key-constraints.html",
'mariadb' => "foreign-keys/", 'mariadb' => "foreign-keys/",

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,6 @@
<?php <?php
namespace Adminer; namespace Adminer;
$connection = '';
$has_token = $_SESSION["token"];
if (!$has_token) {
$_SESSION["token"] = rand(1, 1e6); // defense against cross-site request forgery
}
$token = get_token(); ///< @var string CSRF protection
$permanent = array(); $permanent = array();
if ($_COOKIE["adminer_permanent"]) { if ($_COOKIE["adminer_permanent"]) {
foreach (explode(" ", $_COOKIE["adminer_permanent"]) as $val) { foreach (explode(" ", $_COOKIE["adminer_permanent"]) as $val) {
@@ -17,8 +9,7 @@ if ($_COOKIE["adminer_permanent"]) {
} }
} }
function add_invalid_login() { function add_invalid_login(): void {
global $adminer;
$base = 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 // adminer.invalid may not be writable by us, try the files with random suffixes
foreach (glob("$base*") ?: array($base) as $filename) { foreach (glob("$base*") ?: array($base) as $filename) {
@@ -42,7 +33,7 @@ function add_invalid_login() {
} }
} }
} }
$invalid = &$invalids[$adminer->bruteForceKey()]; $invalid = &$invalids[adminer()->bruteForceKey()];
if (!$invalid) { if (!$invalid) {
$invalid = array($time + 30*60, 0); // active for 30 minutes $invalid = array($time + 30*60, 0); // active for 30 minutes
} }
@@ -50,8 +41,8 @@ function add_invalid_login() {
file_write_unlock($fp, serialize($invalids)); file_write_unlock($fp, serialize($invalids));
} }
function check_invalid_login() { /** @param string[] $permanent */
global $adminer; function check_invalid_login(array &$permanent): void {
$invalids = array(); $invalids = array();
foreach (glob(get_temp_dir() . "/adminer.invalid*") as $filename) { foreach (glob(get_temp_dir() . "/adminer.invalid*") as $filename) {
$fp = file_open_lock($filename); $fp = file_open_lock($filename);
@@ -61,10 +52,11 @@ function check_invalid_login() {
break; break;
} }
} }
$invalid = ($invalids ? $invalids[$adminer->bruteForceKey()] : array()); /** @var array{int, int} */
$invalid = idx($invalids, adminer()->bruteForceKey(), array());
$next_attempt = ($invalid[1] > 29 ? $invalid[0] - time() : 0); // allow 30 invalid attempts $next_attempt = ($invalid[1] > 29 ? $invalid[0] - time() : 0); // allow 30 invalid attempts
if ($next_attempt > 0) { //! do the same with permanent login if ($next_attempt > 0) { //! do the same with permanent login
auth_error(lang('Too many unsuccessful logins, try again in %d minute(s).', ceil($next_attempt / 60))); auth_error(lang('Too many unsuccessful logins, try again in %d minute(s).', ceil($next_attempt / 60)), $permanent);
} }
} }
@@ -80,7 +72,7 @@ if ($auth) {
$_SESSION["db"][$vendor][$server][$username][$db] = true; $_SESSION["db"][$vendor][$server][$username][$db] = true;
if ($auth["permanent"]) { if ($auth["permanent"]) {
$key = implode("-", array_map('base64_encode', array($vendor, $server, $username, $db))); $key = implode("-", array_map('base64_encode', array($vendor, $server, $username, $db)));
$private = $adminer->permanentLogin(true); $private = adminer()->permanentLogin(true);
$permanent[$key] = "$key:" . base64_encode($private ? encrypt_string($password, $private) : ""); $permanent[$key] = "$key:" . base64_encode($private ? encrypt_string($password, $private) : "");
cookie("adminer_permanent", implode(" ", $permanent)); cookie("adminer_permanent", implode(" ", $permanent));
} }
@@ -94,16 +86,16 @@ if ($auth) {
redirect(auth_url($vendor, $server, $username, $db)); redirect(auth_url($vendor, $server, $username, $db));
} }
} elseif ($_POST["logout"] && (!$has_token || verify_token())) { } elseif ($_POST["logout"] && (!$_SESSION["token"] || verify_token())) {
foreach (array("pwds", "db", "dbs", "queries") as $key) { foreach (array("pwds", "db", "dbs", "queries") as $key) {
set_session($key, null); set_session($key, null);
} }
unset_permanent(); unset_permanent($permanent);
redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1), lang('Logout successful.') . ' ' . lang('Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.')); redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1), lang('Logout successful.') . ' ' . lang('Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.'));
} elseif ($permanent && !$_SESSION["pwds"]) { } elseif ($permanent && !$_SESSION["pwds"]) {
session_regenerate_id(); session_regenerate_id();
$private = $adminer->permanentLogin(); $private = adminer()->permanentLogin();
foreach ($permanent as $key => $val) { foreach ($permanent as $key => $val) {
list(, $cipher) = explode(":", $val); list(, $cipher) = explode(":", $val);
list($vendor, $server, $username, $db) = array_map('base64_decode', explode("-", $key)); list($vendor, $server, $username, $db) = array_map('base64_decode', explode("-", $key));
@@ -112,8 +104,10 @@ if ($auth) {
} }
} }
function unset_permanent() { /** Remove credentials from permanent login
global $permanent; * @param string[] $permanent
*/
function unset_permanent(array &$permanent): void {
foreach ($permanent as $key => $val) { foreach ($permanent as $key => $val) {
list($vendor, $server, $username, $db) = array_map('base64_decode', explode("-", $key)); list($vendor, $server, $username, $db) = array_map('base64_decode', explode("-", $key));
if ($vendor == DRIVER && $server == SERVER && $username == $_GET["username"] && $db == DB) { if ($vendor == DRIVER && $server == SERVER && $username == $_GET["username"] && $db == DB) {
@@ -124,15 +118,15 @@ function unset_permanent() {
} }
/** Render an error message and a login form /** Render an error message and a login form
* @param string plain text * @param string $error plain text
* @return null exits * @param string[] $permanent
* @return never
*/ */
function auth_error($error) { function auth_error(string $error, array &$permanent) {
global $adminer, $has_token;
$session_name = session_name(); $session_name = session_name();
if (isset($_GET["username"])) { if (isset($_GET["username"])) {
header("HTTP/1.1 403 Forbidden"); // 401 requires sending WWW-Authenticate header header("HTTP/1.1 403 Forbidden"); // 401 requires sending WWW-Authenticate header
if (($_COOKIE[$session_name] || $_GET[$session_name]) && !$has_token) { if (($_COOKIE[$session_name] || $_GET[$session_name]) && !$_SESSION["token"]) {
$error = lang('Session expired, please login again.'); $error = lang('Session expired, please login again.');
} else { } else {
restart_session(); restart_session();
@@ -144,7 +138,7 @@ function auth_error($error) {
} }
set_password(DRIVER, SERVER, $_GET["username"], null); set_password(DRIVER, SERVER, $_GET["username"], null);
} }
unset_permanent(); unset_permanent($permanent);
} }
} }
if (!$_COOKIE[$session_name] && $_GET[$session_name] && ini_bool("session.use_only_cookies")) { if (!$_COOKIE[$session_name] && $_GET[$session_name] && ini_bool("session.use_only_cookies")) {
@@ -152,6 +146,9 @@ function auth_error($error) {
} }
$params = session_get_cookie_params(); $params = session_get_cookie_params();
cookie("adminer_key", ($_COOKIE["adminer_key"] ?: rand_string()), $params["lifetime"]); cookie("adminer_key", ($_COOKIE["adminer_key"] ?: rand_string()), $params["lifetime"]);
if (!$_SESSION["token"]) {
$_SESSION["token"] = rand(1, 1e6); // this is for next attempt
}
page_header(lang('Login'), $error, null); page_header(lang('Login'), $error, null);
echo "<form action='' method='post'>\n"; echo "<form action='' method='post'>\n";
echo "<div>"; echo "<div>";
@@ -159,7 +156,7 @@ function auth_error($error) {
echo "<p class='message'>" . lang('The action will be performed after successful login with the same credentials.') . "\n"; echo "<p class='message'>" . lang('The action will be performed after successful login with the same credentials.') . "\n";
} }
echo "</div>\n"; echo "</div>\n";
$adminer->loginForm(); adminer()->loginForm();
echo "</form>\n"; echo "</form>\n";
page_footer("auth"); page_footer("auth");
exit; exit;
@@ -167,46 +164,49 @@ function auth_error($error) {
if (isset($_GET["username"]) && !class_exists('Adminer\Db')) { if (isset($_GET["username"]) && !class_exists('Adminer\Db')) {
unset($_SESSION["pwds"][DRIVER]); unset($_SESSION["pwds"][DRIVER]);
unset_permanent(); unset_permanent($permanent);
page_header(lang('No extension'), lang('None of the supported PHP extensions (%s) are available.', implode(", ", Driver::$possibleDrivers)), false); page_header(lang('No extension'), lang('None of the supported PHP extensions (%s) are available.', implode(", ", Driver::$extensions)), false);
page_footer("auth"); page_footer("auth");
exit; exit;
} }
stop_session(true); $connection = '';
if (isset($_GET["username"]) && is_string(get_password())) { if (isset($_GET["username"]) && is_string(get_password())) {
list($host, $port) = explode(":", SERVER, 2); list($host, $port) = explode(":", SERVER, 2);
if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80 if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80
auth_error(lang('Connecting to privileged ports is not allowed.')); auth_error(lang('Connecting to privileged ports is not allowed.'), $permanent);
} }
check_invalid_login(); check_invalid_login($permanent);
$connection = connect($adminer->credentials()); $credentials = adminer()->credentials();
$connection = Driver::connect($credentials[0], $credentials[1], $credentials[2]);
if (is_object($connection)) { if (is_object($connection)) {
$driver = new Driver($connection); Db::$instance = $connection;
if ($adminer->operators === null) { Driver::$instance = new Driver($connection);
$adminer->operators = $driver->operators; if ($connection->flavor) {
} save_settings(array("vendor-" . DRIVER . "-" . SERVER => get_driver(DRIVER)));
if (Driver::$jush == 'sql' || $connection->flavor == 'cockroach') {
save_settings(array("vendor-" . DRIVER . "-" . SERVER => $drivers[DRIVER]));
} }
} }
} }
$login = null; $login = null;
if (!is_object($connection) || ($login = $adminer->login($_GET["username"], get_password())) !== true) { if (!is_object($connection) || ($login = adminer()->login($_GET["username"], get_password())) !== true) {
$error = (is_string($connection) ? nl_br(h($connection)) : (is_string($login) ? $login : lang('Invalid credentials.'))); $error = (is_string($connection) ? nl_br(h($connection)) : (is_string($login) ? $login : lang('Invalid credentials.')))
auth_error($error . (preg_match('~^ | $~', get_password()) ? '<br>' . lang('There is a space in the input password which might be the cause.') : '')); . (preg_match('~^ | $~', get_password()) ? '<br>' . lang('There is a space in the input password which might be the cause.') : '');
auth_error($error, $permanent);
} }
if ($_POST["logout"] && $has_token && !verify_token()) { if ($_POST["logout"] && $_SESSION["token"] && !verify_token()) {
page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.')); page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.'));
page_footer("db"); page_footer("db");
exit; exit;
} }
if (!$_SESSION["token"]) {
$_SESSION["token"] = rand(1, 1e6); // defense against cross-site request forgery
}
stop_session(true);
if ($auth && $_POST["token"]) { if ($auth && $_POST["token"]) {
$_POST["token"] = $token; // reset token after explicit login $_POST["token"] = get_token(); // reset token after explicit login
} }
$error = ''; ///< @var string $error = ''; ///< @var string

View File

@@ -39,7 +39,7 @@ if ($_GET["script"] == "version") {
exit; exit;
} }
global $adminer, $connection, $driver, $drivers, $error, $HTTPS, $LANG, $langs, $permanent, $has_token, $token, $translations, $VERSION; // allows including Adminer inside a function // Adminer doesn't use any global variables; they used to be declared here
if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility if (!$_SERVER["REQUEST_URI"]) { // IIS 5 compatibility
$_SERVER["REQUEST_URI"] = $_SERVER["ORIG_PATH_INFO"]; $_SERVER["REQUEST_URI"] = $_SERVER["ORIG_PATH_INFO"];
@@ -50,13 +50,13 @@ if (!strpos($_SERVER["REQUEST_URI"], '?') && $_SERVER["QUERY_STRING"] != "") { /
if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) { if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
$_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"]; $_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"];
} }
$HTTPS = ($_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off")) || ini_bool("session.cookie_secure"); // session.cookie_secure could be set on HTTP if we are behind a reverse proxy define('Adminer\HTTPS', ($_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off")) || ini_bool("session.cookie_secure")); // session.cookie_secure could be set on HTTP if we are behind a reverse proxy
@ini_set("session.use_trans_sid", false); // protect links in export, @ - may be disabled @ini_set("session.use_trans_sid", '0'); // protect links in export, @ - may be disabled
if (!defined("SID")) { if (!defined("SID")) {
session_cache_limiter(""); // to allow restarting session session_cache_limiter(""); // to allow restarting session
session_name("adminer_sid"); // use specific session name to get own namespace session_name("adminer_sid"); // use specific session name to get own namespace
session_set_cookie_params(0, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", $HTTPS, true); // ini_set() may be disabled session_set_cookie_params(0, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", HTTPS, true); // ini_set() may be disabled
session_start(); session_start();
} }
@@ -66,10 +66,11 @@ if (function_exists("get_magic_quotes_runtime") && get_magic_quotes_runtime()) {
set_magic_quotes_runtime(false); set_magic_quotes_runtime(false);
} }
@set_time_limit(0); // @ - can be disabled @set_time_limit(0); // @ - can be disabled
@ini_set("precision", 15); // @ - can be disabled, 15 - internal PHP precision @ini_set("precision", '15'); // @ - can be disabled, 15 - internal PHP precision
include "../adminer/include/lang.inc.php"; include "../adminer/include/lang.inc.php";
include "../adminer/lang/$LANG.inc.php"; include "../adminer/lang/" . LANG . ".inc.php";
include "../adminer/include/db.inc.php";
include "../adminer/include/pdo.inc.php"; include "../adminer/include/pdo.inc.php";
include "../adminer/include/driver.inc.php"; include "../adminer/include/driver.inc.php";
include "../adminer/drivers/sqlite.inc.php"; include "../adminer/drivers/sqlite.inc.php";
@@ -78,20 +79,19 @@ include "../adminer/drivers/oracle.inc.php";
include "../adminer/drivers/mssql.inc.php"; include "../adminer/drivers/mssql.inc.php";
include "./include/adminer.inc.php"; include "./include/adminer.inc.php";
include "../adminer/include/plugins.inc.php"; include "../adminer/include/plugins.inc.php";
include "../adminer/include/plugin.inc.php";
if (function_exists('adminer_object')) { Adminer::$instance =
$adminer = adminer_object(); (function_exists('adminer_object') ? adminer_object() :
} elseif (is_dir("adminer-plugins") || file_exists("adminer-plugins.php")) { (is_dir("adminer-plugins") || file_exists("adminer-plugins.php") ? new Plugins(null) :
$adminer = new Plugins(null); new Adminer
} else { ));
$adminer = new Adminer;
}
// this is matched by compile.php // this is matched by compile.php
include "../adminer/drivers/mysql.inc.php"; // must be included as last driver include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
define('Adminer\JUSH', Driver::$jush); define('Adminer\JUSH', Driver::$jush);
define('Adminer\SERVER', $_GET[DRIVER]); // read from pgsql=localhost define('Adminer\SERVER', $_GET[DRIVER]); // read from pgsql=localhost, '' means default server, null means no server
define('Adminer\DB', $_GET["db"]); // for the sake of speed and size define('Adminer\DB', $_GET["db"]); // for the sake of speed and size
define( define(
'Adminer\ME', 'Adminer\ME',

View File

@@ -10,7 +10,7 @@ if (isset($_GET["import"])) {
if ( if (
!(DB != "" !(DB != ""
? $connection->select_db(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"]) : 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" || $_GET["script"] == "connect" || $_GET["script"] == "kill"
) )
@@ -42,17 +42,10 @@ if (
echo "<a href='" . h(ME) . "$key='>$val</a>\n"; echo "<a href='" . h(ME) . "$key='>$val</a>\n";
} }
} }
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('%s version: %s through PHP extension %s', get_driver(DRIVER), "<b>" . h(connection()->server_info) . "</b>", "<b>" . connection()->extension . "</b>") . "\n";
echo "<p>" . lang('Logged as: %s', "<b>" . h(logged_user()) . "</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"; $databases = adminer()->databases();
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) { if ($databases) {
$scheme = support("scheme"); $scheme = support("scheme");
$collations = collations(); $collations = collations();
@@ -69,7 +62,6 @@ if (
; ;
$databases = ($_GET["dbsize"] ? count_tables($databases) : array_flip($databases)); $databases = ($_GET["dbsize"] ? count_tables($databases) : array_flip($databases));
foreach ($databases as $db => $tables) { foreach ($databases as $db => $tables) {
$root = h(ME) . "db=" . urlencode($db); $root = h(ME) . "db=" . urlencode($db);
$id = h("Db-" . $db); $id = h("Db-" . $db);
@@ -96,6 +88,29 @@ if (
echo "</form>\n"; echo "</form>\n";
echo script("tableCheck();"); echo script("tableCheck();");
} }
if (!empty(adminer()->plugins)) {
echo "<div class='plugins'>\n";
echo "<h3>" . lang('Loaded plugins') . "</h3>\n<ul>\n";
foreach (adminer()->plugins as $plugin) {
$description = (method_exists($plugin, 'description') ? $plugin->description() : "");
if (!$description) {
$reflection = new \ReflectionObject($plugin);
if (preg_match('~^/[\s*]+(.+)~', $reflection->getDocComment(), $match)) {
$description = $match[1];
}
}
$screenshot = (method_exists($plugin, 'screenshot') ? $plugin->screenshot() : "");
echo "<li><b>" . get_class($plugin) . "</b>"
. h($description ? ": $description" : "")
. ($screenshot ? " (<a href='" . h($screenshot) . "'" . target_blank() . ">" . lang('screenshot') . "</a>)" : "")
. "\n"
;
}
echo "</ul>\n";
adminer()->pluginsLinks();
echo "</div>\n";
}
} }
page_footer("db"); page_footer("db");

View File

@@ -3,12 +3,12 @@ namespace Adminer;
// coverage is used in tests and removed in compilation // coverage is used in tests and removed in compilation
if (extension_loaded("xdebug") && file_exists(sys_get_temp_dir() . "/adminer.coverage")) { if (extension_loaded("xdebug") && file_exists(sys_get_temp_dir() . "/adminer.coverage")) {
function save_coverage() { function save_coverage(): void {
$coverage_filename = sys_get_temp_dir() . "/adminer.coverage"; $coverage_filename = sys_get_temp_dir() . "/adminer.coverage";
$coverage = unserialize(file_get_contents($coverage_filename)); $coverage = unserialize(file_get_contents($coverage_filename));
foreach (xdebug_get_code_coverage() as $filename => $lines) { foreach (xdebug_get_code_coverage() as $filename => $lines) {
foreach ($lines as $l => $val) { foreach ($lines as $l => $val) {
if (!$coverage[$filename][$l] || $val > 0) { if (!idx($coverage[$filename], $l) || $val > 0) {
$coverage[$filename][$l] = $val; $coverage[$filename][$l] = $val;
} }
} }

View File

@@ -0,0 +1,56 @@
<?php
namespace Adminer;
// this could be interface when "Db extends \mysqli" can have compatible type declarations (PHP 7)
// interfaces can include properties only since PHP 8.4
abstract class SqlDb {
/** @var Db */ static $instance;
/** @var string */ public $extension; // extension name
/** @var string */ public $flavor = ''; // different vendor with the same API, e.g. MariaDB; usually stays empty
/** @var string */ public $server_info; // server version
/** @var int */ public $affected_rows = 0; // number of affected rows
/** @var string */ public $info = ''; // see https://php.net/mysql_info
/** @var int */ public $errno = 0; // last error code
/** @var string */ public $error = ''; // last error message
/** @var Result|bool */ protected $multi; // used for multiquery
/** Connect to server
* @return string error message
*/
abstract function attach(?string $server, string $username, string $password): string;
/** Quote string to use in SQL
* @return string escaped string enclosed in '
*/
abstract function quote(string $string): string;
/** Select database
* @return bool boolish
*/
abstract function select_db(string $database);
/** Send query
* @return Result|bool
*/
abstract function query(string $query, bool $unbuffered = false);
/** Send query with more resultsets
* @return Result|bool
*/
function multi_query(string $query) {
return $this->multi = $this->query($query);
}
/** Get current resultset
* @return Result|bool
*/
function store_result() {
return $this->multi;
}
/** Fetch next resultset */
function next_result(): bool {
return false;
}
}

View File

@@ -2,35 +2,32 @@
namespace Adminer; namespace Adminer;
/** Print HTML header /** Print HTML header
* @param string used in title, breadcrumb and heading, should be HTML escaped * @param string $title used in title, breadcrumb and heading, should be HTML escaped
* @param string * @param mixed $breadcrumb ["key" => "link", "key2" => ["link", "desc"]], null for nothing, false for driver only, true for driver and server
* @param mixed ["key" => "link", "key2" => ["link", "desc"]], null for nothing, false for driver only, true for driver and server * @param string $title2 used after colon in title and heading, should be HTML escaped
* @param string used after colon in title and heading, should be HTML escaped
* @return null
*/ */
function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") { function page_header(string $title, string $error = "", $breadcrumb = array(), string $title2 = ""): void {
global $LANG, $VERSION, $adminer, $drivers;
page_headers(); page_headers();
if (is_ajax() && $error) { if (is_ajax() && $error) {
page_messages($error); page_messages($error);
exit; exit;
} }
if (!ob_get_level()) { if (!ob_get_level()) {
ob_start(null, 4096); ob_start('ob_gzhandler', 4096);
} }
$title_all = $title . ($title2 != "" ? ": $title2" : ""); $title_all = $title . ($title2 != "" ? ": $title2" : "");
$title_page = strip_tags($title_all . (SERVER != "" && SERVER != "localhost" ? h(" - " . SERVER) : "") . " - " . $adminer->name()); $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 // initial-scale=1 is the default but Chrome 134 on iOS is not able to zoom out without it
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="<?php echo $LANG; ?>" dir="<?php echo lang('ltr'); ?>"> <html lang="<?php echo LANG; ?>" dir="<?php echo lang('ltr'); ?>">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title><?php echo $title_page; ?></title> <title><?php echo $title_page; ?></title>
<link rel="stylesheet" href="../adminer/static/default.css"> <link rel="stylesheet" href="../adminer/static/default.css">
<?php <?php
$css = $adminer->css(); $css = adminer()->css();
$has_light = false; $has_light = false;
$has_dark = false; $has_dark = false;
foreach ($css as $filename) { foreach ($css as $filename) {
@@ -53,12 +50,12 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
// this is matched by compile.php // this is matched by compile.php
echo script_src("../adminer/static/functions.js"); echo script_src("../adminer/static/functions.js");
echo script_src("static/editing.js"); echo script_src("static/editing.js");
if ($adminer->head($dark)) { if (adminer()->head($dark)) {
echo "<link rel='shortcut icon' type='image/x-icon' href='../adminer/static/favicon.ico'>\n"; echo "<link rel='icon' href='data:image/gif;base64,R0lGODlhEAAQAJEAAAQCBPz+/PwCBAROZCH5BAEAAAAALAAAAAAQABAAAAI2hI+pGO1rmghihiUdvUBnZ3XBQA7f05mOak1RWXrNq5nQWHMKvuoJ37BhVEEfYxQzHjWQ5qIAADs='>\n";
echo "<link rel='apple-touch-icon' href='../adminer/static/favicon.ico'>\n"; echo "<link rel='apple-touch-icon' href='../adminer/static/logo.png'>\n";
} }
foreach ($css as $val) { foreach ($css as $val) {
echo "<link rel='stylesheet'" . (preg_match('~-dark~', $val) && !$dark ? $media : "") . " href='" . h($val) . "'>\n"; echo "<link rel='stylesheet'" . (preg_match('~-dark\.~', $val) && !$dark ? $media : "") . " href='" . h($val) . "'>\n";
} }
echo "\n<body class='" . lang('ltr') . " nojs'>\n"; echo "\n<body class='" . lang('ltr') . " nojs'>\n";
$filename = get_temp_dir() . "/adminer.version"; $filename = get_temp_dir() . "/adminer.version";
@@ -79,7 +76,7 @@ fQIDAQAB
} }
} }
echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick" echo script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick"
. (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '$VERSION', '" . js_escape(ME) . "', '" . get_token() . "')") // $token may be empty in auth.inc.php . (isset($_COOKIE["adminer_version"]) ? "" : ", onload: partial(verifyVersion, '" . VERSION . "', '" . js_escape(ME) . "', '" . get_token() . "')")
. "}); . "});
document.body.classList.replace('nojs', 'js'); document.body.classList.replace('nojs', 'js');
const offlineMessage = '" . js_escape(lang('You are offline.')) . "'; const offlineMessage = '" . js_escape(lang('You are offline.')) . "';
@@ -88,11 +85,12 @@ const thousandsSeparator = '" . js_escape(lang(',')) . "';")
echo "<div id='help' class='jush-" . JUSH . " jsonly hidden'></div>\n"; echo "<div id='help' class='jush-" . JUSH . " jsonly hidden'></div>\n";
echo script("mixin(qs('#help'), {onmouseover: () => { helpOpen = 1; }, onmouseout: helpMouseout});"); echo script("mixin(qs('#help'), {onmouseover: () => { helpOpen = 1; }, onmouseout: helpMouseout});");
echo "<div id='content'>\n"; echo "<div id='content'>\n";
echo "<span id='menuopen' class='jsonly'>" . icon("move", "", "menu", "") . "</span>" . script("qs('#menuopen').onclick = event => { qs('#foot').classList.toggle('foot'); event.stopPropagation(); }");
if ($breadcrumb !== null) { if ($breadcrumb !== null) {
$link = substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1); $link = substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1);
echo '<p id="breadcrumb"><a href="' . h($link ?: ".") . '">' . $drivers[DRIVER] . '</a> » '; echo '<p id="breadcrumb"><a href="' . h($link ?: ".") . '">' . get_driver(DRIVER) . '</a> » ';
$link = substr(preg_replace('~\b(db|ns)=[^&]*&~', '', ME), 0, -1); $link = substr(preg_replace('~\b(db|ns)=[^&]*&~', '', ME), 0, -1);
$server = $adminer->serverName(SERVER); $server = adminer()->serverName(SERVER);
$server = ($server != "" ? $server : lang('Server')); $server = ($server != "" ? $server : lang('Server'));
if ($breadcrumb === false) { if ($breadcrumb === false) {
echo "$server\n"; echo "$server\n";
@@ -127,31 +125,28 @@ const thousandsSeparator = '" . js_escape(lang(',')) . "';")
define('Adminer\PAGE_HEADER', 1); define('Adminer\PAGE_HEADER', 1);
} }
/** Send HTTP headers /** Send HTTP headers */
* @return null function page_headers(): void {
*/
function page_headers() {
global $adminer;
header("Content-Type: text/html; charset=utf-8"); header("Content-Type: text/html; charset=utf-8");
header("Cache-Control: no-cache"); header("Cache-Control: no-cache");
header("X-Frame-Options: deny"); // ClickJacking protection in IE8, Safari 4, Chrome 2, Firefox 3.6.9 header("X-Frame-Options: deny"); // ClickJacking protection in IE8, Safari 4, Chrome 2, Firefox 3.6.9
header("X-XSS-Protection: 0"); // prevents introducing XSS in IE8 by removing safe parts of the page header("X-XSS-Protection: 0"); // prevents introducing XSS in IE8 by removing safe parts of the page
header("X-Content-Type-Options: nosniff"); header("X-Content-Type-Options: nosniff");
header("Referrer-Policy: origin-when-cross-origin"); header("Referrer-Policy: origin-when-cross-origin");
foreach ($adminer->csp() as $csp) { foreach (adminer()->csp(csp()) as $csp) {
$header = array(); $header = array();
foreach ($csp as $key => $val) { foreach ($csp as $key => $val) {
$header[] = "$key $val"; $header[] = "$key $val";
} }
header("Content-Security-Policy: " . implode("; ", $header)); header("Content-Security-Policy: " . implode("; ", $header));
} }
$adminer->headers(); adminer()->headers();
} }
/** Get Content Security Policy headers /** Get Content Security Policy headers
* @return array of arrays with directive name in key, allowed sources in value * @return list<string[]> of arrays with directive name in key, allowed sources in value
*/ */
function csp() { function csp(): array {
return array( return array(
array( array(
"script-src" => "'self' 'unsafe-inline' 'nonce-" . get_nonce() . "' 'strict-dynamic'", // 'self' is a fallback for browsers not supporting 'strict-dynamic', 'unsafe-inline' is a fallback for browsers not supporting 'nonce-' "script-src" => "'self' 'unsafe-inline' 'nonce-" . get_nonce() . "' 'strict-dynamic'", // 'self' is a fallback for browsers not supporting 'strict-dynamic', 'unsafe-inline' is a fallback for browsers not supporting 'nonce-'
@@ -167,7 +162,7 @@ function csp() {
/** Get a CSP nonce /** Get a CSP nonce
* @return string Base64 value * @return string Base64 value
*/ */
function get_nonce() { function get_nonce(): string {
static $nonce; static $nonce;
if (!$nonce) { if (!$nonce) {
$nonce = base64_encode(rand_string()); $nonce = base64_encode(rand_string());
@@ -175,14 +170,10 @@ function get_nonce() {
return $nonce; return $nonce;
} }
/** Print flash and error messages /** Print flash and error messages */
* @param string function page_messages(string $error): void {
* @return null
*/
function page_messages($error) {
global $adminer;
$uri = preg_replace('~^[^?]*~', '', $_SERVER["REQUEST_URI"]); $uri = preg_replace('~^[^?]*~', '', $_SERVER["REQUEST_URI"]);
$messages = $_SESSION["messages"][$uri]; $messages = idx($_SESSION["messages"], $uri);
if ($messages) { if ($messages) {
echo "<div class='message'>" . implode("</div>\n<div class='message'>", $messages) . "</div>" . script("messagesPrint();"); echo "<div class='message'>" . implode("</div>\n<div class='message'>", $messages) . "</div>" . script("messagesPrint();");
unset($_SESSION["messages"][$uri]); unset($_SESSION["messages"][$uri]);
@@ -190,20 +181,18 @@ function page_messages($error) {
if ($error) { if ($error) {
echo "<div class='error'>$error</div>\n"; echo "<div class='error'>$error</div>\n";
} }
if ($adminer->error) { // separate <div> if (adminer()->error) { // separate <div>
echo "<div class='error'>$adminer->error</div>\n"; echo "<div class='error'>" . adminer()->error . "</div>\n";
} }
} }
/** Print HTML footer /** Print HTML footer
* @param string "auth", "db", "ns" * @param ''|'auth'|'db'|'ns' $missing
* @return null
*/ */
function page_footer($missing = "") { function page_footer(string $missing = ""): void {
global $adminer; echo "</div>\n\n<div id='foot' class='foot'>\n<div id='menu'>\n";
echo "</div>\n\n<div id='menu'>\n"; adminer()->navigation($missing);
$adminer->navigation($missing); echo "</div>\n";
echo "</div>\n\n";
if ($missing != "auth") { if ($missing != "auth") {
?> ?>
<form action="" method="post"> <form action="" method="post">
@@ -211,9 +200,9 @@ function page_footer($missing = "") {
<span><?php echo h($_GET["username"]) . "\n"; ?></span> <span><?php echo h($_GET["username"]) . "\n"; ?></span>
<input type="submit" name="logout" value="<?php echo lang('Logout'); ?>" id="logout"> <input type="submit" name="logout" value="<?php echo lang('Logout'); ?>" id="logout">
<?php echo input_token(); ?> <?php echo input_token(); ?>
</p>
</form> </form>
<?php <?php
} }
echo "</div>\n\n";
echo script("setupSubmitHighlight(document);"); echo script("setupSubmitHighlight(document);");
} }

View File

@@ -1,98 +1,94 @@
<?php <?php
namespace Adminer; namespace Adminer;
$drivers = array(); /** Add or overwrite a driver */
function add_driver(string $id, string $name): void {
/** Add a driver SqlDriver::$drivers[$id] = $name;
* @param string
* @param string
* @return null
*/
function add_driver($id, $name) {
global $drivers;
$drivers[$id] = $name;
} }
/** Get driver name /** Get driver name */
* @param string function get_driver(string $id): string {
* @return string return SqlDriver::$drivers[$id];
*/
function get_driver($id) {
global $drivers;
return $drivers[$id];
} }
abstract class SqlDriver { abstract class SqlDriver {
static $possibleDrivers = array(); /** @var Driver */ static $instance;
static $jush; ///< @var string JUSH identifier /** @var string[] */ static $drivers = array(); // all available drivers
/** @var list<string> */ static $extensions = array(); // possible extensions in the current driver
/** @var string */ static $jush; // JUSH identifier
protected $conn; /** @var Db */ protected $conn;
protected $types = array(); ///< @var array [$description => [$type => $maximum_unsigned_length, ...], ...] /** @var int[][] */ protected $types = array(); // [$group => [$type => $maximum_unsigned_length, ...], ...]
public $editFunctions = array(); ///< @var array of ["$type|$type2" => "$function/$function2"] functions used in editing, [0] - edit and insert, [1] - edit only /** @var string[] */ public $insertFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit and insert
public $unsigned = array(); ///< @var array number variants /** @var string[] */ public $editFunctions = array(); // ["$type|$type2" => "$function/$function2"] functions used in edit only
public $operators = array(); ///< @var array operators used in select /** @var list<string> */ public $unsigned = array(); // number variants
public $functions = array(); ///< @var array functions used in select /** @var list<string> */ public $operators = array(); // operators used in select
public $grouping = array(); ///< @var array grouping functions used in select /** @var list<string> */ public $functions = array(); // functions used in select
public $onActions = "RESTRICT|NO ACTION|CASCADE|SET NULL|SET DEFAULT"; ///< @var string used in foreign_keys() /** @var list<string> */ public $grouping = array(); // grouping functions used in select
public $inout = "IN|OUT|INOUT"; ///< @var string used in routines /** @var string */ public $onActions = "RESTRICT|NO ACTION|CASCADE|SET NULL|SET DEFAULT"; // used in foreign_keys()
public $enumLength = "'(?:''|[^'\\\\]|\\\\.)*'"; ///< @var string regular expression for parsing enum lengths /** @var string */ public $inout = "IN|OUT|INOUT"; // used in routines
public $generated = array(); ///< @var array allowed types of generated columns /** @var string */ public $enumLength = "'(?:''|[^'\\\\]|\\\\.)*'"; // regular expression for parsing enum lengths
/** @var list<string> */ public $generated = array(); // allowed types of generated columns
/** Create object for performing database operations /** Connect to the database
* @param Db * @return Db|string string for error
*/ */
function __construct($connection) { static function connect(?string $server, string $username, string $password) {
$connection = new Db;
return ($connection->attach($server, $username, $password) ?: $connection);
}
/** Create object for performing database operations */
function __construct(Db $connection) {
$this->conn = $connection; $this->conn = $connection;
} }
/** Get all types /** Get all types
* @return array [$type => $maximum_unsigned_length, ...] * @return int[] [$type => $maximum_unsigned_length, ...]
*/ */
function types() { function types(): array {
return call_user_func_array('array_merge', array_values($this->types)); return call_user_func_array('array_merge', array_values($this->types));
} }
/** Get structured types /** Get structured types
* @return array [$description => [$type, ...], ...] * @return list<string>[]|list<string> [$description => [$type, ...], ...]
*/ */
function structuredTypes() { function structuredTypes(): array {
return array_map('array_keys', $this->types); return array_map('array_keys', $this->types);
} }
/** Get enum values /** Get enum values
* @param array * @param Field $field
* @return string or null * @return string|void
*/ */
function enumLength($field) { function enumLength(array $field) {
} }
/** Function used to convert the value inputted by user /** Function used to convert the value inputted by user
* @param array * @param Field $field
* @return string or null * @return string|void
*/ */
function unconvertFunction($field) { function unconvertFunction(array $field) {
} }
/** Select data from table /** Select data from table
* @param string * @param list<string> $select result of adminer()->selectColumnsProcess()[0]
* @param array result of $adminer->selectColumnsProcess()[0] * @param list<string> $where result of adminer()->selectSearchProcess()
* @param array result of $adminer->selectSearchProcess() * @param list<string> $group result of adminer()->selectColumnsProcess()[1]
* @param array result of $adminer->selectColumnsProcess()[1] * @param list<string> $order result of adminer()->selectOrderProcess()
* @param array result of $adminer->selectOrderProcess() * @param int $limit result of adminer()->selectLimitProcess()
* @param int result of $adminer->selectLimitProcess() * @param int $page index of page starting at zero
* @param int index of page starting at zero * @param bool $print whether to print the query
* @param bool whether to print the query * @return Result|false
* @return Result
*/ */
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) { function select(string $table, array $select, array $where, array $group, array $order = array(), int $limit = 1, ?int $page = 0, bool $print = false) {
global $adminer;
$is_group = (count($group) < count($select)); $is_group = (count($group) < count($select));
$query = $adminer->selectQueryBuild($select, $where, $group, $order, $limit, $page); $query = adminer()->selectQueryBuild($select, $where, $group, $order, $limit, $page);
if (!$query) { if (!$query) {
$query = "SELECT" . limit( $query = "SELECT" . limit(
($_GET["page"] != "last" && $limit != "" && $group && $is_group && JUSH == "sql" ? "SQL_CALC_FOUND_ROWS " : "") . implode(", ", $select) . "\nFROM " . table($table), ($_GET["page"] != "last" && $limit && $group && $is_group && JUSH == "sql" ? "SQL_CALC_FOUND_ROWS " : "") . implode(", ", $select) . "\nFROM " . table($table),
($where ? "\nWHERE " . implode(" AND ", $where) : "") . ($group && $is_group ? "\nGROUP BY " . implode(", ", $group) : "") . ($order ? "\nORDER BY " . implode(", ", $order) : ""), ($where ? "\nWHERE " . implode(" AND ", $where) : "") . ($group && $is_group ? "\nGROUP BY " . implode(", ", $group) : "") . ($order ? "\nORDER BY " . implode(", ", $order) : ""),
($limit != "" ? +$limit : null), $limit,
($page ? $limit * $page : 0), ($page ? $limit * $page : 0),
"\n" "\n"
); );
@@ -100,31 +96,28 @@ abstract class SqlDriver {
$start = microtime(true); $start = microtime(true);
$return = $this->conn->query($query); $return = $this->conn->query($query);
if ($print) { if ($print) {
echo $adminer->selectQuery($query, $start, !$return); echo adminer()->selectQuery($query, $start, !$return);
} }
return $return; return $return;
} }
/** Delete data from table /** Delete data from table
* @param string * @param string $queryWhere " WHERE ..."
* @param string " WHERE ..." * @param int $limit 0 or 1
* @param int 0 or 1 * @return Result|bool
* @return bool
*/ */
function delete($table, $queryWhere, $limit = 0) { function delete(string $table, string $queryWhere, int $limit = 0) {
$query = "FROM " . table($table); $query = "FROM " . table($table);
return queries("DELETE" . ($limit ? limit1($table, $query, $queryWhere) : " $query$queryWhere")); return queries("DELETE" . ($limit ? limit1($table, $query, $queryWhere) : " $query$queryWhere"));
} }
/** Update data in table /** Update data in table
* @param string * @param string[] $set escaped columns in keys, quoted data in values
* @param array escaped columns in keys, quoted data in values * @param string $queryWhere " WHERE ..."
* @param string " WHERE ..." * @param int $limit 0 or 1
* @param int 0 or 1 * @return Result|bool
* @param string
* @return bool
*/ */
function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") { function update(string $table, array $set, string $queryWhere, int $limit = 0, string $separator = "\n") {
$values = array(); $values = array();
foreach ($set as $key => $val) { foreach ($set as $key => $val) {
$values[] = "$key = $val"; $values[] = "$key = $val";
@@ -134,150 +127,142 @@ abstract class SqlDriver {
} }
/** Insert data into table /** Insert data into table
* @param string * @param string[] $set escaped columns in keys, quoted data in values
* @param array escaped columns in keys, quoted data in values * @return Result|bool
* @return bool
*/ */
function insert($table, $set) { function insert(string $table, array $set) {
return queries("INSERT INTO " . table($table) . ($set return queries("INSERT INTO " . table($table) . ($set
? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")" ? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")"
: " DEFAULT VALUES" : " DEFAULT VALUES"
) . $this->insertReturning($table)); ) . $this->insertReturning($table));
} }
/** Get RETURNING clause for INSERT queries, PostgreSQL specific /** Get RETURNING clause for INSERT queries (PostgreSQL specific) */
* @param string function insertReturning(string $table): string {
* @return string
*/
function insertReturning($table) {
return ""; return "";
} }
/** Insert or update data in table /** Insert or update data in table
* @param string * @param list<string[]> $rows of arrays with escaped columns in keys and quoted data in values
* @param array * @param int[] $primary column names in keys
* @param array of arrays with escaped columns in keys and quoted data in values * @return Result|bool
* @return bool
*/ */
function insertUpdate($table, $rows, $primary) { function insertUpdate(string $table, array $rows, array $primary) {
return false; return false;
} }
/** Begin transaction /** Begin transaction
* @return bool * @return Result|bool
*/ */
function begin() { function begin() {
return queries("BEGIN"); return queries("BEGIN");
} }
/** Commit transaction /** Commit transaction
* @return bool * @return Result|bool
*/ */
function commit() { function commit() {
return queries("COMMIT"); return queries("COMMIT");
} }
/** Rollback transaction /** Rollback transaction
* @return bool * @return Result|bool
*/ */
function rollback() { function rollback() {
return queries("ROLLBACK"); return queries("ROLLBACK");
} }
/** Return query with a timeout /** Return query with a timeout
* @param string * @param int $timeout seconds
* @param int seconds * @return string|void null if the driver doesn't support query timeouts
* @return string or null if the driver doesn't support query timeouts
*/ */
function slowQuery($query, $timeout) { function slowQuery(string $query, int $timeout) {
} }
/** Convert column to be searchable /** Convert column to be searchable
* @param string escaped column name * @param string $idf escaped column name
* @param array ["op" => , "val" => ] * @param array{op:string, val:string} $val
* @param array * @param Field $field
* @return string
*/ */
function convertSearch($idf, $val, $field) { function convertSearch(string $idf, array $val, array $field): string {
return $idf; return $idf;
} }
/** Convert operator so it can be used in search /** Convert operator so it can be used in search */
* @param string $operator function convertOperator(string $operator): string {
* @return string
*/
function convertOperator($operator) {
return $operator; return $operator;
} }
/** Convert value returned by database to actual value /** Convert value returned by database to actual value
* @param string * @param Field $field
* @param array
* @return string
*/ */
function value($val, $field) { function value(?string $val, array $field): ?string {
return (method_exists($this->conn, 'value') return (method_exists($this->conn, 'value') ? $this->conn->value($val, $field) : $val);
? $this->conn->value($val, $field)
: (is_resource($val) ? stream_get_contents($val) : $val)
);
} }
/** Quote binary string /** Quote binary string */
* @param string function quoteBinary(string $s): string {
* @return string
*/
function quoteBinary($s) {
return q($s); return q($s);
} }
/** Get warnings about the last command /** Get warnings about the last command
* @return string HTML * @return string|void HTML
*/ */
function warnings() { function warnings() {
return '';
} }
/** Get help link for table /** Get help link for table
* @param string * @return string|void relative URL
* @param bool
* @return string relative URL or null
*/ */
function tableHelp($name, $is_view = false) { function tableHelp(string $name, bool $is_view = false) {
} }
/** Check if C-style escapes are supported /** Check if C-style escapes are supported */
* @return bool function hasCStyleEscapes(): bool {
*/
function hasCStyleEscapes() {
return false; return false;
} }
/** Get supported engines /** Get supported engines
* @return array * @return list<string>
*/ */
function engines() { function engines(): array {
return array(); return array();
} }
/** Check whether table supports indexes /** Check whether table supports indexes
* @param array result of table_status() * @param TableStatus $table_status
* @return bool
*/ */
function supportsIndex($table_status) { function supportsIndex(array $table_status): bool {
return !is_view($table_status); return !is_view($table_status);
} }
/** Get defined check constraints /** Get defined check constraints
* @param string * @return string[] [$name => $clause]
* @return array [$name => $clause]
*/ */
function checkConstraints($table) { function checkConstraints(string $table): array {
// MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not // MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not
return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE
FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME
WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . " WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . "
AND t.TABLE_NAME = " . q($table) . " AND t.TABLE_NAME = " . q($table) . "
AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'"); // ignore default IS NOT NULL checks in PostrgreSQL AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'", $this->conn); // ignore default IS NOT NULL checks in PostrgreSQL
}
/** Get all fields in the current schema
* @return array<list<array{field:string, null:bool, type:string, length:?numeric-string, primary?:numeric-string}>>
*/
function allFields(): array {
$return = array();
foreach (
get_rows("SELECT TABLE_NAME AS tab, COLUMN_NAME AS field, IS_NULLABLE AS nullable, DATA_TYPE AS type, CHARACTER_MAXIMUM_LENGTH AS length" . (JUSH == 'sql' ? ", COLUMN_KEY = 'PRI' AS `primary`" : "") . "
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . "
ORDER BY TABLE_NAME, ORDINAL_POSITION", $this->conn) as $row
) {
$row["null"] = ($row["nullable"] == "YES");
$return[$row["tab"]][] = $row;
}
return $return;
} }
} }

View File

@@ -4,13 +4,12 @@ namespace Adminer;
// This file is not used in Adminer Editor. // This file is not used in Adminer Editor.
/** Print select result /** Print select result
* @param Result * @param Result $result
* @param Db connection to examine indexes * @param string[] $orgtables
* @param array * @param int|numeric-string $limit
* @param int * @return string[] $orgtables
* @return array $orgtables
*/ */
function select($result, $connection2 = null, $orgtables = array(), $limit = 0) { function print_select_result($result, ?Db $connection2 = null, array $orgtables = array(), $limit = 0): array {
$links = array(); // colno => orgtable - create links from these columns $links = array(); // colno => orgtable - create links from these columns
$indexes = array(); // orgtable => array(column => colno) - primary keys $indexes = array(); // orgtable => array(column => colno) - primary keys
$columns = array(); // orgtable => array(column => ) - not selected columns in primary key $columns = array(); // orgtable => array(column => ) - not selected columns in primary key
@@ -101,10 +100,9 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
} }
/** Get referencable tables with single column primary key except self /** Get referencable tables with single column primary key except self
* @param string * @return array<string, Field> [$table_name => $field]
* @return array [$table_name => $field]
*/ */
function referencable_primary($self) { function referencable_primary(string $self): array {
$return = array(); // table_name => field $return = array(); // table_name => field
foreach (table_status('', true) as $table_name => $table) { foreach (table_status('', true) as $table_name => $table) {
if ($table_name != $self && fk_support($table)) { if ($table_name != $self && fk_support($table)) {
@@ -123,13 +121,9 @@ function referencable_primary($self) {
} }
/** Print SQL <textarea> tag /** Print SQL <textarea> tag
* @param string * @param string|list<array{string}> $value
* @param string or array in which case [0] of every element is used
* @param int
* @param int
* @return null
*/ */
function textarea($name, $value, $rows = 10, $cols = 80) { function textarea(string $name, $value, int $rows = 10, int $cols = 80): void {
echo "<textarea name='" . h($name) . "' rows='$rows' cols='$cols' class='sqlarea jush-" . JUSH . "' spellcheck='false' wrap='off'>"; echo "<textarea name='" . h($name) . "' rows='$rows' cols='$cols' class='sqlarea jush-" . JUSH . "' spellcheck='false' wrap='off'>";
if (is_array($value)) { if (is_array($value)) {
foreach ($value as $val) { // not implode() to save memory foreach ($value as $val) { // not implode() to save memory
@@ -142,14 +136,9 @@ function textarea($name, $value, $rows = 10, $cols = 80) {
} }
/** Generate HTML <select> or <input> if $options are empty /** Generate HTML <select> or <input> if $options are empty
* @param string * @param string[] $options
* @param array
* @param string
* @param string
* @param string
* @return string
*/ */
function select_input($attrs, $options, $value = "", $onchange = "", $placeholder = "") { function select_input(string $attrs, array $options, ?string $value = "", string $onchange = "", string $placeholder = ""): string {
$tag = ($options ? "select" : "input"); $tag = ($options ? "select" : "input");
return "<$tag$attrs" . ($options return "<$tag$attrs" . ($options
? "><option value=''>$placeholder" . optionlist($options, $value, true) . "</select>" ? "><option value=''>$placeholder" . optionlist($options, $value, true) . "</select>"
@@ -158,11 +147,10 @@ function select_input($attrs, $options, $value = "", $onchange = "", $placeholde
} }
/** Print one row in JSON object /** Print one row in JSON object
* @param string or "" to close the object * @param string $key or "" to close the object
* @param string * @param string|int $val
* @return null
*/ */
function json_row($key, $val = null) { function json_row(string $key, $val = null): void {
static $first = true; static $first = true;
if ($first) { if ($first) {
echo "{"; echo "{";
@@ -177,21 +165,18 @@ function json_row($key, $val = null) {
} }
/** Print table columns for type edit /** Print table columns for type edit
* @param string * @param Field $field
* @param array * @param list<string> $collations
* @param array * @param string[] $foreign_keys
* @param array returned by referencable_primary() * @param list<string> $extra_types extra types to prepend
* @param array extra types to prepend
* @return null
*/ */
function edit_type($key, $field, $collations, $foreign_keys = array(), $extra_types = array()) { function edit_type(string $key, array $field, array $collations, array $foreign_keys = array(), array $extra_types = array()): void {
global $driver;
$type = $field["type"]; $type = $field["type"];
echo "<td><select name='" . h($key) . "[type]' class='type' aria-labelledby='label-type'>"; 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)) { if ($type && !array_key_exists($type, driver()->types()) && !isset($foreign_keys[$type]) && !in_array($type, $extra_types)) {
$extra_types[] = $type; $extra_types[] = $type;
} }
$structured_types = $driver->structuredTypes(); $structured_types = driver()->structuredTypes();
if ($foreign_keys) { if ($foreign_keys) {
$structured_types[lang('Foreign keys')] = $foreign_keys; $structured_types[lang('Foreign keys')] = $foreign_keys;
} }
@@ -205,25 +190,23 @@ function edit_type($key, $field, $collations, $foreign_keys = array(), $extra_ty
? "<input list='collations' name='" . h($key) . "[collation]'" . (preg_match('~(char|text|enum|set)$~', $type) ? "" : " class='hidden'") . " value='" . h($field["collation"]) . "' placeholder='(" . lang('collation') . ")'>" ? "<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 (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'") . '>' 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"])) . optionlist(array("" => "(" . lang('ON UPDATE') . ")", "CURRENT_TIMESTAMP"), (preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) ? "CURRENT_TIMESTAMP" : $field["on_update"]))
. '</select>' : '' . '</select>' : ''
); );
echo ($foreign_keys 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> " ? "<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 : " " // space for IE
); );
} }
/** Get partition info /** Get partition info
* @param string * @return array{partition_by:string, partition:string, partitions:string, partition_names:list<string>, partition_values:list<string>}
* @return array
*/ */
function get_partitions_info($table) { function get_partitions_info(string $table): array {
global $connection;
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table); $from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table);
$result = $connection->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1"); $result = connection()->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
$return = array(); $return = array();
list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row(); list($return["partition_by"], $return["partition"], $return["partitions"]) = $result->fetch_row();
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION"); $partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
@@ -232,13 +215,9 @@ function get_partitions_info($table) {
return $return; return $return;
} }
/** Filter length value including enums /** Filter length value including enums */
* @param string function process_length(?string $length): string {
* @return string $enum_length = driver()->enumLength;
*/
function process_length($length) {
global $driver;
$enum_length = $driver->enumLength;
return (preg_match("~^\\s*\\(?\\s*$enum_length(?:\\s*,\\s*$enum_length)*+\\s*\\)?\\s*\$~", $length) && preg_match_all("~$enum_length~", $length, $matches) return (preg_match("~^\\s*\\(?\\s*$enum_length(?:\\s*,\\s*$enum_length)*+\\s*\\)?\\s*\$~", $length) && preg_match_all("~$enum_length~", $length, $matches)
? "(" . implode(",", $matches[0]) . ")" ? "(" . implode(",", $matches[0]) . ")"
: preg_replace('~^[0-9].*~', '(\0)', preg_replace('~[^-0-9,+()[\]]~', '', $length)) : preg_replace('~^[0-9].*~', '(\0)', preg_replace('~[^-0-9,+()[\]]~', '', $length))
@@ -246,25 +225,22 @@ function process_length($length) {
} }
/** Create SQL string from field type /** Create SQL string from field type
* @param array * @param FieldType $field
* @param string
* @return string
*/ */
function process_type($field, $collate = "COLLATE") { function process_type(array $field, string $collate = "COLLATE"): string {
global $driver;
return " $field[type]" return " $field[type]"
. process_length($field["length"]) . process_length($field["length"])
. (preg_match(number_type(), $field["type"]) && in_array($field["unsigned"], $driver->unsigned) ? " $field[unsigned]" : "") . (preg_match(number_type(), $field["type"]) && in_array($field["unsigned"], driver()->unsigned) ? " $field[unsigned]" : "")
. (preg_match('~char|text|enum|set~', $field["type"]) && $field["collation"] ? " $collate " . (JUSH == "mssql" ? $field["collation"] : q($field["collation"])) : "") . (preg_match('~char|text|enum|set~', $field["type"]) && $field["collation"] ? " $collate " . (JUSH == "mssql" ? $field["collation"] : q($field["collation"])) : "")
; ;
} }
/** Create SQL string from field /** Create SQL string from field
* @param array basic field information * @param Field $field basic field information
* @param array information about field type * @param Field $type_field information about field type
* @return array ["field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT"] * @return list<string> ["field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT"]
*/ */
function process_field($field, $type_field) { function process_field(array $field, array $type_field): array {
// MariaDB exports CURRENT_TIMESTAMP as a function. // MariaDB exports CURRENT_TIMESTAMP as a function.
if ($field["on_update"]) { if ($field["on_update"]) {
$field["on_update"] = str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", $field["on_update"]); $field["on_update"] = str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", $field["on_update"]);
@@ -281,14 +257,12 @@ function process_field($field, $type_field) {
} }
/** Get default value clause /** Get default value clause
* @param array * @param Field $field
* @return string
*/ */
function default_value($field) { function default_value(array $field): string {
global $driver;
$default = $field["default"]; $default = $field["default"];
$generated = $field["generated"]; $generated = $field["generated"];
return ($default === null ? "" : (in_array($generated, $driver->generated) return ($default === null ? "" : (in_array($generated, driver()->generated)
? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") . "" : " GENERATED ALWAYS AS ($default) $generated") ? (JUSH == "mssql" ? " AS ($default)" . ($generated == "VIRTUAL" ? "" : " $generated") . "" : " GENERATED ALWAYS AS ($default) $generated")
: " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default)) : " DEFAULT " . (!preg_match('~^GENERATED ~i', $default) && (preg_match('~char|binary|text|json|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default))
? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column ? (JUSH == "sql" && preg_match('~text|json~', $field["type"]) ? "(" . q($default) . ")" : q($default)) // MySQL requires () around default value of text column
@@ -298,10 +272,9 @@ function default_value($field) {
} }
/** Get type class to use in CSS /** Get type class to use in CSS
* @param string * @return string|void class=''
* @return string class=''
*/ */
function type_class($type) { function type_class(string $type) {
foreach ( foreach (
array( array(
'char' => 'text', 'char' => 'text',
@@ -317,14 +290,12 @@ function type_class($type) {
} }
/** Print table interior for fields editing /** Print table interior for fields editing
* @param array * @param (Field|RoutineField)[] $fields
* @param array * @param list<string> $collations
* @param string TABLE or PROCEDURE * @param 'TABLE'|'PROCEDURE' $type
* @param array returned by referencable_primary() * @param string[] $foreign_keys
* @return null
*/ */
function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = array()) { function edit_fields(array $fields, array $collations, $type = "TABLE", array $foreign_keys = array()): void {
global $driver;
$fields = array_values($fields); $fields = array_values($fields);
$default_class = (($_POST ? $_POST["defaults"] : get_setting("defaults")) ? "" : " class='hidden'"); $default_class = (($_POST ? $_POST["defaults"] : get_setting("defaults")) ? "" : " class='hidden'");
$comment_class = (($_POST ? $_POST["comments"] : get_setting("comments")) ? "" : " class='hidden'"); $comment_class = (($_POST ? $_POST["comments"] : get_setting("comments")) ? "" : " class='hidden'");
@@ -347,15 +318,15 @@ function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = arra
echo "<td id='label-default'$default_class>" . lang('Default value'); echo "<td id='label-default'$default_class>" . lang('Default value');
echo (support("comment") ? "<td id='label-comment'$comment_class>" . lang('Comment') : ""); 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 "<td>" . icon("plus", "add[" . (support("move_col") ? 0 : count($fields)) . "]", "+", lang('Add next'));
echo "</thead>\n<tbody>\n"; echo "</thead>\n<tbody>\n";
echo script("mixin(qsl('tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput});"); echo script("mixin(qsl('tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput});");
foreach ($fields as $i => $field) { foreach ($fields as $i => $field) {
$i++; $i++;
$orig = $field[($_POST ? "orig" : "field")]; $orig = $field[($_POST ? "orig" : "field")];
$display = (isset($_POST["add"][$i-1]) || (isset($field["field"]) && !$_POST["drop_col"][$i])) && (support("drop_col") || $orig == ""); $display = (isset($_POST["add"][$i-1]) || (isset($field["field"]) && !idx($_POST["drop_col"], $i))) && (support("drop_col") || $orig == "");
echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n"; echo "<tr" . ($display ? "" : " style='display: none;'") . ">\n";
echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", $driver->inout), $field["inout"]) : "") . "<th>"; echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", explode("|", driver()->inout), $field["inout"]) : "") . "<th>";
if ($display) { if ($display) {
echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'>"; echo "<input name='fields[$i][field]' value='" . h($field["field"]) . "' data-maxlength='64' autocapitalize='off' aria-labelledby='label-name'>";
} }
@@ -364,8 +335,8 @@ function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = arra
if ($type == "TABLE") { if ($type == "TABLE") {
echo "<td>" . checkbox("fields[$i][null]", 1, $field["null"], "", "", "block", "label-null"); 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><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 echo "<td$default_class>" . (driver()->generated
? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), $driver->generated), $field["generated"]) . " " ? html_select("fields[$i][generated]", array_merge(array("", "DEFAULT"), driver()->generated), $field["generated"]) . " "
: checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default") : checkbox("fields[$i][generated]", 1, $field["generated"], "", "", "", "label-default")
); );
echo "<input name='fields[$i][default]' value='" . h($field["default"]) . "' aria-labelledby='label-default'>"; echo "<input name='fields[$i][default]' value='" . h($field["default"]) . "' aria-labelledby='label-default'>";
@@ -373,19 +344,18 @@ function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = arra
} }
echo "<td>"; echo "<td>";
echo (support("move_col") ? echo (support("move_col") ?
"<input type='image' class='icon' name='add[$i]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'> " icon("plus", "add[$i]", "+", lang('Add next')) . " "
. "<input type='image' class='icon' name='up[$i]' src='../adminer/static/up.gif' alt='↑' title='" . lang('Move up') . "'> " . icon("up", "up[$i]", "", lang('Move up')) . " "
. "<input type='image' class='icon' name='down[$i]' src='../adminer/static/down.gif' alt='↓' title='" . lang('Move down') . "'> " . icon("down", "down[$i]", "", lang('Move down')) . " "
: ""); : "");
echo ($orig == "" || support("drop_col") ? "<input type='image' class='icon' name='drop_col[$i]' src='../adminer/static/cross.gif' alt='x' title='" . lang('Remove') . "'>" : ""); echo ($orig == "" || support("drop_col") ? icon("cross", "drop_col[$i]", "x", lang('Remove')) : "");
} }
} }
/** Move fields up and down or add field /** Move fields up and down or add field
* @param array * @param Field[] $fields
* @return bool
*/ */
function process_fields(&$fields) { function process_fields(array &$fields): bool {
$offset = 0; $offset = 0;
if ($_POST["up"]) { if ($_POST["up"]) {
$last = 0; $last = 0;
@@ -423,21 +393,19 @@ function process_fields(&$fields) {
} }
/** Callback used in routine() /** Callback used in routine()
* @param array * @param list<string> $match
* @return string
*/ */
function normalize_enum($match) { function normalize_enum(array $match): string {
return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($match[0][0] . $match[0][0], $match[0][0], substr($match[0], 1, -1))), '\\')) . "'"; $val = $match[0];
return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($val[0] . $val[0], $val[0], substr($val, 1, -1))), '\\')) . "'";
} }
/** Issue grant or revoke commands /** Issue grant or revoke commands
* @param string GRANT or REVOKE * @param 'GRANT'|'REVOKE' $grant
* @param array * @param list<string> $privileges
* @param string * @return Result|bool
* @param string
* @return bool
*/ */
function grant($grant, $privileges, $columns, $on) { function grant(string $grant, array $privileges, ?string $columns, string $on) {
if (!$privileges) { if (!$privileges) {
return true; return true;
} }
@@ -452,20 +420,14 @@ function grant($grant, $privileges, $columns, $on) {
} }
/** Drop old object and create a new one /** Drop old object and create a new one
* @param string drop old object query * @param string $drop drop old object query
* @param string create new object query * @param string $create create new object query
* @param string drop new object query * @param string $drop_created drop new object query
* @param string create test object query * @param string $test create test object query
* @param string drop test object query * @param string $drop_test drop test object query
* @param string * @return void redirect on success
* @param string
* @param string
* @param string
* @param string
* @param string
* @return null redirect in success
*/ */
function drop_create($drop, $create, $drop_created, $test, $drop_test, $location, $message_drop, $message_alter, $message_create, $old_name, $new_name) { function drop_create(string $drop, string $create, string $drop_created, string $test, string $drop_test, string $location, string $message_drop, string $message_alter, string $message_create, string $old_name, string $new_name): void {
if ($_POST["drop"]) { if ($_POST["drop"]) {
query_redirect($drop, $location, $message_drop); query_redirect($drop, $location, $message_drop);
} elseif ($old_name == "") { } elseif ($old_name == "") {
@@ -486,11 +448,9 @@ function drop_create($drop, $create, $drop_created, $test, $drop_test, $location
} }
/** Generate SQL query for creating trigger /** Generate SQL query for creating trigger
* @param string * @param Trigger $row
* @param array result of trigger()
* @return string
*/ */
function create_trigger($on, $row) { function create_trigger(string $on, array $row): string {
$timing_event = " $row[Timing] $row[Event]" . (preg_match('~ OF~', $row["Event"]) ? " $row[Of]" : ""); // SQL injection $timing_event = " $row[Timing] $row[Event]" . (preg_match('~ OF~', $row["Event"]) ? " $row[Of]" : ""); // SQL injection
return "CREATE TRIGGER " return "CREATE TRIGGER "
. idf_escape($row["Trigger"]) . idf_escape($row["Trigger"])
@@ -501,18 +461,16 @@ function create_trigger($on, $row) {
} }
/** Generate SQL query for creating routine /** Generate SQL query for creating routine
* @param string "PROCEDURE" or "FUNCTION" * @param 'PROCEDURE'|'FUNCTION' $routine
* @param array result of routine() * @param Routine $row
* @return string
*/ */
function create_routine($routine, $row) { function create_routine($routine, array $row): string {
global $driver;
$set = array(); $set = array();
$fields = (array) $row["fields"]; $fields = (array) $row["fields"];
ksort($fields); // enforce fields order ksort($fields); // enforce fields order
foreach ($fields as $field) { foreach ($fields as $field) {
if ($field["field"] != "") { if ($field["field"] != "") {
$set[] = (preg_match("~^($driver->inout)\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET"); $set[] = (preg_match("~^(" . driver()->inout . ")\$~", $field["inout"]) ? "$field[inout] " : "") . idf_escape($field["field"]) . process_type($field, "CHARACTER SET");
} }
} }
$definition = rtrim($row["definition"], ";"); $definition = rtrim($row["definition"], ";");
@@ -525,20 +483,15 @@ function create_routine($routine, $row) {
; ;
} }
/** Remove current user definer from SQL command /** Remove current user definer from SQL command */
* @param string function remove_definer(string $query): string {
* @return string
*/
function remove_definer($query) {
return preg_replace('~^([A-Z =]+) DEFINER=`' . preg_replace('~@(.*)~', '`@`(%|\1)', logged_user()) . '`~', '\1', $query); //! proper escaping of user return preg_replace('~^([A-Z =]+) DEFINER=`' . preg_replace('~@(.*)~', '`@`(%|\1)', logged_user()) . '`~', '\1', $query); //! proper escaping of user
} }
/** Format foreign key to use in SQL query /** Format foreign key to use in SQL query
* @param array ["db" => string, "ns" => string, "table" => string, "source" => array, "target" => array, "on_delete" => one of $on_actions, "on_update" => one of $on_actions] * @param ForeignKey $foreign_key
* @return string
*/ */
function format_foreign_key($foreign_key) { function format_foreign_key(array $foreign_key): string {
global $driver;
$db = $foreign_key["db"]; $db = $foreign_key["db"];
$ns = $foreign_key["ns"]; $ns = $foreign_key["ns"];
return " FOREIGN KEY (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["source"])) . ") REFERENCES " return " FOREIGN KEY (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["source"])) . ") REFERENCES "
@@ -546,17 +499,16 @@ function format_foreign_key($foreign_key) {
. ($ns != "" && $ns != $_GET["ns"] ? idf_escape($ns) . "." : "") . ($ns != "" && $ns != $_GET["ns"] ? idf_escape($ns) . "." : "")
. idf_escape($foreign_key["table"]) . idf_escape($foreign_key["table"])
. " (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions . " (" . implode(", ", array_map('Adminer\idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions
. (preg_match("~^($driver->onActions)\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "") . (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "")
. (preg_match("~^($driver->onActions)\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "") . (preg_match("~^(" . driver()->onActions . ")\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "")
; ;
} }
/** Add a file to TAR /** Add a file to TAR
* @param string * @param TmpFile $tmp_file
* @param TmpFile * @return void prints the output
* @return null prints the output
*/ */
function tar_file($filename, $tmp_file) { function tar_file(string $filename, $tmp_file): void {
$return = pack("a100a8a8a8a12a12", $filename, 644, 0, 0, decoct($tmp_file->size), decoct(time())); $return = pack("a100a8a8a8a12a12", $filename, 644, 0, 0, decoct($tmp_file->size), decoct(time()));
$checksum = 8*32; // space for checksum itself $checksum = 8*32; // space for checksum itself
for ($i=0; $i < strlen($return); $i++) { for ($i=0; $i < strlen($return); $i++) {
@@ -569,11 +521,8 @@ function tar_file($filename, $tmp_file) {
echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512); echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512);
} }
/** Get INI bytes value /** Get INI bytes value */
* @param string function ini_bytes(string $ini): int {
* @return int
*/
function ini_bytes($ini) {
$val = ini_get($ini); $val = ini_get($ini);
switch (strtolower(substr($val, -1))) { switch (strtolower(substr($val, -1))) {
case 'g': case 'g':
@@ -587,22 +536,21 @@ function ini_bytes($ini) {
} }
/** Create link to database documentation /** Create link to database documentation
* @param array JUSH => $path * @param string[] $paths JUSH => $path
* @param string HTML code * @param string $text HTML code
* @return string HTML code * @return string HTML code
*/ */
function doc_link($paths, $text = "<sup>?</sup>") { function doc_link(array $paths, string $text = "<sup>?</sup>"): string {
global $connection; $server_info = connection()->server_info;
$server_info = $connection->server_info;
$version = preg_replace('~^(\d\.?\d).*~s', '\1', $server_info); // two most significant digits $version = preg_replace('~^(\d\.?\d).*~s', '\1', $server_info); // two most significant digits
$urls = array( $urls = array(
'sql' => "https://dev.mysql.com/doc/refman/$version/en/", 'sql' => "https://dev.mysql.com/doc/refman/$version/en/",
'sqlite' => "https://www.sqlite.org/", 'sqlite' => "https://www.sqlite.org/",
'pgsql' => "https://www.postgresql.org/docs/" . ($connection->flavor == 'cockroach' ? "current" : $version) . "/", 'pgsql' => "https://www.postgresql.org/docs/" . (connection()->flavor == 'cockroach' ? "current" : $version) . "/",
'mssql' => "https://learn.microsoft.com/en-us/sql/", '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=", 'oracle' => "https://www.oracle.com/pls/topic/lookup?ctx=db" . preg_replace('~^.* (\d+)\.(\d+)\.\d+\.\d+\.\d+.*~s', '\1\2', $server_info) . "&id=",
); );
if ($connection->flavor == 'maria') { if (connection()->flavor == 'maria') {
$urls['sql'] = "https://mariadb.com/kb/en/"; $urls['sql'] = "https://mariadb.com/kb/en/";
$paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql'])); $paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql']));
} }
@@ -610,12 +558,10 @@ function doc_link($paths, $text = "<sup>?</sup>") {
} }
/** Compute size of database /** Compute size of database
* @param string
* @return string formatted * @return string formatted
*/ */
function db_size($db) { function db_size(string $db): string {
global $connection; if (!connection()->select_db($db)) {
if (!$connection->select_db($db)) {
return "?"; return "?";
} }
$return = 0; $return = 0;
@@ -625,15 +571,11 @@ function db_size($db) {
return format_number($return); return format_number($return);
} }
/** Print SET NAMES if utf8mb4 might be needed /** Print SET NAMES if utf8mb4 might be needed */
* @param string function set_utf8mb4(string $create): void {
* @return null
*/
function set_utf8mb4($create) {
global $connection;
static $set = false; static $set = false;
if (!$set && preg_match('~\butf8mb4~i', $create)) { // possible false positive if (!$set && preg_match('~\butf8mb4~i', $create)) { // possible false positive
$set = true; $set = true;
echo "SET NAMES " . charset($connection) . ";\n\n"; echo "SET NAMES " . charset(connection()) . ";\n\n";
} }
} }

View File

@@ -3,8 +3,7 @@ namespace Adminer;
error_reporting(24575); // all but E_DEPRECATED (overriding mysqli methods without types is deprecated) error_reporting(24575); // all but E_DEPRECATED (overriding mysqli methods without types is deprecated)
set_error_handler(function ($errno, $errstr) { set_error_handler(function ($errno, $errstr) {
// "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 array key" mutes $_GET["q"] if there's no ?q=
// "Undefined offset" and "Undefined index" are older messages for the same thing // "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); return !!preg_match('~^Undefined (array key|offset|index)~', $errstr);
}, E_WARNING | E_NOTICE); // warning since PHP 8.0 }, E_WARNING | E_NOTICE); // warning since PHP 8.0

File diff suppressed because it is too large Load Diff

View File

@@ -1,82 +1,55 @@
<?php <?php
namespace Adminer; namespace Adminer;
/** Return <script> element /** Return <script> element */
* @param string function script(string $source, string $trailing = "\n"): string {
* @param string
* @return string
*/
function script($source, $trailing = "\n") {
return "<script" . nonce() . ">$source</script>$trailing"; return "<script" . nonce() . ">$source</script>$trailing";
} }
/** Return <script src> element /** Return <script src> element */
* @param string function script_src(string $url, bool $defer = false): string {
* @return string return "<script src='" . h($url) . "'" . nonce() . ($defer ? " defer" : "") . "></script>\n";
*/
function script_src($url) {
return "<script src='" . h($url) . "'" . nonce() . "></script>\n";
} }
/** Get a nonce="" attribute with CSP nonce /** Get a nonce="" attribute with CSP nonce */
* @return string function nonce(): string {
*/
function nonce() {
return ' nonce="' . get_nonce() . '"'; return ' nonce="' . get_nonce() . '"';
} }
/** Get <input type="hidden"> /** Get <input type="hidden">
* @param string * @param string|int $value
* @param string
* @return string HTML * @return string HTML
*/ */
function input_hidden($name, $value = "") { function input_hidden(string $name, $value = ""): string {
return "<input type='hidden' name='" . h($name) . "' value='" . h($value) . "'>\n"; return "<input type='hidden' name='" . h($name) . "' value='" . h($value) . "'>\n";
} }
/** Get <input type="hidden" name="token"> /** Get CSRF <input type="hidden" name="token">
* @param string token to use instead of global $token
* @return string HTML * @return string HTML
*/ */
function input_token($special = "") { function input_token(): string {
global $token; return input_hidden("token", get_token());
return input_hidden("token", ($special ?: $token));
} }
/** Get a target="_blank" attribute /** Get a target="_blank" attribute */
* @return string function target_blank(): string {
*/
function target_blank() {
return ' target="_blank" rel="noreferrer noopener"'; return ' target="_blank" rel="noreferrer noopener"';
} }
/** Escape for HTML /** Escape for HTML */
* @param string function h(?string $string): string {
* @return string
*/
function h($string) {
return str_replace("\0", "&#0;", htmlspecialchars($string, ENT_QUOTES, 'utf-8')); return str_replace("\0", "&#0;", htmlspecialchars($string, ENT_QUOTES, 'utf-8'));
} }
/** Convert \n to <br> /** Convert \n to <br> */
* @param string function nl_br(string $string): string {
* @return string
*/
function nl_br($string) {
return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3 return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3
} }
/** Generate HTML checkbox /** Generate HTML checkbox
* @param string * @param string|int $value
* @param string
* @param bool
* @param string
* @param string
* @param string
* @param string
* @return string
*/ */
function checkbox($name, $value, $checked, $label = "", $onclick = "", $class = "", $labelled_by = "") { function checkbox(string $name, $value, ?bool $checked, string $label = "", string $onclick = "", string $class = "", string $labelled_by = ""): string {
$return = "<input type='checkbox' name='$name' value='" . h($value) . "'" $return = "<input type='checkbox' name='$name' value='" . h($value) . "'"
. ($checked ? " checked" : "") . ($checked ? " checked" : "")
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "") . ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
@@ -87,12 +60,11 @@ function checkbox($name, $value, $checked, $label = "", $onclick = "", $class =
} }
/** Generate list of HTML options /** Generate list of HTML options
* @param array array of strings or arrays (creates optgroup) * @param string[]|string[][] $options array of strings or arrays (creates optgroup)
* @param mixed * @param mixed $selected
* @param bool always use array keys for value="", otherwise only string keys are used * @param bool $use_keys always use array keys for value="", otherwise only string keys are used
* @return string
*/ */
function optionlist($options, $selected = null, $use_keys = false) { function optionlist($options, $selected = null, bool $use_keys = false): string {
$return = ""; $return = "";
foreach ($options as $k => $v) { foreach ($options as $k => $v) {
$opts = array($k => $v); $opts = array($k => $v);
@@ -115,51 +87,44 @@ function optionlist($options, $selected = null, $use_keys = false) {
} }
/** Generate HTML <select> /** Generate HTML <select>
* @param string * @param string[] $options
* @param array
* @param string
* @param string
* @param string
* @return string
*/ */
function html_select($name, $options, $value = "", $onchange = "", $labelled_by = "") { function html_select(string $name, array $options, ?string $value = "", string $onchange = "", string $labelled_by = ""): string {
static $label = 0;
$label_option = "";
if (!$labelled_by && substr($options[""], 0, 1) == "(") {
$label++;
$labelled_by = "label-$label";
$label_option = "<option value='' id='$labelled_by'>" . h($options[""]);
unset($options[""]);
}
return "<select name='" . h($name) . "'" return "<select name='" . h($name) . "'"
. ($labelled_by ? " aria-labelledby='$labelled_by'" : "") . ($labelled_by ? " aria-labelledby='$labelled_by'" : "")
. ">" . optionlist($options, $value) . "</select>" . ">" . $label_option . optionlist($options, $value) . "</select>"
. ($onchange ? script("qsl('select').onchange = function () { $onchange };", "") : "") . ($onchange ? script("qsl('select').onchange = function () { $onchange };", "") : "")
; ;
} }
/** Generate HTML radio list /** Generate HTML radio list
* @param string * @param string[] $options
* @param array
* @param string
* @return string
*/ */
function html_radios($name, $options, $value = "") { function html_radios(string $name, array $options, ?string $value = "", string $separator = ""): string {
$return = ""; $return = "";
foreach ($options as $key => $val) { foreach ($options as $key => $val) {
$return .= "<label><input type='radio' name='" . h($name) . "' value='" . h($key) . "'" . ($key == $value ? " checked" : "") . ">" . h($val) . "</label>"; $return .= "<label><input type='radio' name='" . h($name) . "' value='" . h($key) . "'" . ($key == $value ? " checked" : "") . ">" . h($val) . "</label>$separator";
} }
return $return; return $return;
} }
/** Get onclick confirmation /** Get onclick confirmation */
* @param string function confirm(string $message = "", string $selector = "qsl('input')"): string {
* @param string
* @return string
*/
function confirm($message = "", $selector = "qsl('input')") {
return script("$selector.onclick = () => confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "');", ""); return script("$selector.onclick = () => confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "');", "");
} }
/** Print header for hidden fieldset (close by </div></fieldset>) /** Print header for hidden fieldset (close by </div></fieldset>)
* @param string * @param bool $visible
* @param string
* @param bool
* @return null
*/ */
function print_fieldset($id, $legend, $visible = false) { function print_fieldset(string $id, string $legend, $visible = false): void {
echo "<fieldset><legend>"; echo "<fieldset><legend>";
echo "<a href='#fieldset-$id'>$legend</a>"; echo "<a href='#fieldset-$id'>$legend</a>";
echo script("qsl('a').onclick = partial(toggle, 'fieldset-$id');", ""); echo script("qsl('a').onclick = partial(toggle, 'fieldset-$id');", "");
@@ -167,29 +132,18 @@ function print_fieldset($id, $legend, $visible = false) {
echo "<div id='fieldset-$id'" . ($visible ? "" : " class='hidden'") . ">\n"; echo "<div id='fieldset-$id'" . ($visible ? "" : " class='hidden'") . ">\n";
} }
/** Return class='active' if $bold is true /** Return class='active' if $bold is true */
* @param bool function bold(bool $bold, string $class = ""): string {
* @param string
* @return string
*/
function bold($bold, $class = "") {
return ($bold ? " class='active $class'" : ($class ? " class='$class'" : "")); return ($bold ? " class='active $class'" : ($class ? " class='$class'" : ""));
} }
/** Escape string for JavaScript apostrophes /** Escape string for JavaScript apostrophes */
* @param string function js_escape(string $string): string {
* @return string
*/
function js_escape($string) {
return addcslashes($string, "\r\n'\\/"); // slash for <script> return addcslashes($string, "\r\n'\\/"); // slash for <script>
} }
/** Generate page number for pagination /** Generate page number for pagination */
* @param int function pagination(int $page, ?int $current): string {
* @param int
* @return string
*/
function pagination($page, $current) {
return " " . ($page == $current return " " . ($page == $current
? $page + 1 ? $page + 1
: '<a href="' . h(remove_from_uri("page") . ($page ? "&page=$page" . ($_GET["next"] ? "&next=" . urlencode($_GET["next"]) : "") : "")) . '">' . ($page + 1) . "</a>" : '<a href="' . h(remove_from_uri("page") . ($page ? "&page=$page" . ($_GET["next"] ? "&next=" . urlencode($_GET["next"]) : "") : "")) . '">' . ($page + 1) . "</a>"
@@ -197,12 +151,10 @@ function pagination($page, $current) {
} }
/** Print hidden fields /** Print hidden fields
* @param array * @param mixed[] $process
* @param array * @param list<string> $ignore
* @param string
* @return bool
*/ */
function hidden_fields($process, $ignore = array(), $prefix = '') { function hidden_fields(array $process, array $ignore = array(), string $prefix = ''): bool {
$return = false; $return = false;
foreach ($process as $key => $val) { foreach ($process as $key => $val) {
if (!in_array($key, $ignore)) { if (!in_array($key, $ignore)) {
@@ -217,44 +169,34 @@ function hidden_fields($process, $ignore = array(), $prefix = '') {
return $return; return $return;
} }
/** Print hidden fields for GET forms /** Print hidden fields for GET forms */
* @return null function hidden_fields_get(): void {
*/
function hidden_fields_get() {
echo (sid() ? input_hidden(session_name(), session_id()) : ''); echo (sid() ? input_hidden(session_name(), session_id()) : '');
echo (SERVER !== null ? input_hidden(DRIVER, SERVER) : ""); echo (SERVER !== null ? input_hidden(DRIVER, SERVER) : "");
echo input_hidden("username", $_GET["username"]); echo input_hidden("username", $_GET["username"]);
} }
/** Print enum or set input field /** Print enum or set input field
* @param string "radio"|"checkbox" * @param 'radio'|'checkbox' $type
* @param string * @param Field $field
* @param array * @param mixed $value string|array
* @param mixed string|array
* @param string
* @return null
*/ */
function enum_input($type, $attrs, $field, $value, $empty = null) { function enum_input(string $type, string $attrs, array $field, $value, ?string $empty = null): string {
global $adminer;
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches); preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === $empty) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : ""); $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) { foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val)); $val = stripcslashes(str_replace("''", "'", $val));
$checked = (is_array($value) ? in_array($val, $value) : $value === $val); $checked = (is_array($value) ? in_array($val, $value) : $value === $val);
$return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h($adminer->editVal($val, $field)) . '</label>'; $return .= " <label><input type='$type'$attrs value='" . h($val) . "'" . ($checked ? ' checked' : '') . '>' . h(adminer()->editVal($val, $field)) . '</label>';
} }
return $return; return $return;
} }
/** Print edit input field /** Print edit input field
* @param array one field from fields() * @param Field|RoutineField $field
* @param mixed * @param mixed $value
* @param string
* @param bool
* @return null
*/ */
function input($field, $value, $function, $autofocus = false) { function input(array $field, $value, ?string $function, ?bool $autofocus = false): void {
global $driver, $adminer;
$name = h(bracket_escape($field["field"])); $name = h(bracket_escape($field["field"]));
echo "<td class='function'>"; echo "<td class='function'>";
if (is_array($value) && !$function) { if (is_array($value) && !$function) {
@@ -265,17 +207,18 @@ function input($field, $value, $function, $autofocus = false) {
if ($reset && !$_POST["save"]) { if ($reset && !$_POST["save"]) {
$function = null; $function = null;
} }
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field); $functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + adminer()->editFunctions($field);
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : ""; $disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
$attrs = " name='fields[$name]'$disabled" . ($autofocus ? " autofocus" : ""); $attrs = " name='fields[$name]'$disabled" . ($autofocus ? " autofocus" : "");
$enums = $driver->enumLength($field); $enums = driver()->enumLength($field);
if ($enums) { if ($enums) {
$field["type"] = "enum"; $field["type"] = "enum";
$field["length"] = $enums; $field["length"] = $enums;
} }
echo $driver->unconvertFunction($field) . " "; echo driver()->unconvertFunction($field) . " ";
$table = $_GET["edit"] ?: $_GET["select"];
if ($field["type"] == "enum") { if ($field["type"] == "enum") {
echo h($functions[""]) . "<td>" . $adminer->editInput($_GET["edit"], $field, $attrs, $value); echo h($functions[""]) . "<td>" . adminer()->editInput($table, $field, $attrs, $value);
} else { } else {
$has_function = (in_array($function, $functions) || isset($functions[$function])); $has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1 echo (count($functions) > 1
@@ -284,7 +227,7 @@ function input($field, $value, $function, $autofocus = false) {
. script("qsl('select').onchange = functionChange;", "") . script("qsl('select').onchange = functionChange;", "")
: h(reset($functions)) : h(reset($functions))
) . '<td>'; ) . '<td>';
$input = $adminer->editInput($_GET["edit"], $field, $attrs, $value); // usage in call is without a table $input = adminer()->editInput($table, $field, $attrs, $value); // usage in call is without a table
if ($input != "") { if ($input != "") {
echo $input; echo $input;
} elseif (preg_match('~bool~', $field["type"])) { } elseif (preg_match('~bool~', $field["type"])) {
@@ -295,7 +238,7 @@ function input($field, $value, $function, $autofocus = false) {
foreach ($matches[1] as $i => $val) { foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val)); $val = stripcslashes(str_replace("''", "'", $val));
$checked = in_array($val, explode(",", $value), true); $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>'; 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")) { } elseif (preg_match('~blob|bytea|raw|file~', $field["type"]) && ini_bool("file_uploads")) {
echo "<input type='file' name='fields-$name'>"; echo "<input type='file' name='fields-$name'>";
@@ -311,7 +254,7 @@ function input($field, $value, $function, $autofocus = false) {
echo "<textarea$attrs>" . h($value) . '</textarea>'; echo "<textarea$attrs>" . h($value) . '</textarea>';
} else { } else {
// int(3) is only a display hint // int(3) is only a display hint
$types = $driver->types(); $types = driver()->types();
$maxlength = (!preg_match('~int~', $field["type"]) && preg_match('~^(\d+)(,(\d+))?$~', $field["length"], $match) $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)) ? ((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) : ($types[$field["type"]] ? $types[$field["type"]] + ($field["unsigned"] ? 0 : 1) : 0)
@@ -327,7 +270,7 @@ function input($field, $value, $function, $autofocus = false) {
. "$attrs>" . "$attrs>"
; ;
} }
echo $adminer->editHint($_GET["edit"], $field, $value); echo adminer()->editHint($table, $field, $value);
// skip 'original' // skip 'original'
$first = 0; $first = 0;
foreach ($functions as $key => $val) { foreach ($functions as $key => $val) {
@@ -343,18 +286,17 @@ function input($field, $value, $function, $autofocus = false) {
} }
/** Process edit input field /** Process edit input field
* @param one field from fields() * @param Field|RoutineField $field
* @return string or false to leave the original value * @return mixed false to leave the original value
*/ */
function process_input($field) { function process_input(array $field) {
global $adminer, $driver;
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) { if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
return null; return;
} }
$idf = bracket_escape($field["field"]); $idf = bracket_escape($field["field"]);
$function = $_POST["function"][$idf]; $function = idx($_POST["function"], $idf);
$value = $_POST["fields"][$idf]; $value = $_POST["fields"][$idf];
if ($field["type"] == "enum" || $driver->enumLength($field)) { if ($field["type"] == "enum" || driver()->enumLength($field)) {
if ($value == -1) { if ($value == -1) {
return false; return false;
} }
@@ -387,24 +329,22 @@ function process_input($field) {
if (!is_string($file)) { if (!is_string($file)) {
return false; //! report errors return false; //! report errors
} }
return $driver->quoteBinary($file); return driver()->quoteBinary($file);
} }
return $adminer->processInput($field, $value, $function); return adminer()->processInput($field, $value, $function);
} }
/** Print results of search in all tables /** Print results of search in all tables
* @uses $_GET["where"][0] * @uses $_GET["where"][0]
* @uses $_POST["tables"] * @uses $_POST["tables"]
* @return null
*/ */
function search_tables() { function search_tables(): void {
global $adminer, $connection;
$_GET["where"][0]["val"] = $_POST["query"]; $_GET["where"][0]["val"] = $_POST["query"];
$sep = "<ul>\n"; $sep = "<ul>\n";
foreach (table_status('', true) as $table => $table_status) { foreach (table_status('', true) as $table => $table_status) {
$name = $adminer->tableName($table_status); $name = adminer()->tableName($table_status);
if (isset($table_status["Engine"]) && $name != "" && (!$_POST["tables"] || in_array($table, $_POST["tables"]))) { 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)); $result = connection()->query("SELECT" . limit("1 FROM " . table($table), " WHERE " . implode(" AND ", adminer()->selectSearchProcess(fields($table), array())), 1));
if (!$result || $result->fetch_row()) { 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>"; $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"; echo "$sep<li>" . ($result ? $print : "<p class='error'>$print: " . error()) . "\n";
@@ -416,31 +356,26 @@ function search_tables() {
} }
/** Return events to display help on mouse over /** Return events to display help on mouse over
* @param string JS expression * @param string $command JS expression
* @param bool JS expression * @param int $side 0 top, 1 left
* @return string
*/ */
function on_help($command, $side = 0) { function on_help(string $command, int $side = 0): string {
return script("mixin(qsl('select, input'), {onmouseover: function (event) { helpMouseover.call(this, event, $command, $side) }, onmouseout: helpMouseout});", ""); return script("mixin(qsl('select, input'), {onmouseover: function (event) { helpMouseover.call(this, event, $command, $side) }, onmouseout: helpMouseout});", "");
} }
/** Print edit data form /** Print edit data form
* @param string * @param Field[] $fields
* @param array * @param mixed $row
* @param mixed
* @param bool
* @return null
*/ */
function edit_form($table, $fields, $row, $update) { function edit_form(string $table, array $fields, $row, ?bool $update, string $error = ''): void {
global $adminer, $error; $table_name = adminer()->tableName(table_status1($table, true));
$table_name = $adminer->tableName(table_status1($table, true));
page_header( page_header(
($update ? lang('Edit') : lang('Insert')), ($update ? lang('Edit') : lang('Insert')),
$error, $error,
array("select" => array($table, $table_name)), array("select" => array($table, $table_name)),
$table_name $table_name
); );
$adminer->editRowPrint($table, $fields, $row, $update); adminer()->editRowPrint($table, $fields, $row, $update);
if ($row === false) { if ($row === false) {
echo "<p class='error'>" . lang('No rows.') . "\n"; echo "<p class='error'>" . lang('No rows.') . "\n";
return; return;
@@ -452,8 +387,8 @@ function edit_form($table, $fields, $row, $update) {
echo "<table class='layout'>" . script("qsl('table').onkeydown = editingKeydown;"); echo "<table class='layout'>" . script("qsl('table').onkeydown = editingKeydown;");
$autofocus = !$_POST; $autofocus = !$_POST;
foreach ($fields as $name => $field) { foreach ($fields as $name => $field) {
echo "<tr><th>" . $adminer->fieldName($field); echo "<tr><th>" . adminer()->fieldName($field);
$default = $_GET["set"][bracket_escape($name)]; $default = idx($_GET["set"], bracket_escape($name));
if ($default === null) { if ($default === null) {
$default = $field["default"]; $default = $field["default"];
if ($field["type"] == "bit" && preg_match("~^b'([01]*)'\$~", $default, $regs)) { if ($field["type"] == "bit" && preg_match("~^b'([01]*)'\$~", $default, $regs)) {
@@ -474,10 +409,10 @@ function edit_form($table, $fields, $row, $update) {
) )
); );
if (!$_POST["save"] && is_string($value)) { if (!$_POST["save"] && is_string($value)) {
$value = $adminer->editVal($value, $field); $value = adminer()->editVal($value, $field);
} }
$function = ($_POST["save"] $function = ($_POST["save"]
? (string) $_POST["function"][$name] ? idx($_POST["function"], $name, "")
: ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"]) : ($update && preg_match('~^CURRENT_TIMESTAMP~i', $field["on_update"])
? "now" ? "now"
: ($value === false ? null : ($value !== null ? '' : 'NULL')) : ($value === false ? null : ($value !== null ? '' : 'NULL'))
@@ -507,7 +442,7 @@ function edit_form($table, $fields, $row, $update) {
echo "<tr>" echo "<tr>"
. "<th><input name='field_keys[]'>" . "<th><input name='field_keys[]'>"
. script("qsl('input').oninput = fieldChange;") . script("qsl('input').oninput = fieldChange;")
. "<td class='function'>" . html_select("field_funs[]", $adminer->editFunctions(array("null" => isset($_GET["select"])))) . "<td class='function'>" . html_select("field_funs[]", adminer()->editFunctions(array("null" => isset($_GET["select"]))))
. "<td><input name='field_vals[]'>" . "<td><input name='field_vals[]'>"
. "\n" . "\n"
; ;
@@ -534,3 +469,18 @@ function edit_form($table, $fields, $row, $update) {
echo input_token(); echo input_token();
echo "</form>\n"; echo "</form>\n";
} }
/** Shorten UTF-8 string
* @return string escaped string with appended ...
*/
function shorten_utf8(string $string, int $length = 80, string $suffix = ""): string {
if (!preg_match("(^(" . repeat_pattern("[\t\r\n -\x{10FFFF}]", $length) . ")($)?)u", $string, $match)) { // ~s causes trash in $match[2] under some PHP versions, (.|\n) is slow
preg_match("(^(" . repeat_pattern("[\t\r\n -~]", $length) . ")($)?)", $string, $match);
}
return h($match[1]) . $suffix . (isset($match[2]) ? "" : "<i>…</i>");
}
/** Get button with icon */
function icon(string $icon, string $name, string $html, string $title): string {
return "<button type='submit' name='$name' title='" . h($title) . "' class='icon icon-$icon'><span>$html</span></button>";
}

View File

@@ -1,83 +1,32 @@
<?php <?php
namespace Adminer; namespace Adminer;
// not used in a single language version /** Translate string
* @param literal-string $idf
$langs = array( * @param float|string $number
'en' => 'English', // Jakub Vrána - https://www.vrana.cz
'ar' => 'العربية', // Y.M Amine - Algeria - nbr7@live.fr
'bg' => 'Български', // Deyan Delchev
'bn' => 'বাংলা', // Dipak Kumar - dipak.ndc@gmail.com, Hossain Ahmed Saiman - hossain.ahmed@altscope.com
'bs' => 'Bosanski', // Emir Kurtovic
'ca' => 'Català', // Joan Llosas
'cs' => 'Čeština', // Jakub Vrána - https://www.vrana.cz
'da' => 'Dansk', // Jarne W. Beutnagel - jarne@beutnagel.dk
'de' => 'Deutsch', // Klemens Häckel - http://clickdimension.wordpress.com
'el' => 'Ελληνικά', // Dimitrios T. Tanis - jtanis@tanisfood.gr
'es' => 'Español', // Klemens Häckel - http://clickdimension.wordpress.com
'et' => 'Eesti', // Priit Kallas
'fa' => 'فارسی', // mojtaba barghbani - Iran - mbarghbani@gmail.com, Nima Amini - http://nimlog.com
'fi' => 'Suomi', // Finnish - Kari Eveli - http://www.lexitec.fi/
'fr' => 'Français', // Francis Gagné, Aurélien Royer
'gl' => 'Galego', // Eduardo Penabad Ramos
'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/
'hu' => 'Magyar', // Borsos Szilárd (Borsosfi) - http://www.borsosfi.hu, info@borsosfi.hu
'id' => 'Bahasa Indonesia', // Ivan Lanin - http://ivan.lanin.org
'it' => 'Italiano', // Alessandro Fiorotto, Paolo Asperti
'ja' => '日本語', // Hitoshi Ozawa - http://sourceforge.jp/projects/oss-ja-jpn/releases/
'ka' => 'ქართული', // Saba Khmaladze skhmaladze@uglt.org
'ko' => '한국어', // dalli - skcha67@gmail.com
'lt' => 'Lietuvių', // Paulius Leščinskas - http://www.lescinskas.lt
'lv' => 'Latviešu', // Kristaps Lediņš - https://krysits.com
'ms' => 'Bahasa Melayu', // Pisyek
'nl' => 'Nederlands', // Maarten Balliauw - http://blog.maartenballiauw.be
'no' => 'Norsk', // Iver Odin Kvello, mupublishing.com
'pl' => 'Polski', // Radosław Kowalewski - http://srsbiz.pl/
'pt' => 'Português', // André Dias
'pt-br' => 'Português (Brazil)', // Gian Live - gian@live.com, Davi Alexandre davi@davialexandre.com.br, RobertoPC - http://www.robertopc.com.br
'ro' => 'Limba Română', // .nick .messing - dot.nick.dot.messing@gmail.com
'ru' => 'Русский', // Maksim Izmaylov; Andre Polykanine - https://github.com/Oire/
'sk' => 'Slovenčina', // Ivan Suchy - http://www.ivansuchy.com, Juraj Krivda - http://www.jstudio.cz
'sl' => 'Slovenski', // Matej Ferlan - www.itdinamik.com, matej.ferlan@itdinamik.com
'sr' => 'Српски', // Nikola Radovanović - cobisimo@gmail.com
'sv' => 'Svenska', // rasmusolle - https://github.com/rasmusolle
'ta' => 'த‌மிழ்', // G. Sampath Kumar, Chennai, India, sampathkumar11@gmail.com
'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
);
/** Get current language
* @return string
*/ */
function get_lang() { function lang(string $idf, $number = null): string {
global $LANG; $args = func_get_args();
return $LANG; // this is matched by compile.php
$args[0] = Lang::$translations[$idf] ?: $idf;
return call_user_func_array('Adminer\lang_format', $args);
} }
/** Translate string /** Format translation, usable also by plugins
* @param string * @param string|list<string> $translation
* @param int * @param float|string $number
* @return string
*/ */
// this is matched by compile.php function lang_format($translation, $number = null): string {
function lang($idf, $number = null) {
global $LANG, $translations;
$translation = ($translations[$idf] ?: $idf);
if (is_array($translation)) { if (is_array($translation)) {
// this is matched by compile.php // this is matched by compile.php
$pos = ($number == 1 ? 0 $pos = ($number == 1 ? 0
: ($LANG == 'cs' || $LANG == 'sk' ? ($number && $number < 5 ? 1 : 2) // different forms for 1, 2-4, other : (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 : (LANG == 'fr' ? (!$number ? 0 : 1) // different forms for 0-1, other
: ($LANG == 'pl' ? ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2) // different forms for 1, 2-4 except 12-14, other : (LANG == 'pl' ? ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2) // different forms for 1, 2-4 except 12-14, other
: ($LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other : (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other
: ($LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other : (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other
: ($LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0 : (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0
: (in_array($LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other : (in_array(LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other
: 1)))))))) // different forms for 1, other : 1)))))))) // different forms for 1, other
; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html ; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
$translation = $translation[$pos]; $translation = $translation[$pos];
@@ -92,27 +41,81 @@ function lang($idf, $number = null) {
return vsprintf($format, $args); return vsprintf($format, $args);
} }
function switch_lang() { // this is matched by compile.php
global $LANG, $langs; // not used in a single language version from here
/** Get available languages
* @return string[]
*/
function langs(): array {
return array(
'en' => 'English', // Jakub Vrána - https://www.vrana.cz
'ar' => 'العربية', // Y.M Amine - Algeria - nbr7@live.fr
'bg' => 'Български', // Deyan Delchev
'bn' => 'বাংলা', // Dipak Kumar - dipak.ndc@gmail.com, Hossain Ahmed Saiman - hossain.ahmed@altscope.com
'bs' => 'Bosanski', // Emir Kurtovic
'ca' => 'Català', // Joan Llosas
'cs' => 'Čeština', // Jakub Vrána - https://www.vrana.cz
'da' => 'Dansk', // Jarne W. Beutnagel - jarne@beutnagel.dk
'de' => 'Deutsch', // Klemens Häckel - http://clickdimension.wordpress.com
'el' => 'Ελληνικά', // Dimitrios T. Tanis - jtanis@tanisfood.gr
'es' => 'Español', // Klemens Häckel - http://clickdimension.wordpress.com
'et' => 'Eesti', // Priit Kallas
'fa' => 'فارسی', // mojtaba barghbani - Iran - mbarghbani@gmail.com, Nima Amini - http://nimlog.com
'fi' => 'Suomi', // Finnish - Kari Eveli - http://www.lexitec.fi/
'fr' => 'Français', // Francis Gagné, Aurélien Royer
'gl' => 'Galego', // Eduardo Penabad Ramos
'he' => 'עברית', // Binyamin Yawitz - https://stuff-group.com/
'hu' => 'Magyar', // Borsos Szilárd (Borsosfi) - http://www.borsosfi.hu, info@borsosfi.hu
'id' => 'Bahasa Indonesia', // Ivan Lanin - http://ivan.lanin.org
'it' => 'Italiano', // Alessandro Fiorotto, Paolo Asperti
'ja' => '日本語', // Hitoshi Ozawa - http://sourceforge.jp/projects/oss-ja-jpn/releases/
'ka' => 'ქართული', // Saba Khmaladze skhmaladze@uglt.org
'ko' => '한국어', // dalli - skcha67@gmail.com
'lt' => 'Lietuvių', // Paulius Leščinskas - http://www.lescinskas.lt
'lv' => 'Latviešu', // Kristaps Lediņš - https://krysits.com
'ms' => 'Bahasa Melayu', // Pisyek
'nl' => 'Nederlands', // Maarten Balliauw - http://blog.maartenballiauw.be
'no' => 'Norsk', // Iver Odin Kvello, mupublishing.com
'pl' => 'Polski', // Radosław Kowalewski - http://srsbiz.pl/
'pt' => 'Português', // André Dias
'pt-br' => 'Português (Brazil)', // Gian Live - gian@live.com, Davi Alexandre davi@davialexandre.com.br, RobertoPC - http://www.robertopc.com.br
'ro' => 'Limba Română', // .nick .messing - dot.nick.dot.messing@gmail.com
'ru' => 'Русский', // Maksim Izmaylov; Andre Polykanine - https://github.com/Oire/
'sk' => 'Slovenčina', // Ivan Suchy - http://www.ivansuchy.com, Juraj Krivda - http://www.jstudio.cz
'sl' => 'Slovenski', // Matej Ferlan - www.itdinamik.com, matej.ferlan@itdinamik.com
'sr' => 'Српски', // Nikola Radovanović - cobisimo@gmail.com
'sv' => 'Svenska', // rasmusolle - https://github.com/rasmusolle
'ta' => 'த‌மிழ்', // G. Sampath Kumar, Chennai, India, sampathkumar11@gmail.com
'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
);
}
function switch_lang(): void {
echo "<form action='' method='post'>\n<div id='lang'>"; echo "<form action='' method='post'>\n<div id='lang'>";
echo lang('Language') . ": " . html_select("lang", $langs, $LANG, "this.form.submit();"); echo "<label>" . lang('Language') . ": " . html_select("lang", langs(), LANG, "this.form.submit();") . "</label>";
echo " <input type='submit' value='" . lang('Use') . "' class='hidden'>\n"; echo " <input type='submit' value='" . lang('Use') . "' class='hidden'>\n";
echo input_token(get_token()); // $token may be empty in auth.inc.php echo input_token();
echo "</div>\n</form>\n"; echo "</div>\n</form>\n";
} }
if (isset($_POST["lang"]) && verify_token()) { // $error not yet available if (isset($_POST["lang"]) && verify_token()) { // $error not yet available
cookie("adminer_lang", $_POST["lang"]); cookie("adminer_lang", $_POST["lang"]);
$_SESSION["lang"] = $_POST["lang"]; // cookies may be disabled $_SESSION["lang"] = $_POST["lang"]; // cookies may be disabled
$_SESSION["translations"] = array(); // used in compiled version
redirect(remove_from_uri()); redirect(remove_from_uri());
} }
$LANG = "en"; $LANG = "en";
if (isset($langs[$_COOKIE["adminer_lang"]])) { if (idx(langs(), $_COOKIE["adminer_lang"])) {
cookie("adminer_lang", $_COOKIE["adminer_lang"]); cookie("adminer_lang", $_COOKIE["adminer_lang"]);
$LANG = $_COOKIE["adminer_lang"]; $LANG = $_COOKIE["adminer_lang"];
} elseif (isset($langs[$_SESSION["lang"]])) { } elseif (idx(langs(), $_SESSION["lang"])) {
$LANG = $_SESSION["lang"]; $LANG = $_SESSION["lang"];
} else { } else {
$accept_language = array(); $accept_language = array();
@@ -122,14 +125,20 @@ if (isset($langs[$_COOKIE["adminer_lang"]])) {
} }
arsort($accept_language); arsort($accept_language);
foreach ($accept_language as $key => $q) { foreach ($accept_language as $key => $q) {
if (isset($langs[$key])) { if (idx(langs(), $key)) {
$LANG = $key; $LANG = $key;
break; break;
} }
$key = preg_replace('~-.*~', '', $key); $key = preg_replace('~-.*~', '', $key);
if (!isset($accept_language[$key]) && isset($langs[$key])) { if (!isset($accept_language[$key]) && idx(langs(), $key)) {
$LANG = $key; $LANG = $key;
break; break;
} }
} }
} }
define('Adminer\LANG', $LANG);
class Lang {
/** @var array<literal-string, string|list<string>> */ static array $translations;
}

View File

@@ -3,29 +3,31 @@ namespace Adminer;
// PDO can be used in several database drivers // PDO can be used in several database drivers
if (extension_loaded('pdo')) { if (extension_loaded('pdo')) {
abstract class PdoDb { abstract class PdoDb extends SqlDb {
public $flavor = '', $server_info, $affected_rows, $errno, $error; protected \PDO $pdo;
protected $pdo;
private $result;
function dsn($dsn, $username, $password, $options = array()) { /** Connect to server using DSN
* @param mixed[] $options
* @return string error message
*/
function dsn(string $dsn, string $username, string $password, array $options = array()): string {
$options[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT; $options[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT;
$options[\PDO::ATTR_STATEMENT_CLASS] = array('Adminer\PdoDbStatement'); $options[\PDO::ATTR_STATEMENT_CLASS] = array('Adminer\PdoResult');
try { try {
$this->pdo = new \PDO($dsn, $username, $password, $options); $this->pdo = new \PDO($dsn, $username, $password, $options);
} catch (\Exception $ex) { } catch (\Exception $ex) {
auth_error(h($ex->getMessage())); return $ex->getMessage();
} }
$this->server_info = @$this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION); $this->server_info = @$this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION);
return '';
} }
abstract function select_db($database); function quote(string $string): string {
function quote($string) {
return $this->pdo->quote($string); return $this->pdo->quote($string);
} }
function query($query, $unbuffered = false) { function query(string $query, bool $unbuffered = false) {
/** @var Result|bool */
$result = $this->pdo->query($query); $result = $this->pdo->query($query);
$this->error = ""; $this->error = "";
if (!$result) { if (!$result) {
@@ -39,13 +41,9 @@ if (extension_loaded('pdo')) {
return $result; return $result;
} }
function multi_query($query) {
return $this->result = $this->query($query);
}
function store_result($result = null) { function store_result($result = null) {
if (!$result) { if (!$result) {
$result = $this->result; $result = $this->multi;
if (!$result) { if (!$result) {
return false; return false;
} }
@@ -58,40 +56,38 @@ if (extension_loaded('pdo')) {
return true; return true;
} }
function next_result() { function next_result(): bool {
if (!$this->result) { /** @var PdoResult|bool */
$result = $this->multi;
if (!is_object($result)) {
return false; return false;
} }
$this->result->_offset = 0; $result->_offset = 0;
return @$this->result->nextRowset(); // @ - PDO_PgSQL doesn't support it return @$result->nextRowset(); // @ - PDO_PgSQL doesn't support it
}
function result($query, $field = 0) {
$result = $this->query($query);
if (!$result) {
return false;
}
$row = $result->fetch();
return $row ? $row[$field] : false;
} }
} }
class PdoDbStatement extends \PDOStatement { class PdoResult extends \PDOStatement {
public $_offset = 0, $num_rows; public $_offset = 0, $num_rows;
function fetch_assoc() { function fetch_assoc() {
return $this->fetch(\PDO::FETCH_ASSOC); return $this->fetch_array(\PDO::FETCH_ASSOC);
} }
function fetch_row() { function fetch_row() {
return $this->fetch(\PDO::FETCH_NUM); return $this->fetch_array(\PDO::FETCH_NUM);
} }
function fetch_column($field) { private function fetch_array(int $mode) {
return $this->fetchColumn($field); $return = $this->fetch($mode);
return ($return ? array_map(array($this, 'unresource'), $return) : $return);
} }
function fetch_field() { private function unresource($val) {
return (is_resource($val) ? stream_get_contents($val) : $val);
}
function fetch_field(): \stdClass {
$row = (object) $this->getColumnMeta($this->_offset++); $row = (object) $this->getColumnMeta($this->_offset++);
$type = $row->pdo_type; $type = $row->pdo_type;
$row->type = ($type == \PDO::PARAM_INT ? 0 : 15); $row->type = ($type == \PDO::PARAM_INT ? 0 : 15);

View File

@@ -0,0 +1,31 @@
<?php
namespace Adminer;
// the overridable methods don't use return type declarations so that plugins can be compatible with PHP 5
abstract class Plugin {
/** @var array<literal-string, string|list<string>>[] */ protected $translations = array(); // key is language code
/** Get plain text plugin description; empty string means to use the first line of class doc-comment
* @return string
*/
function description() {
return $this->lang('');
}
/** Get URL of plugin screenshot
* @return string
*/
function screenshot() {
return "";
}
/** Translate a string from $this->translations; Adminer\lang() doesn't work for single language versions
* @param literal-string $idf
* @param float|string $number
*/
protected function lang(string $idf, $number = null): string {
$args = func_get_args();
$args[0] = idx($this->translations[LANG], $idf) ?: $idf;
return call_user_func_array('Adminer\lang_format', $args);
}
}

View File

@@ -1,13 +1,17 @@
<?php <?php
namespace Adminer; namespace Adminer;
class Plugins extends Adminer { class Plugins {
public $plugins; ///< @var protected(set) array /** @var true[] */ private static array $append = array('dumpFormat' => true, 'dumpOutput' => true, 'editRowPrint' => true, 'editFunctions' => true, 'config' => true); // these hooks expect the value to be appended to the result
/** @var list<object> @visibility protected(set) */ public array $plugins;
/** @visibility protected(set) */ public string $error = ''; // HTML
/** @var list<object>[] */ private array $hooks = array();
/** Register plugins /** Register plugins
* @param array object instances or null to autoload plugins from adminer-plugins/ * @param ?list<object> $plugins object instances or null to autoload plugins from adminer-plugins/
*/ */
function __construct($plugins) { function __construct(?array $plugins) {
if ($plugins === null) { if ($plugins === null) {
$plugins = array(); $plugins = array();
$basename = "adminer-plugins"; $basename = "adminer-plugins";
@@ -29,6 +33,7 @@ class Plugins extends Adminer {
} }
foreach (get_declared_classes() as $class) { foreach (get_declared_classes() as $class) {
if (!$plugins[$class] && preg_match('~^Adminer\w~i', $class)) { if (!$plugins[$class] && preg_match('~^Adminer\w~i', $class)) {
// we need to use reflection because PHP 7.1 throws ArgumentCountError for missing arguments but older versions issue a warning
$reflection = new \ReflectionClass($class); $reflection = new \ReflectionClass($class);
$constructor = $reflection->getConstructor(); $constructor = $reflection->getConstructor();
if ($constructor && $constructor->getNumberOfRequiredParameters()) { if ($constructor && $constructor->getNumberOfRequiredParameters()) {
@@ -40,388 +45,41 @@ class Plugins extends Adminer {
} }
} }
$this->plugins = $plugins; $this->plugins = $plugins;
$adminer = new Adminer;
$plugins[] = $adminer;
$reflection = new \ReflectionObject($adminer);
foreach ($reflection->getMethods() as $method) {
foreach ($plugins as $plugin) {
$name = $method->getName();
if (method_exists($plugin, $name)) {
$this->hooks[$name][] = $plugin;
}
}
}
} }
private function callParent($function, $args) { /**
return call_user_func_array(array('parent', $function), $args); * @param literal-string $name
} * @param mixed[] $params
* @return mixed
private function applyPlugin($function, $params) { */
function __call(string $name, array $params) {
$args = array(); $args = array();
foreach ($params as $key => $val) { foreach ($params as $key => $val) {
// some plugins accept params by reference - we don't need to propage it outside, just to the other plugins // some plugins accept params by reference - we don't need to propage it outside, just to the other plugins
$args[] = &$params[$key]; $args[] = &$params[$key];
} }
foreach ($this->plugins as $plugin) { $return = null;
if (method_exists($plugin, $function)) { foreach ($this->hooks[$name] as $plugin) {
$return = call_user_func_array(array($plugin, $function), $args); $value = call_user_func_array(array($plugin, $name), $args);
if ($return !== null) { if ($value !== null) {
return $return; if (!self::$append[$name]) { // non-null value from non-appending method short-circuits the other plugins
} return $value;
}
}
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 = $value + (array) $return;
} }
} }
return $return; 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

@@ -2,18 +2,19 @@
namespace Adminer; namespace Adminer;
class TmpFile { class TmpFile {
private $handler, $size; /** @var resource */ private $handler;
/** @visibility protected(set) */ public int $size;
function __construct() { function __construct() {
$this->handler = tmpfile(); $this->handler = tmpfile();
} }
function write($contents) { function write(string $contents): void {
$this->size += strlen($contents); $this->size += strlen($contents);
fwrite($this->handler, $contents); fwrite($this->handler, $contents);
} }
function send() { function send(): void {
fseek($this->handler, 0); fseek($this->handler, 0);
fpassthru($this->handler); fpassthru($this->handler);
fclose($this->handler); fclose($this->handler);

View File

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

View File

@@ -6,7 +6,7 @@ namespace Adminer;
* @link http://www.coolcode.cn/?action=show&id=128 * @link http://www.coolcode.cn/?action=show&id=128
*/ */
function int32($n) { function int32(int $n): int {
while ($n >= 2147483648) { while ($n >= 2147483648) {
$n -= 4294967296; $n -= 4294967296;
} }
@@ -16,7 +16,10 @@ function int32($n) {
return (int) $n; return (int) $n;
} }
function long2str($v, $w) { /**
* @param int[] $v
*/
function long2str(array $v, bool $w): string {
$s = ''; $s = '';
foreach ($v as $val) { foreach ($v as $val) {
$s .= pack('V', $val); $s .= pack('V', $val);
@@ -27,7 +30,10 @@ function long2str($v, $w) {
return $s; return $s;
} }
function str2long($s, $w) { /**
* @return int[]
*/
function str2long(string $s, bool $w): array {
$v = array_values(unpack('V*', str_pad($s, 4 * ceil(strlen($s) / 4), "\0"))); $v = array_values(unpack('V*', str_pad($s, 4 * ceil(strlen($s) / 4), "\0")));
if ($w) { if ($w) {
$v[] = strlen($s); $v[] = strlen($s);
@@ -35,16 +41,15 @@ function str2long($s, $w) {
return $v; return $v;
} }
function xxtea_mx($z, $y, $sum, $k) { function xxtea_mx(int $z, int $y, int $sum, int $k): int {
return int32((($z >> 5 & 0x7FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k ^ $z)); return int32((($z >> 5 & 0x7FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k ^ $z));
} }
/** Cipher /** Cipher
* @param string plain-text password * @param string $str plain-text password
* @param string
* @return string binary cipher * @return string binary cipher
*/ */
function encrypt_string($str, $key) { function encrypt_string(string $str, string $key): string {
if ($str == "") { if ($str == "") {
return ""; return "";
} }
@@ -73,11 +78,10 @@ function encrypt_string($str, $key) {
} }
/** Decipher /** Decipher
* @param string binary cipher * @param string $str binary cipher
* @param string * @return string|false plain-text password
* @return string plain-text password
*/ */
function decrypt_string($str, $key) { function decrypt_string(string $str, string $key) {
if ($str == "") { if ($str == "") {
return ""; return "";
} }

View File

@@ -3,7 +3,7 @@ namespace Adminer;
$TABLE = $_GET["indexes"]; $TABLE = $_GET["indexes"];
$index_types = array("PRIMARY", "UNIQUE", "INDEX"); $index_types = array("PRIMARY", "UNIQUE", "INDEX");
$table_status = table_status($TABLE, true); $table_status = table_status1($TABLE, true);
if (preg_match('~MyISAM|M?aria' . (min_version(5.6, '10.0.5') ? '|InnoDB' : '') . '~i', $table_status["Engine"])) { if (preg_match('~MyISAM|M?aria' . (min_version(5.6, '10.0.5') ? '|InnoDB' : '') . '~i', $table_status["Engine"])) {
$index_types[] = "FULLTEXT"; $index_types[] = "FULLTEXT";
} }
@@ -33,8 +33,8 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
ksort($index["columns"]); ksort($index["columns"]);
foreach ($index["columns"] as $key => $column) { foreach ($index["columns"] as $key => $column) {
if ($column != "") { if ($column != "") {
$length = $index["lengths"][$key]; $length = idx($index["lengths"], $key);
$desc = $index["descs"][$key]; $desc = idx($index["descs"], $key);
$set[] = idf_escape($column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : ""); $set[] = idf_escape($column) . ($length ? "(" . (+$length) . ")" : "") . ($desc ? " DESC" : "");
$columns[] = $column; $columns[] = $column;
$lengths[] = ($length ?: null); $lengths[] = ($length ?: null);
@@ -106,13 +106,13 @@ $show_options = ($_POST ? $_POST["options"] : get_setting("index_options"));
<thead><tr> <thead><tr>
<th id="label-type"><?php echo lang('Index Type'); ?> <th id="label-type"><?php echo lang('Index Type'); ?>
<th><input type="submit" class="wayoff"><?php <th><input type="submit" class="wayoff"><?php
echo lang('Column') . ($lengths ? "<span class='idxopts" . ($show_options ? "" : " hidden") . "'> (" . lang('length') . ")</span>" : ""); echo lang('Columns') . ($lengths ? "<span class='idxopts" . ($show_options ? "" : " hidden") . "'> (" . lang('length') . ")</span>" : "");
if ($lengths || support("descidx")) { if ($lengths || support("descidx")) {
echo checkbox("options", 1, $show_options, lang('Options'), "indexOptionsShow(this.checked)", "jsonly") . "\n"; echo checkbox("options", 1, $show_options, lang('Options'), "indexOptionsShow(this.checked)", "jsonly") . "\n";
} }
?> ?>
<th id="label-name"><?php echo lang('Name'); ?> <th id="label-name"><?php echo lang('Name'); ?>
<th><noscript><?php echo "<input type='image' class='icon' name='add[0]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>"; ?></noscript> <th><noscript><?php echo icon("plus", "add[0]", "+", lang('Add next')); ?></noscript>
</thead> </thead>
<?php <?php
if ($primary) { if ($primary) {
@@ -139,14 +139,14 @@ foreach ($row["indexes"] as $index) {
"partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')" "partial(" . ($i == count($index["columns"]) ? "indexesAddColumn" : "indexesChangeColumn") . ", '" . js_escape(JUSH == "sql" ? "" : $_GET["indexes"] . "_") . "')"
); );
echo "<span class='idxopts" . ($show_options ? "" : " hidden") . "'>"; echo "<span class='idxopts" . ($show_options ? "" : " hidden") . "'>";
echo ($lengths ? "<input type='number' name='indexes[$j][lengths][$i]' class='size' value='" . h($index["lengths"][$key]) . "' title='" . lang('Length') . "'>" : ""); echo ($lengths ? "<input type='number' name='indexes[$j][lengths][$i]' class='size' value='" . h(idx($index["lengths"], $key)) . "' title='" . lang('Length') . "'>" : "");
echo (support("descidx") ? checkbox("indexes[$j][descs][$i]", 1, $index["descs"][$key], lang('descending')) : ""); echo (support("descidx") ? checkbox("indexes[$j][descs][$i]", 1, idx($index["descs"], $key), lang('descending')) : "");
echo "</span> </span>"; echo "</span> </span>";
$i++; $i++;
} }
echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n"; echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n";
echo "<td><input type='image' class='icon' name='drop_col[$j]' src='../adminer/static/cross.gif' alt='x' title='" . lang('Remove') . "'>" . script("qsl('input').onclick = partial(editingRemoveRow, 'indexes\$1[type]');"); echo "<td>" . icon("cross", "drop_col[$j]", "x", lang('Remove')) . script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");
} }
$j++; $j++;
} }

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'تسجيل الدخول', 'Login' => 'تسجيل الدخول',
'Logout successful.' => 'تم تسجيل الخروج بنجاح.', 'Logout successful.' => 'تم تسجيل الخروج بنجاح.',
'Invalid credentials.' => 'بيانات الدخول غير صالحة.', 'Invalid credentials.' => 'بيانات الدخول غير صالحة.',
@@ -212,11 +212,6 @@ $translations = array(
'Binary' => 'ثنائية', 'Binary' => 'ثنائية',
'Lists' => 'قوائم', 'Lists' => 'قوائم',
'Editor' => 'المحرر', 'Editor' => 'المحرر',
'E-mail' => 'البريد الإلكتروني',
'From' => 'من',
'Subject' => 'الموضوع',
'Send' => 'إرسال',
'%d e-mail(s) have been sent.' => 'تم إرسال %d رسالة.',
'Webserver file %s' => 'ملف %s من خادم الويب', 'Webserver file %s' => 'ملف %s من خادم الويب',
'File does not exist.' => 'الملف غير موجود.', 'File does not exist.' => 'الملف غير موجود.',
'%d in total' => '%d في المجموع', '%d in total' => '%d في المجموع',
@@ -251,7 +246,6 @@ $translations = array(
'Network' => 'شبكة', 'Network' => 'شبكة',
'Geometry' => 'هندسة', 'Geometry' => 'هندسة',
'File exists.' => 'الملف موجود.', 'File exists.' => 'الملف موجود.',
'Attachments' => 'ملفات مرفقة',
'Item%s has been inserted.' => '%sتم إدراج العنصر.', 'Item%s has been inserted.' => '%sتم إدراج العنصر.',
'now' => 'الآن', 'now' => 'الآن',
'%d query(s) executed OK.' => array('تم تنفيذ الاستعلام %d بنجاح.', 'تم تنفيذ الاستعلامات %d بنجاح.'), '%d query(s) executed OK.' => array('تم تنفيذ الاستعلام %d بنجاح.', 'تم تنفيذ الاستعلامات %d بنجاح.'),

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Система', 'System' => 'Система',
'Server' => 'Сървър', 'Server' => 'Сървър',
@@ -279,13 +279,6 @@ $translations = array(
'Delete' => 'Изтриване', 'Delete' => 'Изтриване',
'You have no privileges to update this table.' => 'Нямате праве за обновяване на таблицата.', 'You have no privileges to update this table.' => 'Нямате праве за обновяване на таблицата.',
'E-mail' => 'E-mail',
'From' => 'От',
'Subject' => 'Тема',
'Attachments' => 'Прикачени',
'Send' => 'Изпращане',
'%d e-mail(s) have been sent.' => array('%d писмо беше изпратено.', '%d писма бяха изпратени.'),
// data type descriptions // data type descriptions
'Numbers' => 'Числа', 'Numbers' => 'Числа',
'Date and time' => 'Дата и час', 'Date and time' => 'Дата и час',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'লগইন', 'Login' => 'লগইন',
'Logout successful.' => 'সফলভাবে লগআউট হয়েছে।', 'Logout successful.' => 'সফলভাবে লগআউট হয়েছে।',
'Invalid credentials.' => 'ভুল পাসওয়ার্ড।', 'Invalid credentials.' => 'ভুল পাসওয়ার্ড।',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'বাইনারি', 'Binary' => 'বাইনারি',
'Lists' => 'তালিকা', 'Lists' => 'তালিকা',
'Editor' => 'সম্পাদক', 'Editor' => 'সম্পাদক',
'E-mail' => '​​ই-মেইল',
'From' => 'থেকে',
'Subject' => 'বিষয়',
'Send' => 'পাঠান',
'%d e-mail(s) have been sent.' => array('%d ইমেইল(গুলি) পাঠানো হয়েছে।', '%d ইমেইল(গুলি) পাঠানো হয়েছে।'),
'Webserver file %s' => 'ওয়েবসার্ভার ফাইল %s', 'Webserver file %s' => 'ওয়েবসার্ভার ফাইল %s',
'File does not exist.' => 'ফাইলটির কোন অস্তিত্ব নেই।', 'File does not exist.' => 'ফাইলটির কোন অস্তিত্ব নেই।',
'%d in total' => 'সর্বমোটঃ %d টি', '%d in total' => 'সর্বমোটঃ %d টি',
@@ -252,7 +247,6 @@ $translations = array(
'Network' => 'নেটওয়ার্ক', 'Network' => 'নেটওয়ার্ক',
'Geometry' => 'জ্যামিতি', 'Geometry' => 'জ্যামিতি',
'File exists.' => 'ফাইল রয়েছে।', 'File exists.' => 'ফাইল রয়েছে।',
'Attachments' => 'সংযুক্তিগুলো',
'%d query(s) executed OK.' => array('SQL-অনুসন্ধান সফলভাবে সম্পন্ন হয়েছে।', '%d SQL-অনুসন্ধানসমূহ সফলভাবে সম্পন্ন হয়েছে।'), '%d query(s) executed OK.' => array('SQL-অনুসন্ধান সফলভাবে সম্পন্ন হয়েছে।', '%d SQL-অনুসন্ধানসমূহ সফলভাবে সম্পন্ন হয়েছে।'),
'Show only errors' => 'শুধুমাত্র ত্রুটিগুলো দেখান', 'Show only errors' => 'শুধুমাত্র ত্রুটিগুলো দেখান',
'Refresh' => 'রিফ্রেশ', 'Refresh' => 'রিফ্রেশ',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistem', 'System' => 'Sistem',
'Server' => 'Server', 'Server' => 'Server',
@@ -264,13 +264,6 @@ $translations = array(
'Delete' => 'Izbriši', 'Delete' => 'Izbriši',
'Modify' => 'Izmjene', 'Modify' => 'Izmjene',
'E-mail' => 'El. pošta',
'From' => 'Od',
'Subject' => 'Naslov',
'Attachments' => 'Prilozi',
'Send' => 'Pošalji',
'%d e-mail(s) have been sent.' => array('%d poruka el. pošte je poslata.', '%d poruke el. pošte su poslate.', '%d poruka el. pošte je poslato.'),
// data type descriptions // data type descriptions
'Numbers' => 'Broj', 'Numbers' => 'Broj',
'Date and time' => 'Datum i vrijeme', 'Date and time' => 'Datum i vrijeme',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Inicia la sessió', 'Login' => 'Inicia la sessió',
'Logout successful.' => 'Desconnexió correcta.', 'Logout successful.' => 'Desconnexió correcta.',
'Invalid credentials.' => 'Credencials invàlides.', 'Invalid credentials.' => 'Credencials invàlides.',
@@ -206,11 +206,6 @@ $translations = array(
'History' => 'Història', 'History' => 'Història',
'Variables' => 'Variables', 'Variables' => 'Variables',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Les columnes d\'origen i de destinació han de ser del mateix tipus, la columna de destinació ha d\'estar indexada i les dades referenciades han d\'existir.', 'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Les columnes d\'origen i de destinació han de ser del mateix tipus, la columna de destinació ha d\'estar indexada i les dades referenciades han d\'existir.',
'E-mail' => 'Correu electrònic',
'From' => 'De',
'Subject' => 'Assumpte',
'Send' => 'Envia',
'%d e-mail(s) have been sent.' => array('S\'ha enviat %d correu electrònic.', 'S\'han enviat %d correus electrònics.'),
'Run file' => 'Executa el fitxer', 'Run file' => 'Executa el fitxer',
'Numbers' => 'Nombres', 'Numbers' => 'Nombres',
'Date and time' => 'Data i hora', 'Date and time' => 'Data i hora',
@@ -225,7 +220,6 @@ $translations = array(
'File does not exist.' => 'El fitxer no existeix.', 'File does not exist.' => 'El fitxer no existeix.',
'Permanent login' => 'Sessió permanent', 'Permanent login' => 'Sessió permanent',
'%d in total' => '%d en total', '%d in total' => '%d en total',
'Attachments' => 'Adjuncions',
'System' => 'Sistema', 'System' => 'Sistema',
'last' => 'darrera', 'last' => 'darrera',
'Network' => 'Xarxa', 'Network' => 'Xarxa',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Systém', 'System' => 'Systém',
'Server' => 'Server', 'Server' => 'Server',
@@ -13,9 +13,6 @@ $translations = array(
'Logged as: %s' => 'Přihlášen jako: %s', 'Logged as: %s' => 'Přihlášen jako: %s',
'Logout successful.' => 'Odhlášení proběhlo v pořádku.', 'Logout successful.' => 'Odhlášení proběhlo v pořádku.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Díky za použití Admineru, <a href="https://www.adminer.org/cs/donation/">přispějte</a> na vývoj.', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Díky za použití Admineru, <a href="https://www.adminer.org/cs/donation/">přispějte</a> na vývoj.',
'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.', 'Invalid credentials.' => 'Neplatné přihlašovací údaje.',
'There is a space in the input password which might be the cause.' => 'Problém může být, že je v zadaném hesle mezera.', 'There is a space in the input password which might be the cause.' => 'Problém může být, že je v zadaném hesle mezera.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje přístup k databázi bez hesla, <a href="https://www.adminer.org/cs/password/"%s>více informací</a>.', 'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje přístup k databázi bez hesla, <a href="https://www.adminer.org/cs/password/"%s>více informací</a>.',
@@ -45,6 +42,7 @@ $translations = array(
'User has been created.' => 'Uživatel byl vytvořen.', 'User has been created.' => 'Uživatel byl vytvořen.',
'Hashed' => 'Zahašované', 'Hashed' => 'Zahašované',
'Column' => 'Sloupec', 'Column' => 'Sloupec',
'Columns' => 'Sloupce',
'Routine' => 'Procedura', 'Routine' => 'Procedura',
'Grant' => 'Povolit', 'Grant' => 'Povolit',
'Revoke' => 'Zakázat', 'Revoke' => 'Zakázat',
@@ -295,13 +293,6 @@ $translations = array(
'Delete' => 'Smazat', 'Delete' => 'Smazat',
'You have no privileges to update this table.' => 'Nemáte oprávnění editovat tuto tabulku.', 'You have no privileges to update this table.' => 'Nemáte oprávnění editovat tuto tabulku.',
'E-mail' => 'E-mail',
'From' => 'Odesílatel',
'Subject' => 'Předmět',
'Attachments' => 'Přílohy',
'Send' => 'Odeslat',
'%d e-mail(s) have been sent.' => array('Byl odeslán %d e-mail.', 'Byly odeslány %d e-maily.', 'Bylo odesláno %d e-mailů.'),
// data type descriptions // data type descriptions
'Numbers' => 'Čísla', 'Numbers' => 'Čísla',
'Date and time' => 'Datum a čas', 'Date and time' => 'Datum a čas',
@@ -358,6 +349,11 @@ $translations = array(
'Check has been created.' => 'Kontrola byla vytvořena.', 'Check has been created.' => 'Kontrola byla vytvořena.',
'Check has been altered.' => 'Kontrola byla změněna.', 'Check has been altered.' => 'Kontrola byla změněna.',
'Check has been dropped.' => 'Kontrola byla odstraněna.', 'Check has been dropped.' => 'Kontrola byla odstraněna.',
'Loaded plugins' => 'Nahrané pluginy',
'%s must <a%s>return an array</a>.' => '%s musí <a%s>vracet pole</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Nakonfigurujte</a> %s v %s.',
'screenshot' => 'obrázek',
); );
// run `php ../../lang.php cs` to update this file // run `php ../../lang.php cs` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'System' => 'System', 'System' => 'System',
'Server' => 'Server', 'Server' => 'Server',
'Username' => 'Brugernavn', 'Username' => 'Brugernavn',
@@ -238,12 +238,6 @@ $translations = array(
'Clone' => 'Klon', 'Clone' => 'Klon',
'Delete' => 'Slet', 'Delete' => 'Slet',
'You have no privileges to update this table.' => 'Du mangler rettigheder til at ændre denne tabellen.', 'You have no privileges to update this table.' => 'Du mangler rettigheder til at ændre denne tabellen.',
'E-mail' => 'E-mail',
'From' => 'Fra',
'Subject' => 'Titel',
'Attachments' => 'Vedhæft',
'Send' => 'Send',
'%d e-mail(s) have been sent.' => array('%d email sendt.', '%d emails sendt.'),
'Numbers' => 'Nummer', 'Numbers' => 'Nummer',
'Date and time' => 'Dato og tid', 'Date and time' => 'Dato og tid',
'Strings' => 'Strenge', 'Strings' => 'Strenge',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Login', 'Login' => 'Login',
'Logout successful.' => 'Abmeldung erfolgreich.', 'Logout successful.' => 'Abmeldung erfolgreich.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Danke, dass Sie Adminer genutzt haben. <a href="https://www.adminer.org/de/donation/">Spenden willkommen!</a>.', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Danke, dass Sie Adminer genutzt haben. <a href="https://www.adminer.org/de/donation/">Spenden willkommen!</a>.',
@@ -137,6 +137,7 @@ $translations = array(
'User has been created.' => 'Benutzer wurde erstellt.', 'User has been created.' => 'Benutzer wurde erstellt.',
'Hashed' => 'Hashed', 'Hashed' => 'Hashed',
'Column' => 'Spalte', 'Column' => 'Spalte',
'Columns' => 'Spalten',
'Routine' => 'Routine', 'Routine' => 'Routine',
'Grant' => 'Erlauben', 'Grant' => 'Erlauben',
'Revoke' => 'Widerrufen', 'Revoke' => 'Widerrufen',
@@ -215,11 +216,6 @@ $translations = array(
'Binary' => 'Binär', 'Binary' => 'Binär',
'Lists' => 'Listen', 'Lists' => 'Listen',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'E-Mail',
'From' => 'Von',
'Subject' => 'Betreff',
'Send' => 'Abschicken',
'%d e-mail(s) have been sent.' => array('%d E-Mail abgeschickt.', '%d E-Mails abgeschickt.'),
'Webserver file %s' => 'Webserver Datei %s', 'Webserver file %s' => 'Webserver Datei %s',
'File does not exist.' => 'Datei existiert nicht.', 'File does not exist.' => 'Datei existiert nicht.',
'%d in total' => '%d insgesamt', '%d in total' => '%d insgesamt',
@@ -254,7 +250,6 @@ $translations = array(
'Network' => 'Netzwerk', 'Network' => 'Netzwerk',
'Geometry' => 'Geometrie', 'Geometry' => 'Geometrie',
'File exists.' => 'Datei existiert schon.', 'File exists.' => 'Datei existiert schon.',
'Attachments' => 'Anhänge',
'%d query(s) executed OK.' => array('SQL-Abfrage erfolgreich ausgeführt.', '%d SQL-Abfragen erfolgreich ausgeführt.'), '%d query(s) executed OK.' => array('SQL-Abfrage erfolgreich ausgeführt.', '%d SQL-Abfragen erfolgreich ausgeführt.'),
'Show only errors' => 'Nur Fehler anzeigen', 'Show only errors' => 'Nur Fehler anzeigen',
'Refresh' => 'Aktualisieren', 'Refresh' => 'Aktualisieren',
@@ -294,7 +289,7 @@ $translations = array(
'DB' => 'DB', 'DB' => 'DB',
'ATTACH queries are not supported.' => 'ATTACH Abfragen werden nicht unterstützt.', 'ATTACH queries are not supported.' => 'ATTACH Abfragen werden nicht unterstützt.',
'Warnings' => 'Warnungen', 'Warnings' => 'Warnungen',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer unterstützt den Zugriff auf eine Datenbank ohne Passwort nicht, <a href="https://www.adminer.org/en/password/"%s>mehr Informationen</a>.', 'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer unterstützt den Zugriff auf eine Datenbank ohne Passwort nicht, <a href="https://www.adminer.org/de/password/"%s>mehr Informationen</a>.',
'Full table scan' => 'Vollständige Überprüfung der Tabelle', 'Full table scan' => 'Vollständige Überprüfung der Tabelle',
'The action will be performed after successful login with the same credentials.' => 'Die Aktion wird nach erfolgreicher Anmeldung mit denselben Anmeldedaten ausgeführt.', 'The action will be performed after successful login with the same credentials.' => 'Die Aktion wird nach erfolgreicher Anmeldung mit denselben Anmeldedaten ausgeführt.',
'Connecting to privileged ports is not allowed.' => 'Die Verbindung zu privilegierten Ports ist nicht erlaubt.', 'Connecting to privileged ports is not allowed.' => 'Die Verbindung zu privilegierten Ports ist nicht erlaubt.',
@@ -308,6 +303,10 @@ $translations = array(
'Alter check' => 'Check ändern', 'Alter check' => 'Check ändern',
'Create check' => 'Check erstellen', 'Create check' => 'Check erstellen',
'Checks' => 'Checks', 'Checks' => 'Checks',
'Loaded plugins' => 'Geladene Plugins',
'%s must <a%s>return an array</a>.' => '%s muss <a%s>ein Array zurückgeben</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Konfigure</a> %s mit %s.',
'screenshot' => 'Screenshot',
); );
// run `php ../../lang.php de` to update this file // run `php ../../lang.php de` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Σύστημα', 'System' => 'Σύστημα',
'Server' => 'Διακομιστής', 'Server' => 'Διακομιστής',
@@ -279,13 +279,6 @@ $translations = array(
'Delete' => 'Διαγραφή', 'Delete' => 'Διαγραφή',
'You have no privileges to update this table.' => 'Δεν έχετε δικαίωμα να τροποποιήσετε αυτό τον πίνακα.', 'You have no privileges to update this table.' => 'Δεν έχετε δικαίωμα να τροποποιήσετε αυτό τον πίνακα.',
'E-mail' => 'E-mail',
'From' => 'Από',
'Subject' => 'Θέμα',
'Attachments' => 'Συνημμένα',
'Send' => 'Αποστολή',
'%d e-mail(s) have been sent.' => array('%d e-mail απεστάλη.', '%d e-mail απεστάλησαν.'),
// data type descriptions // data type descriptions
'Numbers' => 'Αριθμοί', 'Numbers' => 'Αριθμοί',
'Date and time' => 'Ημερομηνία και ώρα', 'Date and time' => 'Ημερομηνία και ώρα',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Too many unsuccessful logins, try again in %d minute(s).' => array('Too many unsuccessful logins, try again in %d minute.', 'Too many unsuccessful logins, try again in %d minutes.'), 'Too many unsuccessful logins, try again in %d minute(s).' => array('Too many unsuccessful logins, try again in %d minute.', 'Too many unsuccessful logins, try again in %d minutes.'),
'Query executed OK, %d row(s) affected.' => array('Query executed OK, %d row affected.', 'Query executed OK, %d rows affected.'), 'Query executed OK, %d row(s) affected.' => array('Query executed OK, %d row affected.', 'Query executed OK, %d rows affected.'),
'%d byte(s)' => array('%d byte', '%d bytes'), '%d byte(s)' => array('%d byte', '%d bytes'),
@@ -11,7 +11,6 @@ $translations = array(
'%d row(s)' => array('%d row', '%d rows'), '%d row(s)' => array('%d row', '%d rows'),
'%d item(s) have been affected.' => array('%d item has been affected.', '%d items have been affected.'), '%d item(s) have been affected.' => array('%d item has been affected.', '%d items have been affected.'),
'%d row(s) have been imported.' => array('%d row has been imported.', '%d rows have been imported.'), '%d row(s) have been imported.' => array('%d row has been imported.', '%d rows have been imported.'),
'%d e-mail(s) have been sent.' => array('%d e-mail has been sent.', '%d e-mails have been sent.'),
'%d in total' => '%d in total', '%d in total' => '%d in total',
'%d query(s) executed OK.' => array('%d query executed OK.', '%d queries executed OK.'), '%d query(s) executed OK.' => array('%d query executed OK.', '%d queries executed OK.'),
); );

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Login', 'Login' => 'Login',
'Logout successful.' => 'Sesión finalizada con éxito.', 'Logout successful.' => 'Sesión finalizada con éxito.',
'Invalid credentials.' => 'Usuario y/o clave de acceso incorrecta.', 'Invalid credentials.' => 'Usuario y/o clave de acceso incorrecta.',
@@ -222,11 +222,6 @@ $translations = array(
'Binary' => 'Binario', 'Binary' => 'Binario',
'Lists' => 'Listas', 'Lists' => 'Listas',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'Email',
'From' => 'De',
'Subject' => 'Asunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Webserver file %s' => 'Archivo de servidor web %s', 'Webserver file %s' => 'Archivo de servidor web %s',
'File does not exist.' => 'Ese archivo no existe.', 'File does not exist.' => 'Ese archivo no existe.',
'%d in total' => '%d en total', '%d in total' => '%d en total',
@@ -261,7 +256,6 @@ $translations = array(
'Network' => 'Red', 'Network' => 'Red',
'Geometry' => 'Geometría', 'Geometry' => 'Geometría',
'File exists.' => 'Ese archivo ya existe.', 'File exists.' => 'Ese archivo ya existe.',
'Attachments' => 'Adjuntos',
'%d query(s) executed OK.' => array('%d sentencia SQL ejecutada correctamente.', '%d sentencias SQL ejecutadas correctamente.'), '%d query(s) executed OK.' => array('%d sentencia SQL ejecutada correctamente.', '%d sentencias SQL ejecutadas correctamente.'),
'Show only errors' => 'Mostrar solamente errores', 'Show only errors' => 'Mostrar solamente errores',
'Refresh' => 'Actualizar', 'Refresh' => 'Actualizar',
@@ -276,8 +270,8 @@ $translations = array(
'HH:MM:SS' => 'HH:MM:SS', 'HH:MM:SS' => 'HH:MM:SS',
'Loaded plugins' => 'Plugins cargados', 'Loaded plugins' => 'Plugins cargados',
// '<b>%s</b> must return an array.' => '<b>%s</b> tiene que retornar un arreglo.', '%s must <a%s>return an array</a>.' => '%s tiene que <a%s>retornar un arreglo</a>.',
// 'Configure <b>%s</b> in <b>%s</b>.' => 'Configurar <b>%s</b> en <b>%s</b>.', '<a%s>Configure</a> %s in %s.' => '<a%s>Configurar</a> %s en %s.',
'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.', '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>.', '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.', 'Database does not support password.' => 'La base de datos no soporta password.',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Logi sisse', 'Login' => 'Logi sisse',
'Logout successful.' => 'Väljalogimine õnnestus.', 'Logout successful.' => 'Väljalogimine õnnestus.',
'Invalid credentials.' => 'Ebakorrektsed andmed.', 'Invalid credentials.' => 'Ebakorrektsed andmed.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binaar', 'Binary' => 'Binaar',
'Lists' => 'Listid', 'Lists' => 'Listid',
'Editor' => 'Redaktor', 'Editor' => 'Redaktor',
'E-mail' => 'E-post',
'From' => 'Kellelt',
'Subject' => 'Pealkiri',
'Send' => 'Saada',
'%d e-mail(s) have been sent.' => 'Saadetud kirju: %d.',
'Webserver file %s' => 'Fail serveris: %s', 'Webserver file %s' => 'Fail serveris: %s',
'File does not exist.' => 'Faili ei leitud.', 'File does not exist.' => 'Faili ei leitud.',
'%d in total' => 'Kokku: %d', '%d in total' => 'Kokku: %d',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Võrk (network)', 'Network' => 'Võrk (network)',
'Geometry' => 'Geomeetria', 'Geometry' => 'Geomeetria',
'File exists.' => 'Fail juba eksisteerib.', 'File exists.' => 'Fail juba eksisteerib.',
'Attachments' => 'Manused',
'%d query(s) executed OK.' => array('%d päring edukalt käivitatud.', '%d päringut edukalt käivitatud.'), '%d query(s) executed OK.' => array('%d päring edukalt käivitatud.', '%d päringut edukalt käivitatud.'),
'Show only errors' => 'Kuva vaid veateateid', 'Show only errors' => 'Kuva vaid veateateid',
'Refresh' => 'Uuenda', 'Refresh' => 'Uuenda',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'سیستم', 'System' => 'سیستم',
'Server' => 'سرور', 'Server' => 'سرور',
@@ -277,13 +277,6 @@ $translations = array(
'Delete' => 'حذف', 'Delete' => 'حذف',
'You have no privileges to update this table.' => 'شما اختیار ویرایش این جدول را ندارید.', 'You have no privileges to update this table.' => 'شما اختیار ویرایش این جدول را ندارید.',
'E-mail' => 'پست الکترونیک',
'From' => 'فرستنده',
'Subject' => 'موضوع',
'Attachments' => 'پیوست ها',
'Send' => 'ارسال',
'%d e-mail(s) have been sent.' => array('%d ایمیل ارسال شد.', '%d ایمیل ارسال شد.'),
// data type descriptions // data type descriptions
'Numbers' => 'اعداد', 'Numbers' => 'اعداد',
'Date and time' => 'تاریخ و زمان', 'Date and time' => 'تاریخ و زمان',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Järjestelmä', 'System' => 'Järjestelmä',
'Server' => 'Palvelin', 'Server' => 'Palvelin',
@@ -279,13 +279,6 @@ $translations = array(
'Delete' => 'Poista', 'Delete' => 'Poista',
'You have no privileges to update this table.' => 'Sinulla ei ole oikeutta päivittää tätä taulua.', 'You have no privileges to update this table.' => 'Sinulla ei ole oikeutta päivittää tätä taulua.',
'E-mail' => 'S-posti',
'From' => 'Lähettäjä',
'Subject' => 'Aihe',
'Attachments' => 'Liitteet',
'Send' => 'Lähetä',
'%d e-mail(s) have been sent.' => array('% sähköpostiviestiä lähetetty.', '% sähköpostiviestiä lähetetty.'),
// data type descriptions // data type descriptions
'Numbers' => 'Numerot', 'Numbers' => 'Numerot',
'Date and time' => 'Päiväys ja aika', 'Date and time' => 'Päiväys ja aika',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Authentification', 'Login' => 'Authentification',
'Logout successful.' => 'Au revoir !', 'Logout successful.' => 'Au revoir !',
'Invalid credentials.' => 'Authentification échouée.', 'Invalid credentials.' => 'Authentification échouée.',
@@ -212,11 +212,6 @@ $translations = array(
'Binary' => 'Binaires', 'Binary' => 'Binaires',
'Lists' => 'Listes', 'Lists' => 'Listes',
'Editor' => 'Éditeur', 'Editor' => 'Éditeur',
'E-mail' => 'Courriel',
'From' => 'De',
'Subject' => 'Sujet',
'Send' => 'Envoyer',
'%d e-mail(s) have been sent.' => array('%d message a été envoyé.', '%d messages ont été envoyés.'),
'Webserver file %s' => 'Fichier %s du serveur Web', 'Webserver file %s' => 'Fichier %s du serveur Web',
'File does not exist.' => 'Le fichier est introuvable.', 'File does not exist.' => 'Le fichier est introuvable.',
'%d in total' => '%d au total', '%d in total' => '%d au total',
@@ -252,7 +247,6 @@ $translations = array(
'Network' => 'Réseau', 'Network' => 'Réseau',
'Geometry' => 'Géométrie', 'Geometry' => 'Géométrie',
'File exists.' => 'Le fichier existe.', 'File exists.' => 'Le fichier existe.',
'Attachments' => 'Pièces jointes',
'Item%s has been inserted.' => 'L\'élément%s a été inséré.', 'Item%s has been inserted.' => 'L\'élément%s a été inséré.',
'now' => 'maintenant', 'now' => 'maintenant',
'%d query(s) executed OK.' => array('%d requête exécutée avec succès.', '%d requêtes exécutées avec succès.'), '%d query(s) executed OK.' => array('%d requête exécutée avec succès.', '%d requêtes exécutées avec succès.'),

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Conectar', 'Login' => 'Conectar',
'Logout successful.' => 'Pechouse a sesión con éxito.', 'Logout successful.' => 'Pechouse a sesión con éxito.',
'Invalid credentials.' => 'Credenciais (usuario e/ou contrasinal) inválidos.', 'Invalid credentials.' => 'Credenciais (usuario e/ou contrasinal) inválidos.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binario', 'Binary' => 'Binario',
'Lists' => 'Listas', 'Lists' => 'Listas',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'Email',
'From' => 'De',
'Subject' => 'Asunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Webserver file %s' => 'Ficheiro de servidor web %s', 'Webserver file %s' => 'Ficheiro de servidor web %s',
'File does not exist.' => 'O ficheiro non existe.', 'File does not exist.' => 'O ficheiro non existe.',
'%d in total' => '%d en total', '%d in total' => '%d en total',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Rede', 'Network' => 'Rede',
'Geometry' => 'Xeometría', 'Geometry' => 'Xeometría',
'File exists.' => 'O ficheiro xa existe.', 'File exists.' => 'O ficheiro xa existe.',
'Attachments' => 'Adxuntos',
'%d query(s) executed OK.' => array('%d consulta executada correctamente.', '%d consultas executadas correctamente.'), '%d query(s) executed OK.' => array('%d consulta executada correctamente.', '%d consultas executadas correctamente.'),
'Show only errors' => 'Amosar só erros', 'Show only errors' => 'Amosar só erros',
'Refresh' => 'Refrescar', 'Refresh' => 'Refrescar',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'התחברות', 'Login' => 'התחברות',
'Logout successful.' => 'ההתחברות הצליחה', 'Logout successful.' => 'ההתחברות הצליחה',
'Invalid credentials.' => 'פרטי התחברות שגויים', 'Invalid credentials.' => 'פרטי התחברות שגויים',
@@ -212,11 +212,6 @@ $translations = array(
'Binary' => 'בינארי', 'Binary' => 'בינארי',
'Lists' => 'רשימות', 'Lists' => 'רשימות',
'Editor' => 'עורך', 'Editor' => 'עורך',
'E-mail' => 'דוא"ל',
'From' => 'מ:',
'Subject' => 'נושא',
'Send' => 'שלח',
'%d e-mail(s) have been sent.' => '%d הודעות דוא"ל נשלחו',
'Webserver file %s' => 'קובץ השרת %s', 'Webserver file %s' => 'קובץ השרת %s',
'File does not exist.' => 'הקובץ אינו קיים', 'File does not exist.' => 'הקובץ אינו קיים',
'%d in total' => '%d בסך הכל', '%d in total' => '%d בסך הכל',
@@ -251,7 +246,6 @@ $translations = array(
'Network' => 'רשת', 'Network' => 'רשת',
'Geometry' => 'גיאומטריה', 'Geometry' => 'גיאומטריה',
'File exists.' => 'קובץ קיים', 'File exists.' => 'קובץ קיים',
'Attachments' => 'קבצים מצורפים',
'Item%s has been inserted.' => 'הפריט %s הוזן בהצלחה', 'Item%s has been inserted.' => 'הפריט %s הוזן בהצלחה',
'now' => 'כעת', 'now' => 'כעת',
'%d query(s) executed OK.' => '%d שאילתות בוצעו בהצלחה', '%d query(s) executed OK.' => '%d שאילתות בוצעו בהצלחה',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Belépés', 'Login' => 'Belépés',
'Logout successful.' => 'Sikeres kilépés.', 'Logout successful.' => 'Sikeres kilépés.',
'Invalid credentials.' => 'Érvénytelen adatok.', 'Invalid credentials.' => 'Érvénytelen adatok.',
@@ -206,11 +206,6 @@ $translations = array(
'History' => 'Történet', 'History' => 'Történet',
'Variables' => 'Változók', 'Variables' => 'Változók',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'A forrás és cél oszlopoknak azonos típusúak legyenek, a cél oszlopok indexeltek legyenek, és a hivatkozott adatnak léteznie kell.', 'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'A forrás és cél oszlopoknak azonos típusúak legyenek, a cél oszlopok indexeltek legyenek, és a hivatkozott adatnak léteznie kell.',
'E-mail' => 'E-mail',
'From' => 'Feladó',
'Subject' => 'Tárgy',
'Send' => 'Küldés',
'%d e-mail(s) have been sent.' => array('%d e-mail elküldve.', '%d e-mail elküldve.', '%d e-mail elküldve.'),
'Run file' => 'Fájl futtatása', 'Run file' => 'Fájl futtatása',
'Numbers' => 'Szám', 'Numbers' => 'Szám',
'Date and time' => 'Dátum és idő', 'Date and time' => 'Dátum és idő',
@@ -225,7 +220,6 @@ $translations = array(
'File does not exist.' => 'A fájl nem létezik.', 'File does not exist.' => 'A fájl nem létezik.',
'Permanent login' => 'Emlékezz rám', 'Permanent login' => 'Emlékezz rám',
'%d in total' => 'összesen %d', '%d in total' => 'összesen %d',
'Attachments' => 'Csatolmány',
'System' => 'Adatbázis', 'System' => 'Adatbázis',
'last' => 'utolsó', 'last' => 'utolsó',
'Network' => 'Hálózat', 'Network' => 'Hálózat',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistem', 'System' => 'Sistem',
'Server' => 'Server', 'Server' => 'Server',
@@ -259,13 +259,6 @@ $translations = array(
'Clone' => 'Gandakan', 'Clone' => 'Gandakan',
'Delete' => 'Hapus', 'Delete' => 'Hapus',
'E-mail' => 'Surel',
'From' => 'Dari',
'Subject' => 'Judul',
'Attachments' => 'Lampiran',
'Send' => 'Kirim',
'%d e-mail(s) have been sent.' => '%d surel berhasil dikirim.',
// data type descriptions // data type descriptions
'Numbers' => 'Angka', 'Numbers' => 'Angka',
'Date and time' => 'Tanggal dan waktu', 'Date and time' => 'Tanggal dan waktu',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Autenticazione', 'Login' => 'Autenticazione',
'Logout successful.' => 'Uscita effettuata con successo.', 'Logout successful.' => 'Uscita effettuata con successo.',
'Invalid credentials.' => 'Credenziali non valide.', 'Invalid credentials.' => 'Credenziali non valide.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binari', 'Binary' => 'Binari',
'Lists' => 'Liste', 'Lists' => 'Liste',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'E-mail',
'From' => 'Da',
'Subject' => 'Oggetto',
'Send' => 'Invia',
'%d e-mail(s) have been sent.' => array('%d e-mail inviata.', '%d e-mail inviate.'),
'Webserver file %s' => 'Webserver file %s', 'Webserver file %s' => 'Webserver file %s',
'File does not exist.' => 'Il file non esiste.', 'File does not exist.' => 'Il file non esiste.',
'%d in total' => '%d in totale', '%d in total' => '%d in totale',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Rete', 'Network' => 'Rete',
'Geometry' => 'Geometria', 'Geometry' => 'Geometria',
'File exists.' => 'Il file esiste già.', 'File exists.' => 'Il file esiste già.',
'Attachments' => 'Allegati',
'%d query(s) executed OK.' => array('%d query eseguita con successo.', '%d query eseguite con successo.'), '%d query(s) executed OK.' => array('%d query eseguita con successo.', '%d query eseguite con successo.'),
'Show only errors' => 'Mostra solo gli errori', 'Show only errors' => 'Mostra solo gli errori',
'Refresh' => 'Aggiorna', 'Refresh' => 'Aggiorna',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'ログイン', 'Login' => 'ログイン',
'Logout successful.' => 'ログアウトしました。', 'Logout successful.' => 'ログアウトしました。',
'Invalid credentials.' => '不正なログインです。', 'Invalid credentials.' => '不正なログインです。',
@@ -10,6 +10,8 @@ $translations = array(
'Password' => 'パスワード', 'Password' => 'パスワード',
'Loaded plugins' => '読込済プラグイン', 'Loaded plugins' => '読込済プラグイン',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminerのご利用ありがとうございました。(寄付は<a href="https://www.adminer.org/en/donation/">こちら</a>)', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Adminerのご利用ありがとうございました。(寄付は<a href="https://www.adminer.org/en/donation/">こちら</a>)',
'%s must <a%s>return an array</a>.' => '%s は<a%s>配列を返す</a>必要があります。',
'<a%s>Configure</a> %s in %s.' => '%2$s の %1$s を<a%s>設定</a>してください。',
'There is a space in the input password which might be the cause.' => '入力されたパスワードに空白が含まれているので、それが原因かもしれません。', '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>)', '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.' => 'データベースがパスワードに対応していません。', 'Database does not support password.' => 'データベースがパスワードに対応していません。',
@@ -105,8 +107,8 @@ $translations = array(
'Foreign key has been dropped.' => '外部キーを削除しました。', 'Foreign key has been dropped.' => '外部キーを削除しました。',
'Foreign key has been altered.' => '外部キーを変更しました。', 'Foreign key has been altered.' => '外部キーを変更しました。',
'Foreign key has been created.' => '外部キーを作成しました。', 'Foreign key has been created.' => '外部キーを作成しました。',
'Foreign key' => '外キー', 'Foreign key' => '外キー',
'Target table' => 'テーブル', 'Target table' => '対象テーブル',
'Change' => '変更', 'Change' => '変更',
'Source' => 'ソース', 'Source' => 'ソース',
'Target' => 'ターゲット', 'Target' => 'ターゲット',
@@ -156,6 +158,7 @@ $translations = array(
'User has been created.' => 'ユーザを作成しました。', 'User has been created.' => 'ユーザを作成しました。',
'Hashed' => 'Hashed', 'Hashed' => 'Hashed',
'Column' => '列', 'Column' => '列',
'Columns' => '列',
'Routine' => 'ルーチン', 'Routine' => 'ルーチン',
'Grant' => '権限の付与', 'Grant' => '権限の付与',
'Revoke' => '権限の取消し', 'Revoke' => '権限の取消し',
@@ -226,8 +229,8 @@ $translations = array(
'Stop on error' => 'エラーの場合は停止', 'Stop on error' => 'エラーの場合は停止',
'Select data' => 'データ', 'Select data' => 'データ',
'%.3f s' => '%.3f 秒', '%.3f s' => '%.3f 秒',
'$1-$3-$5' => '$1.$3.$5', '$1-$3-$5' => '$1/$3/$5',
'[yyyy]-mm-dd' => '[yyyy].mm.dd', '[yyyy]-mm-dd' => '[yyyy]/mm/dd',
'History' => '履歴', 'History' => '履歴',
'Variables' => '変数', 'Variables' => '変数',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットの列は同じデータ型でなければなりません。ターゲット列に索引があり、データが存在しなければなりません。', 'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'ソースとターゲットの列は同じデータ型でなければなりません。ターゲット列に索引があり、データが存在しなければなりません。',
@@ -241,11 +244,6 @@ $translations = array(
'Binary' => 'バイナリ', 'Binary' => 'バイナリ',
'Lists' => 'リスト', 'Lists' => 'リスト',
'Editor' => 'エディタ', 'Editor' => 'エディタ',
'E-mail' => 'メール',
'From' => '差出人',
'Subject' => '題名',
'Send' => '送信',
'%d e-mail(s) have been sent.' => '%d メールを送信しました。',
'Webserver file %s' => 'Webサーバファイル %s', 'Webserver file %s' => 'Webサーバファイル %s',
'File does not exist.' => 'ファイルは存在しません。', 'File does not exist.' => 'ファイルは存在しません。',
'%d in total' => '合計 %d', '%d in total' => '合計 %d',
@@ -278,7 +276,6 @@ $translations = array(
'Network' => 'ネットワーク型', 'Network' => 'ネットワーク型',
'Geometry' => 'ジオメトリ型', 'Geometry' => 'ジオメトリ型',
'File exists.' => 'ファイルが既に存在します。', 'File exists.' => 'ファイルが既に存在します。',
'Attachments' => '添付ファイル',
'Item%s has been inserted.' => '%s項目を挿入しました。', 'Item%s has been inserted.' => '%s項目を挿入しました。',
'now' => '現在の日時', 'now' => '現在の日時',
'%d query(s) executed OK.' => '%d クエリーを実行しました。', '%d query(s) executed OK.' => '%d クエリーを実行しました。',
@@ -311,6 +308,8 @@ $translations = array(
'Check has been created.' => 'チェックを作成しました。', 'Check has been created.' => 'チェックを作成しました。',
'Check has been altered.' => 'チェックを変更しました。', 'Check has been altered.' => 'チェックを変更しました。',
'Check has been dropped.' => 'チェックを削除しました。', 'Check has been dropped.' => 'チェックを削除しました。',
'screenshot' => 'スクリーンショット',
); );
// run `php ../../lang.php ja` to update this file // run `php ../../lang.php ja` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'შესვლა', 'Login' => 'შესვლა',
'Logout successful.' => 'გამოხვედით სისტემიდან.', 'Logout successful.' => 'გამოხვედით სისტემიდან.',
'Invalid credentials.' => 'არასწორი მომხმარებელი ან პაროლი.', 'Invalid credentials.' => 'არასწორი მომხმარებელი ან პაროლი.',
@@ -210,11 +210,6 @@ $translations = array(
'Binary' => 'ორობითი', 'Binary' => 'ორობითი',
'Lists' => 'სია', 'Lists' => 'სია',
'Editor' => 'რედაქტორი', 'Editor' => 'რედაქტორი',
'E-mail' => 'ელ. ფოსტა',
'From' => 'ავტორი:',
'Subject' => 'თემა',
'Send' => 'გაგზავნა',
'%d e-mail(s) have been sent.' => 'გაიგზავნა %d წერილი.',
'Webserver file %s' => 'ფაილი %s ვებსერვერზე', 'Webserver file %s' => 'ფაილი %s ვებსერვერზე',
'File does not exist.' => 'ასეთი ფაილი არ არსებობს.', 'File does not exist.' => 'ასეთი ფაილი არ არსებობს.',
'%d in total' => 'სულ %d', '%d in total' => 'სულ %d',
@@ -249,7 +244,6 @@ $translations = array(
'Network' => 'ქსელი', 'Network' => 'ქსელი',
'Geometry' => 'გეომეტრია', 'Geometry' => 'გეომეტრია',
'File exists.' => 'ფაილი უკვე არსებობს.', 'File exists.' => 'ფაილი უკვე არსებობს.',
'Attachments' => 'მიმაგრებული ფაილები',
'%d query(s) executed OK.' => '%d მოთხოვნა შესრულდა.', '%d query(s) executed OK.' => '%d მოთხოვნა შესრულდა.',
'Show only errors' => 'მხოლოდ შეცდომები', 'Show only errors' => 'მხოლოდ შეცდომები',
'Refresh' => 'განახლება', 'Refresh' => 'განახლება',

View File

@@ -1,11 +1,10 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'$1-$3-$5' => '$1-$3-$5', '$1-$3-$5' => '$1-$3-$5',
'%.3f s' => '%.3f 초', '%.3f s' => '%.3f 초',
'%d byte(s)' => '%d 바이트', '%d byte(s)' => '%d 바이트',
'%d e-mail(s) have been sent.' => '%d개 메일을 보냈습니다.',
'%d in total' => '총 %d개', '%d in total' => '총 %d개',
'%d item(s) have been affected.' => '%d개 항목을 갱신했습니다.', '%d item(s) have been affected.' => '%d개 항목을 갱신했습니다.',
'%d process(es) have been killed.' => '%d개 프로세스를 강제 종료하였습니다.', '%d process(es) have been killed.' => '%d개 프로세스를 강제 종료하였습니다.',
@@ -38,7 +37,6 @@ $translations = array(
'anywhere' => '모든', 'anywhere' => '모든',
'Are you sure?' => '실행 하시겠습니까?', 'Are you sure?' => '실행 하시겠습니까?',
'At given time' => '지정 시간', 'At given time' => '지정 시간',
'Attachments' => '첨부 파일',
'Auto Increment' => '자동 증가', 'Auto Increment' => '자동 증가',
'Binary' => '이진', 'Binary' => '이진',
'Call' => '호출', 'Call' => '호출',
@@ -80,7 +78,6 @@ $translations = array(
'Delete' => '삭제', 'Delete' => '삭제',
'descending' => '역순', 'descending' => '역순',
'Drop' => '삭제', 'Drop' => '삭제',
'E-mail' => '메일',
'Edit all' => '모두 편집', 'Edit all' => '모두 편집',
'Edit' => '편집', 'Edit' => '편집',
'edit' => '편집', 'edit' => '편집',
@@ -109,7 +106,6 @@ $translations = array(
'Foreign keys' => '외부 키', 'Foreign keys' => '외부 키',
'Format' => '형식', 'Format' => '형식',
'From server' => '서버에서 실행', 'From server' => '서버에서 실행',
'From' => '보낸 사람',
'Functions' => '함수', 'Functions' => '함수',
'Geometry' => '기하 형', 'Geometry' => '기하 형',
'Grant' => '권한 부여', 'Grant' => '권한 부여',
@@ -211,7 +207,6 @@ $translations = array(
'Select' => '선택', 'Select' => '선택',
'select' => '선택', 'select' => '선택',
'Selected' => '선택됨', 'Selected' => '선택됨',
'Send' => '보내기',
'Sequence has been altered.' => '시퀀스를 변경했습니다.', 'Sequence has been altered.' => '시퀀스를 변경했습니다.',
'Sequence has been created.' => '시퀀스를 추가했습니다.', 'Sequence has been created.' => '시퀀스를 추가했습니다.',
'Sequence has been dropped.' => '시퀀스를 제거했습니다.', 'Sequence has been dropped.' => '시퀀스를 제거했습니다.',
@@ -230,7 +225,6 @@ $translations = array(
'Status' => '상태', 'Status' => '상태',
'Stop on error' => '오류의 경우 중지', 'Stop on error' => '오류의 경우 중지',
'Strings' => '문자열', 'Strings' => '문자열',
'Subject' => '제목',
'System' => '데이터베이스 형식', 'System' => '데이터베이스 형식',
'Table has been altered.' => '테이블을 변경했습니다.', 'Table has been altered.' => '테이블을 변경했습니다.',
'Table has been created.' => '테이블을 만들었습니다.', 'Table has been created.' => '테이블을 만들었습니다.',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistema', 'System' => 'Sistema',
'Server' => 'Serveris', 'Server' => 'Serveris',
@@ -257,13 +257,6 @@ $translations = array(
'Clone' => 'Klonuoti', 'Clone' => 'Klonuoti',
'Delete' => 'Trinti', 'Delete' => 'Trinti',
'E-mail' => 'El. paštas',
'From' => 'Nuo',
'Subject' => 'Antraštė',
'Attachments' => 'Priedai',
'Send' => 'Siųsti',
'%d e-mail(s) have been sent.' => array('Išsiųstas %d laiškas.', 'Išsiųsti %d laiškai.', 'Išsiųsta %d laiškų.'),
// data type descriptions // data type descriptions
'Numbers' => 'Skaičiai', 'Numbers' => 'Skaičiai',
'Date and time' => 'Data ir laikas', 'Date and time' => 'Data ir laikas',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Ieiet', 'Login' => 'Ieiet',
'Logout successful.' => 'Jūs veiksmīgi izgājāt no sistēmas.', 'Logout successful.' => 'Jūs veiksmīgi izgājāt no sistēmas.',
'Invalid credentials.' => 'Nepareizs lietotāja vārds vai parole.', 'Invalid credentials.' => 'Nepareizs lietotāja vārds vai parole.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binārie', 'Binary' => 'Binārie',
'Lists' => 'Saraksti', 'Lists' => 'Saraksti',
'Editor' => 'Redaktors', 'Editor' => 'Redaktors',
'E-mail' => 'Epasts',
'From' => 'No',
'Subject' => 'Tēma',
'Send' => 'Sūtīt',
'%d e-mail(s) have been sent.' => array('Nosūtīts %d epasts.', 'Nosūtīti %d epasti.', 'Nosūtīti %d epasti.'),
'Webserver file %s' => 'Fails %s uz servera', 'Webserver file %s' => 'Fails %s uz servera',
'File does not exist.' => 'Fails neeksistē.', 'File does not exist.' => 'Fails neeksistē.',
'%d in total' => 'Kopā %d', '%d in total' => 'Kopā %d',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Tīkls', 'Network' => 'Tīkls',
'Geometry' => 'Ģeometrija', 'Geometry' => 'Ģeometrija',
'File exists.' => 'Fails eksistē.', 'File exists.' => 'Fails eksistē.',
'Attachments' => 'Pielikumi',
'%d query(s) executed OK.' => array('%d pieprasījums veiksmīgs.', '%d pieprasījumi veiksmīgi.', '%d pieprasījumi veiksmīgi.'), '%d query(s) executed OK.' => array('%d pieprasījums veiksmīgs.', '%d pieprasījumi veiksmīgi.', '%d pieprasījumi veiksmīgi.'),
'Show only errors' => 'Rādīt tikai kļūdas', 'Show only errors' => 'Rādīt tikai kļūdas',
'Refresh' => 'Atjaunot', 'Refresh' => 'Atjaunot',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistem', 'System' => 'Sistem',
'Server' => 'Pelayan', 'Server' => 'Pelayan',
@@ -283,13 +283,6 @@ $translations = array(
'Delete' => 'Padam', 'Delete' => 'Padam',
'You have no privileges to update this table.' => 'Anda tidak mempunyai keistimewaan untuk mengemaskini jadual ini.', 'You have no privileges to update this table.' => 'Anda tidak mempunyai keistimewaan untuk mengemaskini jadual ini.',
'E-mail' => 'Emel',
'From' => 'Dari',
'Subject' => 'Subjek',
'Attachments' => 'Lampiran',
'Send' => 'Hantar',
'%d e-mail(s) have been sent.' => '%d emel telah dihantar.',
// data type descriptions // data type descriptions
'Numbers' => 'Nombor', 'Numbers' => 'Nombor',
'Date and time' => 'Tarikh dan masa', 'Date and time' => 'Tarikh dan masa',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Aanmelden', 'Login' => 'Aanmelden',
'Logout successful.' => 'Successvol afgemeld.', 'Logout successful.' => 'Successvol afgemeld.',
'Invalid credentials.' => 'Ongeldige gebruikersgegevens.', 'Invalid credentials.' => 'Ongeldige gebruikersgegevens.',
@@ -215,11 +215,6 @@ $translations = array(
'Binary' => 'Binaire gegevens', 'Binary' => 'Binaire gegevens',
'Lists' => 'Lijsten', 'Lists' => 'Lijsten',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'E-mail',
'From' => 'Van',
'Subject' => 'Onderwerp',
'Send' => 'Verzenden',
'%d e-mail(s) have been sent.' => array('%d e-mail verzonden.', '%d e-mails verzonden.'),
'Webserver file %s' => 'Webserver bestand %s', 'Webserver file %s' => 'Webserver bestand %s',
'File does not exist.' => 'Bestand niet gevonden.', 'File does not exist.' => 'Bestand niet gevonden.',
'%d in total' => '%d in totaal', '%d in total' => '%d in totaal',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Netwerk', 'Network' => 'Netwerk',
'Geometry' => 'Geometrie', 'Geometry' => 'Geometrie',
'File exists.' => 'Bestand bestaat reeds.', 'File exists.' => 'Bestand bestaat reeds.',
'Attachments' => 'Bijlagen',
'%d query(s) executed OK.' => array('%d query succesvol uitgevoerd.', '%d querys succesvol uitgevoerd.'), '%d query(s) executed OK.' => array('%d query succesvol uitgevoerd.', '%d querys succesvol uitgevoerd.'),
'Show only errors' => 'Enkel fouten tonen', 'Show only errors' => 'Enkel fouten tonen',
'Refresh' => 'Vernieuwen', 'Refresh' => 'Vernieuwen',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'System' => 'System', 'System' => 'System',
'Server' => 'Server', 'Server' => 'Server',
'Username' => 'Brukernavn', 'Username' => 'Brukernavn',
@@ -238,12 +238,6 @@ $translations = array(
'Clone' => 'Klon', 'Clone' => 'Klon',
'Delete' => 'Slett', 'Delete' => 'Slett',
'You have no privileges to update this table.' => 'Du mangler rettighetene som trengs for å endre denne tabellen.', 'You have no privileges to update this table.' => 'Du mangler rettighetene som trengs for å endre denne tabellen.',
'E-mail' => 'E-post',
'From' => 'Fra',
'Subject' => 'Tittel',
'Attachments' => 'Vedlegg',
'Send' => 'Send',
'%d e-mail(s) have been sent.' => array('%d epost sendt.', '%d eposter sendt.'),
'Numbers' => 'Nummer', 'Numbers' => 'Nummer',
'Date and time' => 'Dato og tid', 'Date and time' => 'Dato og tid',
'Strings' => 'Strenger', 'Strings' => 'Strenger',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Rodzaj bazy', 'System' => 'Rodzaj bazy',
'Server' => 'Serwer', 'Server' => 'Serwer',
@@ -45,6 +45,7 @@ $translations = array(
'User has been created.' => 'Użytkownik został dodany.', 'User has been created.' => 'Użytkownik został dodany.',
'Hashed' => 'Zahashowane', 'Hashed' => 'Zahashowane',
'Column' => 'Kolumna', 'Column' => 'Kolumna',
'Columns' => 'Kolumny',
'Routine' => 'Procedura', 'Routine' => 'Procedura',
'Grant' => 'Uprawnienia', 'Grant' => 'Uprawnienia',
'Revoke' => 'Usuń uprawnienia', 'Revoke' => 'Usuń uprawnienia',
@@ -295,13 +296,6 @@ $translations = array(
'Delete' => 'Usuń', 'Delete' => 'Usuń',
'You have no privileges to update this table.' => 'Brak uprawnień do edycji tej tabeli.', 'You have no privileges to update this table.' => 'Brak uprawnień do edycji tej tabeli.',
'E-mail' => 'E-mail',
'From' => 'Nadawca',
'Subject' => 'Temat',
'Attachments' => 'Załączniki',
'Send' => 'Wyślij',
'%d e-mail(s) have been sent.' => array('Wysłano %d e-mail.', 'Wysłano %d e-maile.', 'Wysłano %d e-maili.'),
// data type descriptions // data type descriptions
'Numbers' => 'Numeryczne', 'Numbers' => 'Numeryczne',
'Date and time' => 'Data i czas', 'Date and time' => 'Data i czas',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Entrar', 'Login' => 'Entrar',
'Logout successful.' => 'Saída bem sucedida.', 'Logout successful.' => 'Saída bem sucedida.',
'Invalid credentials.' => 'Identificação inválida.', 'Invalid credentials.' => 'Identificação inválida.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binário', 'Binary' => 'Binário',
'Lists' => 'Listas', 'Lists' => 'Listas',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'E-mail',
'From' => 'De',
'Subject' => 'Assunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email foi enviado.', '%d emails foram enviados.'),
'Webserver file %s' => 'Arquivo do servidor web %s', 'Webserver file %s' => 'Arquivo do servidor web %s',
'File does not exist.' => 'Arquivo não existe.', 'File does not exist.' => 'Arquivo não existe.',
'%d in total' => '%d no total', '%d in total' => '%d no total',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Rede', 'Network' => 'Rede',
'Geometry' => 'Geometria', 'Geometry' => 'Geometria',
'File exists.' => 'Arquivo já existe.', 'File exists.' => 'Arquivo já existe.',
'Attachments' => 'Anexos',
'%d query(s) executed OK.' => array('%d consulta sql executada corretamente.', '%d consultas sql executadas corretamente.'), '%d query(s) executed OK.' => array('%d consulta sql executada corretamente.', '%d consultas sql executadas corretamente.'),
'Show only errors' => 'Mostrar somente erros', 'Show only errors' => 'Mostrar somente erros',
'Refresh' => 'Atualizar', 'Refresh' => 'Atualizar',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Entrar', 'Login' => 'Entrar',
'Logout successful.' => 'Sessão terminada com sucesso.', 'Logout successful.' => 'Sessão terminada com sucesso.',
'Invalid credentials.' => 'Identificação inválida.', 'Invalid credentials.' => 'Identificação inválida.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Binário', 'Binary' => 'Binário',
'Lists' => 'Listas', 'Lists' => 'Listas',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'E-mail',
'From' => 'De',
'Subject' => 'Assunto',
'Send' => 'Enviar',
'%d e-mail(s) have been sent.' => array('%d email enviado.', '%d emails enviados.'),
'Webserver file %s' => 'Ficheiro do servidor web %s', 'Webserver file %s' => 'Ficheiro do servidor web %s',
'File does not exist.' => 'Ficheiro não existe.', 'File does not exist.' => 'Ficheiro não existe.',
'%d in total' => '%d no total', '%d in total' => '%d no total',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Rede', 'Network' => 'Rede',
'Geometry' => 'Geometria', 'Geometry' => 'Geometria',
'File exists.' => 'Ficheiro já existe.', 'File exists.' => 'Ficheiro já existe.',
'Attachments' => 'Anexos',
'%d query(s) executed OK.' => array('%d consulta sql executada corretamente.', '%d consultas sql executadas corretamente.'), '%d query(s) executed OK.' => array('%d consulta sql executada corretamente.', '%d consultas sql executadas corretamente.'),
'Show only errors' => 'Mostrar somente erros', 'Show only errors' => 'Mostrar somente erros',
'Refresh' => 'Atualizar', 'Refresh' => 'Atualizar',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Intră', 'Login' => 'Intră',
'Logout successful.' => 'Ați ieșit cu succes.', 'Logout successful.' => 'Ați ieșit cu succes.',
'Invalid credentials.' => 'Numele de utilizator sau parola este greșită.', 'Invalid credentials.' => 'Numele de utilizator sau parola este greșită.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Tip binar', 'Binary' => 'Tip binar',
'Lists' => 'Liste', 'Lists' => 'Liste',
'Editor' => 'Editor', 'Editor' => 'Editor',
'E-mail' => 'Poșta electronică',
'From' => 'De la',
'Subject' => 'Pentru',
'Send' => 'Trimite',
'%d e-mail(s) have been sent.' => array('A fost trimis %d mail.', 'Au fost trimise %d mail-uri.'),
'Webserver file %s' => 'Fișierul %s pe server', 'Webserver file %s' => 'Fișierul %s pe server',
'File does not exist.' => 'Acest fișier nu există.', 'File does not exist.' => 'Acest fișier nu există.',
'%d in total' => 'În total %d', '%d in total' => 'În total %d',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Rețea', 'Network' => 'Rețea',
'Geometry' => 'Geometrie', 'Geometry' => 'Geometrie',
'File exists.' => 'Fișierul există deja.', 'File exists.' => 'Fișierul există deja.',
'Attachments' => 'Fișiere atașate',
'%d query(s) executed OK.' => array('%d query executat.', '%d query-uri executate cu succes.'), '%d query(s) executed OK.' => array('%d query executat.', '%d query-uri executate cu succes.'),
'Show only errors' => 'Arată doar greșeli', 'Show only errors' => 'Arată doar greșeli',
'Refresh' => 'Împrospătează', 'Refresh' => 'Împrospătează',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Войти', 'Login' => 'Войти',
'Logout successful.' => 'Вы успешно покинули систему.', 'Logout successful.' => 'Вы успешно покинули систему.',
'Invalid credentials.' => 'Неправильное имя пользователя или пароль.', 'Invalid credentials.' => 'Неправильное имя пользователя или пароль.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'Двоичный тип', 'Binary' => 'Двоичный тип',
'Lists' => 'Списки', 'Lists' => 'Списки',
'Editor' => 'Редактор', 'Editor' => 'Редактор',
'E-mail' => 'Эл. почта',
'From' => 'От',
'Subject' => 'Тема',
'Send' => 'Послать',
'%d e-mail(s) have been sent.' => array('Было отправлено %d письмо.', 'Было отправлено %d письма.', 'Было отправлено %d писем.'),
'Webserver file %s' => 'Файл %s на вебсервере', 'Webserver file %s' => 'Файл %s на вебсервере',
'File does not exist.' => 'Такого файла не существует.', 'File does not exist.' => 'Такого файла не существует.',
'%d in total' => 'Всего %d', '%d in total' => 'Всего %d',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Сеть', 'Network' => 'Сеть',
'Geometry' => 'Геометрия', 'Geometry' => 'Геометрия',
'File exists.' => 'Файл уже существует.', 'File exists.' => 'Файл уже существует.',
'Attachments' => 'Прикреплённые файлы',
'%d query(s) executed OK.' => array('%d запрос выполнен успешно.', '%d запроса выполнено успешно.', '%d запросов выполнено успешно.'), '%d query(s) executed OK.' => array('%d запрос выполнен успешно.', '%d запроса выполнено успешно.', '%d запросов выполнено успешно.'),
'Show only errors' => 'Только ошибки', 'Show only errors' => 'Только ошибки',
'Refresh' => 'Обновить', 'Refresh' => 'Обновить',
@@ -302,6 +296,15 @@ $translations = array(
'Unknown error.' => 'Неизвестная ошибка.', 'Unknown error.' => 'Неизвестная ошибка.',
'Database does not support password.' => 'База данных не поддерживает пароль.', 'Database does not support password.' => 'База данных не поддерживает пароль.',
'Disable %s or enable %s or %s extensions.' => 'Отключите %s или включите расширения %s или %s.', 'Disable %s or enable %s or %s extensions.' => 'Отключите %s или включите расширения %s или %s.',
'Check has been dropped.' => 'Проверка удалена.',
'Check has been altered.' => 'Проверка изменена.',
'Check has been created.' => 'Проверка создана.',
'Alter check' => 'Изменить проверку',
'Create check' => 'Создать проверку',
'Checks' => 'Проверки',
'Loaded plugins' => 'Загруженные плагины',
'%s must <a%s>return an array</a>.' => '%s должна <a%s>вернуть массив</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Настроить</a> %s в %s.',
); );
// run `php ../../lang.php ru` to update this file // run `php ../../lang.php ru` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'Prihlásiť sa', 'Login' => 'Prihlásiť sa',
'Logout successful.' => 'Odhlásenie prebehlo v poriadku.', 'Logout successful.' => 'Odhlásenie prebehlo v poriadku.',
'Invalid credentials.' => 'Neplatné prihlasovacie údaje.', 'Invalid credentials.' => 'Neplatné prihlasovacie údaje.',
@@ -205,11 +205,6 @@ $translations = array(
'History' => 'História', 'History' => 'História',
'Variables' => 'Premenné', 'Variables' => 'Premenné',
'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Zdrojové a cieľové stĺpce musia mať rovnaký datový typ, nad cieľovými stĺpcami musí byť definovaný index a odkazované dáta musia existovať.', 'Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.' => 'Zdrojové a cieľové stĺpce musia mať rovnaký datový typ, nad cieľovými stĺpcami musí byť definovaný index a odkazované dáta musia existovať.',
'E-mail' => 'E-mail',
'From' => 'Odosielateľ',
'Subject' => 'Predmet',
'Send' => 'Odoslať',
'%d e-mail(s) have been sent.' => array('Bol odoslaný %d e-mail.', 'Boli odoslané %d e-maily.', 'Bolo odoslaných %d e-mailov.'),
'Run file' => 'Spustiť súbor', 'Run file' => 'Spustiť súbor',
'Numbers' => 'Čísla', 'Numbers' => 'Čísla',
'Date and time' => 'Dátum a čas', 'Date and time' => 'Dátum a čas',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'Sieť', 'Network' => 'Sieť',
'Geometry' => 'Geometria', 'Geometry' => 'Geometria',
'File exists.' => 'Súbor existuje.', 'File exists.' => 'Súbor existuje.',
'Attachments' => 'Prílohy',
'%d query(s) executed OK.' => array('Bol vykonaný %d dotaz.', 'Boli vykonané %d dotazy.', 'Bolo vykonaných %d dotazov.'), '%d query(s) executed OK.' => array('Bol vykonaný %d dotaz.', 'Boli vykonané %d dotazy.', 'Bolo vykonaných %d dotazov.'),
'Show only errors' => 'Zobraziť iba chyby', 'Show only errors' => 'Zobraziť iba chyby',
'Refresh' => 'Obnoviť', 'Refresh' => 'Obnoviť',
@@ -281,7 +275,7 @@ $translations = array(
'Warnings' => 'Varovania', 'Warnings' => 'Varovania',
'%d / ' => '%d / ', '%d / ' => '%d / ',
'Limit rows' => 'Limit riadkov', 'Limit rows' => 'Limit riadkov',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje prístup k databáze bez hesla, <a href="https://www.adminer.org/cs/password/"%s>viac informácií</a>.', 'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Adminer nepodporuje prístup k databáze bez hesla, <a href="https://www.adminer.org/sk/password/"%s>viac informácií</a>.',
'Default value' => 'Predvolená hodnota', 'Default value' => 'Predvolená hodnota',
'Full table scan' => 'Prechod celej tabuľky', 'Full table scan' => 'Prechod celej tabuľky',
'Too many unsuccessful logins, try again in %d minute(s).' => array('Príliš veľa pokusov o prihlásenie, skúste to znova za %d minutu.', 'Príliš veľa pokusov o prihlásenie, skúste to znova za %d minuty.', 'Príliš veľa pokusov o prihlásenie, skúste to znova za %d minút.'), 'Too many unsuccessful logins, try again in %d minute(s).' => array('Príliš veľa pokusov o prihlásenie, skúste to znova za %d minutu.', 'Príliš veľa pokusov o prihlásenie, skúste to znova za %d minuty.', 'Príliš veľa pokusov o prihlásenie, skúste to znova za %d minút.'),

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistem', 'System' => 'Sistem',
'Server' => 'Strežnik', 'Server' => 'Strežnik',
@@ -254,13 +254,6 @@ $translations = array(
'Clone' => 'Kloniraj', 'Clone' => 'Kloniraj',
'Delete' => 'Izbriši', 'Delete' => 'Izbriši',
'E-mail' => 'E-mail',
'From' => 'Od',
'Subject' => 'Zadeva',
'Attachments' => 'Priponke',
'Send' => 'Pošlji',
'%d e-mail(s) have been sent.' => array('Poslan je %d e-mail.', 'Poslana sta %d e-maila.', 'Poslani so %d e-maili.', 'Poslanih je %d e-mailov.'),
// data type descriptions // data type descriptions
'Numbers' => 'Števila', 'Numbers' => 'Števila',
'Date and time' => 'Datum in čas', 'Date and time' => 'Datum in čas',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Систем', 'System' => 'Систем',
'Server' => 'Сервер', 'Server' => 'Сервер',
@@ -262,13 +262,6 @@ $translations = array(
'Clone' => 'Дуплирај', 'Clone' => 'Дуплирај',
'Delete' => 'Избриши', 'Delete' => 'Избриши',
'E-mail' => 'Ел. пошта',
'From' => 'Од',
'Subject' => 'Наслов',
'Attachments' => 'Прилози',
'Send' => 'Пошаљи',
'%d e-mail(s) have been sent.' => array('%d порука ел. поште је послата.', '%d поруке ел. поште су послате.', '%d порука ел. поште је послато.'),
// data type descriptions // data type descriptions
'Numbers' => 'Број', 'Numbers' => 'Број',
'Date and time' => 'Датум и време', 'Date and time' => 'Датум и време',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'System', 'System' => 'System',
'Server' => 'Server', 'Server' => 'Server',
@@ -292,13 +292,6 @@ $translations = array(
'Delete' => 'Ta bort', 'Delete' => 'Ta bort',
'You have no privileges to update this table.' => 'Du har inga privilegier för att uppdatera den här tabellen.', 'You have no privileges to update this table.' => 'Du har inga privilegier för att uppdatera den här tabellen.',
'E-mail' => 'Email',
'From' => 'Från',
'Subject' => 'Ämne',
'Attachments' => 'Bilagor',
'Send' => 'Skicka',
'%d e-mail(s) have been sent.' => array('%d email har blivit skickat.', '%d email har blivit skickade.'),
// data type descriptions // data type descriptions
'Numbers' => 'Nummer', 'Numbers' => 'Nummer',
'Date and time' => 'Datum och tid', 'Date and time' => 'Datum och tid',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'நுழை', 'Login' => 'நுழை',
'Logout successful.' => 'வெற்றிக‌ர‌மாய் வெளியேறியாயிற்று.', 'Logout successful.' => 'வெற்றிக‌ர‌மாய் வெளியேறியாயிற்று.',
'Invalid credentials.' => 'ச‌ரியான‌ விப‌ர‌ங்க‌ள் இல்லை.', 'Invalid credentials.' => 'ச‌ரியான‌ விப‌ர‌ங்க‌ள் இல்லை.',
@@ -212,11 +212,6 @@ $translations = array(
'Binary' => 'பைன‌ரி', 'Binary' => 'பைன‌ரி',
'Lists' => 'ப‌ட்டிய‌ல்', 'Lists' => 'ப‌ட்டிய‌ல்',
'Editor' => 'தொகுப்பாளர்', 'Editor' => 'தொகுப்பாளர்',
'E-mail' => 'மின்ன‌ஞ்ச‌ல்',
'From' => 'அனுப்புனர்',
'Subject' => 'பொருள்',
'Send' => 'அனுப்பு',
'%d e-mail(s) have been sent.' => array('%d மின்ன‌ஞ்ச‌ல் அனுப்ப‌ப‌ட்ட‌து.', '%d மின்ன‌ஞ்ச‌ல்க‌ள் அனுப்ப‌ப்ப‌ட்ட‌ன‌.'),
'Webserver file %s' => 'வெப் ச‌ர்வ‌ர் கோப்பு %s', 'Webserver file %s' => 'வெப் ச‌ர்வ‌ர் கோப்பு %s',
'File does not exist.' => 'கோப்பு இல்லை.', 'File does not exist.' => 'கோப்பு இல்லை.',
'%d in total' => 'மொத்தம் %d ', '%d in total' => 'மொத்தம் %d ',
@@ -252,7 +247,6 @@ $translations = array(
'Network' => 'நெட்வொர்க்', 'Network' => 'நெட்வொர்க்',
'Geometry' => 'வ‌டிவ‌விய‌ல் (Geometry)', 'Geometry' => 'வ‌டிவ‌விய‌ல் (Geometry)',
'File exists.' => 'கோப்பு உள்ள‌து.', 'File exists.' => 'கோப்பு உள்ள‌து.',
'Attachments' => 'இணைப்புக‌ள்',
'now' => 'இப்பொழுது', 'now' => 'இப்பொழுது',
'%d query(s) executed OK.' => array('%d வின‌வ‌ல் செய‌ல்ப‌டுத்த‌ப்ப‌ட்ட‌து.', '%d வின‌வ‌ல்க‌ள் செய‌ல்ப‌டுத்த‌ப்ப‌ட்ட‌ன‌.'), '%d query(s) executed OK.' => array('%d வின‌வ‌ல் செய‌ல்ப‌டுத்த‌ப்ப‌ட்ட‌து.', '%d வின‌வ‌ல்க‌ள் செய‌ல்ப‌டுத்த‌ப்ப‌ட்ட‌ன‌.'),
'Show only errors' => 'பிழைக‌ளை ம‌ட்டும் காண்பிக்க‌வும்', 'Show only errors' => 'பிழைக‌ளை ம‌ட்டும் காண்பிக்க‌வும்',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
'Login' => 'เข้าสู่ระบบ', 'Login' => 'เข้าสู่ระบบ',
'Logout successful.' => 'ออกจากระบบเรียบร้อยแล้ว.', 'Logout successful.' => 'ออกจากระบบเรียบร้อยแล้ว.',
'Invalid credentials.' => 'ข้อมูลไม่ถูกต้อง.', 'Invalid credentials.' => 'ข้อมูลไม่ถูกต้อง.',
@@ -214,11 +214,6 @@ $translations = array(
'Binary' => 'เลขฐานสอง', 'Binary' => 'เลขฐานสอง',
'Lists' => 'รายการ', 'Lists' => 'รายการ',
'Editor' => 'ผู้แก้ไข', 'Editor' => 'ผู้แก้ไข',
'E-mail' => 'อีเมล์',
'From' => 'จาก',
'Subject' => 'หัวข้อ',
'Send' => 'ส่ง',
'%d e-mail(s) have been sent.' => 'มี %d อีเมล์ ถูกส่งออกแล้ว.',
'Webserver file %s' => 'Webserver file %s', 'Webserver file %s' => 'Webserver file %s',
'File does not exist.' => 'ไม่มีไฟล์.', 'File does not exist.' => 'ไม่มีไฟล์.',
'%d in total' => '%d ของทั้งหมด', '%d in total' => '%d ของทั้งหมด',
@@ -253,7 +248,6 @@ $translations = array(
'Network' => 'เครื่องข่าย', 'Network' => 'เครื่องข่าย',
'Geometry' => 'เรขาคณิต', 'Geometry' => 'เรขาคณิต',
'File exists.' => 'มีไฟล์นี้อยู่แล้ว.', 'File exists.' => 'มีไฟล์นี้อยู่แล้ว.',
'Attachments' => 'ไฟล์แนบ',
'%d query(s) executed OK.' => '%d คำสั่งถูกดำเนินการแล้ว.', '%d query(s) executed OK.' => '%d คำสั่งถูกดำเนินการแล้ว.',
'Show only errors' => 'แสดงเฉพาะเออเรอ', 'Show only errors' => 'แสดงเฉพาะเออเรอ',
'Refresh' => 'โหลดใหม่', 'Refresh' => 'โหลดใหม่',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Sistem', 'System' => 'Sistem',
'Server' => 'Sunucu', 'Server' => 'Sunucu',
@@ -286,13 +286,6 @@ $translations = array(
'Delete' => 'Sil', 'Delete' => 'Sil',
'You have no privileges to update this table.' => 'Bu tabloyu güncellemek için yetkiniz yok.', 'You have no privileges to update this table.' => 'Bu tabloyu güncellemek için yetkiniz yok.',
'E-mail' => 'E-posta',
'From' => 'Gönderen',
'Subject' => 'Konu',
'Attachments' => 'Ekler',
'Send' => 'Gönder',
'%d e-mail(s) have been sent.' => array('%d e-posta gönderildi.', '%d adet e-posta gönderildi.'),
// data type descriptions // data type descriptions
'Numbers' => 'Sayılar', 'Numbers' => 'Sayılar',
'Date and time' => 'Tarih ve zaman', 'Date and time' => 'Tarih ve zaman',
@@ -307,7 +300,7 @@ $translations = array(
// date format in Editor: $1 yyyy, $2 yy, $3 mm, $4 m, $5 dd, $6 d // date format in Editor: $1 yyyy, $2 yy, $3 mm, $4 m, $5 dd, $6 d
'$1-$3-$5' => '$6.$4.$1', '$1-$3-$5' => '$6.$4.$1',
// hint for date format - use language equivalents for day, month and year shortcuts // hint for date format - use language equivalents for day, month and year shortcuts
'[yyyy]-mm-dd' => '[yyyy]-aa-gg', '[yyyy]-mm-dd' => 'g.a.[yyyy]',
// hint for time format - use language equivalents for hour, minute and second shortcuts // hint for time format - use language equivalents for hour, minute and second shortcuts
'HH:MM:SS' => 'SS:DD:ss', 'HH:MM:SS' => 'SS:DD:ss',
'now' => 'şimdi', 'now' => 'şimdi',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Система Бази Даних', 'System' => 'Система Бази Даних',
'Server' => 'Сервер', 'Server' => 'Сервер',
@@ -259,13 +259,6 @@ $translations = array(
'Clone' => 'Клонувати', 'Clone' => 'Клонувати',
'Delete' => 'Видалити', 'Delete' => 'Видалити',
'E-mail' => 'E-mail',
'From' => 'Від',
'Subject' => 'Заголовок',
'Attachments' => 'Додатки',
'Send' => 'Надіслати',
'%d e-mail(s) have been sent.' => array('Було надіслано %d повідомлення.', 'Було надіслано %d повідомлення.', 'Було надіслано %d повідомлень.'),
// data type descriptions // data type descriptions
'Numbers' => 'Числа', 'Numbers' => 'Числа',
'Date and time' => 'Дата і час', 'Date and time' => 'Дата і час',
@@ -344,6 +337,18 @@ $translations = array(
'Saving' => 'Збереження', 'Saving' => 'Збереження',
'Unknown error.' => 'Невідома помилка.', 'Unknown error.' => 'Невідома помилка.',
'Database does not support password.' => 'База даних не підтримує пароль.', 'Database does not support password.' => 'База даних не підтримує пароль.',
'Disable %s or enable %s or %s extensions.' => 'Вимкніть %s або увімкніть розширення %s або %s.',
'Check has been dropped.' => 'Перевірку видалено.',
'Check has been altered.' => 'Перевірка змінена.',
'Check has been created.' => 'Перевірку створено.',
'Alter check' => 'Змінити перевірку',
'Create check' => 'Створити перевірку',
'Vacuum' => 'Вакуум',
'%d / ' => '%d / ',
'Checks' => 'Перевірки',
'Loaded plugins' => 'Завантажені плагіни',
'%s must <a%s>return an array</a>.' => '%s має <a%s>повернути масив</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Налаштувати</a> %s у %s.',
); );
// run `php ../../lang.php uk` to update this file // run `php ../../lang.php uk` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Tizim', 'System' => 'Tizim',
'Server' => 'Server', 'Server' => 'Server',
@@ -297,13 +297,6 @@ $translations = array(
'Delete' => 'O\'chirish', 'Delete' => 'O\'chirish',
'You have no privileges to update this table.' => 'Bu jadvalni yangilash uchun sizda huquqlar yo\'q.', '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 // data type descriptions
'Numbers' => 'Raqamlar', 'Numbers' => 'Raqamlar',
'Date and time' => 'Sana va vaqt', 'Date and time' => 'Sana va vaqt',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Hệ thống', 'System' => 'Hệ thống',
'Server' => 'Máy chủ', 'Server' => 'Máy chủ',
@@ -271,13 +271,6 @@ $translations = array(
'Delete' => 'Xoá', 'Delete' => 'Xoá',
'You have no privileges to update this table.' => 'Bạn không có quyền sửa bảng này.', 'You have no privileges to update this table.' => 'Bạn không có quyền sửa bảng này.',
'E-mail' => 'Địa chỉ email',
'From' => 'Người gửi',
'Subject' => 'Chủ đề',
'Attachments' => 'Đính kèm',
'Send' => 'Gửi',
'%d e-mail(s) have been sent.' => '%d thư đã gửi.',
// data type descriptions // data type descriptions
'Numbers' => 'Số', 'Numbers' => 'Số',
'Date and time' => 'Ngày giờ', 'Date and time' => 'Ngày giờ',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => 'Xx', 'System' => 'Xx',
'Server' => 'Xx', 'Server' => 'Xx',
@@ -13,9 +13,6 @@ $translations = array(
'Logged as: %s' => 'Xx: %s', 'Logged as: %s' => 'Xx: %s',
'Logout successful.' => 'Xx.', 'Logout successful.' => 'Xx.',
'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Xx <a href="https://www.adminer.org/en/donation/">xx</a>.', 'Thanks for using Adminer, consider <a href="https://www.adminer.org/en/donation/">donating</a>.' => 'Xx <a href="https://www.adminer.org/en/donation/">xx</a>.',
'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.', 'Invalid credentials.' => 'Xx.',
'There is a space in the input password which might be the cause.' => 'Xx.', 'There is a space in the input password which might be the cause.' => 'Xx.',
'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Xx, <a href="https://www.adminer.org/en/password/"%s>xx</a>.', 'Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/"%s>more information</a>.' => 'Xx, <a href="https://www.adminer.org/en/password/"%s>xx</a>.',
@@ -46,6 +43,7 @@ $translations = array(
'User has been created.' => 'Xx.', 'User has been created.' => 'Xx.',
'Hashed' => 'Xx', 'Hashed' => 'Xx',
'Column' => 'Xx', 'Column' => 'Xx',
'Columns' => 'Xx',
'Routine' => 'Xx', 'Routine' => 'Xx',
'Grant' => 'Xx', 'Grant' => 'Xx',
'Revoke' => 'Xx', 'Revoke' => 'Xx',
@@ -297,13 +295,6 @@ $translations = array(
'Delete' => 'Xx', 'Delete' => 'Xx',
'You have no privileges to update this table.' => 'Xx.', 'You have no privileges to update this table.' => 'Xx.',
'E-mail' => 'Xx',
'From' => 'Xx',
'Subject' => 'Xx',
'Attachments' => 'Xx',
'Send' => 'Xx',
'%d e-mail(s) have been sent.' => array('%d xx.', '%d xx.'),
// data type descriptions // data type descriptions
'Numbers' => 'Xx', 'Numbers' => 'Xx',
'Date and time' => 'Xx', 'Date and time' => 'Xx',
@@ -360,6 +351,11 @@ $translations = array(
'Check has been created.' => 'Xx.', 'Check has been created.' => 'Xx.',
'Check has been altered.' => 'Xx.', 'Check has been altered.' => 'Xx.',
'Check has been dropped.' => 'Xx.', 'Check has been dropped.' => 'Xx.',
'Loaded plugins' => 'Xx',
'%s must <a%s>return an array</a>.' => '%s xx <a%s>xx</a>.',
'<a%s>Configure</a> %s in %s.' => '<a%s>Xx</a> %s xx %s.',
'screenshot' => 'xx',
); );
// run `php ../../lang.php xx` to update this file // run `php ../../lang.php xx` to update this file

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => '資料庫系統', 'System' => '資料庫系統',
'Server' => '伺服器', 'Server' => '伺服器',
@@ -292,13 +292,6 @@ $translations = array(
'Delete' => '刪除', 'Delete' => '刪除',
'You have no privileges to update this table.' => '您沒有許可權更新這個資料表。', 'You have no privileges to update this table.' => '您沒有許可權更新這個資料表。',
'E-mail' => '電子郵件',
'From' => '來自',
'Subject' => '主旨',
'Attachments' => '附件',
'Send' => '寄出',
'%d e-mail(s) have been sent.' => '已寄出 %d 封郵件。',
// data type descriptions // data type descriptions
'Numbers' => '數字', 'Numbers' => '數字',
'Date and time' => '日期時間', 'Date and time' => '日期時間',

View File

@@ -1,7 +1,7 @@
<?php <?php
namespace Adminer; namespace Adminer;
$translations = array( Lang::$translations = array(
// label for database system selection (MySQL, SQLite, ...) // label for database system selection (MySQL, SQLite, ...)
'System' => '系统', 'System' => '系统',
'Server' => '服务器', 'Server' => '服务器',
@@ -208,7 +208,6 @@ $translations = array(
'Alter indexes' => '修改索引', 'Alter indexes' => '修改索引',
'Add next' => '下一行插入', 'Add next' => '下一行插入',
'Index Type' => '索引类型', 'Index Type' => '索引类型',
// 'Column (length)' => '列(长度)',
'Foreign keys' => '外键', 'Foreign keys' => '外键',
'Foreign key' => '外键', 'Foreign key' => '外键',
@@ -292,13 +291,6 @@ $translations = array(
'Delete' => '删除', 'Delete' => '删除',
'You have no privileges to update this table.' => '您没有权限更新这个表。', 'You have no privileges to update this table.' => '您没有权限更新这个表。',
'E-mail' => '电子邮件',
'From' => '来自',
'Subject' => '主题',
'Attachments' => '附件',
'Send' => '发送',
'%d e-mail(s) have been sent.' => '%d 封邮件已发送。',
// data type descriptions // data type descriptions
'Numbers' => '数字', 'Numbers' => '数字',
'Date and time' => '日期时间', 'Date and time' => '日期时间',

View File

@@ -5,11 +5,11 @@ page_header(lang('Privileges'));
echo '<p class="links"><a href="' . h(ME) . 'user=">' . lang('Create user') . "</a>"; echo '<p class="links"><a href="' . h(ME) . 'user=">' . lang('Create user') . "</a>";
$result = $connection->query("SELECT User, Host FROM mysql." . (DB == "" ? "user" : "db WHERE " . q(DB) . " LIKE Db") . " ORDER BY Host, User"); $result = connection()->query("SELECT User, Host FROM mysql." . (DB == "" ? "user" : "db WHERE " . q(DB) . " LIKE Db") . " ORDER BY Host, User");
$grant = $result; $grant = $result;
if (!$result) { if (!$result) {
// list logged user, information_schema.USER_PRIVILEGES lists just the current user too // list logged user, information_schema.USER_PRIVILEGES lists just the current user too
$result = $connection->query("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', 1) AS User, SUBSTRING_INDEX(CURRENT_USER, '@', -1) AS Host"); $result = connection()->query("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', 1) AS User, SUBSTRING_INDEX(CURRENT_USER, '@', -1) AS Host");
} }
echo "<form action=''><p>\n"; echo "<form action=''><p>\n";

View File

@@ -48,7 +48,7 @@ echo ($collations ? "<datalist id='collations'>" . optionlist($collations) . "</
<form action="" method="post" id="form"> <form action="" method="post" id="form">
<p><?php echo lang('Name'); ?>: <input name="name" value="<?php echo h($row["name"]); ?>" data-maxlength="64" autocapitalize="off"> <p><?php echo lang('Name'); ?>: <input name="name" value="<?php echo h($row["name"]); ?>" data-maxlength="64" autocapitalize="off">
<?php echo ($routine_languages ? lang('Language') . ": " . html_select("language", $routine_languages, $row["language"]) . "\n" : ""); ?> <?php echo ($routine_languages ? "<label>" . lang('Language') . ": " . html_select("language", $routine_languages, $row["language"]) . "</label>\n" : ""); ?>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<div class="scrollable"> <div class="scrollable">
<table class="nowrap"> <table class="nowrap">

View File

@@ -3,6 +3,7 @@ namespace Adminer;
page_header(lang('Database schema'), "", array(), h(DB . ($_GET["ns"] ? ".$_GET[ns]" : ""))); page_header(lang('Database schema'), "", array(), h(DB . ($_GET["ns"] ? ".$_GET[ns]" : "")));
/** @var array{float, float}[] */
$table_pos = array(); $table_pos = array();
$table_pos_js = array(); $table_pos_js = array();
$SCHEMA = ($_GET["schema"] ?: $_COOKIE["adminer_schema-" . str_replace(".", "_", DB)]); // $_COOKIE["adminer_schema"] was used before 3.2.0 //! ':' in table name $SCHEMA = ($_GET["schema"] ?: $_COOKIE["adminer_schema-" . str_replace(".", "_", DB)]); // $_COOKIE["adminer_schema"] was used before 3.2.0 //! ':' in table name
@@ -14,26 +15,28 @@ foreach ($matches as $i => $match) {
$top = 0; $top = 0;
$base_left = -1; $base_left = -1;
/** @var array{fields:Field[], pos:array{float, float}, references:string[][][]}[] */
$schema = array(); // table => array("fields" => array(name => field), "pos" => array(top, left), "references" => array(table => array(left => array(source, target)))) $schema = array(); // table => array("fields" => array(name => field), "pos" => array(top, left), "references" => array(table => array(left => array(source, target))))
$referenced = array(); // target_table => array(table => array(left => target_column)) $referenced = array(); // target_table => array(table => array(left => target_column))
$lefts = array(); // float => bool $lefts = array(); // float => bool
$all_fields = driver()->allFields();
foreach (table_status('', true) as $table => $table_status) { foreach (table_status('', true) as $table => $table_status) {
if (is_view($table_status)) { if (is_view($table_status)) {
continue; continue;
} }
$pos = 0; $pos = 0;
$schema[$table]["fields"] = array(); $schema[$table]["fields"] = array();
foreach (fields($table) as $name => $field) { foreach ($all_fields[$table] as $field) {
$pos += 1.25; $pos += 1.25;
$field["pos"] = $pos; $field["pos"] = $pos;
$schema[$table]["fields"][$name] = $field; $schema[$table]["fields"][$field["field"]] = $field;
} }
$schema[$table]["pos"] = ($table_pos[$table] ?: array($top, 0)); $schema[$table]["pos"] = ($table_pos[$table] ?: array($top, 0));
foreach ($adminer->foreignKeys($table) as $val) { foreach (adminer()->foreignKeys($table) as $val) {
if (!$val["db"]) { if (!$val["db"]) {
$left = $base_left; $left = $base_left;
if ($table_pos[$table][1] || $table_pos[$val["table"]][1]) { if (idx($table_pos[$table], 1) || idx($table_pos[$val["table"]], 1)) {
$left = min(floatval($table_pos[$table][1]), floatval($table_pos[$val["table"]][1])) - 1; $left = min(idx($table_pos[$table], 1, 0), idx($table_pos[$val["table"]], 1, 0)) - 1;
} else { } else {
$base_left -= .1; $base_left -= .1;
} }
@@ -65,13 +68,13 @@ foreach ($schema as $name => $table) {
echo script("qsl('div').onmousedown = schemaMousedown;"); echo script("qsl('div').onmousedown = schemaMousedown;");
foreach ($table["fields"] as $field) { foreach ($table["fields"] as $field) {
$val = '<span' . type_class($field["type"]) . ' title="' . h($field["full_type"] . ($field["null"] ? " NULL" : '')) . '">' . h($field["field"]) . '</span>'; $val = '<span' . type_class($field["type"]) . ' title="' . h($field["type"] . ($field["length"] ? "($field[length])" : "") . ($field["null"] ? " NULL" : '')) . '">' . h($field["field"]) . '</span>';
echo "<br>" . ($field["primary"] ? "<i>$val</i>" : $val); echo "<br>" . ($field["primary"] ? "<i>$val</i>" : $val);
} }
foreach ((array) $table["references"] as $target_name => $refs) { foreach ((array) $table["references"] as $target_name => $refs) {
foreach ($refs as $left => $ref) { foreach ($refs as $left => $ref) {
$left1 = $left - $table_pos[$name][1]; $left1 = $left - idx($table_pos[$name], 1);
$i = 0; $i = 0;
foreach ($ref[0] as $source) { 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;'>" 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;'>"
@@ -83,11 +86,10 @@ foreach ($schema as $name => $table) {
foreach ((array) $referenced[$name] as $target_name => $refs) { foreach ((array) $referenced[$name] as $target_name => $refs) {
foreach ($refs as $left => $columns) { foreach ($refs as $left => $columns) {
$left1 = $left - $table_pos[$name][1]; $left1 = $left - idx($table_pos[$name], 1);
$i = 0; $i = 0;
foreach ($columns as $target) { foreach ($columns as $target) {
echo "\n<div class='references' title='" . h($target_name) . "' id='refd$left-" . ($i++) . "'" echo "\n<div class='references arrow' title='" . h($target_name) . "' id='refd$left-" . ($i++) . "' style='left: $left1" . "em; top: " . $table["fields"][$target]["pos"] . "em;'>"
. " 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 style='height: .5em; border-bottom: 1px solid gray; width: " . (-$left1) . "em;'></div>"
. "</div>" . "</div>"
; ;

View File

@@ -36,10 +36,10 @@ if ($_GET["script"] == "db") {
json_row(""); json_row("");
} elseif ($_GET["script"] == "kill") { } elseif ($_GET["script"] == "kill") {
$connection->query("KILL " . number($_POST["kill"])); connection()->query("KILL " . number($_POST["kill"]));
} else { // connect } else { // connect
foreach (count_tables($adminer->databases()) as $db => $val) { foreach (count_tables(adminer()->databases()) as $db => $val) {
json_row("tables-$db", $val); json_row("tables-$db", $val);
json_row("size-$db", db_size($db)); json_row("size-$db", db_size($db));
} }

View File

@@ -13,14 +13,14 @@ $rights = array(); // privilege => 0
$columns = array(); // selectable columns $columns = array(); // selectable columns
$search_columns = array(); // searchable columns $search_columns = array(); // searchable columns
$order_columns = array(); // searchable columns $order_columns = array(); // searchable columns
$text_length = null; $text_length = "";
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
$name = $adminer->fieldName($field); $name = adminer()->fieldName($field);
$name_plain = html_entity_decode(strip_tags($name), ENT_QUOTES); $name_plain = html_entity_decode(strip_tags($name), ENT_QUOTES);
if (isset($field["privileges"]["select"]) && $name != "") { if (isset($field["privileges"]["select"]) && $name != "") {
$columns[$key] = $name_plain; $columns[$key] = $name_plain;
if (is_shortable($field)) { if (is_shortable($field)) {
$text_length = $adminer->selectLengthProcess(); $text_length = adminer()->selectLengthProcess();
} }
} }
if (isset($field["privileges"]["where"]) && $name != "") { if (isset($field["privileges"]["where"]) && $name != "") {
@@ -32,13 +32,13 @@ foreach ($fields as $key => $field) {
$rights += $field["privileges"]; $rights += $field["privileges"];
} }
list($select, $group) = $adminer->selectColumnsProcess($columns, $indexes); list($select, $group) = adminer()->selectColumnsProcess($columns, $indexes);
$select = array_unique($select); $select = array_unique($select);
$group = array_unique($group); $group = array_unique($group);
$is_group = count($group) < count($select); $is_group = count($group) < count($select);
$where = $adminer->selectSearchProcess($fields, $indexes); $where = adminer()->selectSearchProcess($fields, $indexes);
$order = $adminer->selectOrderProcess($fields, $indexes); $order = adminer()->selectOrderProcess($fields, $indexes);
$limit = $adminer->selectLimitProcess(); $limit = adminer()->selectLimitProcess();
if ($_GET["val"] && is_ajax()) { if ($_GET["val"] && is_ajax()) {
header("Content-Type: text/plain; charset=utf-8"); header("Content-Type: text/plain; charset=utf-8");
@@ -46,15 +46,15 @@ if ($_GET["val"] && is_ajax()) {
$as = convert_field($fields[key($row)]); $as = convert_field($fields[key($row)]);
$select = array($as ?: idf_escape(key($row))); $select = array($as ?: idf_escape(key($row)));
$where[] = where_check($unique_idf, $fields); $where[] = where_check($unique_idf, $fields);
$return = $driver->select($TABLE, $select, $where, $select); $return = driver()->select($TABLE, $select, $where, $select);
if ($return) { if ($return) {
echo reset($return->fetch_row()); echo first($return->fetch_row());
} }
} }
exit; exit;
} }
$primary = $unselected = null; $primary = $unselected = array();
foreach ($indexes as $index) { foreach ($indexes as $index) {
if ($index["type"] == "PRIMARY") { if ($index["type"] == "PRIMARY") {
$primary = array_flip($index["columns"]); $primary = array_flip($index["columns"]);
@@ -85,7 +85,7 @@ if ($_POST && !$error) {
if ($_POST["export"]) { if ($_POST["export"]) {
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import"); save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers($TABLE); dump_headers($TABLE);
$adminer->dumpTable($TABLE, ""); adminer()->dumpTable($TABLE, "");
$from = ($select ? implode(", ", $select) : "*") $from = ($select ? implode(", ", $select) : "*")
. convert_fields($columns, $fields, $select) . convert_fields($columns, $fields, $select)
. "\nFROM " . table($TABLE); . "\nFROM " . table($TABLE);
@@ -99,12 +99,12 @@ if ($_POST && !$error) {
} }
$query = implode(" UNION ALL ", $union); $query = implode(" UNION ALL ", $union);
} }
$adminer->dumpData($TABLE, "table", $query); adminer()->dumpData($TABLE, "table", $query);
$adminer->dumpFooter(); adminer()->dumpFooter();
exit; exit;
} }
if (!$adminer->selectEmailProcess($where, $foreign_keys)) { if (!adminer()->selectEmailProcess($where, $foreign_keys)) {
if ($_POST["save"] || $_POST["delete"]) { // edit if ($_POST["save"] || $_POST["delete"]) { // edit
$result = true; $result = true;
$affected = 0; $affected = 0;
@@ -118,33 +118,34 @@ if ($_POST && !$error) {
} }
} }
if ($_POST["delete"] || $set) { if ($_POST["delete"] || $set) {
if ($_POST["clone"]) { $query = ($_POST["clone"] ? "INTO " . table($TABLE) . " (" . implode(", ", array_keys($set)) . ")\nSELECT " . implode(", ", $set) . "\nFROM " . table($TABLE) : "");
$query = "INTO " . table($TABLE) . " (" . implode(", ", array_keys($set)) . ")\nSELECT " . implode(", ", $set) . "\nFROM " . table($TABLE);
}
if ($_POST["all"] || ($primary && is_array($_POST["check"])) || $is_group) { if ($_POST["all"] || ($primary && is_array($_POST["check"])) || $is_group) {
$result = ($_POST["delete"] $result = ($_POST["delete"]
? $driver->delete($TABLE, $where_check) ? driver()->delete($TABLE, $where_check)
: ($_POST["clone"] : ($_POST["clone"]
? queries("INSERT $query$where_check" . $driver->insertReturning($TABLE)) ? queries("INSERT $query$where_check" . driver()->insertReturning($TABLE))
: $driver->update($TABLE, $set, $where_check) : driver()->update($TABLE, $set, $where_check)
) )
); );
$affected = $connection->affected_rows + (is_object($result) ? $result->num_rows : 0); // PostgreSQL with RETURNING fills num_rows $affected = connection()->affected_rows;
if (is_object($result)) { // PostgreSQL with RETURNING fills num_rows
$affected += $result->num_rows;
}
} else { } else {
foreach ((array) $_POST["check"] as $val) { foreach ((array) $_POST["check"] as $val) {
// where is not unique so OR can't be used // where is not unique so OR can't be used
$where2 = "\nWHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($val, $fields); $where2 = "\nWHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($val, $fields);
$result = ($_POST["delete"] $result = ($_POST["delete"]
? $driver->delete($TABLE, $where2, 1) ? driver()->delete($TABLE, $where2, 1)
: ($_POST["clone"] : ($_POST["clone"]
? queries("INSERT" . limit1($TABLE, $query, $where2)) ? queries("INSERT" . limit1($TABLE, $query, $where2))
: $driver->update($TABLE, $set, $where2, 1) : driver()->update($TABLE, $set, $where2, 1)
) )
); );
if (!$result) { if (!$result) {
break; break;
} }
$affected += $connection->affected_rows; $affected += connection()->affected_rows;
} }
} }
} }
@@ -158,7 +159,7 @@ if ($_POST && !$error) {
queries_redirect(remove_from_uri($_POST["all"] && $_POST["delete"] ? "page" : ""), $message, $result); queries_redirect(remove_from_uri($_POST["all"] && $_POST["delete"] ? "page" : ""), $message, $result);
if (!$_POST["delete"]) { if (!$_POST["delete"]) {
$post_fields = (array) $_POST["fields"]; $post_fields = (array) $_POST["fields"];
edit_form($TABLE, array_intersect_key($fields, $post_fields), $post_fields, !$_POST["clone"]); edit_form($TABLE, array_intersect_key($fields, $post_fields), $post_fields, !$_POST["clone"], $error);
page_footer(); page_footer();
exit; exit;
} }
@@ -172,20 +173,20 @@ if ($_POST && !$error) {
foreach ($_POST["val"] as $unique_idf => $row) { foreach ($_POST["val"] as $unique_idf => $row) {
$set = array(); $set = array();
foreach ($row as $key => $val) { foreach ($row as $key => $val) {
$key = bracket_escape($key, 1); // 1 - back $key = bracket_escape($key, true); // true - back
$set[idf_escape($key)] = (preg_match('~char|text~', $fields[$key]["type"]) || $val != "" ? $adminer->processInput($fields[$key], $val) : "NULL"); $set[idf_escape($key)] = (preg_match('~char|text~', $fields[$key]["type"]) || $val != "" ? adminer()->processInput($fields[$key], $val) : "NULL");
} }
$result = $driver->update( $result = driver()->update(
$TABLE, $TABLE,
$set, $set,
" WHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($unique_idf, $fields), " WHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($unique_idf, $fields),
!$is_group && !$primary, ($is_group || $primary ? 0 : 1),
" " " "
); );
if (!$result) { if (!$result) {
break; break;
} }
$affected += $connection->affected_rows; $affected += connection()->affected_rows;
} }
queries_redirect(remove_from_uri(), lang('%d item(s) have been affected.', $affected), $result); queries_redirect(remove_from_uri(), lang('%d item(s) have been affected.', $affected), $result);
} }
@@ -200,7 +201,7 @@ if ($_POST && !$error) {
$cols = array_keys($fields); $cols = array_keys($fields);
preg_match_all('~(?>"[^"]*"|[^"\r\n]+)+~', $file, $matches); preg_match_all('~(?>"[^"]*"|[^"\r\n]+)+~', $file, $matches);
$affected = count($matches[0]); $affected = count($matches[0]);
$driver->begin(); driver()->begin();
$separator = ($_POST["separator"] == "csv" ? "," : ($_POST["separator"] == "tsv" ? "\t" : ";")); $separator = ($_POST["separator"] == "csv" ? "," : ($_POST["separator"] == "tsv" ? "\t" : ";"));
$rows = array(); $rows = array();
foreach ($matches[0] as $key => $val) { foreach ($matches[0] as $key => $val) {
@@ -217,18 +218,18 @@ if ($_POST && !$error) {
$rows[] = $set; $rows[] = $set;
} }
} }
$result = (!$rows || $driver->insertUpdate($TABLE, $rows, $primary)); $result = (!$rows || driver()->insertUpdate($TABLE, $rows, $primary));
if ($result) { if ($result) {
$driver->commit(); driver()->commit();
} }
queries_redirect(remove_from_uri("page"), lang('%d row(s) have been imported.', $affected), $result); queries_redirect(remove_from_uri("page"), lang('%d row(s) have been imported.', $affected), $result);
$driver->rollback(); // after queries_redirect() to not overwrite error driver()->rollback(); // after queries_redirect() to not overwrite error
} }
} }
} }
$table_name = $adminer->tableName($table_status); $table_name = adminer()->tableName($table_status);
if (is_ajax()) { if (is_ajax()) {
page_headers(); page_headers();
ob_start(); ob_start();
@@ -250,7 +251,7 @@ if (isset($rights["insert"]) || !support("table")) {
$set = $params ? "&" . http_build_query($params) : ""; $set = $params ? "&" . http_build_query($params) : "";
} }
$adminer->selectLinks($table_status, $set); adminer()->selectLinks($table_status, $set);
if (!$columns && support("table")) { if (!$columns && support("table")) {
echo "<p class='error'>" . lang('Unable to select the table') . ($fields ? "." : ": " . error()) . "\n"; echo "<p class='error'>" . lang('Unable to select the table') . ($fields ? "." : ": " . error()) . "\n";
@@ -261,18 +262,19 @@ if (!$columns && support("table")) {
echo (DB != "" ? input_hidden("db", DB) . (isset($_GET["ns"]) ? input_hidden("ns", $_GET["ns"]) : "") : ""); // not used in Editor echo (DB != "" ? input_hidden("db", DB) . (isset($_GET["ns"]) ? input_hidden("ns", $_GET["ns"]) : "") : ""); // not used in Editor
echo input_hidden("select", $TABLE); echo input_hidden("select", $TABLE);
echo "</div>\n"; echo "</div>\n";
$adminer->selectColumnsPrint($select, $columns); adminer()->selectColumnsPrint($select, $columns);
$adminer->selectSearchPrint($where, $search_columns, $indexes); adminer()->selectSearchPrint($where, $search_columns, $indexes);
$adminer->selectOrderPrint($order, $order_columns, $indexes); adminer()->selectOrderPrint($order, $order_columns, $indexes);
$adminer->selectLimitPrint($limit); adminer()->selectLimitPrint($limit);
$adminer->selectLengthPrint($text_length); adminer()->selectLengthPrint($text_length);
$adminer->selectActionPrint($indexes); adminer()->selectActionPrint($indexes);
echo "</form>\n"; echo "</form>\n";
$page = $_GET["page"]; $page = $_GET["page"];
$found_rows = null;
if ($page == "last") { if ($page == "last") {
$found_rows = get_val(count_rows($TABLE, $where, $is_group, $group)); $found_rows = get_val(count_rows($TABLE, $where, $is_group, $group));
$page = floor(max(0, $found_rows - 1) / $limit); $page = floor(max(0, intval($found_rows) - 1) / $limit);
} }
$select2 = $select; $select2 = $select;
@@ -298,7 +300,7 @@ if (!$columns && support("table")) {
} }
} }
} }
$result = $driver->select($TABLE, $select2, $where, $group2, $order, $limit, $page, true); $result = driver()->select($TABLE, $select2, $where, $group2, $order, $limit, $page, true);
if (!$result) { if (!$result) {
echo "<p class='error'>" . error() . "\n"; echo "<p class='error'>" . error() . "\n";
@@ -317,14 +319,14 @@ if (!$columns && support("table")) {
} }
// use count($rows) without LIMIT, COUNT(*) without grouping, FOUND_ROWS otherwise (slowest) // use count($rows) without LIMIT, COUNT(*) without grouping, FOUND_ROWS otherwise (slowest)
if ($_GET["page"] != "last" && $limit != "" && $group && $is_group && JUSH == "sql") { if ($_GET["page"] != "last" && $limit && $group && $is_group && JUSH == "sql") {
$found_rows = get_val(" SELECT FOUND_ROWS()"); // space to allow mysql.trace_mode $found_rows = get_val(" SELECT FOUND_ROWS()"); // space to allow mysql.trace_mode
} }
if (!$rows) { if (!$rows) {
echo "<p class='message'>" . lang('No rows.') . "\n"; echo "<p class='message'>" . lang('No rows.') . "\n";
} else { } else {
$backward_keys = $adminer->backwardKeys($TABLE, $table_name); $backward_keys = adminer()->backwardKeys($TABLE, $table_name);
echo "<div class='scrollable'>"; echo "<div class='scrollable'>";
echo "<table id='table' class='nowrap checkable odds'>"; echo "<table id='table' class='nowrap checkable odds'>";
@@ -339,18 +341,19 @@ if (!$columns && support("table")) {
$rank = 1; $rank = 1;
foreach ($rows[0] as $key => $val) { foreach ($rows[0] as $key => $val) {
if (!isset($unselected[$key])) { if (!isset($unselected[$key])) {
$val = $_GET["columns"][key($select)]; /** @var array{fun?:string, col?:string} */
$val = idx($_GET["columns"], key($select)) ?: array();
$field = $fields[$select ? ($val ? $val["col"] : current($select)) : $key]; $field = $fields[$select ? ($val ? $val["col"] : current($select)) : $key];
$name = ($field ? $adminer->fieldName($field, $rank) : ($val["fun"] ? "*" : h($key))); $name = ($field ? adminer()->fieldName($field, $rank) : ($val["fun"] ? "*" : h($key)));
if ($name != "") { if ($name != "") {
$rank++; $rank++;
$names[$key] = $name; $names[$key] = $name;
$column = idf_escape($key); $column = idf_escape($key);
$href = remove_from_uri('(order|desc)[^=]*|page') . '&order%5B0%5D=' . urlencode($key); $href = remove_from_uri('(order|desc)[^=]*|page') . '&order%5B0%5D=' . urlencode($key);
$desc = "&desc%5B0%5D=1"; $desc = "&desc%5B0%5D=1";
$sortable = isset($field["privileges"]["order"]);
echo "<th id='th[" . h(bracket_escape($key)) . "]'>" . script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});", ""); echo "<th id='th[" . h(bracket_escape($key)) . "]'>" . script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});", "");
$fun = apply_sql_function($val["fun"], $name); //! columns looking like functions $fun = apply_sql_function($val["fun"], $name); //! columns looking like functions
$sortable = isset($field["privileges"]["order"]) || $fun;
echo ($sortable ? '<a href="' . h($href . ($order[0] == $column || $order[0] == $key || (!$order && $is_group && $group[0] == $column) ? $desc : '')) . '">' . "$fun</a>" : $fun); // $order[0] == $key - COUNT(*) echo ($sortable ? '<a href="' . h($href . ($order[0] == $column || $order[0] == $key || (!$order && $is_group && $group[0] == $column) ? $desc : '')) . '">' . "$fun</a>" : $fun); // $order[0] == $key - COUNT(*)
echo "<span class='column hidden'>"; echo "<span class='column hidden'>";
if ($sortable) { if ($sortable) {
@@ -382,7 +385,7 @@ if (!$columns && support("table")) {
ob_end_clean(); ob_end_clean();
} }
foreach ($adminer->rowDescriptions($rows, $foreign_keys) as $n => $row) { foreach (adminer()->rowDescriptions($rows, $foreign_keys) as $n => $row) {
$unique_array = unique_array($rows[$n], $indexes); $unique_array = unique_array($rows[$n], $indexes);
if (!$unique_array) { if (!$unique_array) {
$unique_array = array(); $unique_array = array();
@@ -394,9 +397,10 @@ if (!$columns && support("table")) {
} }
$unique_idf = ""; $unique_idf = "";
foreach ($unique_array as $key => $val) { foreach ($unique_array as $key => $val) {
if ((JUSH == "sql" || JUSH == "pgsql") && preg_match('~char|text|enum|set~', $fields[$key]["type"]) && strlen($val) > 64) { $field = (array) $fields[$key];
if ((JUSH == "sql" || JUSH == "pgsql") && preg_match('~char|text|enum|set~', $field["type"]) && strlen($val) > 64) {
$key = (strpos($key, '(') ? $key : idf_escape($key)); //! columns looking like functions $key = (strpos($key, '(') ? $key : idf_escape($key)); //! columns looking like functions
$key = "MD5(" . (JUSH != 'sql' || preg_match("~^utf8~", $fields[$key]["collation"]) ? $key : "CONVERT($key USING " . charset($connection) . ")") . ")"; $key = "MD5(" . (JUSH != 'sql' || preg_match("~^utf8~", $field["collation"]) ? $key : "CONVERT($key USING " . charset(connection()) . ")") . ")";
$val = md5($val); $val = md5($val);
} }
$unique_idf .= "&" . ($val !== null ? urlencode("where[" . bracket_escape($key) . "]") . "=" . urlencode($val === false ? "f" : $val) : "null%5B%5D=" . urlencode($key)); $unique_idf .= "&" . ($val !== null ? urlencode("where[" . bracket_escape($key) . "]") . "=" . urlencode($val === false ? "f" : $val) : "null%5B%5D=" . urlencode($key));
@@ -408,8 +412,8 @@ if (!$columns && support("table")) {
foreach ($row as $key => $val) { foreach ($row as $key => $val) {
if (isset($names[$key])) { if (isset($names[$key])) {
$field = $fields[$key]; $field = (array) $fields[$key];
$val = $driver->value($val, $field); $val = driver()->value($val, $field);
if ($val != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) { if ($val != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
$email_fields[$key] = (is_mail($val) ? $names[$key] : ""); //! filled e-mails can be contained on other pages $email_fields[$key] = (is_mail($val) ? $names[$key] : ""); //! filled e-mails can be contained on other pages
} }
@@ -448,20 +452,20 @@ if (!$columns && support("table")) {
} }
} }
$val = select_value($val, $link, $field, $text_length); $html = select_value($val, $link, $field, $text_length);
$id = h("val[$unique_idf][" . bracket_escape($key) . "]"); $id = h("val[$unique_idf][" . bracket_escape($key) . "]");
$value = $_POST["val"][$unique_idf][bracket_escape($key)]; $posted = idx(idx($_POST["val"], $unique_idf), bracket_escape($key));
$editable = !is_array($row[$key]) && is_utf8($val) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"]; $editable = !is_array($row[$key]) && is_utf8($html) && $rows[$n][$key] == $row[$key] && !$functions[$key] && !$field["generated"];
$text = preg_match('~text|json|lob~', $field["type"]); $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'" : ""); echo "<td id='$id'" . (preg_match(number_type(), $field["type"]) && ($val === null || is_numeric(strip_tags($html))) ? " class='number'" : "");
if (($_GET["modify"] && $editable) || $value !== null) { if (($_GET["modify"] && $editable && $val !== null) || $posted !== null) {
$h_value = h($value !== null ? $value : $row[$key]); $h_value = h($posted !== null ? $posted : $row[$key]);
echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>"); echo ">" . ($text ? "<textarea name='$id' cols='30' rows='" . (substr_count($row[$key], "\n") + 1) . "'>$h_value</textarea>" : "<input name='$id' value='$h_value' size='$lengths[$key]'>");
} else { } else {
$long = strpos($val, "<i>…</i>"); $long = strpos($html, "<i>…</i>");
echo " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'" echo " data-text='" . ($long ? 2 : ($text ? 1 : 0)) . "'"
. ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'") . ($editable ? "" : " data-warning='" . h(lang('Use edit link to modify this value.')) . "'")
. ">$val" . ">$html"
; ;
} }
} }
@@ -470,7 +474,7 @@ if (!$columns && support("table")) {
if ($backward_keys) { if ($backward_keys) {
echo "<td>"; echo "<td>";
} }
$adminer->backwardKeysPrint($backward_keys, $rows[$n]); adminer()->backwardKeysPrint($backward_keys, $rows[$n]);
echo "</tr>\n"; // close to allow white-space: pre echo "</tr>\n"; // close to allow white-space: pre
} }
@@ -485,11 +489,11 @@ if (!$columns && support("table")) {
if ($rows || $page) { if ($rows || $page) {
$exact_count = true; $exact_count = true;
if ($_GET["page"] != "last") { if ($_GET["page"] != "last") {
if ($limit == "" || (count($rows) < $limit && ($rows || !$page))) { if (!$limit || (count($rows) < $limit && ($rows || !$page))) {
$found_rows = ($page ? $page * $limit : 0) + count($rows); $found_rows = ($page ? $page * $limit : 0) + count($rows);
} elseif (JUSH != "sql" || !$is_group) { } elseif (JUSH != "sql" || !$is_group) {
$found_rows = ($is_group ? false : found_rows($table_status, $where)); $found_rows = ($is_group ? false : found_rows($table_status, $where));
if ($found_rows < max(1e4, 2 * ($page + 1) * $limit)) { if (intval($found_rows) < max(1e4, 2 * ($page + 1) * $limit)) {
// slow with big tables // slow with big tables
$found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group))); $found_rows = first(slow_query(count_rows($TABLE, $where, $is_group, $group)));
} else { } else {
@@ -498,19 +502,17 @@ if (!$columns && support("table")) {
} }
} }
$pagination = ($limit != "" && ($found_rows === false || $found_rows > $limit || $page)); $pagination = ($limit && ($found_rows === false || $found_rows > $limit || $page));
if ($pagination) { if ($pagination) {
echo (($found_rows === false ? count($rows) + 1 : $found_rows - $page * $limit) > $limit echo (($found_rows === false ? count($rows) + 1 : $found_rows - $page * $limit) > $limit
? '<p><a href="' . h(remove_from_uri("page") . "&page=" . ($page + 1)) . '" class="loadmore">' . lang('Load more data') . '</a>' ? '<p><a href="' . h(remove_from_uri("page") . "&page=" . ($page + 1)) . '" class="loadmore">' . lang('Load more data') . '</a>'
. script("qsl('a').onclick = partial(selectLoadMore, " . (+$limit) . ", '" . lang('Loading') . "…');", "") . script("qsl('a').onclick = partial(selectLoadMore, $limit, '" . lang('Loading') . "…');", "")
: '' : ''
); );
echo "\n"; echo "\n";
} }
}
echo "<div class='footer'><div>\n"; echo "<div class='footer'><div>\n";
if ($rows || $page) {
if ($pagination) { if ($pagination) {
// display first, previous 4, next 4 and last page // display first, previous 4, next 4 and last page
$max_page = ($found_rows === false $max_page = ($found_rows === false
@@ -548,7 +550,7 @@ if (!$columns && support("table")) {
echo checkbox("all", 1, 0, ($found_rows !== false ? ($exact_count ? "" : "~ ") . lang('%d row(s)', $found_rows) : ""), $onclick) . "\n"; echo checkbox("all", 1, 0, ($found_rows !== false ? ($exact_count ? "" : "~ ") . lang('%d row(s)', $found_rows) : ""), $onclick) . "\n";
echo "</fieldset>\n"; echo "</fieldset>\n";
if ($adminer->selectCommandPrint()) { if (adminer()->selectCommandPrint()) {
?> ?>
<fieldset<?php echo ($_GET["modify"] ? '' : ' class="jsonly"'); ?>><legend><?php echo lang('Modify'); ?></legend><div> <fieldset<?php echo ($_GET["modify"] ? '' : ' class="jsonly"'); ?>><legend><?php echo lang('Modify'); ?></legend><div>
<input type="submit" value="<?php echo lang('Save'); ?>"<?php echo ($_GET["modify"] ? '' : ' title="' . lang('Ctrl+click on a value to modify it.') . '"'); ?>> <input type="submit" value="<?php echo lang('Save'); ?>"<?php echo ($_GET["modify"] ? '' : ' title="' . lang('Ctrl+click on a value to modify it.') . '"'); ?>>
@@ -561,7 +563,7 @@ if (!$columns && support("table")) {
<?php <?php
} }
$format = $adminer->dumpFormat(); $format = adminer()->dumpFormat();
foreach ((array) $_GET["columns"] as $column) { foreach ((array) $_GET["columns"] as $column) {
if ($column["fun"]) { if ($column["fun"]) {
unset($format['sql']); unset($format['sql']);
@@ -570,20 +572,19 @@ if (!$columns && support("table")) {
} }
if ($format) { if ($format) {
print_fieldset("export", lang('Export') . " <span id='selected2'></span>"); print_fieldset("export", lang('Export') . " <span id='selected2'></span>");
$output = $adminer->dumpOutput(); $output = adminer()->dumpOutput();
echo ($output ? html_select("output", $output, $adminer_import["output"]) . " " : ""); echo ($output ? html_select("output", $output, $adminer_import["output"]) . " " : "");
echo html_select("format", $format, $adminer_import["format"]); echo html_select("format", $format, $adminer_import["format"]);
echo " <input type='submit' name='export' value='" . lang('Export') . "'>\n"; echo " <input type='submit' name='export' value='" . lang('Export') . "'>\n";
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
} }
$adminer->selectEmailPrint(array_filter($email_fields, 'strlen'), $columns); adminer()->selectEmailPrint(array_filter($email_fields, 'strlen'), $columns);
echo "</div></div>\n";
} }
echo "</div></div>\n"; if (adminer()->selectImportPrint()) {
echo "<p>";
if ($adminer->selectImportPrint()) {
echo "<div>";
echo "<a href='#import'>" . lang('Import') . "</a>"; echo "<a href='#import'>" . lang('Import') . "</a>";
echo script("qsl('a').onclick = partial(toggle, 'import');", ""); echo script("qsl('a').onclick = partial(toggle, 'import');", "");
echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: "; echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: ";
@@ -591,7 +592,6 @@ if (!$columns && support("table")) {
echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]); echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]);
echo " <input type='submit' name='import' value='" . lang('Import') . "'>"; echo " <input type='submit' name='import' value='" . lang('Import') . "'>";
echo "</span>"; echo "</span>";
echo "</div>";
} }
echo input_token(); echo input_token();

View File

@@ -4,9 +4,9 @@ namespace Adminer;
if (!$error && $_POST["export"]) { if (!$error && $_POST["export"]) {
save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import"); save_settings(array("output" => $_POST["output"], "format" => $_POST["format"]), "adminer_import");
dump_headers("sql"); dump_headers("sql");
$adminer->dumpTable("", ""); adminer()->dumpTable("", "");
$adminer->dumpData("", "table", $_POST["query"]); adminer()->dumpData("", "table", $_POST["query"]);
$adminer->dumpFooter(); adminer()->dumpFooter();
exit; exit;
} }
@@ -17,15 +17,17 @@ if (!$error && $_POST["clear"]) {
$history = array(); $history = array();
redirect(remove_from_uri("history")); redirect(remove_from_uri("history"));
} }
stop_session();
page_header((isset($_GET["import"]) ? lang('Import') : lang('SQL command')), $error); page_header((isset($_GET["import"]) ? lang('Import') : lang('SQL command')), $error);
$line_comment = '--' . (JUSH == 'sql' ? ' ' : '');
if (!$error && $_POST) { if (!$error && $_POST) {
$fp = false; $fp = false;
if (!isset($_GET["import"])) { if (!isset($_GET["import"])) {
$query = $_POST["query"]; $query = $_POST["query"];
} elseif ($_POST["webfile"]) { } elseif ($_POST["webfile"]) {
$sql_file_path = $adminer->importServerPath(); $sql_file_path = adminer()->importServerPath();
$fp = @fopen((file_exists($sql_file_path) $fp = @fopen((file_exists($sql_file_path)
? $sql_file_path ? $sql_file_path
: "compress.zlib://$sql_file_path.gz" : "compress.zlib://$sql_file_path.gz"
@@ -37,7 +39,7 @@ if (!$error && $_POST) {
if (is_string($query)) { // get_file() returns error as number, fread() as false if (is_string($query)) { // get_file() returns error as number, fread() as false
if (function_exists('memory_get_usage') && ($memory_limit = ini_bytes("memory_limit")) != "-1") { if (function_exists('memory_get_usage') && ($memory_limit = ini_bytes("memory_limit")) != "-1") {
@ini_set("memory_limit", max($memory_limit, 2 * strlen($query) + memory_get_usage() + 8e6)); // @ - may be disabled, 2 - substr and trim, 8e6 - other variables @ini_set("memory_limit", max($memory_limit, strval(2 * strlen($query) + memory_get_usage() + 8e6))); // @ - may be disabled, 2 - substr and trim, 8e6 - other variables
} }
if ($query != "" && strlen($query) < 1e6) { // don't add big queries if ($query != "" && strlen($query) < 1e6) { // don't add big queries
@@ -50,12 +52,12 @@ if (!$error && $_POST) {
} }
} }
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)"; $space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$line_comment)[^\n]*\n?|--\r?\n)";
$delimiter = ";"; $delimiter = ";";
$offset = 0; $offset = 0;
$empty = true; $empty = true;
$connection2 = connect($adminer->credentials()); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error $connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
if (is_object($connection2) && DB != "") { if ($connection2 && DB != "") {
$connection2->select_db(DB); $connection2->select_db(DB);
if ($_GET["ns"] != "") { if ($_GET["ns"] != "") {
set_schema($_GET["ns"], $connection2); set_schema($_GET["ns"], $connection2);
@@ -63,18 +65,21 @@ if (!$error && $_POST) {
} }
$commands = 0; $commands = 0;
$errors = array(); $errors = array();
$parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|-- |$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : ''); $parse = '[\'"' . (JUSH == "sql" ? '`#' : (JUSH == "sqlite" ? '`[' : (JUSH == "mssql" ? '[' : ''))) . ']|/\*|' . $line_comment . '|$' . (JUSH == "pgsql" ? '|\$[^$]*\$' : '');
$total_start = microtime(true); $total_start = microtime(true);
$adminer_export = get_settings("adminer_import"); // this doesn't offer SQL export so we match the import/export style at select $adminer_export = get_settings("adminer_import"); // this doesn't offer SQL export so we match the import/export style at select
$dump_format = $adminer->dumpFormat(); $dump_format = adminer()->dumpFormat();
unset($dump_format["sql"]); unset($dump_format["sql"]);
while ($query != "") { while ($query != "") {
if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) { if (!$offset && preg_match("~^$space*+DELIMITER\\s+(\\S+)~i", $query, $match)) {
$delimiter = $match[1]; $delimiter = preg_quote($match[1]);
$query = substr($query, strlen($match[0])); $query = substr($query, strlen($match[0]));
} elseif (!$offset && JUSH == 'pgsql' && preg_match("~^($space*+COPY\\s+)[^;]+\\s+FROM\\s+stdin;~i", $query, $match)) {
$delimiter = "\n\\\\\\.\r?\n";
$offset = strlen($match[0]);
} else { } else {
preg_match('(' . preg_quote($delimiter) . "\\s*|$parse)", $query, $match, PREG_OFFSET_CAPTURE, $offset); // should always match preg_match("($delimiter\\s*|$parse)", $query, $match, PREG_OFFSET_CAPTURE, $offset); // always matches
list($found, $pos) = $match[0]; list($found, $pos) = $match[0];
if (!$found && $fp && !feof($fp)) { if (!$found && $fp && !feof($fp)) {
$query .= fread($fp, 1e5); $query .= fread($fp, 1e5);
@@ -84,14 +89,15 @@ if (!$error && $_POST) {
} }
$offset = $pos + strlen($found); $offset = $pos + strlen($found);
if ($found && rtrim($found) != $delimiter) { // find matching quote or comment end if ($found && !preg_match("(^$delimiter)", $found)) { // find matching quote or comment end
$c_style_escapes = $driver->hasCStyleEscapes() || (JUSH == "pgsql" && ($pos > 0 && strtolower($query[$pos - 1]) == "e")); $c_style_escapes = driver()->hasCStyleEscapes() || (JUSH == "pgsql" && ($pos > 0 && strtolower($query[$pos - 1]) == "e"));
$pattern = ($found == '/*' ? '\*/' $pattern =
: ($found == '[' ? ']' ($found == '/*' ? '\*/' :
: (preg_match('~^-- |^#~', $found) ? "\n" ($found == '[' ? ']' :
: preg_quote($found) . ($c_style_escapes ? "|\\\\." : ""))) (preg_match("~^$line_comment|^#~", $found) ? "\n" :
); preg_quote($found) . ($c_style_escapes ? '|\\\\.' : ''))))
;
while (preg_match("($pattern|\$)s", $query, $match, PREG_OFFSET_CAPTURE, $offset)) { while (preg_match("($pattern|\$)s", $query, $match, PREG_OFFSET_CAPTURE, $offset)) {
$s = $match[0][0]; $s = $match[0][0];
@@ -107,9 +113,9 @@ if (!$error && $_POST) {
} else { // end of a query } else { // end of a query
$empty = false; $empty = false;
$q = substr($query, 0, $pos); $q = substr($query, 0, $pos + ($delimiter[0] == "\n" ? 3 : 0)); // 3 - pass "\n\\." to PostgreSQL COPY
$commands++; $commands++;
$print = "<pre id='sql-$commands'><code class='jush-" . JUSH . "'>" . $adminer->sqlCommandQuery($q) . "</code></pre>\n"; $print = "<pre id='sql-$commands'><code class='jush-" . JUSH . "'>" . adminer()->sqlCommandQuery($q) . "</code></pre>\n";
if (JUSH == "sqlite" && preg_match("~^$space*+ATTACH\\b~i", $q, $match)) { if (JUSH == "sqlite" && preg_match("~^$space*+ATTACH\\b~i", $q, $match)) {
// PHP doesn't support setting SQLITE_LIMIT_ATTACHED // PHP doesn't support setting SQLITE_LIMIT_ATTACHED
echo $print; echo $print;
@@ -126,16 +132,16 @@ if (!$error && $_POST) {
} }
$start = microtime(true); $start = microtime(true);
//! don't allow changing of character_set_results, convert encoding of displayed query //! don't allow changing of character_set_results, convert encoding of displayed query
if ($connection->multi_query($q) && is_object($connection2) && preg_match("~^$space*+USE\\b~i", $q)) { if (connection()->multi_query($q) && $connection2 && preg_match("~^$space*+USE\\b~i", $q)) {
$connection2->query($q); $connection2->query($q);
} }
do { do {
$result = $connection->store_result(); $result = connection()->store_result();
if ($connection->error) { if (connection()->error) {
echo ($_POST["only_errors"] ? $print : ""); echo ($_POST["only_errors"] ? $print : "");
echo "<p class='error'>" . lang('Error in query') . ($connection->errno ? " ($connection->errno)" : "") . ": " . error() . "\n"; echo "<p class='error'>" . lang('Error in query') . (connection()->errno ? " (" . connection()->errno . ")" : "") . ": " . error() . "\n";
$errors[] = " <a href='#sql-$commands'>$commands</a>"; $errors[] = " <a href='#sql-$commands'>$commands</a>";
if ($_POST["error_stops"]) { if ($_POST["error_stops"]) {
break 2; break 2;
@@ -145,17 +151,18 @@ if (!$error && $_POST) {
$time = " <span class='time'>(" . format_time($start) . ")</span>" $time = " <span class='time'>(" . format_time($start) . ")</span>"
. (strlen($q) < 1000 ? " <a href='" . h(ME) . "sql=" . urlencode(trim($q)) . "'>" . lang('Edit') . "</a>" : "") // 1000 - maximum length of encoded URL in IE is 2083 characters . (strlen($q) < 1000 ? " <a href='" . h(ME) . "sql=" . urlencode(trim($q)) . "'>" . lang('Edit') . "</a>" : "") // 1000 - maximum length of encoded URL in IE is 2083 characters
; ;
$affected = $connection->affected_rows; // getting warnings overwrites this $affected = connection()->affected_rows; // getting warnings overwrites this
$warnings = ($_POST["only_errors"] ? "" : $driver->warnings()); $warnings = ($_POST["only_errors"] ? "" : driver()->warnings());
$warnings_id = "warnings-$commands"; $warnings_id = "warnings-$commands";
if ($warnings) { if ($warnings) {
$time .= ", <a href='#$warnings_id'>" . lang('Warnings') . "</a>" . script("qsl('a').onclick = partial(toggle, '$warnings_id');", ""); $time .= ", <a href='#$warnings_id'>" . lang('Warnings') . "</a>" . script("qsl('a').onclick = partial(toggle, '$warnings_id');", "");
} }
$explain = null; $explain = null;
$orgtables = null;
$explain_id = "explain-$commands"; $explain_id = "explain-$commands";
if (is_object($result)) { if (is_object($result)) {
$limit = $_POST["limit"]; $limit = $_POST["limit"];
$orgtables = select($result, $connection2, array(), $limit); $orgtables = print_select_result($result, $connection2, array(), $limit);
if (!$_POST["only_errors"]) { if (!$_POST["only_errors"]) {
echo "<form action='' method='post'>\n"; echo "<form action='' method='post'>\n";
$num_rows = $result->num_rows; $num_rows = $result->num_rows;
@@ -166,7 +173,7 @@ if (!$error && $_POST) {
} }
$id = "export-$commands"; $id = "export-$commands";
echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: " echo ", <a href='#$id'>" . lang('Export') . "</a>" . script("qsl('a').onclick = partial(toggle, '$id');", "") . "<span id='$id' class='hidden'>: "
. html_select("output", $adminer->dumpOutput(), $adminer_export["output"]) . " " . html_select("output", adminer()->dumpOutput(), $adminer_export["output"]) . " "
. html_select("format", $dump_format, $adminer_export["format"]) . html_select("format", $dump_format, $adminer_export["format"])
. input_hidden("query", $q) . input_hidden("query", $q)
. "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n" . "<input type='submit' name='export' value='" . lang('Export') . "'>" . input_token() . "</span>\n"
@@ -181,19 +188,19 @@ if (!$error && $_POST) {
stop_session(); stop_session();
} }
if (!$_POST["only_errors"]) { if (!$_POST["only_errors"]) {
echo "<p class='message' title='" . h(isset($connection->info) ? $connection->info : "") . "'>" . lang('Query executed OK, %d row(s) affected.', $affected) . "$time\n"; echo "<p class='message' title='" . h(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" : ""); echo ($warnings ? "<div id='$warnings_id' class='hidden'>\n$warnings</div>\n" : "");
if ($explain) { if ($explain) {
echo "<div id='$explain_id' class='hidden explain'>\n"; echo "<div id='$explain_id' class='hidden explain'>\n";
select($explain, $connection2, $orgtables); print_select_result($explain, $connection2, $orgtables);
echo "</div>\n"; echo "</div>\n";
} }
} }
$start = microtime(true); $start = microtime(true);
} while ($connection->next_result()); } while (connection()->next_result());
} }
$query = substr($query, $offset); $query = substr($query, $offset);
@@ -230,13 +237,13 @@ if (!isset($_GET["import"])) {
} elseif ($_GET["history"] == "all") { } elseif ($_GET["history"] == "all") {
$q = $history; $q = $history;
} elseif ($_GET["history"] != "") { } elseif ($_GET["history"] != "") {
$q = $history[$_GET["history"]][0]; $q = idx($history[$_GET["history"]], 0);
} }
echo "<p>"; echo "<p>";
textarea("query", $q, 20); 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 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>"; echo "<p>";
$adminer->sqlPrintAfter(); adminer()->sqlPrintAfter();
echo "$execute\n"; echo "$execute\n";
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n"; echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";
@@ -248,7 +255,7 @@ if (!isset($_GET["import"])) {
: lang('File uploads are disabled.') : lang('File uploads are disabled.')
); );
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
$importServerPath = $adminer->importServerPath(); $importServerPath = adminer()->importServerPath();
if ($importServerPath) { if ($importServerPath) {
echo "<fieldset><legend>" . lang('From server') . "</legend><div>"; echo "<fieldset><legend>" . lang('From server') . "</legend><div>";
echo lang('Webserver file %s', "<code>" . h($importServerPath) . "$gz</code>"); echo lang('Webserver file %s', "<code>" . h($importServerPath) . "$gz</code>");
@@ -269,7 +276,7 @@ if (!isset($_GET["import"]) && $history) {
list($q, $time, $elapsed) = $val; list($q, $time, $elapsed) = $val;
echo '<a href="' . h(ME . "sql=&history=$key") . '">' . lang('Edit') . "</a>" echo '<a href="' . h(ME . "sql=&history=$key") . '">' . lang('Edit') . "</a>"
. " <span class='time' title='" . @date('Y-m-d', $time) . "'>" . @date("H:i:s", $time) . "</span>" // @ - time zone may be not set . " <span class='time' title='" . @date('Y-m-d', $time) . "'>" . @date("H:i:s", $time) . "</span>" // @ - time zone may be not set
. " <code class='jush-" . JUSH . "'>" . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace('~^(#|-- ).*~m', '', $q)))), 80, "</code>") . " <code class='jush-" . JUSH . "'>" . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace("~^(#|$line_comment).*~m", '', $q)))), 80, "</code>")
. ($elapsed ? " <span class='time'>($elapsed)</span>" : "") . ($elapsed ? " <span class='time'>($elapsed)</span>" : "")
. "<br>\n" . "<br>\n"
; ;

View File

@@ -1,8 +1,7 @@
<?php <?php
function adminer_object() { function adminer_object() {
include_once "../plugins/plugin.php";
include_once "../plugins/login-password-less.php"; include_once "../plugins/login-password-less.php";
return new AdminerPlugin(array( return new Adminer\Plugins(array(
// TODO: inline the result of password_hash() so that the password is not visible in source codes // TODO: inline the result of password_hash() so that the password is not visible in source codes
new AdminerLoginPasswordLess(password_hash("YOUR_PASSWORD_HERE", PASSWORD_DEFAULT)), new AdminerLoginPasswordLess(password_hash("YOUR_PASSWORD_HERE", PASSWORD_DEFAULT)),
)); ));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 B

View File

@@ -1,10 +1,15 @@
/** @author Robert Mesaros, https://www.rmsoft.sk */ /** @author Robert Mesaros, https://www.rmsoft.sk */
body { color: #829bb0; background: #002240; } html {
a { color: #517fa8; } --bg: #002240;
a:visited { color: #517fa8; } --fg: #829bb0;
--dim: #154269;
}
a { color: #618CB3; }
a:visited { color: #618CB3; }
a:link:hover, a:visited:hover { color: #9bc0e1; } a:link:hover, a:visited:hover { color: #9bc0e1; }
h1 { border-color: #5e94c1; color: #ffddbf; background: #154269; } h1 { border-color: #5e94c1; color: #ffddbf; }
h2 { border-color: #a3bdd3; color: #000; background: #3c678d; } h2 { border-color: #a3bdd3; color: #000; background: #3c678d; }
table, td, th { border-color: #0e416d; } table, td, th { border-color: #0e416d; }
th { background: #11385a; } th { background: #11385a; }
@@ -21,7 +26,6 @@ input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
.error { color: red; background: #efdada; border: 1px solid #e76f6f; } .error { color: red; background: #efdada; border: 1px solid #e76f6f; }
.error b { background: #efeaea; } .error b { background: #efeaea; }
.message { color: #0b860b; background: #efe; border: 1px solid #7fbd7f; } .message { color: #0b860b; background: #efe; border: 1px solid #7fbd7f; }
.message table { color: #829bb0; background: #002240; }
.char { color: #a949a9; } .char { color: #a949a9; }
.date { color: #59c159; } .date { color: #59c159; }
.enum { color: #d55c5c; } .enum { color: #d55c5c; }
@@ -30,13 +34,10 @@ input.required, input.maxlength { box-shadow: 1px 1px 1px red; }
.js .checkable .checked td, .js .checkable .checked th { background: #10395c; color: #67a4a5; } .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:hover td, .js .checkable .checked:hover th { background: #133553; }
.js .checkable .checked a { color: #67a4a5; } .js .checkable .checked a { color: #67a4a5; }
.icon { background-color: #062642; } .icon { filter: invert(1); background-color: #062642; }
.icon:hover { background-color: #d1394e; } .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); } #menu { border-color: #a3bdd3; }
.footer > div { background: #002240; }
#menu p, #logins, #tables { border-color: #326b9c; } #menu p, #logins, #tables { border-color: #326b9c; }
#logins a, #tables a, #tables span { background: #002240; }
#breadcrumb { background: #154269; }
#h1 { color: #ffddbf; } #h1 { color: #ffddbf; }
#version { color: #d2b397; } #version { color: #d2b397; }
#schema .table { border-color: #093459; } #schema .table { border-color: #093459; }

View File

@@ -1,33 +1,39 @@
/** @author Ondrej Valka, http://valka.info */ /** @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; } html {
--bg: #fff;
--fg: #000;
--dim: #eee;
}
body { color: var(--fg); background: var(--bg); font: 90%/1.25 Verdana, Arial, Helvetica, sans-serif; margin: 0; min-width: fit-content; }
a { color: blue; text-decoration: none; } a { color: blue; text-decoration: none; }
a:visited { color: navy; } a:visited { color: navy; }
a:link:hover, a:visited:hover { color: red; text-decoration: underline; } a:link:hover, a:visited:hover { color: red; text-decoration: underline; }
a.text:hover { text-decoration: none; } a.text:hover { text-decoration: none; }
a.jush-help:hover { color: inherit; } a.jush-help:hover { color: inherit; }
h1 { font-size: 150%; margin: 0; padding: .8em 1em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: #eee; } h1 { font-size: 150%; margin: 0; padding: .8em .667em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: var(--dim); }
h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom: 1px solid #000; color: #000; font-weight: normal; background: #ddf; } h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom: 1px solid var(--fg); font-weight: normal; background: #ddf; }
h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; } h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; }
form { margin: 0; } form { margin: 0; }
td table { width: 100%; margin: 0; } td table { width: 100%; margin: 0; }
table { margin: 1em 20px 0 0; font-size: 90%; border-spacing: 0; border-width: 1px 0 0 1px; } table { margin: 1em 20px 0 0; font-size: 90%; border-spacing: 0; border-width: 1px 0 0 1px; }
table, td, th { border-color: #999; border-style: solid; } table, td, th { border-color: #999; border-style: solid; }
td, th { border-width: 0 1px 1px 0; padding: .2em .3em; margin: 0; } td, th { border-width: 0 1px 1px 0; padding: .2em .3em; margin: 0; }
th { background: #eee; text-align: left; } th { background: var(--dim); text-align: left; }
thead { position: sticky; top: 0; } thead { position: sticky; top: 0; }
thead th { text-align: center; padding: .2em .5em; } thead th { text-align: center; padding: .2em .5em; }
thead td, thead th { background: #ddf; } thead td, thead th { background: #ddf; }
fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8em .5em 0 0; border: 1px solid #999; } fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8em .5em 0 0; border: 1px solid #999; border-radius: 5px; }
p { margin: .8em 20px 0 0; } p { margin: .8em 20px 0 0; }
img { vertical-align: middle; border: 0; } img { vertical-align: middle; border: 0; }
td img { max-width: 200px; max-height: 200px; } td img { max-width: 200px; max-height: 200px; }
tbody tr:hover td, tbody tr:hover th { background: #eee; } tbody tr:hover td, tbody tr:hover th { background: var(--dim); }
code { font-size: 110%; padding: 1px 2px; background: #eee; } code { font-size: 110%; padding: 1px 2px; background: var(--dim); }
pre { margin: 1em 0 0; } pre { margin: 1em 0 0; }
td pre { margin: 0; } td pre { margin: 0; }
pre, textarea { font: 110%/1.25 monospace; } pre, textarea { font: 110%/1.25 monospace; }
pre.jush { background: #fff; } pre.jush { background: var(--bg); }
pre code { display: block; font-size: 100%; } pre code { display: block; font-size: 100%; }
input, textarea { box-sizing: border-box; } input, textarea { box-sizing: border-box; }
input, select { vertical-align: middle; } input, select { vertical-align: middle; }
@@ -42,9 +48,9 @@ input.wayoff { left: -1000px; position: absolute; }
.nowrap td, .nowrap th, td.nowrap, p.nowrap { white-space: pre; } .nowrap td, .nowrap th, td.nowrap, p.nowrap { white-space: pre; }
.wrap td { white-space: normal; } .wrap td { white-space: normal; }
.error { color: red; background: #fee; } .error { color: red; background: #fee; }
.error b { background: #fff; font-weight: normal; } .error b { background: var(--bg); font-weight: normal; }
.message { color: green; background: #efe; } .message { color: green; background: #efe; }
.message table { color: #000; background: #fff; } .message table { color: var(--fg); background: var(--bg); }
.error, .message { padding: .5em .8em; margin: 1em 20px 0 0; } .error, .message { padding: .5em .8em; margin: 1em 20px 0 0; }
.char { color: #007F00; } .char { color: #007F00; }
.date { color: #7F007F; } .date { color: #7F007F; }
@@ -61,15 +67,16 @@ input.wayoff { left: -1000px; position: absolute; }
.sqlarea { width: 98%; } .sqlarea { width: 98%; }
.sql-footer { margin-bottom: 2.5em; } .sql-footer { margin-bottom: 2.5em; }
.explain table { white-space: pre; } .explain table { white-space: pre; }
.icon { width: 18px; height: 18px; background-color: navy; } .icon { width: 18px; height: 18px; background: navy center no-repeat; border: 0; vertical-align: middle; }
.icon span { display: none; }
.icon:hover { background-color: red; } .icon:hover { background-color: red; }
.size { width: 7ex; } .size { width: 7ex; }
.help { cursor: help; } .help { cursor: help; }
.footer { position: sticky; bottom: 0; margin-right: -20px; border-top: 20px solid rgba(255, 255, 255, .7); border-image: linear-gradient(rgba(255, 255, 255, .2), #fff) 100% 0; } .footer { position: sticky; bottom: 0; margin: 23px -20px .5em 0; box-shadow: 0 -5px 10px 10px var(--bg); }
.footer > div { background: #fff; padding: 0 0 .5em; } .footer > div { background: var(--bg); padding: 0 0 .5em; }
.footer fieldset { margin-top: 0; } .footer fieldset { margin-top: 0; }
.links a { white-space: nowrap; margin-right: 20px; } .links a { white-space: nowrap; margin-right: 20px; }
.logout { margin-top: .5em; position: absolute; top: 0; right: 0; } .logout { margin-top: .5em; position: absolute; top: 0; right: 0; background-color: var(--bg); box-shadow: 0 0 5px 5px var(--bg); }
.loadmore { margin-left: 1ex; } .loadmore { margin-left: 1ex; }
/* .edit used in designs */ /* .edit used in designs */
#menu { position: absolute; margin: 10px 0 0; top: 2em; left: 0; width: 19em; } #menu { position: absolute; margin: 10px 0 0; top: 2em; left: 0; width: 19em; }
@@ -77,16 +84,26 @@ input.wayoff { left: -1000px; position: absolute; }
#logins li, #tables li { list-style: none; } #logins li, #tables li { list-style: none; }
#dbs { overflow: hidden; } #dbs { overflow: hidden; }
#logins, #tables { white-space: nowrap; overflow: hidden; } #logins, #tables { white-space: nowrap; overflow: hidden; }
#logins a, #tables a, #tables span { background: #fff; } #logins a, #tables a, #tables span { background: var(--bg); }
#content { margin: 2em 0 0 21em; padding: 10px 20px 20px 0; } #content { margin: 2em 0 0 21em; padding: 10px 20px 20px 0; }
#lang { position: absolute; top: -2.6em; left: 0; padding: .3em 1em; } #lang { position: absolute; top: -2.6em; left: 0; padding: .3em 1em; }
#breadcrumb { white-space: nowrap; position: absolute; top: 0; left: 21em; background: #eee; height: 2em; line-height: 1.8em; padding: 0 1em; margin: 0 0 0 -18px; } #menuopen { display: none; }
#breadcrumb { white-space: nowrap; position: absolute; top: 0; left: 21em; background: var(--dim); height: 2em; line-height: 1.8em; padding: 0 1em; margin: 0 0 0 -18px; }
#logo { vertical-align: baseline; margin-bottom: -3px; }
#h1 { color: #777; text-decoration: none; font-style: italic; } #h1 { color: #777; text-decoration: none; font-style: italic; }
#version { color: red; } #version { color: red; }
#schema { margin-left: 60px; position: relative; user-select: none; -webkit-user-select: none; } #schema { margin-left: 60px; position: relative; user-select: none; -webkit-user-select: none; }
#schema .table { border: 1px solid silver; padding: 0 2px; cursor: move; position: absolute; } #schema .table { border: 1px solid silver; padding: 0 2px; cursor: move; position: absolute; }
#schema .references { position: absolute; } #schema .references { position: absolute; }
#help { position: absolute; border: 1px solid #999; background: #eee; padding: 5px; font-family: monospace; z-index: 1; } #help { position: absolute; border: 1px solid #999; background: var(--dim); padding: 5px; font-family: monospace; z-index: 1; }
/* inlined here and not in compile.php because otherwise the development version flickers a little bit when loading the images */
.icon-up { background-image: url(data:image/gif;base64,R0lGODlhEgASAIEAMe7u7gAAgJmZmQAAACH5BAEAAAEALAAAAAASABIAAQIghI+py+0PTQhRTgrvfRP0nmEVOIoReZphxbauAMfyHBcAOw==); }
.icon-down { background-image: url(data:image/gif;base64,R0lGODlhEgASAIEAMe7u7gAAgJmZmQAAACH5BAEAAAEALAAAAAASABIAAQIghI+py+0PTQjxzCopvltX/lyix0wm2ZwdxraVAMfyHBcAOw==); }
.icon-plus { background-image: url(data:image/gif;base64,R0lGODlhEgASAIEAMe7u7gAAgJmZmQAAACH5BAEAAAEALAAAAAASABIAAQIhhI+py+0PTQjxzCopvm/6rykgCHGVGaFliLXuI8TyTMsFADs=); }
.icon-cross { background-image: url(data:image/gif;base64,R0lGODlhEgASAIEAMe7u7gAAgJmZmQAAACH5BAEAAAEALAAAAAASABIAAQIjhI+py+0PIwph1kZvfnnDLoFfd2GU4THnsUruC0fCTNc2XQAAOw==); }
.icon-move { background-image: url(data:image/gif;base64,R0lGODlhEgASAJEAAO7u7gAAAJmZmQAAACH5BAEAAAEALAAAAAASABIAAAIfhI+py+3vgpyU0Rug3gnX5U3cqIWSZZLqigjuC8dvAQA7); }
#schema .arrow { height: 1.25em; background: url(data:image/gif;base64,R0lGODlhCAAKAIAAAICAgP///yH5BAEAAAEALAAAAAAIAAoAAAIPBIJplrGLnpQRqtOy3rsAADs=) no-repeat right center; }
.rtl h2 { margin: 0 -18px 20px 0; } .rtl h2 { margin: 0 -18px 20px 0; }
.rtl p, .rtl table, .rtl .error, .rtl .message { margin: 1em 0 0 20px; } .rtl p, .rtl table, .rtl .error, .rtl .message { margin: 1em 0 0 20px; }
@@ -98,13 +115,17 @@ input.wayoff { left: -1000px; position: absolute; }
.rtl #lang, .rtl #menu { left: auto; right: 0; } .rtl #lang, .rtl #menu { left: auto; right: 0; }
.rtl pre, .rtl code { direction: ltr; } .rtl pre, .rtl code { direction: ltr; }
@media all and (max-width: 880px) { @media all and (max-width: 800px) {
.pages { left: auto; } .pages { left: auto; }
.logout { position: static; padding: 1em; } .js .logout { top: 1.667em; background-color: var(--dim); box-shadow: 0 0 5px 5px var(--dim); }
#menu { position: static; width: auto; } #menu { position: static; width: auto; min-width: 23em; background: var(--bg); border: 1px solid var(--fg); margin-top: 9px; box-shadow: 0 0 20px -3px var(--fg); }
#content { margin-left: 10px; } #content { margin-left: 10px !important; }
#lang { position: static; } #lang { position: static; }
#breadcrumb { left: auto; } #breadcrumb { left: 48px !important; }
.js #foot { position: absolute; top: 2em; left: 0; }
.js .foot { display: none; }
.js #menuopen { display: block; position: absolute; top: 3px; left: 6px; }
.nojs #menu { position: static; }
.rtl .pages { right: auto; } .rtl .pages { right: auto; }
.rtl #content { margin-right: 10px; } .rtl #content { margin-right: 10px; }
.rtl #breadcrumb { right: auto; } .rtl #breadcrumb { right: auto; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

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