_('Quick Install'), 'ico' => 'images/ocilogo.png', 'link' => 'toggle', 'pos' => 11, 'links' => array(), ); // @TODO invoke a hook to get supported applications // need: id (eg. wordpress), weight (to order), ico, and // optionally - a different action path. // @TODO required binaries for installation: eg, wp-cli, drush if ($this->app_is_installable('wordpress')) { $menu['links'][] = array( 'txt' => _('WordPress'), 'url' => 'oci_install.php?app=wordpress', 'ico' => 'images/wordpress.png', ); } if ($this->app_is_installable('drupal')) { $menu['links'][] = array( 'txt' => _('Drupal'), 'url' => 'oci_install.php?app=drupal', 'ico' => 'images/drupal.png', ); } if ($menu['links']) { return $menu; } } /** * Checks to see if requirements are met to install an application. * * @param $app string * The string identifier of the application (eg. drupal, wordpress). */ function app_is_installable($app) { global $hooks; $vals = $hooks->invoke('hook_oci_is_installable', array($app)); foreach ($vals as $class => $rval) { if ($rval) { return TRUE; } } return FALSE; } /** * Implements hook_oci_is_installable(). */ function hook_oci_is_installable($app) { global $L_ALTERNC_DRUSH_BIN, $L_ALTERNC_WP_BIN; switch ($app) { case 'drupal': if ($L_ALTERNC_DRUSH_BIN) { return TRUE; } break; case 'wordpress': if ($L_ALTERNC_WP_BIN) { return TRUE; } break; } return FALSE; } /** * Gets which fields should be loaded for the one click install form. * * @param $app string * * @returns array * Array of fields indexed by parameter name. Each value is an array * of ["fetch_type", "data_type", "default"]. * fetch_type may be "get" or "post". */ function oci_form_fields($app) { global $hooks; $fields = array( 'application' => array('post', 'string', ''), // Used in confirmation. 'domain' => array('post', 'string', ''), 'new_domain_name' => array('post', 'string', ''), 'sub_domain' => array('post', 'string', ''), 'new_sub_domain_name' => array('post', 'string'), 'new_sub_domain_path' => array('post', 'string', ''), 'new_sub_domain_type' => array('post', 'string', 'vhost'), 'db_name' => array('post', 'string', ''), 'new_db_name' => array('post', 'string', ''), 'db_prefix' => array('post', 'string', ''), ); $vals = $hooks->invoke('hook_oci_form_fields', array($app)); foreach ($vals as $v) { $fields = $fields + $v; } return $fields; } /** * Implements hook_oci_form_fields(). */ function hook_oci_form_fields($app) { global $mem; if ($app == 'drupal') { $fields = array( 'drupal_install_source' => array('post', 'string', ''), 'drupal_makefile' => array('post', 'string', ''), 'drupal_core_version' => array('post', 'string', ''), 'drupal_title' => array('post', 'string', ''), 'drupal_admin_name' => array('post', 'string', 'admin'), // @TODO use user e-mail as a default 'drupal_admin_mail' => array('post', 'string', ''), 'drupal_site_name' => array('post', 'string', ''), 'drupal_site_mail' => array('post', 'string', ''), ); return $fields; } if ($app == 'wordpress') { $fields = array( 'wordpress_title' => array('post', 'string', ''), 'wordpress_admin_mail' => array('post', 'string', ''), 'wordpress_admin_name' => array('post', 'string', 'admin'), 'wordpress_locale' => array('post', 'string', ''), ); return $fields; } return array(); } /** * Helper function to get the action script path. * * @returns string * String containing the name of the php script which should be * invoked to run the installation. */ function get_app_action($app) { return 'oci_doinstall_' . $app . '.php'; } /** * Returns a list of subdomain types which can be used for * installing applications into. * * @returns array * Array of vhost types (strings). */ function allowed_subdomain_types() { // Maybe just there the type has target DIRECTORY. global $dom; $dtypes = $dom->domains_type_lst(); $types = array(); foreach ($dtypes as $name => $record) { if ($record['target'] == 'DIRECTORY') { $types[] = $name; } } return $types; } /** * Provides the a base form for installation of applications. * * @param $app string * The string identifier of the application (eg. drupal, wordpress). */ function oci_form($app) { global $hooks, $quota, $dom, $mysql; $form = "
"; ob_start(); csrf_get(); $csrf = ob_get_contents(); ob_end_clean(); $form .= $csrf; $form .= '

' . _('common options') . '

'; $form .= ''; // Domain $new_domain_possible = $quota->cancreate('dom'); $current_domains = $dom->enum_domains(); if ($new_domain_possible) { $current_domains[] = '*new*'; } if ($current_domains) { $form .= '
'; // @TODO on select change which sub-domains are listed. $form .= ''; $form .= '
'; } if ($new_domain_possible) { $form .= ''; $form .= ''; } // Choose a sub-domain or new $allowed_subdomain_types = $this->allowed_subdomain_types(); $form .= '
'; $form .= ''; $form .= '
'; // new sub_domain name ; new sub_domain type ; new sub_domain path $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; // Database $new_db_possible = $quota->cancreate('mysql'); $dbs = $mysql->get_dblist(); $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; // Invoke hook to get app-specific form fields. $extra = ''; $vals = $hooks->invoke('hook_oci_form', array($app)); foreach ($vals as $v) { if ($v) { $extra .= $v; } } if ($extra) { $form .= '
'; $form .= '

' . $app . ' ' . _('installation options') . '

'; $form .= $extra; $form .= '
'; } // Submit // @TODO option to disable confirmation screen? $form .= ''; $form .= "
"; return $form; } /** * Implements hook_oci_form. */ function hook_oci_form($app) { $f = ''; switch ($app) { case 'drupal': // These fields should be defined in hook_oci_form_fields // Choose the install source $f .= '
'; $f .= ''; $f .= '
'; // Core Version $f .= '
'; $f .= ''; $f .= '
'; // Makefile $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // Site Title $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // Drupal Admin Name $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // Drupal Admin Mail $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // Drupal Site Mail $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // Drupal Site Name $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; break; case 'wordpress': // Site Title $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // wordpress Admin Name $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // wordpress Admin Mail $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; // wordpress locale $f .= '
'; $f .= ''; $f .= ''; $f .= '
'; break; default: break; } return $f; } /** * Perform validation of the form data. * * @param array $vars * Array of name/value for the submitted data as received in oci_confirm.php * * @returns array * Array of errors eg. array(0 => array('module' => 'm_oci', 'message' => '...'), ...) */ function oci_form_validate($app, $vars) { global $hooks, $dom, $db; $errors = array(); if ($vars['domain'] == '*new*') { if (!$vars['new_domain_name']) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_domain_name must not be empty when trying to create new domain'), ); } // Repeat validation done in m_dom::add_domain if (checkfqdn(strtolower($vars['new_domain_name']))) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_domain_name is syntaxically incorrect'), ); } $db->query("SELECT domain FROM forbidden_domains WHERE domain= ? ;", array($domain)); if ($db->num_rows()) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_domain_name is forbidden on this server'), ); } if ($domain == $L_FQDN || $domain == "www.$L_FQDN") { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_domain_name is the server\'s domain. You cannot host it on your account'), ); } $db->query("SELECT compte FROM domaines WHERE domaine= ?;", array($domain)); if ($db->num_rows()) { $errors[] = array( 'module' => 'm_oci', 'message' => _('The domain already exists'), ); } $db->query("SELECT compte FROM `sub_domaines` WHERE sub != \"\" AND concat( sub, \".\", domaine )= ? OR domaine= ?;", array($domain, $domain)); if ($db->num_rows()) { $errors[] = array( 'module' => 'm_oci', 'message' => _('The domain already exists'), ); } // There are so many more... whois, dns, quota } $sub = ''; if ($vars['sub_domain'] == '*new*') { if (!$vars['new_sub_domain_name']) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_sub_domain_name must not be empty'), ); } if (!$vars['new_sub_domain_path']) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_sub_domain_path must not be empty'), ); } if (!$vars['new_sub_domain_type']) { $errors[] = array( 'module' => 'm_oci', 'message' => _('new_sub_domain_type must not be empty'), ); } } else { // Make sure the sub-domain is in the current domain list($d, $s) = explode(';', $vars['sub_domain']); if (trim($d) != $vars['domain']) { $errors[] = array( 'module' => 'm_oci', 'message' => _('sub-domain does not belong to selected domain'), 'data' => array( 'sub_domain' => $vars['sub_domain'], 'domain' => $vars['domain'], 'd' => $d, 's' => $s, ), ); } } if ($vars['db_name'] == '*new*') { if (!preg_match("#^[0-9a-z]*$#", $vars['new_db_name'])) { $error[] = array( 'module' => 'm_oci', 'message' => _('new_database_name can only contain letters and numbers'), ); $msg->raise("ERROR", "mysql", _("Database name can contain only letters and numbers")); return false; } $len=variable_get("sql_max_database_length", 64); if (strlen($vars['db_name']) > $len) { $error[] = array( 'module' => 'm_oci', 'message' => _('new_database_name cannot exceed character length') . ': ' . $len, ); } $db->query("SELECT * FROM db WHERE db= ? ;", array($dbname)); if ($db->num_rows()) { $error[] = array( 'module' => 'm_oci', 'message' => _('new_database_name already exists'), ); } } $vals = $hooks->invoke('hook_oci_form_validate', array($app, $vars)); foreach ($vals as $v) { if ($v && is_array($v) && !is_empty($v)) { $errors = $errors + $v; } } return $errors; } /** * Implements hook_oci_form_validate. */ function hook_oci_form_validate($app, $vars) { $errors = array(); // @TODO Drupal // @TODO Wordpress return $errors; } /** * Installs an application. */ function install($app, $vars) { global $hooks, $dom, $mysql, $msg, $mem, $db, $cuid, $L_ALTERNC_HTML; $r = ''; // Add new domain if necessary. if ($vars['domain'] == '*new*') { $dom->lock(); // @TODO don't force dns to on enabled. if (!$dom->add_domain($vars['new_domain_name'], 1)) { $msg->raise('ERROR', '....'); return ''; } else { $vars['domain'] = $vars['new_domain_name']; unset($vars['new_domain_name']); } $dom->unlock(); } // Add new sub-domain if necessary. if ($vars['sub_domain'] == '*new*') { // @TODO special case: handle new_sub_domain_name was a default domain graceully. $dom->lock(); if (!$dom->set_sub_domain($vars['domain'], $vars['new_sub_domain_name'], $vars['new_sub_domain_type'], $vars['new_sub_domain_path'])) { $msg->raise('ERROR', '......'); return ''; } else { $vars['sub_domain'] = $vars['new_sub_domain_name']; unset($vars['new_sub_domain_name']); unset($vars['new_sub_domain_path']); unset($vars['new_sub_domain_type']); } $dom->unlock(); } else { list($d, $s) = explode(';', $vars['sub_domain']); if ($d != $vars['domain']) { $msg->raise('ERROR', 'm_oci', 'sub_domai %s doesn\'t belong to domain %s', array($vars['sub_domain'], $vars['domain'])); } $vars['sub_domain'] = $s; } // Add database if necessary. if ($vars['db_name'] == '*new*') { $login = $mem->user['login']; if(!$mysql->add_db("${login}_${vars['new_db_name']}")) { $msg->raise('ERROR', '....'); return ''; } else { $vars['db_name'] = "${login}_${vars['new_db_name']}"; unset($vars['new_db_name']); } } // Fill out variables to pass on to the install hooks. $db->query("SELECT dbu.name,dbu.password, dbs.host FROM dbusers dbu, db_servers dbs, membres m WHERE dbu.uid= ? and enable='ACTIVATED' and dbs.id=m.db_server_id and m.uid= ? and dbu.name = ?;", array($cuid, $cuid, $vars['db_name'])); if (!$db->num_rows()) { $msg->raise('ERROR', 'm_oci', _('Unable to get database information for user "%s", db "%s"'), array($cuid, $vars['db_name'])); return ''; } $db->next_record(); $vars['db_user'] = $db->Record['name']; $vars['db_pass'] = $db->Record['password']; $vars['db_host'] = $db->Record['host']; $vars['db_port'] = '3306'; // Seems to be hardcoded in AlternC $dom->lock(); $domain_info = $dom->get_domain_all($vars['domain']); $dom->unlock(); $vars['url'] = ($vars['sub_domain']) ? $vars['sub_domain'] . '.' : ''; $vars['url'] .= $vars['domain']; $msg->raise('INFO', 'm_oci', 'Domain info: %s', print_r($domain_info, TRUE)); foreach ($domain_info['sub'] as $delta => $sub_info) { if($sub_info['name'] != $vars['sub_domain']) { continue; } $vars['path'] = $sub_info['dest']; } $login = $mem->user['login']; $vars['path'] = $L_ALTERNC_HTML . '/' . substr($login, 0, 1) . '/' . $login . $vars['path']; // db_user,pass,host,port,name ; url,path $msg->raise('INFO', 'm_oci', 'Invoking hook_oci_install for app %s with args %s', array($app, print_r($vars, TRUE))); $vals = $hooks->invoke('hook_oci_install', array($app, $vars)); foreach ($vals as $v) { $r .= $v; } $vals = $hooks->invoke('hook_oci_post_install', array($app, $hook_vars)); foreach ($vals as $v) { $r .= $v; } return $r; } /** * Implements hook_oci_install. */ function hook_oci_install($app, $vars) { if ($app == 'drupal') { $r .= $this->_install_drupal($app, $vars); } elseif ($app == 'wordpress') { $r .= $this->_install_wordpress($app, $vars); } return ''; } /** * Install drupal */ private function _install_drupal($app, $vars) { global $L_ALTERNC_DRUSH_BIN, $msg; $si_args = array( '--site-mail' => $vars['drupal_site_mail'], '--site-name' => $vars['drupal_title'], '--sites-subdir' => $vars['drupal_site_name'], '--root' => $vars['path'], '--account-mail' => $vars['drupal_admin_mail'], '--account-name' => $vars['drupal_admin_name'], '--db-prefix' => $vars['db_prefix'], '--db-url' => "mysql://${vars['db_user']}:${vars['db_pass']}@${vars['db_host']}:${vars['db_port']}/${vars['db_name']}", ); $r = ''; if ($vars['drupal_makefile']) { $cmd = sprintf("$_ALTERNC_DRUSH_BIN make --concurrency=5 %s %s", $vars['drupal_makefile'], $vars['path']); $msg->raise('INFO', 'm_oci', 'Running command: %s', array($cmd)); $r .= shell_exec($cmd . ' 2>&1'); } if ($vars['drupal_core_version']) { $version = 'drupal-' . $vars['drupal_core_version']; $cmd = sprintf("$L_ALTERNC_DRUSH_BIN dl %s %s %s --yes", escapeshellarg($version), '--destination=' . escapeshellarg($vars['path']), '--drupal-project-rename="."' ); $msg->raise('INFO', 'm_oci', 'Running command: %s', array($cmd)); $r .= shell_exec($cmd . ' 2>&1'); } $si_arg = ''; foreach ($si_args as $name => $value) { if (!$value) { continue; } $si_arg .= "$name=" . escapeshellarg($value) . ' '; } $si_arg .= ' --yes'; // . ' ' . escapeshellarg($vars['drupal_site_name']); $msg->raise('INFO', 'm_oci', _('Starting Drupal installation with arguments: %s'), array($si_arg)); // @FIXME This seems to use an insane about of memory and gets OOM killed. $r .= shell_exec("$L_ALTERNC_DRUSH_BIN si $si_arg 2>&1"); return $r; } /** * Install wordpress */ private function _install_wordpress($app, $vars) { global $L_ALTERNC_WP_BIN, $msg; $dl = array( '--path' => $vars['path'], '--locale' => $vars['wordpress_locale'], ); $dl_arg = ''; foreach ($dl as $n => $v) { if ($v) { $dl_arg .= " $n=" . escapeshellarg($v); } } $msg->raise('INFO', 'm_oci', 'Running command: %s', array("$L_ALTERNC_WP_BIN core download $dl_arg")); $r = shell_exec("$L_ALTERNC_WP_BIN core download $dl_arg 2>&1"); $cfg = array( '--path' => $vars['path'], '--dbname' => $vars['db_name'], '--dbuser' => $vars['db_user'], '--dbhost' => $vars['db_host'], '--dbpass' => $vars['db_pass'], '--locale' => $vars['wordpress_locale'], ); $cfg_arg = ''; foreach ($cfg as $n => $v) { if (!$v) { continue; } $cfg_arg .= ' ' . "$n=" . escapeshellarg($v); } $msg->raise('INFO', 'm_oci', 'Running command: %s', array("$L_ALTERNC_WP_BIN config create $cfg_arg")); $r .= shell_exec("$L_ALTERNC_WP_BIN config create $cfg_arg 2>&1"); $inst = array( '--path' => $vars['path'], '--url' => $vars['url'], '--title' => $vars['wordpress_title'], '--admin_email' => $vars['wordpress_admin_mail'], '--admin_name' => $vars['wordpress_admin_name'], ); $inst_arg = ''; foreach ($inst as $n => $v) { if (!$v) { continue; } $inst_arg .= ' ' . "$n=" . escapeshellarg($v); } $msg->raise('INFO', 'm_oci', 'Running command: %s', array("$L_ALTERNC_WP_BIN core install $inst_arg")); $r .= shell_exec("$L_ALTERNC_WP_BIN core install $inst_arg 2>&1"); return $r; } }