Try to include the database

remotes/origin/database-api-implementation
dorian.hodin 2 years ago committed by felixmielcarek
parent 5788f68519
commit a03edfb813

@ -5,686 +5,686 @@
<entry key="animations">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/animations-2.0.7/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/animations-2.0.7/lib" />
</list>
</value>
</entry>
<entry key="archive">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.3.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.3.2/lib" />
</list>
</value>
</entry>
<entry key="args">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib" />
</list>
</value>
</entry>
<entry key="async">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/async-2.9.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.9.0/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="characters">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.2.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
</list>
</value>
</entry>
<entry key="checked_yaml">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/checked_yaml-2.0.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/checked_yaml-2.0.1/lib" />
</list>
</value>
</entry>
<entry key="cli_util">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.3.5/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.3.5/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
</list>
</value>
</entry>
<entry key="convert">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/convert-3.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/convert-3.1.1/lib" />
</list>
</value>
</entry>
<entry key="crypto">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib" />
</list>
</value>
</entry>
<entry key="csslib">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.17.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.17.2/lib" />
</list>
</value>
</entry>
<entry key="cupertino_icons">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.5/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.5/lib" />
</list>
</value>
</entry>
<entry key="fake_async">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.1/lib" />
</list>
</value>
</entry>
<entry key="ffi">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/ffi-2.0.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-2.0.1/lib" />
</list>
</value>
</entry>
<entry key="file">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib" />
</list>
</value>
</entry>
<entry key="flutter">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/packages/flutter/lib" />
<option value="Z:/flutter/packages/flutter/lib" />
</list>
</value>
</entry>
<entry key="flutter_inappwebview">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-5.7.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-5.7.1/lib" />
</list>
</value>
</entry>
<entry key="flutter_launcher_icons">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_launcher_icons-0.10.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_launcher_icons-0.10.0/lib" />
</list>
</value>
</entry>
<entry key="flutter_lints">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_lints-2.0.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_lints-2.0.1/lib" />
</list>
</value>
</entry>
<entry key="flutter_localizations">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/packages/flutter_localizations/lib" />
<option value="Z:/flutter/packages/flutter_localizations/lib" />
</list>
</value>
</entry>
<entry key="flutter_native_splash">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_native_splash-2.2.11/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_native_splash-2.2.11/lib" />
</list>
</value>
</entry>
<entry key="flutter_styled_toast">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_styled_toast-2.1.3/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_styled_toast-2.1.3/lib" />
</list>
</value>
</entry>
<entry key="flutter_test">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/packages/flutter_test/lib" />
<option value="Z:/flutter/packages/flutter_test/lib" />
</list>
</value>
</entry>
<entry key="flutter_web_plugins">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/packages/flutter_web_plugins/lib" />
<option value="Z:/flutter/packages/flutter_web_plugins/lib" />
</list>
</value>
</entry>
<entry key="fluttericon">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fluttericon-2.0.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fluttericon-2.0.0/lib" />
</list>
</value>
</entry>
<entry key="fluttertoast">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fluttertoast-8.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fluttertoast-8.1.1/lib" />
</list>
</value>
</entry>
<entry key="font_awesome_flutter">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-10.2.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-10.2.1/lib" />
</list>
</value>
</entry>
<entry key="geolocator">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator-9.0.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator-9.0.2/lib" />
</list>
</value>
</entry>
<entry key="geolocator_android">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_android-4.1.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_android-4.1.4/lib" />
</list>
</value>
</entry>
<entry key="geolocator_apple">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_apple-2.2.3/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_apple-2.2.3/lib" />
</list>
</value>
</entry>
<entry key="geolocator_platform_interface">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_platform_interface-4.0.7/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_platform_interface-4.0.7/lib" />
</list>
</value>
</entry>
<entry key="geolocator_web">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_web-2.1.6/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_web-2.1.6/lib" />
</list>
</value>
</entry>
<entry key="geolocator_windows">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_windows-0.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_windows-0.1.1/lib" />
</list>
</value>
</entry>
<entry key="graphs">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/graphs-2.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/graphs-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="home_indicator">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/home_indicator-2.0.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/home_indicator-2.0.2/lib" />
</list>
</value>
</entry>
<entry key="html">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/html-0.15.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/html-0.15.1/lib" />
</list>
</value>
</entry>
<entry key="http">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib" />
</list>
</value>
</entry>
<entry key="http_parser">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib" />
</list>
</value>
</entry>
<entry key="image">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/image-3.2.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/image-3.2.2/lib" />
</list>
</value>
</entry>
<entry key="intl">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/lib" />
</list>
</value>
</entry>
<entry key="js">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
</list>
</value>
</entry>
<entry key="json_annotation">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/json_annotation-4.7.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/json_annotation-4.7.0/lib" />
</list>
</value>
</entry>
<entry key="lints">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/lints-2.0.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/lints-2.0.0/lib" />
</list>
</value>
</entry>
<entry key="logging">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/logging-1.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/logging-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.12/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.12/lib" />
</list>
</value>
</entry>
<entry key="material_color_utilities">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.5/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.5/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.8.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.8.0/lib" />
</list>
</value>
</entry>
<entry key="mime">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/mime-1.0.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/mime-1.0.2/lib" />
</list>
</value>
</entry>
<entry key="nested">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib" />
</list>
</value>
</entry>
<entry key="page_transition">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/page_transition-2.0.9/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/page_transition-2.0.9/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.2/lib" />
</list>
</value>
</entry>
<entry key="path_provider">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/lib" />
</list>
</value>
</entry>
<entry key="path_provider_android">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.21/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.21/lib" />
</list>
</value>
</entry>
<entry key="path_provider_ios">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/lib" />
</list>
</value>
</entry>
<entry key="path_provider_linux">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/lib" />
</list>
</value>
</entry>
<entry key="path_provider_macos">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/lib" />
</list>
</value>
</entry>
<entry key="path_provider_platform_interface">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.5/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.5/lib" />
</list>
</value>
</entry>
<entry key="path_provider_windows">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/lib" />
</list>
</value>
</entry>
<entry key="petitparser">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/petitparser-5.0.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/petitparser-5.0.0/lib" />
</list>
</value>
</entry>
<entry key="platform">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
</list>
</value>
</entry>
<entry key="plugin_platform_interface">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.3/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.3/lib" />
</list>
</value>
</entry>
<entry key="postgresql2">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/postgresql2-1.0.3/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/postgresql2-1.0.3/lib" />
</list>
</value>
</entry>
<entry key="process">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.4/lib" />
</list>
</value>
</entry>
<entry key="provider">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/provider-6.0.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/provider-6.0.4/lib" />
</list>
</value>
</entry>
<entry key="random_string">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/random_string-2.3.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/random_string-2.3.1/lib" />
</list>
</value>
</entry>
<entry key="rikulo_commons">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/rikulo_commons-5.2.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/rikulo_commons-5.2.1/lib" />
</list>
</value>
</entry>
<entry key="rive">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/rive-0.9.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/rive-0.9.1/lib" />
</list>
</value>
</entry>
<entry key="sky_engine">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/bin/cache/pkg/sky_engine/lib" />
<option value="Z:/flutter/bin/cache/pkg/sky_engine/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.9.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.9.0/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.12/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.12/lib" />
</list>
</value>
</entry>
<entry key="typed_data">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib" />
</list>
</value>
</entry>
<entry key="universal_io">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/universal_io-2.0.4/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/universal_io-2.0.4/lib" />
</list>
</value>
</entry>
<entry key="vector_math">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="vibration">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/vibration-1.7.6/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/vibration-1.7.6/lib" />
</list>
</value>
</entry>
<entry key="win32">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/win32-3.0.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-3.0.1/lib" />
</list>
</value>
</entry>
<entry key="xdg_directories">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0+2/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0+2/lib" />
</list>
</value>
</entry>
<entry key="xml">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/xml-6.1.0/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/xml-6.1.0/lib" />
</list>
</value>
</entry>
<entry key="yaml">
<value>
<list>
<option value="$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib" />
<option value="Z:/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/animations-2.0.7/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.3.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/async-2.9.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.2.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/checked_yaml-2.0.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.3.5/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/convert-3.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.17.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.5/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/ffi-2.0.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-5.7.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_launcher_icons-0.10.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_lints-2.0.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_native_splash-2.2.11/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/flutter_styled_toast-2.1.3/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fluttericon-2.0.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/fluttertoast-8.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-10.2.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator-9.0.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_android-4.1.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_apple-2.2.3/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_platform_interface-4.0.7/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_web-2.1.6/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_windows-0.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/graphs-2.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/home_indicator-2.0.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/html-0.15.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/image-3.2.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/json_annotation-4.7.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/lints-2.0.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/logging-1.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.12/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.5/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.8.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/mime-1.0.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/page_transition-2.0.9/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.21/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.5/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/petitparser-5.0.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.3/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/postgresql2-1.0.3/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/provider-6.0.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/random_string-2.3.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/rikulo_commons-5.2.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/rive-0.9.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.9.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.12/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/universal_io-2.0.4/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/vibration-1.7.6/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/win32-3.0.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0+2/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/xml-6.1.0/lib" />
<root url="file://$PROJECT_DIR$/../flutter/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/pkg/sky_engine/lib" />
<root url="file://$PROJECT_DIR$/../flutter/packages/flutter/lib" />
<root url="file://$PROJECT_DIR$/../flutter/packages/flutter_localizations/lib" />
<root url="file://$PROJECT_DIR$/../flutter/packages/flutter_test/lib" />
<root url="file://$PROJECT_DIR$/../flutter/packages/flutter_web_plugins/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/animations-2.0.7/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/archive-3.3.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/args-2.3.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.9.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.2.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/checked_yaml-2.0.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/cli_util-0.3.5/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/convert-3.1.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/csslib-0.17.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.5/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-2.0.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-5.7.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_launcher_icons-0.10.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_lints-2.0.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_native_splash-2.2.11/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_styled_toast-2.1.3/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fluttericon-2.0.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/fluttertoast-8.1.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-10.2.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator-9.0.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_android-4.1.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_apple-2.2.3/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_platform_interface-4.0.7/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_web-2.1.6/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_windows-0.1.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/graphs-2.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/home_indicator-2.0.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/html-0.15.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/image-3.2.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/intl-0.17.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/json_annotation-4.7.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/lints-2.0.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/logging-1.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.12/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.5/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.8.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/mime-1.0.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/page_transition-2.0.9/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.11/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.21/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.5/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/petitparser-5.0.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.1.3/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/postgresql2-1.0.3/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/provider-6.0.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/random_string-2.3.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/rikulo_commons-5.2.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/rive-0.9.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.9.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.12/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/universal_io-2.0.4/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/vibration-1.7.6/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-3.0.1/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0+2/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/xml-6.1.0/lib" />
<root url="file://Z:/flutter/.pub-cache/hosted/pub.dartlang.org/yaml-3.1.1/lib" />
<root url="file://Z:/flutter/bin/cache/pkg/sky_engine/lib" />
<root url="file://Z:/flutter/packages/flutter/lib" />
<root url="file://Z:/flutter/packages/flutter_localizations/lib" />
<root url="file://Z:/flutter/packages/flutter_test/lib" />
<root url="file://Z:/flutter/packages/flutter_web_plugins/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />

@ -1,25 +1,25 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/js" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$PROJECT_DIR$/../flutter/bin/cache/dart-sdk/lib/web_gl" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://Z:/flutter/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />

@ -0,0 +1,160 @@
<?php
/**
* phpMyAdmin sample configuration, you can use it as base for
* manual configuration. For easier setup you can use setup/
*
* All directives are explained in documentation in the doc/ folder
* or at <https://docs.phpmyadmin.net/>.
*/
declare(strict_types=1);
/**
* This is needed for cookie based authentication to encrypt password in
* cookie. Needs to be 32 chars long.
*/
$cfg['blowfish_secret'] = ''; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */
/**
* Servers configuration
*/
$i = 0;
/**
* First server
*/
$i++;
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
/* Server parameters */
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
/**
* phpMyAdmin configuration storage settings.
*/
/* User used to manipulate with storage */
// $cfg['Servers'][$i]['controlhost'] = '';
// $cfg['Servers'][$i]['controlport'] = '';
// $cfg['Servers'][$i]['controluser'] = 'pma';
// $cfg['Servers'][$i]['controlpass'] = 'pmapass';
/* Storage database and tables */
// $cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
// $cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark';
// $cfg['Servers'][$i]['relation'] = 'pma__relation';
// $cfg['Servers'][$i]['table_info'] = 'pma__table_info';
// $cfg['Servers'][$i]['table_coords'] = 'pma__table_coords';
// $cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages';
// $cfg['Servers'][$i]['column_info'] = 'pma__column_info';
// $cfg['Servers'][$i]['history'] = 'pma__history';
// $cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs';
// $cfg['Servers'][$i]['tracking'] = 'pma__tracking';
// $cfg['Servers'][$i]['userconfig'] = 'pma__userconfig';
// $cfg['Servers'][$i]['recent'] = 'pma__recent';
// $cfg['Servers'][$i]['favorite'] = 'pma__favorite';
// $cfg['Servers'][$i]['users'] = 'pma__users';
// $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups';
// $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding';
// $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches';
// $cfg['Servers'][$i]['central_columns'] = 'pma__central_columns';
// $cfg['Servers'][$i]['designer_settings'] = 'pma__designer_settings';
// $cfg['Servers'][$i]['export_templates'] = 'pma__export_templates';
/**
* End of servers configuration
*/
/**
* Directories for saving/loading files from server
*/
$cfg['UploadDir'] = '';
$cfg['SaveDir'] = '';
/**
* Whether to display icons or text or both icons and text in table row
* action segment. Value can be either of 'icons', 'text' or 'both'.
* default = 'both'
*/
//$cfg['RowActionType'] = 'icons';
/**
* Defines whether a user should be displayed a "show all (records)"
* button in browse mode or not.
* default = false
*/
//$cfg['ShowAll'] = true;
/**
* Number of rows displayed when browsing a result set. If the result
* set contains more rows, "Previous" and "Next".
* Possible values: 25, 50, 100, 250, 500
* default = 25
*/
//$cfg['MaxRows'] = 50;
/**
* Disallow editing of binary fields
* valid values are:
* false allow editing
* 'blob' allow editing except for BLOB fields
* 'noblob' disallow editing except for BLOB fields
* 'all' disallow editing
* default = 'blob'
*/
//$cfg['ProtectBinary'] = false;
/**
* Default language to use, if not browser-defined or user-defined
* (you find all languages in the locale folder)
* uncomment the desired line:
* default = 'en'
*/
//$cfg['DefaultLang'] = 'en';
//$cfg['DefaultLang'] = 'de';
/**
* How many columns should be used for table display of a database?
* (a value larger than 1 results in some information being hidden)
* default = 1
*/
//$cfg['PropertiesNumColumns'] = 2;
/**
* Set to true if you want DB-based query history.If false, this utilizes
* JS-routines to display query history (lost by window close)
*
* This requires configuration storage enabled, see above.
* default = false
*/
//$cfg['QueryHistoryDB'] = true;
/**
* When using DB-based query history, how many entries should be kept?
* default = 25
*/
//$cfg['QueryHistoryMax'] = 100;
/**
* Whether or not to query the user before sending the error report to
* the phpMyAdmin team when a JavaScript error occurs
*
* Available options
* ('ask' | 'always' | 'never')
* default = 'ask'
*/
//$cfg['SendErrorReports'] = 'always';
/**
* 'URLQueryEncryption' defines whether phpMyAdmin will encrypt sensitive data from the URL query string.
* 'URLQueryEncryptionSecretKey' is a 32 bytes long secret key used to encrypt/decrypt the URL query string.
*/
//$cfg['URLQueryEncryption'] = true;
//$cfg['URLQueryEncryptionSecretKey'] = '';
/**
* You can find more configuration options in the documentation
* in the doc/ folder or at <https://docs.phpmyadmin.net/>.
*/

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,14 @@
<?php
$username="dafldev";
$host="89.83.53.34";
$password="wrap";
$db_name="positiondaflmusic";
$connect=mysqli_connect($host,$username,$password,$db_name);
/*
if(!$connect)
{
print "Connection Failed";
}
*/

@ -0,0 +1,10 @@
<?php
$dns = 'mysql:host=89.83.53.34;dbname=positiondaflmusic';
$user = 'dafldev';
$password = 'wrap';
try{
$db = new PDO ($dns, $user, $password);
}catch( PDOException $e){
$error = $e->getMessage();
echo $error;
}

@ -0,0 +1,61 @@
<?php
class distance
{
public function meters($lat1, $lng1, $lat2, $lng2): float
{
// Radius of the Earth in meters :
$earth_radius = 6378137;
// Calculation of the distance between 2 GPS coordinates:
$rlo1 = deg2rad($lng1);
$rla1 = deg2rad($lat1);
$rlo2 = deg2rad($lng2);
$rla2 = deg2rad($lat2);
$dlo = ($rlo2 - $rlo1) / 2;
$dla = ($rla2 - $rla1) / 2;
$a = (sin($dla) * sin($dla)) + cos($rla1) * cos($rla2) * (sin($dlo) * sin($dlo));
$d = 2 * atan2(sqrt($a), sqrt(1 - $a));
// Return the distance in meters between 2 GPS points
return round($earth_radius * $d);
}
}
$db=0;
$connect="";
require_once('db.php');
include "config.php";
$id = $_POST['id'];
$query = 'SELECT * FROM gps';
$stm = $db->prepare($query);
$stm->execute();
$row = $stm->fetchAll(PDO::FETCH_ASSOC);
$lat1=0;
$lng1=0;
$listUser=[];
Foreach ($row as $col) {
if (strcmp($col['id'], $id) == 0) {
$lat1 = $col['latitude'];
$lng1 = $col['longitude'];
}
}
if ($lat1==0 && $lng1==0){
print(json_encode("ERROR No user found in the database"));
exit(1);
}
Foreach ($row as $col) {
if (strcmp($col['id'],$id)!=0) {
$lat2 = $col['latitude'];
$lng2 = $col['longitude'];
$userID = $col['id'];
$idMusic = $col['idMusic'];
$dist = (new distance)->meters($lat1, $lng1, $lat2, $lng2);
if ($dist <= 100) {
$listUser[] = ['user' => $userID, 'music' => $idMusic]; }
}
}
print(json_encode($listUser));

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

@ -0,0 +1,440 @@
.. _glossary:
Glossary
========
From Wikipedia, the free encyclopedia
.. glossary::
.htaccess
the default name of Apache's directory-level configuration file.
.. seealso:: <https://en.wikipedia.org/wiki/.htaccess>
ACL
Access Control List
Blowfish
a keyed, symmetric block cipher, designed in 1993 by `Bruce Schneier <https://en.wikipedia.org/wiki/Bruce_Schneier>`_.
.. seealso:: <https://en.wikipedia.org/wiki/Blowfish_(cipher)>
Browser
a software application that enables a user to display and interact with text, images, and other information typically located on a web page at a website on the World Wide Web.
.. seealso:: <https://en.wikipedia.org/wiki/Web_browser>
bzip2
a free software/open-source data compression algorithm and program developed by Julian Seward.
.. seealso:: <https://en.wikipedia.org/wiki/Bzip2>
CGI
Common Gateway Interface is an important World Wide Web technology that
enables a client web browser to request data from a program executed on
the web server.
.. seealso:: <https://en.wikipedia.org/wiki/Common_Gateway_Interface>
Changelog
a log or record of changes made to a project.
.. seealso:: <https://en.wikipedia.org/wiki/Changelog>
Client
a computer system that accesses a (remote) service on another computer by some kind of network.
.. seealso:: <https://en.wikipedia.org/wiki/Client_(computing)>
column
a set of data values of a particularly simple type, one for each row of the table.
.. seealso:: <https://en.wikipedia.org/wiki/Column_(database)>
Cookie
a packet of information sent by a server to a World Wide Web browser and then sent back by the browser each time it accesses that server.
.. seealso:: <https://en.wikipedia.org/wiki/HTTP_cookie>
CSV
Comma-separated values
.. seealso:: <https://en.wikipedia.org/wiki/Comma-separated_values>
DB
look at :term:`Database`
Database
an organized collection of data.
.. seealso:: <https://en.wikipedia.org/wiki/Database>
Engine
look at :term:`Storage Engines`
PHP extension
a PHP module that extends PHP with additional functionality.
.. seealso:: <https://en.wikipedia.org/wiki/Software_extension>
FAQ
Frequently Asked Questions is a list of commonly asked question and their
answers.
.. seealso:: <https://en.wikipedia.org/wiki/FAQ>
Field
one part of divided data/columns.
.. seealso:: <https://en.wikipedia.org/wiki/Field_(computer_science)>
Foreign key
a column or group of columns in a database row that points to a key column
or group of columns forming a key of another database row in some
(usually different) table.
.. seealso:: <https://en.wikipedia.org/wiki/Foreign_key>
GD
Graphics Library by Thomas Boutell and others for dynamically manipulating images.
.. seealso:: <https://en.wikipedia.org/wiki/GD_Graphics_Library>
GD2
look at :term:`GD`
GZip
GZip is short for GNU zip, a GNU free software file compression program.
.. seealso:: <https://en.wikipedia.org/wiki/Gzip>
host
any machine connected to a computer network, a node that has a hostname.
.. seealso:: <https://en.wikipedia.org/wiki/Host_(network)>
hostname
the unique name by which a network-attached device is known on a network.
.. seealso:: <https://en.wikipedia.org/wiki/Hostname>
HTTP
Hypertext Transfer Protocol is the primary method used to transfer or
convey information on the World Wide Web.
.. seealso:: <https://en.wikipedia.org/wiki/HyperText_Transfer_Protocol>
HTTPS
a :term:`HTTP`-connection with additional security measures.
.. seealso:: <https://en.wikipedia.org/wiki/HTTPS>
IEC
International Electrotechnical Commission
IIS
Internet Information Services is a set of internet-based services for
servers using Microsoft Windows.
.. seealso:: <https://en.wikipedia.org/wiki/Internet_Information_Services>
Index
a feature that allows quick access to the rows in a table.
.. seealso:: <https://en.wikipedia.org/wiki/Database_index>
IP
"Internet Protocol" is a data-oriented protocol used by source and
destination hosts for communicating data across a packet-switched
internetwork.
.. seealso:: <https://en.wikipedia.org/wiki/Internet_Protocol>
IP Address
a unique number that devices use in order to identify and communicate with each other on a network utilizing the Internet Protocol standard.
.. seealso:: <https://en.wikipedia.org/wiki/IP_Address>
IPv6
IPv6 (Internet Protocol version 6) is the latest revision of the
Internet Protocol (:term:`IP`), designed to deal with the
long-anticipated problem of its predecessor IPv4 running out of addresses.
.. seealso:: <https://en.wikipedia.org/wiki/IPv6>
ISAPI
Internet Server Application Programming Interface is the API of Internet Information Services (IIS).
.. seealso:: <https://en.wikipedia.org/wiki/Internet_Server_Application_Programming_Interface>
ISP
An Internet service provider is a business or organization that offers users
access to the Internet and related services.
.. seealso:: <https://en.wikipedia.org/wiki/Internet_service_provider>
ISO
International Standards Organization
.. seealso:: `ISO organization website <https://www.iso.org/about-us.html>`_
.. seealso:: <https://en.wikipedia.org/wiki/International_Organization_for_Standardization>
JPEG
a most commonly used standard method of lossy compression for photographic images.
.. seealso:: <https://en.wikipedia.org/wiki/JPEG>
JPG
look at :term:`JPEG`
Key
look at :term:`Index`
LATEX
a document preparation system for the TeX typesetting program.
.. seealso:: <https://en.wikipedia.org/wiki/LaTeX>
Mac
Apple Macintosh is a line of personal computers designed, developed, manufactured, and marketed by Apple Inc.
.. seealso:: <https://en.wikipedia.org/wiki/Macintosh>
macOS
the operating system which is included with all currently shipping Apple Macintosh computers in the consumer and professional markets.
.. seealso:: <https://en.wikipedia.org/wiki/MacOS>
mbstring
The PHP `mbstring` functions provide support for languages represented by multi-byte character sets, most notably UTF-8.
If you have troubles installing this extension, please follow :ref:`faqmysql`, it provides useful hints.
.. seealso:: <https://www.php.net/manual/en/book.mbstring.php>
Media type
A media type (formerly known as MIME type) is a two-part identifier
for file formats and format contents transmitted on the Internet.
.. seealso:: <https://en.wikipedia.org/wiki/Media_type>
MIME
Multipurpose Internet Mail Extensions is
an Internet Standard for the format of e-mail.
.. seealso:: <https://en.wikipedia.org/wiki/MIME>
module
modular extension for the Apache HTTP Server httpd.
.. seealso:: <https://en.wikipedia.org/wiki/Apache_HTTP_Server>
mod_proxy_fcgi
an Apache module implementing a Fast CGI interface; PHP can be run as a CGI module, FastCGI, or
directly as an Apache module.
.. seealso:: <https://en.wikipedia.org/wiki/Mod_proxy>
MySQL
a multithreaded, multi-user, SQL (Structured Query Language) Database Management System (DBMS).
.. seealso:: <https://en.wikipedia.org/wiki/MySQL>
MySQLi
the improved MySQL client PHP extension.
.. seealso:: `PHP manual for MySQL Improved Extension <https://www.php.net/manual/en/book.mysqli.php>`_
.. seealso:: <https://en.wikipedia.org/wiki/MySQLi>
mysql
the MySQL client PHP extension.
.. seealso:: <https://www.php.net/manual/en/book.mysql.php>
OpenDocument
an open standard for office documents.
.. seealso:: <https://en.wikipedia.org/wiki/OpenDocument>
OS X
look at :term:`macOS`.
.. seealso:: <https://en.wikipedia.org/wiki/MacOS>
PDF
Portable Document Format is a file format developed by Adobe Systems for
representing two-dimensional documents in a device-independent and
resolution-independent format.
.. seealso:: <https://en.wikipedia.org/wiki/PDF>
PEAR
the PHP Extension and Application Repository.
.. seealso:: `PEAR website <https://pear.php.net/>`_
.. seealso:: `Wikipedia page for PEAR <https://en.wikipedia.org/wiki/PEAR>`_
PCRE
Perl-Compatible Regular Expressions is the Perl-compatible regular
expression functions for PHP
.. seealso:: <https://www.php.net/pcre>
.. seealso:: `PHP manual for Perl-Compatible Regular Expressions <https://www.php.net/pcre>`_
.. seealso:: <https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions>
PHP
short for "PHP: Hypertext Preprocessor", is an open-source, reflective
programming language used mainly for developing server-side applications
and dynamic web content, and more recently, a broader range of software
applications.
.. seealso:: <https://en.wikipedia.org/wiki/PHP>
port
a connection through which data is sent and received.
.. seealso:: <https://en.wikipedia.org/wiki/Port_(computer_networking)>
primary key
A primary key is an index over one or more fields in a table with
unique values for every single row in this table. Every table should have
a primary key for easier accessing/identifying data in this table. There
can only be one primary key per table and it is named always **PRIMARY**.
In fact, a primary key is just an :term:`unique key` with the name
**PRIMARY**. If no primary key is defined MySQL will use first *unique
key* as primary key if there is one.
You can create the primary key when creating the table (in phpMyAdmin
just check the primary key radio buttons for each field you wish to be
part of the primary key).
You can also add a primary key to an existing table with `ALTER` `TABLE`
or `CREATE` `INDEX` (in phpMyAdmin you can just click on 'add index' on
the table structure page below the listed fields).
RFC
Request for Comments (RFC) documents are a series of memoranda
encompassing new research, innovations, and methodologies applicable to
Internet technologies.
.. seealso:: <https://en.wikipedia.org/wiki/Request_for_Comments>
RFC 1952
GZIP file format specification version 4.3
.. seealso:: :rfc:`1952`
Row (record, tuple)
represents a single, implicitly structured data item in a table.
.. seealso:: <https://en.wikipedia.org/wiki/Row_(database)>
Server
a computer system that provides services to other computing systems over a network.
.. seealso:: <https://en.wikipedia.org/wiki/Server_(computing)>
Storage Engines
MySQL can use several different formats for storing data on disk, these
are called storage engines or table types. phpMyAdmin allows a user to
change their storage engine for a particular table through the operations
tab.
Common table types are InnoDB and MyISAM, though many others exist and
may be desirable in some situations.
.. seealso:: `MySQL doc chapter about Alternative Storage Engines <https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html>`_
.. seealso:: <https://en.wikipedia.org/wiki/Database_engine>
socket
a form of inter-process communication.
.. seealso:: <https://en.wikipedia.org/wiki/Unix_domain_socket>
SSL
Secure Sockets Layer, (now superseded by TLS) is a cryptographic protocol
which provides secure communication on the Internet.
.. seealso:: <https://en.wikipedia.org/wiki/Transport_Layer_Security>
Stored procedure
a subroutine available to applications accessing a relational database system
.. seealso:: <https://en.wikipedia.org/wiki/Stored_procedure>
SQL
Structured Query Language
.. seealso:: <https://en.wikipedia.org/wiki/SQL>
table
a set of data elements (cells) that is organized, defined and stored as
horizontal rows and vertical columns where each item can be uniquely
identified by a label or key or by its position in relation to other
items.
.. seealso:: <https://en.wikipedia.org/wiki/Table_(database)>
tar
a type of archive file format, from "Tape Archive".
.. seealso:: <https://en.wikipedia.org/wiki/Tar_(computing)>
TCP
Transmission Control Protocol is one of the core protocols of the
Internet protocol suite.
.. seealso:: <https://en.wikipedia.org/wiki/Internet_protocol_suite>
TCPDF
PHP library to generate PDF files.
.. seealso:: <https://tcpdf.org/>
.. seealso:: <https://en.wikipedia.org/wiki/TCPDF>
trigger
a procedural code that is automatically executed in response to certain events on a particular table or view in a database
.. seealso:: <https://en.wikipedia.org/wiki/Database_trigger>
unique key
A unique key is an index over one or more fields in a table which has a
unique value for each row. The first unique key will be treated as
:term:`primary key` if there is no *primary key* defined.
URL
Uniform Resource Locator is a sequence of characters, conforming to a
standardized format, that is used for referring to resources, such as
documents and images on the Internet, by their location.
.. seealso:: <https://en.wikipedia.org/wiki/URL>
Web server
A computer (program) that is responsible for accepting HTTP requests from clients and serving them web pages.
.. seealso:: <https://en.wikipedia.org/wiki/Web_server>
XML
Extensible Markup Language is a W3C-recommended general-purpose markup
language for creating special-purpose markup languages, capable of
describing many different kinds of data.
.. seealso:: <https://en.wikipedia.org/wiki/XML>
ZIP
a popular data compression and archival format.
.. seealso:: <https://en.wikipedia.org/wiki/Zip_(file_format)>
Zlib
an open-source, cross-platform data compression library by `Jean-loup Gailly <https://en.wikipedia.org/wiki/Jean-Loup_Gailly>`_ and `Mark Adler <https://en.wikipedia.org/wiki/Mark_Adler>`_.
.. seealso:: <https://en.wikipedia.org/wiki/Zlib>
Content Security Policy
The HTTP `Content-Security-Policy` response header allows web site administrators
to control resources the user agent is allowed to load for a given page.
.. seealso:: <https://en.wikipedia.org/wiki/Content_Security_Policy>
.. seealso:: <https://developer.mozilla.org/en/docs/Web/HTTP/CSP>

@ -0,0 +1,74 @@
User management
===============
User management is the process of controlling which users are allowed to
connect to the MySQL server and what permissions they have on each database.
phpMyAdmin does not handle user management, rather it passes the username and
password on to MySQL, which then determines whether a user is permitted to
perform a particular action. Within phpMyAdmin, administrators have full
control over creating users, viewing and editing privileges for existing users,
and removing users.
Within phpMyAdmin, user management is controlled via the :guilabel:`User accounts` tab
from the main page. Users can be created, edited, and removed.
Creating a new user
-------------------
To create a new user, click the :guilabel:`Add user account` link near the bottom
of the :guilabel:`User accounts` page (you must be a "superuser", e.g., user "root").
Use the textboxes and drop-downs to configure the user to your particular
needs. You can then select whether to create a database for that user and grant
specific global privileges. Once you've created the user (by clicking Go), you
can define that user's permissions on a specific database (don't grant global
privileges in that case). In general, users do not need any global privileges
(other than USAGE), only permissions for their specific database.
Editing an existing user
------------------------
To edit an existing user, simply click the pencil icon to the right of that
user in the :guilabel:`User accounts` page. You can then edit their global- and
database-specific privileges, change their password, or even copy those
privileges to a new user.
Deleting a user
---------------
From the :guilabel:`User accounts` page, check the checkbox for the user you wish to
remove, select whether or not to also remove any databases of the same name (if
they exist), and click Go.
Assigning privileges to user for a specific database
----------------------------------------------------
Users are assigned to databases by editing the user record (from the
:guilabel:`User accounts` link on the home page).
If you are creating a user specifically for a given table
you will have to create the user first (with no global privileges) and then go
back and edit that user to add the table and privileges for the individual
table.
.. _configurablemenus:
Configurable menus and user groups
----------------------------------
By enabling :config:option:`$cfg['Servers'][$i]['users']` and
:config:option:`$cfg['Servers'][$i]['usergroups']` you can customize what users
will see in the phpMyAdmin navigation.
.. warning::
This feature only limits what a user sees, they are still able to use all the
functions. So this can not be considered as a security limitation. Should
you want to limit what users can do, use MySQL privileges to achieve that.
With this feature enabled, the :guilabel:`User accounts` management interface gains
a second tab for managing :guilabel:`User groups`, where you can define what each
group will view (see image below) and you can then assign each user to one of
these groups. Users will be presented with a simplified user interface, which might be
useful for inexperienced users who could be overwhelmed by all the features
phpMyAdmin provides.
.. image:: images/usergroups.png

@ -0,0 +1,158 @@
.. _transformations:
Transformations
===============
.. note::
You need to have configured the :ref:`linked-tables` to use the transformations
feature.
.. _transformationsintro:
Introduction
++++++++++++
To enable transformations, you have to set up the ``column_info``
table and the proper directives. Please see the :ref:`config` on how to do so.
phpMyAdmin has two different types of transformations: browser display
transformations, which affect only how the data is shown when browsing
through phpMyAdmin; and input transformations, which affect a value
prior to being inserted through phpMyAdmin.
You can apply different transformations to the contents of each
column. Each transformation has options to define how it will affect the
stored data.
Say you have a column ``filename`` which contains a filename. Normally
you would see in phpMyAdmin only this filename. Using display transformations
you can transform that filename into a HTML link, so you can click
inside of the phpMyAdmin structure on the column's link and will see
the file displayed in a new browser window. Using transformation
options you can also specify strings to append/prepend to a string or
the format you want the output stored in.
For a general overview of all available transformations and their
options, you can either go to the ``Change`` link for an existing column
or from the dialog to create a new column, in either case there is a link
on that column structure page for "Browser display transformation" and
"Input transformation" which will show more information about each
transformation that is available on your system.
For a tutorial on how to effectively use transformations, see our
`Link section <https://www.phpmyadmin.net/docs/>`_ on the
official phpMyAdmin homepage.
.. _transformationshowto:
Usage
+++++
Go to the table structure page (reached by clicking on
the 'Structure' link for a table). There click on "Change" (or the change
icon) and there you will see the five transformation--related fields at the end of the line.
They are called ':term:`Media type`', 'Browser transformation' and
'Transformation options'.
* The field ':term:`Media type`' is a drop-down field. Select the :term:`Media type` that
corresponds to the column's contents. Please note that many transformations
are inactive until a :term:`Media type` is selected.
* The field 'Browser display transformation' is a drop-down field. You can
choose from a hopefully growing amount of pre-defined transformations.
See below for information on how to build your own transformation.
There are global transformations and mimetype-bound transformations.
Global transformations can be used for any mimetype. They will take
the mimetype, if necessary, into regard. Mimetype-bound
transformations usually only operate on a certain mimetype. There are
transformations which operate on the main mimetype (like 'image'),
which will most likely take the subtype into regard, and those who
only operate on a specific subtype (like 'image/jpeg'). You can use
transformations on mimetypes for which the function was not defined
for. There is no security check for you selected the right
transformation, so take care of what the output will be like.
* The field 'Browser display transformation options' is a free-type textfield. You have
to enter transform-function specific options here. Usually the
transforms can operate with default options, but it is generally a
good idea to look up the overview to see which options are necessary.
Much like the ENUM/SET-Fields, you have to split up several options
using the format 'a','b','c',...(NOTE THE MISSING BLANKS). This is
because internally the options will be parsed as an array, leaving the
first value the first element in the array, and so forth. If you want
to specify a MIME character set you can define it in the
transformation\_options. You have to put that outside of the pre-
defined options of the specific mime-transform, as the last value of
the set. Use the format "'; charset=XXX'". If you use a transform, for
which you can specify 2 options and you want to append a character
set, enter "'first parameter','second parameter','charset=us-ascii'".
You can, however use the defaults for the parameters: "'','','charset
=us-ascii'". The default options can be configured using
:config:option:`$cfg['DefaultTransformations']`.
* 'Input transformation' is another drop-down menu that corresponds exactly
with the instructions above for "Browser display transformation" except
these these affect the data before insertion in to the database. These are
most commonly used to either provide a specialized editor (for example, using
the phpMyAdmin SQL editor interface) or selector (such as for uploading an image).
It's also possible to manipulate the data such as converting an IPv4 address to binary
or parsing it through a regular expression.
* Finally, 'Input transformation options' is the equivalent of the "Browser display
transformation options" section above and is where optional and required parameters are entered.
.. _transformationsfiles:
File structure
++++++++++++++
All specific transformations for mimetypes are defined through class
files in the directory :file:`libraries/classes/Plugins/Transformations/`. Each of
them extends a certain transformation abstract class declared in
:file:`libraries/classes/Plugins/Transformations/Abs`.
They are stored in files to ease customization and to allow easy adding of
new or custom transformations.
Because the user cannot enter their own mimetypes, it is kept certain that
the transformations will always work. It makes no sense to apply a
transformation to a mimetype the transform-function doesn't know to
handle.
There is a file called :file:`libraries/classes/Plugins/Transformations.php` that provides some
basic functions which can be included by any other transform function.
The file name convention is ``[Mimetype]_[Subtype]_[Transformation
Name].php``, while the abstract class that it extends has the
name ``[Transformation Name]TransformationsPlugin``. All of the
methods that have to be implemented by a transformations plug-in are:
#. getMIMEType() and getMIMESubtype() in the main class;
#. getName(), getInfo() and applyTransformation() in the abstract class
it extends.
The getMIMEType(), getMIMESubtype() and getName() methods return the
name of the MIME type, MIME Subtype and transformation accordingly.
getInfo() returns the transformation's description and possible
options it may receive and applyTransformation() is the method that
does the actual work of the transformation plug-in.
Please see the :file:`libraries/classes/Plugins/Transformations/TEMPLATE` and
:file:`libraries/classes/Plugins/Transformations/TEMPLATE\_ABSTRACT` files for adding
your own transformation plug-in. You can also generate a new
transformation plug-in (with or without the abstract transformation
class), by using
:file:`scripts/transformations_generator_plugin.sh` or
:file:`scripts/transformations_generator_main_class.sh`.
The applyTransformation() method always gets passed three variables:
#. **$buffer** - Contains the text inside of the column. This is the
text, you want to transform.
#. **$options** - Contains any user-passed options to a transform
function as an array.
#. **$meta** - Contains an object with information about your column. The
data is drawn from the output of the `mysql\_fetch\_field()
<https://www.php.net/mysql_fetch_field>`_ function. This means, all
object properties described on the `manual page
<https://www.php.net/mysql_fetch_field>`_ are available in this
variable and can be used to transform a column accordingly to
unsigned/zerofill/not\_null/... properties. The $meta->mimetype
variable contains the original :term:`Media type` of the column (i.e.
'text/plain', 'image/jpeg' etc.)

@ -0,0 +1,69 @@
.. _2fa:
Two-factor authentication
=========================
.. versionadded:: 4.8.0
Since phpMyAdmin 4.8.0 you can configure two-factor authentication to be
used when logging in. To use this, you first need to configure the
:ref:`linked-tables`. Once this is done, every user can opt-in for the second
authentication factor in the :guilabel:`Settings`.
When running phpMyAdmin from the Git source repository, the dependencies must be installed
manually; the typical way of doing so is with the command:
.. code-block:: sh
composer require pragmarx/google2fa-qrcode bacon/bacon-qr-code
Or when using a hardware security key with FIDO U2F:
.. code-block:: sh
composer require code-lts/u2f-php-server
Authentication Application (2FA)
--------------------------------
Using an application for authentication is a quite common approach based on HOTP and
`TOTP <https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm>`_.
It is based on transmitting a private key from phpMyAdmin to the authentication
application and the application is then able to generate one time codes based
on this key. The easiest way to enter the key in to the application from phpMyAdmin is
through scanning a QR code.
There are dozens of applications available for mobile phones to implement these
standards, the most widely used include:
* `FreeOTP for iOS, Android and Pebble <https://freeotp.github.io/>`_
* `Authy for iOS, Android, Chrome, OS X <https://authy.com/>`_
* `Google Authenticator for iOS <https://apps.apple.com/us/app/google-authenticator/id388497605>`_
* `Google Authenticator for Android <https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2>`_
* `LastPass Authenticator for iOS, Android, OS X, Windows <https://lastpass.com/auth/>`_
Hardware Security Key (FIDO U2F)
--------------------------------
Using hardware tokens is considered to be more secure than a software based
solution. phpMyAdmin supports `FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_
tokens.
There are several manufacturers of these tokens, for example:
* `youbico FIDO U2F Security Key <https://www.yubico.com/fido-u2f/>`_
* `HyperFIDO <https://www.hypersecu.com/hyperfido>`_
* `Trezor Hardware Wallet <https://trezor.io/?offer_id=12&aff_id=1592&source=phpmyadmin>`_ can act as an `U2F token <https://wiki.trezor.io/User_manual:Two-factor_Authentication_with_U2F>`_
* `List of Two Factor Auth (2FA) Dongles <https://www.dongleauth.info/dongles/>`_
.. _simple2fa:
Simple two-factor authentication
--------------------------------
This authentication is included for testing and demonstration purposes only as
it really does not provide two-factor authentication, it just asks the user to confirm login by
clicking on the button.
It should not be used in the production and is disabled unless
:config:option:`$cfg['DBG']['simple2fa']` is set.

@ -0,0 +1,266 @@
/*
* classic.css_t
* ~~~~~~~~~~~~~
*
* Sphinx stylesheet -- classic theme.
*
* :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
html {
/* CSS hack for macOS's scrollbar (see #1125) */
background-color: #FFFFFF;
}
body {
font-family: sans-serif;
font-size: 100%;
background-color: #11303d;
color: #000;
margin: 0;
padding: 0;
}
div.document {
background-color: #1c4e63;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
div.body {
background-color: #ffffff;
color: #000000;
padding: 0 20px 30px 20px;
}
div.footer {
color: #ffffff;
width: 100%;
padding: 9px 0 9px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #ffffff;
text-decoration: underline;
}
div.related {
background-color: #133f52;
line-height: 30px;
color: #ffffff;
}
div.related a {
color: #ffffff;
}
div.sphinxsidebar {
}
div.sphinxsidebar h3 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.4em;
font-weight: normal;
margin: 0;
padding: 0;
}
div.sphinxsidebar h3 a {
color: #ffffff;
}
div.sphinxsidebar h4 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.3em;
font-weight: normal;
margin: 5px 0 0 0;
padding: 0;
}
div.sphinxsidebar p {
color: #ffffff;
}
div.sphinxsidebar p.topless {
margin: 5px 10px 10px 10px;
}
div.sphinxsidebar ul {
margin: 10px;
padding: 0;
color: #ffffff;
}
div.sphinxsidebar a {
color: #98dbcc;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
/* -- hyperlink styles ------------------------------------------------------ */
a {
color: #355f7c;
text-decoration: none;
}
a:visited {
color: #355f7c;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* -- body styles ----------------------------------------------------------- */
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Trebuchet MS', sans-serif;
background-color: #f2f2f2;
font-weight: normal;
color: #20435c;
border-bottom: 1px solid #ccc;
margin: 20px -20px 10px -20px;
padding: 3px 0 3px 10px;
}
div.body h1 { margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 160%; }
div.body h3 { font-size: 140%; }
div.body h4 { font-size: 120%; }
div.body h5 { font-size: 110%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li, div.body blockquote {
text-align: justify;
line-height: 130%;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.admonition p {
margin-bottom: 5px;
}
div.admonition pre {
margin-bottom: 5px;
}
div.admonition ul, div.admonition ol {
margin-bottom: 5px;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 5px;
background-color: unset;
color: unset;
line-height: 120%;
border: 1px solid #ac9;
border-left: none;
border-right: none;
}
code {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
}
th, dl.field-list > dt {
background-color: #ede;
}
.warning code {
background: #efc2c2;
}
.note code {
background: #d6d6d6;
}
.viewcode-back {
font-family: sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
div.code-block-caption {
color: #efefef;
background-color: #1c4e63;
}

@ -0,0 +1,297 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
/* Non-minified version JS is _stemmer.js if file is provided */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
var splitChars = (function() {
var result = {};
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
var i, j, start, end;
for (i = 0; i < singles.length; i++) {
result[singles[i]] = true;
}
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
for (i = 0; i < ranges.length; i++) {
start = ranges[i][0];
end = ranges[i][1];
for (j = start; j <= end; j++) {
result[j] = true;
}
}
return result;
})();
function splitQuery(query) {
var result = [];
var start = -1;
for (var i = 0; i < query.length; i++) {
if (splitChars[query.charCodeAt(i)]) {
if (start !== -1) {
result.push(query.slice(start, i));
start = -1;
}
} else if (start === -1) {
start = i;
}
}
if (start !== -1) {
result.push(query.slice(start));
}
return result;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,143 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Copyright &#8212; phpMyAdmin 5.2.0 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/classic.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="copyright" title="Copyright" href="#" />
<link rel="next" title="Credits" href="credits.html" />
<link rel="prev" title="Distributing and packaging phpMyAdmin" href="vendors.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="credits.html" title="Credits"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="vendors.html" title="Distributing and packaging phpMyAdmin"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Copyright</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="copyright">
<span id="id1"></span><h1>Copyright<a class="headerlink" href="#copyright" title="Permalink to this headline"></a></h1>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Copyright (C) 1998-2000 Tobias Ratschiller &lt;tobias_at_ratschiller.com&gt;
Copyright (C) 2001-2018 Marc Delisle &lt;marc_at_infomarc.info&gt;
Olivier Müller &lt;om_at_omnis.ch&gt;
Robin Johnson &lt;robbat2_at_users.sourceforge.net&gt;
Alexander M. Turek &lt;me_at_derrabus.de&gt;
Michal Čihař &lt;michal_at_cihar.com&gt;
Garvin Hicking &lt;me_at_supergarv.de&gt;
Michael Keck &lt;mkkeck_at_users.sourceforge.net&gt;
Sebastian Mendel &lt;cybot_tm_at_users.sourceforge.net&gt;
[check credits for more details]
</pre></div>
</div>
<p>This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2, as
published by the Free Software Foundation.</p>
<p>This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.</p>
<p>You should have received a copy of the GNU General Public License
along with this program. If not, see &lt;<a class="reference external" href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>&gt;.</p>
<div class="section" id="third-party-licenses">
<h2>Third party licenses<a class="headerlink" href="#third-party-licenses" title="Permalink to this headline"></a></h2>
<p>phpMyAdmin includes several third-party libraries which come under their
respective licenses.</p>
<p>jQuerys license, which is where we got the files under js/vendor/jquery/ is
(MIT|GPL), a copy of each license is available in this repository (GPL
is available as LICENSE, MIT as js/vendor/jquery/MIT-LICENSE.txt).</p>
<p>The download kit additionally includes several composer libraries. See their
licensing information in the vendor/ directory.</p>
</div>
</div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Copyright</a><ul>
<li><a class="reference internal" href="#third-party-licenses">Third party licenses</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="vendors.html"
title="previous chapter">Distributing and packaging phpMyAdmin</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="credits.html"
title="next chapter">Credits</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/copyright.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="credits.html" title="Credits"
>next</a> |</li>
<li class="right" >
<a href="vendors.html" title="Distributing and packaging phpMyAdmin"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Copyright</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; <a href="#">Copyright</a> 2012 - 2021, The phpMyAdmin devel team.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.4.3.
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,149 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Other sources of information &#8212; phpMyAdmin 5.2.0 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/classic.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="copyright" title="Copyright" href="copyright.html" />
<link rel="next" title="FAQ - Frequently Asked Questions" href="faq.html" />
<link rel="prev" title="Custom Themes" href="themes.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="faq.html" title="FAQ - Frequently Asked Questions"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="themes.html" title="Custom Themes"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="user.html" accesskey="U">User Guide</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Other sources of information</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="other-sources-of-information">
<h1>Other sources of information<a class="headerlink" href="#other-sources-of-information" title="Permalink to this headline"></a></h1>
<div class="section" id="printed-book">
<h2>Printed Book<a class="headerlink" href="#printed-book" title="Permalink to this headline"></a></h2>
<p>The definitive guide to using phpMyAdmin is the book Mastering phpMyAdmin for
Effective MySQL Management by Marc Delisle. You can get information on that
book and other officially endorsed <a class="reference external" href="https://www.phpmyadmin.net/docs/">books at the phpMyAdmin site</a>.</p>
</div>
<div class="section" id="tutorials">
<h2>Tutorials<a class="headerlink" href="#tutorials" title="Permalink to this headline"></a></h2>
<p>Third party tutorials and articles which you might find interesting:</p>
<div class="section" id="cesky-czech">
<h3>Česky (Czech)<a class="headerlink" href="#cesky-czech" title="Permalink to this headline"></a></h3>
<ul class="simple">
<li><p><a class="reference external" href="https://cihar.com/publications/linuxsoft/">Seriál o phpMyAdminovi</a></p></li>
</ul>
</div>
<div class="section" id="english">
<h3>English<a class="headerlink" href="#english" title="Permalink to this headline"></a></h3>
<ul class="simple">
<li><p><a class="reference external" href="https://garv.in/tops/texte/mimetutorial">Having fun with phpMyAdmins MIME-transformations &amp; PDF-features</a></p></li>
<li><p><a class="reference external" href="http://www.php-editors.com/articles/sql_phpmyadmin.php">Learning SQL Using phpMyAdmin (old tutorial)</a></p></li>
</ul>
</div>
<div class="section" id="russian">
<h3>Русский (Russian)<a class="headerlink" href="#russian" title="Permalink to this headline"></a></h3>
<ul class="simple">
<li><p><a class="reference external" href="https://php-myadmin.ru/">Russian server about phpMyAdmin</a></p></li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Other sources of information</a><ul>
<li><a class="reference internal" href="#printed-book">Printed Book</a></li>
<li><a class="reference internal" href="#tutorials">Tutorials</a><ul>
<li><a class="reference internal" href="#cesky-czech">Česky (Czech)</a></li>
<li><a class="reference internal" href="#english">English</a></li>
<li><a class="reference internal" href="#russian">Русский (Russian)</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="themes.html"
title="previous chapter">Custom Themes</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="faq.html"
title="next chapter">FAQ - Frequently Asked Questions</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/other.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="faq.html" title="FAQ - Frequently Asked Questions"
>next</a> |</li>
<li class="right" >
<a href="themes.html" title="Custom Themes"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="user.html" >User Guide</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Other sources of information</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; <a href="copyright.html">Copyright</a> 2012 - 2021, The phpMyAdmin devel team.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.4.3.
</div>
</body>
</html>

@ -0,0 +1,251 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Transformations &#8212; phpMyAdmin 5.2.0 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/classic.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="copyright" title="Copyright" href="copyright.html" />
<link rel="next" title="Bookmarks" href="bookmarks.html" />
<link rel="prev" title="Two-factor authentication" href="two_factor.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="bookmarks.html" title="Bookmarks"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="two_factor.html" title="Two-factor authentication"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="user.html" accesskey="U">User Guide</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Transformations</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="transformations">
<span id="id1"></span><h1>Transformations<a class="headerlink" href="#transformations" title="Permalink to this headline"></a></h1>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>You need to have configured the <a class="reference internal" href="setup.html#linked-tables"><span class="std std-ref">phpMyAdmin configuration storage</span></a> to use the transformations
feature.</p>
</div>
<div class="section" id="introduction">
<span id="transformationsintro"></span><h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline"></a></h2>
<p>To enable transformations, you have to set up the <code class="docutils literal notranslate"><span class="pre">column_info</span></code>
table and the proper directives. Please see the <a class="reference internal" href="config.html#config"><span class="std std-ref">Configuration</span></a> on how to do so.</p>
<p>phpMyAdmin has two different types of transformations: browser display
transformations, which affect only how the data is shown when browsing
through phpMyAdmin; and input transformations, which affect a value
prior to being inserted through phpMyAdmin.
You can apply different transformations to the contents of each
column. Each transformation has options to define how it will affect the
stored data.</p>
<p>Say you have a column <code class="docutils literal notranslate"><span class="pre">filename</span></code> which contains a filename. Normally
you would see in phpMyAdmin only this filename. Using display transformations
you can transform that filename into a HTML link, so you can click
inside of the phpMyAdmin structure on the columns link and will see
the file displayed in a new browser window. Using transformation
options you can also specify strings to append/prepend to a string or
the format you want the output stored in.</p>
<p>For a general overview of all available transformations and their
options, you can either go to the <code class="docutils literal notranslate"><span class="pre">Change</span></code> link for an existing column
or from the dialog to create a new column, in either case there is a link
on that column structure page for “Browser display transformation” and
“Input transformation” which will show more information about each
transformation that is available on your system.</p>
<p>For a tutorial on how to effectively use transformations, see our
<a class="reference external" href="https://www.phpmyadmin.net/docs/">Link section</a> on the
official phpMyAdmin homepage.</p>
</div>
<div class="section" id="usage">
<span id="transformationshowto"></span><h2>Usage<a class="headerlink" href="#usage" title="Permalink to this headline"></a></h2>
<p>Go to the table structure page (reached by clicking on
the Structure link for a table). There click on “Change” (or the change
icon) and there you will see the five transformationrelated fields at the end of the line.
They are called <a class="reference internal" href="glossary.html#term-Media-type"><span class="xref std std-term">Media type</span></a>, Browser transformation and
Transformation options.</p>
<ul class="simple">
<li><p>The field <a class="reference internal" href="glossary.html#term-Media-type"><span class="xref std std-term">Media type</span></a> is a drop-down field. Select the <a class="reference internal" href="glossary.html#term-Media-type"><span class="xref std std-term">Media type</span></a> that
corresponds to the columns contents. Please note that many transformations
are inactive until a <a class="reference internal" href="glossary.html#term-Media-type"><span class="xref std std-term">Media type</span></a> is selected.</p></li>
<li><p>The field Browser display transformation is a drop-down field. You can
choose from a hopefully growing amount of pre-defined transformations.
See below for information on how to build your own transformation.
There are global transformations and mimetype-bound transformations.
Global transformations can be used for any mimetype. They will take
the mimetype, if necessary, into regard. Mimetype-bound
transformations usually only operate on a certain mimetype. There are
transformations which operate on the main mimetype (like image),
which will most likely take the subtype into regard, and those who
only operate on a specific subtype (like image/jpeg). You can use
transformations on mimetypes for which the function was not defined
for. There is no security check for you selected the right
transformation, so take care of what the output will be like.</p></li>
<li><p>The field Browser display transformation options is a free-type textfield. You have
to enter transform-function specific options here. Usually the
transforms can operate with default options, but it is generally a
good idea to look up the overview to see which options are necessary.
Much like the ENUM/SET-Fields, you have to split up several options
using the format a,b,c,…(NOTE THE MISSING BLANKS). This is
because internally the options will be parsed as an array, leaving the
first value the first element in the array, and so forth. If you want
to specify a MIME character set you can define it in the
transformation_options. You have to put that outside of the pre-
defined options of the specific mime-transform, as the last value of
the set. Use the format “’; charset=XXX”. If you use a transform, for
which you can specify 2 options and you want to append a character
set, enter “first parameter,second parameter,charset=us-ascii”.
You can, however use the defaults for the parameters: “’’,,charset
=us-ascii”. The default options can be configured using
<span class="target" id="index-0"></span><a class="reference internal" href="config.html#cfg_DefaultTransformations"><code class="xref config config-option docutils literal notranslate"><span class="pre">$cfg['DefaultTransformations']</span></code></a>.</p></li>
<li><p>Input transformation is another drop-down menu that corresponds exactly
with the instructions above for “Browser display transformation” except
these these affect the data before insertion in to the database. These are
most commonly used to either provide a specialized editor (for example, using
the phpMyAdmin SQL editor interface) or selector (such as for uploading an image).
Its also possible to manipulate the data such as converting an IPv4 address to binary
or parsing it through a regular expression.</p></li>
<li><p>Finally, Input transformation options is the equivalent of the “Browser display
transformation options” section above and is where optional and required parameters are entered.</p></li>
</ul>
</div>
<div class="section" id="file-structure">
<span id="transformationsfiles"></span><h2>File structure<a class="headerlink" href="#file-structure" title="Permalink to this headline"></a></h2>
<p>All specific transformations for mimetypes are defined through class
files in the directory <code class="file docutils literal notranslate"><span class="pre">libraries/classes/Plugins/Transformations/</span></code>. Each of
them extends a certain transformation abstract class declared in
<code class="file docutils literal notranslate"><span class="pre">libraries/classes/Plugins/Transformations/Abs</span></code>.</p>
<p>They are stored in files to ease customization and to allow easy adding of
new or custom transformations.</p>
<p>Because the user cannot enter their own mimetypes, it is kept certain that
the transformations will always work. It makes no sense to apply a
transformation to a mimetype the transform-function doesnt know to
handle.</p>
<p>There is a file called <code class="file docutils literal notranslate"><span class="pre">libraries/classes/Plugins/Transformations.php</span></code> that provides some
basic functions which can be included by any other transform function.</p>
<p>The file name convention is <code class="docutils literal notranslate"><span class="pre">[Mimetype]_[Subtype]_[Transformation</span>
<span class="pre">Name].php</span></code>, while the abstract class that it extends has the
name <code class="docutils literal notranslate"><span class="pre">[Transformation</span> <span class="pre">Name]TransformationsPlugin</span></code>. All of the
methods that have to be implemented by a transformations plug-in are:</p>
<ol class="arabic simple">
<li><p>getMIMEType() and getMIMESubtype() in the main class;</p></li>
<li><p>getName(), getInfo() and applyTransformation() in the abstract class
it extends.</p></li>
</ol>
<p>The getMIMEType(), getMIMESubtype() and getName() methods return the
name of the MIME type, MIME Subtype and transformation accordingly.
getInfo() returns the transformations description and possible
options it may receive and applyTransformation() is the method that
does the actual work of the transformation plug-in.</p>
<p>Please see the <code class="file docutils literal notranslate"><span class="pre">libraries/classes/Plugins/Transformations/TEMPLATE</span></code> and
<code class="file docutils literal notranslate"><span class="pre">libraries/classes/Plugins/Transformations/TEMPLATE_ABSTRACT</span></code> files for adding
your own transformation plug-in. You can also generate a new
transformation plug-in (with or without the abstract transformation
class), by using
<code class="file docutils literal notranslate"><span class="pre">scripts/transformations_generator_plugin.sh</span></code> or
<code class="file docutils literal notranslate"><span class="pre">scripts/transformations_generator_main_class.sh</span></code>.</p>
<p>The applyTransformation() method always gets passed three variables:</p>
<ol class="arabic simple">
<li><p><strong>$buffer</strong> - Contains the text inside of the column. This is the
text, you want to transform.</p></li>
<li><p><strong>$options</strong> - Contains any user-passed options to a transform
function as an array.</p></li>
<li><p><strong>$meta</strong> - Contains an object with information about your column. The
data is drawn from the output of the <a class="reference external" href="https://www.php.net/mysql_fetch_field">mysql_fetch_field()</a> function. This means, all
object properties described on the <a class="reference external" href="https://www.php.net/mysql_fetch_field">manual page</a> are available in this
variable and can be used to transform a column accordingly to
unsigned/zerofill/not_null/… properties. The $meta-&gt;mimetype
variable contains the original <a class="reference internal" href="glossary.html#term-Media-type"><span class="xref std std-term">Media type</span></a> of the column (i.e.
text/plain, image/jpeg etc.)</p></li>
</ol>
</div>
</div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Transformations</a><ul>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#usage">Usage</a></li>
<li><a class="reference internal" href="#file-structure">File structure</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="two_factor.html"
title="previous chapter">Two-factor authentication</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="bookmarks.html"
title="next chapter">Bookmarks</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/transformations.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="bookmarks.html" title="Bookmarks"
>next</a> |</li>
<li class="right" >
<a href="two_factor.html" title="Two-factor authentication"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="user.html" >User Guide</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Transformations</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; <a href="copyright.html">Copyright</a> 2012 - 2021, The phpMyAdmin devel team.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.4.3.
</div>
</body>
</html>

@ -0,0 +1,158 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Distributing and packaging phpMyAdmin &#8212; phpMyAdmin 5.2.0 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/classic.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="copyright" title="Copyright" href="copyright.html" />
<link rel="next" title="Copyright" href="copyright.html" />
<link rel="prev" title="Security policy" href="security.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="copyright.html" title="Copyright"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="security.html" title="Security policy"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Distributing and packaging phpMyAdmin</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="distributing-and-packaging-phpmyadmin">
<h1>Distributing and packaging phpMyAdmin<a class="headerlink" href="#distributing-and-packaging-phpmyadmin" title="Permalink to this headline"></a></h1>
<p>This document is intended to give pieces of advice to people who want to
redistribute phpMyAdmin inside other software packages such as Linux
distribution or some all in one package including web server and MySQL
server.</p>
<p>Generally, you can customize some basic aspects (paths to some files and
behavior) in <code class="file docutils literal notranslate"><span class="pre">libraries/vendor_config.php</span></code>.</p>
<p>For example, if you want setup script to generate a config file in var, change
<code class="docutils literal notranslate"><span class="pre">SETUP_CONFIG_FILE</span></code> to <code class="file docutils literal notranslate"><span class="pre">/var/lib/phpmyadmin/config.inc.php</span></code> and you
will also probably want to skip directory writable check, so set
<code class="docutils literal notranslate"><span class="pre">SETUP_DIR_WRITABLE</span></code> to false.</p>
<div class="section" id="external-libraries">
<h2>External libraries<a class="headerlink" href="#external-libraries" title="Permalink to this headline"></a></h2>
<p>phpMyAdmin includes several external libraries, you might want to
replace them with system ones if they are available, but please note
that you should test whether the version you provide is compatible with the
one we ship.</p>
<p>Currently known list of external libraries:</p>
<dl class="simple">
<dt>js/vendor</dt><dd><p>jQuery js framework libraries and various js libraries.</p>
</dd>
<dt>vendor/</dt><dd><p>The download kit includes various Composer packages as
dependencies.</p>
</dd>
</dl>
</div>
<div class="section" id="specific-files-licenses">
<h2>Specific files LICENSES<a class="headerlink" href="#specific-files-licenses" title="Permalink to this headline"></a></h2>
<p>phpMyAdmin distributed themes contain some content that is under licenses.</p>
<ul class="simple">
<li><p>The icons of the <cite>Original</cite> and <cite>pmahomme</cite> themes are from the <a class="reference external" href="http://www.famfamfam.com/lab/icons/silk/">Silk Icons</a>.</p></li>
<li><p>Some icons of the <cite>Metro</cite> theme are from the <a class="reference external" href="http://www.famfamfam.com/lab/icons/silk/">Silk Icons</a>.</p></li>
<li><p><cite>themes/*/img/b_rename.svg</cite> Is a <a class="reference external" href="https://thenounproject.com/Icons8/">Icons8</a>, icon from the <a class="reference external" href="https://thenounproject.com/Icons8/collection/android-l-icon-pack/">Android L Icon Pack Collection</a>. The icon <a class="reference external" href="https://thenounproject.com/term/rename/61456/">rename</a>.</p></li>
<li><p><cite>themes/metro/img/user.svg</cite> Is a IcoMoon the <a class="reference external" href="https://github.com/Keyamoon/IcoMoon-Free/blob/master/SVG/114-user.svg">user</a></p></li>
</ul>
<p>CC BY 4.0 or GPL</p>
</div>
<div class="section" id="licenses-for-vendors">
<h2>Licenses for vendors<a class="headerlink" href="#licenses-for-vendors" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li><p>Silk Icons are under the <a class="reference external" href="http://www.famfamfam.com/lab/icons/silk/">CC BY 2.5 or CC BY 3.0</a> licenses.</p></li>
<li><p><cite>rename</cite> from <cite>Icons8</cite> is under the <a class="reference external" href="https://creativecommons.org/publicdomain/zero/1.0/">“public domain”</a> (CC0 1.0) license.</p></li>
<li><p>IcoMoon Free is under <a class="reference external" href="https://github.com/Keyamoon/IcoMoon-Free/blob/master/License.txt">“CC BY 4.0 or GPL”</a>.</p></li>
</ul>
</div>
</div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Distributing and packaging phpMyAdmin</a><ul>
<li><a class="reference internal" href="#external-libraries">External libraries</a></li>
<li><a class="reference internal" href="#specific-files-licenses">Specific files LICENSES</a></li>
<li><a class="reference internal" href="#licenses-for-vendors">Licenses for vendors</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="security.html"
title="previous chapter">Security policy</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="copyright.html"
title="next chapter">Copyright</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/vendors.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="copyright.html" title="Copyright"
>next</a> |</li>
<li class="right" >
<a href="security.html" title="Security policy"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">phpMyAdmin 5.2.0 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Distributing and packaging phpMyAdmin</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; <a href="copyright.html">Copyright</a> 2012 - 2021, The phpMyAdmin devel team.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.4.3.
</div>
</body>
</html>

@ -0,0 +1,176 @@
$(function () {
Functions.checkNumberOfFields();
});
/**
* Holds common parameters such as server, db, table, etc
*
* The content for this is normally loaded from Header.php or
* Response.php and executed by ajax.js
*
* @test-module CommonParams
*/
var CommonParams = function () {
/**
* @var {Object} params An associative array of key value pairs
* @access private
*/
var params = {}; // The returned object is the public part of the module
return {
/**
* Saves all the key value pair that
* are provided in the input array
*
* @param obj hash The input array
*
* @return {void}
*/
setAll: function (obj) {
var updateNavigation = false;
for (var i in obj) {
if (params[i] !== undefined && params[i] !== obj[i]) {
if (i === 'db' || i === 'table') {
updateNavigation = true;
}
}
params[i] = obj[i];
}
if (updateNavigation && $('#pma_navigation_tree').hasClass('synced')) {
Navigation.showCurrent();
}
},
/**
* Retrieves a value given its key
* Returns empty string for undefined values
*
* @param {string} name The key
*
* @return {string}
*/
get: function (name) {
return params[name];
},
/**
* Saves a single key value pair
*
* @param {string} name The key
* @param {string} value The value
*
* @return {CommonParams} For chainability
*/
set: function (name, value) {
var updateNavigation = false;
if (name === 'db' || name === 'table' && params[name] !== value) {
updateNavigation = true;
}
params[name] = value;
if (updateNavigation && $('#pma_navigation_tree').hasClass('synced')) {
Navigation.showCurrent();
}
return this;
},
/**
* Returns the url query string using the saved parameters
*
* @param {string} separator New separator
*
* @return {string}
*/
getUrlQuery: function (separator) {
var sep = typeof separator !== 'undefined' ? separator : '?';
var common = this.get('common_query');
var argsep = CommonParams.get('arg_separator');
if (typeof common === 'string' && common.length > 0) {
// If the last char is the separator, do not add it
// Else add it
common = common.substr(common.length - 1, common.length) === argsep ? common : common + argsep;
}
return Functions.sprintf('%s%sserver=%s' + argsep + 'db=%s' + argsep + 'table=%s', sep, common, encodeURIComponent(this.get('server')), encodeURIComponent(this.get('db')), encodeURIComponent(this.get('table')));
}
};
}();
/**
* Holds common parameters such as server, db, table, etc
*
* The content for this is normally loaded from Header.php or
* Response.php and executed by ajax.js
*/
// eslint-disable-next-line no-unused-vars
var CommonActions = {
/**
* Saves the database name when it's changed
* and reloads the query window, if necessary
*
* @param {string} newDb new_db The name of the new database
*
* @return {void}
*/
setDb: function (newDb) {
if (newDb !== CommonParams.get('db')) {
CommonParams.setAll({
'db': newDb,
'table': ''
});
}
},
/**
* Opens a database in the main part of the page
*
* @param {string} newDb The name of the new database
*
* @return {void}
*/
openDb: function (newDb) {
CommonParams.set('db', newDb).set('table', '');
this.refreshMain(CommonParams.get('opendb_url'));
},
/**
* Refreshes the main frame
*
* @param {any} url Undefined to refresh to the same page
* String to go to a different page, e.g: 'index.php'
* @param {function | undefined} callback
*
* @return {void}
*/
refreshMain: function (url) {
let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
var newUrl = url;
if (!newUrl) {
newUrl = $('#selflink').find('a').attr('href') || window.location.pathname;
newUrl = newUrl.substring(0, newUrl.indexOf('?'));
}
if (newUrl.indexOf('?') !== -1) {
newUrl += CommonParams.getUrlQuery(CommonParams.get('arg_separator'));
} else {
newUrl += CommonParams.getUrlQuery('?');
}
$('<a></a>', {
href: newUrl
}).appendTo('body').trigger('click').remove();
if (typeof callback !== 'undefined') {
AJAX.callback = callback;
}
}
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,13 @@
/**
* Conditionally included if framing is not allowed
*/
if (self === top) {
var styleElement = document.getElementById('cfs-style'); // check if styleElement has already been removed
// to avoid frequently reported js error
if (typeof styleElement !== 'undefined' && styleElement !== null) {
styleElement.parentNode.removeChild(styleElement);
}
} else {
top.location = self.location;
}

@ -0,0 +1,611 @@
AJAX.registerTeardown('database/events.js', function () {
$(document).off('click', 'a.ajax.add_anchor, a.ajax.edit_anchor');
$(document).off('click', 'a.ajax.export_anchor');
$(document).off('click', '#bulkActionExportButton');
$(document).off('click', 'a.ajax.drop_anchor');
$(document).off('click', '#bulkActionDropButton');
$(document).off('change', 'select[name=item_type]');
});
const DatabaseEvents = {
/**
* @var $ajaxDialog Query object containing the reference to the
* dialog that contains the editor
*/
$ajaxDialog: null,
/**
* @var syntaxHiglighter Reference to the codemirror editor
*/
syntaxHiglighter: null,
/**
* @var buttonOptions Object containing options for
* the jQueryUI dialog buttons
*/
buttonOptions: {},
/**
* Validate editor form fields.
*
* @return {bool}
*/
validate: function () {
/**
* @var $elm a jQuery object containing the reference
* to an element that is being validated
*/
var $elm = null; // Common validation. At the very least the name
// and the definition must be provided for an item
$elm = $('table.rte_table').last().find('input[name=item_name]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(Messages.strFormEmpty);
return false;
}
$elm = $('table.rte_table').find('textarea[name=item_definition]');
if ($elm.val() === '') {
if (this.syntaxHiglighter !== null) {
this.syntaxHiglighter.focus();
} else {
$('textarea[name=item_definition]').last().trigger('focus');
}
alert(Messages.strFormEmpty);
return false;
} // The validation has so far passed, so now
// we can validate item-specific fields.
return this.validateCustom();
},
exportDialog: function ($this) {
var $msg = Functions.ajaxShowMessage();
if ($this.attr('id') === 'bulkActionExportButton') {
var combined = {
success: true,
title: Messages.strExport,
message: '',
error: ''
}; // export anchors of all selected rows
var exportAnchors = $('input.checkall:checked').parents('tr').find('.export_anchor');
var count = exportAnchors.length;
var returnCount = 0;
var p = $.when();
exportAnchors.each(function () {
var h = $(this).attr('href');
p = p.then(function () {
return $.get(h, {
'ajax_request': true
}, function (data) {
returnCount++;
if (data.success === true) {
combined.message += '\n' + data.message + '\n';
if (returnCount === count) {
showExport(combined);
}
} else {
// complain even if one export is failing
combined.success = false;
combined.error += '\n' + data.error + '\n';
if (returnCount === count) {
showExport(combined);
}
}
});
});
});
} else {
$.get($this.attr('href'), {
'ajax_request': true
}, showExport);
}
Functions.ajaxRemoveMessage($msg);
function showExport(data) {
if (data.success === true) {
Functions.ajaxRemoveMessage($msg);
/**
* @var button_options Object containing options
* for jQueryUI dialog buttons
*/
var buttonOptions = {};
buttonOptions[Messages.strClose] = function () {
$(this).dialog('close').remove();
};
/**
* Display the dialog to the user
*/
data.message = '<textarea cols="40" rows="15" class="w-100">' + data.message + '</textarea>';
var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
width: 500,
buttons: buttonOptions,
title: data.title
}); // Attach syntax highlighted editor to export dialog
/**
* @var $elm jQuery object containing the reference
* to the Export textarea.
*/
var $elm = $ajaxDialog.find('textarea');
Functions.getSqlEditor($elm);
} else {
Functions.ajaxShowMessage(data.error, false);
}
} // end showExport()
},
// end exportDialog()
editorDialog: function (isNew, $this) {
var that = this;
/**
* @var $edit_row jQuery object containing the reference to
* the row of the the item being edited
* from the list of items
*/
var $editRow = null;
if ($this.hasClass('edit_anchor')) {
// Remember the row of the item being edited for later,
// so that if the edit is successful, we can replace the
// row with info about the modified item.
$editRow = $this.parents('tr');
}
/**
* @var $msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage();
$.get($this.attr('href'), {
'ajax_request': true
}, function (data) {
if (data.success === true) {
// We have successfully fetched the editor form
Functions.ajaxRemoveMessage($msg); // Now define the function that is called when
// the user presses the "Go" button
that.buttonOptions[Messages.strGo] = function () {
// Move the data from the codemirror editor back to the
// textarea, where it can be used in the form submission.
if (typeof CodeMirror !== 'undefined') {
that.syntaxHiglighter.save();
} // Validate editor and submit request, if passed.
if (that.validate()) {
/**
* @var data Form data to be sent in the AJAX request
*/
var data = $('form.rte_form').last().serialize();
$msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
var url = $('form.rte_form').last().attr('action');
$.post(url, data, function (data) {
if (data.success === true) {
// Item created successfully
Functions.ajaxRemoveMessage($msg);
Functions.slidingMessage(data.message);
that.$ajaxDialog.dialog('close'); // If we are in 'edit' mode, we must
// remove the reference to the old row.
if (mode === 'edit' && $editRow !== null) {
$editRow.remove();
} // Sometimes, like when moving a trigger from
// a table to another one, the new row should
// not be inserted into the list. In this case
// "data.insert" will be set to false.
if (data.insert) {
// Insert the new row at the correct
// location in the list of items
/**
* @var text Contains the name of an item from
* the list that is used in comparisons
* to find the correct location where
* to insert a new row.
*/
var text = '';
/**
* @var inserted Whether a new item has been
* inserted in the list or not
*/
var inserted = false;
$('table.data').find('tr').each(function () {
text = $(this).children('td').eq(0).find('strong').text().toUpperCase().trim();
if (text !== '' && text > data.name) {
$(this).before(data.new_row);
inserted = true;
return false;
}
});
if (!inserted) {
// If we didn't manage to insert the row yet,
// it must belong at the end of the list,
// so we insert it there.
$('table.data').append(data.new_row);
} // Fade-in the new row
$('tr.ajaxInsert').show('slow').removeClass('ajaxInsert');
} else if ($('table.data').find('tr').has('td').length === 0) {
// If we are not supposed to insert the new row,
// we will now check if the table is empty and
// needs to be hidden. This will be the case if
// we were editing the only item in the list,
// which we removed and will not be inserting
// something else in its place.
$('table.data').hide('slow', function () {
$('#nothing2display').show('slow');
});
} // Now we have inserted the row at the correct
// position, but surely at least some row classes
// are wrong now. So we will iterate through
// all rows and assign correct classes to them
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$('table.data').find('tr').has('td').each(function () {
rowclass = ct % 2 === 0 ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
}); // If this is the first item being added, remove
// the "No items" message and show the list.
if ($('table.data').find('tr').has('td').length > 0 && $('#nothing2display').is(':visible')) {
$('#nothing2display').hide('slow', function () {
$('table.data').show('slow');
});
}
Navigation.reload();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
} // end "if (that.validate())"
}; // end of function that handles the submission of the Editor
that.buttonOptions[Messages.strClose] = function () {
$(this).dialog('close');
};
/**
* Display the dialog to the user
*/
that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
width: 700,
minWidth: 500,
buttons: that.buttonOptions,
// Issue #15810 - use button titles for modals (eg: new procedure)
// Respect the order: title on href tag, href content, title sent in response
title: $this.attr('title') || $this.text() || $(data.title).text(),
modal: true,
open: function () {
$('#rteDialog').dialog('option', 'max-height', $(window).height());
if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
$('#rteDialog').dialog('option', 'height', $(window).height());
}
$(this).find('input[name=item_name]').trigger('focus');
$(this).find('input.datefield').each(function () {
Functions.addDatepicker($(this).css('width', '95%'), 'date');
});
$(this).find('input.datetimefield').each(function () {
Functions.addDatepicker($(this).css('width', '95%'), 'datetime');
});
$.datepicker.initialized = false;
},
close: function () {
$(this).remove();
}
});
/**
* @var mode Used to remember whether the editor is in
* "Edit" or "Add" mode
*/
var mode = 'add';
if ($('input[name=editor_process_edit]').length > 0) {
mode = 'edit';
} // Attach syntax highlighted editor to the definition
/**
* @var elm jQuery object containing the reference to
* the Definition textarea.
*/
var $elm = $('textarea[name=item_definition]').last();
var linterOptions = {};
linterOptions.eventEditor = true;
that.syntaxHiglighter = Functions.getSqlEditor($elm, {}, 'both', linterOptions);
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.get()
},
dropDialog: function ($this) {
/**
* @var $curr_row Object containing reference to the current row
*/
var $currRow = $this.parents('tr');
/**
* @var question String containing the question to be asked for confirmation
*/
var question = $('<div></div>').text($currRow.children('td').children('.drop_sql').html()); // We ask for confirmation first here, before submitting the ajax request
$this.confirm(question, $this.attr('href'), function (url) {
/**
* @var msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
var params = Functions.getJsConfirmCommonParam(this, $this.getPostData());
$.post(url, params, function (data) {
if (data.success === true) {
/**
* @var $table Object containing reference
* to the main list of elements
*/
var $table = $currRow.parent(); // Check how many rows will be left after we remove
// the one that the user has requested us to remove
if ($table.find('tr').length === 3) {
// If there are two rows left, it means that they are
// the header of the table and the rows that we are
// about to remove, so after the removal there will be
// nothing to show in the table, so we hide it.
$table.hide('slow', function () {
$(this).find('tr.even, tr.odd').remove();
$('.withSelected').remove();
$('#nothing2display').show('slow');
});
} else {
$currRow.hide('slow', function () {
$(this).remove(); // Now we have removed the row from the list, but maybe
// some row classes are wrong now. So we will iterate
// through all rows and assign correct classes to them.
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$table.find('tr').has('td').each(function () {
rowclass = ct % 2 === 1 ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
});
});
} // Get rid of the "Loading" message
Functions.ajaxRemoveMessage($msg); // Show the query that we just executed
Functions.slidingMessage(data.sql_query);
Navigation.reload();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
});
},
dropMultipleDialog: function ($this) {
// We ask for confirmation here
$this.confirm(Messages.strDropRTEitems, '', function () {
/**
* @var msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest); // drop anchors of all selected rows
var dropAnchors = $('input.checkall:checked').parents('tr').find('.drop_anchor');
var success = true;
var count = dropAnchors.length;
var returnCount = 0;
dropAnchors.each(function () {
var $anchor = $(this);
/**
* @var $curr_row Object containing reference to the current row
*/
var $currRow = $anchor.parents('tr');
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
$.post($anchor.attr('href'), params, function (data) {
returnCount++;
if (data.success === true) {
/**
* @var $table Object containing reference
* to the main list of elements
*/
var $table = $currRow.parent(); // Check how many rows will be left after we remove
// the one that the user has requested us to remove
if ($table.find('tr').length === 3) {
// If there are two rows left, it means that they are
// the header of the table and the rows that we are
// about to remove, so after the removal there will be
// nothing to show in the table, so we hide it.
$table.hide('slow', function () {
$(this).find('tr.even, tr.odd').remove();
$('.withSelected').remove();
$('#nothing2display').show('slow');
});
} else {
$currRow.hide('fast', function () {
// we will iterate
// through all rows and assign correct classes to them.
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$table.find('tr').has('td').each(function () {
rowclass = ct % 2 === 1 ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
});
});
$currRow.remove();
}
if (returnCount === count) {
if (success) {
// Get rid of the "Loading" message
Functions.ajaxRemoveMessage($msg);
$('#rteListForm_checkall').prop({
checked: false,
indeterminate: false
});
}
Navigation.reload();
}
} else {
Functions.ajaxShowMessage(data.error, false);
success = false;
if (returnCount === count) {
Navigation.reload();
}
}
}); // end $.post()
}); // end drop_anchors.each()
});
},
/**
* Validate custom editor form fields.
*
* @return {bool}
*/
validateCustom: function () {
/**
* @var elm a jQuery object containing the reference
* to an element that is being validated
*/
var $elm = null;
if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'RECURRING') {
// The interval field must not be empty for recurring events
$elm = this.$ajaxDialog.find('input[name=item_interval_value]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(Messages.strFormEmpty);
return false;
}
} else {
// The execute_at field must not be empty for "once off" events
$elm = this.$ajaxDialog.find('input[name=item_execute_at]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(Messages.strFormEmpty);
return false;
}
}
return true;
}
};
AJAX.registerOnload('database/events.js', function () {
/**
* Attach Ajax event handlers for the Add/Edit functionality.
*/
$(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) {
event.preventDefault();
if ($(this).hasClass('add_anchor')) {
$.datepicker.initialized = false;
}
DatabaseEvents.editorDialog($(this).hasClass('add_anchor'), $(this));
});
/**
* Attach Ajax event handlers for Export
*/
$(document).on('click', 'a.ajax.export_anchor', function (event) {
event.preventDefault();
DatabaseEvents.exportDialog($(this));
}); // end $(document).on()
$(document).on('click', '#bulkActionExportButton', function (event) {
event.preventDefault();
DatabaseEvents.exportDialog($(this));
}); // end $(document).on()
/**
* Attach Ajax event handlers for Drop functionality
*/
$(document).on('click', 'a.ajax.drop_anchor', function (event) {
event.preventDefault();
DatabaseEvents.dropDialog($(this));
}); // end $(document).on()
$(document).on('click', '#bulkActionDropButton', function (event) {
event.preventDefault();
DatabaseEvents.dropMultipleDialog($(this));
}); // end $(document).on()
/**
* Attach Ajax event handlers for the "Change event type" functionality, so that the correct
* rows are shown in the editor when changing the event type
*/
$(document).on('change', 'select[name=item_type]', function () {
$(this).closest('table').find('tr.recurring_event_row, tr.onetime_event_row').toggle();
});
});

@ -0,0 +1,170 @@
/**
* @fileoverview function used in server privilege pages
* @name Database Operations
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*
*/
/**
* Ajax event handlers here for /database/operations
*
* Actions Ajaxified here:
* Rename Database
* Copy Database
* Change Charset
* Drop Database
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('database/operations.js', function () {
$(document).off('submit', '#rename_db_form.ajax');
$(document).off('submit', '#copy_db_form.ajax');
$(document).off('submit', '#change_db_charset_form.ajax');
$(document).off('click', '#drop_db_anchor.ajax');
});
AJAX.registerOnload('database/operations.js', function () {
/**
* Ajax event handlers for 'Rename Database'
*/
$(document).on('submit', '#rename_db_form.ajax', function (event) {
event.preventDefault();
if (Functions.emptyCheckTheField(this, 'newname')) {
Functions.ajaxShowMessage(Messages.strFormEmpty, false, 'error');
return false;
}
var oldDbName = CommonParams.get('db');
var newDbName = $('#new_db_name').val();
if (newDbName === oldDbName) {
Functions.ajaxShowMessage(Messages.strDatabaseRenameToSameName, false, 'error');
return false;
}
var $form = $(this);
var question = Functions.escapeHtml('CREATE DATABASE ' + newDbName + ' / DROP DATABASE ' + oldDbName);
Functions.prepareForAjaxRequest($form);
$form.confirm(question, $form.attr('action'), function (url) {
Functions.ajaxShowMessage(Messages.strRenamingDatabases, false);
$.post(url, $('#rename_db_form').serialize() + CommonParams.get('arg_separator') + 'is_js_confirmed=1', function (data) {
if (typeof data !== 'undefined' && data.success === true) {
Functions.ajaxShowMessage(data.message);
CommonParams.set('db', data.newname);
Navigation.reload(function () {
$('#pma_navigation_tree').find('a:not(\'.expander\')').each(function () {
var $thisAnchor = $(this);
if ($thisAnchor.text() === data.newname) {
// simulate a click on the new db name
// in navigation
$thisAnchor.trigger('click');
}
});
});
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
});
}); // end Rename Database
/**
* Ajax Event Handler for 'Copy Database'
*/
$(document).on('submit', '#copy_db_form.ajax', function (event) {
event.preventDefault();
if (Functions.emptyCheckTheField(this, 'newname')) {
Functions.ajaxShowMessage(Messages.strFormEmpty, false, 'error');
return false;
}
Functions.ajaxShowMessage(Messages.strCopyingDatabase, false);
var $form = $(this);
Functions.prepareForAjaxRequest($form);
$.post($form.attr('action'), $form.serialize(), function (data) {
// use messages that stay on screen
$('.alert-success, .alert-danger').fadeOut();
if (typeof data !== 'undefined' && data.success === true) {
if ($('#checkbox_switch').is(':checked')) {
CommonParams.set('db', data.newname);
CommonActions.refreshMain(false, function () {
Functions.ajaxShowMessage(data.message);
});
} else {
CommonParams.set('db', data.db);
Functions.ajaxShowMessage(data.message);
}
Navigation.reload();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
}); // end copy database
/**
* Change tables columns visible only if change tables is checked
*/
$('#span_change_all_tables_columns_collations').hide();
$('#checkbox_change_all_tables_collations').on('click', function () {
$('#span_change_all_tables_columns_collations').toggle();
});
/**
* Ajax Event handler for 'Change Charset' of the database
*/
$(document).on('submit', '#change_db_charset_form.ajax', function (event) {
event.preventDefault();
var $form = $(this);
Functions.prepareForAjaxRequest($form);
Functions.ajaxShowMessage(Messages.strChangingCharset);
$.post($form.attr('action'), $form.serialize(), function (data) {
if (typeof data !== 'undefined' && data.success === true) {
Functions.ajaxShowMessage(data.message);
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
}); // end change charset
/**
* Ajax event handlers for Drop Database
*/
$(document).on('click', '#drop_db_anchor.ajax', function (event) {
event.preventDefault();
var $link = $(this);
/**
* @var {String} question String containing the question to be asked for confirmation
*/
var question = Messages.strDropDatabaseStrongWarning + ' ';
question += Functions.sprintf(Messages.strDoYouReally, 'DROP DATABASE `' + Functions.escapeHtml(CommonParams.get('db') + '`'));
var params = Functions.getJsConfirmCommonParam(this, $link.getPostData());
$(this).confirm(question, $(this).attr('href'), function (url) {
Functions.ajaxShowMessage(Messages.strProcessingRequest);
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success) {
// Database deleted successfully, refresh both the frames
Navigation.reload();
CommonParams.set('db', '');
CommonActions.refreshMain('index.php?route=/server/databases', function () {
Functions.ajaxShowMessage(data.message);
});
} else {
Functions.ajaxShowMessage(data.error, false);
}
});
});
});
});

@ -0,0 +1,147 @@
/**
* @fileoverview function used in QBE for DB
* @name Database Operations
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*
*/
/* global sprintf */
// js/vendor/sprintf.js
function getFormatsText() {
return {
'=': ' = \'%s\'',
'>': ' > \'%s\'',
'>=': ' >= \'%s\'',
'<': ' < \'%s\'',
'<=': ' <= \'%s\'',
'!=': ' != \'%s\'',
'LIKE': ' LIKE \'%s\'',
'LIKE %...%': ' LIKE \'%%%s%%\'',
'NOT LIKE': ' NOT LIKE \'%s\'',
'NOT LIKE %...%': ' NOT LIKE \'%%%s%%\'',
'BETWEEN': ' BETWEEN \'%s\'',
'NOT BETWEEN': ' NOT BETWEEN \'%s\'',
'IS NULL': ' \'%s\' IS NULL',
'IS NOT NULL': ' \'%s\' IS NOT NULL',
'REGEXP': ' REGEXP \'%s\'',
'REGEXP ^...$': ' REGEXP \'^%s$\'',
'NOT REGEXP': ' NOT REGEXP \'%s\''
};
}
function generateCondition(criteriaDiv, table) {
var query = '`' + Functions.escapeBacktick(table.val()) + '`.';
query += '`' + Functions.escapeBacktick(table.siblings('.columnNameSelect').first().val()) + '`';
if (criteriaDiv.find('.criteria_rhs').first().val() === 'text') {
var formatsText = getFormatsText();
query += sprintf(formatsText[criteriaDiv.find('.criteria_op').first().val()], Functions.escapeSingleQuote(criteriaDiv.find('.rhs_text_val').first().val()));
} else {
query += ' ' + criteriaDiv.find('.criteria_op').first().val();
query += ' `' + Functions.escapeBacktick(criteriaDiv.find('.tableNameSelect').first().val()) + '`.';
query += '`' + Functions.escapeBacktick(criteriaDiv.find('.columnNameSelect').first().val()) + '`';
}
return query;
} // eslint-disable-next-line no-unused-vars
function generateWhereBlock() {
var count = 0;
var query = '';
$('.tableNameSelect').each(function () {
var criteriaDiv = $(this).siblings('.jsCriteriaOptions').first();
var useCriteria = $(this).siblings('.criteria_col').first();
if ($(this).val() !== '' && useCriteria.prop('checked')) {
if (count > 0) {
criteriaDiv.find('input.logical_op').each(function () {
if ($(this).prop('checked')) {
query += ' ' + $(this).val() + ' ';
}
});
}
query += generateCondition(criteriaDiv, $(this));
count++;
}
});
return query;
}
function generateJoin(newTable, tableAliases, fk) {
var query = '';
query += ' \n\tLEFT JOIN ' + '`' + Functions.escapeBacktick(newTable) + '`';
if (tableAliases[fk.TABLE_NAME][0] !== '') {
query += ' AS `' + Functions.escapeBacktick(tableAliases[newTable][0]) + '`';
query += ' ON `' + Functions.escapeBacktick(tableAliases[fk.TABLE_NAME][0]) + '`';
} else {
query += ' ON `' + Functions.escapeBacktick(fk.TABLE_NAME) + '`';
}
query += '.`' + fk.COLUMN_NAME + '`';
if (tableAliases[fk.REFERENCED_TABLE_NAME][0] !== '') {
query += ' = `' + Functions.escapeBacktick(tableAliases[fk.REFERENCED_TABLE_NAME][0]) + '`';
} else {
query += ' = `' + Functions.escapeBacktick(fk.REFERENCED_TABLE_NAME) + '`';
}
query += '.`' + fk.REFERENCED_COLUMN_NAME + '`';
return query;
}
function existReference(table, fk, usedTables) {
var isReferredBy = fk.TABLE_NAME === table && usedTables.includes(fk.REFERENCED_TABLE_NAME);
var isReferencedBy = fk.REFERENCED_TABLE_NAME === table && usedTables.includes(fk.TABLE_NAME);
return isReferredBy || isReferencedBy;
}
function tryJoinTable(table, tableAliases, usedTables, foreignKeys) {
for (var i = 0; i < foreignKeys.length; i++) {
var fk = foreignKeys[i];
if (existReference(table, fk, usedTables)) {
return generateJoin(table, tableAliases, fk);
}
}
return '';
}
function appendTable(table, tableAliases, usedTables, foreignKeys) {
var query = tryJoinTable(table, tableAliases, usedTables, foreignKeys);
if (query === '') {
if (usedTables.length > 0) {
query += '\n\t, ';
}
query += '`' + Functions.escapeBacktick(table) + '`';
if (tableAliases[table][0] !== '') {
query += ' AS `' + Functions.escapeBacktick(tableAliases[table][0]) + '`';
}
}
usedTables.push(table);
return query;
} // eslint-disable-next-line no-unused-vars
function generateFromBlock(tableAliases, foreignKeys) {
var usedTables = [];
var query = '';
for (var table in tableAliases) {
if (tableAliases.hasOwnProperty(table)) {
query += appendTable(table, tableAliases, usedTables, foreignKeys);
}
}
return query;
}

@ -0,0 +1,260 @@
/**
* JavaScript functions used on Database Search page
*
* @requires jQuery
* @requires js/functions.js
*
* @package PhpMyAdmin
*/
/* global makeGrid */
// js/makegrid.js
/**
* AJAX script for the Database Search page.
*
* Actions ajaxified here:
* Retrieve result of SQL query
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('database/search.js', function () {
$('a.browse_results').off('click');
$('a.delete_results').off('click');
$('#buttonGo').off('click');
$('#togglesearchresultlink').off('click');
$('#togglequerybox').off('click');
$('#togglesearchformlink').off('click');
$('#select_all').off('click');
$('#unselect_all').off('click');
$(document).off('submit', '#db_search_form.ajax');
});
AJAX.registerOnload('database/search.js', function () {
/** Hide the table link in the initial search result */
var icon = Functions.getImage('s_tbl', '', {
'id': 'table-image'
}).toString();
$('#table-info').prepend(icon).hide();
/** Hide the browse and deleted results in the new search criteria */
$('#buttonGo').on('click', function () {
$('#table-info').hide();
$('#browse-results').hide();
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
});
/**
* Prepare a div containing a link for toggle the search results
*/
$('#togglesearchresultsdiv')
/** don't show it until we have results on-screen */
.hide();
/**
* Changing the displayed text according to
* the hide/show criteria in search result forms
*/
$('#togglesearchresultlink').html(Messages.strHideSearchResults).on('click', function () {
var $link = $(this);
$('#searchresults').slideToggle();
if ($link.text() === Messages.strHideSearchResults) {
$link.text(Messages.strShowSearchResults);
} else {
$link.text(Messages.strHideSearchResults);
}
/** avoid default click action */
return false;
});
/**
* Prepare a div containing a link for toggle the search form,
* otherwise it's incorrectly displayed after a couple of clicks
*/
$('#togglesearchformdiv').hide(); // don't show it until we have results on-screen
/**
* Changing the displayed text according to
* the hide/show criteria in search form
*/
$('#togglequerybox').hide().on('click', function () {
var $link = $(this);
$('#sqlqueryform').slideToggle('medium');
if ($link.text() === Messages.strHideQueryBox) {
$link.text(Messages.strShowQueryBox);
} else {
$link.text(Messages.strHideQueryBox);
}
/** avoid default click action */
return false;
});
/** don't show it until we have results on-screen */
/**
* Changing the displayed text according to
* the hide/show criteria in search criteria form
*/
$('#togglesearchformlink').html(Messages.strShowSearchCriteria).on('click', function () {
var $link = $(this);
$('#db_search_form').slideToggle();
if ($link.text() === Messages.strHideSearchCriteria) {
$link.text(Messages.strShowSearchCriteria);
} else {
$link.text(Messages.strHideSearchCriteria);
}
/** avoid default click action */
return false;
});
/*
* Ajax Event handler for retrieving the results from a table
*/
$(document).on('click', 'a.browse_results', function (e) {
e.preventDefault();
/** Hides the results shown by the delete criteria */
var $msg = Functions.ajaxShowMessage(Messages.strBrowsing, false);
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
/** Load the browse results to the page */
$('#table-info').show();
var tableName = $(this).data('table-name');
$('#table-link').attr({
'href': $(this).attr('href')
}).text(tableName);
var url = $(this).attr('href') + '#searchresults';
var browseSql = $(this).data('browse-sql');
var params = {
'ajax_request': true,
'is_js_confirmed': true,
'sql_query': browseSql
};
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success) {
$('#browse-results').html(data.message);
Functions.ajaxRemoveMessage($msg);
$('.table_results').each(function () {
makeGrid(this, true, true, true, true);
});
$('#browse-results').show();
Functions.highlightSql($('#browse-results'));
$('html, body').animate({
scrollTop: $('#browse-results').offset().top
}, 1000);
} else {
Functions.ajaxShowMessage(data.error, false);
}
});
});
/*
* Ajax Event handler for deleting the results from a table
*/
$(document).on('click', 'a.delete_results', function (e) {
e.preventDefault();
/** Hides the results shown by the browse criteria */
$('#table-info').hide();
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
/** Conformation message for deletion */
var msg = Functions.sprintf(Messages.strConfirmDeleteResults, $(this).data('table-name'));
if (confirm(msg)) {
var $msg = Functions.ajaxShowMessage(Messages.strDeleting, false);
/** Load the deleted option to the page*/
$('#sqlqueryform').html('');
var params = {
'ajax_request': true,
'is_js_confirmed': true,
'sql_query': $(this).data('delete-sql')
};
var url = $(this).attr('href');
$.post(url, params, function (data) {
if (typeof data === 'undefined' || !data.success) {
Functions.ajaxShowMessage(data.error, false);
return;
}
$('#sqlqueryform').html(data.sql_query);
/** Refresh the search results after the deletion */
$('#buttonGo').trigger('click');
$('#togglequerybox').html(Messages.strHideQueryBox);
/** Show the results of the deletion option */
$('#browse-results').hide();
$('#sqlqueryform').show();
$('#togglequerybox').show();
$('html, body').animate({
scrollTop: $('#browse-results').offset().top
}, 1000);
Functions.ajaxRemoveMessage($msg);
});
}
});
/**
* Ajax Event handler for retrieving the result of an SQL Query
*/
$(document).on('submit', '#db_search_form.ajax', function (event) {
event.preventDefault();
if ($('#criteriaTables :selected').length === 0) {
Functions.ajaxShowMessage(Messages.strNoTableSelected);
return;
}
var $msgbox = Functions.ajaxShowMessage(Messages.strSearching, false); // jQuery object to reuse
var $form = $(this);
Functions.prepareForAjaxRequest($form);
var url = $form.serialize() + CommonParams.get('arg_separator') + 'submit_search=' + $('#buttonGo').val();
$.post($form.attr('action'), url, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
// found results
$('#searchresults').html(data.message);
$('#togglesearchresultlink') // always start with the Show message
.text(Messages.strHideSearchResults);
$('#togglesearchresultsdiv') // now it's time to show the div containing the link
.show();
$('#searchresults').show();
$('#db_search_form') // workaround for Chrome problem (bug #3168569)
.slideToggle().hide();
$('#togglesearchformlink') // always start with the Show message
.text(Messages.strShowSearchCriteria);
$('#togglesearchformdiv') // now it's time to show the div containing the link
.show();
} else {
// error message (zero rows)
$('#searchresults').html(data.error).show();
}
Functions.ajaxRemoveMessage($msgbox);
});
});
$('#select_all').on('click', function () {
Functions.setSelectOptions('db_search', 'criteriaTables[]', true);
return false;
});
$('#unselect_all').on('click', function () {
Functions.setSelectOptions('db_search', 'criteriaTables[]', false);
return false;
});
}); // end $()

@ -0,0 +1,105 @@
/**
* Unbind all event handlers before tearing down the page
*/
AJAX.registerTeardown('database/tracking.js', function () {
$('body').off('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]');
$('body').off('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]');
$('body').off('click', 'a.delete_tracking_anchor.ajax');
});
/**
* Bind event handlers
*/
AJAX.registerOnload('database/tracking.js', function () {
var $versions = $('#versions');
$versions.find('tr').first().find('th').append($('<div class="sorticon"></div>'));
$versions.tablesorter({
sortList: [[1, 0]],
headers: {
0: {
sorter: false
},
2: {
sorter: 'integer'
},
5: {
sorter: false
},
6: {
sorter: false
},
7: {
sorter: false
}
}
});
var $noVersions = $('#noversions');
$noVersions.find('tr').first().find('th').append($('<div class="sorticon"></div>'));
$noVersions.tablesorter({
sortList: [[1, 0]],
headers: {
0: {
sorter: false
},
2: {
sorter: false
}
}
});
var $body = $('body');
/**
* Handles multi submit for tracked tables
*/
$body.on('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]', function (e) {
e.preventDefault();
var $button = $(this);
var $form = $button.parent('form');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
if ($button.val() === 'delete_tracking') {
var question = Messages.strDeleteTrackingDataMultiple;
$button.confirm(question, $form.attr('action'), function (url) {
Functions.ajaxShowMessage(Messages.strDeletingTrackingData);
AJAX.source = $form;
$.post(url, submitData, AJAX.responseHandler);
});
} else {
Functions.ajaxShowMessage();
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
}
});
/**
* Handles multi submit for untracked tables
*/
$body.on('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]', function (e) {
e.preventDefault();
var $button = $(this);
var $form = $button.parent('form');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
Functions.ajaxShowMessage();
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
});
/**
* Ajax Event handler for 'Delete tracking'
*/
$body.on('click', 'a.delete_tracking_anchor.ajax', function (e) {
e.preventDefault();
var $anchor = $(this);
var question = Messages.strDeleteTrackingData;
$anchor.confirm(question, $anchor.attr('href'), function (url) {
Functions.ajaxShowMessage(Messages.strDeletingTrackingData);
AJAX.source = $anchor;
var argSep = CommonParams.get('arg_separator');
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
params += argSep + 'ajax_page_request=1';
$.post(url, params, AJAX.responseHandler);
});
});
});

@ -0,0 +1,74 @@
/**
* Initializes the data required to run Designer, then fires it up.
*/
/* global DesignerOfflineDB */
// js/designer/database.js
/* global DesignerHistory */
// js/designer/history.js
/* global DesignerMove */
// js/designer/move.js
/* global DesignerPage */
// js/designer/page.js
/* global designerConfig */
// templates/database/designer/main.twig
/* eslint-disable no-unused-vars */
var jTabs;
var hTabs;
var contr;
var displayField;
var server;
var selectedPage;
/* eslint-enable no-unused-vars */
var db;
var designerTablesEnabled;
AJAX.registerTeardown('designer/init.js', function () {
$('.trigger').off('click');
});
AJAX.registerOnload('designer/init.js', function () {
$('.trigger').on('click', function () {
$('.panel').toggle('fast');
$(this).toggleClass('active');
$('#ab').accordion('refresh');
return false;
});
jTabs = designerConfig.scriptTables.j_tabs;
hTabs = designerConfig.scriptTables.h_tabs;
contr = designerConfig.scriptContr;
displayField = designerConfig.scriptDisplayField;
server = designerConfig.server;
selectedPage = designerConfig.displayPage;
db = designerConfig.db;
designerTablesEnabled = designerConfig.tablesEnabled;
DesignerMove.main();
if (!designerTablesEnabled) {
DesignerOfflineDB.open(function (success) {
if (success) {
DesignerPage.showTablesInLandingPage(db);
}
});
}
$('#query_Aggregate_Button').on('click', function () {
$('#query_Aggregate').css('display', 'none');
});
$('#query_having_button').on('click', function () {
$('#query_having').css('display', 'none');
});
$('#query_rename_to_button').on('click', function () {
$('#query_rename_to').css('display', 'none');
});
$('#build_query_button').on('click', function () {
DesignerHistory.buildQuery('SQL Query on Database', 0);
});
$('#query_where_button').on('click', function () {
$('#query_where').css('display', 'none');
});
});

@ -0,0 +1,208 @@
/* global DesignerOfflineDB */
// js/designer/database.js
// eslint-disable-next-line no-unused-vars
/* global db, selectedPage:writable */
// js/designer/init.js
/* global DesignerMove */
// js/designer/move.js
/* global DesignerObjects */
// js/designer/objects.js
var DesignerPage = {};
DesignerPage.showTablesInLandingPage = function (db) {
DesignerPage.loadFirstPage(db, function (page) {
if (page) {
DesignerPage.loadHtmlForPage(page.pgNr);
selectedPage = page.pgNr;
} else {
DesignerPage.showNewPageTables(true);
}
});
};
DesignerPage.saveToNewPage = function (db, pageName, tablePositions, callback) {
DesignerPage.createNewPage(db, pageName, function (page) {
if (page) {
var tblCords = [];
var saveCallback = function (id) {
tblCords.push(id);
if (tablePositions.length === tblCords.length) {
page.tblCords = tblCords;
DesignerOfflineDB.addObject('pdf_pages', page);
}
};
for (var pos = 0; pos < tablePositions.length; pos++) {
tablePositions[pos].pdfPgNr = page.pgNr;
DesignerPage.saveTablePositions(tablePositions[pos], saveCallback);
}
if (typeof callback !== 'undefined') {
callback(page);
}
}
});
};
DesignerPage.saveToSelectedPage = function (db, pageId, pageName, tablePositions, callback) {
DesignerPage.deletePage(pageId);
DesignerPage.saveToNewPage(db, pageName, tablePositions, function (page) {
if (typeof callback !== 'undefined') {
callback(page);
}
selectedPage = page.pgNr;
});
};
DesignerPage.createNewPage = function (db, pageName, callback) {
var newPage = new DesignerObjects.PdfPage(db, pageName);
DesignerOfflineDB.addObject('pdf_pages', newPage, function (pgNr) {
newPage.pgNr = pgNr;
if (typeof callback !== 'undefined') {
callback(newPage);
}
});
};
DesignerPage.saveTablePositions = function (positions, callback) {
DesignerOfflineDB.addObject('table_coords', positions, callback);
};
DesignerPage.createPageList = function (db, callback) {
DesignerOfflineDB.loadAllObjects('pdf_pages', function (pages) {
var html = '';
for (var p = 0; p < pages.length; p++) {
var page = pages[p];
if (page.dbName === db) {
html += '<option value="' + page.pgNr + '">';
html += Functions.escapeHtml(page.pageDescr) + '</option>';
}
}
if (typeof callback !== 'undefined') {
callback(html);
}
});
};
DesignerPage.deletePage = function (pageId, callback) {
DesignerOfflineDB.loadObject('pdf_pages', pageId, function (page) {
if (page) {
for (var i = 0; i < page.tblCords.length; i++) {
DesignerOfflineDB.deleteObject('table_coords', page.tblCords[i]);
}
DesignerOfflineDB.deleteObject('pdf_pages', pageId, callback);
}
});
};
DesignerPage.loadFirstPage = function (db, callback) {
DesignerOfflineDB.loadAllObjects('pdf_pages', function (pages) {
var firstPage = null;
for (var i = 0; i < pages.length; i++) {
var page = pages[i];
if (page.dbName === db) {
// give preference to a page having same name as the db
if (page.pageDescr === db) {
callback(page);
return;
}
if (firstPage === null) {
firstPage = page;
}
}
}
callback(firstPage);
});
};
DesignerPage.showNewPageTables = function (check) {
var allTables = $('#id_scroll_tab').find('td input:checkbox');
allTables.prop('checked', check);
for (var tab = 0; tab < allTables.length; tab++) {
var input = allTables[tab];
if (input.value) {
var element = document.getElementById(input.value);
element.style.top = DesignerPage.getRandom(550, 20) + 'px';
element.style.left = DesignerPage.getRandom(700, 20) + 'px';
DesignerMove.visibleTab(input, input.value);
}
}
selectedPage = -1;
$('#page_name').text(Messages.strUntitled);
DesignerMove.markUnsaved();
};
DesignerPage.loadHtmlForPage = function (pageId) {
DesignerPage.showNewPageTables(true);
DesignerPage.loadPageObjects(pageId, function (page, tblCords) {
$('#name-panel').find('#page_name').text(page.pageDescr);
var tableMissing = false;
for (var t = 0; t < tblCords.length; t++) {
var tbId = db + '.' + tblCords[t].tableName;
var table = document.getElementById(tbId);
if (table === null) {
tableMissing = true;
continue;
}
table.style.top = tblCords[t].y + 'px';
table.style.left = tblCords[t].x + 'px';
var checkbox = document.getElementById('check_vis_' + tbId);
checkbox.checked = true;
DesignerMove.visibleTab(checkbox, checkbox.value);
}
DesignerMove.markSaved();
if (tableMissing === true) {
DesignerMove.markUnsaved();
Functions.ajaxShowMessage(Messages.strSavedPageTableMissing);
}
selectedPage = page.pgNr;
});
};
DesignerPage.loadPageObjects = function (pageId, callback) {
DesignerOfflineDB.loadObject('pdf_pages', pageId, function (page) {
var tblCords = [];
var count = page.tblCords.length;
for (var i = 0; i < count; i++) {
DesignerOfflineDB.loadObject('table_coords', page.tblCords[i], function (tblCord) {
tblCords.push(tblCord);
if (tblCords.length === count) {
if (typeof callback !== 'undefined') {
callback(page, tblCords);
}
}
});
}
});
};
DesignerPage.getRandom = function (max, min) {
var val = Math.random() * (max - min) + min;
return Math.floor(val);
};

@ -0,0 +1,12 @@
AJAX.registerOnload('export_output.js', function () {
$(document).on('keydown', function (e) {
if ((e.which || e.keyCode) === 116) {
e.preventDefault();
$('#export_refresh_form').trigger('submit');
}
});
$('.export_refresh_btn').on('click', function (e) {
e.preventDefault();
$('#export_refresh_form').trigger('submit');
});
});

@ -0,0 +1,368 @@
/**
* @fileoverview functions used in GIS data editor
*
* @requires jQuery
*
*/
/* global addZoomPanControllers, storeGisSvgRef, selectVisualization, styleOSM, zoomAndPan */
// js/table/gis_visualization.js
/* global themeImagePath */
// templates/javascript/variables.twig
// eslint-disable-next-line no-unused-vars
var gisEditorLoaded = false;
/**
* Closes the GIS data editor and perform necessary clean up work.
*/
function closeGISEditor() {
$('#popup_background').fadeOut('fast');
$('#gis_editor').fadeOut('fast', function () {
$(this).empty();
});
}
/**
* Prepares the HTML received via AJAX.
*/
function prepareJSVersion() {
// Change the text on the submit button
$('#gis_editor').find('input[name=\'gis_data[save]\']').val(Messages.strCopy).insertAfter($('#gis_data_textarea')).before('<br><br>'); // Add close and cancel links
$('#gis_data_editor').prepend('<a class="close_gis_editor" href="#">' + Messages.strClose + '</a>');
$('<a class="cancel_gis_editor" href="#"> ' + Messages.strCancel + '</a>').insertAfter($('input[name=\'gis_data[save]\']')); // Remove the unnecessary text
$('div#gis_data_output p').remove(); // Remove 'add' buttons and add links
$('#gis_editor').find('input.add').each(function () {
var $button = $(this);
$button.addClass('addJs').removeClass('add');
var classes = $button.attr('class');
$button.replaceWith('<a class="' + classes + '" name="' + $button.attr('name') + '" href="#">+ ' + $button.val() + '</a>');
});
}
/**
* Returns the HTML for a data point.
*
* @param {number} pointNumber point number
* @param {string} prefix prefix of the name
* @return {string} the HTML for a data point
*/
function addDataPoint(pointNumber, prefix) {
return '<br>' + Functions.sprintf(Messages.strPointN, pointNumber + 1) + ': ' + '<label for="x">' + Messages.strX + '</label>' + '<input type="text" name="' + prefix + '[' + pointNumber + '][x]" value="">' + '<label for="y">' + Messages.strY + '</label>' + '<input type="text" name="' + prefix + '[' + pointNumber + '][y]" value="">';
}
/**
* Initialize the visualization in the GIS data editor.
*/
function initGISEditorVisualization() {
storeGisSvgRef(); // Loads either SVG or OSM visualization based on the choice
selectVisualization(); // Adds necessary styles to the div that contains the openStreetMap
styleOSM(); // Adds controllers for zooming and panning
addZoomPanControllers();
zoomAndPan();
}
/**
* Loads JavaScript files and the GIS editor.
*
* @param value current value of the geometry field
* @param field field name
* @param type geometry type
* @param inputName name of the input field
* @param token token
*/
// eslint-disable-next-line no-unused-vars
function loadJSAndGISEditor(value, field, type, inputName) {
var head = document.getElementsByTagName('head')[0];
var script;
script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'js/dist/table/gis_visualization.js';
head.appendChild(script); // OpenLayers.js is BIG and takes time. So asynchronous loading would not work.
// Load the JS and do a callback to load the content for the GIS Editor.
script = document.createElement('script');
script.type = 'text/javascript';
script.onreadystatechange = function () {
if (this.readyState === 'complete') {
loadGISEditor(value, field, type, inputName);
}
};
script.onload = function () {
loadGISEditor(value, field, type, inputName);
};
script.onerror = function () {
loadGISEditor(value, field, type, inputName);
};
script.src = 'js/vendor/openlayers/OpenLayers.js';
head.appendChild(script);
gisEditorLoaded = true;
}
/**
* Loads the GIS editor via AJAX
*
* @param value current value of the geometry field
* @param field field name
* @param type geometry type
* @param inputName name of the input field
*/
function loadGISEditor(value, field, type, inputName) {
var $gisEditor = $('#gis_editor');
$.post('index.php?route=/gis-data-editor', {
'field': field,
'value': value,
'type': type,
'input_name': inputName,
'get_gis_editor': true,
'ajax_request': true,
'server': CommonParams.get('server')
}, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$gisEditor.html(data.gis_editor);
initGISEditorVisualization();
prepareJSVersion();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}, 'json');
}
/**
* Opens up the dialog for the GIS data editor.
*/
// eslint-disable-next-line no-unused-vars
function openGISEditor() {
// Center the popup
var windowWidth = document.documentElement.clientWidth;
var windowHeight = document.documentElement.clientHeight;
var popupWidth = windowWidth * 0.9;
var popupHeight = windowHeight * 0.9;
var popupOffsetTop = windowHeight / 2 - popupHeight / 2;
var popupOffsetLeft = windowWidth / 2 - popupWidth / 2;
var $gisEditor = $('#gis_editor');
var $background = $('#popup_background');
$gisEditor.css({
'top': popupOffsetTop,
'left': popupOffsetLeft,
'width': popupWidth,
'height': popupHeight
});
$background.css({
'opacity': '0.7'
});
$gisEditor.append('<div id="gis_data_editor">' + '<img class="ajaxIcon" id="loadingMonitorIcon" src="' + themeImagePath + 'ajax_clock_small.gif" alt="">' + '</div>'); // Make it appear
$background.fadeIn('fast');
$gisEditor.fadeIn('fast');
}
/**
* Prepare and insert the GIS data in Well Known Text format
* to the input field.
*/
function insertDataAndClose() {
var $form = $('form#gis_data_editor_form');
var inputName = $form.find('input[name=\'input_name\']').val();
var argsep = CommonParams.get('arg_separator');
$.post('index.php?route=/gis-data-editor', $form.serialize() + argsep + 'generate=true' + argsep + 'ajax_request=true', function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$('input[name=\'' + inputName + '\']').val(data.result);
} else {
Functions.ajaxShowMessage(data.error, false);
}
}, 'json');
closeGISEditor();
}
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('gis_data_editor.js', function () {
$(document).off('click', '#gis_editor input[name=\'gis_data[save]\']');
$(document).off('submit', '#gis_editor');
$(document).off('change', '#gis_editor input[type=\'text\']');
$(document).off('change', '#gis_editor select.gis_type');
$(document).off('click', '#gis_editor a.close_gis_editor, #gis_editor a.cancel_gis_editor');
$(document).off('click', '#gis_editor a.addJs.addPoint');
$(document).off('click', '#gis_editor a.addLine.addJs');
$(document).off('click', '#gis_editor a.addJs.addPolygon');
$(document).off('click', '#gis_editor a.addJs.addGeom');
});
AJAX.registerOnload('gis_data_editor.js', function () {
/**
* Prepares and insert the GIS data to the input field on clicking 'copy'.
*/
$(document).on('click', '#gis_editor input[name=\'gis_data[save]\']', function (event) {
event.preventDefault();
insertDataAndClose();
});
/**
* Prepares and insert the GIS data to the input field on pressing 'enter'.
*/
$(document).on('submit', '#gis_editor', function (event) {
event.preventDefault();
insertDataAndClose();
});
/**
* Trigger asynchronous calls on data change and update the output.
*/
$(document).on('change', '#gis_editor input[type=\'text\']', function () {
var $form = $('form#gis_data_editor_form');
var argsep = CommonParams.get('arg_separator');
$.post('index.php?route=/gis-data-editor', $form.serialize() + argsep + 'generate=true' + argsep + 'ajax_request=true', function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$('#gis_data_textarea').val(data.result);
$('#placeholder').empty().removeClass('hasSVG').html(data.visualization);
$('#openlayersmap').empty();
/* TODO: the gis_data_editor should rather return JSON than JS code to eval */
// eslint-disable-next-line no-eval
eval(data.openLayers);
initGISEditorVisualization();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}, 'json');
});
/**
* Update the form on change of the GIS type.
*/
$(document).on('change', '#gis_editor select.gis_type', function () {
var $gisEditor = $('#gis_editor');
var $form = $('form#gis_data_editor_form');
var argsep = CommonParams.get('arg_separator');
$.post('index.php?route=/gis-data-editor', $form.serialize() + argsep + 'get_gis_editor=true' + argsep + 'ajax_request=true', function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$gisEditor.html(data.gis_editor);
initGISEditorVisualization();
prepareJSVersion();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}, 'json');
});
/**
* Handles closing of the GIS data editor.
*/
$(document).on('click', '#gis_editor a.close_gis_editor, #gis_editor a.cancel_gis_editor', function () {
closeGISEditor();
});
/**
* Handles adding data points
*/
$(document).on('click', '#gis_editor a.addJs.addPoint', function () {
var $a = $(this);
var name = $a.attr('name'); // Eg. name = gis_data[0][MULTIPOINT][add_point] => prefix = gis_data[0][MULTIPOINT]
var prefix = name.substr(0, name.length - 11); // Find the number of points
var $noOfPointsInput = $('input[name=\'' + prefix + '[no_of_points]' + '\']');
var noOfPoints = parseInt($noOfPointsInput.val(), 10); // Add the new data point
var html = addDataPoint(noOfPoints, prefix);
$a.before(html);
$noOfPointsInput.val(noOfPoints + 1);
});
/**
* Handles adding linestrings and inner rings
*/
$(document).on('click', '#gis_editor a.addLine.addJs', function () {
var $a = $(this);
var name = $a.attr('name'); // Eg. name = gis_data[0][MULTILINESTRING][add_line] => prefix = gis_data[0][MULTILINESTRING]
var prefix = name.substr(0, name.length - 10);
var type = prefix.slice(prefix.lastIndexOf('[') + 1, prefix.lastIndexOf(']')); // Find the number of lines
var $noOfLinesInput = $('input[name=\'' + prefix + '[no_of_lines]' + '\']');
var noOfLines = parseInt($noOfLinesInput.val(), 10); // Add the new linesting of inner ring based on the type
var html = '<br>';
var noOfPoints;
if (type === 'MULTILINESTRING') {
html += Messages.strLineString + ' ' + (noOfLines + 1) + ':';
noOfPoints = 2;
} else {
html += Messages.strInnerRing + ' ' + noOfLines + ':';
noOfPoints = 4;
}
html += '<input type="hidden" name="' + prefix + '[' + noOfLines + '][no_of_points]" value="' + noOfPoints + '">';
for (var i = 0; i < noOfPoints; i++) {
html += addDataPoint(i, prefix + '[' + noOfLines + ']');
}
html += '<a class="addPoint addJs" name="' + prefix + '[' + noOfLines + '][add_point]" href="#">+ ' + Messages.strAddPoint + '</a><br>';
$a.before(html);
$noOfLinesInput.val(noOfLines + 1);
});
/**
* Handles adding polygons
*/
$(document).on('click', '#gis_editor a.addJs.addPolygon', function () {
var $a = $(this);
var name = $a.attr('name'); // Eg. name = gis_data[0][MULTIPOLYGON][add_polygon] => prefix = gis_data[0][MULTIPOLYGON]
var prefix = name.substr(0, name.length - 13); // Find the number of polygons
var $noOfPolygonsInput = $('input[name=\'' + prefix + '[no_of_polygons]' + '\']');
var noOfPolygons = parseInt($noOfPolygonsInput.val(), 10); // Add the new polygon
var html = Messages.strPolygon + ' ' + (noOfPolygons + 1) + ':<br>';
html += '<input type="hidden" name="' + prefix + '[' + noOfPolygons + '][no_of_lines]" value="1">' + '<br>' + Messages.strOuterRing + ':' + '<input type="hidden" name="' + prefix + '[' + noOfPolygons + '][0][no_of_points]" value="4">';
for (var i = 0; i < 4; i++) {
html += addDataPoint(i, prefix + '[' + noOfPolygons + '][0]');
}
html += '<a class="addPoint addJs" name="' + prefix + '[' + noOfPolygons + '][0][add_point]" href="#">+ ' + Messages.strAddPoint + '</a><br>' + '<a class="addLine addJs" name="' + prefix + '[' + noOfPolygons + '][add_line]" href="#">+ ' + Messages.strAddInnerRing + '</a><br><br>';
$a.before(html);
$noOfPolygonsInput.val(noOfPolygons + 1);
});
/**
* Handles adding geoms
*/
$(document).on('click', '#gis_editor a.addJs.addGeom', function () {
var $a = $(this);
var prefix = 'gis_data[GEOMETRYCOLLECTION]'; // Find the number of geoms
var $noOfGeomsInput = $('input[name=\'' + prefix + '[geom_count]' + '\']');
var noOfGeoms = parseInt($noOfGeomsInput.val(), 10);
var html1 = Messages.strGeometry + ' ' + (noOfGeoms + 1) + ':<br>';
var $geomType = $('select[name=\'gis_data[' + (noOfGeoms - 1) + '][gis_type]\']').clone();
$geomType.attr('name', 'gis_data[' + noOfGeoms + '][gis_type]').val('POINT');
var html2 = '<br>' + Messages.strPoint + ' :' + '<label for="x"> ' + Messages.strX + ' </label>' + '<input type="text" name="gis_data[' + noOfGeoms + '][POINT][x]" value="">' + '<label for="y"> ' + Messages.strY + ' </label>' + '<input type="text" name="gis_data[' + noOfGeoms + '][POINT][y]" value="">' + '<br><br>';
$a.before(html1);
$geomType.insertBefore($a);
$a.before(html2);
$noOfGeomsInput.val(noOfGeoms + 1);
});
});

@ -0,0 +1,795 @@
/**
* @fileoverview function used for index manipulation pages
* @name Table Structure
*
* @requires jQuery
* @requires jQueryUI
* @required js/functions.js
*/
/* global fulltextIndexes:writable, indexes:writable, primaryIndexes:writable, spatialIndexes:writable, uniqueIndexes:writable */
// js/functions.js
var Indexes = {};
/**
* Returns the array of indexes based on the index choice
*
* @param {string} indexChoice index choice
*
* @return {null|object}
*/
Indexes.getIndexArray = function (indexChoice) {
var sourceArray = null;
switch (indexChoice.toLowerCase()) {
case 'primary':
sourceArray = primaryIndexes;
break;
case 'unique':
sourceArray = uniqueIndexes;
break;
case 'index':
sourceArray = indexes;
break;
case 'fulltext':
sourceArray = fulltextIndexes;
break;
case 'spatial':
sourceArray = spatialIndexes;
break;
default:
return null;
}
return sourceArray;
};
/**
* Hides/shows the inputs and submits appropriately depending
* on whether the index type chosen is 'SPATIAL' or not.
*/
Indexes.checkIndexType = function () {
/**
* @var {JQuery<HTMLElement}, Dropdown to select the index choice.
*/
var $selectIndexChoice = $('#select_index_choice');
/**
* @var {JQuery<HTMLElement}, Dropdown to select the index type.
*/
var $selectIndexType = $('#select_index_type');
/**
* @var {JQuery<HTMLElement}, Table header for the size column.
*/
var $sizeHeader = $('#index_columns').find('thead tr').children('th').eq(1);
/**
* @var {JQuery<HTMLElement}, Inputs to specify the columns for the index.
*/
var $columnInputs = $('select[name="index[columns][names][]"]');
/**
* @var {JQuery<HTMLElement}, Inputs to specify sizes for columns of the index.
*/
var $sizeInputs = $('input[name="index[columns][sub_parts][]"]');
/**
* @var {JQuery<HTMLElement}, Footer containing the controllers to add more columns
*/
var $addMore = $('#index_frm').find('.add_more');
if ($selectIndexChoice.val() === 'SPATIAL') {
// Disable and hide the size column
$sizeHeader.hide();
$sizeInputs.each(function () {
$(this).prop('disabled', true).parent('td').hide();
}); // Disable and hide the columns of the index other than the first one
var initial = true;
$columnInputs.each(function () {
var $columnInput = $(this);
if (!initial) {
$columnInput.prop('disabled', true).parent('td').hide();
} else {
initial = false;
}
}); // Hide controllers to add more columns
$addMore.hide();
} else {
// Enable and show the size column
$sizeHeader.show();
$sizeInputs.each(function () {
$(this).prop('disabled', false).parent('td').show();
}); // Enable and show the columns of the index
$columnInputs.each(function () {
$(this).prop('disabled', false).parent('td').show();
}); // Show controllers to add more columns
$addMore.show();
}
if ($selectIndexChoice.val() === 'SPATIAL' || $selectIndexChoice.val() === 'FULLTEXT') {
$selectIndexType.val('').prop('disabled', true);
} else {
$selectIndexType.prop('disabled', false);
}
};
/**
* Sets current index information into form parameters.
*
* @param {any[]} sourceArray Array containing index columns
* @param {string} indexChoice Choice of index
*
* @return {void}
*/
Indexes.setIndexFormParameters = function (sourceArray, indexChoice) {
if (indexChoice === 'index') {
$('input[name="indexes"]').val(JSON.stringify(sourceArray));
} else {
$('input[name="' + indexChoice + '_indexes"]').val(JSON.stringify(sourceArray));
}
};
/**
* Removes a column from an Index.
*
* @param {string} colIndex Index of column in form
*
* @return {void}
*/
Indexes.removeColumnFromIndex = function (colIndex) {
// Get previous index details.
var previousIndex = $('select[name="field_key[' + colIndex + ']"]').attr('data-index');
if (previousIndex.length) {
previousIndex = previousIndex.split(',');
var sourceArray = Indexes.getIndexArray(previousIndex[0]);
if (sourceArray === null) {
return;
} // Remove column from index array.
var sourceLength = sourceArray[previousIndex[1]].columns.length;
for (var i = 0; i < sourceLength; i++) {
if (sourceArray[previousIndex[1]].columns[i].col_index === colIndex) {
sourceArray[previousIndex[1]].columns.splice(i, 1);
}
} // Remove index completely if no columns left.
if (sourceArray[previousIndex[1]].columns.length === 0) {
sourceArray.splice(previousIndex[1], 1);
} // Update current index details.
$('select[name="field_key[' + colIndex + ']"]').attr('data-index', ''); // Update form index parameters.
Indexes.setIndexFormParameters(sourceArray, previousIndex[0].toLowerCase());
}
};
/**
* Adds a column to an Index.
*
* @param {any[]} sourceArray Array holding corresponding indexes
* @param {string} arrayIndex Index of an INDEX in array
* @param {string} indexChoice Choice of Index
* @param {string} colIndex Index of column on form
*
* @return {void}
*/
Indexes.addColumnToIndex = function (sourceArray, arrayIndex, indexChoice, colIndex) {
if (colIndex >= 0) {
// Remove column from other indexes (if any).
Indexes.removeColumnFromIndex(colIndex);
}
var indexName = $('input[name="index[Key_name]"]').val();
var indexComment = $('input[name="index[Index_comment]"]').val();
var keyBlockSize = $('input[name="index[Key_block_size]"]').val();
var parser = $('input[name="index[Parser]"]').val();
var indexType = $('select[name="index[Index_type]"]').val();
var columns = [];
$('#index_columns').find('tbody').find('tr').each(function () {
// Get columns in particular order.
var colIndex = $(this).find('select[name="index[columns][names][]"]').val();
var size = $(this).find('input[name="index[columns][sub_parts][]"]').val();
columns.push({
'col_index': colIndex,
'size': size
});
}); // Update or create an index.
sourceArray[arrayIndex] = {
'Key_name': indexName,
'Index_comment': indexComment,
'Index_choice': indexChoice.toUpperCase(),
'Key_block_size': keyBlockSize,
'Parser': parser,
'Index_type': indexType,
'columns': columns
}; // Display index name (or column list)
var displayName = indexName;
if (displayName === '') {
var columnNames = [];
$.each(columns, function () {
columnNames.push($('input[name="field_name[' + this.col_index + ']"]').val());
});
displayName = '[' + columnNames.join(', ') + ']';
}
$.each(columns, function () {
var id = 'index_name_' + this.col_index + '_8';
var $name = $('#' + id);
if ($name.length === 0) {
$name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
$name.insertAfter($('select[name="field_key[' + this.col_index + ']"]'));
}
var $text = $('<small>').text(displayName);
$name.html($text);
});
if (colIndex >= 0) {
// Update index details on form.
$('select[name="field_key[' + colIndex + ']"]').attr('data-index', indexChoice + ',' + arrayIndex);
}
Indexes.setIndexFormParameters(sourceArray, indexChoice.toLowerCase());
};
/**
* Get choices list for a column to create a composite index with.
*
* @param {any[]} sourceArray Array hodling columns for particular index
* @param {string} colIndex Choice of index
*
* @return {JQuery} jQuery Object
*/
Indexes.getCompositeIndexList = function (sourceArray, colIndex) {
// Remove any previous list.
if ($('#composite_index_list').length) {
$('#composite_index_list').remove();
} // Html list.
var $compositeIndexList = $('<ul id="composite_index_list">' + '<div>' + Messages.strCompositeWith + '</div>' + '</ul>'); // Add each column to list available for composite index.
var sourceLength = sourceArray.length;
var alreadyPresent = false;
for (var i = 0; i < sourceLength; i++) {
var subArrayLen = sourceArray[i].columns.length;
var columnNames = [];
for (var j = 0; j < subArrayLen; j++) {
columnNames.push($('input[name="field_name[' + sourceArray[i].columns[j].col_index + ']"]').val());
if (colIndex === sourceArray[i].columns[j].col_index) {
alreadyPresent = true;
}
}
$compositeIndexList.append('<li>' + '<input type="radio" name="composite_with" ' + (alreadyPresent ? 'checked="checked"' : '') + ' id="composite_index_' + i + '" value="' + i + '">' + '<label for="composite_index_' + i + '">' + columnNames.join(', ') + '</label>' + '</li>');
}
return $compositeIndexList;
};
/**
* Shows 'Add Index' dialog.
*
* @param {any[]} sourceArray Array holding particular index
* @param {string} arrayIndex Index of an INDEX in array
* @param {any[]} targetColumns Columns for an INDEX
* @param {string} colIndex Index of column on form
* @param {object} index Index detail object
* @param {boolean} showDialog Whether to show index creation dialog or not
*
* @return {void}
*/
Indexes.showAddIndexDialog = function (sourceArray, arrayIndex, targetColumns, colIndex, index, showDialog) {
var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true; // Prepare post-data.
var $table = $('input[name="table"]');
var table = $table.length > 0 ? $table.val() : '';
var postData = {
'server': CommonParams.get('server'),
'db': $('input[name="db"]').val(),
'table': table,
'ajax_request': 1,
'create_edit_table': 1,
'index': index
};
var columns = {};
for (var i = 0; i < targetColumns.length; i++) {
var columnName = $('input[name="field_name[' + targetColumns[i] + ']"]').val();
var columnType = $('select[name="field_type[' + targetColumns[i] + ']"]').val().toLowerCase();
columns[columnName] = [columnType, targetColumns[i]];
}
postData.columns = JSON.stringify(columns);
var buttonOptions = {};
buttonOptions[Messages.strGo] = function () {
var isMissingValue = false;
$('select[name="index[columns][names][]"]').each(function () {
if ($(this).val() === '') {
isMissingValue = true;
}
});
if (!isMissingValue) {
Indexes.addColumnToIndex(sourceArray, arrayIndex, index.Index_choice, colIndex);
} else {
Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' + ' class="icon ic_s_error"> ' + Messages.strMissingColumn + ' </div>', false);
return false;
}
$(this).remove();
};
buttonOptions[Messages.strCancel] = function () {
if (colIndex >= 0) {
// Handle state on 'Cancel'.
var $selectList = $('select[name="field_key[' + colIndex + ']"]');
if (!$selectList.attr('data-index').length) {
$selectList.find('option[value*="none"]').attr('selected', 'selected');
} else {
var previousIndex = $selectList.attr('data-index').split(',');
$selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]').attr('selected', 'selected');
}
}
$(this).dialog('close');
};
var $msgbox = Functions.ajaxShowMessage();
$.post('index.php?route=/table/indexes', postData, function (data) {
if (data.success === false) {
// in the case of an error, show the error message returned.
Functions.ajaxShowMessage(data.error, false);
} else {
Functions.ajaxRemoveMessage($msgbox);
var $div = $('<div></div>');
if (showDialogLocal) {
// Show dialog if the request was successful
if ($('#addIndex').length > 0) {
$('#addIndex').remove();
}
$div.append(data.message).dialog({
title: Messages.strAddIndex,
width: 450,
minHeight: 250,
create: function () {
$(this).on('keypress', function (e) {
if (e.which === 13 || e.keyCode === 13 || window.event.keyCode === 13) {
e.preventDefault();
buttonOptions[Messages.strGo]();
$(this).remove();
}
});
},
open: function () {
Functions.checkIndexName('index_frm');
Functions.showHints($div);
$('#index_columns').find('td').each(function () {
$(this).css('width', $(this).width() + 'px');
});
$('#index_columns').find('tbody').sortable({
axis: 'y',
containment: $('#index_columns').find('tbody'),
tolerance: 'pointer'
});
},
modal: true,
buttons: buttonOptions,
close: function () {
$(this).remove();
}
});
} else {
$div.append(data.message);
$div.css({
'display': 'none'
});
$div.appendTo($('body'));
$div.attr({
'id': 'addIndex'
});
var isMissingValue = false;
$('select[name="index[columns][names][]"]').each(function () {
if ($(this).val() === '') {
isMissingValue = true;
}
});
if (!isMissingValue) {
Indexes.addColumnToIndex(sourceArray, arrayIndex, index.Index_choice, colIndex);
} else {
Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' + ' class="icon ic_s_error"> ' + Messages.strMissingColumn + ' </div>', false);
return false;
}
}
}
});
};
/**
* Creates a advanced index type selection dialog.
*
* @param {any[]} sourceArray Array holding a particular type of indexes
* @param {string} indexChoice Choice of index
* @param {string} colIndex Index of new column on form
*
* @return {void}
*/
Indexes.indexTypeSelectionDialog = function (sourceArray, indexChoice, colIndex) {
var $singleColumnRadio = $('<input type="radio" id="single_column" name="index_choice"' + ' checked="checked">' + '<label for="single_column">' + Messages.strCreateSingleColumnIndex + '</label>');
var $compositeIndexRadio = $('<input type="radio" id="composite_index"' + ' name="index_choice">' + '<label for="composite_index">' + Messages.strCreateCompositeIndex + '</label>');
var $dialogContent = $('<fieldset class="pma-fieldset" id="advance_index_creator"></fieldset>');
$dialogContent.append('<legend>' + indexChoice.toUpperCase() + '</legend>'); // For UNIQUE/INDEX type, show choice for single-column and composite index.
$dialogContent.append($singleColumnRadio);
$dialogContent.append($compositeIndexRadio);
var buttonOptions = {}; // 'OK' operation.
buttonOptions[Messages.strGo] = function () {
if ($('#single_column').is(':checked')) {
var index = {
'Key_name': indexChoice === 'primary' ? 'PRIMARY' : '',
'Index_choice': indexChoice.toUpperCase()
};
Indexes.showAddIndexDialog(sourceArray, sourceArray.length, [colIndex], colIndex, index);
}
if ($('#composite_index').is(':checked')) {
if ($('input[name="composite_with"]').length !== 0 && $('input[name="composite_with"]:checked').length === 0) {
Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title=""' + ' alt="" class="icon ic_s_error"> ' + Messages.strFormEmpty + ' </div>', false);
return false;
}
var arrayIndex = $('input[name="composite_with"]:checked').val();
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
targetColumns.push(colIndex);
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex, sourceArray[arrayIndex]);
}
$(this).remove();
};
buttonOptions[Messages.strCancel] = function () {
// Handle state on 'Cancel'.
var $selectList = $('select[name="field_key[' + colIndex + ']"]');
if (!$selectList.attr('data-index').length) {
$selectList.find('option[value*="none"]').attr('selected', 'selected');
} else {
var previousIndex = $selectList.attr('data-index').split(',');
$selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]').attr('selected', 'selected');
}
$(this).remove();
};
$('<div></div>').append($dialogContent).dialog({
minWidth: 525,
minHeight: 200,
modal: true,
title: Messages.strAddIndex,
resizable: false,
buttons: buttonOptions,
open: function () {
$('#composite_index').on('change', function () {
if ($(this).is(':checked')) {
$dialogContent.append(Indexes.getCompositeIndexList(sourceArray, colIndex));
}
});
$('#single_column').on('change', function () {
if ($(this).is(':checked')) {
if ($('#composite_index_list').length) {
$('#composite_index_list').remove();
}
}
});
},
close: function () {
$('#composite_index').off('change');
$('#single_column').off('change');
$(this).remove();
}
});
};
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('indexes.js', function () {
$(document).off('click', '#save_index_frm');
$(document).off('click', '#preview_index_frm');
$(document).off('change', '#select_index_choice');
$(document).off('click', 'a.drop_primary_key_index_anchor.ajax');
$(document).off('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax');
$(document).off('click', '#table_index tbody tr td.rename_index.ajax');
$(document).off('click', '#index_frm input[type=submit]');
$('body').off('change', 'select[name*="field_key"]');
$(document).off('click', '.show_index_dialog');
});
/**
* @description <p>Ajax scripts for table index page</p>
*
* Actions ajaxified here:
* <ul>
* <li>Showing/hiding inputs depending on the index type chosen</li>
* <li>create/edit/drop indexes</li>
* </ul>
*/
AJAX.registerOnload('indexes.js', function () {
// Re-initialize variables.
primaryIndexes = [];
uniqueIndexes = [];
indexes = [];
fulltextIndexes = [];
spatialIndexes = []; // for table creation form
var $engineSelector = $('.create_table_form select[name=tbl_storage_engine]');
if ($engineSelector.length) {
Functions.hideShowConnection($engineSelector);
}
var $form = $('#index_frm');
if ($form.length > 0) {
Functions.showIndexEditDialog($form);
}
$(document).on('click', '#save_index_frm', function (event) {
event.preventDefault();
var $form = $('#index_frm');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'do_save_data=1' + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
Functions.ajaxShowMessage(Messages.strProcessingRequest);
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
});
$(document).on('click', '#preview_index_frm', function (event) {
event.preventDefault();
Functions.previewSql($('#index_frm'));
});
$(document).on('change', '#select_index_choice', function (event) {
event.preventDefault();
Indexes.checkIndexType();
Functions.checkIndexName('index_frm');
});
/**
* Ajax Event handler for 'Drop Index'
*/
$(document).on('click', 'a.drop_primary_key_index_anchor.ajax', function (event) {
event.preventDefault();
var $anchor = $(this);
/**
* @var $currRow Object containing reference to the current field's row
*/
var $currRow = $anchor.parents('tr');
/** @var {number} rows Number of columns in the key */
var rows = $anchor.parents('td').attr('rowspan') || 1;
/** @var {number} $rowsToHide Rows that should be hidden */
var $rowsToHide = $currRow;
for (var i = 1, $lastRow = $currRow.next(); i < rows; i++, $lastRow = $lastRow.next()) {
$rowsToHide = $rowsToHide.add($lastRow);
}
var question = $currRow.children('td').children('.drop_primary_key_index_msg').val();
Functions.confirmPreviewSql(question, $anchor.attr('href'), function (url) {
var $msg = Functions.ajaxShowMessage(Messages.strDroppingPrimaryKeyIndex, false);
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
Functions.ajaxRemoveMessage($msg);
var $tableRef = $rowsToHide.closest('table');
if ($rowsToHide.length === $tableRef.find('tbody > tr').length) {
// We are about to remove all rows from the table
$tableRef.hide('medium', function () {
$('div.no_indexes_defined').show('medium');
$rowsToHide.remove();
});
$tableRef.siblings('.alert-primary').hide('medium');
} else {
// We are removing some of the rows only
$rowsToHide.hide('medium', function () {
$(this).remove();
});
}
if ($('.result_query').length) {
$('.result_query').remove();
}
if (data.sql_query) {
$('<div class="result_query"></div>').html(data.sql_query).prependTo('#structure_content');
Functions.highlightSql($('#page_content'));
}
Navigation.reload();
CommonActions.refreshMain('index.php?route=/table/structure');
} else {
Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
}
}); // end $.post()
});
}); // end Drop Primary Key/Index
/**
* Ajax event handler for index edit
**/
$(document).on('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax', function (event) {
event.preventDefault();
var url;
var title;
if ($(this).find('a').length === 0) {
// Add index
var valid = Functions.checkFormElementInRange($(this).closest('form')[0], 'added_fields', 'Column count has to be larger than zero.');
if (!valid) {
return;
}
url = $(this).closest('form').serialize();
title = Messages.strAddIndex;
} else {
// Edit index
url = $(this).find('a').getPostData();
title = Messages.strEditIndex;
}
url += CommonParams.get('arg_separator') + 'ajax_request=true';
Functions.indexEditorDialog(url, title, function (data) {
CommonParams.set('db', data.params.db);
CommonParams.set('table', data.params.table);
CommonActions.refreshMain('index.php?route=/table/structure');
});
});
/**
* Ajax event handler for index rename
**/
$(document).on('click', '#table_index tbody tr td.rename_index.ajax', function (event) {
event.preventDefault();
var url = $(this).find('a').getPostData();
var title = Messages.strRenameIndex;
url += CommonParams.get('arg_separator') + 'ajax_request=true';
Functions.indexRenameDialog(url, title, function (data) {
CommonParams.set('db', data.params.db);
CommonParams.set('table', data.params.table);
CommonActions.refreshMain('index.php?route=/table/structure');
});
});
/**
* Ajax event handler for advanced index creation during table creation
* and column addition.
*/
$('body').on('change', 'select[name*="field_key"]', function (e, showDialog) {
var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true; // Index of column on Table edit and create page.
var colIndex = /\d+/.exec($(this).attr('name'));
colIndex = colIndex[0]; // Choice of selected index.
var indexChoice = /[a-z]+/.exec($(this).val());
indexChoice = indexChoice[0]; // Array containing corresponding indexes.
var sourceArray = null;
if (indexChoice === 'none') {
Indexes.removeColumnFromIndex(colIndex);
var id = 'index_name_' + '0' + '_8';
var $name = $('#' + id);
if ($name.length === 0) {
$name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
$name.insertAfter($('select[name="field_key[' + '0' + ']"]'));
}
$name.html('');
return false;
} // Select a source array.
sourceArray = Indexes.getIndexArray(indexChoice);
if (sourceArray === null) {
return;
}
if (sourceArray.length === 0) {
var index = {
'Key_name': indexChoice === 'primary' ? 'PRIMARY' : '',
'Index_choice': indexChoice.toUpperCase()
};
Indexes.showAddIndexDialog(sourceArray, 0, [colIndex], colIndex, index, showDialogLocal);
} else {
if (indexChoice === 'primary') {
var arrayIndex = 0;
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
targetColumns.push(colIndex);
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex, sourceArray[arrayIndex], showDialogLocal);
} else {
// If there are multiple columns selected for an index, show advanced dialog.
Indexes.indexTypeSelectionDialog(sourceArray, indexChoice, colIndex);
}
}
});
$(document).on('click', '.show_index_dialog', function (e) {
e.preventDefault(); // Get index details.
var previousIndex = $(this).prev('select').attr('data-index').split(',');
var indexChoice = previousIndex[0];
var arrayIndex = previousIndex[1];
var sourceArray = Indexes.getIndexArray(indexChoice);
if (sourceArray !== null) {
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, -1, sourceArray[arrayIndex]);
}
});
$('#index_frm').on('submit', function () {
if (typeof this.elements['index[Key_name]'].disabled !== 'undefined') {
this.elements['index[Key_name]'].disabled = false;
}
});
});

@ -0,0 +1,208 @@
/**
* Handles the resizing of a menu according to the available screen width
*
* Uses themes/original/css/resizable-menu.css.php
*
* To initialize:
* $('#myMenu').menuResizer(function () {
* // This function will be called to find out how much
* // available horizontal space there is for the menu
* return $('body').width() - 5; // Some extra margin for good measure
* });
*
* To trigger a resize operation:
* $('#myMenu').menuResizer('resize'); // Bind this to $(window).resize()
*
* To restore the menu to a state like before it was initialized:
* $('#myMenu').menuResizer('destroy');
*
* @package PhpMyAdmin
*/
(function ($) {
function MenuResizer($container, widthCalculator) {
var self = this;
self.$container = $container;
self.widthCalculator = widthCalculator;
var windowWidth = $(window).width();
if (windowWidth < 768) {
$('#pma_navigation_resizer').css({
'width': '0px'
});
} // create submenu container
var link = $('<a></a>', {
'href': '#',
'class': 'nav-link dropdown-toggle',
'id': 'navbarDropdown',
'role': 'button',
'data-bs-toggle': 'dropdown',
'aria-haspopup': 'true',
'aria-expanded': 'false'
}).text(Messages.strMore);
var img = $container.find('li img');
if (img.length) {
$(Functions.getImage('b_more').toString()).prependTo(link);
}
var $submenu = $('<li></li>', {
'class': 'nav-item dropdown d-none'
}).append(link).append($('<ul></ul>', {
'class': 'dropdown-menu dropdown-menu-end',
'aria-labelledby': 'navbarDropdown'
}));
$container.append($submenu);
setTimeout(function () {
self.resize();
}, 4);
}
MenuResizer.prototype.resize = function () {
var wmax = this.widthCalculator.call(this.$container);
var windowWidth = $(window).width();
var $submenu = this.$container.find('.nav-item.dropdown').last();
var submenuW = $submenu.outerWidth(true);
var $submenuUl = $submenu.find('.dropdown-menu');
var $li = this.$container.find('> li');
var $li2 = $submenuUl.find('.dropdown-item');
var moreShown = $li2.length > 0; // Calculate the total width used by all the shown tabs
var totalLen = moreShown ? submenuW : 0;
var l = $li.length - 1;
var i;
for (i = 0; i < l; i++) {
totalLen += $($li[i]).outerWidth(true);
} // eslint-disable-next-line compat/compat
var hasVScroll = document.body.scrollHeight > document.body.clientHeight;
if (hasVScroll) {
windowWidth += 15;
}
if (windowWidth < 768) {
wmax = 2000;
} // Now hide menu elements that don't fit into the menubar
var hidden = false; // Whether we have hidden any tabs
while (totalLen >= wmax && --l >= 0) {
// Process the tabs backwards
hidden = true;
var el = $($li[l]);
el.removeClass('nav-item').addClass('dropdown-item');
var elWidth = el.outerWidth(true);
el.data('width', elWidth);
if (!moreShown) {
totalLen -= elWidth;
el.prependTo($submenuUl);
totalLen += submenuW;
moreShown = true;
} else {
totalLen -= elWidth;
el.prependTo($submenuUl);
}
} // If we didn't hide any tabs, then there might be some space to show some
if (!hidden) {
// Show menu elements that do fit into the menubar
for (i = 0, l = $li2.length; i < l; i++) {
totalLen += $($li2[i]).data('width'); // item fits or (it is the last item
// and it would fit if More got removed)
if (totalLen < wmax || i === $li2.length - 1 && totalLen - submenuW < wmax) {
$($li2[i]).removeClass('dropdown-item').addClass('nav-item');
$($li2[i]).insertBefore($submenu);
} else {
break;
}
}
} // Show/hide the "More" tab as needed
if (windowWidth < 768) {
$('.navbar-collapse').css({
'width': windowWidth - 80 - $('#pma_navigation').width()
});
$submenu.addClass('d-none');
$('.navbar-collapse').css({
'overflow': 'hidden'
});
} else {
$('.navbar-collapse').css({
'width': 'auto'
});
$('.navbar-collapse').css({
'overflow': 'visible'
});
if ($submenuUl.find('li').length > 0) {
$submenu.removeClass('d-none');
} else {
$submenu.addClass('d-none');
}
}
};
MenuResizer.prototype.destroy = function () {
var $submenu = this.$container.find('.nav-item.dropdown').removeData();
$submenu.find('li').appendTo(this.$container);
$submenu.remove();
};
/** Public API */
var methods = {
init: function (widthCalculator) {
return this.each(function () {
var $this = $(this);
if (!$this.data('menuResizer')) {
$this.data('menuResizer', new MenuResizer($this, widthCalculator));
}
});
},
resize: function () {
return this.each(function () {
var self = $(this).data('menuResizer');
if (self) {
self.resize();
}
});
},
destroy: function () {
return this.each(function () {
var self = $(this).data('menuResizer');
if (self) {
self.destroy();
}
});
}
};
/**
* Extend jQuery
*
* @param {string} method
*
* @return {any}
*/
$.fn.menuResizer = function (method) {
if (methods[method]) {
return methods[method].call(this);
} else if (typeof method === 'function') {
return methods.init.apply(this, [method]);
} else {
$.error('Method ' + method + ' does not exist on jQuery.menuResizer');
}
};
})(jQuery);

@ -0,0 +1,455 @@
/**
* @fileoverview functions used in server privilege pages
* @name Server Privileges
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*
*/
/**
* Validates the "add a user" form
*
* @param theForm
*
* @return {bool} whether the form is validated or not
*/
function checkAddUser(theForm) {
if (theForm.elements.hostname.value === '') {
alert(Messages.strHostEmpty);
theForm.elements.hostname.focus();
return false;
}
if (theForm.elements.pred_username && theForm.elements.pred_username.value === 'userdefined' && theForm.elements.username.value === '') {
alert(Messages.strUserEmpty);
theForm.elements.username.focus();
return false;
}
return Functions.checkPassword($(theForm));
}
/**
* Export privileges modal handler
*
* @param {object} data
*
* @param {JQuery} msgbox
*
*/
function exportPrivilegesModalHandler(data, msgbox) {
if (typeof data !== 'undefined' && data.success === true) {
var modal = $('#exportPrivilegesModal'); // Remove any previous privilege modal data, if any
modal.find('.modal-body').first().html('');
$('#exportPrivilegesModalLabel').first().html('Loading');
modal.modal('show');
modal.on('shown.bs.modal', function () {
modal.find('.modal-body').first().html(data.message);
$('#exportPrivilegesModalLabel').first().html(data.title);
Functions.ajaxRemoveMessage(msgbox); // Attach syntax highlighted editor to export dialog
Functions.getSqlEditor(modal.find('textarea'));
});
return;
}
Functions.ajaxShowMessage(data.error, false);
}
/**
* @implements EventListener
*/
const EditUserGroup = {
/**
* @param {MouseEvent} event
*/
handleEvent: function (event) {
const editUserGroupModal = document.getElementById('editUserGroupModal');
const button = event.relatedTarget;
const username = button.getAttribute('data-username');
$.get('index.php?route=/server/user-groups/edit-form', {
'username': username,
'server': CommonParams.get('server')
}, data => {
if (typeof data === 'undefined' || data.success !== true) {
Functions.ajaxShowMessage(data.error, false, 'error');
return;
}
const modal = bootstrap.Modal.getInstance(editUserGroupModal);
const modalBody = editUserGroupModal.querySelector('.modal-body');
const saveButton = editUserGroupModal.querySelector('#editUserGroupModalSaveButton');
modalBody.innerHTML = data.message;
saveButton.addEventListener('click', () => {
const form = $(editUserGroupModal.querySelector('#changeUserGroupForm'));
$.post('index.php?route=/server/privileges', form.serialize() + CommonParams.get('arg_separator') + 'ajax_request=1', data => {
if (typeof data === 'undefined' || data.success !== true) {
Functions.ajaxShowMessage(data.error, false, 'error');
return;
}
const userGroup = form.serializeArray().find(el => el.name === 'userGroup').value; // button -> td -> tr -> td.usrGroup
const userGroupTableCell = button.parentElement.parentElement.querySelector('.usrGroup');
userGroupTableCell.textContent = userGroup;
});
modal.hide();
});
});
}
};
/**
* @implements EventListener
*/
const AccountLocking = {
handleEvent: function () {
const button = this;
const isLocked = button.dataset.isLocked === 'true';
const url = isLocked ? 'index.php?route=/server/privileges/account-unlock' : 'index.php?route=/server/privileges/account-lock';
const params = {
'username': button.dataset.userName,
'hostname': button.dataset.hostName,
'ajax_request': true,
'server': CommonParams.get('server')
};
$.post(url, params, data => {
if (data.success === false) {
Functions.ajaxShowMessage(data.error);
return;
}
if (isLocked) {
const lockIcon = Functions.getImage('s_lock', Messages.strLock, {}).toString();
button.innerHTML = '<span class="text-nowrap">' + lockIcon + ' ' + Messages.strLock + '</span>';
button.title = Messages.strLockAccount;
button.dataset.isLocked = 'false';
} else {
const unlockIcon = Functions.getImage('s_unlock', Messages.strUnlock, {}).toString();
button.innerHTML = '<span class="text-nowrap">' + unlockIcon + ' ' + Messages.strUnlock + '</span>';
button.title = Messages.strUnlockAccount;
button.dataset.isLocked = 'true';
}
Functions.ajaxShowMessage(data.message);
});
}
};
/**
* AJAX scripts for /server/privileges page.
*
* Actions ajaxified here:
* Add user
* Revoke a user
* Edit privileges
* Export privileges
* Paginate table of users
* Flush privileges
*
* @memberOf jQuery
* @name document.ready
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('server/privileges.js', function () {
$('#fieldset_add_user_login').off('change', 'input[name=\'username\']');
$(document).off('click', '#deleteUserCard .btn.ajax');
const editUserGroupModal = document.getElementById('editUserGroupModal');
if (editUserGroupModal) {
editUserGroupModal.removeEventListener('show.bs.modal', EditUserGroup);
}
$(document).off('click', 'button.mult_submit[value=export]');
$(document).off('click', 'a.export_user_anchor.ajax');
$('button.jsAccountLocking').off('click');
$('#dropUsersDbCheckbox').off('click');
$(document).off('click', '.checkall_box');
$(document).off('change', '#checkbox_SSL_priv');
$(document).off('change', 'input[name="ssl_type"]');
$(document).off('change', '#select_authentication_plugin');
});
AJAX.registerOnload('server/privileges.js', function () {
/**
* Display a warning if there is already a user by the name entered as the username.
*/
$('#fieldset_add_user_login').on('change', 'input[name=\'username\']', function () {
var username = $(this).val();
var $warning = $('#user_exists_warning');
if ($('#select_pred_username').val() === 'userdefined' && username !== '') {
var href = $('form[name=\'usersForm\']').attr('action');
var params = {
'ajax_request': true,
'server': CommonParams.get('server'),
'validate_username': true,
'username': username
};
$.get(href, params, function (data) {
if (data.user_exists) {
$warning.show();
} else {
$warning.hide();
}
});
} else {
$warning.hide();
}
});
/**
* Indicating password strength
*/
$('#text_pma_pw').on('keyup', function () {
var meterObj = $('#password_strength_meter');
var meterObjLabel = $('#password_strength');
var username = $('input[name="username"]');
username = username.val();
Functions.checkPasswordStrength($(this).val(), meterObj, meterObjLabel, username);
});
/**
* Automatically switching to 'Use Text field' from 'No password' once start writing in text area
*/
$('#text_pma_pw').on('input', function () {
if ($('#text_pma_pw').val() !== '') {
$('#select_pred_password').val('userdefined');
}
});
$('#text_pma_change_pw').on('keyup', function () {
var meterObj = $('#change_password_strength_meter');
var meterObjLabel = $('#change_password_strength');
Functions.checkPasswordStrength($(this).val(), meterObj, meterObjLabel, CommonParams.get('user'));
});
/**
* Display a notice if sha256_password is selected
*/
$(document).on('change', '#select_authentication_plugin', function () {
var selectedPlugin = $(this).val();
if (selectedPlugin === 'sha256_password') {
$('#ssl_reqd_warning').show();
} else {
$('#ssl_reqd_warning').hide();
}
});
/**
* AJAX handler for 'Revoke User'
*
* @see Functions.ajaxShowMessage()
* @memberOf jQuery
* @name revoke_user_click
*/
$(document).on('click', '#deleteUserCard .btn.ajax', function (event) {
event.preventDefault();
var $thisButton = $(this);
var $form = $('#usersForm');
$thisButton.confirm(Messages.strDropUserWarning, $form.attr('action'), function (url) {
var $dropUsersDbCheckbox = $('#dropUsersDbCheckbox');
if ($dropUsersDbCheckbox.is(':checked')) {
var isConfirmed = confirm(Messages.strDropDatabaseStrongWarning + '\n' + Functions.sprintf(Messages.strDoYouReally, 'DROP DATABASE'));
if (!isConfirmed) {
// Uncheck the drop users database checkbox
$dropUsersDbCheckbox.prop('checked', false);
}
}
Functions.ajaxShowMessage(Messages.strRemovingSelectedUsers);
var argsep = CommonParams.get('arg_separator');
$.post(url, $form.serialize() + argsep + 'delete=' + $thisButton.val() + argsep + 'ajax_request=true', function (data) {
if (typeof data !== 'undefined' && data.success === true) {
Functions.ajaxShowMessage(data.message); // Refresh navigation, if we dropped some databases with the name
// that is the same as the username of the deleted user
if ($('#dropUsersDbCheckbox:checked').length) {
Navigation.reload();
} // Remove the revoked user from the users list
$form.find('input:checkbox:checked').parents('tr').slideUp('medium', function () {
var thisUserInitial = $(this).find('input:checkbox').val().charAt(0).toUpperCase();
$(this).remove(); // If this is the last user with thisUserInitial, remove the link from #userAccountsPagination
if ($('#userRightsTable').find('input:checkbox[value^="' + thisUserInitial + '"], input:checkbox[value^="' + thisUserInitial.toLowerCase() + '"]').length === 0) {
$('#userAccountsPagination').find('.page-item > .page-link:contains(' + thisUserInitial + ')').parent('.page-item').addClass('disabled').html('<a class="page-link" href="#" tabindex="-1" aria-disabled="true">' + thisUserInitial + '</a>');
} // Re-check the classes of each row
$form.find('tbody').find('tr').each(function (index) {
if (index >= 0 && index % 2 === 0) {
$(this).removeClass('odd').addClass('even');
} else if (index >= 0 && index % 2 !== 0) {
$(this).removeClass('even').addClass('odd');
}
}); // update the checkall checkbox
$(Functions.checkboxesSel).trigger('change');
});
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
});
}); // end Revoke User
const editUserGroupModal = document.getElementById('editUserGroupModal');
if (editUserGroupModal) {
editUserGroupModal.addEventListener('show.bs.modal', EditUserGroup);
}
/**
* AJAX handler for 'Export Privileges'
*
* @see Functions.ajaxShowMessage()
* @memberOf jQuery
* @name export_user_click
*/
$(document).on('click', 'button.mult_submit[value=export]', function (event) {
event.preventDefault(); // can't export if no users checked
if ($(this.form).find('input:checked').length === 0) {
Functions.ajaxShowMessage(Messages.strNoAccountSelected, 2000, 'success');
return;
}
var msgbox = Functions.ajaxShowMessage();
var argsep = CommonParams.get('arg_separator');
var serverId = CommonParams.get('server');
var selectedUsers = $('#usersForm input[name*=\'selected_usr\']:checkbox').serialize();
var postStr = selectedUsers + '&submit_mult=export' + argsep + 'ajax_request=true&server=' + serverId;
$.post($(this.form).prop('action'), postStr, function (data) {
exportPrivilegesModalHandler(data, msgbox);
}); // end $.post
}); // if exporting non-ajax, highlight anyways
Functions.getSqlEditor($('textarea.export'));
$(document).on('click', 'a.export_user_anchor.ajax', function (event) {
event.preventDefault();
var msgbox = Functions.ajaxShowMessage();
$.get($(this).attr('href'), {
'ajax_request': true
}, function (data) {
exportPrivilegesModalHandler(data, msgbox);
}); // end $.get
}); // end export privileges
$('button.jsAccountLocking').on('click', AccountLocking.handleEvent);
$(document).on('change', 'input[name="ssl_type"]', function () {
var $div = $('#specified_div');
if ($('#ssl_type_SPECIFIED').is(':checked')) {
$div.find('input').prop('disabled', false);
} else {
$div.find('input').prop('disabled', true);
}
});
$(document).on('change', '#checkbox_SSL_priv', function () {
var $div = $('#require_ssl_div');
if ($(this).is(':checked')) {
$div.find('input').prop('disabled', false);
$('#ssl_type_SPECIFIED').trigger('change');
} else {
$div.find('input').prop('disabled', true);
}
});
$('#checkbox_SSL_priv').trigger('change');
/*
* Create submenu for simpler interface
*/
var addOrUpdateSubmenu = function () {
var $subNav = $('.nav-pills');
var $editUserDialog = $('#edit_user_dialog');
var submenuLabel;
var submenuLink;
var linkNumber; // if submenu exists yet, remove it first
if ($subNav.length > 0) {
$subNav.remove();
} // construct a submenu from the existing fieldsets
$subNav = $('<ul></ul>').prop('class', 'nav nav-pills m-2');
$('#edit_user_dialog .submenu-item').each(function () {
submenuLabel = $(this).find('legend[data-submenu-label]').data('submenu-label');
submenuLink = $('<a></a>').prop('class', 'nav-link').prop('href', '#').html(submenuLabel);
$('<li></li>').prop('class', 'nav-item').append(submenuLink).appendTo($subNav);
}); // click handlers for submenu
$subNav.find('a').on('click', function (e) {
e.preventDefault(); // if already active, ignore click
if ($(this).hasClass('active')) {
return;
}
$subNav.find('a').removeClass('active');
$(this).addClass('active'); // which section to show now?
linkNumber = $subNav.find('a').index($(this)); // hide all sections but the one to show
$('#edit_user_dialog .submenu-item').hide().eq(linkNumber).show();
}); // make first menu item active
// TODO: support URL hash history
$subNav.find('> :first-child a').addClass('active');
$editUserDialog.prepend($subNav); // hide all sections but the first
$('#edit_user_dialog .submenu-item').hide().eq(0).show(); // scroll to the top
$('html, body').animate({
scrollTop: 0
}, 'fast');
};
$('input.autofocus').trigger('focus');
$(Functions.checkboxesSel).trigger('change');
Functions.displayPasswordGenerateButton();
if ($('#edit_user_dialog').length > 0) {
addOrUpdateSubmenu();
}
/**
* Select all privileges
*
* @param {HTMLElement} e
* @return {void}
*/
var tableSelectAll = function (e) {
const method = e.target.getAttribute('data-select-target');
var options = $(method).first().children();
options.each(function (_, obj) {
obj.selected = true;
});
};
$('#select_priv_all').on('click', tableSelectAll);
$('#insert_priv_all').on('click', tableSelectAll);
$('#update_priv_all').on('click', tableSelectAll);
$('#references_priv_all').on('click', tableSelectAll);
var windowWidth = $(window).width();
$('.jsresponsive').css('max-width', windowWidth - 35 + 'px');
$('#addUsersForm').on('submit', function () {
return checkAddUser(this);
});
$('#copyUserForm').on('submit', function () {
return checkAddUser(this);
});
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,96 @@
/**
*
*
* @package PhpMyAdmin
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('server/status/variables.js', function () {
$('#filterAlert').off('change');
$('#filterText').off('keyup');
$('#filterCategory').off('change');
$('#dontFormat').off('change');
});
AJAX.registerOnload('server/status/variables.js', function () {
// Filters for status variables
var textFilter = null;
var alertFilter = $('#filterAlert').prop('checked');
var categoryFilter = $('#filterCategory').find(':selected').val();
var text = ''; // Holds filter text
/* 3 Filtering functions */
$('#filterAlert').on('change', function () {
alertFilter = this.checked;
filterVariables();
});
$('#filterCategory').on('change', function () {
categoryFilter = $(this).val();
filterVariables();
});
$('#dontFormat').on('change', function () {
// Hiding the table while changing values speeds up the process a lot
const serverStatusVariables = $('#serverStatusVariables');
serverStatusVariables.hide();
serverStatusVariables.find('td.value span.original').toggle(this.checked);
serverStatusVariables.find('td.value span.formatted').toggle(!this.checked);
serverStatusVariables.show();
}).trigger('change');
$('#filterText').on('keyup', function () {
var word = $(this).val().replace(/_/g, ' ');
if (word.length === 0) {
textFilter = null;
} else {
try {
textFilter = new RegExp('(^| )' + word, 'i');
$(this).removeClass('error');
} catch (e) {
if (e instanceof SyntaxError) {
$(this).addClass('error');
textFilter = null;
}
}
}
text = word;
filterVariables();
}).trigger('keyup');
/* Filters the status variables by name/category/alert in the variables tab */
function filterVariables() {
var usefulLinks = 0;
var section = text;
if (categoryFilter.length > 0) {
section = categoryFilter;
}
if (section.length > 1) {
$('#linkSuggestions').find('span').each(function () {
if ($(this).attr('class').indexOf('status_' + section) !== -1) {
usefulLinks++;
$(this).css('display', '');
} else {
$(this).css('display', 'none');
}
});
}
if (usefulLinks > 0) {
$('#linkSuggestions').css('display', '');
} else {
$('#linkSuggestions').css('display', 'none');
}
$('#serverStatusVariables').find('th.name').each(function () {
if ((textFilter === null || textFilter.exec($(this).text())) && (!alertFilter || $(this).next().find('span.text-danger').length > 0) && (categoryFilter.length === 0 || $(this).parent().hasClass('s_' + categoryFilter))) {
$(this).parent().css('display', '');
} else {
$(this).parent().css('display', 'none');
}
});
}
});

@ -0,0 +1,35 @@
/**
* @fileoverview Javascript functions used in server user groups page
* @name Server User Groups
*
* @requires jQuery
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('server/user_groups.js', function () {
$('#deleteUserGroupModal').off('show.bs.modal');
});
/**
* Bind event handlers
*/
AJAX.registerOnload('server/user_groups.js', function () {
const deleteUserGroupModal = $('#deleteUserGroupModal');
deleteUserGroupModal.on('show.bs.modal', function (event) {
const userGroupName = $(event.relatedTarget).data('user-group');
this.querySelector('.modal-body').innerText = Functions.sprintf(Messages.strDropUserGroupWarning, Functions.escapeHtml(userGroupName));
});
deleteUserGroupModal.on('shown.bs.modal', function (event) {
const userGroupName = $(event.relatedTarget).data('user-group');
$('#deleteUserGroupConfirm').on('click', function () {
$.post('index.php?route=/server/user-groups', {
'deleteUserGroup': true,
'userGroup': userGroupName,
'ajax_request': true
}, AJAX.responseHandler);
$('#deleteUserGroupModal').modal('hide');
});
});
});

@ -0,0 +1,836 @@
/**
* @fileoverview function used in table data manipulation pages
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*
*/
/* global extendingValidatorMessages */
// templates/javascript/variables.twig
/* global openGISEditor, gisEditorLoaded, loadJSAndGISEditor, loadGISEditor */
// js/gis_data_editor.js
/**
* Modify form controls when the "NULL" checkbox is checked
*
* @param {string} theType the MySQL field type
* @param {string} urlField the urlencoded field name - OBSOLETE
* @param {string} md5Field the md5 hashed field name
* @param {string} multiEdit the multi_edit row sequence number
*
* @return {boolean} always true
*/
function nullify(theType, urlField, md5Field, multiEdit) {
var rowForm = document.forms.insertForm;
if (typeof rowForm.elements['funcs' + multiEdit + '[' + md5Field + ']'] !== 'undefined') {
rowForm.elements['funcs' + multiEdit + '[' + md5Field + ']'].selectedIndex = -1;
} // "ENUM" field with more than 20 characters
if (Number(theType) === 1) {
rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'][1].selectedIndex = -1; // Other "ENUM" field
} else if (Number(theType) === 2) {
var elts = rowForm.elements['fields' + multiEdit + '[' + md5Field + ']']; // when there is just one option in ENUM:
if (elts.checked) {
elts.checked = false;
} else {
var eltsCnt = elts.length;
for (var i = 0; i < eltsCnt; i++) {
elts[i].checked = false;
} // end for
} // end if
// "SET" field
} else if (Number(theType) === 3) {
rowForm.elements['fields' + multiEdit + '[' + md5Field + '][]'].selectedIndex = -1; // Foreign key field (drop-down)
} else if (Number(theType) === 4) {
rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].selectedIndex = -1; // foreign key field (with browsing icon for foreign values)
} else if (Number(theType) === 6) {
rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].value = ''; // Other field types
} else
/* if (theType === 5)*/
{
rowForm.elements['fields' + multiEdit + '[' + md5Field + ']'].value = '';
} // end if... else if... else
return true;
} // end of the 'nullify()' function
/**
* javascript DateTime format validation.
* its used to prevent adding default (0000-00-00 00:00:00) to database when user enter wrong values
* Start of validation part
*/
// function checks the number of days in febuary
function daysInFebruary(year) {
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28;
} // function to convert single digit to double digit
function fractionReplace(number) {
var num = parseInt(number, 10);
return num >= 1 && num <= 9 ? '0' + num : '00';
}
/* function to check the validity of date
* The following patterns are accepted in this validation (accepted in mysql as well)
* 1) 2001-12-23
* 2) 2001-1-2
* 3) 02-12-23
* 4) And instead of using '-' the following punctuations can be used (+,.,*,^,@,/) All these are accepted by mysql as well. Therefore no issues
*/
function isDate(val, tmstmp) {
var value = val.replace(/[.|*|^|+|//|@]/g, '-');
var arrayVal = value.split('-');
for (var a = 0; a < arrayVal.length; a++) {
if (arrayVal[a].length === 1) {
arrayVal[a] = fractionReplace(arrayVal[a]);
}
}
value = arrayVal.join('-');
var pos = 2;
var dtexp = new RegExp(/^([0-9]{4})-(((01|03|05|07|08|10|12)-((0[0-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)-((0[0-9])|([1-2][0-9])|30))|((00)-(00)))$/);
if (value.length === 8) {
pos = 0;
}
if (dtexp.test(value)) {
var month = parseInt(value.substring(pos + 3, pos + 5), 10);
var day = parseInt(value.substring(pos + 6, pos + 8), 10);
var year = parseInt(value.substring(0, pos + 2), 10);
if (month === 2 && day > daysInFebruary(year)) {
return false;
}
if (value.substring(0, pos + 2).length === 2) {
year = parseInt('20' + value.substring(0, pos + 2), 10);
}
if (tmstmp === true) {
if (year < 1978) {
return false;
}
if (year > 2038 || year > 2037 && day > 19 && month >= 1 || year > 2037 && month > 1) {
return false;
}
}
} else {
return false;
}
return true;
}
/* function to check the validity of time
* The following patterns are accepted in this validation (accepted in mysql as well)
* 1) 2:3:4
* 2) 2:23:43
* 3) 2:23:43.123456
*/
function isTime(val) {
var arrayVal = val.split(':');
for (var a = 0, l = arrayVal.length; a < l; a++) {
if (arrayVal[a].length === 1) {
arrayVal[a] = fractionReplace(arrayVal[a]);
}
}
var newVal = arrayVal.join(':');
var tmexp = new RegExp(/^(-)?(([0-7]?[0-9][0-9])|(8[0-2][0-9])|(83[0-8])):((0[0-9])|([1-5][0-9])):((0[0-9])|([1-5][0-9]))(\.[0-9]{1,6}){0,1}$/);
return tmexp.test(newVal);
}
/**
* To check whether insert section is ignored or not
* @param {string} multiEdit
* @return {boolean}
*/
function checkForCheckbox(multiEdit) {
if ($('#insert_ignore_' + multiEdit).length) {
return $('#insert_ignore_' + multiEdit).is(':unchecked');
}
return true;
} // used in Search page mostly for INT fields
// eslint-disable-next-line no-unused-vars
function verifyAfterSearchFieldChange(index, searchFormId) {
var $thisInput = $('input[name=\'criteriaValues[' + index + ']\']'); // Add data-skip-validators attribute to skip validation in changeValueFieldType function
if ($('#fieldID_' + index).data('data-skip-validators')) {
$(searchFormId).validate().destroy();
return;
} // validation for integer type
if ($thisInput.data('type') === 'INT' || $thisInput.data('type') === 'TINYINT') {
// Trim spaces if it's an integer
$thisInput.val($thisInput.val().trim());
var hasMultiple = $thisInput.prop('multiple');
if (hasMultiple) {
$(searchFormId).validate({
// update errors as we write
onkeyup: function (element) {
$(element).valid();
}
}); // validator method for IN(...), NOT IN(...)
// BETWEEN and NOT BETWEEN
jQuery.validator.addMethod('validationFunctionForMultipleInt', function (value) {
return value.match(/^(?:(?:\d\s*)|\s*)+(?:,\s*\d+)*$/i) !== null;
}, Messages.strEnterValidNumber);
validateMultipleIntField($thisInput, true);
} else {
$(searchFormId).validate({
// update errors as we write
onkeyup: function (element) {
$(element).valid();
}
});
validateIntField($thisInput, true);
} // Update error on dropdown change
$thisInput.valid();
}
}
/**
* Validate the an input contains multiple int values
* @param {jQuery} jqueryInput the Jquery object
* @param {boolean} returnValueIfFine the value to return if the validator passes
* @return {void}
*/
function validateMultipleIntField(jqueryInput, returnValueIfFine) {
// removing previous rules
jqueryInput.rules('remove');
jqueryInput.rules('add', {
validationFunctionForMultipleInt: {
param: jqueryInput.value,
depends: function () {
return returnValueIfFine;
}
}
});
}
/**
* Validate the an input contains an int value
* @param {jQuery} jqueryInput the Jquery object
* @param {boolean} returnValueIfIsNumber the value to return if the validator passes
* @return {void}
*/
function validateIntField(jqueryInput, returnValueIfIsNumber) {
var mini = parseInt(jqueryInput.data('min'));
var maxi = parseInt(jqueryInput.data('max')); // removing previous rules
jqueryInput.rules('remove');
jqueryInput.rules('add', {
number: {
param: true,
depends: function () {
return returnValueIfIsNumber;
}
},
min: {
param: mini,
depends: function () {
if (isNaN(jqueryInput.val())) {
return false;
} else {
return returnValueIfIsNumber;
}
}
},
max: {
param: maxi,
depends: function () {
if (isNaN(jqueryInput.val())) {
return false;
} else {
return returnValueIfIsNumber;
}
}
}
});
}
function verificationsAfterFieldChange(urlField, multiEdit, theType) {
var evt = window.event || arguments.callee.caller.arguments[0];
var target = evt.target || evt.srcElement;
var $thisInput = $(':input[name^=\'fields[multi_edit][' + multiEdit + '][' + urlField + ']\']'); // the function drop-down that corresponds to this input field
var $thisFunction = $('select[name=\'funcs[multi_edit][' + multiEdit + '][' + urlField + ']\']');
var functionSelected = false;
if (typeof $thisFunction.val() !== 'undefined' && $thisFunction.val() !== null && $thisFunction.val().length > 0) {
functionSelected = true;
} // To generate the textbox that can take the salt
var newSaltBox = '<br><input type=text name=salt[multi_edit][' + multiEdit + '][' + urlField + ']' + ' id=salt_' + target.id + ' placeholder=\'' + Messages.strEncryptionKey + '\'>'; // If encrypting or decrypting functions that take salt as input is selected append the new textbox for salt
if (target.value === 'AES_ENCRYPT' || target.value === 'AES_DECRYPT' || target.value === 'DES_ENCRYPT' || target.value === 'DES_DECRYPT' || target.value === 'ENCRYPT') {
if (!$('#salt_' + target.id).length) {
$thisInput.after(newSaltBox);
}
} else {
// Remove the textbox for salt
$('#salt_' + target.id).prev('br').remove();
$('#salt_' + target.id).remove();
} // Remove possible blocking rules if the user changed functions
$('#' + target.id).rules('remove', 'validationFunctionForMd5');
$('#' + target.id).rules('remove', 'validationFunctionForAesDesEncrypt');
if (target.value === 'MD5') {
$('#' + target.id).rules('add', {
validationFunctionForMd5: {
param: $thisInput,
depends: function () {
return checkForCheckbox(multiEdit);
}
}
});
}
if (target.value === 'DES_ENCRYPT' || target.value === 'AES_ENCRYPT') {
$('#' + target.id).rules('add', {
validationFunctionForAesDesEncrypt: {
param: $thisInput,
depends: function () {
return checkForCheckbox(multiEdit);
}
}
});
}
if (target.value === 'HEX' && theType.substring(0, 3) === 'int') {
// Add note when HEX function is selected on a int
var newHexInfo = '<br><p id="note' + target.id + '">' + Messages.HexConversionInfo + '</p>';
if (!$('#note' + target.id).length) {
$thisInput.after(newHexInfo);
}
} else {
$('#note' + target.id).prev('br').remove();
$('#note' + target.id).remove();
} // Unchecks the corresponding "NULL" control
$('input[name=\'fields_null[multi_edit][' + multiEdit + '][' + urlField + ']\']').prop('checked', false); // Unchecks the Ignore checkbox for the current row
$('input[name=\'insert_ignore_' + multiEdit + '\']').prop('checked', false);
var charExceptionHandling;
if (theType.substring(0, 4) === 'char') {
charExceptionHandling = theType.substring(5, 6);
} else if (theType.substring(0, 7) === 'varchar') {
charExceptionHandling = theType.substring(8, 9);
}
if (functionSelected) {
$thisInput.removeAttr('min');
$thisInput.removeAttr('max'); // @todo: put back attributes if corresponding function is deselected
}
if ($thisInput.data('rulesadded') === null && !functionSelected) {
// call validate before adding rules
$($thisInput[0].form).validate(); // validate for date time
if (theType === 'datetime' || theType === 'time' || theType === 'date' || theType === 'timestamp') {
$thisInput.rules('add', {
validationFunctionForDateTime: {
param: theType,
depends: function () {
return checkForCheckbox(multiEdit);
}
}
});
} // validation for integer type
if ($thisInput.data('type') === 'INT') {
validateIntField($thisInput, checkForCheckbox(multiEdit)); // validation for CHAR types
} else if ($thisInput.data('type') === 'CHAR') {
var maxlen = $thisInput.data('maxlength');
if (typeof maxlen !== 'undefined') {
if (maxlen <= 4) {
maxlen = charExceptionHandling;
}
$thisInput.rules('add', {
maxlength: {
param: maxlen,
depends: function () {
return checkForCheckbox(multiEdit);
}
}
});
} // validate binary & blob types
} else if ($thisInput.data('type') === 'HEX') {
$thisInput.rules('add', {
validationFunctionForHex: {
param: true,
depends: function () {
return checkForCheckbox(multiEdit);
}
}
});
}
$thisInput.data('rulesadded', true);
} else if ($thisInput.data('rulesadded') === true && functionSelected) {
// remove any rules added
$thisInput.rules('remove'); // remove any error messages
$thisInput.removeClass('error').removeAttr('aria-invalid').siblings('.error').remove();
$thisInput.data('rulesadded', null);
}
}
/* End of fields validation*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('table/change.js', function () {
$(document).off('click', 'span.open_gis_editor');
$(document).off('click', 'input[name^=\'insert_ignore_\']');
$(document).off('click', 'input[name=\'gis_data[save]\']');
$(document).off('click', 'input.checkbox_null');
$('select[name="submit_type"]').off('change');
$(document).off('change', '#insert_rows');
});
/**
* Ajax handlers for Change Table page
*
* Actions Ajaxified here:
* Submit Data to be inserted into the table.
* Restart insertion with 'N' rows.
*/
AJAX.registerOnload('table/change.js', function () {
if ($('#insertForm').length) {
// validate the comment form when it is submitted
$('#insertForm').validate();
jQuery.validator.addMethod('validationFunctionForHex', function (value) {
return value.match(/^[a-f0-9]*$/i) !== null;
});
jQuery.validator.addMethod('validationFunctionForMd5', function (value, element, options) {
return !(value.substring(0, 3) === 'MD5' && typeof options.data('maxlength') !== 'undefined' && options.data('maxlength') < 32);
});
jQuery.validator.addMethod('validationFunctionForAesDesEncrypt', function (value, element, options) {
var funType = value.substring(0, 3);
if (funType !== 'AES' && funType !== 'DES') {
return false;
}
var dataType = options.data('type');
if (dataType === 'HEX' || dataType === 'CHAR') {
return true;
}
return false;
});
jQuery.validator.addMethod('validationFunctionForDateTime', function (value, element, options) {
var dtValue = value;
var theType = options;
if (theType === 'date') {
return isDate(dtValue);
} else if (theType === 'time') {
return isTime(dtValue);
} else if (theType === 'datetime' || theType === 'timestamp') {
var tmstmp = false;
dtValue = dtValue.trim();
if (dtValue === 'CURRENT_TIMESTAMP' || dtValue === 'current_timestamp()') {
return true;
}
if (theType === 'timestamp') {
tmstmp = true;
}
if (dtValue === '0000-00-00 00:00:00') {
return true;
}
var dv = dtValue.indexOf(' ');
if (dv === -1) {
// Only the date component, which is valid
return isDate(dtValue, tmstmp);
}
return isDate(dtValue.substring(0, dv), tmstmp) && isTime(dtValue.substring(dv + 1));
}
});
}
/*
* message extending script must be run
* after initiation of functions
*/
extendingValidatorMessages();
$.datepicker.initialized = false;
$(document).on('click', 'span.open_gis_editor', function (event) {
event.preventDefault();
var $span = $(this); // Current value
var value = $span.parent('td').children('input[type=\'text\']').val(); // Field name
var field = $span.parents('tr').children('td').first().find('input[type=\'hidden\']').val(); // Column type
var type = $span.parents('tr').find('span.column_type').text(); // Names of input field and null checkbox
var inputName = $span.parent('td').children('input[type=\'text\']').attr('name');
openGISEditor();
if (!gisEditorLoaded) {
loadJSAndGISEditor(value, field, type, inputName);
} else {
loadGISEditor(value, field, type, inputName);
}
});
/**
* Forced validation check of fields
*/
$(document).on('click', 'input[name^=\'insert_ignore_\']', function () {
$('#insertForm').valid();
});
/**
* Uncheck the null checkbox as geometry data is placed on the input field
*/
$(document).on('click', 'input[name=\'gis_data[save]\']', function () {
var inputName = $('form#gis_data_editor_form').find('input[name=\'input_name\']').val();
var currentRow = $('input[name=\'' + inputName + '\']').parents('tr');
var $nullCheckbox = currentRow.find('.checkbox_null');
$nullCheckbox.prop('checked', false);
var rowId = currentRow.find('.open_gis_editor').data('row-id'); // Unchecks the Ignore checkbox for the current row
$('input[name=\'insert_ignore_' + rowId + '\']').prop('checked', false);
});
/**
* Handles all current checkboxes for Null; this only takes care of the
* checkboxes on currently displayed rows as the rows generated by
* "Continue insertion" are handled in the "Continue insertion" code
*
*/
$(document).on('click', 'input.checkbox_null', function () {
nullify( // use hidden fields populated by /table/change
$(this).siblings('.nullify_code').val(), $(this).closest('tr').find('input:hidden').first().val(), $(this).siblings('.hashed_field').val(), $(this).siblings('.multi_edit').val());
});
/**
* Reset the auto_increment column to 0 when selecting any of the
* insert options in submit_type-dropdown. Only perform the reset
* when we are in edit-mode, and not in insert-mode(no previous value
* available).
*/
$('select[name="submit_type"]').on('change', function () {
var thisElemSubmitTypeVal = $(this).val();
var $table = $('table.insertRowTable');
var autoIncrementColumn = $table.find('input[name^="auto_increment"]');
autoIncrementColumn.each(function () {
var $thisElemAIField = $(this);
var thisElemName = $thisElemAIField.attr('name');
var prevValueField = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields_prev') + '"]');
var valueField = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields') + '"]');
var previousValue = $(prevValueField).val();
if (previousValue !== undefined) {
if (thisElemSubmitTypeVal === 'insert' || thisElemSubmitTypeVal === 'insertignore' || thisElemSubmitTypeVal === 'showinsert') {
$(valueField).val(null);
} else {
$(valueField).val(previousValue);
}
}
});
});
/**
* Handle ENTER key when press on Continue insert with field
*/
$('#insert_rows').on('keypress', function (e) {
var key = e.which;
if (key === 13) {
addNewContinueInsertionFields(e);
}
});
/**
* Continue Insertion form
*/
$(document).on('change', '#insert_rows', addNewContinueInsertionFields);
});
function addNewContinueInsertionFields(event) {
event.preventDefault();
/**
* @var columnCount Number of number of columns table has.
*/
var columnCount = $('table.insertRowTable').first().find('tr').has('input[name*=\'fields_name\']').length;
/**
* @var curr_rows Number of current insert rows already on page
*/
var currRows = $('table.insertRowTable').length;
/**
* @var target_rows Number of rows the user wants
*/
var targetRows = $('#insert_rows').val(); // remove all datepickers
$('input.datefield, input.datetimefield').each(function () {
$(this).datepicker('destroy');
});
if (currRows < targetRows) {
var tempIncrementIndex = function () {
var $thisElement = $(this);
/**
* Extract the index from the name attribute for all input/select fields and increment it
* name is of format funcs[multi_edit][10][<long random string of alphanum chars>]
*/
/**
* @var this_name String containing name of the input/select elements
*/
var thisName = $thisElement.attr('name');
/** split {@link thisName} at [10], so we have the parts that can be concatenated later */
var nameParts = thisName.split(/\[\d+\]/);
/** extract the [10] from {@link nameParts} */
var oldRowIndexString = thisName.match(/\[\d+\]/)[0];
/** extract 10 - had to split into two steps to accomodate double digits */
var oldRowIndex = parseInt(oldRowIndexString.match(/\d+/)[0], 10);
/** calculate next index i.e. 11 */
newRowIndex = oldRowIndex + 1;
/** generate the new name i.e. funcs[multi_edit][11][foobarbaz] */
var newName = nameParts[0] + '[' + newRowIndex + ']' + nameParts[1];
var hashedField = nameParts[1].match(/\[(.+)\]/)[1];
$thisElement.attr('name', newName);
/** If element is select[name*='funcs'], update id */
if ($thisElement.is('select[name*=\'funcs\']')) {
var thisId = $thisElement.attr('id');
var idParts = thisId.split(/_/);
var oldIdIndex = idParts[1];
var prevSelectedValue = $('#field_' + oldIdIndex + '_1').val();
var newIdIndex = parseInt(oldIdIndex) + columnCount;
var newId = 'field_' + newIdIndex + '_1';
$thisElement.attr('id', newId);
$thisElement.find('option').filter(function () {
return $(this).text() === prevSelectedValue;
}).attr('selected', 'selected'); // If salt field is there then update its id.
var nextSaltInput = $thisElement.parent().next('td').next('td').find('input[name*=\'salt\']');
if (nextSaltInput.length !== 0) {
nextSaltInput.attr('id', 'salt_' + newId);
}
} // handle input text fields and textareas
if ($thisElement.is('.textfield') || $thisElement.is('.char') || $thisElement.is('textarea')) {
// do not remove the 'value' attribute for ENUM columns
// special handling for radio fields after updating ids to unique - see below
if ($thisElement.closest('tr').find('span.column_type').html() !== 'enum') {
$thisElement.val($thisElement.closest('tr').find('span.default_value').html());
}
$thisElement.off('change') // Remove onchange attribute that was placed
// by /table/change; it refers to the wrong row index
.attr('onchange', null) // Keep these values to be used when the element
// will change
.data('hashed_field', hashedField).data('new_row_index', newRowIndex).on('change', function () {
var $changedElement = $(this);
verificationsAfterFieldChange($changedElement.data('hashed_field'), $changedElement.data('new_row_index'), $changedElement.closest('tr').find('span.column_type').html());
});
}
if ($thisElement.is('.checkbox_null')) {
$thisElement // this event was bound earlier by jQuery but
// to the original row, not the cloned one, so unbind()
.off('click') // Keep these values to be used when the element
// will be clicked
.data('hashed_field', hashedField).data('new_row_index', newRowIndex).on('click', function () {
var $changedElement = $(this);
nullify($changedElement.siblings('.nullify_code').val(), $thisElement.closest('tr').find('input:hidden').first().val(), $changedElement.data('hashed_field'), '[multi_edit][' + $changedElement.data('new_row_index') + ']');
});
}
};
var tempReplaceAnchor = function () {
var $anchor = $(this);
var newValue = 'rownumber=' + newRowIndex; // needs improvement in case something else inside
// the href contains this pattern
var newHref = $anchor.attr('href').replace(/rownumber=\d+/, newValue);
$anchor.attr('href', newHref);
};
var restoreValue = function () {
if ($(this).closest('tr').find('span.column_type').html() === 'enum') {
if ($(this).val() === $checkedValue) {
$(this).prop('checked', true);
} else {
$(this).prop('checked', false);
}
}
};
while (currRows < targetRows) {
/**
* @var $last_row Object referring to the last row
*/
var $lastRow = $('#insertForm').find('.insertRowTable').last(); // need to access this at more than one level
// (also needs improvement because it should be calculated
// just once per cloned row, not once per column)
var newRowIndex = 0;
var $checkedValue = $lastRow.find('input:checked').val(); // Clone the insert tables
$lastRow.clone(true, true).insertBefore('#actions_panel').find('input[name*=multi_edit],select[name*=multi_edit],textarea[name*=multi_edit]').each(tempIncrementIndex).end().find('.foreign_values_anchor').each(tempReplaceAnchor);
var $oldRow = $lastRow.find('.textfield');
$oldRow.each(restoreValue); // set the value of enum field of new row to default
var $newRow = $('#insertForm').find('.insertRowTable').last();
$newRow.find('.textfield').each(function () {
if ($(this).closest('tr').find('span.column_type').html() === 'enum') {
if ($(this).val() === $(this).closest('tr').find('span.default_value').html()) {
$(this).prop('checked', true);
} else {
$(this).prop('checked', false);
}
}
}); // Insert/Clone the ignore checkboxes
if (currRows === 1) {
$('<input id="insert_ignore_1" type="checkbox" name="insert_ignore_1" checked="checked">').insertBefore($('table.insertRowTable').last()).after('<label for="insert_ignore_1">' + Messages.strIgnore + '</label>');
} else {
/**
* @var $last_checkbox Object reference to the last checkbox in #insertForm
*/
var $lastCheckbox = $('#insertForm').children('input:checkbox').last();
/** name of {@link $lastCheckbox} */
var lastCheckboxName = $lastCheckbox.attr('name');
/** index of {@link $lastCheckbox} */
var lastCheckboxIndex = parseInt(lastCheckboxName.match(/\d+/), 10);
/** name of new {@link $lastCheckbox} */
var newName = lastCheckboxName.replace(/\d+/, lastCheckboxIndex + 1);
$('<br><div class="clearfloat"></div>').insertBefore($('table.insertRowTable').last());
$lastCheckbox.clone().attr({
'id': newName,
'name': newName
}).prop('checked', true).insertBefore($('table.insertRowTable').last());
$('label[for^=insert_ignore]').last().clone().attr('for', newName).insertBefore($('table.insertRowTable').last());
$('<br>').insertBefore($('table.insertRowTable').last());
}
currRows++;
} // recompute tabindex for text fields and other controls at footer;
// IMO it's not really important to handle the tabindex for
// function and Null
var tabIndex = 0;
$('.textfield, .char, textarea').each(function () {
tabIndex++;
$(this).attr('tabindex', tabIndex); // update the IDs of textfields to ensure that they are unique
$(this).attr('id', 'field_' + tabIndex + '_3');
});
$('.control_at_footer').each(function () {
tabIndex++;
$(this).attr('tabindex', tabIndex);
});
} else if (currRows > targetRows) {
/**
* Displays alert if data loss possible on decrease
* of rows.
*/
var checkLock = jQuery.isEmptyObject(AJAX.lockedTargets);
if (checkLock || confirm(Messages.strConfirmRowChange) === true) {
while (currRows > targetRows) {
$('input[id^=insert_ignore]').last().nextUntil('fieldset').addBack().remove();
currRows--;
}
} else {
document.getElementById('insert_rows').value = currRows;
}
} // Add all the required datepickers back
Functions.addDateTimePicker();
} // eslint-disable-next-line no-unused-vars
function changeValueFieldType(elem, searchIndex) {
var fieldsValue = $('input#fieldID_' + searchIndex);
if (0 === fieldsValue.size()) {
return;
}
var type = $(elem).val();
if ('LIKE' === type || 'LIKE %...%' === type || 'NOT LIKE' === type || 'NOT LIKE %...%' === type) {
$('#fieldID_' + searchIndex).data('data-skip-validators', true);
return;
} else {
$('#fieldID_' + searchIndex).data('data-skip-validators', false);
}
if ('IN (...)' === type || 'NOT IN (...)' === type || 'BETWEEN' === type || 'NOT BETWEEN' === type) {
$('#fieldID_' + searchIndex).prop('multiple', true);
} else {
$('#fieldID_' + searchIndex).prop('multiple', false);
}
}

@ -0,0 +1,435 @@
/* global ColumnType, DataTable, JQPlotChartFactory */
// js/chart.js
/* global codeMirrorEditor */
// js/functions.js
var chartData = {};
var tempChartTitle;
var currentChart = null;
var currentSettings = null;
var dateTimeCols = [];
var numericCols = [];
function extractDate(dateString) {
var matches;
var match;
var dateTimeRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/;
var dateRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
matches = dateTimeRegExp.exec(dateString);
if (matches !== null && matches.length > 0) {
match = matches[0];
return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2), match.substr(11, 2), match.substr(14, 2), match.substr(17, 2));
} else {
matches = dateRegExp.exec(dateString);
if (matches !== null && matches.length > 0) {
match = matches[0];
return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2));
}
}
return null;
}
function queryChart(data, columnNames, settings) {
if ($('#querychart').length === 0) {
return;
}
var plotSettings = {
title: {
text: settings.title,
escapeHtml: true
},
grid: {
drawBorder: false,
shadow: false,
background: 'rgba(0,0,0,0)'
},
legend: {
show: true,
placement: 'outsideGrid',
location: 'e',
rendererOptions: {
numberColumns: 2
}
},
axes: {
xaxis: {
label: Functions.escapeHtml(settings.xaxisLabel)
},
yaxis: {
label: settings.yaxisLabel
}
},
stackSeries: settings.stackSeries
}; // create the chart
var factory = new JQPlotChartFactory();
var chart = factory.createChart(settings.type, 'querychart'); // create the data table and add columns
var dataTable = new DataTable();
if (settings.type === 'timeline') {
dataTable.addColumn(ColumnType.DATE, columnNames[settings.mainAxis]);
} else if (settings.type === 'scatter') {
dataTable.addColumn(ColumnType.NUMBER, columnNames[settings.mainAxis]);
} else {
dataTable.addColumn(ColumnType.STRING, columnNames[settings.mainAxis]);
}
var i;
var values = [];
if (settings.seriesColumn === null) {
$.each(settings.selectedSeries, function (index, element) {
dataTable.addColumn(ColumnType.NUMBER, columnNames[element]);
}); // set data to the data table
var columnsToExtract = [settings.mainAxis];
$.each(settings.selectedSeries, function (index, element) {
columnsToExtract.push(element);
});
var newRow;
var row;
var col;
for (i = 0; i < data.length; i++) {
row = data[i];
newRow = [];
for (var j = 0; j < columnsToExtract.length; j++) {
col = columnNames[columnsToExtract[j]];
if (j === 0) {
if (settings.type === 'timeline') {
// first column is date type
newRow.push(extractDate(row[col]));
} else if (settings.type === 'scatter') {
newRow.push(parseFloat(row[col]));
} else {
// first column is string type
newRow.push(row[col]);
}
} else {
// subsequent columns are of type, number
newRow.push(parseFloat(row[col]));
}
}
values.push(newRow);
}
dataTable.setData(values);
} else {
var seriesNames = {};
var seriesNumber = 1;
var seriesColumnName = columnNames[settings.seriesColumn];
for (i = 0; i < data.length; i++) {
if (!seriesNames[data[i][seriesColumnName]]) {
seriesNames[data[i][seriesColumnName]] = seriesNumber;
seriesNumber++;
}
}
$.each(seriesNames, function (seriesName) {
dataTable.addColumn(ColumnType.NUMBER, seriesName);
});
var valueMap = {};
var xValue;
var value;
var mainAxisName = columnNames[settings.mainAxis];
var valueColumnName = columnNames[settings.valueColumn];
for (i = 0; i < data.length; i++) {
xValue = data[i][mainAxisName];
value = valueMap[xValue];
if (!value) {
value = [xValue];
valueMap[xValue] = value;
}
seriesNumber = seriesNames[data[i][seriesColumnName]];
value[seriesNumber] = parseFloat(data[i][valueColumnName]);
}
$.each(valueMap, function (index, value) {
values.push(value);
});
dataTable.setData(values);
} // draw the chart and return the chart object
chart.draw(dataTable, plotSettings);
return chart;
}
function drawChart() {
currentSettings.width = $('#resizer').width() - 20;
currentSettings.height = $('#resizer').height() - 20; // TODO: a better way using .redraw() ?
if (currentChart !== null) {
currentChart.destroy();
}
var columnNames = [];
$('#chartXAxisSelect option').each(function () {
columnNames.push(Functions.escapeHtml($(this).text()));
});
try {
currentChart = queryChart(chartData, columnNames, currentSettings);
if (currentChart !== null) {
$('#saveChart').attr('href', currentChart.toImageString());
}
} catch (err) {
Functions.ajaxShowMessage(err.message, false);
}
}
function getSelectedSeries() {
var val = $('#chartSeriesSelect').val() || [];
var ret = [];
$.each(val, function (i, v) {
ret.push(parseInt(v, 10));
});
return ret;
}
function onXAxisChange() {
var $xAxisSelect = $('#chartXAxisSelect');
currentSettings.mainAxis = parseInt($xAxisSelect.val(), 10);
if (dateTimeCols.indexOf(currentSettings.mainAxis) !== -1) {
document.getElementById('timelineChartType').classList.remove('d-none');
} else {
document.getElementById('timelineChartType').classList.add('d-none');
if (currentSettings.type === 'timeline') {
$('#lineChartTypeRadio').prop('checked', true);
currentSettings.type = 'line';
}
}
if (numericCols.indexOf(currentSettings.mainAxis) !== -1) {
document.getElementById('scatterChartType').classList.remove('d-none');
} else {
document.getElementById('scatterChartType').classList.add('d-none');
if (currentSettings.type === 'scatter') {
$('#lineChartTypeRadio').prop('checked', true);
currentSettings.type = 'line';
}
}
var xAxisTitle = $xAxisSelect.children('option:selected').text();
$('#xAxisLabelInput').val(xAxisTitle);
currentSettings.xaxisLabel = xAxisTitle;
}
function onDataSeriesChange() {
var $seriesSelect = $('#chartSeriesSelect');
currentSettings.selectedSeries = getSelectedSeries();
var yAxisTitle;
if (currentSettings.selectedSeries.length === 1) {
document.getElementById('pieChartType').classList.remove('d-none');
yAxisTitle = $seriesSelect.children('option:selected').text();
} else {
document.getElementById('pieChartType').classList.add('d-none');
if (currentSettings.type === 'pie') {
$('#lineChartTypeRadio').prop('checked', true);
currentSettings.type = 'line';
}
yAxisTitle = Messages.strYValues;
}
$('#yAxisLabelInput').val(yAxisTitle);
currentSettings.yaxisLabel = yAxisTitle;
}
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('table/chart.js', function () {
$('input[name="chartType"]').off('click');
$('#barStackedCheckbox').off('click');
$('#seriesColumnCheckbox').off('click');
$('#chartTitleInput').off('focus').off('keyup').off('blur');
$('#chartXAxisSelect').off('change');
$('#chartSeriesSelect').off('change');
$('#chartSeriesColumnSelect').off('change');
$('#chartValueColumnSelect').off('change');
$('#xAxisLabelInput').off('keyup');
$('#yAxisLabelInput').off('keyup');
$('#resizer').off('resizestop');
$('#tblchartform').off('submit');
});
AJAX.registerOnload('table/chart.js', function () {
// handle manual resize
$('#resizer').on('resizestop', function () {
// make room so that the handle will still appear
$('#querychart').height($('#resizer').height() * 0.96);
$('#querychart').width($('#resizer').width() * 0.96);
if (currentChart !== null) {
currentChart.redraw({
resetAxes: true
});
}
}); // handle chart type changes
$('input[name="chartType"]').on('click', function () {
var type = currentSettings.type = $(this).val();
if (type === 'bar' || type === 'column' || type === 'area') {
document.getElementById('barStacked').classList.remove('d-none');
} else {
$('#barStackedCheckbox').prop('checked', false);
$.extend(true, currentSettings, {
stackSeries: false
});
document.getElementById('barStacked').classList.add('d-none');
}
drawChart();
}); // handle chosing alternative data format
$('#seriesColumnCheckbox').on('click', function () {
var $seriesColumn = $('#chartSeriesColumnSelect');
var $valueColumn = $('#chartValueColumnSelect');
var $chartSeries = $('#chartSeriesSelect');
if ($(this).is(':checked')) {
$seriesColumn.prop('disabled', false);
$valueColumn.prop('disabled', false);
$chartSeries.prop('disabled', true);
currentSettings.seriesColumn = parseInt($seriesColumn.val(), 10);
currentSettings.valueColumn = parseInt($valueColumn.val(), 10);
} else {
$seriesColumn.prop('disabled', true);
$valueColumn.prop('disabled', true);
$chartSeries.prop('disabled', false);
currentSettings.seriesColumn = null;
currentSettings.valueColumn = null;
}
drawChart();
}); // handle stacking for bar, column and area charts
$('#barStackedCheckbox').on('click', function () {
if ($(this).is(':checked')) {
$.extend(true, currentSettings, {
stackSeries: true
});
} else {
$.extend(true, currentSettings, {
stackSeries: false
});
}
drawChart();
}); // handle changes in chart title
$('#chartTitleInput').on('focus', function () {
tempChartTitle = $(this).val();
}).on('keyup', function () {
currentSettings.title = $('#chartTitleInput').val();
drawChart();
}).on('blur', function () {
if ($(this).val() !== tempChartTitle) {
drawChart();
}
}); // handle changing the x-axis
$('#chartXAxisSelect').on('change', function () {
onXAxisChange();
drawChart();
}); // handle changing the selected data series
$('#chartSeriesSelect').on('change', function () {
onDataSeriesChange();
drawChart();
}); // handle changing the series column
$('#chartSeriesColumnSelect').on('change', function () {
currentSettings.seriesColumn = parseInt($(this).val(), 10);
drawChart();
}); // handle changing the value column
$('#chartValueColumnSelect').on('change', function () {
currentSettings.valueColumn = parseInt($(this).val(), 10);
drawChart();
}); // handle manual changes to the chart x-axis labels
$('#xAxisLabelInput').on('keyup', function () {
currentSettings.xaxisLabel = $(this).val();
drawChart();
}); // handle manual changes to the chart y-axis labels
$('#yAxisLabelInput').on('keyup', function () {
currentSettings.yaxisLabel = $(this).val();
drawChart();
}); // handler for ajax form submission
$('#tblchartform').on('submit', function () {
var $form = $(this);
if (codeMirrorEditor) {
$form[0].elements.sql_query.value = codeMirrorEditor.getValue();
}
if (!Functions.checkSqlQuery($form[0])) {
return false;
}
var $msgbox = Functions.ajaxShowMessage();
Functions.prepareForAjaxRequest($form);
$.post($form.attr('action'), $form.serialize(), function (data) {
if (typeof data !== 'undefined' && data.success === true && typeof data.chartData !== 'undefined') {
chartData = JSON.parse(data.chartData);
drawChart();
Functions.ajaxRemoveMessage($msgbox);
} else {
Functions.ajaxShowMessage(data.error, false);
}
}, 'json'); // end $.post()
return false;
}); // from jQuery UI
$('#resizer').resizable({
minHeight: 240,
minWidth: 300
}).width($('#div_view_options').width() - 50).trigger('resizestop');
currentSettings = {
type: 'line',
width: $('#resizer').width() - 20,
height: $('#resizer').height() - 20,
xaxisLabel: $('#xAxisLabelInput').val(),
yaxisLabel: $('#yAxisLabelInput').val(),
title: $('#chartTitleInput').val(),
stackSeries: false,
mainAxis: parseInt($('#chartXAxisSelect').val(), 10),
selectedSeries: getSelectedSeries(),
seriesColumn: null
};
var vals = $('input[name="dateTimeCols"]').val().split(' ');
$.each(vals, function (i, v) {
dateTimeCols.push(parseInt(v, 10));
});
vals = $('input[name="numericCols"]').val().split(' ');
$.each(vals, function (i, v) {
numericCols.push(parseInt(v, 10));
});
onXAxisChange();
onDataSeriesChange();
$('#tblchartform').trigger('submit');
});

@ -0,0 +1,310 @@
/**
* @fileoverview JavaScript functions used on /table/search
*
* @requires jQuery
* @requires js/functions.js
*/
/* global changeValueFieldType, verifyAfterSearchFieldChange */
// js/table/change.js
/* global openGISEditor, gisEditorLoaded, loadJSAndGISEditor, loadGISEditor */
// js/gis_data_editor.js
var TableSelect = {};
/**
* Checks if given data-type is numeric or date.
*
* @param {string} dataType Column data-type
*
* @return {boolean | string}
*/
TableSelect.checkIfDataTypeNumericOrDate = function (dataType) {
// To test for numeric data-types.
var numericRegExp = new RegExp('TINYINT|SMALLINT|MEDIUMINT|INT|BIGINT|DECIMAL|FLOAT|DOUBLE|REAL', 'i'); // To test for date data-types.
var dateRegExp = new RegExp('DATETIME|DATE|TIMESTAMP|TIME|YEAR', 'i'); // Return matched data-type
if (numericRegExp.test(dataType)) {
return numericRegExp.exec(dataType)[0];
}
if (dateRegExp.test(dataType)) {
return dateRegExp.exec(dataType)[0];
}
return false;
};
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('table/select.js', function () {
$('#togglesearchformlink').off('click');
$(document).off('submit', '#tbl_search_form.ajax');
$('select.geom_func').off('change');
$(document).off('click', 'span.open_search_gis_editor');
$('body').off('change', 'select[name*="criteriaColumnOperators"]'); // Fix for bug #13778, changed 'click' to 'change'
});
AJAX.registerOnload('table/select.js', function () {
/**
* Prepare a div containing a link, otherwise it's incorrectly displayed
* after a couple of clicks
*/
$('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>').insertAfter('#tbl_search_form') // don't show it until we have results on-screen
.hide();
$('#togglesearchformlink').html(Messages.strShowSearchCriteria).on('click', function () {
var $link = $(this);
$('#tbl_search_form').slideToggle();
if ($link.text() === Messages.strHideSearchCriteria) {
$link.text(Messages.strShowSearchCriteria);
} else {
$link.text(Messages.strHideSearchCriteria);
} // avoid default click action
return false;
});
var tableRows = $('#fieldset_table_qbe select.column-operator');
$.each(tableRows, function (index, item) {
$(item).on('change', function () {
changeValueFieldType(this, index);
verifyAfterSearchFieldChange(index, '#tbl_search_form');
});
});
/**
* Ajax event handler for Table search
*/
$(document).on('submit', '#tbl_search_form.ajax', function (event) {
var unaryFunctions = ['IS NULL', 'IS NOT NULL', '= \'\'', '!= \'\''];
var geomUnaryFunctions = ['IsEmpty', 'IsSimple', 'IsRing', 'IsClosed']; // jQuery object to reuse
var $searchForm = $(this);
event.preventDefault(); // empty previous search results while we are waiting for new results
$('#sqlqueryresultsouter').empty();
var $msgbox = Functions.ajaxShowMessage(Messages.strSearching, false);
Functions.prepareForAjaxRequest($searchForm);
var values = {};
$searchForm.find(':input').each(function () {
var $input = $(this);
if ($input.attr('type') === 'checkbox' || $input.attr('type') === 'radio') {
if ($input.is(':checked')) {
values[this.name] = $input.val();
}
} else {
values[this.name] = $input.val();
}
});
var columnCount = $('select[name="columnsToDisplay[]"] option').length; // Submit values only for the columns that have unary column operator or a search criteria
for (var a = 0; a < columnCount; a++) {
if ($.inArray(values['criteriaColumnOperators[' + a + ']'], unaryFunctions) >= 0) {
continue;
}
if (values['geom_func[' + a + ']'] && $.inArray(values['geom_func[' + a + ']'], geomUnaryFunctions) >= 0) {
continue;
}
if (values['criteriaValues[' + a + ']'] === '' || values['criteriaValues[' + a + ']'] === null) {
delete values['criteriaValues[' + a + ']'];
delete values['criteriaColumnOperators[' + a + ']'];
delete values['criteriaColumnNames[' + a + ']'];
delete values['criteriaColumnTypes[' + a + ']'];
delete values['criteriaColumnCollations[' + a + ']'];
}
} // If all columns are selected, use a single parameter to indicate that
if (values['columnsToDisplay[]'] !== null) {
if (values['columnsToDisplay[]'].length === columnCount) {
delete values['columnsToDisplay[]'];
values.displayAllColumns = true;
}
} else {
values.displayAllColumns = true;
}
$.post($searchForm.attr('action'), values, function (data) {
Functions.ajaxRemoveMessage($msgbox);
if (typeof data !== 'undefined' && data.success === true) {
if (typeof data.sql_query !== 'undefined') {
// zero rows
$('#sqlqueryresultsouter').html(data.sql_query);
} else {
// results found
$('#sqlqueryresultsouter').html(data.message);
$('.sqlqueryresults').trigger('makegrid');
}
$('#tbl_search_form') // workaround for bug #3168569 - Issue on toggling the "Hide search criteria" in chrome.
.slideToggle().hide();
$('#togglesearchformlink') // always start with the Show message
.text(Messages.strShowSearchCriteria);
$('#togglesearchformdiv') // now it's time to show the div containing the link
.show();
$('html, body').animate({
scrollTop: 0
}, 'fast');
} else {
$('#sqlqueryresultsouter').html(data.error);
}
Functions.highlightSql($('#sqlqueryresultsouter'));
}); // end $.post()
}); // Following section is related to the 'function based search' for geometry data types.
// Initially hide all the open_gis_editor spans
$('span.open_search_gis_editor').hide();
$('select.geom_func').on('change', function () {
var $geomFuncSelector = $(this);
var binaryFunctions = ['Contains', 'Crosses', 'Disjoint', 'Equals', 'Intersects', 'Overlaps', 'Touches', 'Within', 'MBRContains', 'MBRDisjoint', 'MBREquals', 'MBRIntersects', 'MBROverlaps', 'MBRTouches', 'MBRWithin', 'ST_Contains', 'ST_Crosses', 'ST_Disjoint', 'ST_Equals', 'ST_Intersects', 'ST_Overlaps', 'ST_Touches', 'ST_Within'];
var tempArray = ['Envelope', 'EndPoint', 'StartPoint', 'ExteriorRing', 'Centroid', 'PointOnSurface'];
var outputGeomFunctions = binaryFunctions.concat(tempArray); // If the chosen function takes two geometry objects as parameters
var $operator = $geomFuncSelector.parents('tr').find('td').eq(4).find('select');
if ($.inArray($geomFuncSelector.val(), binaryFunctions) >= 0) {
$operator.prop('readonly', true);
} else {
$operator.prop('readonly', false);
} // if the chosen function's output is a geometry, enable GIS editor
var $editorSpan = $geomFuncSelector.parents('tr').find('span.open_search_gis_editor');
if ($.inArray($geomFuncSelector.val(), outputGeomFunctions) >= 0) {
$editorSpan.show();
} else {
$editorSpan.hide();
}
});
$(document).on('click', 'span.open_search_gis_editor', function (event) {
event.preventDefault();
var $span = $(this); // Current value
var value = $span.parent('td').children('input[type=\'text\']').val(); // Field name
var field = 'Parameter'; // Column type
var geomFunc = $span.parents('tr').find('.geom_func').val();
var type;
if (geomFunc === 'Envelope') {
type = 'polygon';
} else if (geomFunc === 'ExteriorRing') {
type = 'linestring';
} else {
type = 'point';
} // Names of input field and null checkbox
var inputName = $span.parent('td').children('input[type=\'text\']').attr('name'); // Token
openGISEditor();
if (!gisEditorLoaded) {
loadJSAndGISEditor(value, field, type, inputName);
} else {
loadGISEditor(value, field, type, inputName);
}
});
/**
* Ajax event handler for Range-Search.
*/
$('body').on('change', 'select[name*="criteriaColumnOperators"]', function () {
// Fix for bug #13778, changed 'click' to 'change'
var $sourceSelect = $(this); // Get the column name.
var columnName = $(this).closest('tr').find('th').first().text(); // Get the data-type of column excluding size.
var dataType = $(this).closest('tr').find('td[data-type]').attr('data-type');
dataType = TableSelect.checkIfDataTypeNumericOrDate(dataType); // Get the operator.
var operator = $(this).val();
if ((operator === 'BETWEEN' || operator === 'NOT BETWEEN') && dataType) {
var $msgbox = Functions.ajaxShowMessage();
$.ajax({
url: 'index.php?route=/table/search',
type: 'POST',
data: {
'server': CommonParams.get('server'),
'ajax_request': 1,
'db': $('input[name="db"]').val(),
'table': $('input[name="table"]').val(),
'column': columnName,
'range_search': 1
},
success: function (response) {
Functions.ajaxRemoveMessage($msgbox);
if (response.success) {
// Get the column min value.
var min = response.column_data.min ? '(' + Messages.strColumnMin + ' ' + response.column_data.min + ')' : ''; // Get the column max value.
var max = response.column_data.max ? '(' + Messages.strColumnMax + ' ' + response.column_data.max + ')' : '';
$('#rangeSearchModal').modal('show');
$('#rangeSearchLegend').first().html(operator);
$('#rangeSearchMin').first().text(min);
$('#rangeSearchMax').first().text(max); // Reset input values on reuse
$('#min_value').first().val('');
$('#max_value').first().val(''); // Add datepicker wherever required.
Functions.addDatepicker($('#min_value'), dataType);
Functions.addDatepicker($('#max_value'), dataType);
$('#rangeSearchModalGo').on('click', function () {
var minValue = $('#min_value').val();
var maxValue = $('#max_value').val();
var finalValue = '';
if (minValue.length && maxValue.length) {
finalValue = minValue + ', ' + maxValue;
}
var $targetField = $sourceSelect.closest('tr').find('[name*="criteriaValues"]'); // If target field is a select list.
if ($targetField.is('select')) {
$targetField.val(finalValue);
var $options = $targetField.find('option');
var $closestMin = null;
var $closestMax = null; // Find closest min and max value.
$options.each(function () {
if ($closestMin === null || Math.abs($(this).val() - minValue) < Math.abs($closestMin.val() - minValue)) {
$closestMin = $(this);
}
if ($closestMax === null || Math.abs($(this).val() - maxValue) < Math.abs($closestMax.val() - maxValue)) {
$closestMax = $(this);
}
});
$closestMin.attr('selected', 'selected');
$closestMax.attr('selected', 'selected');
} else {
$targetField.val(finalValue);
}
$('#rangeSearchModal').modal('hide');
});
} else {
Functions.ajaxShowMessage(response.error);
}
},
error: function () {
Functions.ajaxShowMessage(Messages.strErrorProcessingRequest);
}
});
}
});
var windowWidth = $(window).width();
$('.jsresponsive').css('max-width', windowWidth - 69 + 'px');
});

@ -0,0 +1,10 @@
/**
* SQL syntax highlighting transformation plugin js
*
* @package PhpMyAdmin
*/
AJAX.registerOnload('transformations/sql_editor.js', function () {
$('textarea.transform_sql_editor').each(function () {
Functions.getSqlEditor($(this), {}, 'both');
});
});

@ -0,0 +1,90 @@
/* global u2f */
// js/vendor/u2f-api-polyfill.js
AJAX.registerOnload('u2f.js', function () {
var $inputReg = $('#u2f_registration_response');
if ($inputReg.length > 0) {
var $formReg = $inputReg.parents('form');
$formReg.find('input[type=submit]').hide();
setTimeout(function () {
// A magic JS function that talks to the USB device. This function will keep polling for the USB device until it finds one.
var request = JSON.parse($inputReg.attr('data-request'));
u2f.register(request.appId, [request], JSON.parse($inputReg.attr('data-signatures')), function (data) {
// Handle returning error data
if (data.errorCode && data.errorCode !== 0) {
switch (data.errorCode) {
case 5:
Functions.ajaxShowMessage(Messages.strU2FTimeout, false, 'error');
break;
case 4:
Functions.ajaxShowMessage(Messages.strU2FErrorRegister, false, 'error');
break;
case 3:
Functions.ajaxShowMessage(Messages.strU2FInvalidClient, false, 'error');
break;
case 2:
Functions.ajaxShowMessage(Messages.strU2FBadRequest, false, 'error');
break;
default:
Functions.ajaxShowMessage(Messages.strU2FUnknown, false, 'error');
break;
}
return;
} // Fill and submit form.
$inputReg.val(JSON.stringify(data));
$formReg.trigger('submit');
});
}, 1000);
}
var $inputAuth = $('#u2f_authentication_response');
if ($inputAuth.length > 0) {
var $formAuth = $inputAuth.parents('form');
$formAuth.find('input[type=submit]').hide();
setTimeout(function () {
// Magic JavaScript talking to your HID
// appid, challenge, authenticateRequests
var request = JSON.parse($inputAuth.attr('data-request'));
u2f.sign(request[0].appId, request[0].challenge, request, function (data) {
// Handle returning error data
if (data.errorCode && data.errorCode !== 0) {
switch (data.errorCode) {
case 5:
Functions.ajaxShowMessage(Messages.strU2FTimeout, false, 'error');
break;
case 4:
Functions.ajaxShowMessage(Messages.strU2FErrorAuthenticate, false, 'error');
break;
case 3:
Functions.ajaxShowMessage(Messages.strU2FInvalidClient, false, 'error');
break;
case 2:
Functions.ajaxShowMessage(Messages.strU2FBadRequest, false, 'error');
break;
default:
Functions.ajaxShowMessage(Messages.strU2FUnknown, false, 'error');
break;
}
return;
} // Fill and submit form.
$inputAuth.val(JSON.stringify(data));
$formAuth.trigger('submit');
});
}, 1000);
}
});

@ -0,0 +1,13 @@
/**
* Conditionally included if framing is not allowed
*/
if (self === top) {
var styleElement = document.getElementById('cfs-style');
// check if styleElement has already been removed
// to avoid frequently reported js error
if (typeof(styleElement) !== 'undefined' && styleElement !== null) {
styleElement.parentNode.removeChild(styleElement);
}
} else {
top.location = self.location;
}

@ -0,0 +1,261 @@
/**
* JavaScript functions used on Database Search page
*
* @requires jQuery
* @requires js/functions.js
*
* @package PhpMyAdmin
*/
/* global makeGrid */ // js/makegrid.js
/**
* AJAX script for the Database Search page.
*
* Actions ajaxified here:
* Retrieve result of SQL query
*/
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('database/search.js', function () {
$('a.browse_results').off('click');
$('a.delete_results').off('click');
$('#buttonGo').off('click');
$('#togglesearchresultlink').off('click');
$('#togglequerybox').off('click');
$('#togglesearchformlink').off('click');
$('#select_all').off('click');
$('#unselect_all').off('click');
$(document).off('submit', '#db_search_form.ajax');
});
AJAX.registerOnload('database/search.js', function () {
/** Hide the table link in the initial search result */
var icon = Functions.getImage('s_tbl', '', { 'id': 'table-image' }).toString();
$('#table-info').prepend(icon).hide();
/** Hide the browse and deleted results in the new search criteria */
$('#buttonGo').on('click', function () {
$('#table-info').hide();
$('#browse-results').hide();
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
});
/**
* Prepare a div containing a link for toggle the search results
*/
$('#togglesearchresultsdiv')
/** don't show it until we have results on-screen */
.hide();
/**
* Changing the displayed text according to
* the hide/show criteria in search result forms
*/
$('#togglesearchresultlink')
.html(Messages.strHideSearchResults)
.on('click', function () {
var $link = $(this);
$('#searchresults').slideToggle();
if ($link.text() === Messages.strHideSearchResults) {
$link.text(Messages.strShowSearchResults);
} else {
$link.text(Messages.strHideSearchResults);
}
/** avoid default click action */
return false;
});
/**
* Prepare a div containing a link for toggle the search form,
* otherwise it's incorrectly displayed after a couple of clicks
*/
$('#togglesearchformdiv')
.hide(); // don't show it until we have results on-screen
/**
* Changing the displayed text according to
* the hide/show criteria in search form
*/
$('#togglequerybox')
.hide()
.on('click', function () {
var $link = $(this);
$('#sqlqueryform').slideToggle('medium');
if ($link.text() === Messages.strHideQueryBox) {
$link.text(Messages.strShowQueryBox);
} else {
$link.text(Messages.strHideQueryBox);
}
/** avoid default click action */
return false;
});
/** don't show it until we have results on-screen */
/**
* Changing the displayed text according to
* the hide/show criteria in search criteria form
*/
$('#togglesearchformlink')
.html(Messages.strShowSearchCriteria)
.on('click', function () {
var $link = $(this);
$('#db_search_form').slideToggle();
if ($link.text() === Messages.strHideSearchCriteria) {
$link.text(Messages.strShowSearchCriteria);
} else {
$link.text(Messages.strHideSearchCriteria);
}
/** avoid default click action */
return false;
});
/*
* Ajax Event handler for retrieving the results from a table
*/
$(document).on('click', 'a.browse_results', function (e) {
e.preventDefault();
/** Hides the results shown by the delete criteria */
var $msg = Functions.ajaxShowMessage(Messages.strBrowsing, false);
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
/** Load the browse results to the page */
$('#table-info').show();
var tableName = $(this).data('table-name');
$('#table-link').attr({ 'href' : $(this).attr('href') }).text(tableName);
var url = $(this).attr('href') + '#searchresults';
var browseSql = $(this).data('browse-sql');
var params = {
'ajax_request': true,
'is_js_confirmed': true,
'sql_query' : browseSql
};
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success) {
$('#browse-results').html(data.message);
Functions.ajaxRemoveMessage($msg);
$('.table_results').each(function () {
makeGrid(this, true, true, true, true);
});
$('#browse-results').show();
Functions.highlightSql($('#browse-results'));
$('html, body')
.animate({
scrollTop: $('#browse-results').offset().top
}, 1000);
} else {
Functions.ajaxShowMessage(data.error, false);
}
});
});
/*
* Ajax Event handler for deleting the results from a table
*/
$(document).on('click', 'a.delete_results', function (e) {
e.preventDefault();
/** Hides the results shown by the browse criteria */
$('#table-info').hide();
$('#sqlqueryform').hide();
$('#togglequerybox').hide();
/** Conformation message for deletion */
var msg = Functions.sprintf(
Messages.strConfirmDeleteResults,
$(this).data('table-name')
);
if (confirm(msg)) {
var $msg = Functions.ajaxShowMessage(Messages.strDeleting, false);
/** Load the deleted option to the page*/
$('#sqlqueryform').html('');
var params = {
'ajax_request': true,
'is_js_confirmed': true,
'sql_query': $(this).data('delete-sql')
};
var url = $(this).attr('href');
$.post(url, params, function (data) {
if (typeof data === 'undefined' || !data.success) {
Functions.ajaxShowMessage(data.error, false);
return;
}
$('#sqlqueryform').html(data.sql_query);
/** Refresh the search results after the deletion */
$('#buttonGo').trigger('click');
$('#togglequerybox').html(Messages.strHideQueryBox);
/** Show the results of the deletion option */
$('#browse-results').hide();
$('#sqlqueryform').show();
$('#togglequerybox').show();
$('html, body')
.animate({
scrollTop: $('#browse-results').offset().top
}, 1000);
Functions.ajaxRemoveMessage($msg);
});
}
});
/**
* Ajax Event handler for retrieving the result of an SQL Query
*/
$(document).on('submit', '#db_search_form.ajax', function (event) {
event.preventDefault();
if ($('#criteriaTables :selected').length === 0) {
Functions.ajaxShowMessage(Messages.strNoTableSelected);
return;
}
var $msgbox = Functions.ajaxShowMessage(Messages.strSearching, false);
// jQuery object to reuse
var $form = $(this);
Functions.prepareForAjaxRequest($form);
var url = $form.serialize() + CommonParams.get('arg_separator') + 'submit_search=' + $('#buttonGo').val();
$.post($form.attr('action'), url, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
// found results
$('#searchresults').html(data.message);
$('#togglesearchresultlink')
// always start with the Show message
.text(Messages.strHideSearchResults);
$('#togglesearchresultsdiv')
// now it's time to show the div containing the link
.show();
$('#searchresults').show();
$('#db_search_form')
// workaround for Chrome problem (bug #3168569)
.slideToggle()
.hide();
$('#togglesearchformlink')
// always start with the Show message
.text(Messages.strShowSearchCriteria);
$('#togglesearchformdiv')
// now it's time to show the div containing the link
.show();
} else {
// error message (zero rows)
$('#searchresults').html(data.error).show();
}
Functions.ajaxRemoveMessage($msgbox);
});
});
$('#select_all').on('click', function () {
Functions.setSelectOptions('db_search', 'criteriaTables[]', true);
return false;
});
$('#unselect_all').on('click', function () {
Functions.setSelectOptions('db_search', 'criteriaTables[]', false);
return false;
});
}); // end $()

@ -0,0 +1,93 @@
/**
* Unbind all event handlers before tearing down the page
*/
AJAX.registerTeardown('database/tracking.js', function () {
$('body').off('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]');
$('body').off('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]');
$('body').off('click', 'a.delete_tracking_anchor.ajax');
});
/**
* Bind event handlers
*/
AJAX.registerOnload('database/tracking.js', function () {
var $versions = $('#versions');
$versions.find('tr').first().find('th').append($('<div class="sorticon"></div>'));
$versions.tablesorter({
sortList: [[1, 0]],
headers: {
0: { sorter: false },
2: { sorter: 'integer' },
5: { sorter: false },
6: { sorter: false },
7: { sorter: false }
}
});
var $noVersions = $('#noversions');
$noVersions.find('tr').first().find('th').append($('<div class="sorticon"></div>'));
$noVersions.tablesorter({
sortList: [[1, 0]],
headers: {
0: { sorter: false },
2: { sorter: false }
}
});
var $body = $('body');
/**
* Handles multi submit for tracked tables
*/
$body.on('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]', function (e) {
e.preventDefault();
var $button = $(this);
var $form = $button.parent('form');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
if ($button.val() === 'delete_tracking') {
var question = Messages.strDeleteTrackingDataMultiple;
$button.confirm(question, $form.attr('action'), function (url) {
Functions.ajaxShowMessage(Messages.strDeletingTrackingData);
AJAX.source = $form;
$.post(url, submitData, AJAX.responseHandler);
});
} else {
Functions.ajaxShowMessage();
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
}
});
/**
* Handles multi submit for untracked tables
*/
$body.on('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]', function (e) {
e.preventDefault();
var $button = $(this);
var $form = $button.parent('form');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
Functions.ajaxShowMessage();
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
});
/**
* Ajax Event handler for 'Delete tracking'
*/
$body.on('click', 'a.delete_tracking_anchor.ajax', function (e) {
e.preventDefault();
var $anchor = $(this);
var question = Messages.strDeleteTrackingData;
$anchor.confirm(question, $anchor.attr('href'), function (url) {
Functions.ajaxShowMessage(Messages.strDeletingTrackingData);
AJAX.source = $anchor;
var argSep = CommonParams.get('arg_separator');
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
params += argSep + 'ajax_page_request=1';
$.post(url, params, AJAX.responseHandler);
});
});
});

@ -0,0 +1,530 @@
AJAX.registerTeardown('database/triggers.js', function () {
$(document).off('click', 'a.ajax.add_anchor, a.ajax.edit_anchor');
$(document).off('click', 'a.ajax.export_anchor');
$(document).off('click', '#bulkActionExportButton');
$(document).off('click', 'a.ajax.drop_anchor');
$(document).off('click', '#bulkActionDropButton');
});
const DatabaseTriggers = {
/**
* @var $ajaxDialog Query object containing the reference to the
* dialog that contains the editor
*/
$ajaxDialog: null,
/**
* @var syntaxHiglighter Reference to the codemirror editor
*/
syntaxHiglighter: null,
/**
* @var buttonOptions Object containing options for
* the jQueryUI dialog buttons
*/
buttonOptions: {},
/**
* Validate editor form fields.
*
* @return {bool}
*/
validate: function () {
/**
* @var $elm a jQuery object containing the reference
* to an element that is being validated
*/
var $elm = null;
// Common validation. At the very least the name
// and the definition must be provided for an item
$elm = $('table.rte_table').last().find('input[name=item_name]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(Messages.strFormEmpty);
return false;
}
$elm = $('table.rte_table').find('textarea[name=item_definition]');
if ($elm.val() === '') {
if (this.syntaxHiglighter !== null) {
this.syntaxHiglighter.focus();
} else {
$('textarea[name=item_definition]').last().trigger('focus');
}
alert(Messages.strFormEmpty);
return false;
}
// The validation has so far passed, so now
// we can validate item-specific fields.
return this.validateCustom();
}, // end validate()
/**
* Validate custom editor form fields.
* This function can be overridden by
* other files in this folder
*
* @return {bool}
*/
validateCustom: function () {
return true;
}, // end validateCustom()
exportDialog: function ($this) {
var $msg = Functions.ajaxShowMessage();
if ($this.attr('id') === 'bulkActionExportButton') {
var combined = {
success: true,
title: Messages.strExport,
message: '',
error: ''
};
// export anchors of all selected rows
var exportAnchors = $('input.checkall:checked').parents('tr').find('.export_anchor');
var count = exportAnchors.length;
var returnCount = 0;
var p = $.when();
exportAnchors.each(function () {
var h = $(this).attr('href');
p = p.then(function () {
return $.get(h, { 'ajax_request': true }, function (data) {
returnCount++;
if (data.success === true) {
combined.message += '\n' + data.message + '\n';
if (returnCount === count) {
showExport(combined);
}
} else {
// complain even if one export is failing
combined.success = false;
combined.error += '\n' + data.error + '\n';
if (returnCount === count) {
showExport(combined);
}
}
});
});
});
} else {
$.get($this.attr('href'), { 'ajax_request': true }, showExport);
}
Functions.ajaxRemoveMessage($msg);
function showExport (data) {
if (data.success === true) {
Functions.ajaxRemoveMessage($msg);
/**
* @var button_options Object containing options
* for jQueryUI dialog buttons
*/
var buttonOptions = {};
buttonOptions[Messages.strClose] = function () {
$(this).dialog('close').remove();
};
/**
* Display the dialog to the user
*/
data.message = '<textarea cols="40" rows="15" class="w-100">' + data.message + '</textarea>';
var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
width: 500,
buttons: buttonOptions,
title: data.title
});
// Attach syntax highlighted editor to export dialog
/**
* @var $elm jQuery object containing the reference
* to the Export textarea.
*/
var $elm = $ajaxDialog.find('textarea');
Functions.getSqlEditor($elm);
} else {
Functions.ajaxShowMessage(data.error, false);
}
} // end showExport()
}, // end exportDialog()
editorDialog: function (isNew, $this) {
var that = this;
/**
* @var $edit_row jQuery object containing the reference to
* the row of the the item being edited
* from the list of items
*/
var $editRow = null;
if ($this.hasClass('edit_anchor')) {
// Remember the row of the item being edited for later,
// so that if the edit is successful, we can replace the
// row with info about the modified item.
$editRow = $this.parents('tr');
}
/**
* @var $msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage();
$.get($this.attr('href'), { 'ajax_request': true }, function (data) {
if (data.success === true) {
// We have successfully fetched the editor form
Functions.ajaxRemoveMessage($msg);
// Now define the function that is called when
// the user presses the "Go" button
that.buttonOptions[Messages.strGo] = function () {
// Move the data from the codemirror editor back to the
// textarea, where it can be used in the form submission.
if (typeof CodeMirror !== 'undefined') {
that.syntaxHiglighter.save();
}
// Validate editor and submit request, if passed.
if (that.validate()) {
/**
* @var data Form data to be sent in the AJAX request
*/
var data = $('form.rte_form').last().serialize();
$msg = Functions.ajaxShowMessage(
Messages.strProcessingRequest
);
var url = $('form.rte_form').last().attr('action');
$.post(url, data, function (data) {
if (data.success === true) {
// Item created successfully
Functions.ajaxRemoveMessage($msg);
Functions.slidingMessage(data.message);
that.$ajaxDialog.dialog('close');
// If we are in 'edit' mode, we must
// remove the reference to the old row.
if (mode === 'edit' && $editRow !== null) {
$editRow.remove();
}
// Sometimes, like when moving a trigger from
// a table to another one, the new row should
// not be inserted into the list. In this case
// "data.insert" will be set to false.
if (data.insert) {
// Insert the new row at the correct
// location in the list of items
/**
* @var text Contains the name of an item from
* the list that is used in comparisons
* to find the correct location where
* to insert a new row.
*/
var text = '';
/**
* @var inserted Whether a new item has been
* inserted in the list or not
*/
var inserted = false;
$('table.data').find('tr').each(function () {
text = $(this)
.children('td')
.eq(0)
.find('strong')
.text()
.toUpperCase()
.trim();
if (text !== '' && text > data.name) {
$(this).before(data.new_row);
inserted = true;
return false;
}
});
if (! inserted) {
// If we didn't manage to insert the row yet,
// it must belong at the end of the list,
// so we insert it there.
$('table.data').append(data.new_row);
}
// Fade-in the new row
$('tr.ajaxInsert')
.show('slow')
.removeClass('ajaxInsert');
} else if ($('table.data').find('tr').has('td').length === 0) {
// If we are not supposed to insert the new row,
// we will now check if the table is empty and
// needs to be hidden. This will be the case if
// we were editing the only item in the list,
// which we removed and will not be inserting
// something else in its place.
$('table.data').hide('slow', function () {
$('#nothing2display').show('slow');
});
}
// Now we have inserted the row at the correct
// position, but surely at least some row classes
// are wrong now. So we will iterate through
// all rows and assign correct classes to them
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$('table.data').find('tr').has('td').each(function () {
rowclass = (ct % 2 === 0) ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
});
// If this is the first item being added, remove
// the "No items" message and show the list.
if ($('table.data').find('tr').has('td').length > 0 &&
$('#nothing2display').is(':visible')
) {
$('#nothing2display').hide('slow', function () {
$('table.data').show('slow');
});
}
Navigation.reload();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
} // end "if (that.validate())"
}; // end of function that handles the submission of the Editor
that.buttonOptions[Messages.strClose] = function () {
$(this).dialog('close');
};
/**
* Display the dialog to the user
*/
that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
width: 700,
minWidth: 500,
buttons: that.buttonOptions,
// Issue #15810 - use button titles for modals (eg: new procedure)
// Respect the order: title on href tag, href content, title sent in response
title: $this.attr('title') || $this.text() || $(data.title).text(),
modal: true,
open: function () {
$('#rteDialog').dialog('option', 'max-height', $(window).height());
if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
$('#rteDialog').dialog('option', 'height', $(window).height());
}
$(this).find('input[name=item_name]').trigger('focus');
$(this).find('input.datefield').each(function () {
Functions.addDatepicker($(this).css('width', '95%'), 'date');
});
$(this).find('input.datetimefield').each(function () {
Functions.addDatepicker($(this).css('width', '95%'), 'datetime');
});
$.datepicker.initialized = false;
},
close: function () {
$(this).remove();
}
});
/**
* @var mode Used to remember whether the editor is in
* "Edit" or "Add" mode
*/
var mode = 'add';
if ($('input[name=editor_process_edit]').length > 0) {
mode = 'edit';
}
// Attach syntax highlighted editor to the definition
/**
* @var elm jQuery object containing the reference to
* the Definition textarea.
*/
var $elm = $('textarea[name=item_definition]').last();
var linterOptions = {};
linterOptions.triggerEditor = true;
that.syntaxHiglighter = Functions.getSqlEditor($elm, {}, 'both', linterOptions);
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.get()
},
dropDialog: function ($this) {
/**
* @var $curr_row Object containing reference to the current row
*/
var $currRow = $this.parents('tr');
/**
* @var question String containing the question to be asked for confirmation
*/
var question = $('<div></div>').text(
$currRow.children('td').children('.drop_sql').html()
);
// We ask for confirmation first here, before submitting the ajax request
$this.confirm(question, $this.attr('href'), function (url) {
/**
* @var msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
var params = Functions.getJsConfirmCommonParam(this, $this.getPostData());
$.post(url, params, function (data) {
if (data.success === true) {
/**
* @var $table Object containing reference
* to the main list of elements
*/
var $table = $currRow.parent();
// Check how many rows will be left after we remove
// the one that the user has requested us to remove
if ($table.find('tr').length === 3) {
// If there are two rows left, it means that they are
// the header of the table and the rows that we are
// about to remove, so after the removal there will be
// nothing to show in the table, so we hide it.
$table.hide('slow', function () {
$(this).find('tr.even, tr.odd').remove();
$('.withSelected').remove();
$('#nothing2display').show('slow');
});
} else {
$currRow.hide('slow', function () {
$(this).remove();
// Now we have removed the row from the list, but maybe
// some row classes are wrong now. So we will iterate
// through all rows and assign correct classes to them.
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$table.find('tr').has('td').each(function () {
rowclass = (ct % 2 === 1) ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
});
});
}
// Get rid of the "Loading" message
Functions.ajaxRemoveMessage($msg);
// Show the query that we just executed
Functions.slidingMessage(data.sql_query);
Navigation.reload();
} else {
Functions.ajaxShowMessage(data.error, false);
}
}); // end $.post()
});
},
dropMultipleDialog: function ($this) {
// We ask for confirmation here
$this.confirm(Messages.strDropRTEitems, '', function () {
/**
* @var msg jQuery object containing the reference to
* the AJAX message shown to the user
*/
var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
// drop anchors of all selected rows
var dropAnchors = $('input.checkall:checked').parents('tr').find('.drop_anchor');
var success = true;
var count = dropAnchors.length;
var returnCount = 0;
dropAnchors.each(function () {
var $anchor = $(this);
/**
* @var $curr_row Object containing reference to the current row
*/
var $currRow = $anchor.parents('tr');
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
$.post($anchor.attr('href'), params, function (data) {
returnCount++;
if (data.success === true) {
/**
* @var $table Object containing reference
* to the main list of elements
*/
var $table = $currRow.parent();
// Check how many rows will be left after we remove
// the one that the user has requested us to remove
if ($table.find('tr').length === 3) {
// If there are two rows left, it means that they are
// the header of the table and the rows that we are
// about to remove, so after the removal there will be
// nothing to show in the table, so we hide it.
$table.hide('slow', function () {
$(this).find('tr.even, tr.odd').remove();
$('.withSelected').remove();
$('#nothing2display').show('slow');
});
} else {
$currRow.hide('fast', function () {
// we will iterate
// through all rows and assign correct classes to them.
/**
* @var ct Count of processed rows
*/
var ct = 0;
/**
* @var rowclass Class to be attached to the row
* that is being processed
*/
var rowclass = '';
$table.find('tr').has('td').each(function () {
rowclass = (ct % 2 === 1) ? 'odd' : 'even';
$(this).removeClass().addClass(rowclass);
ct++;
});
});
$currRow.remove();
}
if (returnCount === count) {
if (success) {
// Get rid of the "Loading" message
Functions.ajaxRemoveMessage($msg);
$('#rteListForm_checkall').prop({ checked: false, indeterminate: false });
}
Navigation.reload();
}
} else {
Functions.ajaxShowMessage(data.error, false);
success = false;
if (returnCount === count) {
Navigation.reload();
}
}
}); // end $.post()
}); // end drop_anchors.each()
});
}
};
AJAX.registerOnload('database/triggers.js', function () {
/**
* Attach Ajax event handlers for the Add/Edit functionality.
*/
$(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) {
event.preventDefault();
if ($(this).hasClass('add_anchor')) {
$.datepicker.initialized = false;
}
DatabaseTriggers.editorDialog($(this).hasClass('add_anchor'), $(this));
});
/**
* Attach Ajax event handlers for Export
*/
$(document).on('click', 'a.ajax.export_anchor', function (event) {
event.preventDefault();
DatabaseTriggers.exportDialog($(this));
});
$(document).on('click', '#bulkActionExportButton', function (event) {
event.preventDefault();
DatabaseTriggers.exportDialog($(this));
});
/**
* Attach Ajax event handlers for Drop functionality
*/
$(document).on('click', 'a.ajax.drop_anchor', function (event) {
event.preventDefault();
DatabaseTriggers.dropDialog($(this));
});
$(document).on('click', '#bulkActionDropButton', function (event) {
event.preventDefault();
DatabaseTriggers.dropMultipleDialog($(this));
});
});

@ -0,0 +1,178 @@
/* global DesignerOfflineDB */ // js/designer/database.js
// eslint-disable-next-line no-unused-vars
/* global db, selectedPage:writable */ // js/designer/init.js
/* global DesignerMove */ // js/designer/move.js
/* global DesignerObjects */ // js/designer/objects.js
var DesignerPage = {};
DesignerPage.showTablesInLandingPage = function (db) {
DesignerPage.loadFirstPage(db, function (page) {
if (page) {
DesignerPage.loadHtmlForPage(page.pgNr);
selectedPage = page.pgNr;
} else {
DesignerPage.showNewPageTables(true);
}
});
};
DesignerPage.saveToNewPage = function (db, pageName, tablePositions, callback) {
DesignerPage.createNewPage(db, pageName, function (page) {
if (page) {
var tblCords = [];
var saveCallback = function (id) {
tblCords.push(id);
if (tablePositions.length === tblCords.length) {
page.tblCords = tblCords;
DesignerOfflineDB.addObject('pdf_pages', page);
}
};
for (var pos = 0; pos < tablePositions.length; pos++) {
tablePositions[pos].pdfPgNr = page.pgNr;
DesignerPage.saveTablePositions(tablePositions[pos], saveCallback);
}
if (typeof callback !== 'undefined') {
callback(page);
}
}
});
};
DesignerPage.saveToSelectedPage = function (db, pageId, pageName, tablePositions, callback) {
DesignerPage.deletePage(pageId);
DesignerPage.saveToNewPage(db, pageName, tablePositions, function (page) {
if (typeof callback !== 'undefined') {
callback(page);
}
selectedPage = page.pgNr;
});
};
DesignerPage.createNewPage = function (db, pageName, callback) {
var newPage = new DesignerObjects.PdfPage(db, pageName);
DesignerOfflineDB.addObject('pdf_pages', newPage, function (pgNr) {
newPage.pgNr = pgNr;
if (typeof callback !== 'undefined') {
callback(newPage);
}
});
};
DesignerPage.saveTablePositions = function (positions, callback) {
DesignerOfflineDB.addObject('table_coords', positions, callback);
};
DesignerPage.createPageList = function (db, callback) {
DesignerOfflineDB.loadAllObjects('pdf_pages', function (pages) {
var html = '';
for (var p = 0; p < pages.length; p++) {
var page = pages[p];
if (page.dbName === db) {
html += '<option value="' + page.pgNr + '">';
html += Functions.escapeHtml(page.pageDescr) + '</option>';
}
}
if (typeof callback !== 'undefined') {
callback(html);
}
});
};
DesignerPage.deletePage = function (pageId, callback) {
DesignerOfflineDB.loadObject('pdf_pages', pageId, function (page) {
if (page) {
for (var i = 0; i < page.tblCords.length; i++) {
DesignerOfflineDB.deleteObject('table_coords', page.tblCords[i]);
}
DesignerOfflineDB.deleteObject('pdf_pages', pageId, callback);
}
});
};
DesignerPage.loadFirstPage = function (db, callback) {
DesignerOfflineDB.loadAllObjects('pdf_pages', function (pages) {
var firstPage = null;
for (var i = 0; i < pages.length; i++) {
var page = pages[i];
if (page.dbName === db) {
// give preference to a page having same name as the db
if (page.pageDescr === db) {
callback(page);
return;
}
if (firstPage === null) {
firstPage = page;
}
}
}
callback(firstPage);
});
};
DesignerPage.showNewPageTables = function (check) {
var allTables = $('#id_scroll_tab').find('td input:checkbox');
allTables.prop('checked', check);
for (var tab = 0; tab < allTables.length; tab++) {
var input = allTables[tab];
if (input.value) {
var element = document.getElementById(input.value);
element.style.top = DesignerPage.getRandom(550, 20) + 'px';
element.style.left = DesignerPage.getRandom(700, 20) + 'px';
DesignerMove.visibleTab(input, input.value);
}
}
selectedPage = -1;
$('#page_name').text(Messages.strUntitled);
DesignerMove.markUnsaved();
};
DesignerPage.loadHtmlForPage = function (pageId) {
DesignerPage.showNewPageTables(true);
DesignerPage.loadPageObjects(pageId, function (page, tblCords) {
$('#name-panel').find('#page_name').text(page.pageDescr);
var tableMissing = false;
for (var t = 0; t < tblCords.length; t++) {
var tbId = db + '.' + tblCords[t].tableName;
var table = document.getElementById(tbId);
if (table === null) {
tableMissing = true;
continue;
}
table.style.top = tblCords[t].y + 'px';
table.style.left = tblCords[t].x + 'px';
var checkbox = document.getElementById('check_vis_' + tbId);
checkbox.checked = true;
DesignerMove.visibleTab(checkbox, checkbox.value);
}
DesignerMove.markSaved();
if (tableMissing === true) {
DesignerMove.markUnsaved();
Functions.ajaxShowMessage(Messages.strSavedPageTableMissing);
}
selectedPage = page.pgNr;
});
};
DesignerPage.loadPageObjects = function (pageId, callback) {
DesignerOfflineDB.loadObject('pdf_pages', pageId, function (page) {
var tblCords = [];
var count = page.tblCords.length;
for (var i = 0; i < count; i++) {
DesignerOfflineDB.loadObject('table_coords', page.tblCords[i], function (tblCord) {
tblCords.push(tblCord);
if (tblCords.length === count) {
if (typeof callback !== 'undefined') {
callback(page, tblCords);
}
}
});
}
});
};
DesignerPage.getRandom = function (max, min) {
var val = Math.random() * (max - min) + min;
return Math.floor(val);
};

@ -0,0 +1,154 @@
/**
* Functions used in the import tab
*
*/
/**
* Toggles the hiding and showing of each plugin's options
* according to the currently selected plugin from the dropdown list
*/
function changePluginOpts () {
$('#format_specific_opts').find('div.format_specific_options').each(function () {
$(this).hide();
});
var selectedPluginName = $('#plugins').find('option:selected').val();
$('#' + selectedPluginName + '_options').fadeIn('slow');
const importNotification = document.getElementById('import_notification');
importNotification.innerText = '';
if (selectedPluginName === 'csv') {
importNotification.innerHTML = '<div class="alert alert-info mb-0 mt-3" role="alert">' + Messages.strImportCSV + '</div>';
}
}
/**
* Toggles the hiding and showing of each plugin's options and sets the selected value
* in the plugin dropdown list according to the format of the selected file
*
* @param {string} fname
*/
function matchFile (fname) {
var fnameArray = fname.toLowerCase().split('.');
var len = fnameArray.length;
if (len !== 0) {
var extension = fnameArray[len - 1];
if (extension === 'gz' || extension === 'bz2' || extension === 'zip') {
len--;
}
// Only toggle if the format of the file can be imported
if ($('select[name=\'format\'] option').filterByValue(fnameArray[len - 1]).length === 1) {
$('select[name=\'format\'] option').filterByValue(fnameArray[len - 1]).prop('selected', true);
changePluginOpts();
}
}
}
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('import.js', function () {
$('#plugins').off('change');
$('#input_import_file').off('change');
$('#select_local_import_file').off('change');
$('#input_import_file').off('change').off('focus');
$('#select_local_import_file').off('focus');
$('#text_csv_enclosed').add('#text_csv_escaped').off('keyup');
});
AJAX.registerOnload('import.js', function () {
// import_file_form validation.
$(document).on('submit', '#import_file_form', function () {
var radioLocalImport = $('#localFileTab');
var radioImport = $('#uploadFileTab');
var fileMsg = '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error"> ' + Messages.strImportDialogMessage + '</div>';
var wrongTblNameMsg = '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error">' + Messages.strTableNameDialogMessage + '</div>';
var wrongDBNameMsg = '<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error">' + Messages.strDBNameDialogMessage + '</div>';
if (radioLocalImport.length !== 0) {
// remote upload.
if (radioImport.hasClass('active') && $('#input_import_file').val() === '') {
$('#input_import_file').trigger('focus');
Functions.ajaxShowMessage(fileMsg, false);
return false;
}
if (radioLocalImport.hasClass('active')) {
if ($('#select_local_import_file').length === 0) {
Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error"> ' + Messages.strNoImportFile + ' </div>', false);
return false;
}
if ($('#select_local_import_file').val() === '') {
$('#select_local_import_file').trigger('focus');
Functions.ajaxShowMessage(fileMsg, false);
return false;
}
}
} else {
// local upload.
if ($('#input_import_file').val() === '') {
$('#input_import_file').trigger('focus');
Functions.ajaxShowMessage(fileMsg, false);
return false;
}
if ($('#text_csv_new_tbl_name').length > 0) {
var newTblName = $('#text_csv_new_tbl_name').val();
if (newTblName.length > 0 && newTblName.trim().length === 0) {
Functions.ajaxShowMessage(wrongTblNameMsg, false);
return false;
}
}
if ($('#text_csv_new_db_name').length > 0) {
var newDBName = $('#text_csv_new_db_name').val();
if (newDBName.length > 0 && newDBName.trim().length === 0) {
Functions.ajaxShowMessage(wrongDBNameMsg, false);
return false;
}
}
}
// show progress bar.
$('#upload_form_status').css('display', 'inline');
$('#upload_form_status_info').css('display', 'inline');
});
// Initially display the options for the selected plugin
changePluginOpts();
// Whenever the selected plugin changes, change the options displayed
$('#plugins').on('change', function () {
changePluginOpts();
});
$('#input_import_file').on('change', function () {
matchFile($(this).val());
});
$('#select_local_import_file').on('change', function () {
matchFile($(this).val());
});
/**
* Set up the interface for Javascript-enabled browsers since the default is for
* Javascript-disabled browsers
*/
$('#format_specific_opts').find('div.format_specific_options')
.find('h3')
.remove();
// $("form[name=import] *").unwrap();
/**
* for input element text_csv_enclosed and text_csv_escaped allow just one character to enter.
* as mysql allows just one character for these fields,
* if first character is escape then allow two including escape character.
*/
$('#text_csv_enclosed').add('#text_csv_escaped').on('keyup', function () {
if ($(this).val().length === 2 && $(this).val().charAt(0) !== '\\') {
$(this).val($(this).val().substring(0, 1));
return false;
}
return true;
});
});

@ -0,0 +1,819 @@
/**
* @fileoverview function used for index manipulation pages
* @name Table Structure
*
* @requires jQuery
* @requires jQueryUI
* @required js/functions.js
*/
/* global fulltextIndexes:writable, indexes:writable, primaryIndexes:writable, spatialIndexes:writable, uniqueIndexes:writable */ // js/functions.js
var Indexes = {};
/**
* Returns the array of indexes based on the index choice
*
* @param {string} indexChoice index choice
*
* @return {null|object}
*/
Indexes.getIndexArray = function (indexChoice) {
var sourceArray = null;
switch (indexChoice.toLowerCase()) {
case 'primary':
sourceArray = primaryIndexes;
break;
case 'unique':
sourceArray = uniqueIndexes;
break;
case 'index':
sourceArray = indexes;
break;
case 'fulltext':
sourceArray = fulltextIndexes;
break;
case 'spatial':
sourceArray = spatialIndexes;
break;
default:
return null;
}
return sourceArray;
};
/**
* Hides/shows the inputs and submits appropriately depending
* on whether the index type chosen is 'SPATIAL' or not.
*/
Indexes.checkIndexType = function () {
/**
* @var {JQuery<HTMLElement}, Dropdown to select the index choice.
*/
var $selectIndexChoice = $('#select_index_choice');
/**
* @var {JQuery<HTMLElement}, Dropdown to select the index type.
*/
var $selectIndexType = $('#select_index_type');
/**
* @var {JQuery<HTMLElement}, Table header for the size column.
*/
var $sizeHeader = $('#index_columns').find('thead tr').children('th').eq(1);
/**
* @var {JQuery<HTMLElement}, Inputs to specify the columns for the index.
*/
var $columnInputs = $('select[name="index[columns][names][]"]');
/**
* @var {JQuery<HTMLElement}, Inputs to specify sizes for columns of the index.
*/
var $sizeInputs = $('input[name="index[columns][sub_parts][]"]');
/**
* @var {JQuery<HTMLElement}, Footer containing the controllers to add more columns
*/
var $addMore = $('#index_frm').find('.add_more');
if ($selectIndexChoice.val() === 'SPATIAL') {
// Disable and hide the size column
$sizeHeader.hide();
$sizeInputs.each(function () {
$(this)
.prop('disabled', true)
.parent('td').hide();
});
// Disable and hide the columns of the index other than the first one
var initial = true;
$columnInputs.each(function () {
var $columnInput = $(this);
if (! initial) {
$columnInput
.prop('disabled', true)
.parent('td').hide();
} else {
initial = false;
}
});
// Hide controllers to add more columns
$addMore.hide();
} else {
// Enable and show the size column
$sizeHeader.show();
$sizeInputs.each(function () {
$(this)
.prop('disabled', false)
.parent('td').show();
});
// Enable and show the columns of the index
$columnInputs.each(function () {
$(this)
.prop('disabled', false)
.parent('td').show();
});
// Show controllers to add more columns
$addMore.show();
}
if ($selectIndexChoice.val() === 'SPATIAL' ||
$selectIndexChoice.val() === 'FULLTEXT') {
$selectIndexType.val('').prop('disabled', true);
} else {
$selectIndexType.prop('disabled', false);
}
};
/**
* Sets current index information into form parameters.
*
* @param {any[]} sourceArray Array containing index columns
* @param {string} indexChoice Choice of index
*
* @return {void}
*/
Indexes.setIndexFormParameters = function (sourceArray, indexChoice) {
if (indexChoice === 'index') {
$('input[name="indexes"]').val(JSON.stringify(sourceArray));
} else {
$('input[name="' + indexChoice + '_indexes"]').val(JSON.stringify(sourceArray));
}
};
/**
* Removes a column from an Index.
*
* @param {string} colIndex Index of column in form
*
* @return {void}
*/
Indexes.removeColumnFromIndex = function (colIndex) {
// Get previous index details.
var previousIndex = $('select[name="field_key[' + colIndex + ']"]')
.attr('data-index');
if (previousIndex.length) {
previousIndex = previousIndex.split(',');
var sourceArray = Indexes.getIndexArray(previousIndex[0]);
if (sourceArray === null) {
return;
}
// Remove column from index array.
var sourceLength = sourceArray[previousIndex[1]].columns.length;
for (var i = 0; i < sourceLength; i++) {
if (sourceArray[previousIndex[1]].columns[i].col_index === colIndex) {
sourceArray[previousIndex[1]].columns.splice(i, 1);
}
}
// Remove index completely if no columns left.
if (sourceArray[previousIndex[1]].columns.length === 0) {
sourceArray.splice(previousIndex[1], 1);
}
// Update current index details.
$('select[name="field_key[' + colIndex + ']"]').attr('data-index', '');
// Update form index parameters.
Indexes.setIndexFormParameters(sourceArray, previousIndex[0].toLowerCase());
}
};
/**
* Adds a column to an Index.
*
* @param {any[]} sourceArray Array holding corresponding indexes
* @param {string} arrayIndex Index of an INDEX in array
* @param {string} indexChoice Choice of Index
* @param {string} colIndex Index of column on form
*
* @return {void}
*/
Indexes.addColumnToIndex = function (sourceArray, arrayIndex, indexChoice, colIndex) {
if (colIndex >= 0) {
// Remove column from other indexes (if any).
Indexes.removeColumnFromIndex(colIndex);
}
var indexName = $('input[name="index[Key_name]"]').val();
var indexComment = $('input[name="index[Index_comment]"]').val();
var keyBlockSize = $('input[name="index[Key_block_size]"]').val();
var parser = $('input[name="index[Parser]"]').val();
var indexType = $('select[name="index[Index_type]"]').val();
var columns = [];
$('#index_columns').find('tbody').find('tr').each(function () {
// Get columns in particular order.
var colIndex = $(this).find('select[name="index[columns][names][]"]').val();
var size = $(this).find('input[name="index[columns][sub_parts][]"]').val();
columns.push({
'col_index': colIndex,
'size': size
});
});
// Update or create an index.
sourceArray[arrayIndex] = {
'Key_name': indexName,
'Index_comment': indexComment,
'Index_choice': indexChoice.toUpperCase(),
'Key_block_size': keyBlockSize,
'Parser': parser,
'Index_type': indexType,
'columns': columns
};
// Display index name (or column list)
var displayName = indexName;
if (displayName === '') {
var columnNames = [];
$.each(columns, function () {
columnNames.push($('input[name="field_name[' + this.col_index + ']"]').val());
});
displayName = '[' + columnNames.join(', ') + ']';
}
$.each(columns, function () {
var id = 'index_name_' + this.col_index + '_8';
var $name = $('#' + id);
if ($name.length === 0) {
$name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
$name.insertAfter($('select[name="field_key[' + this.col_index + ']"]'));
}
var $text = $('<small>').text(displayName);
$name.html($text);
});
if (colIndex >= 0) {
// Update index details on form.
$('select[name="field_key[' + colIndex + ']"]')
.attr('data-index', indexChoice + ',' + arrayIndex);
}
Indexes.setIndexFormParameters(sourceArray, indexChoice.toLowerCase());
};
/**
* Get choices list for a column to create a composite index with.
*
* @param {any[]} sourceArray Array hodling columns for particular index
* @param {string} colIndex Choice of index
*
* @return {JQuery} jQuery Object
*/
Indexes.getCompositeIndexList = function (sourceArray, colIndex) {
// Remove any previous list.
if ($('#composite_index_list').length) {
$('#composite_index_list').remove();
}
// Html list.
var $compositeIndexList = $(
'<ul id="composite_index_list">' +
'<div>' + Messages.strCompositeWith + '</div>' +
'</ul>'
);
// Add each column to list available for composite index.
var sourceLength = sourceArray.length;
var alreadyPresent = false;
for (var i = 0; i < sourceLength; i++) {
var subArrayLen = sourceArray[i].columns.length;
var columnNames = [];
for (var j = 0; j < subArrayLen; j++) {
columnNames.push(
$('input[name="field_name[' + sourceArray[i].columns[j].col_index + ']"]').val()
);
if (colIndex === sourceArray[i].columns[j].col_index) {
alreadyPresent = true;
}
}
$compositeIndexList.append(
'<li>' +
'<input type="radio" name="composite_with" ' +
(alreadyPresent ? 'checked="checked"' : '') +
' id="composite_index_' + i + '" value="' + i + '">' +
'<label for="composite_index_' + i + '">' + columnNames.join(', ') +
'</label>' +
'</li>'
);
}
return $compositeIndexList;
};
/**
* Shows 'Add Index' dialog.
*
* @param {any[]} sourceArray Array holding particular index
* @param {string} arrayIndex Index of an INDEX in array
* @param {any[]} targetColumns Columns for an INDEX
* @param {string} colIndex Index of column on form
* @param {object} index Index detail object
* @param {boolean} showDialog Whether to show index creation dialog or not
*
* @return {void}
*/
Indexes.showAddIndexDialog = function (sourceArray, arrayIndex, targetColumns, colIndex, index, showDialog) {
var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true;
// Prepare post-data.
var $table = $('input[name="table"]');
var table = $table.length > 0 ? $table.val() : '';
var postData = {
'server': CommonParams.get('server'),
'db': $('input[name="db"]').val(),
'table': table,
'ajax_request': 1,
'create_edit_table': 1,
'index': index
};
var columns = {};
for (var i = 0; i < targetColumns.length; i++) {
var columnName = $('input[name="field_name[' + targetColumns[i] + ']"]').val();
var columnType = $('select[name="field_type[' + targetColumns[i] + ']"]').val().toLowerCase();
columns[columnName] = [columnType, targetColumns[i]];
}
postData.columns = JSON.stringify(columns);
var buttonOptions = {};
buttonOptions[Messages.strGo] = function () {
var isMissingValue = false;
$('select[name="index[columns][names][]"]').each(function () {
if ($(this).val() === '') {
isMissingValue = true;
}
});
if (! isMissingValue) {
Indexes.addColumnToIndex(
sourceArray,
arrayIndex,
index.Index_choice,
colIndex
);
} else {
Functions.ajaxShowMessage(
'<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' +
' class="icon ic_s_error"> ' + Messages.strMissingColumn +
' </div>', false
);
return false;
}
$(this).remove();
};
buttonOptions[Messages.strCancel] = function () {
if (colIndex >= 0) {
// Handle state on 'Cancel'.
var $selectList = $('select[name="field_key[' + colIndex + ']"]');
if (! $selectList.attr('data-index').length) {
$selectList.find('option[value*="none"]').attr('selected', 'selected');
} else {
var previousIndex = $selectList.attr('data-index').split(',');
$selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]')
.attr('selected', 'selected');
}
}
$(this).dialog('close');
};
var $msgbox = Functions.ajaxShowMessage();
$.post('index.php?route=/table/indexes', postData, function (data) {
if (data.success === false) {
// in the case of an error, show the error message returned.
Functions.ajaxShowMessage(data.error, false);
} else {
Functions.ajaxRemoveMessage($msgbox);
var $div = $('<div></div>');
if (showDialogLocal) {
// Show dialog if the request was successful
if ($('#addIndex').length > 0) {
$('#addIndex').remove();
}
$div
.append(data.message)
.dialog({
title: Messages.strAddIndex,
width: 450,
minHeight: 250,
create: function () {
$(this).on('keypress', function (e) {
if (e.which === 13 || e.keyCode === 13 || window.event.keyCode === 13) {
e.preventDefault();
buttonOptions[Messages.strGo]();
$(this).remove();
}
});
},
open: function () {
Functions.checkIndexName('index_frm');
Functions.showHints($div);
$('#index_columns').find('td').each(function () {
$(this).css('width', $(this).width() + 'px');
});
$('#index_columns').find('tbody').sortable({
axis: 'y',
containment: $('#index_columns').find('tbody'),
tolerance: 'pointer'
});
},
modal: true,
buttons: buttonOptions,
close: function () {
$(this).remove();
}
});
} else {
$div
.append(data.message);
$div.css({ 'display' : 'none' });
$div.appendTo($('body'));
$div.attr({ 'id' : 'addIndex' });
var isMissingValue = false;
$('select[name="index[columns][names][]"]').each(function () {
if ($(this).val() === '') {
isMissingValue = true;
}
});
if (! isMissingValue) {
Indexes.addColumnToIndex(
sourceArray,
arrayIndex,
index.Index_choice,
colIndex
);
} else {
Functions.ajaxShowMessage(
'<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title="" alt=""' +
' class="icon ic_s_error"> ' + Messages.strMissingColumn +
' </div>', false
);
return false;
}
}
}
});
};
/**
* Creates a advanced index type selection dialog.
*
* @param {any[]} sourceArray Array holding a particular type of indexes
* @param {string} indexChoice Choice of index
* @param {string} colIndex Index of new column on form
*
* @return {void}
*/
Indexes.indexTypeSelectionDialog = function (sourceArray, indexChoice, colIndex) {
var $singleColumnRadio = $('<input type="radio" id="single_column" name="index_choice"' +
' checked="checked">' +
'<label for="single_column">' + Messages.strCreateSingleColumnIndex + '</label>');
var $compositeIndexRadio = $('<input type="radio" id="composite_index"' +
' name="index_choice">' +
'<label for="composite_index">' + Messages.strCreateCompositeIndex + '</label>');
var $dialogContent = $('<fieldset class="pma-fieldset" id="advance_index_creator"></fieldset>');
$dialogContent.append('<legend>' + indexChoice.toUpperCase() + '</legend>');
// For UNIQUE/INDEX type, show choice for single-column and composite index.
$dialogContent.append($singleColumnRadio);
$dialogContent.append($compositeIndexRadio);
var buttonOptions = {};
// 'OK' operation.
buttonOptions[Messages.strGo] = function () {
if ($('#single_column').is(':checked')) {
var index = {
'Key_name': (indexChoice === 'primary' ? 'PRIMARY' : ''),
'Index_choice': indexChoice.toUpperCase()
};
Indexes.showAddIndexDialog(sourceArray, (sourceArray.length), [colIndex], colIndex, index);
}
if ($('#composite_index').is(':checked')) {
if ($('input[name="composite_with"]').length !== 0 && $('input[name="composite_with"]:checked').length === 0
) {
Functions.ajaxShowMessage(
'<div class="alert alert-danger" role="alert"><img src="themes/dot.gif" title=""' +
' alt="" class="icon ic_s_error"> ' +
Messages.strFormEmpty +
' </div>',
false
);
return false;
}
var arrayIndex = $('input[name="composite_with"]:checked').val();
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
targetColumns.push(colIndex);
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex,
sourceArray[arrayIndex]);
}
$(this).remove();
};
buttonOptions[Messages.strCancel] = function () {
// Handle state on 'Cancel'.
var $selectList = $('select[name="field_key[' + colIndex + ']"]');
if (! $selectList.attr('data-index').length) {
$selectList.find('option[value*="none"]').attr('selected', 'selected');
} else {
var previousIndex = $selectList.attr('data-index').split(',');
$selectList.find('option[value*="' + previousIndex[0].toLowerCase() + '"]')
.attr('selected', 'selected');
}
$(this).remove();
};
$('<div></div>').append($dialogContent).dialog({
minWidth: 525,
minHeight: 200,
modal: true,
title: Messages.strAddIndex,
resizable: false,
buttons: buttonOptions,
open: function () {
$('#composite_index').on('change', function () {
if ($(this).is(':checked')) {
$dialogContent.append(Indexes.getCompositeIndexList(sourceArray, colIndex));
}
});
$('#single_column').on('change', function () {
if ($(this).is(':checked')) {
if ($('#composite_index_list').length) {
$('#composite_index_list').remove();
}
}
});
},
close: function () {
$('#composite_index').off('change');
$('#single_column').off('change');
$(this).remove();
}
});
};
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('indexes.js', function () {
$(document).off('click', '#save_index_frm');
$(document).off('click', '#preview_index_frm');
$(document).off('change', '#select_index_choice');
$(document).off('click', 'a.drop_primary_key_index_anchor.ajax');
$(document).off('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax');
$(document).off('click', '#table_index tbody tr td.rename_index.ajax');
$(document).off('click', '#index_frm input[type=submit]');
$('body').off('change', 'select[name*="field_key"]');
$(document).off('click', '.show_index_dialog');
});
/**
* @description <p>Ajax scripts for table index page</p>
*
* Actions ajaxified here:
* <ul>
* <li>Showing/hiding inputs depending on the index type chosen</li>
* <li>create/edit/drop indexes</li>
* </ul>
*/
AJAX.registerOnload('indexes.js', function () {
// Re-initialize variables.
primaryIndexes = [];
uniqueIndexes = [];
indexes = [];
fulltextIndexes = [];
spatialIndexes = [];
// for table creation form
var $engineSelector = $('.create_table_form select[name=tbl_storage_engine]');
if ($engineSelector.length) {
Functions.hideShowConnection($engineSelector);
}
var $form = $('#index_frm');
if ($form.length > 0) {
Functions.showIndexEditDialog($form);
}
$(document).on('click', '#save_index_frm', function (event) {
event.preventDefault();
var $form = $('#index_frm');
var argsep = CommonParams.get('arg_separator');
var submitData = $form.serialize() + argsep + 'do_save_data=1' + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
Functions.ajaxShowMessage(Messages.strProcessingRequest);
AJAX.source = $form;
$.post($form.attr('action'), submitData, AJAX.responseHandler);
});
$(document).on('click', '#preview_index_frm', function (event) {
event.preventDefault();
Functions.previewSql($('#index_frm'));
});
$(document).on('change', '#select_index_choice', function (event) {
event.preventDefault();
Indexes.checkIndexType();
Functions.checkIndexName('index_frm');
});
/**
* Ajax Event handler for 'Drop Index'
*/
$(document).on('click', 'a.drop_primary_key_index_anchor.ajax', function (event) {
event.preventDefault();
var $anchor = $(this);
/**
* @var $currRow Object containing reference to the current field's row
*/
var $currRow = $anchor.parents('tr');
/** @var {number} rows Number of columns in the key */
var rows = $anchor.parents('td').attr('rowspan') || 1;
/** @var {number} $rowsToHide Rows that should be hidden */
var $rowsToHide = $currRow;
for (var i = 1, $lastRow = $currRow.next(); i < rows; i++, $lastRow = $lastRow.next()) {
$rowsToHide = $rowsToHide.add($lastRow);
}
var question = $currRow.children('td')
.children('.drop_primary_key_index_msg')
.val();
Functions.confirmPreviewSql(question, $anchor.attr('href'), function (url) {
var $msg = Functions.ajaxShowMessage(Messages.strDroppingPrimaryKeyIndex, false);
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
$.post(url, params, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
Functions.ajaxRemoveMessage($msg);
var $tableRef = $rowsToHide.closest('table');
if ($rowsToHide.length === $tableRef.find('tbody > tr').length) {
// We are about to remove all rows from the table
$tableRef.hide('medium', function () {
$('div.no_indexes_defined').show('medium');
$rowsToHide.remove();
});
$tableRef.siblings('.alert-primary').hide('medium');
} else {
// We are removing some of the rows only
$rowsToHide.hide('medium', function () {
$(this).remove();
});
}
if ($('.result_query').length) {
$('.result_query').remove();
}
if (data.sql_query) {
$('<div class="result_query"></div>')
.html(data.sql_query)
.prependTo('#structure_content');
Functions.highlightSql($('#page_content'));
}
Navigation.reload();
CommonActions.refreshMain('index.php?route=/table/structure');
} else {
Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
}
}); // end $.post()
});
}); // end Drop Primary Key/Index
/**
* Ajax event handler for index edit
**/
$(document).on('click', '#table_index tbody tr td.edit_index.ajax, #index_div .add_index.ajax', function (event) {
event.preventDefault();
var url;
var title;
if ($(this).find('a').length === 0) {
// Add index
var valid = Functions.checkFormElementInRange(
$(this).closest('form')[0],
'added_fields',
'Column count has to be larger than zero.'
);
if (! valid) {
return;
}
url = $(this).closest('form').serialize();
title = Messages.strAddIndex;
} else {
// Edit index
url = $(this).find('a').getPostData();
title = Messages.strEditIndex;
}
url += CommonParams.get('arg_separator') + 'ajax_request=true';
Functions.indexEditorDialog(url, title, function (data) {
CommonParams.set('db', data.params.db);
CommonParams.set('table', data.params.table);
CommonActions.refreshMain('index.php?route=/table/structure');
});
});
/**
* Ajax event handler for index rename
**/
$(document).on('click', '#table_index tbody tr td.rename_index.ajax', function (event) {
event.preventDefault();
var url = $(this).find('a').getPostData();
var title = Messages.strRenameIndex;
url += CommonParams.get('arg_separator') + 'ajax_request=true';
Functions.indexRenameDialog(url, title, function (data) {
CommonParams.set('db', data.params.db);
CommonParams.set('table', data.params.table);
CommonActions.refreshMain('index.php?route=/table/structure');
});
});
/**
* Ajax event handler for advanced index creation during table creation
* and column addition.
*/
$('body').on('change', 'select[name*="field_key"]', function (e, showDialog) {
var showDialogLocal = typeof showDialog !== 'undefined' ? showDialog : true;
// Index of column on Table edit and create page.
var colIndex = /\d+/.exec($(this).attr('name'));
colIndex = colIndex[0];
// Choice of selected index.
var indexChoice = /[a-z]+/.exec($(this).val());
indexChoice = indexChoice[0];
// Array containing corresponding indexes.
var sourceArray = null;
if (indexChoice === 'none') {
Indexes.removeColumnFromIndex(colIndex);
var id = 'index_name_' + '0' + '_8';
var $name = $('#' + id);
if ($name.length === 0) {
$name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
$name.insertAfter($('select[name="field_key[' + '0' + ']"]'));
}
$name.html('');
return false;
}
// Select a source array.
sourceArray = Indexes.getIndexArray(indexChoice);
if (sourceArray === null) {
return;
}
if (sourceArray.length === 0) {
var index = {
'Key_name': (indexChoice === 'primary' ? 'PRIMARY' : ''),
'Index_choice': indexChoice.toUpperCase()
};
Indexes.showAddIndexDialog(sourceArray, 0, [colIndex], colIndex, index, showDialogLocal);
} else {
if (indexChoice === 'primary') {
var arrayIndex = 0;
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
targetColumns.push(colIndex);
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, colIndex,
sourceArray[arrayIndex], showDialogLocal);
} else {
// If there are multiple columns selected for an index, show advanced dialog.
Indexes.indexTypeSelectionDialog(sourceArray, indexChoice, colIndex);
}
}
});
$(document).on('click', '.show_index_dialog', function (e) {
e.preventDefault();
// Get index details.
var previousIndex = $(this).prev('select')
.attr('data-index')
.split(',');
var indexChoice = previousIndex[0];
var arrayIndex = previousIndex[1];
var sourceArray = Indexes.getIndexArray(indexChoice);
if (sourceArray !== null) {
var sourceLength = sourceArray[arrayIndex].columns.length;
var targetColumns = [];
for (var i = 0; i < sourceLength; i++) {
targetColumns.push(sourceArray[arrayIndex].columns[i].col_index);
}
Indexes.showAddIndexDialog(sourceArray, arrayIndex, targetColumns, -1, sourceArray[arrayIndex]);
}
});
$('#index_frm').on('submit', function () {
if (typeof(this.elements['index[Key_name]'].disabled) !== 'undefined') {
this.elements['index[Key_name]'].disabled = false;
}
});
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,792 @@
/**
* @fileoverview events handling from normalization page
* @name normalization
*
* @requires jQuery
*/
// eslint-disable-next-line no-unused-vars
/* global centralColumnList:writable */ // js/functions.js
/**
* AJAX scripts for normalization
*
*/
var normalizeto = '1nf';
var primaryKey;
var dataParsed = null;
function appendHtmlColumnsList () {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'getColumns': true
},
function (data) {
if (data.success === true) {
$('select[name=makeAtomic]').html(data.message);
}
}
);
}
function goTo3NFStep1 (newTables) {
var tables = newTables;
if (Object.keys(tables).length === 1) {
tables = [CommonParams.get('table')];
}
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'server': CommonParams.get('server'),
'tables': tables,
'step': '3.1'
}, function (data) {
$('#page_content').find('h3').html(Messages.str3NFNormalization);
$('#mainContent').find('legend').html(data.legendText);
$('#mainContent').find('h4').html(data.headText);
$('#mainContent').find('p').html(data.subText);
$('#mainContent').find('#extra').html(data.extra);
$('#extra').find('form').each(function () {
var formId = $(this).attr('id');
var colName = $(this).data('colname');
$('#' + formId + ' input[value=\'' + colName + '\']').next().remove();
$('#' + formId + ' input[value=\'' + colName + '\']').remove();
});
$('#mainContent').find('#newCols').html('');
$('.tblFooters').html('');
if (data.subText !== '') {
$('<input>')
.attr({
type: 'button',
value: Messages.strDone,
class: 'btn btn-primary'
})
.on('click', function () {
processDependencies('', true);
})
.appendTo('.tblFooters');
}
}
);
}
function goTo2NFStep1 () {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'step': '2.1'
}, function (data) {
$('#page_content h3').html(Messages.str2NFNormalization);
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html(data.subText);
$('#mainContent #extra').html(data.extra);
$('#mainContent #newCols').html('');
if (data.subText !== '') {
$('<input>')
.attr({
type: 'submit',
value: Messages.strDone,
class: 'btn btn-primary'
})
.on('click', function () {
processDependencies(data.primary_key);
})
.appendTo('.tblFooters');
} else {
if (normalizeto === '3nf') {
$('#mainContent #newCols').html(Messages.strToNextStep);
setTimeout(function () {
goTo3NFStep1([CommonParams.get('table')]);
}, 3000);
}
}
});
}
function goToFinish1NF () {
if (normalizeto !== '1nf') {
goTo2NFStep1();
return true;
}
$('#mainContent legend').html(Messages.strEndStep);
$('#mainContent h4').html(
'<h3>' + Functions.sprintf(Messages.strFinishMsg, Functions.escapeHtml(CommonParams.get('table'))) + '</h3>'
);
$('#mainContent p').html('');
$('#mainContent #extra').html('');
$('#mainContent #newCols').html('');
$('.tblFooters').html('');
}
// eslint-disable-next-line no-unused-vars
function goToStep4 () {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'step4': true
}, function (data) {
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html(data.subText);
$('#mainContent #extra').html(data.extra);
$('#mainContent #newCols').html('');
$('.tblFooters').html('');
for (var pk in primaryKey) {
$('#extra input[value=\'' + Functions.escapeJsString(primaryKey[pk]) + '\']').attr('disabled','disabled');
}
}
);
}
function goToStep3 () {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'step3': true
}, function (data) {
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html(data.subText);
$('#mainContent #extra').html(data.extra);
$('#mainContent #newCols').html('');
$('.tblFooters').html('');
primaryKey = JSON.parse(data.primary_key);
for (var pk in primaryKey) {
$('#extra input[value=\'' + Functions.escapeJsString(primaryKey[pk]) + '\']').attr('disabled','disabled');
}
}
);
}
function goToStep2 (extra) {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'step2': true
}, function (data) {
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html(data.subText);
$('#mainContent #extra,#mainContent #newCols').html('');
$('.tblFooters').html('');
if (data.hasPrimaryKey === '1') {
if (extra === 'goToStep3') {
$('#mainContent h4').html(Messages.strPrimaryKeyAdded);
$('#mainContent p').html(Messages.strToNextStep);
}
if (extra === 'goToFinish1NF') {
goToFinish1NF();
} else {
setTimeout(function () {
goToStep3();
}, 3000);
}
} else {
// form to select columns to make primary
$('#mainContent #extra').html(data.extra);
}
}
);
}
function goTo2NFFinish (pd) {
var tables = {};
for (var dependson in pd) {
tables[dependson] = $('#extra input[name="' + dependson + '"]').val();
}
var datastring = {
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'pd': JSON.stringify(pd),
'newTablesName':JSON.stringify(tables),
'createNewTables2NF':1 };
$.ajax({
type: 'POST',
url: 'index.php?route=/normalization',
data: datastring,
async:false,
success: function (data) {
if (data.success === true) {
if (data.queryError === false) {
if (normalizeto === '3nf') {
$('#pma_navigation_reload').trigger('click');
goTo3NFStep1(tables);
return true;
}
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html('');
$('#mainContent #extra').html('');
$('.tblFooters').html('');
} else {
Functions.ajaxShowMessage(data.extra, false);
}
$('#pma_navigation_reload').trigger('click');
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
}
function goTo3NFFinish (newTables) {
for (var table in newTables) {
for (var newtbl in newTables[table]) {
var updatedname = $('#extra input[name="' + newtbl + '"]').val();
newTables[table][updatedname] = newTables[table][newtbl];
if (updatedname !== newtbl) {
delete newTables[table][newtbl];
}
}
}
var datastring = {
'ajax_request': true,
'db': CommonParams.get('db'),
'server': CommonParams.get('server'),
'newTables':JSON.stringify(newTables),
'createNewTables3NF':1 };
$.ajax({
type: 'POST',
url: 'index.php?route=/normalization',
data: datastring,
async:false,
success: function (data) {
if (data.success === true) {
if (data.queryError === false) {
$('#mainContent legend').html(data.legendText);
$('#mainContent h4').html(data.headText);
$('#mainContent p').html('');
$('#mainContent #extra').html('');
$('.tblFooters').html('');
} else {
Functions.ajaxShowMessage(data.extra, false);
}
$('#pma_navigation_reload').trigger('click');
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
}
var backup = '';
function goTo2NFStep2 (pd, primaryKey) {
$('#newCols').html('');
$('#mainContent legend').html(Messages.strStep + ' 2.2 ' + Messages.strConfirmPd);
$('#mainContent h4').html(Messages.strSelectedPd);
$('#mainContent p').html(Messages.strPdHintNote);
var extra = '<div class="dependencies_box">';
var pdFound = false;
for (var dependson in pd) {
if (dependson !== primaryKey) {
pdFound = true;
extra += '<p class="d-block m-1">' + Functions.escapeHtml(dependson) + ' -> ' + Functions.escapeHtml(pd[dependson].toString()) + '</p>';
}
}
if (!pdFound) {
extra += '<p class="d-block m-1">' + Messages.strNoPdSelected + '</p>';
extra += '</div>';
} else {
extra += '</div>';
var datastring = {
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'pd': JSON.stringify(pd),
'getNewTables2NF':1 };
$.ajax({
type: 'POST',
url: 'index.php?route=/normalization',
data: datastring,
async:false,
success: function (data) {
if (data.success === true) {
extra += data.message;
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
}
$('#mainContent #extra').html(extra);
$('.tblFooters').html('<input type="button" class="btn btn-primary" value="' + Messages.strBack + '" id="backEditPd"><input type="button" class="btn btn-primary" id="goTo2NFFinish" value="' + Messages.strGo + '">');
$('#goTo2NFFinish').on('click', function () {
goTo2NFFinish(pd);
});
}
function goTo3NFStep2 (pd, tablesTds) {
$('#newCols').html('');
$('#mainContent legend').html(Messages.strStep + ' 3.2 ' + Messages.strConfirmTd);
$('#mainContent h4').html(Messages.strSelectedTd);
$('#mainContent p').html(Messages.strPdHintNote);
var extra = '<div class="dependencies_box">';
var pdFound = false;
for (var table in tablesTds) {
for (var i in tablesTds[table]) {
var dependson = tablesTds[table][i];
if (dependson !== '' && dependson !== table) {
pdFound = true;
extra += '<p class="d-block m-1">' + Functions.escapeHtml(dependson) + ' -> ' + Functions.escapeHtml(pd[dependson].toString()) + '</p>';
}
}
}
if (!pdFound) {
extra += '<p class="d-block m-1">' + Messages.strNoTdSelected + '</p>';
extra += '</div>';
} else {
extra += '</div>';
var datastring = {
'ajax_request': true,
'db': CommonParams.get('db'),
'tables': JSON.stringify(tablesTds),
'server': CommonParams.get('server'),
'pd': JSON.stringify(pd),
'getNewTables3NF':1 };
$.ajax({
type: 'POST',
url: 'index.php?route=/normalization',
data: datastring,
async:false,
success: function (data) {
dataParsed = data;
if (data.success === true) {
extra += dataParsed.html;
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
}
$('#mainContent #extra').html(extra);
$('.tblFooters').html('<input type="button" class="btn btn-primary" value="' + Messages.strBack + '" id="backEditPd"><input type="button" class="btn btn-primary" id="goTo3NFFinish" value="' + Messages.strGo + '">');
$('#goTo3NFFinish').on('click', function () {
if (!pdFound) {
goTo3NFFinish([]);
} else {
goTo3NFFinish(dataParsed.newTables);
}
});
}
function processDependencies (primaryKey, isTransitive) {
var pk = primaryKey;
var pd = {};
var tablesTds = {};
var dependsOn;
pd[pk] = [];
$('#extra form').each(function () {
var tblname;
if (isTransitive === true) {
tblname = $(this).data('tablename');
pk = tblname;
if (!(tblname in tablesTds)) {
tablesTds[tblname] = [];
}
tablesTds[tblname].push(pk);
}
var formId = $(this).attr('id');
$('#' + formId + ' input[type=checkbox]:not(:checked)').prop('checked', false);
dependsOn = '';
$('#' + formId + ' input[type=checkbox]:checked').each(function () {
dependsOn += $(this).val() + ', ';
$(this).attr('checked','checked');
});
if (dependsOn === '') {
dependsOn = pk;
} else {
dependsOn = dependsOn.slice(0, -2);
}
if (! (dependsOn in pd)) {
pd[dependsOn] = [];
}
pd[dependsOn].push($(this).data('colname'));
if (isTransitive === true) {
if (!(tblname in tablesTds)) {
tablesTds[tblname] = [];
}
if ($.inArray(dependsOn, tablesTds[tblname]) === -1) {
tablesTds[tblname].push(dependsOn);
}
}
});
backup = $('#mainContent').html();
if (isTransitive === true) {
goTo3NFStep2(pd, tablesTds);
} else {
goTo2NFStep2(pd, pk);
}
return false;
}
function moveRepeatingGroup (repeatingCols) {
var newTable = $('input[name=repeatGroupTable]').val();
var newColumn = $('input[name=repeatGroupColumn]').val();
if (!newTable) {
$('input[name=repeatGroupTable]').trigger('focus');
return false;
}
if (!newColumn) {
$('input[name=repeatGroupColumn]').trigger('focus');
return false;
}
var datastring = {
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'repeatingColumns': repeatingCols,
'newTable':newTable,
'newColumn':newColumn,
'primary_columns':primaryKey.toString()
};
$.ajax({
type: 'POST',
url: 'index.php?route=/normalization',
data: datastring,
async:false,
success: function (data) {
if (data.success === true) {
if (data.queryError === false) {
goToStep3();
}
Functions.ajaxShowMessage(data.message, false);
$('#pma_navigation_reload').trigger('click');
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
}
AJAX.registerTeardown('normalization.js', function () {
$('#extra').off('click', '#selectNonAtomicCol');
$('#splitGo').off('click');
$('.tblFooters').off('click', '#saveSplit');
$('#extra').off('click', '#addNewPrimary');
$('.tblFooters').off('click', '#saveNewPrimary');
$('#extra').off('click', '#removeRedundant');
$('#mainContent p').off('click', '#createPrimaryKey');
$('#mainContent').off('click', '#backEditPd');
$('#mainContent').off('click', '#showPossiblePd');
$('#mainContent').off('click', '.pickPd');
});
AJAX.registerOnload('normalization.js', function () {
var selectedCol;
normalizeto = $('#mainContent').data('normalizeto');
$('#extra').on('click', '#selectNonAtomicCol', function () {
if ($(this).val() === 'no_such_col') {
goToStep2();
} else {
selectedCol = $(this).val();
}
});
$('#splitGo').on('click', function () {
if (!selectedCol || selectedCol === '') {
return false;
}
var numField = $('#numField').val();
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'splitColumn': true,
'numFields': numField
},
function (data) {
if (data.success === true) {
$('#newCols').html(data.message);
$('.default_value').hide();
$('.enum_notice').hide();
$('<input>')
.attr({
type: 'submit',
id: 'saveSplit',
value: Messages.strSave,
class: 'btn btn-primary'
})
.appendTo('.tblFooters');
$('<input>')
.attr({
type: 'submit',
id: 'cancelSplit',
value: Messages.strCancel,
class: 'btn btn-secondary'
})
.on('click', function () {
$('#newCols').html('');
$(this).parent().html('');
})
.appendTo('.tblFooters');
}
}
);
return false;
});
$('.tblFooters').on('click','#saveSplit', function () {
centralColumnList = [];
if ($('#newCols #field_0_1').val() === '') {
$('#newCols #field_0_1').trigger('focus');
return false;
}
var argsep = CommonParams.get('arg_separator');
var datastring = $('#newCols :input').serialize();
datastring += argsep + 'ajax_request=1' + argsep + 'do_save_data=1' + argsep + 'field_where=last';
$.post('index.php?route=/table/add-field', datastring, function (data) {
if (data.success) {
$.post(
'index.php?route=/sql',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'dropped_column': selectedCol,
'purge' : 1,
'sql_query': 'ALTER TABLE `' + CommonParams.get('table') + '` DROP `' + selectedCol + '`;',
'is_js_confirmed': 1
},
function (data) {
if (data.success === true) {
appendHtmlColumnsList();
$('#newCols').html('');
$('.tblFooters').html('');
} else {
Functions.ajaxShowMessage(data.error, false);
}
selectedCol = '';
}
);
} else {
Functions.ajaxShowMessage(data.error, false);
}
});
});
$('#extra').on('click', '#addNewPrimary', function () {
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'addNewPrimary': true
},
function (data) {
if (data.success === true) {
$('#newCols').html(data.message);
$('.default_value').hide();
$('.enum_notice').hide();
$('<input>')
.attr({
type: 'submit',
id: 'saveNewPrimary',
value: Messages.strSave,
class: 'btn btn-primary'
})
.appendTo('.tblFooters');
$('<input>')
.attr({
type: 'submit',
id: 'cancelSplit',
value: Messages.strCancel,
class: 'btn btn-secondary'
})
.on('click', function () {
$('#newCols').html('');
$(this).parent().html('');
})
.appendTo('.tblFooters');
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
);
return false;
});
$('.tblFooters').on('click', '#saveNewPrimary', function () {
var datastring = $('#newCols :input').serialize();
var argsep = CommonParams.get('arg_separator');
datastring += argsep + 'field_key[0]=primary_0' + argsep + 'ajax_request=1' + argsep + 'do_save_data=1' + argsep + 'field_where=last';
$.post('index.php?route=/table/add-field', datastring, function (data) {
if (data.success === true) {
$('#mainContent h4').html(Messages.strPrimaryKeyAdded);
$('#mainContent p').html(Messages.strToNextStep);
$('#mainContent #extra').html('');
$('#mainContent #newCols').html('');
$('.tblFooters').html('');
setTimeout(function () {
goToStep3();
}, 2000);
} else {
Functions.ajaxShowMessage(data.error, false);
}
});
});
$('#extra').on('click', '#removeRedundant', function () {
var dropQuery = 'ALTER TABLE `' + CommonParams.get('table') + '` ';
$('#extra input[type=checkbox]:checked').each(function () {
dropQuery += 'DROP `' + $(this).val() + '`, ';
});
dropQuery = dropQuery.slice(0, -2);
$.post(
'index.php?route=/sql',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'sql_query': dropQuery,
'is_js_confirmed': 1
},
function (data) {
if (data.success === true) {
goToStep2('goToFinish1NF');
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
);
});
$('#extra').on('click', '#moveRepeatingGroup', function () {
var repeatingCols = '';
$('#extra input[type=checkbox]:checked').each(function () {
repeatingCols += $(this).val() + ', ';
});
if (repeatingCols !== '') {
var newColName = $('#extra input[type=checkbox]:checked').first().val();
repeatingCols = repeatingCols.slice(0, -2);
var confirmStr = Functions.sprintf(Messages.strMoveRepeatingGroup, Functions.escapeHtml(repeatingCols), Functions.escapeHtml(CommonParams.get('table')));
confirmStr += '<input type="text" name="repeatGroupTable" placeholder="' + Messages.strNewTablePlaceholder + '">' +
'( ' + Functions.escapeHtml(primaryKey.toString()) + ', <input type="text" name="repeatGroupColumn" placeholder="' + Messages.strNewColumnPlaceholder + '" value="' + Functions.escapeHtml(newColName) + '">)' +
'</ol>';
$('#newCols').html(confirmStr);
$('<input>')
.attr({
type: 'submit',
value: Messages.strCancel,
class: 'btn btn-secondary'
})
.on('click', function () {
$('#newCols').html('');
$('#extra input[type=checkbox]').prop('checked', false);
})
.appendTo('.tblFooters');
$('<input>')
.attr({
type: 'submit',
value: Messages.strGo,
class: 'btn btn-primary'
})
.on('click', function () {
moveRepeatingGroup(repeatingCols);
})
.appendTo('.tblFooters');
}
});
$('#mainContent p').on('click', '#createPrimaryKey', function (event) {
event.preventDefault();
var url = {
'create_index': 1,
'server': CommonParams.get('server'),
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'added_fields': 1,
'add_fields':1,
'index': { 'Key_name':'PRIMARY' },
'ajax_request': true
};
var title = Messages.strAddPrimaryKey;
Functions.indexEditorDialog(url, title, function () {
// on success
$('.sqlqueryresults').remove();
$('.result_query').remove();
$('.tblFooters').html('');
goToStep2('goToStep3');
});
return false;
});
$('#mainContent').on('click', '#backEditPd', function () {
$('#mainContent').html(backup);
});
$('#mainContent').on('click', '#showPossiblePd', function () {
if ($(this).hasClass('hideList')) {
$(this).html('+ ' + Messages.strShowPossiblePd);
$(this).removeClass('hideList');
$('#newCols').slideToggle('slow');
return false;
}
if ($('#newCols').html() !== '') {
$('#showPossiblePd').html('- ' + Messages.strHidePd);
$('#showPossiblePd').addClass('hideList');
$('#newCols').slideToggle('slow');
return false;
}
$('#newCols').insertAfter('#mainContent h4');
$('#newCols').html('<div class="text-center">' + Messages.strLoading + '<br>' + Messages.strWaitForPd + '</div>');
$.post(
'index.php?route=/normalization',
{
'ajax_request': true,
'db': CommonParams.get('db'),
'table': CommonParams.get('table'),
'server': CommonParams.get('server'),
'findPdl': true
}, function (data) {
$('#showPossiblePd').html('- ' + Messages.strHidePd);
$('#showPossiblePd').addClass('hideList');
$('#newCols').html(data.message);
});
});
$('#mainContent').on('click', '.pickPd', function () {
var strColsLeft = $(this).next('.determinants').html();
var colsLeft = strColsLeft.split(',');
var strColsRight = $(this).next().next().html();
var colsRight = strColsRight.split(',');
for (var i in colsRight) {
$('form[data-colname="' + colsRight[i].trim() + '"] input[type="checkbox"]').prop('checked', false);
for (var j in colsLeft) {
$('form[data-colname="' + colsRight[i].trim() + '"] input[value="' + colsLeft[j].trim() + '"]').prop('checked', true);
}
}
});
});

@ -0,0 +1,106 @@
/**
* @fileoverview Javascript functions used in server replication page
* @name Server Replication
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*/
var randomServerId = Math.floor(Math.random() * 10000000);
var confPrefix = 'server-id=' + randomServerId + '\nlog_bin=mysql-bin\nlog_error=mysql-bin.err\n';
function updateConfig () {
var confIgnore = 'binlog_ignore_db=';
var confDo = 'binlog_do_db=';
var databaseList = '';
if ($('#db_select option:selected').length === 0) {
$('#rep').text(confPrefix);
} else if ($('#db_type option:selected').val() === 'all') {
$('#db_select option:selected').each(function () {
databaseList += confIgnore + $(this).val() + '\n';
});
$('#rep').text(confPrefix + databaseList);
} else {
$('#db_select option:selected').each(function () {
databaseList += confDo + $(this).val() + '\n';
});
$('#rep').text(confPrefix + databaseList);
}
}
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('replication.js', function () {
$('#db_type').off('change');
$('#db_select').off('change');
$('#primary_status_href').off('click');
$('#primary_replicas_href').off('click');
$('#replica_status_href').off('click');
$('#replica_control_href').off('click');
$('#replica_errormanagement_href').off('click');
$('#replica_synchronization_href').off('click');
$('#db_reset_href').off('click');
$('#db_select_href').off('click');
$('#reset_replica').off('click');
});
AJAX.registerOnload('replication.js', function () {
$('#rep').text(confPrefix);
$('#db_type').on('change', updateConfig);
$('#db_select').on('change', updateConfig);
$('#primary_status_href').on('click', function () {
$('#replication_primary_section').toggle();
});
$('#primary_replicas_href').on('click', function () {
$('#replication_replicas_section').toggle();
});
$('#replica_status_href').on('click', function () {
$('#replication_replica_section').toggle();
});
$('#replica_control_href').on('click', function () {
$('#replica_control_gui').toggle();
});
$('#replica_errormanagement_href').on('click', function () {
$('#replica_errormanagement_gui').toggle();
});
$('#replica_synchronization_href').on('click', function () {
$('#replica_synchronization_gui').toggle();
});
$('#db_reset_href').on('click', function () {
$('#db_select option:selected').prop('selected', false);
$('#db_select').trigger('change');
});
$('#db_select_href').on('click', function () {
$('#db_select option').prop('selected', true);
$('#db_select').trigger('change');
});
$('#reset_replica').on('click', function (e) {
e.preventDefault();
var $anchor = $(this);
var question = Messages.strResetReplicaWarning;
$anchor.confirm(question, $anchor.attr('href'), function (url) {
Functions.ajaxShowMessage();
AJAX.source = $anchor;
var params = Functions.getJsConfirmCommonParam({
'ajax_page_request': true,
'ajax_request': true
}, $anchor.getPostData());
$.post(url, params, AJAX.responseHandler);
});
});
$('#button_generate_password').on('click', function () {
Functions.suggestPassword(this.form);
});
$('#nopass_1').on('click', function () {
this.form.pma_pw.value = '';
this.form.pma_pw2.value = '';
this.checked = true;
});
$('#nopass_0').on('click', function () {
document.getElementById('text_pma_change_pw').focus();
});
});

@ -0,0 +1,15 @@
/**
* Functions used in server plugins pages
*/
AJAX.registerOnload('server/plugins.js', function () {
// Make columns sortable, but only for tables with more than 1 data row
var $tables = $('#plugins_plugins table:has(tbody tr + tr)');
$tables.tablesorter({
sortList: [[0, 0]],
headers: {
1: { sorter: false }
}
});
$tables.find('thead th')
.append('<div class="sorticon"></div>');
});

@ -0,0 +1,45 @@
/**
* @fileoverview Javascript functions used in server status query page
* @name Server Status Query
*
* @requires jQuery
* @requires jQueryUI
* @requires js/functions.js
*/
/* global initTableSorter */ // js/server/status/sorter.js
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('server/status/queries.js', function () {
if (document.getElementById('serverstatusquerieschart') !== null) {
var queryPieChart = $('#serverstatusquerieschart').data('queryPieChart');
if (queryPieChart) {
queryPieChart.destroy();
}
}
});
AJAX.registerOnload('server/status/queries.js', function () {
// Build query statistics chart
var cdata = [];
try {
if (document.getElementById('serverstatusquerieschart') !== null) {
$.each($('#serverstatusquerieschart').data('chart'), function (key, value) {
cdata.push([key, parseInt(value, 10)]);
});
$('#serverstatusquerieschart').data(
'queryPieChart',
Functions.createProfilingChart(
'serverstatusquerieschart',
cdata
)
);
}
} catch (exception) {
// Could not load chart, no big deal...
}
initTableSorter('statustabs_queries');
});

@ -0,0 +1,241 @@
/**
* Functions used in Setup configuration forms
*/
/* global displayErrors, getAllValues, getIdPrefix, validators */ // js/config.js
// show this window in top frame
if (top !== self) {
window.top.location.href = location;
}
// ------------------------------------------------------------------
// Messages
//
$(function () {
if (window.location.protocol === 'https:') {
$('#no_https').remove();
} else {
$('#no_https a').on('click', function () {
var oldLocation = window.location;
window.location.href = 'https:' + oldLocation.href.substring(oldLocation.protocol.length);
return false;
});
}
var hiddenMessages = $('.hiddenmessage');
if (hiddenMessages.length > 0) {
hiddenMessages.hide();
var link = $('#show_hidden_messages');
link.on('click', function (e) {
e.preventDefault();
hiddenMessages.show();
$(this).remove();
});
link.html(link.html().replace('#MSG_COUNT', hiddenMessages.length));
link.show();
}
});
// set document width
$(function () {
var width = 0;
$('ul.tabs li').each(function () {
width += $(this).width() + 10;
});
var contentWidth = width;
width += 250;
$('body').css('min-width', width);
$('.tabs_contents').css('min-width', contentWidth);
});
//
// END: Messages
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// Form validation and field operations
//
/**
* Calls server-side validation procedures
*
* @param {Element} parent input field in <fieldset> or <fieldset>
* @param {String} id validator id
* @param {object} values values hash {element1_id: value, ...}
*
* @return {bool|void}
*/
function ajaxValidate (parent, id, values) {
var $parent = $(parent);
// ensure that parent is a fieldset
if ($parent.attr('tagName') !== 'FIELDSET') {
$parent = $parent.closest('fieldset');
if ($parent.length === 0) {
return false;
}
}
if ($parent.data('ajax') !== null) {
$parent.data('ajax').abort();
}
$parent.data('ajax', $.ajax({
url: 'validate.php',
cache: false,
type: 'POST',
data: {
token: $parent.closest('form').find('input[name=token]').val(),
id: id,
values: JSON.stringify(values)
},
success: function (response) {
if (response === null) {
return;
}
var error = {};
if (typeof response !== 'object') {
error[$parent.id] = [response];
} else if (typeof response.error !== 'undefined') {
error[$parent.id] = [response.error];
} else {
for (var key in response) {
var value = response[key];
error[key] = Array.isArray(value) ? value : [value];
}
}
displayErrors(error);
},
complete: function () {
$parent.removeData('ajax');
}
}));
return true;
}
/**
* Automatic form submission on change.
*/
$(document).on('change', '.autosubmit', function (e) {
e.target.form.submit();
});
$.extend(true, validators, {
// field validators
field: {
/**
* hide_db field
*
* @param {boolean} isKeyUp
*
* @return {true}
*/
hide_db: function (isKeyUp) { // eslint-disable-line camelcase
if (!isKeyUp && this.value !== '') {
var data = {};
data[this.id] = this.value;
ajaxValidate(this, 'Servers/1/hide_db', data);
}
return true;
},
/**
* TrustedProxies field
*
* @param {boolean} isKeyUp
*
* @return {true}
*/
TrustedProxies: function (isKeyUp) {
if (!isKeyUp && this.value !== '') {
var data = {};
data[this.id] = this.value;
ajaxValidate(this, 'TrustedProxies', data);
}
return true;
}
},
// fieldset validators
fieldset: {
/**
* Validates Server fieldset
*
* @param {boolean} isKeyUp
*
* @return {true}
*/
Server: function (isKeyUp) {
if (!isKeyUp) {
ajaxValidate(this, 'Server', getAllValues());
}
return true;
},
/**
* Validates Server_login_options fieldset
*
* @param {boolean} isKeyUp
*
* @return {true}
*/
Server_login_options: function (isKeyUp) { // eslint-disable-line camelcase
return validators.fieldset.Server.apply(this, [isKeyUp]);
},
/**
* Validates Server_pmadb fieldset
*
* @param {boolean} isKeyUp
*
* @return {true}
*/
Server_pmadb: function (isKeyUp) { // eslint-disable-line camelcase
if (isKeyUp) {
return true;
}
var prefix = getIdPrefix($(this).find('input'));
if ($('#' + prefix + 'pmadb').val() !== '') {
ajaxValidate(this, 'Server_pmadb', getAllValues());
}
return true;
}
}
});
//
// END: Form validation and field operations
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// User preferences allow/disallow UI
//
$(function () {
$('.userprefs-allow').on('click', function (e) {
if (this !== e.target) {
return;
}
var el = $(this).find('input');
if (el.prop('disabled')) {
return;
}
el.prop('checked', !el.prop('checked'));
});
});
//
// END: User preferences allow/disallow UI
// ------------------------------------------------------------------
$(function () {
$('.delete-server').on('click', function (e) {
e.preventDefault();
var $this = $(this);
$.post($this.attr('href'), $this.attr('data-post'), function () {
window.location.replace('index.php');
});
});
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,46 @@
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('table/find_replace.js', function () {
$('#find_replace_form').off('submit');
$('#toggle_find').off('click');
});
/**
* Bind events
*/
AJAX.registerOnload('table/find_replace.js', function () {
$('<div id="toggle_find_div"><a id="toggle_find"></a></div>')
.insertAfter('#find_replace_form')
.hide();
$('#toggle_find')
.html(Messages.strHideFindNReplaceCriteria)
.on('click', function () {
var $link = $(this);
$('#find_replace_form').slideToggle();
if ($link.text() === Messages.strHideFindNReplaceCriteria) {
$link.text(Messages.strShowFindNReplaceCriteria);
} else {
$link.text(Messages.strHideFindNReplaceCriteria);
}
return false;
});
$('#find_replace_form').on('submit', function (e) {
e.preventDefault();
var findReplaceForm = $('#find_replace_form');
Functions.prepareForAjaxRequest(findReplaceForm);
var $msgbox = Functions.ajaxShowMessage();
$.post(findReplaceForm.attr('action'), findReplaceForm.serialize(), function (data) {
Functions.ajaxRemoveMessage($msgbox);
if (data.success === true) {
$('#toggle_find_div').show();
$('#toggle_find').trigger('click');
$('#sqlqueryresultsouter').html(data.preview);
} else {
$('#sqlqueryresultsouter').html(data.error);
}
});
});
});

@ -0,0 +1,255 @@
/**
* for table relation
*/
var TableRelation = {};
TableRelation.showHideClauses = function ($thisDropdown) {
if ($thisDropdown.val() === '') {
$thisDropdown.parent().nextAll('span').hide();
} else {
if ($thisDropdown.is('select[name^="destination_foreign_column"]')) {
$thisDropdown.parent().nextAll('span').show();
}
}
};
/**
* Sets dropdown options to values
* @param $dropdown
* @param values
* @param selectedValue
* @return {void}
*/
TableRelation.setDropdownValues = function ($dropdown, values, selectedValue) {
$dropdown.empty();
var optionsAsString = '';
// add an empty string to the beginning for empty selection
values.unshift('');
$.each(values, function () {
optionsAsString += '<option value=\'' + Functions.escapeHtml(this) + '\'' + (selectedValue === Functions.escapeHtml(this) ? ' selected=\'selected\'' : '') + '>' + Functions.escapeHtml(this) + '</option>';
});
$dropdown.append($(optionsAsString));
};
/**
* Retrieves and populates dropdowns to the left based on the selected value
*
* @param $dropdown the dropdown whose value got changed
* @return {void}
*/
TableRelation.getDropdownValues = function ($dropdown) {
var foreignDb = null;
var foreignTable = null;
var $databaseDd;
var $tableDd;
var $columnDd;
var foreign = '';
// if the changed dropdown is for foreign key constraints
if ($dropdown.is('select[name^="destination_foreign"]')) {
$databaseDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_db"]');
$tableDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_table"]');
$columnDd = $dropdown.parent().parent().parent().find('select[name^="destination_foreign_column"]');
foreign = '_foreign';
} else { // internal relations
$databaseDd = $dropdown.parent().find('select[name^="destination_db"]');
$tableDd = $dropdown.parent().find('select[name^="destination_table"]');
$columnDd = $dropdown.parent().find('select[name^="destination_column"]');
}
// if the changed dropdown is a database selector
if ($dropdown.is('select[name^="destination' + foreign + '_db"]')) {
foreignDb = $dropdown.val();
// if no database is selected empty table and column dropdowns
if (foreignDb === '') {
TableRelation.setDropdownValues($tableDd, []);
TableRelation.setDropdownValues($columnDd, []);
return;
}
} else { // if a table selector
foreignDb = $databaseDd.val();
foreignTable = $dropdown.val();
// if no table is selected empty the column dropdown
if (foreignTable === '') {
TableRelation.setDropdownValues($columnDd, []);
return;
}
}
var $msgbox = Functions.ajaxShowMessage();
var $form = $dropdown.parents('form');
var $db = $form.find('input[name="db"]').val();
var $table = $form.find('input[name="table"]').val();
var argsep = CommonParams.get('arg_separator');
var params = 'getDropdownValues=true' + argsep + 'ajax_request=true' +
argsep + 'db=' + encodeURIComponent($db) +
argsep + 'table=' + encodeURIComponent($table) +
argsep + 'foreign=' + (foreign !== '') +
argsep + 'foreignDb=' + encodeURIComponent(foreignDb) +
(foreignTable !== null ?
argsep + 'foreignTable=' + encodeURIComponent(foreignTable) : ''
);
var $server = $form.find('input[name="server"]');
if ($server.length > 0) {
params += argsep + 'server=' + $form.find('input[name="server"]').val();
}
$.ajax({
type: 'POST',
url: 'index.php?route=/table/relation',
data: params,
dataType: 'json',
success: function (data) {
Functions.ajaxRemoveMessage($msgbox);
if (typeof data !== 'undefined' && data.success) {
// if the changed dropdown is a database selector
if (foreignTable === null) {
// set values for table and column dropdowns
TableRelation.setDropdownValues($tableDd, data.tables);
TableRelation.setDropdownValues($columnDd, []);
} else { // if a table selector
// set values for the column dropdown
var primary = null;
if (typeof data.primary !== 'undefined'
&& 1 === data.primary.length
) {
primary = data.primary[0];
}
TableRelation.setDropdownValues($columnDd.first(), data.columns, primary);
TableRelation.setDropdownValues($columnDd.slice(1), data.columns);
}
} else {
Functions.ajaxShowMessage(data.error, false);
}
}
});
};
/**
* Unbind all event handlers before tearing down a page
*/
AJAX.registerTeardown('table/relation.js', function () {
$('body').off('change',
'select[name^="destination_db"], ' +
'select[name^="destination_table"], ' +
'select[name^="destination_foreign_db"], ' +
'select[name^="destination_foreign_table"]'
);
$('body').off('click', 'a.add_foreign_key_field');
$('body').off('click', 'a.add_foreign_key');
$('a.drop_foreign_key_anchor.ajax').off('click');
});
AJAX.registerOnload('table/relation.js', function () {
/**
* Ajax event handler to fetch table/column dropdown values.
*/
$('body').on('change',
'select[name^="destination_db"], ' +
'select[name^="destination_table"], ' +
'select[name^="destination_foreign_db"], ' +
'select[name^="destination_foreign_table"]',
function () {
TableRelation.getDropdownValues($(this));
}
);
/**
* Ajax event handler to add a column to a foreign key constraint.
*/
$('body').on('click', 'a.add_foreign_key_field', function (event) {
event.preventDefault();
event.stopPropagation();
// Add field.
$(this)
.prev('span')
.clone(true, true)
.insertBefore($(this))
.find('select')
.val('');
// Add foreign field.
var $sourceElem = $('select[name^="destination_foreign_column[' +
$(this).attr('data-index') + ']"]').last().parent();
$sourceElem
.clone(true, true)
.insertAfter($sourceElem)
.find('select')
.val('');
});
/**
* Ajax event handler to add a foreign key constraint.
*/
$('body').on('click', 'a.add_foreign_key', function (event) {
event.preventDefault();
event.stopPropagation();
var $prevRow = $(this).closest('tr').prev('tr');
var $newRow = $prevRow.clone(true, true);
// Update serial number.
var currIndex = $newRow
.find('a.add_foreign_key_field')
.attr('data-index');
var newIndex = parseInt(currIndex) + 1;
$newRow.find('a.add_foreign_key_field').attr('data-index', newIndex);
// Update form parameter names.
$newRow.find('select[name^="foreign_key_fields_name"]')
.not($newRow.find('select[name^="foreign_key_fields_name"]').first())
.find('select[name^="destination_foreign_column"]')
.not($newRow.find('select[name^="foreign_key_fields_name"]')
.not($newRow.find('select[name^="foreign_key_fields_name"]').first())
.find('select[name^="destination_foreign_column"]').first()
).each(function () {
$(this).parent().remove();
});
$newRow.find('input, select').each(function () {
$(this).attr('name',
$(this).attr('name').replace(/\d/, newIndex)
);
});
$newRow.find('input[type="text"]').each(function () {
$(this).val('');
});
// Finally add the row.
$newRow.insertAfter($prevRow);
});
/**
* Ajax Event handler for 'Drop Foreign key'
*/
$('a.drop_foreign_key_anchor.ajax').on('click', function (event) {
event.preventDefault();
var $anchor = $(this);
// Object containing reference to the current field's row
var $currRow = $anchor.parents('tr');
var dropQuery = Functions.escapeHtml(
$currRow.children('td')
.children('.drop_foreign_key_msg')
.val()
);
var question = Functions.sprintf(Messages.strDoYouReally, dropQuery);
$anchor.confirm(question, $anchor.attr('href'), function (url) {
var $msg = Functions.ajaxShowMessage(Messages.strDroppingForeignKey, false);
var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
$.post(url, params, function (data) {
if (data.success === true) {
Functions.ajaxRemoveMessage($msg);
CommonActions.refreshMain(false, function () {
// Do nothing
});
} else {
Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
}
}); // end $.post()
});
}); // end Drop Foreign key
var windowWidth = $(window).width();
$('.jsresponsive').css('max-width', (windowWidth - 35) + 'px');
});

@ -0,0 +1,10 @@
/**
* SQL syntax highlighting transformation plugin js
*
* @package PhpMyAdmin
*/
AJAX.registerOnload('transformations/sql_editor.js', function () {
$('textarea.transform_sql_editor').each(function () {
Functions.getSqlEditor($(this), {}, 'both');
});
});

File diff suppressed because one or more lines are too long

@ -0,0 +1,76 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.runMode = function(string, modespec, callback, options) {
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
// Create a tokenizing callback function if passed-in callback is a DOM element.
if (callback.appendChild) {
var ie = /MSIE \d/.test(navigator.userAgent);
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
var node = callback, col = 0;
node.innerHTML = "";
callback = function(text, style) {
if (text == "\n") {
// Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
// Emitting a carriage return makes everything ok.
node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
col = 0;
return;
}
var content = "";
// replace tabs
for (var pos = 0;;) {
var idx = text.indexOf("\t", pos);
if (idx == -1) {
content += text.slice(pos);
col += text.length - pos;
break;
} else {
col += idx - pos;
content += text.slice(pos, idx);
var size = tabSize - col % tabSize;
col += size;
for (var i = 0; i < size; ++i) content += " ";
pos = idx + 1;
}
}
// Create a node with token style and append it to the callback DOM element.
if (style) {
var sp = node.appendChild(document.createElement("span"));
sp.className = "cm-" + style.replace(/ +/g, " cm-");
sp.appendChild(document.createTextNode(content));
} else {
node.appendChild(document.createTextNode(content));
}
};
}
var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new CodeMirror.StringStream(lines[i], null, {
lookAhead: function(n) { return lines[i + n] },
baseToken: function() {}
});
if (!stream.string && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state, mode);
stream.start = stream.pos;
}
}
};
});

@ -0,0 +1,203 @@
/**
* jqPlot
* Pure JavaScript plotting plugin using jQuery
*
* Version: 1.0.9
* Revision: dff2f04
*
* Copyright (c) 2009-2016 Chris Leonello
* jqPlot is currently available for use in all personal or commercial projects
* under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
* version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
* choose the license that best suits your project and use it accordingly.
*
* Although not required, the author would appreciate an email letting him
* know of any substantial use of jqPlot. You can reach the author at:
* chris at jqplot dot com or see http://www.jqplot.com/info.php .
*
* If you are feeling kind and generous, consider supporting the project by
* making a donation at: http://www.jqplot.com/donate.php .
*
* sprintf functions contained in jqplot.sprintf.js by Ash Searle:
*
* version 2007.04.27
* author Ash Searle
* http://hexmen.com/blog/2007/03/printf-sprintf/
* http://hexmen.com/js/sprintf.js
* The author (Ash Searle) has placed this code in the public domain:
* "This code is unrestricted: you are free to use it however you like."
*
*/
(function($) {
/**
* Class: $.jqplot.CanvasAxisLabelRenderer
* Renderer to draw axis labels with a canvas element to support advanced
* featrues such as rotated text. This renderer uses a separate rendering engine
* to draw the text on the canvas. Two modes of rendering the text are available.
* If the browser has native font support for canvas fonts (currently Mozila 3.5
* and Safari 4), you can enable text rendering with the canvas fillText method.
* You do so by setting the "enableFontSupport" option to true.
*
* Browsers lacking native font support will have the text drawn on the canvas
* using the Hershey font metrics. Even if the "enableFontSupport" option is true
* non-supporting browsers will still render with the Hershey font.
*
*/
$.jqplot.CanvasAxisLabelRenderer = function(options) {
// Group: Properties
// prop: angle
// angle of text, measured clockwise from x axis.
this.angle = 0;
// name of the axis associated with this tick
this.axis;
// prop: show
// whether or not to show the tick (mark and label).
this.show = true;
// prop: showLabel
// whether or not to show the label.
this.showLabel = true;
// prop: label
// label for the axis.
this.label = '';
// prop: fontFamily
// CSS spec for the font-family css attribute.
// Applies only to browsers supporting native font rendering in the
// canvas tag. Currently Mozilla 3.5 and Safari 4.
this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif';
// prop: fontSize
// CSS spec for font size.
this.fontSize = '11pt';
// prop: fontWeight
// CSS spec for fontWeight: normal, bold, bolder, lighter or a number 100 - 900
this.fontWeight = 'normal';
// prop: fontStretch
// Multiplier to condense or expand font width.
// Applies only to browsers which don't support canvas native font rendering.
this.fontStretch = 1.0;
// prop: textColor
// css spec for the color attribute.
this.textColor = '#666666';
// prop: enableFontSupport
// true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+.
// If true, label will be drawn with canvas tag native support for fonts.
// If false, label will be drawn with Hershey font metrics.
this.enableFontSupport = true;
// prop: pt2px
// Point to pixel scaling factor, used for computing height of bounding box
// around a label. The labels text renderer has a default setting of 1.4, which
// should be suitable for most fonts. Leave as null to use default. If tops of
// letters appear clipped, increase this. If bounding box seems too big, decrease.
// This is an issue only with the native font renderering capabilities of Mozilla
// 3.5 and Safari 4 since they do not provide a method to determine the font height.
this.pt2px = null;
this._elem;
this._ctx;
this._plotWidth;
this._plotHeight;
this._plotDimensions = {height:null, width:null};
$.extend(true, this, options);
if (options.angle == null && this.axis != 'xaxis' && this.axis != 'x2axis') {
this.angle = -90;
}
var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily};
if (this.pt2px) {
ropts.pt2px = this.pt2px;
}
if (this.enableFontSupport) {
if ($.jqplot.support_canvas_text()) {
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts);
}
else {
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts);
}
}
else {
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts);
}
};
$.jqplot.CanvasAxisLabelRenderer.prototype.init = function(options) {
$.extend(true, this, options);
this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily});
};
// return width along the x axis
// will check first to see if an element exists.
// if not, will return the computed text box width.
$.jqplot.CanvasAxisLabelRenderer.prototype.getWidth = function(ctx) {
if (this._elem) {
return this._elem.outerWidth(true);
}
else {
var tr = this._textRenderer;
var l = tr.getWidth(ctx);
var h = tr.getHeight(ctx);
var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l);
return w;
}
};
// return height along the y axis.
$.jqplot.CanvasAxisLabelRenderer.prototype.getHeight = function(ctx) {
if (this._elem) {
return this._elem.outerHeight(true);
}
else {
var tr = this._textRenderer;
var l = tr.getWidth(ctx);
var h = tr.getHeight(ctx);
var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l);
return w;
}
};
$.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad = function() {
var a = this.angle * Math.PI/180;
return a;
};
$.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx, plot) {
// Memory Leaks patch
if (this._elem) {
if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
}
this._elem.emptyForce();
this._elem = null;
}
// create a canvas here, but can't draw on it untill it is appended
// to dom for IE compatability.
var elem = plot.canvasManager.getCanvas();
this._textRenderer.setText(this.label, ctx);
var w = this.getWidth(ctx);
var h = this.getHeight(ctx);
elem.width = w;
elem.height = h;
elem.style.width = w;
elem.style.height = h;
elem = plot.canvasManager.initCanvas(elem);
this._elem = $(elem);
this._elem.css({ position: 'absolute'});
this._elem.addClass('jqplot-'+this.axis+'-label');
elem = null;
return this._elem;
};
$.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() {
this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label);
};
})(jQuery);

@ -0,0 +1,484 @@
/**
* jqPlot
* Pure JavaScript plotting plugin using jQuery
*
* Version: 1.0.9
* Revision: dff2f04
*
* Copyright (c) 2009-2016 Chris Leonello
* jqPlot is currently available for use in all personal or commercial projects
* under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
* version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
* choose the license that best suits your project and use it accordingly.
*
* Although not required, the author would appreciate an email letting him
* know of any substantial use of jqPlot. You can reach the author at:
* chris at jqplot dot com or see http://www.jqplot.com/info.php .
*
* If you are feeling kind and generous, consider supporting the project by
* making a donation at: http://www.jqplot.com/donate.php .
*
* sprintf functions contained in jqplot.sprintf.js by Ash Searle:
*
* version 2007.04.27
* author Ash Searle
* http://hexmen.com/blog/2007/03/printf-sprintf/
* http://hexmen.com/js/sprintf.js
* The author (Ash Searle) has placed this code in the public domain:
* "This code is unrestricted: you are free to use it however you like."
*
*/
(function($) {
$.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
/**
* Class: $.jqplot.Highlighter
* Plugin which will highlight data points when they are moused over.
*
* To use this plugin, include the js
* file in your source:
*
* > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
*
* A tooltip providing information about the data point is enabled by default.
* To disable the tooltip, set "showTooltip" to false.
*
* You can control what data is displayed in the tooltip with various
* options. The "tooltipAxes" option controls whether the x, y or both
* data values are displayed.
*
* Some chart types (e.g. hi-low-close) have more than one y value per
* data point. To display the additional values in the tooltip, set the
* "yvalues" option to the desired number of y values present (3 for a hlc chart).
*
* By default, data values will be formatted with the same formatting
* specifiers as used to format the axis ticks. A custom format code
* can be supplied with the tooltipFormatString option. This will apply
* to all values in the tooltip.
*
* For more complete control, the "formatString" option can be set. This
* Allows conplete control over tooltip formatting. Values are passed to
* the format string in an order determined by the "tooltipAxes" and "yvalues"
* options. So, if you have a hi-low-close chart and you just want to display
* the hi-low-close values in the tooltip, you could set a formatString like:
*
* > highlighter: {
* > tooltipAxes: 'y',
* > yvalues: 3,
* > formatString:'<table class="jqplot-highlighter">
* > <tr><td>hi:</td><td>%s</td></tr>
* > <tr><td>low:</td><td>%s</td></tr>
* > <tr><td>close:</td><td>%s</td></tr></table>'
* > }
*
*/
$.jqplot.Highlighter = function(options) {
// Group: Properties
//
//prop: show
// true to show the highlight.
this.show = $.jqplot.config.enablePlugins;
// prop: markerRenderer
// Renderer used to draw the marker of the highlighted point.
// Renderer will assimilate attributes from the data point being highlighted,
// so no attributes need set on the renderer directly.
// Default is to turn off shadow drawing on the highlighted point.
this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
// prop: showMarker
// true to show the marker
this.showMarker = true;
// prop: lineWidthAdjust
// Pixels to add to the lineWidth of the highlight.
this.lineWidthAdjust = 2.5;
// prop: sizeAdjust
// Pixels to add to the overall size of the highlight.
this.sizeAdjust = 5;
// prop: showTooltip
// Show a tooltip with data point values.
this.showTooltip = true;
// prop: tooltipLocation
// Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
this.tooltipLocation = 'nw';
// prop: fadeTooltip
// true = fade in/out tooltip, flase = show/hide tooltip
this.fadeTooltip = true;
// prop: tooltipFadeSpeed
// 'slow', 'def', 'fast', or number of milliseconds.
this.tooltipFadeSpeed = "fast";
// prop: tooltipOffset
// Pixel offset of tooltip from the highlight.
this.tooltipOffset = 2;
// prop: tooltipAxes
// Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
// 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
this.tooltipAxes = 'both';
// prop; tooltipSeparator
// String to use to separate x and y axes in tooltip.
this.tooltipSeparator = ', ';
// prop; tooltipContentEditor
// Function used to edit/augment/replace the formatted tooltip contents.
// Called as str = tooltipContentEditor(str, seriesIndex, pointIndex)
// where str is the generated tooltip html and seriesIndex and pointIndex identify
// the data point being highlighted. Should return the html for the tooltip contents.
this.tooltipContentEditor = null;
// prop: useAxesFormatters
// Use the x and y axes formatters to format the text in the tooltip.
this.useAxesFormatters = true;
// prop: tooltipFormatString
// sprintf format string for the tooltip.
// Uses Ash Searle's javascript sprintf implementation
// found here: http://hexmen.com/blog/2007/03/printf-sprintf/
// See http://perldoc.perl.org/functions/sprintf.html for reference.
// Additional "p" and "P" format specifiers added by Chris Leonello.
this.tooltipFormatString = '%.5P';
// prop: formatString
// alternative to tooltipFormatString
// will format the whole tooltip text, populating with x, y values as
// indicated by tooltipAxes option. So, you could have a tooltip like:
// 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
// If useAxesFormatters is true, values will be formatted according to
// Axes formatters and you can populate your tooltip string with
// %s placeholders.
this.formatString = null;
// prop: yvalues
// Number of y values to expect in the data point array.
// Typically this is 1. Certain plots, like OHLC, will
// have more y values in each data point array.
this.yvalues = 1;
// prop: bringSeriesToFront
// This option requires jQuery 1.4+
// True to bring the series of the highlighted point to the front
// of other series.
this.bringSeriesToFront = false;
this._tooltipElem;
this.isHighlighting = false;
this.currentNeighbor = null;
$.extend(true, this, options);
};
var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7};
var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];
// axis.renderer.tickrenderer.formatter
// called with scope of plot
$.jqplot.Highlighter.init = function (target, data, opts){
var options = opts || {};
// add a highlighter attribute to the plot
this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
};
// called within scope of series
$.jqplot.Highlighter.parseOptions = function (defaults, options) {
// Add a showHighlight option to the series
// and set it to true by default.
this.showHighlight = true;
};
// called within context of plot
// create a canvas which we can draw on.
// insert it before the eventCanvas, so eventCanvas will still capture events.
$.jqplot.Highlighter.postPlotDraw = function() {
// Memory Leaks patch
if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) {
this.plugins.highlighter.highlightCanvas.resetCanvas();
this.plugins.highlighter.highlightCanvas = null;
}
if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) {
this.plugins.highlighter._tooltipElem.emptyForce();
this.plugins.highlighter._tooltipElem = null;
}
this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this));
this.plugins.highlighter.highlightCanvas.setContext();
var elem = document.createElement('div');
this.plugins.highlighter._tooltipElem = $(elem);
elem = null;
this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip');
this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'});
this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem);
};
$.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
$.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
$.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
function draw(plot, neighbor) {
var hl = plot.plugins.highlighter;
var s = plot.series[neighbor.seriesIndex];
var smr = s.markerRenderer;
var mr = hl.markerRenderer;
mr.style = smr.style;
mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
mr.size = smr.size + hl.sizeAdjust;
var rgba = $.jqplot.getColorComponents(smr.color);
var newrgb = [rgba[0], rgba[1], rgba[2]];
var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
mr.init();
var x_pos = s.gridData[neighbor.pointIndex][0];
var y_pos = s.gridData[neighbor.pointIndex][1];
// Adjusting with s._barNudge
if (s.renderer.constructor == $.jqplot.BarRenderer) {
if (s.barDirection == "vertical") {
x_pos += s._barNudge;
}
else {
y_pos -= s._barNudge;
}
}
mr.draw(x_pos, y_pos, hl.highlightCanvas._ctx);
}
function showTooltip(plot, series, neighbor) {
// neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
// gridData should be x,y pixel coords on the grid.
// add the plot._gridPadding to that to get x,y in the target.
var hl = plot.plugins.highlighter;
var elem = hl._tooltipElem;
var serieshl = series.highlighter || {};
var opts = $.extend(true, {}, hl, serieshl);
if (opts.useAxesFormatters) {
var xf = series._xaxis._ticks[0].formatter;
var yf = series._yaxis._ticks[0].formatter;
var xfstr = series._xaxis._ticks[0].formatString;
var yfstr = series._yaxis._ticks[0].formatString;
var str;
var xstr = xf(xfstr, neighbor.data[0]);
var ystrs = [];
for (var i=1; i<opts.yvalues+1; i++) {
ystrs.push(yf(yfstr, neighbor.data[i]));
}
if (typeof opts.formatString === 'string') {
switch (opts.tooltipAxes) {
case 'both':
case 'xy':
ystrs.unshift(xstr);
ystrs.unshift(opts.formatString);
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
break;
case 'yx':
ystrs.push(xstr);
ystrs.unshift(opts.formatString);
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
break;
case 'x':
str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]);
break;
case 'y':
ystrs.unshift(opts.formatString);
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
break;
default: // same as xy
ystrs.unshift(xstr);
ystrs.unshift(opts.formatString);
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
break;
}
}
else {
switch (opts.tooltipAxes) {
case 'both':
case 'xy':
str = xstr;
for (var i=0; i<ystrs.length; i++) {
str += opts.tooltipSeparator + ystrs[i];
}
break;
case 'yx':
str = '';
for (var i=0; i<ystrs.length; i++) {
str += ystrs[i] + opts.tooltipSeparator;
}
str += xstr;
break;
case 'x':
str = xstr;
break;
case 'y':
str = ystrs.join(opts.tooltipSeparator);
break;
default: // same as 'xy'
str = xstr;
for (var i=0; i<ystrs.length; i++) {
str += opts.tooltipSeparator + ystrs[i];
}
break;
}
}
}
else {
var str;
if (typeof opts.formatString === 'string') {
str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data));
}
else {
if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') {
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
}
else if (opts.tooltipAxes == 'yx') {
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
}
else if (opts.tooltipAxes == 'x') {
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
}
else if (opts.tooltipAxes == 'y') {
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
}
}
}
if ($.isFunction(opts.tooltipContentEditor)) {
// args str, seriesIndex, pointIndex are essential so the hook can look up
// extra data for the point.
str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot);
}
elem.html(str);
var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
var ms = 0;
var fact = 0.707;
if (series.markerRenderer.show == true) {
ms = (series.markerRenderer.size + opts.sizeAdjust)/2;
}
var loc = locations;
if (series.fillToZero && series.fill && neighbor.data[1] < 0) {
loc = oppositeLocations;
}
switch (loc[locationIndicies[opts.tooltipLocation]]) {
case 'nw':
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
break;
case 'n':
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms;
break;
case 'ne':
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
break;
case 'e':
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms;
var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
break;
case 'se':
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
break;
case 's':
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms;
break;
case 'sw':
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
break;
case 'w':
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms;
var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
break;
default: // same as 'nw'
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
break;
}
if (series.renderer.constructor == $.jqplot.BarRenderer) {
if (series.barDirection == 'vertical') {
x += series._barNudge;
}
else {
y -= series._barNudge;
}
}
elem.css('left', x);
elem.css('top', y);
if (opts.fadeTooltip) {
// Fix for stacked up animations. Thnanks Trevor!
elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed);
}
else {
elem.show();
}
elem = null;
}
function handleMove(ev, gridpos, datapos, neighbor, plot) {
var hl = plot.plugins.highlighter;
var c = plot.plugins.cursor;
if (hl.show) {
if (neighbor == null && hl.isHighlighting) {
var evt = jQuery.Event('jqplotHighlighterUnhighlight');
plot.target.trigger(evt);
var ctx = hl.highlightCanvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
if (hl.fadeTooltip) {
hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
}
else {
hl._tooltipElem.hide();
}
if (hl.bringSeriesToFront) {
plot.restorePreviousSeriesOrder();
}
hl.isHighlighting = false;
hl.currentNeighbor = null;
ctx = null;
}
else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
var evt = jQuery.Event('jqplotHighlighterHighlight');
evt.which = ev.which;
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot];
plot.target.trigger(evt, ins);
hl.isHighlighting = true;
hl.currentNeighbor = neighbor;
if (hl.showMarker) {
draw(plot, neighbor);
}
if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) {
showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
}
if (hl.bringSeriesToFront) {
plot.moveSeriesToFront(neighbor.seriesIndex);
}
}
// check to see if we're highlighting the wrong point.
else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) {
// highlighting the wrong point.
// if new series allows highlighting, highlight new point.
if (plot.series[neighbor.seriesIndex].showHighlight) {
var ctx = hl.highlightCanvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
hl.isHighlighting = true;
hl.currentNeighbor = neighbor;
if (hl.showMarker) {
draw(plot, neighbor);
}
if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) {
showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
}
if (hl.bringSeriesToFront) {
plot.moveSeriesToFront(neighbor.seriesIndex);
}
}
}
}
}
})(jQuery);

@ -0,0 +1,20 @@
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,82 @@
/**
* Debounce and throttle function's decorator plugin 1.0.6
*
* Copyright (c) 2009 Filatov Dmitry (alpha@zforms.ru)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function ($) {
$.extend({
/**
* Debounce's decorator
* @param {Function} fn original function
* @param {Number} timeout timeout
* @param {Boolean} [invokeAsap=false] invoke function as soon as possible
* @param {Object} [ctx] context of original function
*/
debounce: function (fn, timeout, invokeAsap, ctx) {
if (arguments.length == 3 && typeof invokeAsap != 'boolean') {
ctx = invokeAsap;
invokeAsap = false;
}
var timer;
return function () {
var args = arguments;
ctx = ctx || this;
invokeAsap && !timer && fn.apply(ctx, args);
clearTimeout(timer);
timer = setTimeout(function () {
invokeAsap || fn.apply(ctx, args);
timer = null;
}, timeout);
};
},
/**
* Throttle's decorator
* @param {Function} fn original function
* @param {Number} timeout timeout
* @param {Object} [ctx] context of original function
*/
throttle: function (fn, timeout, ctx) {
var timer, args, needInvoke;
return function () {
args = arguments;
needInvoke = true;
ctx = ctx || this;
timer || (function () {
if (needInvoke) {
fn.apply(ctx, args);
needInvoke = false;
timer = setTimeout(arguments.callee, timeout);
}
else {
timer = null;
}
})();
};
}
});
})(jQuery);

File diff suppressed because one or more lines are too long

@ -0,0 +1,285 @@
.ol-box {
box-sizing: border-box;
border-radius: 2px;
border: 1.5px solid rgb(179,197,219);
background-color: rgba(255,255,255,0.4);
}
.ol-mouse-position {
top: 8px;
right: 8px;
position: absolute;
}
.ol-scale-line {
background: rgba(0,60,136,0.3);
border-radius: 4px;
bottom: 8px;
left: 8px;
padding: 2px;
position: absolute;
}
.ol-scale-line-inner {
border: 1px solid #eee;
border-top: none;
color: #eee;
font-size: 10px;
text-align: center;
margin: 1px;
will-change: contents, width;
transition: all 0.25s;
}
.ol-scale-bar {
position: absolute;
bottom: 8px;
left: 8px;
}
.ol-scale-step-marker {
width: 1px;
height: 15px;
background-color: #000000;
float: right;
z-index: 10;
}
.ol-scale-step-text {
position: absolute;
bottom: -5px;
font-size: 12px;
z-index: 11;
color: #000000;
text-shadow: -2px 0 #FFFFFF, 0 2px #FFFFFF, 2px 0 #FFFFFF, 0 -2px #FFFFFF;
}
.ol-scale-text {
position: absolute;
font-size: 14px;
text-align: center;
bottom: 25px;
color: #000000;
text-shadow: -2px 0 #FFFFFF, 0 2px #FFFFFF, 2px 0 #FFFFFF, 0 -2px #FFFFFF;
}
.ol-scale-singlebar {
position: relative;
height: 10px;
z-index: 9;
box-sizing: border-box;
border: 1px solid black;
}
.ol-unsupported {
display: none;
}
.ol-viewport, .ol-unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.ol-viewport canvas {
all: unset;
}
.ol-selectable {
-webkit-touch-callout: default;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.ol-grabbing {
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
.ol-grab {
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.ol-control {
position: absolute;
background-color: rgba(255,255,255,0.4);
border-radius: 4px;
padding: 2px;
}
.ol-control:hover {
background-color: rgba(255,255,255,0.6);
}
.ol-zoom {
top: .5em;
left: .5em;
}
.ol-rotate {
top: .5em;
right: .5em;
transition: opacity .25s linear, visibility 0s linear;
}
.ol-rotate.ol-hidden {
opacity: 0;
visibility: hidden;
transition: opacity .25s linear, visibility 0s linear .25s;
}
.ol-zoom-extent {
top: 4.643em;
left: .5em;
}
.ol-full-screen {
right: .5em;
top: .5em;
}
.ol-control button {
display: block;
margin: 1px;
padding: 0;
color: white;
font-weight: bold;
text-decoration: none;
font-size: inherit;
text-align: center;
height: 1.375em;
width: 1.375em;
line-height: .4em;
background-color: rgba(0,60,136,0.5);
border: none;
border-radius: 2px;
}
.ol-control button::-moz-focus-inner {
border: none;
padding: 0;
}
.ol-zoom-extent button {
line-height: 1.4em;
}
.ol-compass {
display: block;
font-weight: normal;
font-size: 1.2em;
will-change: transform;
}
.ol-touch .ol-control button {
font-size: 1.5em;
}
.ol-touch .ol-zoom-extent {
top: 5.5em;
}
.ol-control button:hover,
.ol-control button:focus {
text-decoration: none;
background-color: rgba(0,60,136,0.7);
}
.ol-zoom .ol-zoom-in {
border-radius: 2px 2px 0 0;
}
.ol-zoom .ol-zoom-out {
border-radius: 0 0 2px 2px;
}
.ol-attribution {
text-align: right;
bottom: .5em;
right: .5em;
max-width: calc(100% - 1.3em);
display: flex;
flex-flow: row-reverse;
align-items: center;
}
.ol-attribution a {
color: rgba(0,60,136,0.7);
text-decoration: none;
}
.ol-attribution ul {
margin: 0;
padding: 1px .5em;
color: #000;
text-shadow: 0 0 2px #fff;
font-size: 12px;
}
.ol-attribution li {
display: inline;
list-style: none;
}
.ol-attribution li:not(:last-child):after {
content: " ";
}
.ol-attribution img {
max-height: 2em;
max-width: inherit;
vertical-align: middle;
}
.ol-attribution button {
flex-shrink: 0;
}
.ol-attribution.ol-collapsed ul {
display: none;
}
.ol-attribution:not(.ol-collapsed) {
background: rgba(255,255,255,0.8);
}
.ol-attribution.ol-uncollapsible {
bottom: 0;
right: 0;
border-radius: 4px 0 0;
}
.ol-attribution.ol-uncollapsible img {
margin-top: -.2em;
max-height: 1.6em;
}
.ol-attribution.ol-uncollapsible button {
display: none;
}
.ol-zoomslider {
top: 4.5em;
left: .5em;
height: 200px;
}
.ol-zoomslider button {
position: relative;
height: 10px;
}
.ol-touch .ol-zoomslider {
top: 5.5em;
}
.ol-overviewmap {
left: 0.5em;
bottom: 0.5em;
}
.ol-overviewmap.ol-uncollapsible {
bottom: 0;
left: 0;
border-radius: 0 4px 0 0;
}
.ol-overviewmap .ol-overviewmap-map,
.ol-overviewmap button {
display: block;
}
.ol-overviewmap .ol-overviewmap-map {
border: 1px solid #7b98bc;
height: 150px;
margin: 2px;
width: 150px;
}
.ol-overviewmap:not(.ol-collapsed) button {
bottom: 2px;
left: 2px;
position: absolute;
}
.ol-overviewmap.ol-collapsed .ol-overviewmap-map,
.ol-overviewmap.ol-uncollapsible button {
display: none;
}
.ol-overviewmap:not(.ol-collapsed) {
background: rgba(255,255,255,0.8);
}
.ol-overviewmap-box {
border: 2px dotted rgba(0,60,136,0.7);
}
.ol-overviewmap .ol-overviewmap-box:hover {
cursor: move;
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
return [
// Query cache
[
'id' => 'Query cache disabled',
'name' => __('Query cache disabled'),
'formula' => 'query_cache_size',
'test' => 'value == 0 || query_cache_type == \'OFF\' || query_cache_type == \'0\'',
'issue' => __('The query cache is not enabled.'),
'recommendation' => __(
'The query cache is known to greatly improve performance if configured correctly. Enable it by'
. ' setting {query_cache_size} to a 2 digit MiB value and setting {query_cache_type} to \'ON\'.'
. ' <b>Note:</b> If you are using memcached, ignore this recommendation.'
),
'justification' => __('query_cache_size is set to 0 or query_cache_type is set to \'OFF\''),
],
[
'id' => 'Query cache efficiency (%)',
/* xgettext:no-php-format */
'name' => __('Query cache efficiency (%)'),
'precondition' => 'Com_select + Qcache_hits > 0 && !fired(\'Query cache disabled\')',
'formula' => 'Qcache_hits / (Com_select + Qcache_hits) * 100',
'test' => 'value < 20',
'issue' => __('Query cache not running efficiently, it has a low hit rate.'),
'recommendation' => __('Consider increasing {query_cache_limit}.'),
'justification' => __('The current query cache hit rate of %s%% is below 20%%'),
'justification_formula' => 'round(value,1)',
],
[
'id' => 'Query Cache usage',
'name' => __('Query Cache usage'),
'precondition' => '!fired(\'Query cache disabled\')',
'formula' => '100 - Qcache_free_memory / query_cache_size * 100',
'test' => 'value < 80',
/* xgettext:no-php-format */
'issue' => __('Less than 80% of the query cache is being utilized.'),
'recommendation' => __(
'This might be caused by {query_cache_limit} being too low.'
. ' Flushing the query cache might help as well.'
),
'justification' => __(
'The current ratio of free query cache memory to total query'
. ' cache size is %s%%. It should be above 80%%'
),
'justification_formula' => 'round(value,1)',
],
[
'id' => 'Query cache fragmentation',
'name' => __('Query cache fragmentation'),
'precondition' => '!fired(\'Query cache disabled\')',
'formula' => 'Qcache_free_blocks / (Qcache_total_blocks / 2) * 100',
'test' => 'value > 20',
'issue' => __('The query cache is considerably fragmented.'),
'recommendation' => __(
'Severe fragmentation is likely to (further) increase Qcache_lowmem_prunes. This might be'
. ' caused by many Query cache low memory prunes due to {query_cache_size} being too small. For a'
. ' immediate but short lived fix you can flush the query cache (might lock the query cache for a'
. ' long time). Carefully adjusting {query_cache_min_res_unit} to a lower value might help too,'
. ' e.g. you can set it to the average size of your queries in the cache using this formula:'
. ' (query_cache_size - qcache_free_memory) / qcache_queries_in_cache'
),
'justification' => __(
'The cache is currently fragmented by %s%% , with 100%% fragmentation meaning that the query'
. ' cache is an alternating pattern of free and used blocks. This value should be below 20%%.'
),
'justification_formula' => 'round(value,1)',
],
[
'id' => 'Query cache low memory prunes',
'name' => __('Query cache low memory prunes'),
'precondition' => 'Qcache_inserts > 0 && !fired(\'Query cache disabled\')',
'formula' => 'Qcache_lowmem_prunes / Qcache_inserts * 100',
'test' => 'value > 0.1',
'issue' => __('Cached queries are removed due to low query cache memory from the query cache.'),
'recommendation' => __(
'You might want to increase {query_cache_size}, however keep in mind that the overhead of'
. ' maintaining the cache is likely to increase with its size, so do this in small increments'
. ' and monitor the results.'
),
'justification' => __(
'The ratio of removed queries to inserted queries is %s%%. The lower this value is,'
. ' the better (This rules firing limit: 0.1%%)'
),
'justification_formula' => 'round(value,1)',
],
[
'id' => 'Query cache max size',
'name' => __('Query cache max size'),
'precondition' => '!fired(\'Query cache disabled\')',
'formula' => 'query_cache_size',
'test' => 'value > 1024 * 1024 * 128',
'issue' => __(
'The query cache size is above 128 MiB. Big query caches may cause significant'
. ' overhead that is required to maintain the cache.'
),
'recommendation' => __(
'Depending on your environment, it might be performance increasing to reduce this value.'
),
'justification' => __('Current query cache size: %s'),
'justification_formula' => 'ADVISOR_formatByteDown(value, 2, 2)',
],
[
'id' => 'Query cache min result size',
'name' => __('Query cache min result size'),
'precondition' => '!fired(\'Query cache disabled\')',
'formula' => 'query_cache_limit',
'test' => 'value == 1024*1024',
'issue' => __('The max size of the result set in the query cache is the default of 1 MiB.'),
'recommendation' => __(
'Changing {query_cache_limit} (usually by increasing) may increase efficiency. This variable'
. ' determines the maximum size a query result may have to be inserted into the query cache.'
. ' If there are many query results above 1 MiB that are well cacheable (many reads, little writes)'
. ' then increasing {query_cache_limit} will increase efficiency. Whereas in the case of many query'
. ' results being above 1 MiB that are not very well cacheable (often invalidated due to table'
. ' updates) increasing {query_cache_limit} might reduce efficiency.'
),
'justification' => __('query_cache_limit is set to 1 MiB'),
],
];

@ -0,0 +1,271 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Command;
use PhpMyAdmin\Template;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Twig\Error\Error;
use Twig\Loader\ArrayLoader;
use Twig\Source;
use function array_push;
use function closedir;
use function count;
use function explode;
use function file_get_contents;
use function is_dir;
use function is_file;
use function max;
use function min;
use function opendir;
use function preg_match;
use function readdir;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use const DIRECTORY_SEPARATOR;
use const E_USER_DEPRECATED;
/**
* Command that will validate your template syntax and output encountered errors.
* Author: Marc Weistroff <marc.weistroff@sensiolabs.com>
* Author: Jérôme Tamarelle <jerome@tamarelle.net>
*
* Copyright (c) 2013-2021 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
class TwigLintCommand extends Command
{
/** @var string|null */
protected static $defaultName = 'lint:twig';
/** @var string|null */
protected static $defaultDescription = 'Lint a Twig template and outputs encountered errors';
protected function configure(): void
{
$this
->setDescription((string) self::$defaultDescription)
->addOption('show-deprecations', null, InputOption::VALUE_NONE, 'Show deprecations as errors');
}
protected function findFiles(string $baseFolder): array
{
/* Open the handle */
$handle = @opendir($baseFolder);
if ($handle === false) {
return [];
}
$foundFiles = [];
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$itemPath = $baseFolder . DIRECTORY_SEPARATOR . $file;
if (is_dir($itemPath)) {
array_push($foundFiles, ...$this->findFiles($itemPath));
continue;
}
if (! is_file($itemPath)) {
continue;
}
$foundFiles[] = $itemPath;
}
/* Close the handle */
closedir($handle);
return $foundFiles;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$showDeprecations = $input->getOption('show-deprecations');
if ($showDeprecations) {
$prevErrorHandler = set_error_handler(
static function (int $level, string $message, string $file, int $line) use (&$prevErrorHandler) {
if ($level === E_USER_DEPRECATED) {
$templateLine = 0;
if (preg_match('/ at line (\d+)[ .]/', $message, $matches)) {
$templateLine = (int) $matches[1];
}
throw new Error($message, $templateLine);
}
return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
}
);
}
try {
$filesInfo = $this->getFilesInfo(ROOT_PATH . 'templates');
} finally {
if ($showDeprecations) {
restore_error_handler();
}
}
return $this->display($output, $io, $filesInfo);
}
protected function getFilesInfo(string $templatesPath): array
{
$filesInfo = [];
$filesFound = $this->findFiles($templatesPath);
foreach ($filesFound as $file) {
$filesInfo[] = $this->validate($this->getTemplateContents($file), $file);
}
return $filesInfo;
}
/**
* Allows easier testing
*/
protected function getTemplateContents(string $filePath): string
{
return (string) file_get_contents($filePath);
}
private function validate(string $template, string $file): array
{
$twig = Template::getTwigEnvironment(null);
$realLoader = $twig->getLoader();
try {
$temporaryLoader = new ArrayLoader([$file => $template]);
$twig->setLoader($temporaryLoader);
$nodeTree = $twig->parse($twig->tokenize(new Source($template, $file)));
$twig->compile($nodeTree);
$twig->setLoader($realLoader);
} catch (Error $e) {
$twig->setLoader($realLoader);
return [
'template' => $template,
'file' => $file,
'line' => $e->getTemplateLine(),
'valid' => false,
'exception' => $e,
];
}
return ['template' => $template, 'file' => $file, 'valid' => true];
}
private function display(OutputInterface $output, SymfonyStyle $io, array $filesInfo): int
{
$errors = 0;
foreach ($filesInfo as $info) {
if ($info['valid'] && $output->isVerbose()) {
$io->comment('<info>OK</info>' . ($info['file'] ? sprintf(' in %s', $info['file']) : ''));
} elseif (! $info['valid']) {
++$errors;
$this->renderException($io, $info['template'], $info['exception'], $info['file']);
}
}
if ($errors === 0) {
$io->success(sprintf('All %d Twig files contain valid syntax.', count($filesInfo)));
return Command::SUCCESS;
}
$io->warning(
sprintf(
'%d Twig files have valid syntax and %d contain errors.',
count($filesInfo) - $errors,
$errors
)
);
return Command::FAILURE;
}
private function renderException(
SymfonyStyle $output,
string $template,
Error $exception,
?string $file = null
): void {
$line = $exception->getTemplateLine();
if ($file) {
$output->text(sprintf('<error> ERROR </error> in %s (line %s)', $file, $line));
} else {
$output->text(sprintf('<error> ERROR </error> (line %s)', $line));
}
// If the line is not known (this might happen for deprecations if we fail at detecting the line for instance),
// we render the message without context, to ensure the message is displayed.
if ($line <= 0) {
$output->text(sprintf('<error> >> %s</error> ', $exception->getRawMessage()));
return;
}
foreach ($this->getContext($template, $line) as $lineNumber => $code) {
$output->text(sprintf(
'%s %-6s %s',
$lineNumber === $line ? '<error> >> </error>' : ' ',
$lineNumber,
$code
));
if ($lineNumber !== $line) {
continue;
}
$output->text(sprintf('<error> >> %s</error> ', $exception->getRawMessage()));
}
}
private function getContext(string $template, int $line, int $context = 3): array
{
$lines = explode("\n", $template);
$position = max(0, $line - $context);
$max = min(count($lines), $line - 1 + $context);
$result = [];
while ($position < $max) {
$result[$position + 1] = $lines[$position];
++$position;
}
return $result;
}
}

@ -0,0 +1,889 @@
<?php
/**
* Form management class, displays and processes forms
*
* Explanation of used terms:
* o work_path - original field path, eg. Servers/4/verbose
* o system_path - work_path modified so that it points to the first server,
* eg. Servers/1/verbose
* o translated_path - work_path modified for HTML field name, a path with
* slashes changed to hyphens, eg. Servers-4-verbose
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config\Forms\User\UserFormList;
use PhpMyAdmin\Html\MySQLDocumentation;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Util;
use function __;
use function array_flip;
use function array_keys;
use function array_search;
use function count;
use function explode;
use function function_exists;
use function gettype;
use function implode;
use function is_array;
use function is_bool;
use function is_numeric;
use function mb_substr;
use function preg_match;
use function settype;
use function sprintf;
use function str_replace;
use function trigger_error;
use function trim;
use const E_USER_WARNING;
/**
* Form management class, displays and processes forms
*/
class FormDisplay
{
/**
* ConfigFile instance
*
* @var ConfigFile
*/
private $configFile;
/**
* Form list
*
* @var Form[]
*/
private $forms = [];
/**
* Stores validation errors, indexed by paths
* [ Form_name ] is an array of form errors
* [path] is a string storing error associated with single field
*
* @var array
*/
private $errors = [];
/**
* Paths changed so that they can be used as HTML ids, indexed by paths
*
* @var array
*/
private $translatedPaths = [];
/**
* Server paths change indexes so we define maps from current server
* path to the first one, indexed by work path
*
* @var array
*/
private $systemPaths = [];
/**
* Tells whether forms have been validated
*
* @var bool
*/
private $isValidated = true;
/**
* Dictionary with user preferences keys
*
* @var array|null
*/
private $userprefsKeys;
/**
* Dictionary with disallowed user preferences keys
*
* @var array
*/
private $userprefsDisallow;
/** @var FormDisplayTemplate */
private $formDisplayTemplate;
/**
* @param ConfigFile $cf Config file instance
*/
public function __construct(ConfigFile $cf)
{
$this->formDisplayTemplate = new FormDisplayTemplate($GLOBALS['config']);
$this->configFile = $cf;
// initialize validators
Validator::getValidators($this->configFile);
}
/**
* Returns {@link ConfigFile} associated with this instance
*
* @return ConfigFile
*/
public function getConfigFile()
{
return $this->configFile;
}
/**
* Registers form in form manager
*
* @param string $formName Form name
* @param array $form Form data
* @param int $serverId 0 if new server, validation; >= 1 if editing a server
*/
public function registerForm($formName, array $form, $serverId = null): void
{
$this->forms[$formName] = new Form($formName, $form, $this->configFile, $serverId);
$this->isValidated = false;
foreach ($this->forms[$formName]->fields as $path) {
$workPath = $serverId === null
? $path
: str_replace('Servers/1/', 'Servers/' . $serverId . '/', $path);
$this->systemPaths[$workPath] = $path;
$this->translatedPaths[$workPath] = str_replace('/', '-', $workPath);
}
}
/**
* Processes forms, returns true on successful save
*
* @param bool $allowPartialSave allows for partial form saving
* on failed validation
* @param bool $checkFormSubmit whether check for $_POST['submit_save']
*/
public function process($allowPartialSave = true, $checkFormSubmit = true): bool
{
if ($checkFormSubmit && ! isset($_POST['submit_save'])) {
return false;
}
// save forms
if (count($this->forms) > 0) {
return $this->save(array_keys($this->forms), $allowPartialSave);
}
return false;
}
/**
* Runs validation for all registered forms
*/
private function validate(): void
{
if ($this->isValidated) {
return;
}
$paths = [];
$values = [];
foreach ($this->forms as $form) {
$paths[] = $form->name;
// collect values and paths
foreach ($form->fields as $path) {
$workPath = array_search($path, $this->systemPaths);
$values[$path] = $this->configFile->getValue($workPath);
$paths[] = $path;
}
}
// run validation
$errors = Validator::validate($this->configFile, $paths, $values, false);
// change error keys from canonical paths to work paths
if (is_array($errors) && count($errors) > 0) {
$this->errors = [];
foreach ($errors as $path => $errorList) {
$workPath = array_search($path, $this->systemPaths);
// field error
if (! $workPath) {
// form error, fix path
$workPath = $path;
}
$this->errors[$workPath] = $errorList;
}
}
$this->isValidated = true;
}
/**
* Outputs HTML for forms
*
* @param bool $showButtons whether show submit and reset button
* @param string $formAction action attribute for the form
* @param array|null $hiddenFields array of form hidden fields (key: field
* name)
*
* @return string HTML for forms
*/
public function getDisplay(
$showButtons = true,
$formAction = null,
$hiddenFields = null
) {
$js = [];
$jsDefault = [];
/**
* We do validation on page refresh when browser remembers field values,
* add a field with known value which will be used for checks.
*/
static $hasCheckPageRefresh = false;
if (! $hasCheckPageRefresh) {
$hasCheckPageRefresh = true;
}
$tabs = [];
foreach ($this->forms as $form) {
$tabs[$form->name] = Descriptions::get('Form_' . $form->name);
}
// validate only when we aren't displaying a "new server" form
$isNewServer = false;
foreach ($this->forms as $form) {
if ($form->index === 0) {
$isNewServer = true;
break;
}
}
if (! $isNewServer) {
$this->validate();
}
// user preferences
$this->loadUserprefsInfo();
$validators = Validator::getValidators($this->configFile);
$forms = [];
foreach ($this->forms as $key => $form) {
$this->formDisplayTemplate->group = 0;
$forms[$key] = [
'name' => $form->name,
'descriptions' => [
'name' => Descriptions::get('Form_' . $form->name, 'name'),
'desc' => Descriptions::get('Form_' . $form->name, 'desc'),
],
'errors' => $this->errors[$form->name] ?? null,
'fields_html' => '',
];
foreach ($form->fields as $field => $path) {
$workPath = array_search($path, $this->systemPaths);
$translatedPath = $this->translatedPaths[$workPath];
// always true/false for user preferences display
// otherwise null
$userPrefsAllow = isset($this->userprefsKeys[$path])
? ! isset($this->userprefsDisallow[$path])
: null;
// display input
$forms[$key]['fields_html'] .= $this->displayFieldInput(
$form,
$field,
$path,
$workPath,
$translatedPath,
true,
$userPrefsAllow,
$jsDefault
);
// register JS validators for this field
if (! isset($validators[$path])) {
continue;
}
$this->formDisplayTemplate->addJsValidate($translatedPath, $validators[$path], $js);
}
}
return $this->formDisplayTemplate->display([
'action' => $formAction,
'has_check_page_refresh' => $hasCheckPageRefresh,
'hidden_fields' => (array) $hiddenFields,
'tabs' => $tabs,
'forms' => $forms,
'show_buttons' => $showButtons,
'js_array' => $js,
'js_default' => $jsDefault,
]);
}
/**
* Prepares data for input field display and outputs HTML code
*
* @param Form $form Form object
* @param string $field field name as it appears in $form
* @param string $systemPath field path, eg. Servers/1/verbose
* @param string $workPath work path, eg. Servers/4/verbose
* @param string $translatedPath work path changed so that it can be
* used as XHTML id
* @param bool $showRestoreDefault whether show "restore default" button
* besides the input field
* @param bool|null $userPrefsAllow whether user preferences are enabled
* for this field (null - no support,
* true/false - enabled/disabled)
* @param array $jsDefault array which stores JavaScript code
* to be displayed
*
* @return string|null HTML for input field
*/
private function displayFieldInput(
Form $form,
$field,
$systemPath,
$workPath,
$translatedPath,
$showRestoreDefault,
$userPrefsAllow,
array &$jsDefault
) {
$name = Descriptions::get($systemPath);
$description = Descriptions::get($systemPath, 'desc');
$value = $this->configFile->get($workPath);
$valueDefault = $this->configFile->getDefault($systemPath);
$valueIsDefault = false;
if ($value === null || $value === $valueDefault) {
$value = $valueDefault;
$valueIsDefault = true;
}
$opts = [
'doc' => $this->getDocLink($systemPath),
'show_restore_default' => $showRestoreDefault,
'userprefs_allow' => $userPrefsAllow,
'userprefs_comment' => Descriptions::get($systemPath, 'cmt'),
];
if (isset($form->default[$systemPath])) {
$opts['setvalue'] = (string) $form->default[$systemPath];
}
if (isset($this->errors[$workPath])) {
$opts['errors'] = $this->errors[$workPath];
}
$type = '';
switch ($form->getOptionType($field)) {
case 'string':
$type = 'text';
break;
case 'short_string':
$type = 'short_text';
break;
case 'double':
case 'integer':
$type = 'number_text';
break;
case 'boolean':
$type = 'checkbox';
break;
case 'select':
$type = 'select';
$opts['values'] = $form->getOptionValueList($form->fields[$field]);
break;
case 'array':
$type = 'list';
$value = (array) $value;
$valueDefault = (array) $valueDefault;
break;
case 'group':
// :group:end is changed to :group:end:{unique id} in Form class
$htmlOutput = '';
if (mb_substr($field, 7, 4) !== 'end:') {
$htmlOutput .= $this->formDisplayTemplate->displayGroupHeader(
mb_substr($field, 7)
);
} else {
$this->formDisplayTemplate->displayGroupFooter();
}
return $htmlOutput;
case 'NULL':
trigger_error('Field ' . $systemPath . ' has no type', E_USER_WARNING);
return null;
}
// detect password fields
if (
$type === 'text'
&& (mb_substr($translatedPath, -9) === '-password'
|| mb_substr($translatedPath, -4) === 'pass'
|| mb_substr($translatedPath, -4) === 'Pass')
) {
$type = 'password';
}
// TrustedProxies requires changes before displaying
if ($systemPath === 'TrustedProxies') {
foreach ($value as $ip => &$v) {
if (preg_match('/^-\d+$/', $ip)) {
continue;
}
$v = $ip . ': ' . $v;
}
}
$this->setComments($systemPath, $opts);
// send default value to form's JS
$jsLine = '\'' . $translatedPath . '\': ';
switch ($type) {
case 'text':
case 'short_text':
case 'number_text':
case 'password':
$jsLine .= '\'' . Sanitize::escapeJsString($valueDefault) . '\'';
break;
case 'checkbox':
$jsLine .= $valueDefault ? 'true' : 'false';
break;
case 'select':
$valueDefaultJs = is_bool($valueDefault)
? (int) $valueDefault
: $valueDefault;
$jsLine .= '[\'' . Sanitize::escapeJsString($valueDefaultJs) . '\']';
break;
case 'list':
$val = $valueDefault;
if (isset($val['wrapper_params'])) {
unset($val['wrapper_params']);
}
$jsLine .= '\'' . Sanitize::escapeJsString(implode("\n", $val))
. '\'';
break;
}
$jsDefault[] = $jsLine;
return $this->formDisplayTemplate->displayInput(
$translatedPath,
$name,
$type,
$value,
$description,
$valueIsDefault,
$opts
);
}
/**
* Displays errors
*
* @return string|null HTML for errors
*/
public function displayErrors()
{
$this->validate();
if (count($this->errors) === 0) {
return null;
}
$htmlOutput = '';
foreach ($this->errors as $systemPath => $errorList) {
if (isset($this->systemPaths[$systemPath])) {
$name = Descriptions::get($this->systemPaths[$systemPath]);
} else {
$name = Descriptions::get('Form_' . $systemPath);
}
$htmlOutput .= $this->formDisplayTemplate->displayErrors($name, $errorList);
}
return $htmlOutput;
}
/**
* Reverts erroneous fields to their default values
*/
public function fixErrors(): void
{
$this->validate();
if (count($this->errors) === 0) {
return;
}
$cf = $this->configFile;
foreach (array_keys($this->errors) as $workPath) {
if (! isset($this->systemPaths[$workPath])) {
continue;
}
$canonicalPath = $this->systemPaths[$workPath];
$cf->set($workPath, $cf->getDefault($canonicalPath));
}
}
/**
* Validates select field and casts $value to correct type
*
* @param string|bool $value Current value
* @param array $allowed List of allowed values
*/
private function validateSelect(&$value, array $allowed): bool
{
$valueCmp = is_bool($value)
? (int) $value
: $value;
foreach (array_keys($allowed) as $vk) {
// equality comparison only if both values are numeric or not numeric
// (allows to skip 0 == 'string' equalling to true)
// or identity (for string-string)
if (! (($vk == $value && ! (is_numeric($valueCmp) xor is_numeric($vk))) || $vk === $value)) {
continue;
}
// keep boolean value as boolean
if (! is_bool($value)) {
// phpcs:ignore Generic.PHP.ForbiddenFunctions
settype($value, gettype($vk));
}
return true;
}
return false;
}
/**
* Validates and saves form data to session
*
* @param array|string $forms array of form names
* @param bool $allowPartialSave allows for partial form saving on
* failed validation
*/
public function save($forms, $allowPartialSave = true): bool
{
$result = true;
$forms = (array) $forms;
$values = [];
$toSave = [];
$isSetupScript = $GLOBALS['config']->get('is_setup');
if ($isSetupScript) {
$this->loadUserprefsInfo();
}
$this->errors = [];
foreach ($forms as $formName) {
if (! isset($this->forms[$formName])) {
continue;
}
$form = $this->forms[$formName];
// get current server id
$changeIndex = $form->index === 0
? $this->configFile->getServerCount() + 1
: false;
// grab POST values
foreach ($form->fields as $field => $systemPath) {
$workPath = array_search($systemPath, $this->systemPaths);
$key = $this->translatedPaths[$workPath];
$type = (string) $form->getOptionType($field);
// skip groups
if ($type === 'group') {
continue;
}
// ensure the value is set
if (! isset($_POST[$key])) {
// checkboxes aren't set by browsers if they're off
if ($type !== 'boolean') {
$this->errors[$form->name][] = sprintf(
__('Missing data for %s'),
'<i>' . Descriptions::get($systemPath) . '</i>'
);
$result = false;
continue;
}
$_POST[$key] = false;
}
// user preferences allow/disallow
if ($isSetupScript && isset($this->userprefsKeys[$systemPath])) {
if (isset($this->userprefsDisallow[$systemPath], $_POST[$key . '-userprefs-allow'])) {
unset($this->userprefsDisallow[$systemPath]);
} elseif (! isset($_POST[$key . '-userprefs-allow'])) {
$this->userprefsDisallow[$systemPath] = true;
}
}
// cast variables to correct type
switch ($type) {
case 'double':
$_POST[$key] = Util::requestString($_POST[$key]);
// phpcs:ignore Generic.PHP.ForbiddenFunctions
settype($_POST[$key], 'float');
break;
case 'boolean':
case 'integer':
if ($_POST[$key] !== '') {
$_POST[$key] = Util::requestString($_POST[$key]);
// phpcs:ignore Generic.PHP.ForbiddenFunctions
settype($_POST[$key], $type);
}
break;
case 'select':
$successfullyValidated = $this->validateSelect(
$_POST[$key],
$form->getOptionValueList($systemPath)
);
if (! $successfullyValidated) {
$this->errors[$workPath][] = __('Incorrect value!');
$result = false;
// "continue" for the $form->fields foreach-loop
continue 2;
}
break;
case 'string':
case 'short_string':
$_POST[$key] = Util::requestString($_POST[$key]);
break;
case 'array':
// eliminate empty values and ensure we have an array
$postValues = is_array($_POST[$key])
? $_POST[$key]
: explode("\n", $_POST[$key]);
$_POST[$key] = [];
$this->fillPostArrayParameters($postValues, $key);
break;
}
// now we have value with proper type
$values[$systemPath] = $_POST[$key];
if ($changeIndex !== false) {
$workPath = str_replace(
'Servers/' . $form->index . '/',
'Servers/' . $changeIndex . '/',
$workPath
);
}
$toSave[$workPath] = $systemPath;
}
}
// save forms
if (! $allowPartialSave && ! empty($this->errors)) {
// don't look for non-critical errors
$this->validate();
return $result;
}
foreach ($toSave as $workPath => $path) {
// TrustedProxies requires changes before saving
if ($path === 'TrustedProxies') {
$proxies = [];
$i = 0;
foreach ($values[$path] as $value) {
$matches = [];
$match = preg_match('/^(.+):(?:[ ]?)(\\w+)$/', $value, $matches);
if ($match) {
// correct 'IP: HTTP header' pair
$ip = trim($matches[1]);
$proxies[$ip] = trim($matches[2]);
} else {
// save also incorrect values
$proxies['-' . $i] = $value;
$i++;
}
}
$values[$path] = $proxies;
}
$this->configFile->set($workPath, $values[$path], $path);
}
if ($isSetupScript) {
$this->configFile->set(
'UserprefsDisallow',
array_keys($this->userprefsDisallow)
);
}
// don't look for non-critical errors
$this->validate();
return $result;
}
/**
* Tells whether form validation failed
*/
public function hasErrors(): bool
{
return count($this->errors) > 0;
}
/**
* Returns link to documentation
*
* @param string $path Path to documentation
*
* @return string
*/
public function getDocLink($path)
{
$test = mb_substr($path, 0, 6);
if ($test === 'Import' || $test === 'Export') {
return '';
}
return MySQLDocumentation::getDocumentationLink(
'config',
'cfg_' . $this->getOptName($path),
Sanitize::isSetup() ? '../' : './'
);
}
/**
* Changes path so it can be used in URLs
*
* @param string $path Path
*
* @return string
*/
private function getOptName($path)
{
return str_replace(['Servers/1/', '/'], ['Servers/', '_'], $path);
}
/**
* Fills out {@link userprefs_keys} and {@link userprefs_disallow}
*/
private function loadUserprefsInfo(): void
{
if ($this->userprefsKeys !== null) {
return;
}
$this->userprefsKeys = array_flip(UserFormList::getFields());
// read real config for user preferences display
$userPrefsDisallow = $GLOBALS['config']->get('is_setup')
? $this->configFile->get('UserprefsDisallow', [])
: $GLOBALS['cfg']['UserprefsDisallow'];
$this->userprefsDisallow = array_flip($userPrefsDisallow ?? []);
}
/**
* Sets field comments and warnings based on current environment
*
* @param string $systemPath Path to settings
* @param array $opts Chosen options
*/
private function setComments($systemPath, array &$opts): void
{
// RecodingEngine - mark unavailable types
if ($systemPath === 'RecodingEngine') {
$comment = '';
if (! function_exists('iconv')) {
$opts['values']['iconv'] .= ' (' . __('unavailable') . ')';
$comment = sprintf(
__('"%s" requires %s extension'),
'iconv',
'iconv'
);
}
if (! function_exists('recode_string')) {
$opts['values']['recode'] .= ' (' . __('unavailable') . ')';
$comment .= ($comment ? ', ' : '') . sprintf(
__('"%s" requires %s extension'),
'recode',
'recode'
);
}
/* mbstring is always there thanks to polyfill */
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
// ZipDump, GZipDump, BZipDump - check function availability
if ($systemPath === 'ZipDump' || $systemPath === 'GZipDump' || $systemPath === 'BZipDump') {
$comment = '';
$funcs = [
'ZipDump' => [
'zip_open',
'gzcompress',
],
'GZipDump' => [
'gzopen',
'gzencode',
],
'BZipDump' => [
'bzopen',
'bzcompress',
],
];
if (! function_exists($funcs[$systemPath][0])) {
$comment = sprintf(
__(
'Compressed import will not work due to missing function %s.'
),
$funcs[$systemPath][0]
);
}
if (! function_exists($funcs[$systemPath][1])) {
$comment .= ($comment ? '; ' : '') . sprintf(
__(
'Compressed export will not work due to missing function %s.'
),
$funcs[$systemPath][1]
);
}
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
if ($GLOBALS['config']->get('is_setup')) {
return;
}
if ($systemPath !== 'MaxDbList' && $systemPath !== 'MaxTableList' && $systemPath !== 'QueryHistoryMax') {
return;
}
$opts['comment'] = sprintf(
__('maximum %s'),
$GLOBALS['cfg'][$systemPath]
);
}
/**
* Copy items of an array to $_POST variable
*
* @param array $postValues List of parameters
* @param string $key Array key
*/
private function fillPostArrayParameters(array $postValues, $key): void
{
foreach ($postValues as $v) {
$v = Util::requestString($v);
if ($v === '') {
continue;
}
$_POST[$key][] = $v;
}
}
}

@ -0,0 +1,177 @@
<?php
/**
* Form templates
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Config;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Template;
use function array_shift;
use function implode;
/**
* PhpMyAdmin\Config\FormDisplayTemplate class
*/
class FormDisplayTemplate
{
/** @var int */
public $group;
/** @var Config */
protected $config;
/** @var Template */
public $template;
/**
* @param Config $config Config instance
*/
public function __construct(Config $config)
{
$this->config = $config;
$this->template = new Template();
}
/**
* Displays input field
*
* $opts keys:
* o doc - (string) documentation link
* o errors - error array
* o setvalue - (string) shows button allowing to set predefined value
* o show_restore_default - (boolean) whether show "restore default" button
* o userprefs_allow - whether user preferences are enabled for this field
* (null - no support, true/false - enabled/disabled)
* o userprefs_comment - (string) field comment
* o values - key - value pairs for <select> fields
* o values_escaped - (boolean) tells whether values array is already escaped
* (defaults to false)
* o values_disabled - (array)list of disabled values (keys from values)
* o comment - (string) tooltip comment
* o comment_warning - (bool) whether this comments warns about something
*
* @param string $path config option path
* @param string $name config option name
* @param string $type type of config option
* @param mixed $value current value
* @param string $description verbose description
* @param bool $valueIsDefault whether value is default
* @param array|null $opts see above description
*/
public function displayInput(
$path,
$name,
$type,
$value,
$description = '',
$valueIsDefault = true,
$opts = null
): string {
$isSetupScript = $this->config->get('is_setup');
$optionIsDisabled = ! $isSetupScript && isset($opts['userprefs_allow']) && ! $opts['userprefs_allow'];
$trClass = $this->group > 0 ? 'group-field group-field-' . $this->group : '';
if (isset($opts['setvalue']) && $opts['setvalue'] === ':group') {
unset($opts['setvalue']);
$this->group++;
$trClass = 'group-header-field group-header-' . $this->group;
}
return $this->template->render('config/form_display/input', [
'is_setup' => $isSetupScript,
'allows_customization' => $opts['userprefs_allow'] ?? null,
'path' => $path,
'has_errors' => isset($opts['errors']) && ! empty($opts['errors']),
'errors' => $opts['errors'] ?? [],
'show_restore_default' => $opts['show_restore_default'] ?? null,
'set_value' => $opts['setvalue'] ?? null,
'tr_class' => $trClass,
'name' => $name,
'doc' => $opts['doc'] ?? '',
'option_is_disabled' => $optionIsDisabled,
'description' => $description,
'comment' => $opts['userprefs_comment'] ?? null,
'type' => $type,
'value' => $value,
'value_is_default' => $valueIsDefault,
'select_values' => $opts['values'] ?? [],
'select_values_disabled' => $opts['values_disabled'] ?? [],
]);
}
/**
* Display group header
*
* @param string $headerText Text of header
*/
public function displayGroupHeader(string $headerText): string
{
$this->group++;
if ($headerText === '') {
return '';
}
$colspan = $this->config->get('is_setup') ? 3 : 2;
return $this->template->render('config/form_display/group_header', [
'group' => $this->group,
'colspan' => $colspan,
'header_text' => $headerText,
]);
}
/**
* Display group footer
*/
public function displayGroupFooter(): void
{
$this->group--;
}
/**
* Appends JS validation code to $js_array
*
* @param string $fieldId ID of field to validate
* @param string|array $validators validators callback
* @param array $jsArray will be updated with javascript code
*/
public function addJsValidate($fieldId, $validators, array &$jsArray): void
{
foreach ((array) $validators as $validator) {
$validator = (array) $validator;
$vName = array_shift($validator);
$vArgs = [];
foreach ($validator as $arg) {
$vArgs[] = Sanitize::escapeJsString($arg);
}
$vArgs = $vArgs ? ", ['" . implode("', '", $vArgs) . "']" : '';
$jsArray[] = "registerFieldValidator('" . $fieldId . "', '" . $vName . "', true" . $vArgs . ')';
}
}
/**
* Displays error list
*
* @param string $name Name of item with errors
* @param array $errorList List of errors to show
*
* @return string HTML for errors
*/
public function displayErrors($name, array $errorList): string
{
return $this->template->render('config/form_display/errors', [
'name' => $name,
'error_list' => $errorList,
]);
}
public function display(array $data): string
{
return $this->template->render('config/form_display/display', $data);
}
}

@ -0,0 +1,86 @@
<?php
/**
* Base class for preferences.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\FormDisplay;
use function is_int;
/**
* Base form for user preferences
*/
abstract class BaseForm extends FormDisplay
{
/**
* @param ConfigFile $cf Config file instance
* @param int|null $serverId 0 if new server, validation; >= 1 if editing a server
*/
final public function __construct(ConfigFile $cf, $serverId = null)
{
parent::__construct($cf);
foreach (static::getForms() as $formName => $form) {
$this->registerForm($formName, $form, $serverId);
}
}
/**
* List of available forms, each form is described as an array of fields to display.
* Fields MUST have their counterparts in the $cfg array.
*
* To define form field, use the notation below:
* $forms['Form group']['Form name'] = array('Option/path');
*
* You can assign default values set by special button ("set value: ..."), eg.:
* 'Servers/1/pmadb' => 'phpmyadmin'
*
* To group options, use:
* ':group:' . __('group name') // just define a group
* or
* 'option' => ':group' // group starting from this option
* End group blocks with:
* ':group:end'
*
* @return array
*
* @todo This should be abstract, but that does not work in PHP 5
*/
public static function getForms()
{
return [];
}
/**
* Returns list of fields used in the form.
*
* @return string[]
*/
public static function getFields()
{
$names = [];
foreach (static::getForms() as $form) {
foreach ($form as $k => $v) {
$names[] = is_int($k) ? $v : $k;
}
}
return $names;
}
/**
* Returns name of the form
*
* @return string
*
* @todo This should be abstract, but that does not work in PHP 5
*/
public static function getName()
{
return '';
}
}

@ -0,0 +1,12 @@
<?php
/**
* User preferences form
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms\Page;
class ExportForm extends \PhpMyAdmin\Config\Forms\User\ExportForm
{
}

@ -0,0 +1,12 @@
<?php
/**
* User preferences form
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms\Page;
class NaviForm extends \PhpMyAdmin\Config\Forms\User\NaviForm
{
}

@ -0,0 +1,26 @@
<?php
/**
* User preferences form
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms\Setup;
use PhpMyAdmin\Config\Forms\BaseForm;
class ConfigForm extends BaseForm
{
/**
* @return array
*/
public static function getForms()
{
return [
'Config' => [
'DefaultLang',
'ServerDefault',
],
];
}
}

@ -0,0 +1,155 @@
<?php
/**
* User preferences form
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
use function __;
class ExportForm extends BaseForm
{
/**
* @return array
*/
public static function getForms()
{
// phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified,Squiz.Arrays.ArrayDeclaration.NoKeySpecified
return [
'Export_defaults' => [
'Export/method',
':group:' . __('Quick'),
'Export/quick_export_onserver',
'Export/quick_export_onserver_overwrite',
':group:end',
':group:' . __('Custom'),
'Export/format',
'Export/compression',
'Export/charset',
'Export/lock_tables',
'Export/as_separate_files',
'Export/asfile' => ':group',
'Export/onserver',
'Export/onserver_overwrite',
':group:end',
'Export/file_template_table',
'Export/file_template_database',
'Export/file_template_server',
],
'Sql' => [
'Export/sql_include_comments' => ':group',
'Export/sql_dates',
'Export/sql_relation',
'Export/sql_mime',
':group:end',
'Export/sql_use_transaction',
'Export/sql_disable_fk',
'Export/sql_views_as_tables',
'Export/sql_metadata',
'Export/sql_compatibility',
'Export/sql_structure_or_data',
':group:' . __('Structure'),
'Export/sql_drop_database',
'Export/sql_create_database',
'Export/sql_drop_table',
'Export/sql_create_table' => ':group',
'Export/sql_if_not_exists',
'Export/sql_auto_increment',
':group:end',
'Export/sql_create_view' => ':group',
'Export/sql_view_current_user',
'Export/sql_or_replace_view',
':group:end',
'Export/sql_procedure_function',
'Export/sql_create_trigger',
'Export/sql_backquotes',
':group:end',
':group:' . __('Data'),
'Export/sql_delayed',
'Export/sql_ignore',
'Export/sql_type',
'Export/sql_insert_syntax',
'Export/sql_max_query_size',
'Export/sql_hex_for_binary',
'Export/sql_utc_time',
],
'CodeGen' => ['Export/codegen_format'],
'Csv' => [
':group:' . __('CSV'),
'Export/csv_separator',
'Export/csv_enclosed',
'Export/csv_escaped',
'Export/csv_terminated',
'Export/csv_null',
'Export/csv_removeCRLF',
'Export/csv_columns',
':group:end',
':group:' . __('CSV for MS Excel'),
'Export/excel_null',
'Export/excel_removeCRLF',
'Export/excel_columns',
'Export/excel_edition',
],
'Latex' => [
'Export/latex_caption',
'Export/latex_structure_or_data',
':group:' . __('Structure'),
'Export/latex_structure_caption',
'Export/latex_structure_continued_caption',
'Export/latex_structure_label',
'Export/latex_relation',
'Export/latex_comments',
'Export/latex_mime',
':group:end',
':group:' . __('Data'),
'Export/latex_columns',
'Export/latex_data_caption',
'Export/latex_data_continued_caption',
'Export/latex_data_label',
'Export/latex_null',
],
'Microsoft_Office' => [
':group:' . __('Microsoft Word 2000'),
'Export/htmlword_structure_or_data',
'Export/htmlword_null',
'Export/htmlword_columns',
],
'Open_Document' => [
':group:' . __('OpenDocument Spreadsheet'),
'Export/ods_columns',
'Export/ods_null',
':group:end',
':group:' . __('OpenDocument Text'),
'Export/odt_structure_or_data',
':group:' . __('Structure'),
'Export/odt_relation',
'Export/odt_comments',
'Export/odt_mime',
':group:end',
':group:' . __('Data'),
'Export/odt_columns',
'Export/odt_null',
],
'Texy' => [
'Export/texytext_structure_or_data',
':group:' . __('Data'),
'Export/texytext_null',
'Export/texytext_columns',
],
];
// phpcs:enable
}
/**
* @return string
*/
public static function getName()
{
return __('Export');
}
}

@ -0,0 +1,50 @@
<?php
/**
* User preferences form
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config\Forms\User;
use PhpMyAdmin\Config\Forms\BaseForm;
use function __;
class SqlForm extends BaseForm
{
/**
* @return array
*/
public static function getForms()
{
return [
'Sql_queries' => [
'ShowSQL',
'Confirm',
'QueryHistoryMax',
'IgnoreMultiSubmitErrors',
'MaxCharactersInDisplayedSQL',
'RetainQueryBox',
'CodemirrorEnable',
'LintEnable',
'EnableAutocompleteForTablesAndColumns',
'DefaultForeignKeyChecks',
],
'Sql_box' => [
'SQLQuery/Edit',
'SQLQuery/Explain',
'SQLQuery/ShowAsPHP',
'SQLQuery/Refresh',
],
];
}
/**
* @return string
*/
public static function getName()
{
return __('SQL queries');
}
}

@ -0,0 +1,553 @@
<?php
/**
* Server config checks management
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Core;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Setup\Index as SetupIndex;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function count;
use function function_exists;
use function htmlspecialchars;
use function implode;
use function ini_get;
use function preg_match;
use function sprintf;
use function strlen;
/**
* Performs various compatibility, security and consistency checks on current config
*
* Outputs results to message list, must be called between SetupIndex::messagesBegin()
* and SetupIndex::messagesEnd()
*/
class ServerConfigChecks
{
/** @var ConfigFile configurations being checked */
protected $cfg;
/**
* @param ConfigFile $cfg Configuration
*/
public function __construct(ConfigFile $cfg)
{
$this->cfg = $cfg;
}
/**
* Perform config checks
*/
public function performConfigChecks(): void
{
$blowfishSecret = $this->cfg->get('blowfish_secret');
$blowfishSecretSet = false;
$cookieAuthUsed = false;
[$cookieAuthUsed, $blowfishSecret, $blowfishSecretSet] = $this->performConfigChecksServers(
$cookieAuthUsed,
$blowfishSecret,
$blowfishSecretSet
);
$this->performConfigChecksCookieAuthUsed($cookieAuthUsed, $blowfishSecretSet, $blowfishSecret);
// $cfg['AllowArbitraryServer']
// should be disabled
if ($this->cfg->getValue('AllowArbitraryServer')) {
$sAllowArbitraryServerWarn = sprintf(
__(
'This %soption%s should be disabled as it allows attackers to '
. 'bruteforce login to any MySQL server. If you feel this is necessary, '
. 'use %srestrict login to MySQL server%s or %strusted proxies list%s. '
. 'However, IP-based protection with trusted proxies list may not be '
. 'reliable if your IP belongs to an ISP where thousands of users, '
. 'including you, are connected to.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]'
);
SetupIndex::messagesSet(
'notice',
'AllowArbitraryServer',
Descriptions::get('AllowArbitraryServer'),
Sanitize::sanitizeMessage($sAllowArbitraryServerWarn)
);
}
$this->performConfigChecksLoginCookie();
$sDirectoryNotice = __(
'This value should be double checked to ensure that this directory is '
. 'neither world accessible nor readable or writable by other users on '
. 'your server.'
);
// $cfg['SaveDir']
// should not be world-accessible
if ($this->cfg->getValue('SaveDir') != '') {
SetupIndex::messagesSet(
'notice',
'SaveDir',
Descriptions::get('SaveDir'),
Sanitize::sanitizeMessage($sDirectoryNotice)
);
}
// $cfg['TempDir']
// should not be world-accessible
if ($this->cfg->getValue('TempDir') != '') {
SetupIndex::messagesSet(
'notice',
'TempDir',
Descriptions::get('TempDir'),
Sanitize::sanitizeMessage($sDirectoryNotice)
);
}
$this->performConfigChecksZips();
}
/**
* Check config of servers
*
* @param bool $cookieAuthUsed Cookie auth is used
* @param string $blowfishSecret Blowfish secret
* @param bool $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServers(
$cookieAuthUsed,
$blowfishSecret,
$blowfishSecretSet
) {
$serverCnt = $this->cfg->getServerCount();
$isCookieAuthUsed = (int) $cookieAuthUsed;
for ($i = 1; $i <= $serverCnt; $i++) {
$cookieAuthServer = ($this->cfg->getValue('Servers/' . $i . '/auth_type') === 'cookie');
$isCookieAuthUsed |= (int) $cookieAuthServer;
$serverName = $this->performConfigChecksServersGetServerName(
$this->cfg->getServerName($i),
$i
);
$serverName = htmlspecialchars($serverName);
[$blowfishSecret, $blowfishSecretSet] = $this->performConfigChecksServersSetBlowfishSecret(
$blowfishSecret,
$cookieAuthServer,
$blowfishSecretSet
);
// $cfg['Servers'][$i]['ssl']
// should be enabled if possible
if (! $this->cfg->getValue('Servers/' . $i . '/ssl')) {
$title = Descriptions::get('Servers/1/ssl') . ' (' . $serverName . ')';
SetupIndex::messagesSet(
'notice',
'Servers/' . $i . '/ssl',
$title,
__(
'You should use SSL connections if your database server supports it.'
)
);
}
$sSecurityInfoMsg = Sanitize::sanitizeMessage(sprintf(
__(
'If you feel this is necessary, use additional protection settings - '
. '%1$shost authentication%2$s settings and %3$strusted proxies list%4$s. '
. 'However, IP-based protection may not be reliable if your IP belongs '
. 'to an ISP where thousands of users, including you, are connected to.'
),
'[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server_config]',
'[/a]',
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]'
));
// $cfg['Servers'][$i]['auth_type']
// warn about full user credentials if 'auth_type' is 'config'
if (
$this->cfg->getValue('Servers/' . $i . '/auth_type') === 'config'
&& $this->cfg->getValue('Servers/' . $i . '/user') != ''
&& $this->cfg->getValue('Servers/' . $i . '/password') != ''
) {
$title = Descriptions::get('Servers/1/auth_type')
. ' (' . $serverName . ')';
SetupIndex::messagesSet(
'notice',
'Servers/' . $i . '/auth_type',
$title,
Sanitize::sanitizeMessage(sprintf(
__(
'You set the [kbd]config[/kbd] authentication type and included '
. 'username and password for auto-login, which is not a desirable '
. 'option for live hosts. Anyone who knows or guesses your phpMyAdmin '
. 'URL can directly access your phpMyAdmin panel. Set %1$sauthentication '
. 'type%2$s to [kbd]cookie[/kbd] or [kbd]http[/kbd].'
),
'[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server]',
'[/a]'
))
. ' ' . $sSecurityInfoMsg
);
}
// $cfg['Servers'][$i]['AllowRoot']
// $cfg['Servers'][$i]['AllowNoPassword']
// serious security flaw
if (
! $this->cfg->getValue('Servers/' . $i . '/AllowRoot')
|| ! $this->cfg->getValue('Servers/' . $i . '/AllowNoPassword')
) {
continue;
}
$title = Descriptions::get('Servers/1/AllowNoPassword')
. ' (' . $serverName . ')';
SetupIndex::messagesSet(
'notice',
'Servers/' . $i . '/AllowNoPassword',
$title,
__('You allow for connecting to the server without a password.')
. ' ' . $sSecurityInfoMsg
);
}
return [
(bool) $isCookieAuthUsed,
$blowfishSecret,
$blowfishSecretSet,
];
}
/**
* Set blowfish secret
*
* @param string|null $blowfishSecret Blowfish secret
* @param bool $cookieAuthServer Cookie auth is used
* @param bool $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServersSetBlowfishSecret(
$blowfishSecret,
$cookieAuthServer,
$blowfishSecretSet
): array {
if ($cookieAuthServer && $blowfishSecret === null) {
$blowfishSecretSet = true;
$this->cfg->set('blowfish_secret', Util::generateRandom(32));
}
return [
$blowfishSecret,
$blowfishSecretSet,
];
}
/**
* Define server name
*
* @param string $serverName Server name
* @param int $serverId Server id
*
* @return string Server name
*/
protected function performConfigChecksServersGetServerName(
$serverName,
$serverId
) {
if ($serverName === 'localhost') {
return $serverName . ' [' . $serverId . ']';
}
return $serverName;
}
/**
* Perform config checks for zip part.
*/
protected function performConfigChecksZips(): void
{
$this->performConfigChecksServerGZipdump();
$this->performConfigChecksServerBZipdump();
$this->performConfigChecksServersZipdump();
}
/**
* Perform config checks for zip part.
*/
protected function performConfigChecksServersZipdump(): void
{
// $cfg['ZipDump']
// requires zip_open in import
if ($this->cfg->getValue('ZipDump') && ! $this->functionExists('zip_open')) {
SetupIndex::messagesSet(
'error',
'ZipDump_import',
Descriptions::get('ZipDump'),
Sanitize::sanitizeMessage(sprintf(
__(
'%sZip decompression%s requires functions (%s) which are unavailable on this system.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
'[/a]',
'zip_open'
))
);
}
// $cfg['ZipDump']
// requires gzcompress in export
if (! $this->cfg->getValue('ZipDump') || $this->functionExists('gzcompress')) {
return;
}
SetupIndex::messagesSet(
'error',
'ZipDump_export',
Descriptions::get('ZipDump'),
Sanitize::sanitizeMessage(sprintf(
__(
'%sZip compression%s requires functions (%s) which are unavailable on this system.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
'[/a]',
'gzcompress'
))
);
}
/**
* Check config of servers
*
* @param bool $cookieAuthUsed Cookie auth is used
* @param bool $blowfishSecretSet Blowfish secret set
* @param string $blowfishSecret Blowfish secret
*/
protected function performConfigChecksCookieAuthUsed(
$cookieAuthUsed,
$blowfishSecretSet,
$blowfishSecret
): void {
// $cfg['blowfish_secret']
// it's required for 'cookie' authentication
if (! $cookieAuthUsed) {
return;
}
if ($blowfishSecretSet) {
// 'cookie' auth used, blowfish_secret was generated
SetupIndex::messagesSet(
'notice',
'blowfish_secret_created',
Descriptions::get('blowfish_secret'),
Sanitize::sanitizeMessage(__(
'You didn\'t have blowfish secret set and have enabled '
. '[kbd]cookie[/kbd] authentication, so a key was automatically '
. 'generated for you. It is used to encrypt cookies; you don\'t need to '
. 'remember it.'
))
);
return;
}
$blowfishWarnings = [];
// check length
if (strlen($blowfishSecret) < 32) {
// too short key
$blowfishWarnings[] = __('Key is too short, it should have at least 32 characters.');
}
// check used characters
$hasDigits = (bool) preg_match('/\d/', $blowfishSecret);
$hasChars = (bool) preg_match('/\S/', $blowfishSecret);
$hasNonword = (bool) preg_match('/\W/', $blowfishSecret);
if (! $hasDigits || ! $hasChars || ! $hasNonword) {
$blowfishWarnings[] = Sanitize::sanitizeMessage(
__(
'Key should contain letters, numbers [em]and[/em] special characters.'
)
);
}
if (empty($blowfishWarnings)) {
return;
}
SetupIndex::messagesSet(
'error',
'blowfish_warnings' . count($blowfishWarnings),
Descriptions::get('blowfish_secret'),
implode('<br>', $blowfishWarnings)
);
}
/**
* Check configuration for login cookie
*/
protected function performConfigChecksLoginCookie(): void
{
// $cfg['LoginCookieValidity']
// value greater than session.gc_maxlifetime will cause
// random session invalidation after that time
$loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
if ($loginCookieValidity > ini_get('session.gc_maxlifetime')) {
SetupIndex::messagesSet(
'error',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitizeMessage(sprintf(
__(
'%1$sLogin cookie validity%2$s greater than %3$ssession.gc_maxlifetime%4$s may '
. 'cause random session invalidation (currently session.gc_maxlifetime '
. 'is %5$d).'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]',
'[a@' . Core::getPHPDocLink('session.configuration.php#ini.session.gc-maxlifetime') . ']',
'[/a]',
ini_get('session.gc_maxlifetime')
))
);
}
// $cfg['LoginCookieValidity']
// should be at most 1800 (30 min)
if ($loginCookieValidity > 1800) {
SetupIndex::messagesSet(
'notice',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitizeMessage(sprintf(
__(
'%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) '
. 'at most. Values larger than 1800 may pose a security risk such as '
. 'impersonation.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]'
))
);
}
// $cfg['LoginCookieValidity']
// $cfg['LoginCookieStore']
// LoginCookieValidity must be less or equal to LoginCookieStore
if (
($this->cfg->getValue('LoginCookieStore') == 0)
|| ($loginCookieValidity <= $this->cfg->getValue('LoginCookieStore'))
) {
return;
}
SetupIndex::messagesSet(
'error',
'LoginCookieValidity',
Descriptions::get('LoginCookieValidity'),
Sanitize::sanitizeMessage(sprintf(
__(
'If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s '
. 'is not 0, %sLogin cookie validity%s must be set to a value less or '
. 'equal to it.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]',
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
'[/a]'
))
);
}
/**
* Check GZipDump configuration
*/
protected function performConfigChecksServerBZipdump(): void
{
// $cfg['BZipDump']
// requires bzip2 functions
if (
! $this->cfg->getValue('BZipDump')
|| ($this->functionExists('bzopen') && $this->functionExists('bzcompress'))
) {
return;
}
$functions = $this->functionExists('bzopen')
? '' :
'bzopen';
$functions .= $this->functionExists('bzcompress')
? ''
: ($functions ? ', ' : '') . 'bzcompress';
SetupIndex::messagesSet(
'error',
'BZipDump',
Descriptions::get('BZipDump'),
Sanitize::sanitizeMessage(
sprintf(
__(
'%1$sBzip2 compression and decompression%2$s requires functions (%3$s) which '
. 'are unavailable on this system.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
'[/a]',
$functions
)
)
);
}
/**
* Check GZipDump configuration
*/
protected function performConfigChecksServerGZipdump(): void
{
// $cfg['GZipDump']
// requires zlib functions
if (
! $this->cfg->getValue('GZipDump')
|| ($this->functionExists('gzopen') && $this->functionExists('gzencode'))
) {
return;
}
SetupIndex::messagesSet(
'error',
'GZipDump',
Descriptions::get('GZipDump'),
Sanitize::sanitizeMessage(sprintf(
__(
'%1$sGZip compression and decompression%2$s requires functions (%3$s) which '
. 'are unavailable on this system.'
),
'[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
'[/a]',
'gzencode'
))
);
}
/**
* Wrapper around function_exists to allow mock in test
*
* @param string $name Function name
*/
protected function functionExists($name): bool
{
return function_exists($name);
}
}

@ -0,0 +1,205 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Config\Settings;
use function in_array;
// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
/**
* @psalm-immutable
*/
final class Console
{
/** @var bool */
public $StartHistory;
/** @var bool */
public $AlwaysExpand;
/** @var bool */
public $CurrentQuery;
/** @var bool */
public $EnterExecutes;
/** @var bool */
public $DarkTheme;
/**
* @var string
* @psalm-var 'info'|'show'|'collapse'
*/
public $Mode;
/**
* @var int
* @psalm-var positive-int
*/
public $Height;
/** @var bool */
public $GroupQueries;
/**
* @var string
* @psalm-var 'exec'|'time'|'count'
*/
public $OrderBy;
/**
* @var string
* @psalm-var 'asc'|'desc'
*/
public $Order;
/**
* @param mixed[] $console
*/
public function __construct(array $console = [])
{
$this->StartHistory = $this->setStartHistory($console);
$this->AlwaysExpand = $this->setAlwaysExpand($console);
$this->CurrentQuery = $this->setCurrentQuery($console);
$this->EnterExecutes = $this->setEnterExecutes($console);
$this->DarkTheme = $this->setDarkTheme($console);
$this->Mode = $this->setMode($console);
$this->Height = $this->setHeight($console);
$this->GroupQueries = $this->setGroupQueries($console);
$this->OrderBy = $this->setOrderBy($console);
$this->Order = $this->setOrder($console);
}
/**
* @param mixed[] $console
*/
private function setStartHistory(array $console): bool
{
if (isset($console['StartHistory'])) {
return (bool) $console['StartHistory'];
}
return false;
}
/**
* @param mixed[] $console
*/
private function setAlwaysExpand(array $console): bool
{
if (isset($console['AlwaysExpand'])) {
return (bool) $console['AlwaysExpand'];
}
return false;
}
/**
* @param mixed[] $console
*/
private function setCurrentQuery(array $console): bool
{
if (isset($console['CurrentQuery'])) {
return (bool) $console['CurrentQuery'];
}
return true;
}
/**
* @param mixed[] $console
*/
private function setEnterExecutes(array $console): bool
{
if (isset($console['EnterExecutes'])) {
return (bool) $console['EnterExecutes'];
}
return false;
}
/**
* @param mixed[] $console
*/
private function setDarkTheme(array $console): bool
{
if (isset($console['DarkTheme'])) {
return (bool) $console['DarkTheme'];
}
return false;
}
/**
* @param mixed[] $console
*
* @psalm-return 'info'|'show'|'collapse'
*/
private function setMode(array $console): string
{
if (isset($console['Mode']) && in_array($console['Mode'], ['show', 'collapse'], true)) {
return $console['Mode'];
}
return 'info';
}
/**
* @param mixed[] $console
*
* @psalm-return positive-int
*/
private function setHeight(array $console): int
{
if (isset($console['Height'])) {
$height = (int) $console['Height'];
if ($height >= 1) {
return $height;
}
}
return 92;
}
/**
* @param mixed[] $console
*/
private function setGroupQueries(array $console): bool
{
if (isset($console['GroupQueries'])) {
return (bool) $console['GroupQueries'];
}
return false;
}
/**
* @param mixed[] $console
*
* @psalm-return 'exec'|'time'|'count'
*/
private function setOrderBy(array $console): string
{
if (isset($console['OrderBy']) && in_array($console['OrderBy'], ['time', 'count'], true)) {
return $console['OrderBy'];
}
return 'exec';
}
/**
* @param mixed[] $console
*
* @psalm-return 'asc'|'desc'
*/
private function setOrder(array $console): string
{
if (isset($console['Order']) && $console['Order'] === 'desc') {
return 'desc';
}
return 'asc';
}
}

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Config\Settings;
/**
* @psalm-immutable
*/
final class Debug
{
/**
* Output executed queries and their execution times.
*
* @var bool
*/
public $sql;
/**
* Log executed queries and their execution times to syslog.
*
* @var bool
*/
public $sqllog;
/**
* Enable to let server present itself as demo server.
*
* @var bool
*/
public $demo;
/**
* Enable Simple two-factor authentication.
*
* @var bool
*/
public $simple2fa;
/**
* @param mixed[] $debug
*/
public function __construct(array $debug = [])
{
$this->sql = $this->setSql($debug);
$this->sqllog = $this->setSqlLog($debug);
$this->demo = $this->setDemo($debug);
$this->simple2fa = $this->setSimple2fa($debug);
}
/**
* @param mixed[] $debug
*/
private function setSql(array $debug): bool
{
return isset($debug['sql']) && $debug['sql'];
}
/**
* @param mixed[] $debug
*/
private function setSqlLog(array $debug): bool
{
return isset($debug['sqllog']) && $debug['sqllog'];
}
/**
* @param mixed[] $debug
*/
private function setDemo(array $debug): bool
{
return isset($debug['demo']) && $debug['demo'];
}
/**
* @param mixed[] $debug
*/
private function setSimple2fa(array $debug): bool
{
return isset($debug['simple2fa']) && $debug['simple2fa'];
}
}

@ -0,0 +1,489 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Config\Settings;
// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
use function in_array;
/**
* @psalm-immutable
*/
final class Import
{
/**
* @var string
* @psalm-var 'csv'|'docsql'|'ldi'|'sql'
*/
public $format;
/**
* Default charset for import.
*
* @var string
*/
public $charset;
/** @var bool */
public $allow_interrupt;
/**
* @var int
* @psalm-var 0|positive-int
*/
public $skip_queries;
/**
* @var string
* @psalm-var 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL'
*/
public $sql_compatibility;
/** @var bool */
public $sql_no_auto_value_on_zero;
/** @var bool */
public $sql_read_as_multibytes;
/** @var bool */
public $csv_replace;
/** @var bool */
public $csv_ignore;
/** @var string */
public $csv_terminated;
/** @var string */
public $csv_enclosed;
/** @var string */
public $csv_escaped;
/** @var string */
public $csv_new_line;
/** @var string */
public $csv_columns;
/** @var bool */
public $csv_col_names;
/** @var bool */
public $ldi_replace;
/** @var bool */
public $ldi_ignore;
/** @var string */
public $ldi_terminated;
/** @var string */
public $ldi_enclosed;
/** @var string */
public $ldi_escaped;
/** @var string */
public $ldi_new_line;
/** @var string */
public $ldi_columns;
/**
* 'auto' for auto-detection, true or false for forcing
*
* @var string|bool
* @psalm-var 'auto'|bool
*/
public $ldi_local_option;
/** @var bool */
public $ods_col_names;
/** @var bool */
public $ods_empty_rows;
/** @var bool */
public $ods_recognize_percentages;
/** @var bool */
public $ods_recognize_currency;
/**
* @param array<int|string, mixed> $import
*/
public function __construct(array $import = [])
{
$this->format = $this->setFormat($import);
$this->charset = $this->setCharset($import);
$this->allow_interrupt = $this->setAllowInterrupt($import);
$this->skip_queries = $this->setSkipQueries($import);
$this->sql_compatibility = $this->setSqlCompatibility($import);
$this->sql_no_auto_value_on_zero = $this->setSqlNoAutoValueOnZero($import);
$this->sql_read_as_multibytes = $this->setSqlReadAsMultibytes($import);
$this->csv_replace = $this->setCsvReplace($import);
$this->csv_ignore = $this->setCsvIgnore($import);
$this->csv_terminated = $this->setCsvTerminated($import);
$this->csv_enclosed = $this->setCsvEnclosed($import);
$this->csv_escaped = $this->setCsvEscaped($import);
$this->csv_new_line = $this->setCsvNewLine($import);
$this->csv_columns = $this->setCsvColumns($import);
$this->csv_col_names = $this->setCsvColNames($import);
$this->ldi_replace = $this->setLdiReplace($import);
$this->ldi_ignore = $this->setLdiIgnore($import);
$this->ldi_terminated = $this->setLdiTerminated($import);
$this->ldi_enclosed = $this->setLdiEnclosed($import);
$this->ldi_escaped = $this->setLdiEscaped($import);
$this->ldi_new_line = $this->setLdiNewLine($import);
$this->ldi_columns = $this->setLdiColumns($import);
$this->ldi_local_option = $this->setLdiLocalOption($import);
$this->ods_col_names = $this->setOdsColNames($import);
$this->ods_empty_rows = $this->setOdsEmptyRows($import);
$this->ods_recognize_percentages = $this->setOdsRecognizePercentages($import);
$this->ods_recognize_currency = $this->setOdsRecognizeCurrency($import);
}
/**
* @param array<int|string, mixed> $import
*
* @psalm-return 'csv'|'docsql'|'ldi'|'sql'
*/
private function setFormat(array $import): string
{
if (! isset($import['format']) || ! in_array($import['format'], ['csv', 'docsql', 'ldi'], true)) {
return 'sql';
}
return $import['format'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCharset(array $import): string
{
if (! isset($import['charset'])) {
return '';
}
return (string) $import['charset'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setAllowInterrupt(array $import): bool
{
if (! isset($import['allow_interrupt'])) {
return true;
}
return (bool) $import['allow_interrupt'];
}
/**
* @param array<int|string, mixed> $import
*
* @psalm-return 0|positive-int
*/
private function setSkipQueries(array $import): int
{
if (! isset($import['skip_queries'])) {
return 0;
}
$skipQueries = (int) $import['skip_queries'];
return $skipQueries >= 1 ? $skipQueries : 0;
}
/**
* @param array<int|string, mixed> $import
*
* @psalm-return 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL'
*/
private function setSqlCompatibility(array $import): string
{
if (
! isset($import['sql_compatibility']) || ! in_array(
$import['sql_compatibility'],
['ANSI', 'DB2', 'MAXDB', 'MYSQL323', 'MYSQL40', 'MSSQL', 'ORACLE', 'TRADITIONAL'],
true
)
) {
return 'NONE';
}
return $import['sql_compatibility'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setSqlNoAutoValueOnZero(array $import): bool
{
if (! isset($import['sql_no_auto_value_on_zero'])) {
return true;
}
return (bool) $import['sql_no_auto_value_on_zero'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setSqlReadAsMultibytes(array $import): bool
{
if (! isset($import['sql_read_as_multibytes'])) {
return false;
}
return (bool) $import['sql_read_as_multibytes'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvReplace(array $import): bool
{
if (! isset($import['csv_replace'])) {
return false;
}
return (bool) $import['csv_replace'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvIgnore(array $import): bool
{
if (! isset($import['csv_ignore'])) {
return false;
}
return (bool) $import['csv_ignore'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvTerminated(array $import): string
{
if (! isset($import['csv_terminated'])) {
return ',';
}
return (string) $import['csv_terminated'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvEnclosed(array $import): string
{
if (! isset($import['csv_enclosed'])) {
return '"';
}
return (string) $import['csv_enclosed'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvEscaped(array $import): string
{
if (! isset($import['csv_escaped'])) {
return '"';
}
return (string) $import['csv_escaped'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvNewLine(array $import): string
{
if (! isset($import['csv_new_line'])) {
return 'auto';
}
return (string) $import['csv_new_line'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvColumns(array $import): string
{
if (! isset($import['csv_columns'])) {
return '';
}
return (string) $import['csv_columns'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setCsvColNames(array $import): bool
{
if (! isset($import['csv_col_names'])) {
return false;
}
return (bool) $import['csv_col_names'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiReplace(array $import): bool
{
if (! isset($import['ldi_replace'])) {
return false;
}
return (bool) $import['ldi_replace'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiIgnore(array $import): bool
{
if (! isset($import['ldi_ignore'])) {
return false;
}
return (bool) $import['ldi_ignore'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiTerminated(array $import): string
{
if (! isset($import['ldi_terminated'])) {
return ';';
}
return (string) $import['ldi_terminated'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiEnclosed(array $import): string
{
if (! isset($import['ldi_enclosed'])) {
return '"';
}
return (string) $import['ldi_enclosed'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiEscaped(array $import): string
{
if (! isset($import['ldi_escaped'])) {
return '\\';
}
return (string) $import['ldi_escaped'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiNewLine(array $import): string
{
if (! isset($import['ldi_new_line'])) {
return 'auto';
}
return (string) $import['ldi_new_line'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setLdiColumns(array $import): string
{
if (! isset($import['ldi_columns'])) {
return '';
}
return (string) $import['ldi_columns'];
}
/**
* @param array<int|string, mixed> $import
*
* @return bool|string
* @psalm-return 'auto'|bool
*/
private function setLdiLocalOption(array $import)
{
if (! isset($import['ldi_local_option']) || $import['ldi_local_option'] === 'auto') {
return 'auto';
}
return (bool) $import['ldi_local_option'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setOdsColNames(array $import): bool
{
if (! isset($import['ods_col_names'])) {
return false;
}
return (bool) $import['ods_col_names'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setOdsEmptyRows(array $import): bool
{
if (! isset($import['ods_empty_rows'])) {
return true;
}
return (bool) $import['ods_empty_rows'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setOdsRecognizePercentages(array $import): bool
{
if (! isset($import['ods_recognize_percentages'])) {
return true;
}
return (bool) $import['ods_recognize_percentages'];
}
/**
* @param array<int|string, mixed> $import
*/
private function setOdsRecognizeCurrency(array $import): bool
{
if (! isset($import['ods_recognize_currency'])) {
return true;
}
return (bool) $import['ods_recognize_currency'];
}
}

@ -0,0 +1,391 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Config\Settings;
use function is_array;
// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
/**
* @psalm-immutable
*/
final class Transformations
{
/**
* Displays a part of a string.
* - The first option is the number of characters to skip from the beginning of the string (Default 0).
* - The second option is the number of characters to return (Default: until end of string).
* - The third option is the string to append and/or prepend when truncation occurs (Default: "…").
*
* @var array<int, int|string>
* @psalm-var array{0: int, 1: 'all'|int, 2: string}
*/
public $Substring;
/**
* Converts Boolean values to text (default 'T' and 'F').
* - First option is for TRUE, second for FALSE. Nonzero=true.
*
* @var string[]
* @psalm-var array{0: string, 1: string}
*/
public $Bool2Text;
/**
* LINUX ONLY: Launches an external application and feeds it the column data via standard input.
* Returns the standard output of the application. The default is Tidy, to pretty-print HTML code.
* For security reasons, you have to manually edit the file
* libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php and list the tools
* you want to make available.
* - The first option is then the number of the program you want to use.
* - The second option should be blank for historical reasons.
* - The third option, if set to 1, will convert the output using htmlspecialchars() (Default 1).
* - The fourth option, if set to 1, will prevent wrapping and ensure that the output appears
* all on one line (Default 1).
*
* @var array<int, int|string>
* @psalm-var array{0: int, 1: string, 2: int, 3: int}
*/
public $External;
/**
* Prepends and/or Appends text to a string.
* - First option is text to be prepended. second is appended (enclosed in single quotes, default empty string).
*
* @var string[]
* @psalm-var array{0: string, 1: string}
*/
public $PreApPend;
/**
* Displays hexadecimal representation of data.
* Optional first parameter specifies how often space will be added (defaults to 2 nibbles).
*
* @var string[]
* @psalm-var array{0: 0|positive-int}
*/
public $Hex;
/**
* Displays a TIME, TIMESTAMP, DATETIME or numeric unix timestamp column as formatted date.
* - The first option is the offset (in hours) which will be added to the timestamp (Default: 0).
* - Use second option to specify a different date/time format string.
* - Third option determines whether you want to see local date or UTC one (use "local" or "utc" strings) for that.
* According to that, date format has different value - for "local" see the documentation
* for PHP's strftime() function and for "utc" it is done using gmdate() function.
*
* @var array<int, int|string>
* @psalm-var array{0: 0|positive-int, 1: string, 2: 'local'|'utc'}
*/
public $DateFormat;
/**
* Displays a clickable thumbnail.
* The options are the maximum width and height in pixels.
* The original aspect ratio is preserved.
*
* @var array<(int|string), (int|string|array<string, string>|null)>
* @psalm-var array{
* 0: 0|positive-int,
* 1: 0|positive-int,
* wrapper_link: string|null,
* wrapper_params: array<array-key, string>
* }
*/
public $Inline;
/**
* Displays an image and a link; the column contains the filename.
* - The first option is a URL prefix like "https://www.example.com/".
* - The second and third options are the width and the height in pixels.
*
* @var array<int, int|string|null>
* @psalm-var array{0: string|null, 1: 0|positive-int, 2: 0|positive-int}
*/
public $TextImageLink;
/**
* Displays a link; the column contains the filename.
* - The first option is a URL prefix like "https://www.example.com/".
* - The second option is a title for the link.
*
* @var array<int, string|null>
* @psalm-var array{0: string|null, 1: string|null, 2: bool|null}
*/
public $TextLink;
/**
* @param array<int|string, mixed> $transformations
*/
public function __construct(array $transformations = [])
{
$this->Substring = $this->setSubstring($transformations);
$this->Bool2Text = $this->setBool2Text($transformations);
$this->External = $this->setExternal($transformations);
$this->PreApPend = $this->setPreApPend($transformations);
$this->Hex = $this->setHex($transformations);
$this->DateFormat = $this->setDateFormat($transformations);
$this->Inline = $this->setInline($transformations);
$this->TextImageLink = $this->setTextImageLink($transformations);
$this->TextLink = $this->setTextLink($transformations);
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<int, int|string>
* @psalm-return array{0: int, 1: 'all'|int, 2: string}
*/
private function setSubstring(array $transformations): array
{
$substring = [0, 'all', '…'];
if (isset($transformations['Substring']) && is_array($transformations['Substring'])) {
if (isset($transformations['Substring'][0])) {
$substring[0] = (int) $transformations['Substring'][0];
}
if (isset($transformations['Substring'][1]) && $transformations['Substring'][1] !== 'all') {
$substring[1] = (int) $transformations['Substring'][1];
}
if (isset($transformations['Substring'][2])) {
$substring[2] = (string) $transformations['Substring'][2];
}
}
return $substring;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return string[]
* @psalm-return array{0: string, 1: string}
*/
private function setBool2Text(array $transformations): array
{
$bool2Text = ['T', 'F'];
if (isset($transformations['Bool2Text']) && is_array($transformations['Bool2Text'])) {
if (isset($transformations['Bool2Text'][0])) {
$bool2Text[0] = (string) $transformations['Bool2Text'][0];
}
if (isset($transformations['Bool2Text'][1])) {
$bool2Text[1] = (string) $transformations['Bool2Text'][1];
}
}
return $bool2Text;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<int, int|string>
* @psalm-return array{0: int, 1: string, 2: int, 3: int}
*/
private function setExternal(array $transformations): array
{
$external = [0, '-f /dev/null -i -wrap -q', 1, 1];
if (isset($transformations['External']) && is_array($transformations['External'])) {
if (isset($transformations['External'][0])) {
$external[0] = (int) $transformations['External'][0];
}
if (isset($transformations['External'][1])) {
$external[1] = (string) $transformations['External'][1];
}
if (isset($transformations['External'][2])) {
$external[2] = (int) $transformations['External'][2];
}
if (isset($transformations['External'][3])) {
$external[3] = (int) $transformations['External'][3];
}
}
return $external;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return string[]
* @psalm-return array{0: string, 1: string}
*/
private function setPreApPend(array $transformations): array
{
$preApPend = ['', ''];
if (isset($transformations['PreApPend']) && is_array($transformations['PreApPend'])) {
if (isset($transformations['PreApPend'][0])) {
$preApPend[0] = (string) $transformations['PreApPend'][0];
}
if (isset($transformations['PreApPend'][1])) {
$preApPend[1] = (string) $transformations['PreApPend'][1];
}
}
return $preApPend;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return string[]
* @psalm-return array{0: 0|positive-int}
*/
private function setHex(array $transformations): array
{
if (isset($transformations['Hex']) && is_array($transformations['Hex'])) {
if (isset($transformations['Hex'][0])) {
$length = (int) $transformations['Hex'][0];
if ($length >= 0) {
return [$length];
}
}
}
return [2];
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<int, int|string>
* @psalm-return array{0: 0|positive-int, 1: string, 2: 'local'|'utc'}
*/
private function setDateFormat(array $transformations): array
{
$dateFormat = [0, '', 'local'];
if (isset($transformations['DateFormat']) && is_array($transformations['DateFormat'])) {
if (isset($transformations['DateFormat'][0])) {
$offset = (int) $transformations['DateFormat'][0];
if ($offset >= 1) {
$dateFormat[0] = $offset;
}
}
if (isset($transformations['DateFormat'][1])) {
$dateFormat[1] = (string) $transformations['DateFormat'][1];
}
if (isset($transformations['DateFormat'][2]) && $transformations['DateFormat'][2] === 'utc') {
$dateFormat[2] = 'utc';
}
}
return $dateFormat;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<(int|string), (int|string|array<string, string>|null)>
* @psalm-return array{
* 0: 0|positive-int,
* 1: 0|positive-int,
* wrapper_link: string|null,
* wrapper_params: array<array-key, string>
* }
*/
private function setInline(array $transformations): array
{
$inline = [100, 100, 'wrapper_link' => null, 'wrapper_params' => []];
if (isset($transformations['Inline']) && is_array($transformations['Inline'])) {
if (isset($transformations['Inline'][0])) {
$width = (int) $transformations['Inline'][0];
if ($width >= 0) {
$inline[0] = $width;
}
}
if (isset($transformations['Inline'][1])) {
$height = (int) $transformations['Inline'][1];
if ($height >= 0) {
$inline[1] = $height;
}
}
if (isset($transformations['Inline']['wrapper_link'])) {
$inline['wrapper_link'] = (string) $transformations['Inline']['wrapper_link'];
}
if (
isset($transformations['Inline']['wrapper_params'])
&& is_array($transformations['Inline']['wrapper_params'])
) {
/**
* @var int|string $key
* @var mixed $value
*/
foreach ($transformations['Inline']['wrapper_params'] as $key => $value) {
$inline['wrapper_params'][$key] = (string) $value;
}
}
}
return $inline;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<int, int|string|null>
* @psalm-return array{0: string|null, 1: 0|positive-int, 2: 0|positive-int}
*/
private function setTextImageLink(array $transformations): array
{
$textImageLink = [null, 100, 50];
if (isset($transformations['TextImageLink']) && is_array($transformations['TextImageLink'])) {
if (isset($transformations['TextImageLink'][0])) {
$textImageLink[0] = (string) $transformations['TextImageLink'][0];
}
if (isset($transformations['TextImageLink'][1])) {
$width = (int) $transformations['TextImageLink'][1];
if ($width >= 0) {
$textImageLink[1] = $width;
}
}
if (isset($transformations['TextImageLink'][2])) {
$height = (int) $transformations['TextImageLink'][2];
if ($height >= 0) {
$textImageLink[2] = $height;
}
}
}
return $textImageLink;
}
/**
* @param array<int|string, mixed> $transformations
*
* @return array<int, string|null>
* @psalm-return array{0: string|null, 1: string|null, 2: bool|null}
*/
private function setTextLink(array $transformations): array
{
$textLink = [null, null, null];
if (isset($transformations['TextLink']) && is_array($transformations['TextLink'])) {
if (isset($transformations['TextLink'][0])) {
$textLink[0] = (string) $transformations['TextLink'][0];
}
if (isset($transformations['TextLink'][1])) {
$textLink[1] = (string) $transformations['TextLink'][1];
}
if (isset($transformations['TextLink'][2])) {
$textLink[2] = (bool) $transformations['TextLink'][2];
}
}
return $textLink;
}
}

@ -0,0 +1,485 @@
<?php
/**
* Links configuration for MySQL system tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Config;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
class SpecialSchemaLinks
{
/**
* This array represent the details for generating links inside
* special schemas like mysql, information_schema etc.
* Major element represent a schema.
* All the strings in this array represented in lower case
*
* Array structure ex:
* array(
* // Database name is the major element
* 'mysql' => array(
* // Table name
* 'db' => array(
* // Column name
* 'user' => array(
* // Main url param (can be an array where represent sql)
* 'link_param' => 'username',
* // Other url params
* 'link_dependancy_params' => array(
* 0 => array(
* // URL parameter name
* // (can be array where url param has static value)
* 'param_info' => 'hostname',
* // Column name related to url param
* 'column_name' => 'host'
* )
* ),
* // Page to link
* 'default_page' => './' . Url::getFromRoute('/server/privileges')
* )
* )
* )
* );
*
* @return array<string,array<string,array<string,array<string,array<int,array<string,string>>|string>>>>
* @phpstan-return array<
* string, array<
* string, array<
* string,
* array{
* 'link_param': string,
* 'link_dependancy_params'?: array<
* int,
* array{'param_info': string, 'column_name': string}
* >,
* 'default_page': string
* }>
* >
* >
* }
*/
public static function get(): array
{
global $cfg;
$defaultPage = './' . Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
return [
'mysql' => [
'columns_priv' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'Db',
],
],
'default_page' => $defaultPage,
],
'column_name' => [
'link_param' => 'field',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'Db',
],
1 => [
'param_info' => 'table',
'column_name' => 'Table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure/change', ['change_column' => 1]),
],
],
'db' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
],
'event' => [
'name' => [
'link_param' => 'item_name',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'db',
],
],
'default_page' => './' . Url::getFromRoute('/database/events', ['edit_item' => 1]),
],
],
'innodb_index_stats' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'database_name',
],
],
'default_page' => $defaultPage,
],
'index_name' => [
'link_param' => 'index',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'database_name',
],
1 => [
'param_info' => 'table',
'column_name' => 'table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure'),
],
],
'innodb_table_stats' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'database_name',
],
],
'default_page' => $defaultPage,
],
],
'proc' => [
'name' => [
'link_param' => 'item_name',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'db',
],
1 => [
'param_info' => 'item_type',
'column_name' => 'type',
],
],
'default_page' => './' . Url::getFromRoute('/database/routines', ['edit_item' => 1]),
],
'specific_name' => [
'link_param' => 'item_name',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'db',
],
1 => [
'param_info' => 'item_type',
'column_name' => 'type',
],
],
'default_page' => './' . Url::getFromRoute('/database/routines', ['edit_item' => 1]),
],
],
'proc_priv' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'Host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
'routine_name' => [
'link_param' => 'item_name',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'Db',
],
1 => [
'param_info' => 'item_type',
'column_name' => 'Routine_type',
],
],
'default_page' => './' . Url::getFromRoute('/database/routines', ['edit_item' => 1]),
],
],
'proxies_priv' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'Host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
],
'tables_priv' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'Host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'Db',
],
],
'default_page' => $defaultPage,
],
],
'user' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
],
],
'information_schema' => [
'columns' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
'column_name' => [
'link_param' => 'field',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
1 => [
'param_info' => 'table',
'column_name' => 'table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure/change', ['change_column' => 1]),
],
],
'key_column_usage' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'constraint_schema',
],
],
'default_page' => $defaultPage,
],
'column_name' => [
'link_param' => 'field',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
1 => [
'param_info' => 'table',
'column_name' => 'table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure/change', ['change_column' => 1]),
],
'referenced_table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'referenced_table_schema',
],
],
'default_page' => $defaultPage,
],
'referenced_column_name' => [
'link_param' => 'field',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'referenced_table_schema',
],
1 => [
'param_info' => 'table',
'column_name' => 'referenced_table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure/change', ['change_column' => 1]),
],
],
'partitions' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
],
'processlist' => [
'user' => [
'link_param' => 'username',
'link_dependancy_params' => [
0 => [
'param_info' => 'hostname',
'column_name' => 'host',
],
],
'default_page' => './' . Url::getFromRoute('/server/privileges'),
],
],
'referential_constraints' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'constraint_schema',
],
],
'default_page' => $defaultPage,
],
'referenced_table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'constraint_schema',
],
],
'default_page' => $defaultPage,
],
],
'routines' => [
'routine_name' => [
'link_param' => 'item_name',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'routine_schema',
],
1 => [
'param_info' => 'item_type',
'column_name' => 'routine_type',
],
],
'default_page' => './' . Url::getFromRoute('/database/routines'),
],
],
'schemata' => [
'schema_name' => [
'link_param' => 'db',
'default_page' => $defaultPage,
],
],
'statistics' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
'column_name' => [
'link_param' => 'field',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
1 => [
'param_info' => 'table',
'column_name' => 'table_name',
],
],
'default_page' => './' . Url::getFromRoute('/table/structure/change', ['change_column' => 1]),
],
],
'tables' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
],
'table_constraints' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
],
'views' => [
'table_name' => [
'link_param' => 'table',
'link_dependancy_params' => [
0 => [
'param_info' => 'db',
'column_name' => 'table_schema',
],
],
'default_page' => $defaultPage,
],
],
],
];
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\ConfigStorage\Features;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Dbal\TableName;
/**
* @psalm-immutable
*/
final class BookmarkFeature
{
/** @var DatabaseName */
public $database;
/** @var TableName */
public $bookmark;
public function __construct(DatabaseName $database, TableName $bookmark)
{
$this->database = $database;
$this->bookmark = $bookmark;
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\ConfigStorage\Features;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Dbal\TableName;
/**
* @psalm-immutable
*/
final class ExportTemplatesFeature
{
/** @var DatabaseName */
public $database;
/** @var TableName */
public $exportTemplates;
public function __construct(DatabaseName $database, TableName $exportTemplates)
{
$this->database = $database;
$this->exportTemplates = $exportTemplates;
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\ConfigStorage\Features;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Dbal\TableName;
/**
* @psalm-immutable
*/
final class FavoriteTablesFeature
{
/** @var DatabaseName */
public $database;
/** @var TableName */
public $favorite;
public function __construct(DatabaseName $database, TableName $favorite)
{
$this->database = $database;
$this->favorite = $favorite;
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\ConfigStorage\Features;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Dbal\TableName;
/**
* @psalm-immutable
*/
final class SqlHistoryFeature
{
/** @var DatabaseName */
public $database;
/** @var TableName */
public $history;
public function __construct(DatabaseName $database, TableName $history)
{
$this->database = $database;
$this->history = $history;
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\ConfigStorage\Features;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Dbal\TableName;
/**
* @psalm-immutable
*/
final class TrackingFeature
{
/** @var DatabaseName */
public $database;
/** @var TableName */
public $tracking;
public function __construct(DatabaseName $database, TableName $tracking)
{
$this->database = $database;
$this->tracking = $tracking;
}
}

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use const SQL_DIR;
/**
* Displays status of phpMyAdmin configuration storage
*/
class CheckRelationsController extends AbstractController
{
/** @var Relation */
private $relation;
public function __construct(ResponseRenderer $response, Template $template, Relation $relation)
{
parent::__construct($response, $template);
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $db, $cfg;
/** @var string|null $createPmaDb */
$createPmaDb = $request->getParsedBodyParam('create_pmadb');
/** @var string|null $fixAllPmaDb */
$fixAllPmaDb = $request->getParsedBodyParam('fixall_pmadb');
/** @var string|null $fixPmaDb */
$fixPmaDb = $request->getParsedBodyParam('fix_pmadb');
$cfgStorageDbName = $this->relation->getConfigurationStorageDbName();
// If request for creating the pmadb
if (isset($createPmaDb) && $this->relation->createPmaDatabase($cfgStorageDbName)) {
$this->relation->fixPmaTables($cfgStorageDbName);
}
// If request for creating all PMA tables.
if (isset($fixAllPmaDb)) {
$this->relation->fixPmaTables($db);
}
// If request for creating missing PMA tables.
if (isset($fixPmaDb)) {
$relationParameters = $this->relation->getRelationParameters();
$this->relation->fixPmaTables((string) $relationParameters->db);
}
// Do not use any previous $relationParameters value as it could have changed after a successful fixPmaTables()
$relationParameters = $this->relation->getRelationParameters();
$this->render('relation/check_relations', [
'db' => $db,
'zero_conf' => $cfg['ZeroConf'],
'relation_parameters' => $relationParameters->toArray(),
'sql_dir' => SQL_DIR,
'config_storage_database_name' => $cfgStorageDbName,
'are_config_storage_tables_defined' => $this->relation->arePmadbTablesDefined(),
]);
}
}

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Config;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class CollationConnectionController extends AbstractController
{
/** @var Config */
private $config;
public function __construct(ResponseRenderer $response, Template $template, Config $config)
{
parent::__construct($response, $template);
$this->config = $config;
}
public function __invoke(): void
{
$this->config->setUserValue(
null,
'DefaultConnectionCollation',
$_POST['collation_connection'],
'utf8mb4_unicode_ci'
);
$this->response->header('Location: index.php?route=/' . Url::getCommonRaw([], '&'));
}
}

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function strlen;
final class EventsController extends AbstractController
{
/** @var Events */
private $events;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Events $events,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->events = $events;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $tables, $num_tables, $total_num_tables, $sub_part, $errors, $text_dir;
global $tooltip_truename, $tooltip_aliasname, $pos, $cfg, $errorUrl;
$this->addScriptFiles(['database/events.js']);
if (! $this->response->isAjax()) {
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
} elseif (strlen($db) > 0) {
$this->dbi->selectDb($db);
}
/**
* Keep a list of errors that occurred while
* processing an 'Add' or 'Edit' operation.
*/
$errors = [];
$this->events->handleEditor();
$this->events->export();
$items = $this->dbi->getEvents($db);
$this->render('database/events/index', [
'db' => $db,
'items' => $items,
'has_privilege' => Util::currentUserHasPrivilege('EVENT', $db),
'scheduler_state' => $this->events->getEventSchedulerStatus(),
'text_dir' => $text_dir,
'is_ajax' => $this->response->isAjax() && empty($_REQUEST['ajax_page_request']),
]);
}
}

@ -0,0 +1,63 @@
<?php
/**
* Controller for database privileges
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Privileges;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Controller for database privileges
*/
class PrivilegesController extends AbstractController
{
/** @var Privileges */
private $privileges;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Privileges $privileges,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->privileges = $privileges;
$this->dbi = $dbi;
}
/**
* @param array $params Request parameters
*/
public function __invoke(array $params): string
{
global $cfg, $text_dir;
$scriptName = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$privileges = [];
if ($this->dbi->isSuperUser()) {
$privileges = $this->privileges->getAllPrivileges($params['checkprivsdb']);
}
return $this->template->render('database/privileges/index', [
'is_superuser' => $this->dbi->isSuperUser(),
'db' => $params['checkprivsdb'],
'database_url' => $scriptName,
'text_dir' => $text_dir,
'is_createuser' => $this->dbi->isCreateUser(),
'is_grantuser' => $this->dbi->isGrantUser(),
'privileges' => $privileges,
]);
}
}

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

Loading…
Cancel
Save