From f412a2a2bfef68fc343b88b6f416033567fdfabe Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <reinhold@kainhofer.com> Date: Mon, 11 Jul 2016 23:34:13 +0200 Subject: [PATCH] First version of VM3 on J3 full installer (automatic setup) --- j3vm3_full/Dockerfile | 54 +++ j3vm3_full/docker-entrypoint.sh | 163 ++++++++ j3vm3_full/install-joomla-extension.php | 458 +++++++++++++++++++++++ j3vm3_full/install-joomla.php | 476 ++++++++++++++++++++++++ j3vm3_full/makedb.php | 46 +++ j3vm3_full/supervisord.conf | 10 + 6 files changed, 1207 insertions(+) create mode 100644 j3vm3_full/Dockerfile create mode 100755 j3vm3_full/docker-entrypoint.sh create mode 100644 j3vm3_full/install-joomla-extension.php create mode 100644 j3vm3_full/install-joomla.php create mode 100644 j3vm3_full/makedb.php create mode 100644 j3vm3_full/supervisord.conf diff --git a/j3vm3_full/Dockerfile b/j3vm3_full/Dockerfile new file mode 100644 index 0000000..c74fff5 --- /dev/null +++ b/j3vm3_full/Dockerfile @@ -0,0 +1,54 @@ +FROM php:5.6-apache +MAINTAINER Reinhold Kainhofer <reinhold@kainhofer.com> + +# Enable Apache Rewrite Module +RUN a2enmod rewrite + +RUN apt-get update + +# Install PHP extensions +RUN apt-get install -y libpng12-dev libjpeg-dev zip unzip sudo \ + && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ + && docker-php-ext-install gd +RUN docker-php-ext-install mysqli + +VOLUME /var/www/html + +# Install MySQL (will only be started if needed) +# RUN export DEBIAN_FRONTEND=noninteractive +RUN echo "mysql-server-5.5 mysql-server/root_password password root" | debconf-set-selections +RUN echo "mysql-server-5.5 mysql-server/root_password_again password root" | debconf-set-selections +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mysql-server + +RUN apt-get install -y supervisor + + +# Define Joomla version and expected SHA1 signature +ENV J_VERSION 3.5.1 +ENV VM_VERSION 3.0.16 +ENV VM_DLDIR 1001 +ENV VM_MD5 3a445fc3db1cb0373359bf7a36aec4c8 +ENV VM_POSTFIX "" + +# Download package and extract to web volume +RUN curl -o virtuemart.zip -SL http://dev.virtuemart.net/attachments/download/${VM_DLDIR}/VirtueMart${VM_VERSION}_Joomla_${J_VERSION}-Stable-Full_Package${VM_POSTFIX}.zip \ + && echo "$VM_MD5 *virtuemart.zip" | md5sum -c - \ + && mkdir /usr/src/virtuemart \ + && unzip virtuemart.zip -d /usr/src/virtuemart \ + && rm virtuemart.zip \ + && chown -R www-data:www-data /usr/src/virtuemart + + +# Clean up the apt cache etc. +RUN rm -rf /var/lib/apt/lists/* + +# Copy init scripts and custom .htaccess +RUN echo "[program:mysql]\ncommand=/usr/bin/pidproxy /run/mysqld/mysqld.pid /usr/bin/mysqld_safe \nautorestart=true\n" > /etc/supervisor/conf.d/mysql.conf.save +COPY supervisord.conf /etc/supervisor/supervisord.conf +COPY docker-entrypoint.sh /entrypoint.sh +COPY makedb.php /makedb.php +COPY install-joomla.php /usr/src/virtuemart/installation/install.php +COPY install-joomla-extension.php /usr/src/virtuemart/cli/install-joomla-extension.php + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/j3vm3_full/docker-entrypoint.sh b/j3vm3_full/docker-entrypoint.sh new file mode 100755 index 0000000..5fb3093 --- /dev/null +++ b/j3vm3_full/docker-entrypoint.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +set -e + +if [[ ! -e "/etc/opentools-docker-configured" ]]; then + # This docker container has not been configured yet, use the env variables + # to set up the MYSQL server or linked container + + # check if a MYSQL container is linked: + if [ -n "$MYSQL_PORT_3306_TCP" ]; then + if [ -z "$JOOMLA_DB_HOST" ]; then + JOOMLA_DB_HOST='mysql' + else + echo >&2 "warning: both JOOMLA_DB_HOST and MYSQL_PORT_3306_TCP found" + echo >&2 " Connecting to JOOMLA_DB_HOST ($JOOMLA_DB_HOST)" + echo >&2 " instead of the linked mysql container" + fi + fi + + # If the DB user is 'root' and no DB password is given, then use the MySQL root password env var + : ${JOOMLA_DB_USER:=root} + if [ "$JOOMLA_DB_USER" = 'root' ]; then + : ${JOOMLA_DB_PASSWORD:=$MYSQL_ENV_MYSQL_ROOT_PASSWORD} + fi + + # Check for local MySQL installation: + if [ -z "$JOOMLA_DB_HOST" ]; then + # No linked container and no explicit DB host => local MySQL installation + echo >&2 "Neither linked database container nor mysql dabase server host given. " + echo >&2 " Assuming local installation. An instance of the MySQL server will be installed locally." + MYSQL_LOCAL=1 + JOOMLA_DB_HOST="127.0.0.1" + if [ -z "${JOOMLA_DB_PASSWORD}" ]; then + JOOMLA_DB_PASSWORD='root' + echo >&2 "No MySQL root password given. Assuming password 'root'." + fi + echo >&2 " Root password is ${JOOMLA_DB_PASSWORD}." + + # Temporarily start the mysql daemon to set up the database and shut it + # down again (supervisord will start it at the very end) + echo "Starting local mysql server temporarily to set up the database..." + /usr/bin/mysqld_safe > /dev/null 2>&1 & + timeout=30 + echo -n "Waiting for database server to accept connections" + while ! /usr/bin/mysqladmin --user=root --password=root status > /dev/null 2>&1; do + timeout=$(($timeout-1)) + if [ $timeout -eq 0 ]; then + echo -e "\n Unable to connecto the database server. Aborting..." + exit 1 + fi + echo -n "." + sleep 1 + done + echo + mysqladmin --password=root password "${JOOMLA_DB_PASSWORD}" + + # enable mysqld in the supervisor config + cp /etc/supervisor/conf.d/mysql.conf.save /etc/supervisor/conf.d/mysql.conf + fi + + + # Now set up the Database for Joomla/VirtueMart: + : ${JOOMLA_DB_NAME:=virtuemart} + + if [ -z "$JOOMLA_DB_PASSWORD" ]; then + echo >&2 "error: missing required JOOMLA_DB_PASSWORD environment variable" + echo >&2 " Did you forget to -e JOOMLA_DB_PASSWORD=... or link to a container?" + echo >&2 + echo >&2 " (Also of interest might be JOOMLA_DB_USER and JOOMLA_DB_NAME.)" + exit 1 + fi + # Ensure the MySQL Database is created + php /makedb.php "$JOOMLA_DB_HOST" "$JOOMLA_DB_USER" "$JOOMLA_DB_PASSWORD" "$JOOMLA_DB_NAME" + + + # Now set up the Joomla/VirtueMart installation files in apache's directory: + if ! [ -e index.php -a -e libraries/cms/version/version.php ]; then + echo >&2 "Virtuemart/Joomla not found in $(pwd) - copying now..." + + if [ "$(ls -A)" ]; then + echo >&2 "WARNING: $(pwd) is not empty - press Ctrl+C now if this is an error!" + ( set -x; ls -A; sleep 10 ) + fi + + # extract the joomla installer + tar cf - --one-file-system -C /usr/src/virtuemart . | tar xf - + + # Some versions of the full installer hae an additional subdir in the ZIP file. + # Search for web.config.txt and mv everything from that directory to the webroot + if [ ! -e web.config.txt ]; then + for jmanifest in *web.config.txt; do + jdir=$(dirname $jmanifest) + echo + mv $jdir/** . + rm -rf $jdir + done + fi + + if [ -e htaccess.txt -a ! -e .htaccess ]; then + # NOTE: The "Indexes" option is disabled in the php:apache base image so remove it as we enable .htaccess + sed -r 's/^(Options -Indexes.*)$/#\1/' htaccess.txt > .htaccess + chown www-data:www-data .htaccess + fi + + sed 's/default="localhost"/default="'$JOOMLA_DB_HOST'"/; + s/\(db_user.*\)$/\1 default="'$JOOMLA_DB_USER'"/; + s/\(db_pass.*\)$/\1 default="'$JOOMLA_DB_PASSWORD'"/; + s/\(db_name.*\)$/\1 default="'$JOOMLA_DB_NAME'"/' installation/model/forms/database.xml > installation/model/forms/database.xml.new + mv installation/model/forms/database.xml.new installation/model/forms/database.xml + + echo >&2 "Complete! Virtuemart has been successfully copied to $(pwd)" + fi + + # Now run the joomla Installer: + : ${JOOMLA_ADMIN_USER:=admin} + : ${JOOMLA_ADMIN_PASSWORD:=admin} + : ${JOOMLA_ADMIN_EMAIL:=admin@example.com} + : ${JOOMLA_SITE_NAME:=Joomla Installation} + if [ -z "$JOOMLA_DB_PREFIX" ]; then + DBPREFIX="--dbprefix=\"${JOOMLA_DB_PREFIX}_\"" + fi + + echo "Installing Joomla/VirtueMart site $JOOMLA_SITE_NAME using the CLI and database host $JOOMLA_DB_HOST" + php ./installation/install.php --name "$JOOMLA_SITE_NAME" \ + --admin-user "$JOOMLA_ADMIN_USER" --admin-pass "$JOOMLA_ADMIN_PASSWORD" --admin-email "$JOOMLA_ADMIN_EMAIL" \ + --db-host "$JOOMLA_DB_HOST" --db-user "$JOOMLA_DB_USER" --db-pass "$JOOMLA_DB_PASSWORD" --db-name "$JOOMLA_DB_NAME" $DBPREFIX && \ + rm -rf "./installation/" + chown www-data:www-data configuration.php + + + for p in /usr/src/virtuemart/*.zip; do + echo "Installing package $p" + if [ -e "$p" ]; then + sudo -u www-data php ./cli/install-joomla-extension.php --package=$p + fi + done + + + if [ "$MYSQL_LOCAL" = "1" ]; then + # Local installation, so shut down MySQL again, will later be started through supervisord + echo "Shutting down temporary MySQL instance ..." + /usr/bin/mysqladmin --user=root --password="${JOOMLA_DB_PASSWORD}" shutdown + fi + + echo >&2 "========================================================================" + echo >&2 + echo >&2 "This server is now configured to run Joomla!" + echo >&2 "You will need the following database information to install Joomla:" + echo >&2 "Host Name: $JOOMLA_DB_HOST" + echo >&2 "Database Name: $JOOMLA_DB_NAME" + echo >&2 "Database Username: $JOOMLA_DB_USER" + echo >&2 "Database Password: $JOOMLA_DB_PASSWORD" + echo >&2 + echo >&2 "Joomla admin user: $JOOMLA_ADMIN_USER" + echo >&2 "Joomla admin password: $JOOMLA_ADMIN_PASSWORD" + echo >&2 + echo >&2 "========================================================================" + + # create the file to indicate this container has been configured: + touch /etc/opentools-docker-configured +fi + +exec "$@" diff --git a/j3vm3_full/install-joomla-extension.php b/j3vm3_full/install-joomla-extension.php new file mode 100644 index 0000000..d3e52f7 --- /dev/null +++ b/j3vm3_full/install-joomla-extension.php @@ -0,0 +1,458 @@ +<?php +/** + * @package Joomla.Cli + * + * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + * + * Command-line installer for Joomla! extensions. Please note that this must be called + * from the command line, not the web. For example: + * + * /usr/bin/php /path/to/site/cli/install-extension.php + * + * This file is based on ideas from + * https://github.com/alikon/joomla-platform-examples/blob/master/Joomla%20CLI%20App/cli/jeicli.php + * and + * https://github.com/akeeba/vagrant/master/vagrant/files/joomla/install-joomla-extension.php + * + * Error codes returned from this script are: + * 0 Success + * 1 Missing parameters + * 2 Package file not found + * 3 Could not find download URL in the XML manifest + * 4 Could not download package + * 5 Could not extract package + * 250 Installation failed (package error, unwriteable directories, etc) + */ + +// Set flag that this is a parent file. +const _JEXEC = 1; + +// Load system defines +if (file_exists(dirname(__DIR__) . '/defines.php')) +{ + require_once dirname(__DIR__) . '/defines.php'; +} + +if ( !defined('_JDEFINES')) +{ + define('JPATH_BASE', dirname(__DIR__)); + require_once JPATH_BASE . '/includes/defines.php'; +} + +require_once JPATH_LIBRARIES . '/import.legacy.php'; +require_once JPATH_LIBRARIES . '/cms.php'; + +// Load the configuration +require_once JPATH_CONFIGURATION . '/configuration.php'; + +/** + * A command line script to install extensions and extension updates from a folder, file, URL or update XML source + * + * @since 3.4 + */ +class JoomlaExtensionInstallerCli extends JApplicationCli +{ + /** + * The installation method. One of folder, package, url, web + * + * @var null|string + * @since 3.4 + */ + private $installationMethod = null; + + /** + * The installation source. It can be a folder, file or URL depending on the installationMethod. + * + * @var null|string + * @since 3.4 + */ + private $installationSource = null; + + /** + * The path to the temporary package file downloaded from the web. Only used with url and web installation methods. + * + * @var null|string + * @since 3.4 + */ + private $temporaryPackage = null; + + /** + * The path to the temporary folder where the package is extracted. Not used with the folder installation method. + * + * @var null|string + * @since 3.4 + */ + private $temporaryFolder = null; + + /** + * Get the metadata of the possible options + * + * @return array + * + * @since 3.4 + */ + private function getOptionsMeta() + { + return array( + array( + 'short_name' => 'f', + 'long_name' => 'folder', + 'filter' => 'raw', + 'help_parameter' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_FOLDER_PARAM', + 'help_text' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_FOLDER' + ), + array( + 'short_name' => 'p', + 'long_name' => 'package', + 'filter' => 'raw', + 'help_parameter' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_PACKAGE_PARAM', + 'help_text' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_PACKAGE' + ), + array( + 'short_name' => 'u', + 'long_name' => 'url', + 'filter' => 'raw', + 'help_parameter' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_URL_PARAM', + 'help_text' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_URL' + ), + array( + 'short_name' => 'w', + 'long_name' => 'web', + 'filter' => 'raw', + 'help_parameter' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_WEB_PARAM', + 'help_text' => 'CLI_INSTALL_EXTENSION_HELP_OPTION_WEB' + ), + ); + } + + /** + * Shows the usage instructions of this script + * + * @return void + * + * @since 3.4 + */ + private function showUsage() + { + $phpPath = defined('PHP_BINARY') ? PHP_BINARY : '/usr/bin/php'; + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_USAGE', $phpPath, basename(__FILE__))); + $this->out(''); + $this->out(JText::_('CLI_INSTALL_EXTENSION_OPTIONS')); + $this->out(''); + + foreach ($this->getOptionsMeta() as $optionDef) + { + $this->out('-' . $optionDef['short_name'], false); + $this->out(' ' . JText::_($optionDef['help_parameter']), false); + $this->out(' | ', false); + $this->out('--' . $optionDef['long_name'], false); + $this->out('=' . JText::_($optionDef['help_parameter']), false); + $this->out(); + $this->out("\t" . JText::_($optionDef['help_text'])); + $this->out(); + } + } + + /** + * Checks if at least one of the installation options is set and populates the installationMethod and + * installationSource properties. + * + * @return bool True if there was an installation method and source specified + * + * @since 3.4 + */ + private function getAndValidateParameters() + { + foreach ($this->getOptionsMeta() as $optionDef) + { + $value = $this->input->get($optionDef['short_name'], null, $optionDef['filter']); + + if (empty($value)) + { + $value = $this->input->get($optionDef['long_name'], null, $optionDef['filter']); + } + + if ( !empty($value)) + { + $this->installationMethod = $optionDef['long_name']; + $this->installationSource = $value; + + return true; + } + } + + return false; + } + + /** + * Execute the application. + * + * @return void + * + * @since 3.4 + */ + public function execute() + { + // Unset the exceptions handler (allows the exceptions to be presented to the user) + restore_exception_handler(); + + // Load the language files for the Joomla! library (lib_joomla) + $jlang = JFactory::getLanguage(); + $jlang->load('lib_joomla', JPATH_SITE, 'en-GB', true); + $jlang->load('lib_joomla', JPATH_SITE, null, true); + + // Load the language files for the extensions installer (com_installer). IMPORTANT: These language files are + // located in the back-end of the site, hence JPATH_ADMINISTRATOR. + $jlang->load('com_installer', JPATH_ADMINISTRATOR, 'en-GB', true); + $jlang->load('com_installer', JPATH_ADMINISTRATOR, null, true); + + // Load the language files for this CLI APP + $jlang->load('cli_install_extension', JPATH_SITE, 'en-GB', true); + $jlang->load('cli_install_extension', JPATH_SITE, null, true); + + // Add a logger + JLog::addLogger( + array( + // Set the name of the log file. + 'text_file' => 'installer_cli.php', + ), JLog::DEBUG + ); + + // Show the application banner + $jVersion = new JVersion; +// $this->out(JText::sprintf('CLI_INSTALL_EXTENSION', $jVersion->getShortVersion())); +// $this->out($jVersion->COPYRIGHT); +// $this->out(str_repeat('=', 79)); +// $this->out(); + + // Verify the command-line options + if (!$this->getAndValidateParameters()) + { + $this->showUsage(); + $this->close(1); + } + +// $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_INSTALL_WITH_METHOD', $this->installationMethod)); +// $this->out(); + + // Find the package file to extract + $packageFile = null; + + switch ($this->installationMethod) + { + case 'folder' : + $packageFile = null; + + case 'package' : + $packageFile = $this->installationSource; + break; + + case 'web' : + case 'url' : + $url = $this->installationSource; + + if ($this->installationMethod == 'web') + { + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_FIND_DOWNLOAD_FROM_XML', $this->installationSource)); + $url = $this->getDownloadUrlFromXML($this->installationSource); + + if ($url === false) + { + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_ERR_XML_PROVIDES_NO_URL', $this->installationSource)); + $this->close(3); + } + } + + // Download the package + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_DOWNLOAD_FROM_URL', $url)); + $this->temporaryPackage = JInstallerHelper::downloadPackage($url); + + if ($this->temporaryPackage === false) + { + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_ERR_CANT_DOWNLOAD', $url)); + $this->close(4); + } + + $this->temporaryPackage = JFactory::getConfig()->get('tmp_path') . '/' . $this->temporaryPackage; + $packageFile = $this->temporaryPackage; + + break; + + default : + $this->showUsage(); + $this->close(1); + break; + } + + // Make sure the package file exists + if (!is_null($packageFile) && !file_exists($packageFile)) + { + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_ERR_PACKAGE_NOT_EXISTS', $packageFile)); + $this->close(2); + } + + // Extract the package file, if required + $extensionDirectory = null; + + if ($this->installationMethod == 'folder') + { + $extensionDirectory = $this->installationSource; + } + + if (!is_null($packageFile)) + { +// $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_EXTRACTING_PACKAGE', $packageFile)); + $package = JInstallerHelper::unpack($packageFile); + + if ($package === false) + { + $this->cleanUp(); + + $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_ERR_CANNOT_EXTRACT', $packageFile)); + $this->close(5); + } + + $this->temporaryFolder = $package['extractdir']; + $extensionDirectory = $this->temporaryFolder; + } + + // Try installing the extension +// $this->out(JText::sprintf('CLI_INSTALL_EXTENSION_INSTALLING_FROM', $extensionDirectory)); + $installer = new JInstaller; + $installed = $installer->install($extensionDirectory); + + // Remove the temporary folders and files + $this->cleanUp(); + + // Print a message + if ($installed) + { + $this->out(JText::_('CLI_INSTALL_EXTENSION_MSG_SUCCESS')); + $this->close(0); + } + else + { + $this->out(JText::_('CLI_INSTALL_EXTENSION_MSG_FAIL')); + $this->close(250); + } + } + + /** + * Gets the download URL of an extension from an Update XML source + * + * @param string $url The URL to the update XML source + * + * @return string|bool The download URL or false if it's not found + * + * @since 3.4 + */ + private function getDownloadUrlFromXML($url) + { + jimport('joomla.updater.update'); + + $update = new JUpdate; + $update->loadFromXML($url); + $package_url = trim($update->get('downloadurl', false)->_data); + + return $package_url; + } + + /** + * Flush the media version to refresh versionable assets. Called by the extensions installation code. + * + * @return void + * + * @since 3.4 + */ + public function flushAssets() + { + $version = new JVersion; + $version->refreshMediaVersion(); + } + + /** + * Gets the name of the current template. Called by the extensions installation code. + * + * @param boolean $params An optional associative array of configuration settings + * + * @return mixed System is the fallback. + * + * @since 3.4 + * + * @deprecated 4.0 + */ + public function getTemplate($params = false) + { + $template = new stdClass; + $template->template = 'system'; + $template->params = new \Joomla\Registry\Registry; + + if ($params) + { + return $template; + } + + return $template->template; + } + + /** + * This method is called by the extensions installation code. Since we're in the context of a CLI application we + * cannot set HTTP headers, so we'll simply ignore any call to this method. + * + * @param string $name Ignored + * @param string $value Ignored + * @param boolean $replace Ignored + * + * @return $this + * + * @since 3.4 + */ + public function setHeader($name, $value, $replace = false) + { + return $this; + } + + /** + * Cleans up temporary files and folders used in the installation + * + * @return void + * + * @since 3.4 + */ + private function cleanUp() + { + if (!empty($this->temporaryFolder) && JFolder::exists($this->temporaryFolder)) + { + JFolder::delete($this->temporaryFolder); + } + + if (!empty($this->temporaryPackage) && JFile::exists($this->temporaryPackage)) + { + JFile::delete($this->temporaryPackage); + } + } + + public function isSite() { + return false; + } + public function isAdmin() { + return false; + } + public function enqueueMessage($msg, $type="message") { + print "[type] $msg\n"; + } + public function getRouter() { + return null; + } + +} + +// Instantiate the application object +$app = JApplicationCli::getInstance('JoomlaExtensionInstallerCli'); + +// The installation code assumes that JFactory::getApplication returns a valid reference. We must not disappoint it! +JFactory::$application = $app; + +// Execute the CLI extensions installer application +$app->execute(); \ No newline at end of file diff --git a/j3vm3_full/install-joomla.php b/j3vm3_full/install-joomla.php new file mode 100644 index 0000000..22bb755 --- /dev/null +++ b/j3vm3_full/install-joomla.php @@ -0,0 +1,476 @@ +<?php +/** + * @package Joomla.Cli + * + * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +/** + * TODO description + */ + +// Set flag that this is a parent file. +const _JEXEC = 1; + +error_reporting(E_ALL | E_NOTICE); +ini_set('display_errors', 1); + +// Bootstrap the application +if (!file_exists(dirname(__DIR__) . '/installation/application/bootstrap.php')) +{ + die("Installation application has been removed.\n"); +} + +require_once dirname(__DIR__) . '/installation/application/bootstrap.php'; +chdir(dirname(__DIR__) . '/installation'); + +require_once JPATH_LIBRARIES . '/import.legacy.php'; +require_once JPATH_LIBRARIES . '/cms.php'; + +/** + * TODO description + * + * @package Joomla.Cli + * @since 3.4 + */ +class JApplicationCliInstaller extends JApplicationCli +{ + /** + * Get a list of command-line options + * + * @return array each item is keyed by the installation system's internal option name; values arrays with keys: + * - arg: string, the name of the command-line argument + * - required: bool, an indication of whether the value is required + * - default: mixed, default value to use if none is provided + * - factory: callable, a fnction which produces the default value + * - type: string, e.g. "bool" + * + * @since 3.4 + */ + public function getOptionsMetadata() + { + $optionsMetadata = array( + 'help' => array( + 'arg' => 'help', + 'description' => 'Display help', + 'type' => 'bool', + ), + 'admin_email' => array( + 'arg' => 'admin-email', + 'description' => 'Admin user\'s email', + 'required' => true, + ), + 'admin_password' => array( + 'arg' => 'admin-pass', + 'description' => 'Admin user\'s password', + 'required' => true, + ), + 'admin_user' => array( + 'arg' => 'admin-user', + 'description' => 'Admin user\'s username', + 'default' => 'admin', + ), + 'db_host' => array( + 'arg' => 'db-host', + 'description' => 'Hostname (or hostname:port)', + 'default' => 'localhost', + ), + 'db_name' => array( + 'arg' => 'db-name', + 'description' => 'Database name', + 'required' => true, + ), + 'db_old' => array( + 'arg' => 'db-old', + 'description' => 'Policy to use with old DB [remove,backup]]', + 'default' => 'backup', + ), + 'db_pass' => array( + 'arg' => 'db-pass', + 'description' => 'Database password', + 'required' => true, + ), + 'db_prefix' => array( + 'arg' => 'db-prefix', + 'description' => 'Table prefix', + 'factory' => function () { + // FIXME: Duplicated from installation/model/fields/prefix.php + $size = 5; + $prefix = ''; + $chars = range('a', 'z'); + $numbers = range(0, 9); + + // We want the fist character to be a random letter: + shuffle($chars); + $prefix .= $chars[0]; + + // Next we combine the numbers and characters to get the other characters: + $symbols = array_merge($numbers, $chars); + shuffle($symbols); + + for ($i = 0, $j = $size - 1; $i < $j; ++$i) + { + $prefix .= $symbols[$i]; + } + + // Add in the underscore: + $prefix .= '_'; + + return $prefix; + }, + ), + 'db_type' => array( + 'arg' => 'db-type', + 'description' => 'Database type [mysql,mysqli,pdomysql,postgresql,sqlsrv,sqlazure]', + 'default' => 'mysqli', + ), + 'db_user' => array( + 'arg' => 'db-user', + 'description' => 'Database user', + 'required' => true, + ), + 'helpurl' => array( + 'arg' => 'help-url', + 'description' => 'Help URL', + 'default' => 'http://help.joomla.org/proxy/index.php?option=com_help&keyref=Help{major}{minor}:{keyref}', + ), + // FIXME: Not clear if this is useful. Seems to be "the language of the installation application" + // and not "the language of the installed CMS" + 'language' => array( + 'arg' => 'lang', + 'description' => 'Language', + 'default' => 'en-GB', + ), + 'site_metadesc' => array( + 'arg' => 'desc', + 'description' => 'Site description', + 'default' => '' + ), + 'site_name' => array( + 'arg' => 'name', + 'description' => 'Site name', + 'default' => 'Joomla' + ), + 'site_offline' => array( + 'arg' => 'offline', + 'description' => 'Set site as offline', + 'default' => 0, + 'type' => 'bool', + ), + 'sample_file' => array( + 'arg' => 'sample', + 'description' => 'Sample SQL file (sample_blog.sql,sample_brochure.sql,...)', + 'default' => '', + ), + 'summary_email' => array( + 'arg' => 'email', + 'description' => 'Send email notification', + 'default' => 0, + 'type' => 'bool', + ), + ); + + // Installer internally has an option "admin_password2", but it + // doesn't seem to be necessary. + foreach (array_keys($optionsMetadata) as $key) + { + if (!isset($optionsMetadata[$key]['type'])) + { + $optionsMetadata[$key]['type'] = 'raw'; + } + if (!isset($optionsMetadata[$key]['syntax'])) + { + if ($optionsMetadata[$key]['type'] == 'bool') + { + $optionsMetadata[$key]['syntax'] = '--' . $optionsMetadata[$key]['arg']; + } + else + { + $optionsMetadata[$key]['syntax'] = '--' . rtrim($optionsMetadata[$key]['arg'], ':') . '="..."'; + } + } + } + + return $optionsMetadata; + } + + /** + * Entry point for the script + * + * @return void + * + * @since 3.4 + */ + public function doExecute() + { + JFactory::getApplication('CliInstaller'); + + // Parse options + $options = $this->parseOptions(); + + if (array_key_exists('help', $options)) + { + $this->displayUsage(); + $this->close(0); + } + + $errors = $this->validateOptions($options); + + if (!empty($errors)) + { + foreach ($errors as $error) + { + $this->enqueueMessage($error, 'fatal'); + } + + $this->displayUsage(); + $this->close(1); + } + + // Attempt to initialise the database. + $db = new InstallationModelDatabase; + + if (!$db->createDatabase($options)) + { + $this->fatal("Error executing createDatabase"); + } + + /* + FIXME InstallationModelDatabase relies on session manipulation which doesn't work well in cli + $session = JFactory::getSession(); + $options = $session->get('setup.options', NULL); + */ + $options['db_created'] = 1; + $options['db_select'] = 1; + + if ($options['db_old'] == 'backup') + { + if (!$db->handleOldDatabase($options)) + { + $this->fatal("Error executing handleOldDatabase"); + } + } + + if (!$db->createTables($options)) + { + $this->fatal("Error executing createTables"); + } + + // Attempt to setup the configuration. + $configuration = new InstallationModelConfiguration; + + if (!$configuration->setup($options)) + { + $this->fatal("Error executing setup"); + } + + // Attempt to create the database tables. + if ($options['sample_file']) + { + if (!$db->installSampleData($options)) + { + $this->fatal("Error executing installSampleData"); + } + } + + $this->out('Done'); + } + + /** + * Display help text + * + * @return void + * + * @since 3.4 + */ + public function displayUsage() + { + $this->out("Install Joomla"); + $this->out("usage: php install.php [options]"); + + foreach ($this->getOptionsMetadata() as $spec) + { + $syntax = sprintf("%-25s", $spec['syntax']); + + if (isset($spec['description'])) + { + $syntax .= $spec['description']; + } + + if (isset($spec['required']) && $spec['required']) + { + $syntax .= ' (required)'; + } + + if (isset($spec['default'])) + { + $syntax .= " (default: {$spec['default']})"; + } + + if (isset($spec['factory'])) + { + $syntax .= " (default: auto-generated)"; + } + + $this->out(" " . $syntax); + } + } + + /** + * Validate the inputs + * + * @param array $options parsed input values + * + * @return array An array of error messages + * + * @since 3.4 + */ + public function validateOptions($options) + { + $optionsMetadata = $this->getOptionsMetadata(); + $errors = array(); + + foreach ($optionsMetadata as $key => $spec) + { + if (!isset($options[$key]) && isset($spec['required']) && $spec['required']) + { + $errors[] = "Missing required option: {$spec['syntax']}"; + } + } + + return $errors; + } + + /** + * Parse all options from the command-line + * + * @return array + * + * @since 3.4 + */ + public function parseOptions() + { + global $argv; + + if (count($argv) <= 1) + { + return array('help' => 1); + } + + $optionsMetadata = $this->getOptionsMetadata(); + $options = array(); + + foreach ($optionsMetadata as $key => $spec) + { + if ($this->input->get($spec['arg'], null, $spec['type'])) + { + $options[$key] = $this->input->get($spec['arg'], null, $spec['type']); + } + elseif (isset($spec['factory'])) + { + $options[$key] = call_user_func($spec['factory']); + } + elseif (isset($spec['default'])) + { + $options[$key] = $spec['default']; + } + } + + return $options; + } + + /** + * Enqueue a system message. + * + * @param string $msg The message to enqueue. + * @param string $type The message type. Default is message. + * + * @return void + * + * @since 3.4 + */ + public function enqueueMessage($msg, $type = 'message') + { + $this->out("[$type] $msg"); + } + + /** + * Trigger a fatal error + * + * @param string $msg The message to enqueue. + * + * @return void + * + * @since 3.4 + */ + public function fatal($msg) + { + $this->enqueueMessage($msg, 'fatal'); + $this->close(1); + } + + /** + * Returns the installed language files in the administrative and + * front-end area. + * + * @param mixed $db JDatabaseDriver instance. + * + * @return array Array with installed language packs in admin and site area. + * + * @since 3.4 + */ + public function getLocaliseAdmin($db = false) + { + // Read the files in the admin area + $path = JLanguage::getLanguagePath(JPATH_ADMINISTRATOR); + $langfiles['admin'] = JFolder::folders($path); + + // Read the files in the site area + $path = JLanguage::getLanguagePath(JPATH_SITE); + $langfiles['site'] = JFolder::folders($path); + + if ($db) + { + $langfiles_disk = $langfiles; + $langfiles = array(); + $langfiles['admin'] = array(); + $langfiles['site'] = array(); + + $query = $db->getQuery(true) + ->select($db->quoteName(array('element', 'client_id'))) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('type') . ' = ' . $db->quote('language')); + $db->setQuery($query); + $langs = $db->loadObjectList(); + + foreach ($langs as $lang) + { + switch ($lang->client_id) + { + // Site + case 0 : + if (in_array($lang->element, $langfiles_disk['site'])) + { + $langfiles['site'][] = $lang->element; + } + + break; + + // Administrator + case 1 : + if (in_array($lang->element, $langfiles_disk['admin'])) + { + $langfiles['admin'][] = $lang->element; + } + + break; + } + } + } + + return $langfiles; + } +} + +JApplicationCli::getInstance('JApplicationCliInstaller')->execute(); diff --git a/j3vm3_full/makedb.php b/j3vm3_full/makedb.php new file mode 100644 index 0000000..2362ee7 --- /dev/null +++ b/j3vm3_full/makedb.php @@ -0,0 +1,46 @@ +<?php +// Args: 0 => makedb.php, 1 => "$VM_DB_HOST", 2 => "$VM_DB_USER", 3 => "$VM_DB_PASSWORD", 4 => "$VM_DB_NAME" +$stderr = fopen('php://stderr', 'w'); +fwrite($stderr, "\nEnsuring Joomla database is present\n"); + +if (strpos($argv[1], ':') !== false) +{ + list($host, $port) = explode(':', $argv[1], 2); +} +else +{ + $host = $argv[1]; + $port = 3306; +} + +$maxTries = 10; + +do +{ + $mysql = new mysqli($host, $argv[2], $argv[3], '', (int) $port); + + if ($mysql->connect_error) + { + fwrite($stderr, "\nMySQL Connection Error: ({$mysql->connect_errno}) {$mysql->connect_error}\n"); + --$maxTries; + + if ($maxTries <= 0) + { + exit(1); + } + + sleep(3); + } +} +while ($mysql->connect_error); + +if (!$mysql->query('CREATE DATABASE IF NOT EXISTS `' . $mysql->real_escape_string($argv[4]) . '`')) +{ + fwrite($stderr, "\nMySQL 'CREATE DATABASE' Error: " . $mysql->error . "\n"); + $mysql->close(); + exit(1); +} + +fwrite($stderr, "\nMySQL Database Created\n"); + +$mysql->close(); diff --git a/j3vm3_full/supervisord.conf b/j3vm3_full/supervisord.conf new file mode 100644 index 0000000..d5d19a3 --- /dev/null +++ b/j3vm3_full/supervisord.conf @@ -0,0 +1,10 @@ +[supervisord] +nodaemon=true + +[program:apache] +command=/usr/local/bin/apache2-foreground +autorestart=true + +[include] +files = /etc/supervisor/conf.d/*.conf + -- GitLab