diff --git a/.distignore b/.distignore
new file mode 100644
index 0000000..acbaa32
--- /dev/null
+++ b/.distignore
@@ -0,0 +1,16 @@
+.DS_Store
+.git
+.gitignore
+.gitlab-ci.yml
+.editorconfig
+.travis.yml
+behat.yml
+circle.yml
+bin/
+features/
+utils/
+*.zip
+*.tar.gz
+*.swp
+*.txt
+*.log
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e14d024
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,25 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+# WordPress Coding Standards
+# https://make.wordpress.org/core/handbook/coding-standards/
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = tab
+
+[{.jshintrc,*.json,*.yml,*.feature}]
+indent_style = space
+indent_size = 2
+
+[{*.txt}]
+end_of_line = crlf
+
+[composer.json]
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6b6c3ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+node_modules/
+vendor/
+*.zip
+*.tar.gz
+*.swp
+*.txt
+*.log
+composer.lock
+.idea
diff --git a/admin-tools-command.php b/admin-tools-command.php
new file mode 100644
index 0000000..185d3ee
--- /dev/null
+++ b/admin-tools-command.php
@@ -0,0 +1,20 @@
+
+
+ WordPress Coding Standards for EE
+
+
+
+
+
+
+
+
+
+
+ .
+ */ci/*
+ */features/*
+ */packages/*
+ */tests/*
+ */utils/*
+ */vendor/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Admin_Tools_Command.php b/src/Admin_Tools_Command.php
new file mode 100644
index 0000000..bc5b9fe
--- /dev/null
+++ b/src/Admin_Tools_Command.php
@@ -0,0 +1,331 @@
+fs = new Filesystem();
+ define( 'ADMIN_TOOL_DIR', EE_CONF_ROOT . '/admin-tools' );
+ }
+
+ /**
+ * Installs admin-tools for EasyEngine.
+ */
+ public function install() {
+
+ if ( ! $this->is_installed() ) {
+ EE::log( 'Installing admin-tools. This may take some time.' );
+ $this->fs->mkdir( ADMIN_TOOL_DIR );
+ }
+
+ $tools_file_info = pathinfo( ADMIN_TOOLS_FILE );
+ EE::debug( 'admin-tools file: ' . ADMIN_TOOLS_FILE );
+
+ if ( 'json' !== $tools_file_info['extension'] ) {
+ EE::error( 'Invalid admin-tools file found. Aborting.' );
+ }
+
+ $tools_file = file_get_contents( ADMIN_TOOLS_FILE );
+ if ( empty( $tools_file ) ) {
+ EE::error( 'admin-tools file is empty. Can\'t proceed further.' );
+ }
+ $tools = json_decode( $tools_file, true );
+ $json_error = json_last_error();
+ if ( $json_error != JSON_ERROR_NONE ) {
+ EE::debug( 'Json last error: ' . $json_error );
+ EE::error( 'Error decoding admin-tools file.' );
+ }
+ if ( empty( $tools ) ) {
+ EE::error( 'No data found in admin-tools file. Can\'t proceed further.' );
+ }
+
+ foreach ( $tools as $tool => $data ) {
+ if ( ! $this->is_installed( $tool ) ) {
+ EE::log( "Installing $tool" );
+ $tool_path = ADMIN_TOOL_DIR . '/' . $tool;
+ if ( method_exists( $this, "install_$tool" ) ) {
+ call_user_func_array( [ $this, "install_$tool" ], [ $data, $tool_path ] );
+ } else {
+ EE::error( "No method found to install $tool. Aborting." );
+ }
+ EE::success( "Installed $tool successfully." );
+ } else {
+ EE::log( "$tool already installed." );
+ }
+ }
+ }
+
+ /**
+ * Enables admin-tools on given site.
+ *
+ * ## OPTIONS
+ *
+ * []
+ * : Name of website to enable admin-tools on.
+ *
+ * [--force]
+ * : Force enabling of admin-tools for a site.
+ */
+ public function up( $args, $assoc_args ) {
+
+ EE\Utils\delem_log( 'admin-tools ' . __FUNCTION__ . ' start' );
+ $args = EE\SiteUtils\auto_site_name( $args, $this->command, __FUNCTION__ );
+ $force = EE\Utils\get_flag_value( $assoc_args, 'force' );
+ $this->db = Site::find( EE\Utils\remove_trailing_slash( $args[0] ) );
+ if ( ! $this->db || ! $this->db->site_enabled ) {
+ EE::error( sprintf( 'Site %s does not exist / is not enabled.', $args[0] ) );
+ }
+
+ if ( $this->db->admin_tools && ! $force ) {
+ EE::error( sprintf( 'admin-tools already seem to be enabled for %s', $this->db->site_url ) );
+ }
+
+ chdir( $this->db->site_fs_path );
+
+ $launch = EE::launch( 'docker-compose config --services' );
+ $services = explode( PHP_EOL, trim( $launch->stdout ) );
+ $min_req_services = [ 'nginx', 'php' ];
+
+ if ( count( array_intersect( $services, $min_req_services ) ) !== count( $min_req_services ) ) {
+ EE::error( sprintf( '%s site-type of %s-command does not support admin-tools.', $this->db->app_sub_type, $this->db->site_type ) );
+ }
+
+ if ( ! $this->is_installed() ) {
+ EE::log( 'It seems admin-tools have not yet been installed.' );
+ $this->install();
+ }
+
+ $this->move_config_file( 'docker-compose-admin.mustache', $this->db->site_fs_path . '/docker-compose-admin.yml' );
+
+ if ( EE::exec( 'docker-compose -f docker-compose.yml -f docker-compose-admin.yml up -d nginx' ) ) {
+ EE::success( sprintf( 'admin-tools enabled for %s site.', $this->db->site_url ) );
+ $this->db->admin_tools = 1;
+ $this->db->save();
+ } else {
+ EE::error( sprintf( 'Error in enabling admin-tools for %s site. Check logs.', $this->db->site_url ) );
+ }
+
+ EE\Utils\delem_log( 'admin-tools ' . __FUNCTION__ . ' stop' );
+ }
+
+ /**
+ * Disables admin-tools on given site.
+ *
+ * ## OPTIONS
+ *
+ * []
+ * : Name of website to disable admin-tools on.
+ *
+ * [--force]
+ * : Force disabling of admin-tools for a site.
+ */
+ public function down( $args, $assoc_args ) {
+
+ EE\Utils\delem_log( 'admin-tools ' . __FUNCTION__ . ' start' );
+ $args = EE\SiteUtils\auto_site_name( $args, $this->command, __FUNCTION__ );
+ $force = EE\Utils\get_flag_value( $assoc_args, 'force' );
+ $this->db = Site::find( EE\Utils\remove_trailing_slash( $args[0] ) );
+ if ( ! $this->db || ! $this->db->site_enabled ) {
+ EE::error( sprintf( 'Site %s does not exist / is not enabled.', $args[0] ) );
+ }
+
+ if ( ! $this->db->admin_tools && ! $force ) {
+ EE::error( sprintf( 'admin-tools already seem to be enabled for %s', $this->db->site_url ) );
+ }
+
+ EE::docker()::docker_compose_up( $this->db->site_fs_path, [ 'nginx', 'php' ] );
+ EE::success( sprintf( 'admin-tools disabled for %s site.', $this->db->site_url ) );
+ $this->db->admin_tools = 0;
+ $this->db->save();
+ EE\Utils\delem_log( 'admin-tools ' . __FUNCTION__ . ' stop' );
+ }
+
+ /**
+ * Check if a tools directory is installed.
+ *
+ * @param string $tool The tool whose directory has to be checked.
+ *
+ * @return bool status.
+ */
+ private function is_installed( $tool = '' ) {
+
+ $tool = in_array( $tool, [ 'index', 'phpinfo' ] ) ? $tool . '.php' : $tool;
+ $tool = 'opcache' === $tool ? $tool . '-gui.php' : $tool;
+
+ return $this->fs->exists( ADMIN_TOOL_DIR . '/' . $tool );
+ }
+
+ /**
+ * Function to download file to a path.
+ *
+ * @param string $path Path to download the file on.
+ * @param string $download_url Url to download the file from.
+ */
+ private function download( $path, $download_url ) {
+
+ $headers = array();
+ $options = array(
+ 'timeout' => 1200, // 20 minutes ought to be enough for everybody.
+ 'filename' => $path,
+ );
+ EE\Utils\http_request( 'GET', $download_url, null, $headers, $options );
+ }
+
+ /**
+ * Extract zip files.
+ *
+ * @param string $zip_file Path to the zip file.
+ * @param string $path_to_extract Path where zip needs to be extracted to.
+ *
+ * @return bool Success of extraction.
+ */
+ private function extract_zip( $zip_file, $path_to_extract ) {
+
+ $zip = new ZipArchive;
+ $res = $zip->open( $zip_file );
+ if ( true === $res ) {
+ $zip->extractTo( $path_to_extract );
+ $zip->close();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Place config files from templates to tools.
+ *
+ * @param string $config_file Destination Path where the config file needs to go.
+ * @param string $template_file Source Template file from which the config needs to be created.
+ */
+ private function move_config_file( $template_file, $config_file ) {
+
+ $this->fs->dumpFile( $config_file, file_get_contents( ADMIN_TEMPLATE_ROOT . '/' . $template_file ) );
+ }
+
+ /**
+ * Function to run install composer dependencies in tools that require it.
+ *
+ * @param string $tool_path Directory of the tool that contains `composer.json` file.
+ */
+ private function composer_install( $tool_path ) {
+
+ putenv( 'COMPOSER_HOME=' . EE_VENDOR_DIR . '/bin/composer' );
+ chdir( $tool_path );
+ $input = new ArrayInput( array( 'command' => 'update' ) );
+ $application = new Application();
+ $application->setAutoExit( false );
+ $application->run( $input );
+ }
+
+ /**
+ * Function to install index.php file.
+ *
+ * @param array $data Data about url and version from `tools.json`.
+ * @param string $tool_path Path to where the tool needs to be installed.
+ */
+ private function install_index( $data, $tool_path ) {
+
+ $index_path_data = [
+ 'db_path' => DB,
+ 'ee_admin_path' => '/var/www/htdocs/ee-admin',
+ ];
+ $index_file = EE\Utils\mustache_render( ADMIN_TEMPLATE_ROOT . '/index.mustache', $index_path_data );
+ $this->fs->dumpFile( $tool_path . '.php', $index_file );
+ }
+
+ /**
+ * Function to install phpinfo.php file.
+ *
+ * @param array $data Data about url and version from `tools.json`.
+ * @param string $tool_path Path to where the tool needs to be installed.
+ */
+ private function install_phpinfo( $data, $tool_path ) {
+
+ $this->move_config_file( 'phpinfo.mustache', $tool_path . '.php' );
+ }
+
+ /**
+ * Function to install phpMyAdmin.
+ *
+ * @param array $data Data about url and version from `tools.json`.
+ * @param string $tool_path Path to where the tool needs to be installed.
+ */
+ private function install_pma( $data, $tool_path ) {
+
+ $temp_dir = EE\Utils\get_temp_dir();
+ $download_path = $temp_dir . 'pma.zip';
+ $version = str_replace( '.', '_', $data['version'] );
+ $download_url = str_replace( '{version}', $version, $data['url'] );
+ $this->download( $download_path, $download_url );
+ $this->extract_zip( $download_path, $temp_dir );
+ $this->fs->rename( $temp_dir . 'phpmyadmin-RELEASE_' . $version, $tool_path );
+ $this->move_config_file( 'pma.config.mustache', $tool_path . '/config.inc.php' );
+ $this->composer_install( $tool_path );
+ }
+
+ /**
+ * Function to install phpRedisAdmin.
+ *
+ * @param array $data Data about url and version from `tools.json`.
+ * @param string $tool_path Path to where the tool needs to be installed.
+ */
+ private function install_pra( $data, $tool_path ) {
+
+ $temp_dir = EE\Utils\get_temp_dir();
+ $download_path = $temp_dir . 'pra.zip';
+ $download_url = str_replace( '{version}', $data['version'], $data['url'] );
+ $this->download( $download_path, $download_url );
+ $this->extract_zip( $download_path, $temp_dir );
+ $this->fs->rename( $temp_dir . 'phpRedisAdmin-' . $data['version'], $tool_path );
+ $this->move_config_file( 'pra.config.mustache', $tool_path . '/includes/config.inc.php' );
+ $this->composer_install( $tool_path );
+ }
+
+ /**
+ * Function to install opcache gui.
+ *
+ * @param array $data Data about url and version from `tools.json`.
+ * @param string $tool_path Path to where the tool needs to be installed.
+ */
+ private function install_opcache( $data, $tool_path ) {
+
+ $temp_dir = EE\Utils\get_temp_dir();
+ $download_path = $temp_dir . 'opcache-gui.php';
+ $this->download( $download_path, $data['url'] );
+ $this->fs->rename( $temp_dir . 'opcache-gui.php', $tool_path . '-gui.php' );
+ }
+
+}
diff --git a/templates/docker-compose-admin.mustache b/templates/docker-compose-admin.mustache
new file mode 100644
index 0000000..7a40d72
--- /dev/null
+++ b/templates/docker-compose-admin.mustache
@@ -0,0 +1,13 @@
+version: '3'
+
+services:
+
+ php:
+ volumes:
+ - "/opt/easyengine/admin-tools:/var/www/htdocs/ee-admin:ro"
+ - "/opt/easyengine/ee.sqlite:/opt/easyengine/ee.sqlite:ro"
+
+ nginx:
+ volumes:
+ - "/opt/easyengine/admin-tools:/var/www/htdocs/ee-admin:ro"
+ - "/opt/easyengine/ee.sqlite:/opt/easyengine/ee.sqlite:ro"
diff --git a/templates/index.mustache b/templates/index.mustache
new file mode 100644
index 0000000..a406200
--- /dev/null
+++ b/templates/index.mustache
@@ -0,0 +1,86 @@
+ $site_name ), 'sites', 1 );
+
+ return $db_select[0];
+
+}
+
+/**
+ * Select data from the database.
+ *
+ * @param array $columns
+ * @param array $where
+ * @param string $table_name
+ * @param int|null $limit
+ *
+ * @return array|bool
+ */
+function select( $columns = array(), $where = array(), $table_name = 'services', $limit = null ) {
+
+ $db_path = '{{db_path}}';
+ $db = new SQLite3( $db_path );
+
+ $conditions = array();
+ if ( empty( $columns ) ) {
+ $columns = '*';
+ } else {
+ $columns = implode( ',', $columns );
+ }
+
+ foreach ( $where as $key => $value ) {
+ $conditions[] = "`$key`='" . $value . "'";
+ }
+
+ $conditions = implode( ' AND ', $conditions );
+
+ $select_data_query = "SELECT {$columns} FROM `$table_name`";
+
+ if ( ! empty( $conditions ) ) {
+ $select_data_query .= " WHERE $conditions";
+ }
+
+ if ( ! empty( $limit ) ) {
+ $select_data_query .= " LIMIT $limit";
+ }
+
+ $select_data_exec = $db->query( $select_data_query );
+ $select_data = array();
+ if ( $select_data_exec ) {
+ while ( $row = $select_data_exec->fetchArray( SQLITE3_ASSOC ) ) {
+ $select_data[] = $row;
+ }
+ }
+ if ( empty( $select_data ) ) {
+ return false;
+ }
+
+ return $select_data;
+}
+
+$services = populate_site_info();
+unset( $services['id'] );
+unset( $services['site_url'] );
+
+
+$scan = scandir( '{{ee_admin_path}}' );
+$tools = array_slice( $scan, 2 );
+
+$tools = array_diff( $tools, [ 'index.php' ] );
+
+if ( ! $services['cache_nginx_fullpage'] ) {
+ $tools = array_diff( $tools, [ 'pra' ] );
+}
+
+foreach ( $tools as $tool ) {
+ echo '' . $tool . '
';
+}
diff --git a/templates/phpinfo.mustache b/templates/phpinfo.mustache
new file mode 100644
index 0000000..2fa5939
--- /dev/null
+++ b/templates/phpinfo.mustache
@@ -0,0 +1,2 @@
+.
+ *
+ * @package PhpMyAdmin
+ */
+
+/**
+ * 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;
+
+$cfg['AllowArbitraryServer'] = true;
+
+/**
+ * First server
+ */
+$i++;
+/* Authentication type */
+$cfg['Servers'][$i]['auth_type'] = 'cookie';
+/* Server parameters */
+$cfg['Servers'][$i]['host'] = 'db';
+$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';
+
+/**
+ * You can find more configuration options in the documentation
+ * in the doc/ folder or at .
+ */
diff --git a/templates/pra.config.mustache b/templates/pra.config.mustache
new file mode 100644
index 0000000..1f47fd6
--- /dev/null
+++ b/templates/pra.config.mustache
@@ -0,0 +1,84 @@
+ array(
+ array(
+ 'name' => getenv('VIRTUAL_HOST'), // Optional name.
+ 'host' => 'redis',
+ 'port' => 6379,
+ 'filter' => '*',
+ 'scheme' => 'tcp', // Optional. Connection scheme. 'tcp' - for TCP connection, 'unix' - for connection by unix domain socket
+ 'path' => '' // Optional. Path to unix domain socket. Uses only if 'scheme' => 'unix'. Example: '/var/run/redis/redis.sock'
+
+ // Optional Redis authentication.
+ //'auth' => 'redispasswordhere' // Warning: The password is sent in plain-text to the Redis server.
+ ),
+
+ /*array(
+ 'host' => 'localhost',
+ 'port' => 6380
+ ),*/
+
+ /*array(
+ 'name' => 'local db 2',
+ 'host' => 'localhost',
+ 'port' => 6379,
+ 'db' => 1, // Optional database number, see http://redis.io/commands/select
+ 'databases' => 1, // Optional number of databases (prevents use of CONFIG command).
+ 'filter' => 'something:*', // Show only parts of database for speed or security reasons.
+ 'seperator' => '/', // Use a different seperator on this database (default uses config default).
+ 'flush' => false, // Set to true to enable the flushdb button for this instance.
+ 'charset' => 'cp1251', // Keys and values are stored in redis using this encoding (default utf-8).
+ 'keys' => false, // Use the old KEYS command instead of SCAN to fetch all keys for this server (default uses config default).
+ 'scansize' => 1000 // How many entries to fetch using each SCAN command for this server (default uses config default).
+ ),*/
+ ),
+
+
+ 'seperator' => ':',
+
+
+ // Uncomment to show less information and make phpRedisAdmin fire less commands to the Redis server. Recommended for a really busy Redis server.
+ //'faster' => true,
+
+
+ // Uncomment to enable HTTP authentication
+ /*'login' => array(
+ // Username => Password
+ // Multiple combinations can be used
+ 'admin' => array(
+ 'password' => 'adminpassword',
+ ),
+ 'guest' => array(
+ 'password' => '',
+ 'servers' => array(1) // Optional list of servers this user can access.
+ )
+ ),*/
+
+ // Use HTML form/cookie-based auth instead of HTTP Basic/Digest auth
+ 'cookie_auth' => false,
+
+
+ /*'serialization' => array(
+ 'foo*' => array( // Match like KEYS
+ // Function called when saving to redis.
+ 'save' => function($data) { return json_encode(json_decode($data)); },
+ // Function called when loading from redis.
+ 'load' => function($data) { return json_encode(json_decode($data), JSON_PRETTY_PRINT); },
+ ),
+ ),*/
+
+
+ // You can ignore settings below this point.
+
+ 'maxkeylen' => 100,
+ 'count_elements_page' => 100,
+
+ // Use the old KEYS command instead of SCAN to fetch all keys.
+ 'keys' => false,
+
+ // How many entries to fetch using each SCAN command.
+ 'scansize' => 1000
+);
+
+?>
diff --git a/tools.json b/tools.json
new file mode 100644
index 0000000..1b00bce
--- /dev/null
+++ b/tools.json
@@ -0,0 +1,22 @@
+{
+ "index": {
+ "url": "",
+ "version": ""
+ },
+ "phpinfo": {
+ "url": "",
+ "version": ""
+ },
+ "pma": {
+ "url": "https://github.com/phpmyadmin/phpmyadmin/archive/RELEASE_{version}.zip",
+ "version": "4.8.2"
+ },
+ "pra": {
+ "url": "https://github.com/erikdubbelboer/phpRedisAdmin/archive/v{version}.zip",
+ "version": "1.10.2"
+ },
+ "opcache": {
+ "url": "https://raw.githubusercontent.com/amnuts/opcache-gui/master/index.php",
+ "version": ""
+ }
+}