diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cc064912a4db4e0bc400ac075355654a73d07ace --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# What is VirtueMart + +Virtuemart is a full-feature e-Commerce suite, which relies on and needs to be installed inside Joomla. For further information see http://www.virtuemart.net/ + +# About this image and its requirements + +This docker image provides the stock VirtueMart installation. There are tags for the full installer (which includes Joomla 2.5.28 and Virtuemart 3.x including sample products) and for a Joomla 3 installation (latest Joomla with Virtuemart 3.x, no sample products will be set up). +Unless you configure the image differently, the MySQL database connection to the linked mysql contaner will be: + - Database Host: mysql + - Database User: root + - Database Password: (the root password configured with the MYSQL_ROOT_PASSWORD env var when creating the mysql container) + - Database: virtuemart + +This image needs a mysql daemon set up (the database will be created by this image). Typically, you want to set up a mysql docker container and link it to this virtuemart container: + +```console +$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag +``` +where some-mysql is the name you want to assign to your container, my-secret-pw is the password to be set for the MySQL root user and tag is the tag specifying the MySQL version you want (e.g. 5.6). + +This mysql container can then be linked to the virtuemart container as described below. + +# Which tags / variants are available for this image? + + - `opentools/docker-virtuemart:fullinstall` ... The stock fullinstaller provided by the VirtueMart Team (Joomla 2.5.28, Virtuemart 3.0.10). Automatic installation not possible, but the joomla installer will be run when you point your webbrowser to this container (see below). + - `opentools/docker-virtuemart:j3vm3` ... Joomla 3 installation with Virtuemart 3.x. Automatic installation of Joomla and VirtueMart is attempted, admin username, password, email etc. are passed as env variables (see below) +# How to use this image + + +```console +$ docker run --name some-virtuemart --link some-mysql:mysql -d opentools/virtuemart +``` + +The following environment variables are also honored for configuring your Joomla instance: + +- `-e JOOMLA_DB_HOST=...` (defaults to the IP and port of the linked `mysql` container) +- `-e JOOMLA_DB_USER=...` (defaults to "root") +- `-e JOOMLA_DB_PASSWORD=...` (defaults to the value of the `MYSQL_ROOT_PASSWORD` environment variable from the linked `mysql` container) +- `-e JOOMLA_DB_NAME=...` (defaults to "virtuemart") + +The following environment variables are only used with the `opentools/docker-virtuemart:j3vm3` tag (where automatic installation of Joomla and VirtueMart is attempted): +- `-e JOOMLA_ADMIN_USER=...` (defaults to 'admin') +- `-e JOOMLA_ADMIN_PASSWORD=...` (defaults to 'admin') +- `-e JOOMLA_ADMIN_EMAIL=...` (defaults to admin@example.com) +- `-e JOOMLA_SITE_NAME=...` (defaults to 'Joomla Installation') +- `-e JOOMLA_DB_PREFIX=...` (defaults to Joomla's default of a random prefix) + +If the `JOOMLA_DB_NAME` specified does not already exist on the given MySQL server, it will be created automatically upon startup of the `wordpress` container, provided that the `JOOMLA_DB_USER` specified has the necessary permissions to create it. + +If you'd like to be able to access the instance from the host without the container's IP, standard port mappings can be used: + +```console +$ docker run --name some-virtuemart --link some-mysql:mysql -p 8080:80 -d opentools/virtuemart:tag +``` + +Then, access it via `http://localhost:8080` or `http://host-ip:8080` in a browser. This will start the VirtueMart Full Installer setup. + +If you'd like to use an external database instead of a linked `mysql` container, specify the hostname and port with `JOOMLA_DB_HOST` along with the password in `JOOMLA_DB_PASSWORD` and the username in `JOOMLA_DB_USER` (if it is something other than `root`): + +```console +$ docker run --name some-virtuemart -e JOOMLA_DB_HOST=10.1.2.3:3306 \ + -e JOOMLA_DB_USER=... -e JOOMLA_DB_PASSWORD=... -d opentools/virtuemart:tag +``` + +A typical installation of Joomla 3.x with VirtueMart 3.x will thus use a docker line like: +```console +$ docker run --name=some-virtuemart -e JOOMLA_DB_NAME=joomla_j3vm3 -e JOOMLA_DB_PREFIX=vm3 -e JOOMLA_SITE_NAME="My VirtueMart Installation" --link some-mysql:mysql -p 8080:80 -d opentools/virtuemart:j3vm3 +``` \ No newline at end of file diff --git a/j2vm2_full/Dockerfile b/j2vm2_full/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..db11608006592f5236c615fed37008699cb11ee6 --- /dev/null +++ b/j2vm2_full/Dockerfile @@ -0,0 +1,32 @@ +FROM php:5.6-apache +MAINTAINER Reinhold Kainhofer <reinhold@kainhofer.com> + +# Enable Apache Rewrite Module +RUN a2enmod rewrite + +# Install PHP extensions +RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev zip unzip && rm -rf /var/lib/apt/lists/* \ + && 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 + +# Define Joomla version and expected SHA1 signature +ENV VMFULL_VERSION 2.6.14 +ENV VMFULL_MD5 0a589d9db3831ae4e3d8eb95db28aeee + +# Download package and extract to web volume +RUN curl -o virtuemart.zip -SL http://dev.virtuemart.net/attachments/download/880/VirtueMart${VMFULL_VERSION}_Joomla_2.5.27-Stable-Full_Package.zip \ + && echo "$VMFULL_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 + +# Copy init scripts and custom .htaccess +COPY docker-entrypoint.sh /entrypoint.sh +COPY makedb.php /makedb.php + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/j2vm2_full/docker-entrypoint.sh b/j2vm2_full/docker-entrypoint.sh new file mode 100755 index 0000000000000000000000000000000000000000..44e92040e20c819c2767db633a4e3badb7f24934 --- /dev/null +++ b/j2vm2_full/docker-entrypoint.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -e + +if [[ "$1" == apache2* ]]; then + if [ -n "$MYSQL_PORT_3306_TCP" ]; then + if [ -z "$VM_DB_HOST" ]; then + VM_DB_HOST='mysql' + else + echo >&2 "warning: both VM_DB_HOST and MYSQL_PORT_3306_TCP found" + echo >&2 " Connecting to VM_DB_HOST ($VM_DB_HOST)" + echo >&2 " instead of the linked mysql container" + fi + fi + + if [ -z "$VM_DB_HOST" ]; then + echo >&2 "error: missing VM_DB_HOST and MYSQL_PORT_3306_TCP environment variables" + echo >&2 " Did you forget to --link some_mysql_container:mysql or set an external db" + echo >&2 " with -e VM_DB_HOST=hostname:port?" + exit 1 + fi + + # If the DB user is 'root' then use the MySQL root password env var + : ${VM_DB_USER:=root} + if [ "$VM_DB_USER" = 'root' ]; then + : ${VM_DB_PASSWORD:=$MYSQL_ENV_MYSQL_ROOT_PASSWORD} + fi + : ${VM_DB_NAME:=j2vm2} + + if [ -z "$VM_DB_PASSWORD" ]; then + echo >&2 "error: missing required VM_DB_PASSWORD environment variable" + echo >&2 " Did you forget to -e VM_DB_PASSWORD=... ?" + echo >&2 + echo >&2 " (Also of interest might be VM_DB_USER and VM_DB_NAME.)" + exit 1 + fi + + 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 + + tar cf - --one-file-system -C /usr/src/virtuemart . | tar xf - + + if [ ! -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 + + echo >&2 "Complete! Virtuemart has been successfully copied to $(pwd)" + fi + + # Ensure the MySQL Database is created + php /makedb.php "$VM_DB_HOST" "$VM_DB_USER" "$VM_DB_PASSWORD" "$VM_DB_NAME" + + 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: $VM_DB_HOST" + echo >&2 "Database Name: $VM_DB_NAME" + echo >&2 "Database Username: $VM_DB_USER" + echo >&2 "Database Password: $VM_DB_PASSWORD" + echo >&2 + echo >&2 "========================================================================" +fi + +exec "$@" diff --git a/j2vm2_full/makedb.php b/j2vm2_full/makedb.php new file mode 100644 index 0000000000000000000000000000000000000000..2362ee777c3ff64639b66b871200d7524eef1333 --- /dev/null +++ b/j2vm2_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/j2vm3_full/Dockerfile b/j2vm3_full/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6614d786a660e3b4adbe816909aeb052bdcf2c08 --- /dev/null +++ b/j2vm3_full/Dockerfile @@ -0,0 +1,32 @@ +FROM php:5.6-apache +MAINTAINER Reinhold Kainhofer <reinhold@kainhofer.com> + +# Enable Apache Rewrite Module +RUN a2enmod rewrite + +# Install PHP extensions +RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev zip unzip && rm -rf /var/lib/apt/lists/* \ + && 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 + +# Define Joomla version and expected SHA1 signature +ENV VMFULL_VERSION 3.0.10 +ENV VMFULL_MD5 f10c0e702a89751382beaba04a094e90 + +# Download package and extract to web volume +RUN curl -o virtuemart.zip -SL http://dev.virtuemart.net/attachments/download/973/VirtueMart${VMFULL_VERSION}_Joomla_2.5.28-Stable-Full_Package.zip \ + && echo "$VMFULL_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 + +# Copy init scripts and custom .htaccess +COPY docker-entrypoint.sh /entrypoint.sh +COPY makedb.php /makedb.php + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/j2vm3_full/docker-entrypoint.sh b/j2vm3_full/docker-entrypoint.sh new file mode 100755 index 0000000000000000000000000000000000000000..811c5b44995f693ead56f262c033d39d8d832b75 --- /dev/null +++ b/j2vm3_full/docker-entrypoint.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -e + +if [[ "$1" == apache2* ]]; then + if [ -n "$MYSQL_PORT_3306_TCP" ]; then + if [ -z "$VM_DB_HOST" ]; then + VM_DB_HOST='mysql' + else + echo >&2 "warning: both VM_DB_HOST and MYSQL_PORT_3306_TCP found" + echo >&2 " Connecting to VM_DB_HOST ($VM_DB_HOST)" + echo >&2 " instead of the linked mysql container" + fi + fi + + if [ -z "$VM_DB_HOST" ]; then + echo >&2 "error: missing VM_DB_HOST and MYSQL_PORT_3306_TCP environment variables" + echo >&2 " Did you forget to --link some_mysql_container:mysql or set an external db" + echo >&2 " with -e VM_DB_HOST=hostname:port?" + exit 1 + fi + + # If the DB user is 'root' then use the MySQL root password env var + : ${VM_DB_USER:=root} + if [ "$VM_DB_USER" = 'root' ]; then + : ${VM_DB_PASSWORD:=$MYSQL_ENV_MYSQL_ROOT_PASSWORD} + fi + : ${VM_DB_NAME:=virtuemart} + + if [ -z "$VM_DB_PASSWORD" ]; then + echo >&2 "error: missing required VM_DB_PASSWORD environment variable" + echo >&2 " Did you forget to -e VM_DB_PASSWORD=... ?" + echo >&2 + echo >&2 " (Also of interest might be VM_DB_USER and VM_DB_NAME.)" + exit 1 + fi + + 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 + + tar cf - --one-file-system -C /usr/src/virtuemart . | tar xf - + + if [ ! -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 + + echo >&2 "Complete! Virtuemart has been successfully copied to $(pwd)" + fi + + # Ensure the MySQL Database is created + php /makedb.php "$VM_DB_HOST" "$VM_DB_USER" "$VM_DB_PASSWORD" "$VM_DB_NAME" + + 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: $VM_DB_HOST" + echo >&2 "Database Name: $VM_DB_NAME" + echo >&2 "Database Username: $VM_DB_USER" + echo >&2 "Database Password: $VM_DB_PASSWORD" + echo >&2 + echo >&2 "========================================================================" +fi + +exec "$@" diff --git a/j2vm3_full/makedb.php b/j2vm3_full/makedb.php new file mode 100644 index 0000000000000000000000000000000000000000..2362ee777c3ff64639b66b871200d7524eef1333 --- /dev/null +++ b/j2vm3_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/Dockerfile b/j3vm3/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f1b549c7faef9d42e43c7ec782ea4cc278110fd8 --- /dev/null +++ b/j3vm3/Dockerfile @@ -0,0 +1,34 @@ +FROM joomla:latest +MAINTAINER Reinhold Kainhofer <reinhold@kainhofer.com> + +# Enable Apache Rewrite Module +RUN a2enmod rewrite + +# Install PHP extensions +RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev zip unzip && rm -rf /var/lib/apt/lists/* \ + && 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 + +# Define Joomla version and expected SHA1 signature +ENV VM_VERSION 3.0.10 +ENV VM_MD5 a684928d8b1e2dde0c9ac8e047d65038 + +# Download package and extract to web volume +RUN curl -o virtuemart.zip -SL http://dev.virtuemart.net/attachments/download/972/com_virtuemart.${VM_VERSION}_extract_first.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 + +# Copy init scripts and custom .htaccess +COPY docker-entrypoint.sh /entrypoint.sh +COPY makedb.php /makedb.php +COPY install-joomla.php /usr/src/joomla/installation/install.php +COPY install-joomla-extension.php /usr/src/joomla/cli/install-joomla-extension.php + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/j3vm3/docker-entrypoint.sh b/j3vm3/docker-entrypoint.sh new file mode 100755 index 0000000000000000000000000000000000000000..a86549268694ae91ff4cd56f73324fc9c66d8d7e --- /dev/null +++ b/j3vm3/docker-entrypoint.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +set -e + +if [[ "$1" == apache2* ]]; then + 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 [ -z "$JOOMLA_DB_HOST" ]; then + echo >&2 "error: missing JOOMLA_DB_HOST and MYSQL_PORT_3306_TCP environment variables" + echo >&2 " Did you forget to --link some_mysql_container:mysql or set an external db" + echo >&2 " with -e JOOMLA_DB_HOST=hostname:port?" + exit 1 + fi + + # If the DB user is 'root' 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 + : ${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=... ?" + echo >&2 + echo >&2 " (Also of interest might be JOOMLA_DB_USER and JOOMLA_DB_NAME.)" + exit 1 + fi + + if ! [ -e index.php -a -e libraries/cms/version/version.php ]; then + echo >&2 "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 + + tar cf - --one-file-system -C /usr/src/joomla . | tar xf - + + if [ ! -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 + + echo >&2 "Complete! Joomla has been successfully copied to $(pwd)" + fi + + # Ensure the MySQL Database is created + php /makedb.php "$JOOMLA_DB_HOST" "$JOOMLA_DB_USER" "$JOOMLA_DB_PASSWORD" "$JOOMLA_DB_NAME" + + 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 "========================================================================" + + + # 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 + + 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/" + + + for p in /usr/src/virtuemart/*.zip; do + php ./cli/install-joomla-extension.php --package=$p + done +fi + +exec "$@" diff --git a/j3vm3/install-joomla-extension.php b/j3vm3/install-joomla-extension.php new file mode 100644 index 0000000000000000000000000000000000000000..d3e52f7dc0a2966c0c2de8663fc4a92b14b17c1b --- /dev/null +++ b/j3vm3/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/install-joomla.php b/j3vm3/install-joomla.php new file mode 100644 index 0000000000000000000000000000000000000000..22bb755a740a1f143d4f041f059db0724c8837da --- /dev/null +++ b/j3vm3/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/makedb.php b/j3vm3/makedb.php new file mode 100644 index 0000000000000000000000000000000000000000..2362ee777c3ff64639b66b871200d7524eef1333 --- /dev/null +++ b/j3vm3/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();