Note: This does not import the docs. Those should go to the dev guide.
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.run', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+
+ \Civi\Setup::log()->info(sprintf('[%s] Parse inputs', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+ $values = $e->getField('advanced', array());
+
+ $placeholderDb = 'mysql://USER:PASS@HOST/DB';
+
+ if (empty($values['db']) || $values['db'] === $placeholderDb) {
+ $e->getModel()->extras['advanced']['db'] = $placeholderDb;
+ }
+ else {
+ $e->getModel()->extras['advanced']['db'] = trim($values['db']);
+ $e->getModel()->db = \Civi\Setup\DbUtil::parseDsn(trim($values['db']));
+ }
+
+ }, \Civi\Setup::PRIORITY_LATE);
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['advanced'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'advanced.tpl.php',
+ 'class' => '',
+ 'weight' => 60,
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2 id="environment"><?php echo ts('Environment'); ?></h2>
+
+<p>
+ <?php echo ts('The system settings were auto-detected. CiviCRM will be installed with:'); ?>
+</p>
+
+<div style="">
+ <table class="settingsTable">
+ <tbody>
+ <tr>
+ <th><?php echo ts('CMS Database'); ?></th>
+ <td>
+ <code><?php echo htmlentities('mysql://' . $model->cmsDb['username'] . ':HIDDEN@' . $model->cmsDb['server'] . '/' . $model->cmsDb['database']); ?></code>
+ </td>
+ </tr>
+ <tr>
+ <th><?php echo ts('CiviCRM Database'); ?></th>
+ <td class="advanced-db">
+ <div class="ro">
+ <code><?php echo htmlentities('mysql://' . $model->db['username'] . ':HIDDEN@' . $model->db['server'] . '/' . $model->db['database']); ?></code>
+ <a href="" onclick="csj$('.advanced-db .ro').hide(); csj$('.advanced-db .rw').show(); return false;" title="<?php echo htmlentities(ts('Edit')) ?>"><i class="fa fa-pencil"></i></a>
+ </div>
+ <div class="rw" style="display: none;">
+ <div>
+
+ <input type="text" name="civisetup[advanced][db]" value="<?php echo htmlentities($model->extras['advanced']['db']); ?>" data-original="<?php echo htmlentities($model->extras['advanced']['db']); ?>">
+ <input id="db_apply_button" type="submit" name="civisetup[action][Start]" value="<?php echo htmlentities(ts('Apply')); ?>" />
+ <a href="" onclick="civisetupAdvancedDbCancel(); return false;" title="<?php echo htmlentities(ts('Cancel')) ?>"><i class="fa fa-close"></i></a>
+ <script type="text/javascript">
+ function civisetupAdvancedDbCancel() {
+ csj$('.advanced-db .rw').hide();
+ csj$('.advanced-db .ro').show();
+ csj$('.advanced-db .rw input[type=text]').val(csj$('.advanced-db .rw input[type=text]').attr('data-original'));
+ }
+ </script>
+ </div>
+ <p><?php echo ts('By default, CiviCRM uses the same database as your website. You may install on a separate database if you need more fine-grained control over permissions, replication, hardware capacity, etc.'); ?></p>
+ <p><?php echo ts('<strong>Example</strong>: <code>%1</code>', array(1 => 'mysql://admin:secret@localhost/civicrm')); ?></p>
+ <p><?php echo ts('<strong>Example</strong>: <code>%1</code>', array(1 => 'mysql://admin:secret@127.0.0.1:3306/otherdb')); ?></p>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <th><?php echo ts('CiviCRM Settings File'); ?></th>
+ <td><code><?php echo htmlentities($model->settingsPath); ?></code></td>
+ </tr>
+ <tr>
+ <th><?php echo ts('CiviCRM Source Code'); ?></th>
+ <td><code><?php echo htmlentities($model->srcPath); ?></code></td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<p class="tip">
+ <strong><?php echo ts('Tip'); ?></strong>:
+ <?php echo ts('Need more advanced control? You may alternatively use the <a href="%1" target="%2">command-line installer</a>.', array(1 => 'https://github.com/civicrm/cv', 2 => '_blank')); ?>
+</p>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['components'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'components.tpl.php',
+ 'class' => 'if-no-errors',
+ 'weight' => 50,
+ 'component_labels' => array(
+ 'CiviContribute' => ts('Accept donations and payments'),
+ 'CiviEvent' => ts('Accept event registrations'),
+ 'CiviMail' => ts('Send email blasts and newsletters'),
+ 'CiviMember' => ts('Manage recurring memberships'),
+ 'CiviCase' => ts('Track case histories'),
+ 'CiviPledge' => ts('Accept pledges'),
+ 'CiviReport' => ts('Generate reports'),
+ 'CiviCampaign' => ts('Organize campaigns, surveys, and petitions'),
+ 'CiviGrant' => ts('Receive grant applications'),
+ ),
+ );
+
+ if ($e->getMethod() === 'POST' || is_array($e->getField('components'))) {
+ $e->getModel()->components = array_keys($e->getField('components'));
+ }
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2 id="components"><?php echo ts('Components'); ?></h2>
+
+<div>
+ <?php foreach ($model->getField('components', 'options') as $comp => $label): ?>
+ <input class="comp-cb sr-only" style="display: none;" type="checkbox" name="civisetup[components][<?php echo $comp; ?>]" id="civisetup[components][<?php echo $comp; ?>]" <?php echo in_array($comp, $model->components) ? 'checked' : '' ?>>
+ <label class="comp-box" for="civisetup[components][<?php echo $comp; ?>]">
+ <span class="comp-label"><?php echo $label; ?></span>
+ <span class="comp-desc"><?php echo $_tpl_block['component_labels'][$comp] ?></span>
+ </label>
+ <?php endforeach; ?>
+</div>
+
+<p class="tip">
+ <strong><?php echo ts('Tip'); ?></strong>:
+ <?php echo ts('Not sure? That\'s OK. After installing, you can enable and disable components at any time.'); ?>
+</p>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['header'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'header.tpl.php',
+ 'class' => '',
+ 'weight' => 10,
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<div class="civicrm-setup-header">
+ <div class="title">
+ <h1><?php echo ts("Thanks for choosing CiviCRM. You're nearly there!"); ?><hr></h1>
+ </div>
+ <div class="civicrm-logo"><strong><?php echo ts('Version %1', array(1 => "{$civicrm_version} {$model->cms}")); ?></strong>
+ <span><img src=<?php echo $installURLPath . "updated-logo.jpg"?> /></span>
+ </div>
+</div>
+<h2><?php echo ts("CiviCRM Installer"); ?></h2>
+
+<noscript>
+<p class="error"><?php echo ts("Error: Javascipt appears to be disabled. The CiviCRM web-based installer requires Javascript.");?></p>
+</noscript>
+
+<p><?php echo ts("Thanks for choosing CiviCRM! Please follow the instructions below to install CiviCRM."); ?></p>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['install'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'install.tpl.php',
+ 'class' => 'if-no-errors',
+ 'weight' => 70,
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<div class="action-box">
+ <input id="install_button" type="submit" name="civisetup[action][Install]"
+ value="<?php echo htmlentities(ts('Install CiviCRM')); ?>"
+ onclick="document.getElementById('saving_top').style.display = ''; this.value = '<?php echo ts('Installing CiviCRM...', array('escape' => 'js')); ?>'"/>
+ <div id="saving_top" style="display: none;">
+ <img src="<?php echo htmlentities($installURLPath . "network-save.gif") ?>"/>
+ <?php echo ts('(this will take a few minutes)'); ?>
+ </div>
+</div>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['l10n'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'l10n.tpl.php',
+ 'class' => 'if-no-errors',
+ 'weight' => 30,
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2><?php echo ts('Localization'); ?></h2>
+
+<p><?php echo ts('CiviCRM has been translated to many languages, thanks to its community of translators. By selecting another language, the installer may be available in that language. The initial configuration of the basic data will also be set to that language (ex: individual prefixes, suffixes, activity types, etc.). <a href="%1" target="%2">Learn more about using CiviCRM in other languages.</a>', array(1 => 'http://wiki.civicrm.org/confluence/pages/viewpage.action?pageId=88408149', 2 => '_blank')); ?></p>
+
+<script>
+ function civicrmInstallerSetLanguage(language) {
+ var location = window.location.toString();
+
+ if (location.match(/lang=.._../)) {
+ location = location.replace(/lang=.._../, 'lang=' + language);
+ window.location = location;
+ }
+ else {
+ window.location += (location.indexOf('?') < 0 ? '?' : '&') + 'lang=' + language;
+ }
+ }
+</script>
+
+<p style="margin-left: 2em" id="locale">
+ <label for="lang"><span><?php echo ts('Language of basic data:'); ?></span></label>
+ <select id="lang" name="lang" onchange="civicrmInstallerSetLanguage(this.value);">
+ <?php
+ foreach ($model->getField('lang', 'options') as $locale => $language):
+ $selected = ($locale == $model->lang) ? 'selected="selected"' : '';
+ echo "<option value='$locale' $selected>$language</option>";
+ endforeach;
+ ?>
+ </select>
+
+ <span class="advancedTip">
+ <?php
+ if (count($model->getField('lang', 'options')) < 2):
+ echo "(download the civicrm-{$civicrm_version}-l10n.tar.gz file and unzip into CiviCRM’s directory to add languages here)";
+ endif;
+ ?>
+</span>
+</p>
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Display a block with opt-in settings.
+ *
+ * The file `opt-in.disabled.php` is ignored by default. To enable, you should
+ * either rename it to `opt-in.civi-setup.php` or symlink it.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+// First pass: initialize 'settings' block.
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ $e->getCtrl()->blocks['opt-in'] = array(
+ 'is_active' => TRUE, // FIXME
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'opt-in.tpl.php',
+ 'class' => 'if-no-errors',
+ 'weight' => 55,
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
+
+// Second pass: Parse any settings that have been approved for use in this form.
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ if (!$e->getCtrl()->blocks['opt-in']['is_active']) {
+ return;
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Parse inputs', basename(__FILE__)));
+ $values = $e->getField('opt-in', array());
+ $e->getModel()->extras['opt-in']['empoweredBy'] = !empty($values['empoweredBy']);
+ $e->getModel()->extras['opt-in']['versionCheck'] = !empty($values['versionCheck']);
+
+ // echo '<pre>'; print_r(['model'=> $e->getModel()->getValues(), 'v'=>$values]); echo '</pre>';
+
+ }, \Civi\Setup::PRIORITY_LATE);
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ $m = $e->getModel();
+
+ if (isset($m->extras['opt-in']['empoweredBy'])) {
+ \Civi\Setup::log()->info(sprintf('[%s] Set empoweredBy', basename(__FILE__)));
+ \Civi::settings()->set('empoweredBy', (bool) $m->extras['opt-in']['empoweredBy']);
+ }
+
+ if (isset($m->extras['opt-in']['versionCheck'])) {
+ \Civi\Setup::log()->info(sprintf('[%s] Set versionCheck', basename(__FILE__)));
+ \CRM_Core_DAO::executeQuery('UPDATE civicrm_job SET is_active = %1 WHERE api_entity LIKE "job" AND api_action LIKE "version_check"', array(
+ 1 => array($m->extras['opt-in']['versionCheck'] ? 1 : 0, 'Int'),
+ ));
+ }
+ }, \Civi\Setup::PRIORITY_LATE + 100);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2 id="settings"><?php echo ts('Opt-in'); ?></h2>
+
+<p>
+ <?php echo ts('CiviCRM is provided for free -- built with donations and volunteerism. We don\'t have the marketing or data-analytics prowess of a large corporation, so we rely on users to keep us informed -- and to spread the word.'); ?>
+ <?php echo ts('Of course, not everyone can help in these ways. But if you can, opt-in to help enrich the product and community.'); ?>
+</p>
+
+<div>
+ <input class="optin-cb sr-only" style="display: none;" type="checkbox" name="civisetup[opt-in][versionCheck]" id="civisetup[opt-in][versionCheck]" value="1" <?php echo $model->extras['opt-in']['versionCheck'] ? 'checked' : ''; ?>>
+ <label class="optin-box" for="civisetup[opt-in][versionCheck]">
+ <span class="optin-label"><?php echo ts('Version pingback'); ?></span>
+ <span class="optin-desc"><?php echo ts('Check for CiviCRM version updates. Report anonymous usage statistics.'); ?></span>
+ </label>
+
+ <input class="optin-cb sr-only" style="display: none;" type="checkbox" name="civisetup[opt-in][empoweredBy]" id="civisetup[opt-in][empoweredBy]" value="1" <?php echo $model->extras['opt-in']['empoweredBy'] ? 'checked' : ''; ?>>
+ <label class="optin-box" for="civisetup[opt-in][empoweredBy]">
+ <span class="optin-label"><?php echo ts('Empowered by CiviCRM'); ?></span>
+ <span class="optin-desc"><?php echo ts('Display "Empowered by CiviCRM" in the footer of public forms.'); ?></span>
+ </label>
+</div>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['requirements'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'requirements.tpl.php',
+ 'class' => 'if-problems',
+ 'weight' => 20,
+ 'severity_labels' => array(
+ 'info' => ts('Info'),
+ 'warning' => ts('Warning'),
+ 'error' => ts('Error'),
+ ),
+ 'section_labels' => array(
+ 'database' => ts('Database'),
+ 'system' => ts('System'),
+ ),
+ );
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2 id="requirements"><?php echo ts('System Requirements'); ?></h2>
+
+<?php
+if (count($reqs->getErrors()) > 0):
+ ?><p class="error"><?php echo ts('We are not able to install the software. Please review the errors and warnings below.'); ?></p><?php
+elseif (count($reqs->getWarnings()) > 0):
+ ?><p class="warning"><?php echo ts('There are some issues that we recommend you look at before installing. However, you are still able to install the software.'); ?></p><?php
+else:
+ ?><p class="good"><?php echo ts("You're ready to install!"); ?></p><?php
+endif;
+?>
+
+<?php
+$msgs = array_filter($reqs->getMessages(), function($m) {
+ return $m['severity'] != 'info';
+});
+uasort($msgs, function($a, $b) {
+ return strcmp(
+ $a['severity'] . '-' . $a['section'] . '-' . $a['name'],
+ $b['severity'] . '-' . $b['section'] . '-' . $b['name']
+ );
+});
+?>
+
+<table class="reqTable">
+ <thead>
+ <tr>
+ <th width="10%"><?php echo ts('Severity'); ?></th>
+ <th width="10%"><?php echo ts('Section'); ?></th>
+ <th width="20%"><?php echo ts('Name'); ?></th>
+ <th width="69%"><?php echo ts('Details'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($msgs as $msg):?>
+ <tr class="<?php echo 'reqSeverity-' . $msg['severity']; ?>">
+ <td><?php echo htmlentities($_tpl_block['severity_labels'][$msg['severity']]); ?></td>
+ <td><?php echo htmlentities(isset($_tpl_block['section_labels'][$msg['section']]) ? $_tpl_block['section_labels'][$msg['section']] : $msg['section']); ?></td>
+ <td><?php echo htmlentities($msg['name']); ?></td>
+ <td><?php echo htmlentities($msg['message']); ?></td>
+ </tr>
+ <?php endforeach; ?>
+ </tbody>
+</table>
+
+<div class="action-box">
+ <input id="recheck_button" type="submit" name="civisetup[action][Start]" value="<?php echo htmlentities(ts('Refresh')); ?>" />
+ <div class="advancedTip">
+ <?php echo ts('After updating your system, refresh to test the requirements again.'); ?>
+ </div>
+</div>
--- /dev/null
+<?php
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.boot', function (\Civi\Setup\UI\Event\UIBootEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Register blocks', basename(__FILE__)));
+
+ /**
+ * @var \Civi\Setup\UI\SetupController $ctrl
+ */
+ $ctrl = $e->getCtrl();
+
+ $ctrl->blocks['sample-data'] = array(
+ 'is_active' => TRUE,
+ 'file' => __DIR__ . DIRECTORY_SEPARATOR . 'sample-data.tpl.php',
+ 'class' => 'if-no-errors',
+ 'weight' => 40,
+ );
+
+ if ($e->getMethod() === 'POST') {
+ $e->getModel()->loadGenerated = !empty($e->getField('loadGenerated'));
+ }
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php if (!defined('CIVI_SETUP')): exit("Installation plugins must only be loaded by the installer.\n"); endif; ?>
+<h2><?php echo ts('Sample Data'); ?></h2>
+
+<p>
+ <label for="loadGenerated"><span>Load sample data:</span><input id="loadGenerated" type="checkbox" name="civisetup[loadGenerated]" value=1 <?php echo $model->loadGenerated ? "checked='checked'" : ""; ?> /></label> <br />
+ <span class="advancedTip"><?php echo ts("Check this box to pre-populate CiviCRM with sample English contact records, online contribution pages, profile forms, etc. These examples can help you learn about CiviCRM features."); ?></span><br />
+</p>
+
+<script type="text/javascript">
+ csj$(function(){
+ function hideLang() {
+ if (csj$('[name=lang]').val() == 'en_US') {
+ csj$('#loadGenerated').prop('disabled', false);
+ }
+ else {
+ csj$('#loadGenerated').prop('disabled', true).prop('checked', false);
+ }
+ setTimeout(hideLang, 100);
+ }
+ setTimeout(hideLang, 100);
+ });
+</script>
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine whether Civi has been installed.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkInstalled', function (\Civi\Setup\Event\CheckInstalledEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkInstalled'));
+ $model = $e->getModel();
+
+ if ($model->db) {
+ try {
+ $conn = \Civi\Setup\DbUtil::connect($model->db);
+ }
+ catch (\Civi\Setup\Exception\SqlException $exception) {
+ $e->setDatabaseInstalled(FALSE);
+ return;
+ }
+ $found = FALSE;
+ foreach ($conn->query('SHOW TABLES LIKE "civicrm_%"') as $result) {
+ $found = TRUE;
+ }
+ $conn->close();
+ $e->setDatabaseInstalled($found);
+ }
+ else {
+ throw new \Exception("The \"db\" is unspecified. Cannot determine whether the database schema file exists.");
+ }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine whether Civi has been installed.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkInstalled', function (\Civi\Setup\Event\CheckInstalledEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkInstalled'));
+
+ $model = $e->getModel();
+
+ if ($model->settingsPath) {
+ $e->setSettingInstalled(file_exists($model->settingsPath));
+ }
+ else {
+ throw new \Exception("The \"settingsPath\" is unspecified. Cannot determine whether the settings file exists.");
+ }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Verify that the CMS base URL is well-formed.
+ *
+ * Ex: When installing via CLI, the URL cannot be determined automatically.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+ $model = $e->getModel();
+
+ if (!$model->cmsBaseUrl || !preg_match('/^https?:/', $model->cmsBaseUrl)) {
+ $e->addError('system', 'cmsBaseUrl', "The \"cmsBaseUrl\" ($model->cmsBaseUrl) is unavailable or malformed. Consider setting it explicitly.");
+ return;
+ }
+
+ $selfDir = dirname($_SERVER['PHP_SELF']);
+ if (PHP_SAPI === 'cli' && $selfDir !== '/' && strpos($model->cmsBaseUrl, $selfDir) !== FALSE) {
+ $e->addError('system', 'cmsBaseUrl', "The \"cmsBaseUrl\" ($model->cmsBaseUrl) is unavailable or malformed. Consider setting it explicitly.");
+ return;
+ }
+
+ $e->addInfo('system', 'cmsBaseUrl', 'The "cmsBaseUrl" appears well-formed.');
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Verify that the database parameters are well-formed.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+
+ $dbFields = array('db', 'cmsDb');
+ foreach ($dbFields as $dbField) {
+ $errors = 0;
+ $db = $e->getModel()->{$dbField};
+
+ $keys = array_keys($db);
+ sort($keys);
+ $expectedKeys = array('server', 'username', 'password', 'database');
+ sort($expectedKeys);
+ if ($keys !== $expectedKeys) {
+ $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)",
+ $dbField,
+ implode(',', $expectedKeys),
+ implode(',', $keys)
+ ));
+ $errors++;
+ }
+
+ foreach ($db as $k => $v) {
+ if ($k === 'password' && empty($v)) {
+ $e->addWarning('database', "$dbField.$k", "The property \"$dbField.$k\" is blank. This may be correct in some controlled environments; it could also be a mistake or a symptom of an insecure configuration.");
+ }
+ elseif (!is_scalar($v)) {
+ $e->addError('database', "$dbField.$k", "The property \"$dbField.$k\" is not well-formed.");
+ $errors++;
+ }
+ }
+
+ if (0 == $errors) {
+ $e->addInfo('database', $dbField, "The database credentials for \"$dbField\" are well-formed.");
+ }
+ }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Verify that the CMS base URL is well-formed.
+ *
+ * Ex: When installing via CLI, the URL cannot be determined automatically.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+ $model = $e->getModel();
+
+ if (!$model->cmsBaseUrl) {
+ return;
+ }
+
+ if (\Civi\Setup\DrupalUtil::isDrush() && preg_match(';^https?://default/?;', $model->cmsBaseUrl)) {
+ // If you run "drush8 en civicrm", it may fabricate the URL as "http://default/". Not good enough b/c this will be stored for future use..
+ $e->addError('system', 'drushUrl', "Please specify a realistic site URL (Ex: drush -l http://example.com:456 ...).");
+ }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Run the PHP+MySQL system requirements checks from Civi\Install\Requirements.
+ *
+ * Aesthetically, I'd sorta prefer to remove this and (instead) migrate the
+ * `Requirements.php` so that each check was its own plugin. But for now this works.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ $model = $e->getModel();
+ $r = new \Civi\Install\Requirements();
+
+ \Civi\Setup::log()->info(sprintf('[%s] Run Requirements::checkSystem()', basename(__FILE__)));
+ $systemMsgs = $r->checkSystem(array(/* no $file_paths to pass - we check those elsewhere */));
+ _corereqadapter_addMessages($e, 'system', $systemMsgs);
+
+ \Civi\Setup::log()->info(sprintf('[%s] Run Requirements::checkDatabase()', basename(__FILE__)));
+ list ($host, $port) = \Civi\Setup\DbUtil::decodeHostPort($model->db['server']);
+ $dbMsgs = $r->checkDatabase(array(
+ 'host' => $host,
+ 'port' => $port,
+ 'username' => $model->db['username'],
+ 'password' => $model->db['password'],
+ 'database' => $model->db['database'],
+ ));
+ _corereqadapter_addMessages($e, 'database', $dbMsgs);
+ });
+
+/**
+ * @param \Civi\Setup\Event\CheckRequirementsEvent $e
+ * Symbolic machine name for this group of messages.
+ * Ex: 'database' or 'system'.
+ * @param array $msgs
+ * A list of messages in the format used by \Civi\Install\Requirements
+ */
+function _corereqadapter_addMessages($e, $section, $msgs) {
+ $severityMap = array(
+ \Civi\Install\Requirements::REQUIREMENT_OK => 'info',
+ \Civi\Install\Requirements::REQUIREMENT_WARNING => 'warning',
+ \Civi\Install\Requirements::REQUIREMENT_ERROR => 'error',
+ );
+
+ foreach ($msgs as $msg) {
+ $e->addMessage($severityMap[$msg['severity']], $section, $msg['title'], $msg['details']);
+ }
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Record a log message at the start and end of each major business operation.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+use Civi\Setup;
+
+$setup = Setup::instance();
+
+$eventNames = array(
+ 'civi.setup.init',
+ 'civi.setup.checkAuthorized',
+ 'civi.setup.checkRequirements',
+ 'civi.setup.checkInstalled',
+ 'civi.setup.installFiles',
+ 'civi.setup.installDatabase',
+ 'civi.setup.uninstallDatabase',
+ 'civi.setup.uninstallFiles',
+ 'civi.setupui.construct',
+ 'civi.setupui.boot',
+);
+foreach ($eventNames as $eventName) {
+ $setup->getDispatcher()
+ ->addListener(
+ $eventName,
+ function ($event) use ($eventName, $setup) {
+ $setup->getLog()->debug("[LogEvents.civi-setup.php] Start $eventName");
+ },
+ Setup::PRIORITY_START + 1
+ );
+ $setup->getDispatcher()
+ ->addListener(
+ $eventName,
+ function ($event) use ($eventName, $setup) {
+ $setup->getLog()->debug("[LogEvents.civi-setup.php] Finish $eventName");
+ },
+ Setup::PRIORITY_END - 1
+ );
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Generate the default web form.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setupui.construct', function (\Civi\Setup\UI\Event\UIConstructEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Construct default UI', basename(__FILE__)));
+
+ $e->setCtrl(new \Civi\Setup\UI\SetupController(\Civi\Setup::instance()));
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Build a list of available CiviCRM components.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ /**
+ * @var \Civi\Setup\Model $m
+ */
+ $m = $e->getModel();
+ $comps = array(
+ 'CiviContribute',
+ 'CiviEvent',
+ 'CiviMail',
+ 'CiviMember',
+ 'CiviCase',
+ 'CiviPledge',
+ 'CiviReport',
+ 'CiviCampaign',
+ 'CiviGrant',
+ );
+ $m->setField('components', 'options', array_combine($comps, $comps));
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Build a list of available translations.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ /**
+ * @var \Civi\Setup\Model $m
+ */
+ $m = $e->getModel();
+
+ $langs = NULL;
+ require implode(DIRECTORY_SEPARATOR, [$m->srcPath, 'install', 'langs.php']);
+ foreach ($langs as $locale => $_) {
+ if ($locale == 'en_US') {
+ continue;
+ }
+ if (!file_exists(implode(DIRECTORY_SEPARATOR, array($m->srcPath, 'sql', "civicrm_data.$locale.mysql")))) {
+ unset($langs[$locale]);
+ }
+ }
+
+ $m->setField('lang', 'options', $langs);
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine default settings for Backdrop.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkAuthorized', function (\Civi\Setup\Event\CheckAuthorizedEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Backdrop') {
+ return;
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkAuthorized'));
+ $e->setAuthorized(user_access('administer modules'));
+ });
+
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Backdrop' || !function_exists('user_access')) {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ $object = new \CRM_Utils_System_Backdrop();
+ $cmsPath = $object->cmsRootPath();
+
+ // Compute settingsPath.
+ $model->settingsPath = $cmsPath . DIRECTORY_SEPARATOR . 'civicrm.settings.php';
+
+ $model->templateCompilePath = 'FIXME';
+
+ // Compute DSN.
+ global $databases;
+ $model->db = $model->cmsDb = array(
+ 'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL),
+ 'username' => $databases['default']['default']['username'],
+ 'password' => $databases['default']['default']['password'],
+ 'database' => $databases['default']['default']['database'],
+ );
+
+ // Compute URLs
+ global $base_url, $base_path;
+ $model->cmsBaseUrl = $base_url . $base_path;
+
+ // Compute general paths
+ // $model->paths['civicrm.files']['url'] = $filePublicPath;
+ $model->paths['civicrm.files']['path'] = implode(DIRECTORY_SEPARATOR,
+ [_backdrop_civisetup_getPublicFiles(), 'civicrm']);
+
+ // Compute templateCompileDir.
+ $model->templateCompilePath = implode(DIRECTORY_SEPARATOR,
+ [_backdrop_civisetup_getPrivateFiles(), 'civicrm', 'templates_c']);
+
+ // Compute default locale.
+ global $language;
+ $model->lang = \Civi\Setup\LocaleUtil::pickClosest($language->langcode, $model->getField('lang', 'options'));
+ });
+
+function _backdrop_civisetup_getPublicFiles() {
+ $filePublicPath = variable_get('file_public_path', conf_path() . '/files');
+
+ if (!CRM_Utils_File::isAbsolute($filePublicPath)) {
+ $ufSystem = new CRM_Utils_System_Backdrop();
+ $cmsPath = $ufSystem->cmsRootPath();
+ $filePublicPath = $cmsPath . DIRECTORY_SEPARATOR . $filePublicPath;
+ }
+
+ // We sometimes get `/./` in the middle. That's silly.
+ $DS = DIRECTORY_SEPARATOR;
+ $filePublicPath = str_replace("$DS.$DS", $DS, $filePublicPath);
+
+ return $filePublicPath;
+}
+
+function _backdrop_civisetup_getPrivateFiles() {
+ $filePrivatePath = variable_get('file_private_path', '');
+
+ if (!$filePrivatePath) {
+ $filePrivatePath = _backdrop_civisetup_getPublicFiles();
+ }
+ elseif ($filePrivatePath && !CRM_Utils_File::isAbsolute($filePrivatePath)) {
+ $ufSystem = new CRM_Utils_System_Backdrop();
+ $cmsPath = $ufSystem->cmsRootPath();
+
+ $filePrivatePath = $cmsPath . DIRECTORY_SEPARATOR . $filePrivatePath;
+ }
+
+ $DS = DIRECTORY_SEPARATOR;
+ $filePrivatePath = str_replace("$DS.$DS", $DS, $filePrivatePath);
+
+ return $filePrivatePath;
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Build a list of available CiviCRM components.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ if (!$e->getModel()->setupPath) {
+ $e->getModel()->setupPath = dirname(dirname(__DIR__));
+ }
+
+ }, \Civi\Setup::PRIORITY_START);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Specify default components.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ $e->getModel()->components = array('CiviEvent', 'CiviContribute', 'CiviMember', 'CiviMail', 'CiviReport');
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine default settings for Drupal 7.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkAuthorized', function (\Civi\Setup\Event\CheckAuthorizedEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Drupal' || !function_exists('user_access')) {
+ return;
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkAuthorized'));
+ $e->setAuthorized(user_access('administer modules'));
+ });
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Drupal' || !function_exists('user_access')) {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ // Compute settingsPath.
+ $drupalSystem = new CRM_Utils_System_Drupal();
+ $cmsPath = $drupalSystem->cmsRootPath();
+ $siteDir = \Civi\Setup\DrupalUtil::getDrupalSiteDir($cmsPath);
+ $model->settingsPath = implode(DIRECTORY_SEPARATOR,
+ [$cmsPath, 'sites', $siteDir, 'civicrm.settings.php']);
+
+ // Compute DSN.
+ global $databases;
+ $model->db = $model->cmsDb = array(
+ 'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL),
+ 'username' => $databases['default']['default']['username'],
+ 'password' => $databases['default']['default']['password'],
+ 'database' => $databases['default']['default']['database'],
+ );
+
+ // Compute cmsBaseUrl.
+ global $base_url, $base_path;
+ $model->cmsBaseUrl = $base_url . $base_path;
+
+ // Compute general paths
+ // $model->paths['civicrm.files']['url'] = $filePublicPath;
+ $model->paths['civicrm.files']['path'] = implode(DIRECTORY_SEPARATOR,
+ [_drupal_civisetup_getPublicFiles(), 'civicrm']);
+
+ // Compute templateCompileDir.
+ $model->templateCompilePath = implode(DIRECTORY_SEPARATOR,
+ [_drupal_civisetup_getPrivateFiles(), 'civicrm', 'templates_c']);
+
+ // Compute default locale.
+ global $language;
+ $model->lang = \Civi\Setup\LocaleUtil::pickClosest($language->langcode, $model->getField('lang', 'options'));
+ });
+
+function _drupal_civisetup_getPublicFiles() {
+ $filePublicPath = variable_get('file_public_path', conf_path() . '/files');
+
+ if (!CRM_Utils_File::isAbsolute($filePublicPath)) {
+ $drupalSystem = new CRM_Utils_System_Drupal();
+ $cmsPath = $drupalSystem->cmsRootPath();
+ $filePublicPath = $cmsPath . DIRECTORY_SEPARATOR . $filePublicPath;
+ }
+
+ return $filePublicPath;
+}
+
+function _drupal_civisetup_getPrivateFiles() {
+ $filePrivatePath = variable_get('file_private_path', '');
+
+ if (!$filePrivatePath) {
+ $filePrivatePath = _drupal_civisetup_getPublicFiles();
+ }
+ elseif ($filePrivatePath && !CRM_Utils_File::isAbsolute($filePrivatePath)) {
+ $drupalSystem = new CRM_Utils_System_Drupal();
+ $cmsPath = $drupalSystem->cmsRootPath();
+
+ $filePrivatePath = $cmsPath . DIRECTORY_SEPARATOR . $filePrivatePath;
+ }
+
+ return $filePrivatePath;
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine default settings for Drupal 8.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkAuthorized', function (\Civi\Setup\Event\CheckAuthorizedEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Drupal8' || !is_callable(['Drupal', 'currentUser'])) {
+ return;
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkAuthorized'));
+ $e->setAuthorized(\Civi\Setup\DrupalUtil::isDrush() || \Drupal::currentUser()->hasPermission('administer modules'));
+ });
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'Drupal8' || !is_callable(['Drupal', 'currentUser'])) {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ $cmsPath = \Drupal::root();
+
+ // Compute settingsPath.
+ $siteDir = \Civi\Setup\DrupalUtil::getDrupalSiteDir($cmsPath);
+ $model->settingsPath = implode(DIRECTORY_SEPARATOR, [$cmsPath, 'sites', $siteDir, 'civicrm.settings.php']);
+
+ if (($loadGenerated = \Drupal\Core\Site\Settings::get('civicrm_load_generated', NULL)) !== NULL) {
+ $model->loadGenerated = $loadGenerated;
+ }
+
+ // Compute DSN.
+ $connectionOptions = \Drupal::database()->getConnectionOptions();
+ $model->db = $model->cmsDb = array(
+ 'server' => \Civi\Setup\DbUtil::encodeHostPort($connectionOptions['host'], $connectionOptions['port'] ?: NULL),
+ 'username' => $connectionOptions['username'],
+ 'password' => $connectionOptions['password'],
+ 'database' => $connectionOptions['database'],
+ );
+
+ // Compute cmsBaseUrl.
+ if (empty($model->cmsBaseUrl)) {
+ global $base_url, $base_path;
+ $model->cmsBaseUrl = $base_url . $base_path;
+ }
+
+ // Compute general paths
+ $model->paths['civicrm.files']['url'] = implode('/', [$model->cmsBaseUrl, \Drupal\Core\StreamWrapper\PublicStream::basePath(), 'civicrm']);
+ $model->paths['civicrm.files']['path'] = implode(DIRECTORY_SEPARATOR, [_drupal8_civisetup_getPublicFiles(), 'civicrm']);
+
+ // Compute templateCompileDir.
+ $model->templateCompilePath = implode(DIRECTORY_SEPARATOR, [_drupal8_civisetup_getPrivateFiles(), 'civicrm', 'templates_c']);
+
+ // Compute default locale.
+ $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
+ $model->lang = \Civi\Setup\LocaleUtil::pickClosest($langcode, $model->getField('lang', 'options'));
+ });
+
+function _drupal8_civisetup_getPublicFiles() {
+ $filePublicPath = \Drupal\Core\StreamWrapper\PublicStream::basePath();
+
+ if (!$filePublicPath) {
+ throw new \Civi\Setup\Exception\InitException("Failed to identify public files path");
+ }
+ elseif (!CRM_Utils_File::isAbsolute($filePublicPath)) {
+ $filePublicPath = \Drupal::root() . DIRECTORY_SEPARATOR . $filePublicPath;
+ }
+
+ return $filePublicPath;
+}
+
+function _drupal8_civisetup_getPrivateFiles() {
+ $filePrivatePath = \Drupal\Core\StreamWrapper\PrivateStream::basePath();
+
+ if (!$filePrivatePath) {
+ $filePrivatePath = _drupal8_civisetup_getPublicFiles();
+ }
+ elseif ($filePrivatePath && !CRM_Utils_File::isAbsolute($filePrivatePath)) {
+ $filePrivatePath = \Drupal::root() . DIRECTORY_SEPARATOR . $filePrivatePath;
+ }
+
+ return $filePrivatePath;
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * This is an example plugin which manipulates the installation options.
+ *
+ * Note: The filename `Example.disabled.php` indicates that the example is
+ * a disabled. A real plugin must end in `*.civi-setup.php`.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ // Override the default list of Civi components.
+ $e->getModel()->components = array('CiviEvent', 'CiviContribute', 'CiviMember', 'CiviMail');
+
+ // Activate some extensions during installation.
+ $e->getModel()->extensions[] = 'org.civicrm.flexmailer';
+
+ // Manipulate some settings during installation.
+ $e->getModel()->settings['max_attachments'] = 10;
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Determine default settings for WordPress.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkAuthorized', function (\Civi\Setup\Event\CheckAuthorizedEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'WordPress') {
+ return;
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkAuthorized'));
+ $e->setAuthorized(current_user_can('activate_plugins'));
+ });
+
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.init', function (\Civi\Setup\Event\InitEvent $e) {
+ $model = $e->getModel();
+ if ($model->cms !== 'WordPress' || !function_exists('current_user_can')) {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'init'));
+
+ // Note: We know WP is bootstrapped, but we don't know if the `civicrm` plugin is active,
+ // so we have to make an educated guess.
+ $civicrmPluginFile = implode(DIRECTORY_SEPARATOR, [WP_PLUGIN_DIR, 'civicrm', 'civicrm.php']);
+
+ // Compute settingsPath.
+ $uploadDir = wp_upload_dir();
+ $preferredSettingsPath = $uploadDir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm.settings.php';
+ $oldSettingsPath = plugin_dir_path($civicrmPluginFile) . 'civicrm.settings.php';
+ if (file_exists($preferredSettingsPath)) {
+ $model->settingsPath = $preferredSettingsPath;
+ }
+ elseif (file_exists($oldSettingsPath)) {
+ $model->settingsPath = $oldSettingsPath;
+ }
+ else {
+ $model->settingsPath = $preferredSettingsPath;
+ }
+
+ $model->templateCompilePath = implode(DIRECTORY_SEPARATOR, [$uploadDir['basedir'], 'civicrm', 'templates_c']);
+
+ // Compute DSN.
+ $model->db = $model->cmsDb = array(
+ 'server' => DB_HOST,
+ 'username' => DB_USER,
+ 'password' => DB_PASSWORD,
+ 'database' => DB_NAME,
+ );
+
+ // Compute URLs
+ $model->cmsBaseUrl = site_url();
+ $model->paths['wp.frontend.base']['url'] = home_url() . '/';
+ $model->paths['wp.backend.base']['url'] = admin_url();
+ $model->mandatorySettings['userFrameworkResourceURL'] = plugin_dir_url($civicrmPluginFile) . 'civicrm';
+
+ // Compute default locale.
+ $langs = $model->getField('lang', 'options');
+ $wpLang = get_locale();
+ $model->lang = isset($langs[$wpLang]) ? $wpLang : 'en_US';
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Perform the first system bootstrap.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Bootstrap CiviCRM', basename(__FILE__)));
+
+ if (!defined('CIVICRM_SETTINGS_PATH')) {
+ define('CIVICRM_SETTINGS_PATH', $e->getModel()->settingsPath);
+ }
+
+ if (realpath(CIVICRM_SETTINGS_PATH) !== realpath($e->getModel()->settingsPath)) {
+ throw new \RuntimeException(sprintf("Cannot boot: The civicrm.settings.php path appears inconsistent (%s vs %s)", CIVICRM_SETTINGS_PATH, $e->getModel()->settingsPath));
+ }
+
+ include_once CIVICRM_SETTINGS_PATH;
+
+ require_once 'CRM/Core/ClassLoader.php';
+ CRM_Core_ClassLoader::singleton()->register();
+
+ CRM_Core_Config::singleton(TRUE);
+
+ }, \Civi\Setup::PRIORITY_MAIN - 200);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Configure settings on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if ($e->getModel()->cms !== 'Backdrop') {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Flush CMS metadata', basename(__FILE__)));
+
+ system_rebuild_module_data();
+ module_enable(array('civicrm', 'civicrmtheme'));
+ backdrop_flush_all_caches();
+ civicrm_install_set_backdrop_perms();
+ }, \Civi\Setup::PRIORITY_LATE + 50);
+
+function civicrm_install_set_backdrop_perms() {
+ $perms = array(
+ 'access all custom data',
+ 'access uploaded files',
+ 'make online contributions',
+ 'profile create',
+ 'profile edit',
+ 'profile view',
+ 'register for events',
+ 'view event info',
+ 'view event participants',
+ 'access CiviMail subscribe/unsubscribe pages',
+ );
+
+ // Adding a permission that has not yet been assigned to a module by
+ // a hook_permission implementation results in a database error.
+ // CRM-9042
+ $allPerms = array_keys(module_invoke_all('permission'));
+ foreach (array_diff($perms, $allPerms) as $perm) {
+ watchdog('civicrm',
+ 'Cannot grant the %perm permission because it does not yet exist.',
+ array('%perm' => $perm),
+ WATCHDOG_ERROR
+ );
+ }
+ $perms = array_intersect($perms, $allPerms);
+ user_role_grant_permissions(BACKDROP_AUTHENTICATED_ROLE, $perms);
+ user_role_grant_permissions(BACKDROP_ANONYMOUS_ROLE, $perms);
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Finalize any extra CMS changes in Drupal.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if ($e->getModel()->cms !== 'Drupal') {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Flush CMS metadata', basename(__FILE__)));
+
+ system_rebuild_module_data();
+ module_enable(array('civicrm', 'civicrmtheme'));
+ drupal_flush_all_caches();
+ civicrm_install_set_drupal_perms();
+
+ }, \Civi\Setup::PRIORITY_LATE - 50);
+
+function civicrm_install_set_drupal_perms() {
+ if (!function_exists('db_select')) {
+ db_query('UPDATE {permission} SET perm = CONCAT( perm, \', access CiviMail subscribe/unsubscribe pages, access all custom data, access uploaded files, make online contributions, profile listings and forms, register for events, view event info, view event participants\') WHERE rid IN (1, 2)');
+ }
+ else {
+ $perms = array(
+ 'access all custom data',
+ 'access uploaded files',
+ 'make online contributions',
+ 'profile create',
+ 'profile edit',
+ 'profile view',
+ 'register for events',
+ 'view event info',
+ 'view event participants',
+ 'access CiviMail subscribe/unsubscribe pages',
+ );
+
+ // Adding a permission that has not yet been assigned to a module by
+ // a hook_permission implementation results in a database error.
+ // CRM-9042
+ $allPerms = array_keys(module_invoke_all('permission'));
+ foreach (array_diff($perms, $allPerms) as $perm) {
+ watchdog('civicrm',
+ 'Cannot grant the %perm permission because it does not yet exist.',
+ array('%perm' => $perm),
+ WATCHDOG_ERROR
+ );
+ }
+ $perms = array_intersect($perms, $allPerms);
+ user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
+ user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
+ }
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Finalize any extra CMS changes in Drupal.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if ($e->getModel()->cms !== 'Drupal8') {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Flush CMS metadata', basename(__FILE__)));
+
+ system_rebuild_module_data();
+ \Drupal::service('module_installer')->install(['civicrm', 'civicrmtheme']);
+ drupal_flush_all_caches();
+ civicrm_install_set_drupal8_perms();
+
+ }, \Civi\Setup::PRIORITY_LATE - 50);
+
+function civicrm_install_set_drupal8_perms() {
+ $perms = array(
+ 'access all custom data',
+ 'access uploaded files',
+ 'make online contributions',
+ 'profile create',
+ 'profile edit',
+ 'profile view',
+ 'register for events',
+ 'view event info',
+ 'view event participants',
+ 'access CiviMail subscribe/unsubscribe pages',
+ );
+
+ // Adding a permission that has not yet been assigned to a module by
+ // a hook_permission implementation results in a database error.
+ // CRM-9042
+
+ /** @var \Drupal\user\PermissionHandlerInterface $permissionHandler */
+ $permissionHandler = \Drupal::service('user.permissions');
+
+ $allPerms = array_keys($permissionHandler->getPermissions());
+ foreach (array_diff($perms, $allPerms) as $perm) {
+ \Drupal::logger('my_module')->error('Cannot grant the %perm permission because it does not yet exist.', [
+ '%perm' => $perm,
+ ]);
+ }
+ $perms = array_intersect($perms, $allPerms);
+ user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
+ user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
+}
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Configure settings on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if ($e->getModel()->cms !== 'WordPress') {
+ return;
+ }
+ \Civi\Setup::log()->info(sprintf('[%s] Flush CMS metadata', basename(__FILE__)));
+
+ // Should we set the default permissions -- like in Drupal?
+ }, \Civi\Setup::PRIORITY_LATE + 50);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Activate Civi components on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ \Civi\Setup::log()->info('[InstallComponents.civi-setup.php] Activate components: ' . implode(" ", $e->getModel()->components));
+
+ if (empty($e->getModel()->components)) {
+ throw new \Exception("System must have at least one active component.");
+ }
+
+ \Civi::settings()->set('enable_components', $e->getModel()->components);
+ }, \Civi\Setup::PRIORITY_LATE + 300);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Activate Civi extensions on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if (!$e->getModel()->extensions) {
+ \Civi\Setup::log()->info('[InstallExtensions.civi-setup.php] No extensions to activate.');
+ return;
+ }
+
+ \Civi\Setup::log()->info('[InstallExtensions.civi-setup.php] Activate extensions: ' . implode(' ', $e->getModel()->extensions));
+ \civicrm_api3('Extension', 'enable', array(
+ 'keys' => $e->getModel()->extensions,
+ ));
+ }, \Civi\Setup::PRIORITY_LATE + 200);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Populate the database schema.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+class InstallSchemaPlugin implements \Symfony\Component\EventDispatcher\EventSubscriberInterface {
+
+ public static function getSubscribedEvents() {
+ return [
+ 'civi.setup.checkRequirements' => [
+ ['checkXmlFiles', 0],
+ ['checkSqlFiles', 0],
+ ],
+ 'civi.setup.installDatabase' => [
+ ['installDatabase', 0],
+ ],
+ ];
+ }
+
+ public function checkXmlFiles(\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ $m = $e->getModel();
+ $files = array(
+ 'xmlMissing' => implode(DIRECTORY_SEPARATOR, [$m->srcPath, 'xml']),
+ 'xmlSchemaMissing' => implode(DIRECTORY_SEPARATOR, [$m->srcPath, 'xml', 'schema', 'Schema.xml']),
+ 'xmlVersionMissing' => implode(DIRECTORY_SEPARATOR, [$m->srcPath, 'xml', 'version.xml']),
+ );
+
+ foreach ($files as $key => $file) {
+ if (!file_exists($file)) {
+ $e->addError('system', $key, "Schema file is missing: \"$file\"");
+ }
+ }
+ }
+
+ public function checkSqlFiles(\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+ $seedLanguage = $e->getModel()->lang;
+ $sqlPath = $e->getModel()->srcPath . DIRECTORY_SEPARATOR . 'sql';
+
+ if (!$seedLanguage || $seedLanguage === 'en_US') {
+ $e->addInfo('system', 'lang', "Default language is allowed");
+ return;
+ }
+
+ if (!preg_match('/^[a-z][a-z]_[A-Z][A-Z]$/', $seedLanguage)) {
+ $e->addError('system', 'langMalformed', 'Language name is malformed.');
+ return;
+ }
+
+ if (!file_exists($e->getModel()->settingsPath)) {
+ $e->addError('system', 'settingsPath', sprintf('The CiviCRM setting file is missing.'));
+ }
+
+ $e->addInfo('system', 'lang', "Language $seedLanguage is allowed.");
+ }
+
+ public function installDatabase(\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Install database schema', basename(__FILE__)));
+
+ $model = $e->getModel();
+
+ $sqlPath = $model->srcPath . DIRECTORY_SEPARATOR . 'sql';
+ $spec = $this->loadSpecification($model->srcPath);
+
+ $conn = \Civi\Setup\DbUtil::connect($model->db);
+ \CRM_Core_I18n::$SQL_ESCAPER = function($text) use ($conn) {
+ return $conn->escape_string($text);
+ };
+
+ \Civi\Setup::log()->info(sprintf('[%s] Load basic tables', basename(__FILE__)));
+ \Civi\Setup\DbUtil::sourceSQL($model->db, \Civi\Setup\SchemaGenerator::generateCreateSql($model->srcPath, $spec->database, $spec->tables));
+
+ $seedLanguage = $model->lang;
+ if (!empty($model->loadGenerated)) {
+ \Civi\Setup::log()->info(sprintf('[%s] Load sample data', basename(__FILE__)));
+ // At time of writing, `generateSampleData()` is not yet a full replacement for `civicrm_generated.mysql`.
+ \Civi\Setup\DbUtil::sourceSQL($model->db, file_get_contents($sqlPath . DIRECTORY_SEPARATOR . 'civicrm_generated.mysql'));
+ // \Civi\Setup\DbUtil::sourceSQL($model->db, \Civi\Setup\SchemaGenerator::generateSampleData($model->srcPath));
+ }
+ elseif ($seedLanguage) {
+ global $tsLocale;
+ $tsLocale = $seedLanguage;
+ \Civi\Setup::log()->info(sprintf('[%s] Load basic data', basename(__FILE__)));
+ \Civi\Setup\DbUtil::sourceSQL($model->db, \Civi\Setup\SchemaGenerator::generateBasicData($model->srcPath));
+ }
+
+ require_once $model->settingsPath;
+ \Civi\Core\Container::boot(TRUE);
+
+ \CRM_Core_I18n::$SQL_ESCAPER = NULL;
+ }
+
+ /**
+ * @param string $srcPath
+ * @return \CRM_Core_CodeGen_Specification
+ */
+ protected function loadSpecification($srcPath) {
+ $schemaFile = implode(DIRECTORY_SEPARATOR, [$srcPath, 'xml', 'schema', 'Schema.xml']);
+ $versionFile = implode(DIRECTORY_SEPARATOR, [$srcPath, 'xml', 'version.xml']);
+ $xmlBuilt = \CRM_Core_CodeGen_Util_Xml::parse($versionFile);
+ $buildVersion = preg_replace('/^(\d{1,2}\.\d{1,2})\.(\d{1,2}|\w{4,7})$/i', '$1', $xmlBuilt->version_no);
+ $specification = new \CRM_Core_CodeGen_Specification();
+ $specification->parse($schemaFile, $buildVersion, FALSE);
+ return $specification;
+ }
+
+}
+
+\Civi\Setup::dispatcher()->addSubscriber(new InstallSchemaPlugin());
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Configure settings on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ foreach ($e->getModel()->settings as $settingKey => $settingValue) {
+ \Civi\Setup::log()->info(sprintf('[%s] Set value of %s', basename(__FILE__), $settingKey));
+
+ \Civi::settings()->set($settingKey, $settingValue);
+ }
+ }, \Civi\Setup::PRIORITY_LATE + 100);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Configure settings on the newly populated database.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installDatabase', function (\Civi\Setup\Event\InstallDatabaseEvent $e) {
+ if ($e->getModel()->lang) {
+ \Civi\Setup::log()->info('[SetLanguage.civi-setup.php] Set default language to ' . $e->getModel()->lang);
+ \Civi::settings()->set('lcMessages', $e->getModel()->lang);
+ }
+ }, \Civi\Setup::PRIORITY_LATE + 400);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Validate and create the template compile folder.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function (\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+ $m = $e->getModel();
+
+ if (empty($m->templateCompilePath)) {
+ $e->addError('system', 'templateCompilePath', sprintf('The templateCompilePath is undefined.'));
+ }
+ else {
+ $e->addInfo('system', 'templateCompilePath', 'The templateCompilePath is defined.');
+ }
+
+ if (!\Civi\Setup\FileUtil::isCreateable($m->templateCompilePath)) {
+ $e->addError('system', 'templateCompilePathWritable', sprintf('The template compile dir "%s" cannot be created. Ensure the parent folder is writable.', $m->templateCompilePath));
+ }
+ else {
+ $e->addInfo('system', 'templateCompilePathWritable', sprintf('The template compile dir "%s" can be created.', $m->templateCompilePath));
+ }
+ });
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installFiles', function (\Civi\Setup\Event\InstallFilesEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'installFiles'));
+ $m = $e->getModel();
+
+ if (!file_exists($m->templateCompilePath)) {
+ Civi\Setup::log()->info('[CreateTemplateCompilePath.civi-setup.php] mkdir "{path}"', [
+ 'path' => $m->templateCompilePath,
+ ]);
+ mkdir($m->templateCompilePath, 0777, TRUE);
+ \Civi\Setup\FileUtil::makeWebWriteable($m->templateCompilePath);
+ }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Generate the site key.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installFiles', function (\Civi\Setup\Event\InstallFilesEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'installFiles'));
+
+ $toAlphanum = function($bits) {
+ return preg_replace(';[^a-zA-Z0-9];', '', base64_encode($bits));
+ };
+
+ if (!empty($e->getModel()->siteKey)) {
+ // skip
+ }
+ elseif (function_exists('random_bytes')) {
+ $e->getModel()->siteKey = $toAlphanum(random_bytes(32));
+ }
+ elseif (function_exists('openssl_random_pseudo_bytes')) {
+ $e->getModel()->siteKey = $toAlphanum(openssl_random_pseudo_bytes(32));
+ }
+ else {
+ throw new \RuntimeException("Failed to generate a random site key");
+ }
+
+ \Civi\Setup::log()->info(sprintf('[%s] Done %s', basename(__FILE__), 'installFiles'));
+
+ }, \Civi\Setup::PRIORITY_PREPARE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Generate the civicrm.settings.php file.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+/**
+ * Validate the $model.
+ */
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.checkRequirements', function(\Civi\Setup\Event\CheckRequirementsEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'checkRequirements'));
+
+ /**
+ * @var \Civi\Setup\Model $m
+ */
+ $m = $e->getModel();
+
+ if (empty($m->settingsPath)) {
+ $e->addError('system', 'settingsPath', sprintf('The settingsPath is undefined.'));
+ }
+ else {
+ $e->addInfo('system', 'settingsPath', sprintf('The settingsPath is defined.'));
+ }
+
+ if (!\Civi\Setup\FileUtil::isCreateable($m->settingsPath)) {
+ $e->addError('system', 'settingsWritable', sprintf('The settings file "%s" cannot be created. Ensure the parent folder is writable.', $m->settingsPath));
+ }
+ else {
+ $e->addInfo('system', 'settingsWritable', sprintf('The settings file "%s" can be created.', $m->settingsPath));
+ }
+ });
+
+/**
+ * Read the $model and create the "civicrm.settings.php".
+ */
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.installFiles', function (\Civi\Setup\Event\InstallFilesEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'installFiles'));
+
+ /**
+ * @var \Civi\Setup\Model $m
+ */
+ $m = $e->getModel();
+
+ // Map from the logical $model to civicrm.settings.php variables.
+ $params = array();
+ $params['crmRoot'] = addslashes(rtrim($m->srcPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR);
+ $params['templateCompileDir'] = addslashes($m->templateCompilePath);
+ // ??why is frontEnd=0??
+ $params['frontEnd'] = 0;
+ $params['baseURL'] = addslashes(rtrim($m->cmsBaseUrl, '/'));
+ $params['dbUser'] = addslashes($m->db['username']);
+ $params['dbPass'] = addslashes($m->db['password']);
+ $params['dbHost'] = addslashes($m->db['server']);
+ $params['dbName'] = addslashes($m->db['database']);
+ $params['cms'] = addslashes($m->cms);
+ $params['CMSdbUser'] = addslashes($m->cmsDb['username']);
+ $params['CMSdbPass'] = addslashes($m->cmsDb['password']);
+ $params['CMSdbHost'] = addslashes($m->cmsDb['server']);
+ $params['CMSdbName'] = addslashes($m->cmsDb['database']);
+ $params['siteKey'] = addslashes($m->siteKey);
+
+ $extraSettings = array();
+
+ foreach ($m->paths as $key => $aspects) {
+ foreach ($aspects as $aspect => $value) {
+ $extraSettings[] = sprintf('$civicrm_paths[%s][%s] = %s;', var_export($key, 1), var_export($aspect, 1), var_export($value, 1));
+ }
+ }
+
+ foreach ($m->mandatorySettings as $key => $value) {
+ $extraSettings[] = sprintf('$civicrm_setting[%s][%s] = %s;', '\'domain\'', var_export($key, 1), var_export($value, 1));
+ }
+
+ // FIXME $m->defaultSettings, $m->components, $m->extensions, $m->callbacks
+
+ if ($extraSettings) {
+ $params['extraSettings'] = "Additional settings generated by installer:\n" . implode("\n", $extraSettings);
+ }
+ else {
+ $params['extraSettings'] = "";
+ }
+
+ $parent = dirname($m->settingsPath);
+ if (!file_exists($parent)) {
+ Civi\Setup::log()->info('[InstallSettingsFile.civi-setup.php] mkdir "{path}"', ['path' => $parent]);
+ mkdir($parent, 0777, TRUE);
+ \Civi\Setup\FileUtil::makeWebWriteable($parent);
+ }
+
+ // And persist it...
+ $tplPath = implode(DIRECTORY_SEPARATOR,
+ [$m->srcPath, 'templates', 'CRM', 'common', 'civicrm.settings.php.template']
+ );
+ $str = file_get_contents($tplPath);
+ foreach ($params as $key => $value) {
+ $str = str_replace('%%' . $key . '%%', $value, $str);
+ }
+ $str = trim($str) . "\n";
+ file_put_contents($m->settingsPath, $str);
+
+ }, \Civi\Setup::PRIORITY_LATE);
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Cleanup any CiviCRM session state after uninstallation.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.uninstallDatabase', function (\Civi\Setup\Event\UninstallDatabaseEvent $e) {
+ $supportedCms = array('Drupal', 'Backdrop');
+ if (!in_array($e->getModel()->cms, $supportedCms)) {
+ return;
+ }
+ \Civi\Setup::log()->info('[CleanupDrupalSession.civi-setup.php] Purge Drupal session state which have stale CiviCRM references');
+
+ // This keeps the Drupal user logged in, but it purges any data.
+
+ // It's a bit ham-handed, but no one provides an API like this, and a
+ // more surgical approach would get messy (due to variations of session-encoding),
+ // and... it seems to work...
+
+ db_query('UPDATE sessions SET session = NULL');
+
+ // foreach(db_query('SELECT sid FROM sessions') as $sid) {
+ // $sessionResult = db_query('SELECT session FROM sessions WHERE sid = :sid', array(
+ // 'sid' => $sid->sid,
+ // ));
+ // foreach ($sessionResult as $session) {
+ // $data = session_decode($session->session); // blerg, nothign does this right :(
+ // print_r(['sr'=>$session, 'data'=>$data]);
+ // if (!empty($data['CiviCRM'])) {
+ // echo "must clear " . $sid->sid . "\n";
+ // unset($data['CiviCRM']);
+ // reserialize and write back to DB
+ // }
+ // else {
+ // echo "ignore " . $sid->sid . "\n";
+ // }
+ //
+ // }
+ // }
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Populate the database schema.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.uninstallDatabase', function (\Civi\Setup\Event\UninstallDatabaseEvent $e) {
+ \Civi\Setup::log()->info('[UninstallSchema.civi-setup.php] Remove all tables and views (civicrm_* and log_civicrm_*)');
+ $model = $e->getModel();
+
+ $conn = \Civi\Setup\DbUtil::connect($model->db);
+ \Civi\Setup\DbUtil::execute($conn, 'SET FOREIGN_KEY_CHECKS=0;');
+
+ foreach (\Civi\Setup\DbUtil::findViews($conn, $model->db['database']) as $view) {
+ if (preg_match('/^(civicrm_|log_civicrm_)/', $view)) {
+ \Civi\Setup\DbUtil::execute($conn, sprintf('DROP VIEW `%s`', $conn->escape_string($view)));
+ }
+ }
+
+ foreach (\Civi\Setup\DbUtil::findTables($conn, $model->db['database']) as $table) {
+ if (preg_match('/^(civicrm_|log_civicrm_)/', $table)) {
+ \Civi\Setup\DbUtil::execute($conn, sprintf('DROP TABLE `%s`', $conn->escape_string($table)));
+ }
+ }
+
+ // TODO Perhaps we should also remove stored-procedures/functions?
+
+ $conn->close();
+ });
--- /dev/null
+<?php
+/**
+ * @file
+ *
+ * Remove the civicrm.settings.php file.
+ */
+
+if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+}
+
+\Civi\Setup::dispatcher()
+ ->addListener('civi.setup.uninstallFiles', function (\Civi\Setup\Event\UninstallFilesEvent $e) {
+ \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'uninstallFiles'));
+
+ $file = $e->getModel()->settingsPath;
+ if (file_exists($file)) {
+ if (!\Civi\Setup\FileUtil::isDeletable($file)) {
+ throw new \Exception("Cannot remove \"$file\". Please check permissions on the file and directory.");
+ }
+ unlink($file);
+ }
+ });
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><?php echo $errorTitle ?></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href=<?php echo $installURLPath . "template.css"?> />
+</head>
+
+<body>
+<div id="All">
+ <h1><?php echo $errorTitle ?></h1>
+
+ <?php echo $errorMsg ?>
+
+</div>
+</body>
+</html>
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?>
+<?php
+throw new \Exception("A draft copy of this file is available but has not been tested. Please edit " . __FILE__);
+
+// FIXME: Compute URL's with backdrop functions (e.g. 'url(...)')
+// FIXME: Just echo instead of doing $output silliness.
+// FIXME: Use finished.Common.php instead of $commonOutputMessage.
+
+$registerSiteURL = "https://civicrm.org/register-site";
+$commonOutputMessage = "<li>" . ts("Have you registered this site at CiviCRM.org? If not, please help strengthen the CiviCRM ecosystem by taking a few minutes to <a %1>fill out the site registration form</a>. The information collected will help us prioritize improvements, target our communications and build the community. If you have a technical role for this site, be sure to check Keep in Touch to receive technical updates (a low volume mailing list).", array(1 => "href='$registerSiteURL' target='_blank'")) . "</li>"
+. "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
+
+$output = NULL;
+$output .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
+$output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
+$output .= '<head>';
+$output .= '<title>' . ts('CiviCRM Installed') . '</title>';
+$output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
+$output .= '<link rel="stylesheet" type="text/css" href="template.css" />';
+$output .= '</head>';
+$output .= '<body>';
+$output .= '<div style="padding: 1em;"><p class="good">' . ts('CiviCRM has been successfully installed') . '</p>';
+$output .= '<ul>';
+
+$backdropURL = civicrm_cms_base();
+$backdropPermissionsURL = "{$backdropURL}index.php?q=admin/config/people/permissions";
+$backdropURL .= "index.php?q=civicrm/admin/configtask&reset=1";
+
+$output .= "<li>" . ts("Backdrop user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(
+ 1 => "target='_blank' href='{$backdropPermissionsURL}'",
+ 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'",
+ )) . "</li>";
+$output .= "<li>" . ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$backdropURL'")) . "</li>";
+$output .= $commonOutputMessage;
+$output .= '</ul>';
+$output .= '</div>';
+$output .= '</body>';
+$output .= '</html>';
+echo $output;
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?>
+<?php
+$registerSiteURL = "https://civicrm.org/register-site";
+echo "<li>" . ts("Have you registered this site at CiviCRM.org? If not, please help strengthen the CiviCRM ecosystem by taking a few minutes to <a %1>fill out the site registration form</a>. The information collected will help us prioritize improvements, target our communications and build the community. If you have a technical role for this site, be sure to check Keep in Touch to receive technical updates (a low volume mailing list).", array(1 => "href='$registerSiteURL' target='_blank'")) . "</li>";
+echo "<li>" . ts("We have integrated KCFinder with CKEditor and TinyMCE. This allows a user to upload images. All uploaded images are public.") . "</li>";
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?>
+<?php
+throw new \Exception("A draft copy of this file is available but has not been tested. Please edit " . __FILE__);
+
+$drupalPermissionsURL = url('admin/people/permissions');
+$drupalURL = url('civicrm/admin/configtask', array(
+ 'query' => array(
+ 'reset' => 1,
+ ),
+));
+?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><?php echo ts('CiviCRM Installed'); ?></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="stylesheet" type="text/css" href="template.css"/>
+</head>
+<body>
+<div style="padding: 1em;">
+ <h1><?php echo ts('CiviCRM Installed'); ?></h1>
+ <p class="good"><?php echo ts('CiviCRM has been successfully installed'); ?></p>
+ <ul>
+ <li><?php echo ts("Drupal user permissions have been automatically set - giving anonymous and authenticated users access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(
+ 1 => "target='_blank' href='{$drupalPermissionsURL}'",
+ 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'",
+ )); ?></li>
+ <li><?php echo ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$drupalURL'")); ?></li>
+ <?php include 'finished.Common.php'; ?>
+ </ul>
+</div>
+</body>
+</html>
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?>
+<?php
+// I don't really understand the behavior here -- seems to spend a lot of work
+// building a page, and then it immediately redirects away...
+
+$cmsURL = admin_url('admin.php?page=CiviCRM&q=civicrm/admin/configtask&reset=1');
+$wpPermissionsURL = admin_url('admin.php?page=CiviCRM&q=civicrm/admin/access/wp-permissions&reset=1');
+$wpInstallRedirect = admin_url('admin.php?page=CiviCRM&q=civicrm&reset=1');
+?>
+<h1><?php echo ts('CiviCRM Installed'); ?></h1>
+<div style="padding: 1em;">
+ <p style="background-color: #0C0; border: 1px #070 solid; color: white;">
+ <?php echo ts("CiviCRM has been successfully installed"); ?>
+ </p>
+ <ul>
+ <li><?php
+ echo ts("WordPress user permissions have been automatically set - giving Anonymous and Subscribers access to public CiviCRM forms and features. We recommend that you <a %1>review these permissions</a> to ensure that they are appropriate for your requirements (<a %2>learn more...</a>)", array(
+ 1 => "target='_blank' href='{$wpPermissionsURL}'",
+ 2 => "target='_blank' href='http://wiki.civicrm.org/confluence/display/CRMDOC/Default+Permissions+and+Roles'",
+ ));
+ ?></li>
+ <li><?php
+ echo ts("Use the <a %1>Configuration Checklist</a> to review and configure settings for your new site", array(1 => "target='_blank' href='$cmsURL'"));
+ ?></li>
+ <?php include 'finished.Common.php'; ?>
+ </ul>
+</div>
+<script>
+ window.location = <?php echo json_encode($wpInstallRedirect); ?>;
+</script>
--- /dev/null
+body {
+ background: #eee;
+}
+/* Header */
+.civicrm-setup-header{
+ height: 300px;
+ width: 100%;
+ display: block;
+}
+
+.civicrm-setup-header .title{
+ width: 25%;
+ position: absolute;
+ overflow: hidden;
+ float: left;
+ margin-left: 10%;
+ margin-top: 50px;
+ margin-right: 20%;
+ display: inline-flex;
+}
+
+.civicrm-setup-header h1{
+ line-height: 35px;
+ color: #ddd;
+ position: relative;
+ left: 400px;
+ -webkit-animation: slide 1s forwards;
+ -webkit-animation-delay: 0.5s;
+ animation: slide 1s forwards;
+ animation-delay: 0.5s;
+
+}
+
+@-webkit-keyframes slide{
+ 100% { left: 0; color:#555;}
+}
+
+@keyframes slide{
+ 100% { left: 0; color: #555;}
+}
+
+
+.civicrm-setup-header hr{
+ border-bottom: 2px solid #fff;
+ border-top: 2px solid #ddd;
+}
+
+.civicrm-setup-body .civicrm-logo {
+ float: right;
+ width: 30%;
+ display: grid;
+ margin-top: 50px;
+ margin-right: 10%;
+}
+
+.civicrm-setup-body .civicrm-logo img{
+ width: 100%;
+}
+
+
+/* Header End */
+
+.civicrm-setup-body #All {
+ font-family: Arial, sans-serif;
+ width: auto;
+ margin: 0.5em auto;
+ padding: 1em;
+ border: 1px #ccc solid;
+ border-radius: 10px;
+ background: #fff;
+ max-width: 1200px;
+}
+
+.civicrm-setup-body form {
+ margin: 3%;
+}
+
+.civicrm-setup-body h2 {
+ margin-top: 0;
+ display: inline-block;
+}
+
+.civicrm-setup-body li {
+ padding-bottom: 1.5em;
+}
+
+.civicrm-setup-body label span {
+ float: left;
+ width: 120px;
+}
+
+.civicrm-setup-body p.error {
+ padding: 0.5em;
+ background-color: #c00;
+ border: 1px #700 solid;
+ color: white;
+ clear: both;
+}
+
+.civicrm-setup-body p.warning {
+ padding: 0.5em;
+ background-color: #e70;
+ border: 1px #a70 solid;
+ color: white;
+ clear: both;
+}
+
+.civicrm-setup-body p.good {
+ padding: 0.5em;
+ background-color: #0c0;
+ border: 1px #070 solid;
+ color: white;
+ clear: both;
+}
+
+.civicrm-setup-body p.error a {
+ color: #FFF;
+ font-weight: bold;
+}
+
+.civicrm-setup-body p.tip {
+ background: #ffb;
+ padding: 0.5em;
+ margin: 1em auto;
+ width: 50%
+}
+
+.civicrm-setup-body .advancedTip {
+ border-collapse: collapse;
+ font-size: 80%;
+}
+
+.civicrm-setup-body .reqTable {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+.civicrm-setup-body .reqTable td {
+ border: 1px #ccc solid;
+}
+
+.civicrm-setup-body .reqSeverity-info { color: green; }
+.civicrm-setup-body .reqSeverity-warning { color: #a70; }
+.civicrm-setup-body .reqSeverity-error { color: #c00; }
+
+.civicrm-setup-body .settingsTable {
+ border-collapse: collapse;
+ margin: 2em
+}
+
+.civicrm-setup-body th, .civicrm-setup-body td {
+ text-align: left;
+ padding: 0.25em;
+ vertical-align: top;
+}
+
+.civicrm-setup-body .comp-box {
+ height: 3.5em;
+ width: 31%;
+ display: inline-block;
+ padding: 0.5em;
+ padding-top: 15px;
+ margin: 0.25em;
+ border: 1px dashed #aaa;
+ background: #eee;
+ text-align: center;
+}
+.civicrm-setup-body .comp-box:hover { background: #ddd; cursor: hand; }
+.civicrm-setup-body .comp-box > span { width: 100%; }
+.civicrm-setup-body .comp-label { font-size: 1.5em; }
+.civicrm-setup-body .comp-desc { font-style: italic; }
+.civicrm-setup-body .comp-cb:checked + label { background: #dfd; }
+.civicrm-setup-body .comp-cb:checked + label:hover { background: #cec; }
+
+.civicrm-setup-body .optin-box {
+ height: 3.5em;
+ width: 45%;
+ display: inline-block;
+ padding: 0.5em;
+ margin: 0.75em;
+ border: 1px dashed #aaa;
+ background: #eee;
+ text-align: center;
+}
+.civicrm-setup-body .optin-box:hover { background: #ddd; cursor: hand; }
+.civicrm-setup-body .optin-box > span { width: 100%; }
+.civicrm-setup-body .optin-label { font-size: 1.5em; }
+.civicrm-setup-body .optin-desc { font-style: italic; }
+.civicrm-setup-body .optin-cb:checked + label { background: #dfd; }
+.civicrm-setup-body .optin-cb:checked + label:hover { background: #cec; }
+
+.civicrm-setup-body .sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+
+.civicrm-setup-body.has-errors .if-no-errors {display: none;}
+.civicrm-setup-body.has-warnings .if-no-warnings {display: none;}
+.civicrm-setup-body.has-problems .if-no-problems {display: none;}
+.civicrm-setup-body.has-no-problems .if-problems {display: none;}
+
+.civicrm-setup-body .action-box {
+ width: 100%;
+ text-align: center;
+ margin: 2em 0.5em;
+}
+.civicrm-setup-body input[type=submit] {
+ padding:15px 25px;
+ background:#82C459;
+ color: white;
+ border:0 none;
+ cursor:pointer;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ font-size: 20px;
+}
+.civicrm-setup-body input[type=submit]:hover {
+ background: #60A237;
+}
+
+.civicrm-setup-body .settingsTable input[type=text] { width: 80%; }
+
+
+
+@media only screen and (max-width: 801px){
+ .civicrm-setup-body .comp-box{
+ width: 45%;
+ }
+}
+
+@media only screen and (max-width: 635px){
+ .civicrm-setup-header .title{
+ width: 45%;
+ margin-left: 5%;
+ margin-top: 30px;
+ }
+
+ .civicrm-setup-body .settingsTable{
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ width: 95%;
+ margin: 10px 2% 10px 2%;
+ }
+
+}
+
+@media only screen and (max-width: 503px){
+ .civicrm-setup-body .comp-box{
+ width: 95%;
+ }
+}
+
--- /dev/null
+<?php \Civi\Setup::assertRunning(); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $short_lang_code; ?>" lang="<?php echo $short_lang_code; ?>" dir="<?php echo $text_direction; ?>">
+<head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title><?php echo ts('CiviCRM Installer'); ?></title>
+ <script type="text/javascript" src="<?php echo $ctrl->getUrl('jquery.js'); ?>"></script>
+ <script type="text/javascript">
+ window.csj$ = jQuery.noConflict();
+ </script>
+ <link rel="stylesheet" type="text/css" href=<?php echo $installURLPath . "template.css"?> />
+ <link rel="stylesheet" type="text/css" href=<?php echo $ctrl->getUrl('font-awesome.css') ?> />
+<?php
+if ($text_direction == 'rtl') {
+ echo " <link rel='stylesheet' type='text/css' href='{$installURLPath}template-rtl.css' />\n";
+}
+?>
+</head>
+<body>
+
+<?php
+$mainClasses = array(
+ 'civicrm-setup-body',
+ count($reqs->getErrors()) ? 'has-errors' : '',
+ count($reqs->getWarnings()) ? 'has-warnings' : '',
+ (count($reqs->getErrors()) + count($reqs->getWarnings()) > 0) ? 'has-problems' : '',
+ (count($reqs->getErrors()) + count($reqs->getWarnings()) === 0) ? 'has-no-problems' : '',
+);
+?>
+
+<div class="<?php echo implode(' ', $mainClasses); ?>">
+<div id="All">
+
+<form name="civicrm_form" method="post" action="<?php echo str_replace('%7E', '~', $_SERVER['REQUEST_URI']); ?>">
+
+ <?php echo $ctrl->renderBlocks($_tpl_params); ?>
+
+</form>
+</div>
+</div>
+</body>
+</html>
--- /dev/null
+<?php
+namespace Civi;
+
+use Civi\Setup\Event\CheckAuthorizedEvent;
+use Civi\Setup\Event\CheckRequirementsEvent;
+use Civi\Setup\Event\CheckInstalledEvent;
+use Civi\Setup\UI\Event\UIConstructEvent;
+use Civi\Setup\Event\InitEvent;
+use Civi\Setup\Event\InstallDatabaseEvent;
+use Civi\Setup\Event\InstallFilesEvent;
+use Civi\Setup\Event\UninstallDatabaseEvent;
+use Civi\Setup\Event\UninstallFilesEvent;
+use Civi\Setup\Exception\InitException;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+class Setup {
+
+ const PROTOCOL = '1.0';
+
+ const PRIORITY_START = 2000;
+ const PRIORITY_PREPARE = 1000;
+ const PRIORITY_MAIN = 0;
+ const PRIORITY_LATE = -1000;
+ const PRIORITY_END = -2000;
+
+ private static $instance;
+
+ /**
+ * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ */
+ protected $dispatcher;
+
+ /**
+ * @var \Civi\Setup\Model
+ */
+ protected $model;
+
+ /**
+ * @var \Psr\Log\LoggerInterface
+ */
+ protected $log;
+
+ // ----- Static initialization -----
+
+ /**
+ * The initialization process loads any `*.civi-setup.php` files and
+ * fires the `civi.setup.init` event.
+ *
+ * @param array $modelValues
+ * List of default configuration options.
+ * Recommended fields: 'srcPath', 'cms'
+ * @param callable $pluginCallback
+ * Function which manipulates the list of plugin files.
+ * Use this to add, remove, or re-order callbacks.
+ * function(array $files) => array
+ * Ex: ['hello' => '/var/www/plugins/hello.civi-setup.php']
+ * @param \Psr\Log\LoggerInterface $log
+ */
+ public static function init($modelValues = array(), $pluginCallback = NULL, $log = NULL) {
+ if (!defined('CIVI_SETUP')) {
+ define('CIVI_SETUP', 1);
+ }
+
+ self::$instance = new Setup();
+ self::$instance->model = new \Civi\Setup\Model();
+ self::$instance->model->setValues($modelValues);
+ self::$instance->dispatcher = new EventDispatcher();
+ self::$instance->log = $log ? $log : new NullLogger();
+
+ $pluginDir = dirname(__DIR__) . '/plugins';
+ $pluginFiles = array();
+ foreach (['*.civi-setup.php', '*/*.civi-setup.php'] as $pattern) {
+ foreach ((array) glob("$pluginDir/$pattern") as $file) {
+ $key = substr($file, strlen($pluginDir) + 1);
+ $key = preg_replace('/\.civi-setup\.php$/', '', $key);
+ $pluginFiles[$key] = $file;
+ }
+ }
+ ksort($pluginFiles);
+
+ if ($pluginCallback) {
+ $pluginFiles = $pluginCallback($pluginFiles);
+ }
+
+ foreach ($pluginFiles as $pluginFile) {
+ self::$instance->log->debug('[Setup.php] Load plugin {file}', array(
+ 'file' => $pluginFile,
+ ));
+ require $pluginFile;
+ }
+
+ $event = new InitEvent(self::$instance->getModel());
+ self::$instance->getDispatcher()->dispatch('civi.setup.init', $event);
+ // return $event; ...or... return self::$instance;
+ }
+
+ /**
+ * Assert that this copy of civicrm-setup is compatible with the client.
+ *
+ * @param string $expectedVersion
+ * @throws \Exception
+ */
+ public static function assertProtocolCompatibility($expectedVersion) {
+ if (version_compare(self::PROTOCOL, $expectedVersion, '<')) {
+ throw new InitException(sprintf("civicrm-setup is running protocol v%s. This application expects civicrm-setup to support protocol v%s.", self::PROTOCOL, $expectedVersion));
+ }
+ list ($actualFirst) = explode('.', self::PROTOCOL);
+ list ($expectedFirst) = explode('.', $expectedVersion);
+ if ($actualFirst > $expectedFirst) {
+ throw new InitException(sprintf("civicrm-setup is running protocol v%s. This application expects civicrm-setup to support protocol v%s.", self::PROTOCOL, $expectedVersion));
+ }
+ }
+
+ /**
+ * Assert that the "Setup" subsystem is running.
+ *
+ * This function is mostly just a placeholder -- in practice, if
+ * someone makes a failed call to `assertRunning()`, it will probably
+ * manifest as an unknown class/function. But this gives us a pretty,
+ * one-line, syntactically-valid way to make the assertion.
+ */
+ public static function assertRunning() {
+ if (!defined('CIVI_SETUP')) {
+ exit("Installation plugins must only be loaded by the installer.\n");
+ }
+ }
+
+ /**
+ * @return Setup
+ */
+ public static function instance() {
+ if (self::$instance === NULL) {
+ throw new InitException('\Civi\Setup has not been initialized.');
+ }
+ return self::$instance;
+ }
+
+ /**
+ * @return \Psr\Log\LoggerInterface
+ */
+ public static function log() {
+ return self::instance()->getLog();
+ }
+
+ /**
+ * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ */
+ public static function dispatcher() {
+ return self::instance()->getDispatcher();
+ }
+
+ // ----- Logic ----
+
+ /**
+ * Determine whether the current CMS user is authorized to perform
+ * installation.
+ *
+ * @return \Civi\Setup\Event\CheckAuthorizedEvent
+ */
+ public function checkAuthorized() {
+ $event = new CheckAuthorizedEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.checkAuthorized', $event);
+ }
+
+ /**
+ * Determine whether the local environment meets system requirements.
+ *
+ * @return \Civi\Setup\Event\CheckRequirementsEvent
+ */
+ public function checkRequirements() {
+ $event = new CheckRequirementsEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.checkRequirements', $event);
+ }
+
+ /**
+ * Determine whether the setting and/or schema are already installed.
+ *
+ * @return \Civi\Setup\Event\CheckInstalledEvent
+ */
+ public function checkInstalled() {
+ $event = new CheckInstalledEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.checkInstalled', $event);
+ }
+
+ /**
+ * Create the settings file.
+ *
+ * @return \Civi\Setup\Event\InstallFilesEvent
+ */
+ public function installFiles() {
+ $event = new InstallFilesEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.installFiles', $event);
+ }
+
+ /**
+ * Create the database schema.
+ *
+ * @return \Civi\Setup\Event\InstallDatabaseEvent
+ */
+ public function installDatabase() {
+ $event = new InstallDatabaseEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.installDatabase', $event);
+ }
+
+ /**
+ * Remove the settings file.
+ *
+ * @return \Civi\Setup\Event\UninstallFilesEvent
+ */
+ public function uninstallFiles() {
+ $event = new UninstallFilesEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.uninstallFiles', $event);
+ }
+
+ /**
+ * Remove the database schema.
+ *
+ * @return \Civi\Setup\Event\UninstallDatabaseEvent
+ */
+ public function uninstallDatabase() {
+ $event = new UninstallDatabaseEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setup.uninstallDatabase', $event);
+ }
+
+ /**
+ * Create a page-controller for a web-based installation form.
+ *
+ * @return \Civi\Setup\UI\Event\UIConstructEvent
+ */
+ public function createController() {
+ $event = new UIConstructEvent($this->getModel());
+ return $this->getDispatcher()->dispatch('civi.setupui.construct', $event);
+ }
+
+ // ----- Accessors -----
+
+ /**
+ * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ */
+ public function getDispatcher() {
+ return $this->dispatcher;
+ }
+
+ /**
+ * @return \Civi\Setup\Model
+ */
+ public function getModel() {
+ return $this->model;
+ }
+
+ /**
+ * @return \Psr\Log\LoggerInterface
+ */
+ public function getLog() {
+ return $this->log;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class BasicRunner {
+
+ /**
+ * Execute the controller and display the output.
+ *
+ * Note: This is really just an example which handles input and output using
+ * stock PHP variables and functions. Depending on the environment,
+ * it may be easier to work directly with `getCtrl()->run(...)` which
+ * handles inputs/outputs in a more abstract fashion.
+ *
+ * @param object $ctrl
+ * A web controller.
+ */
+ public static function run($ctrl) {
+ $method = $_SERVER['REQUEST_METHOD'];
+ list ($headers, $body) = $ctrl->run($method, ($method === 'GET' ? $_GET : $_POST));
+ foreach ($headers as $k => $v) {
+ header("$k: $v");
+ }
+ echo $body;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+use Civi\Setup\Exception\SqlException;
+
+class DbUtil {
+
+ /**
+ * @param string $dsn
+ * @return array
+ */
+ public static function parseDsn($dsn) {
+ $parsed = parse_url($dsn);
+ return array(
+ 'server' => self::encodeHostPort($parsed['host'], $parsed['port']),
+ 'username' => $parsed['user'] ?: NULL,
+ 'password' => $parsed['pass'] ?: NULL,
+ 'database' => $parsed['path'] ? ltrim($parsed['path'], '/') : NULL,
+ );
+ }
+
+ /**
+ * Convert an datasource from array notation to URL notation.
+ *
+ * @param array $db
+ * @return string
+ */
+ public static function encodeDsn($db) {
+ return sprintf('mysql://%s:%s@%s/%s',
+ $db['username'],
+ $db['password'],
+ $db['server'],
+ $db['database']
+ );
+ }
+
+ /**
+ * @param array $db
+ * @return \mysqli
+ */
+ public static function softConnect($db) {
+ list($host, $port) = self::decodeHostPort($db['server']);
+ $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port);
+ return $conn;
+ }
+
+ /**
+ * @param array $db
+ * @return \mysqli
+ * @throws SqlException
+ */
+ public static function connect($db) {
+ $conn = self::softConnect($db);
+ if (mysqli_connect_errno()) {
+ throw new SqlException(sprintf("Connection failed: %s\n", mysqli_connect_error()));
+ }
+ return $conn;
+ }
+
+ /**
+ * @param string $host
+ * Ex: 'localhost',
+ * Ex: 'localhost:123'
+ * Ex: '127.0.0.1:123'
+ * Ex: '[1234:abcd]'
+ * Ex: '[1234:abcd]:123'
+ * @return array
+ * Combination: [0 => string $host, 1 => numeric|NULL $port].
+ * Ex: ['localhost', NULL].
+ * Ex: ['127.0.0.1', 3306]
+ */
+ public static function decodeHostPort($host) {
+ $hostParts = explode(':', $host);
+ if (count($hostParts) > 1 && strrpos($host, ']') !== strlen($host) - 1) {
+ $port = array_pop($hostParts);
+ $host = implode(':', $hostParts);
+ }
+ else {
+ $port = NULL;
+ }
+ return array($host, $port);
+ }
+
+ /**
+ * Combine a host and port number.
+ *
+ * @param string $host
+ * @param int|NULL $port
+ * @return string
+ * Ex: 'localhost'.
+ * Ex: '127.0.0.1:3307'.
+ */
+ public static function encodeHostPort($host, $port) {
+ return $host . ($port ? (':' . $port) : '');
+ }
+
+ /**
+ * @param array $db
+ * @param string $SQLcontent
+ * @param bool $lineMode
+ * What does this mean? Seems weird.
+ */
+ public static function sourceSQL($db, $SQLcontent, $lineMode = FALSE) {
+ $conn = self::connect($db);
+
+ $conn->query('SET NAMES ' . ($conn->server_version < 50503 ? 'utf8' : 'utf8mb4'));
+
+ if (!$lineMode) {
+ $string = $SQLcontent;
+
+ // change \r\n to fix windows issues
+ $string = str_replace("\r\n", "\n", $string);
+
+ //get rid of comments starting with # and --
+
+ $string = preg_replace("/^#[^\n]*$/m", "\n", $string);
+ $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
+
+ $queries = preg_split('/;\s*$/m', $string);
+ foreach ($queries as $query) {
+ $query = trim($query);
+ if (!empty($query)) {
+ if ($result = $conn->query($query)) {
+ if (is_object($result)) {
+ mysqli_free_result($result);
+ }
+ }
+ else {
+ throw new SqlException("Cannot execute $query: " . mysqli_error($conn));
+ }
+ }
+ }
+ }
+ else {
+ throw new \RuntimeException("Not implemented: lineMode");
+ // $fd = fopen($SQLcontent, "r");
+ // while ($string = fgets($fd)) {
+ // $string = preg_replace("/^#[^\n]*$/m", "\n", $string);
+ // $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
+ //
+ // $string = trim($string);
+ // if (!empty($string)) {
+ // if ($result = $conn->query($string)) {
+ // if (is_object($result)) {
+ // mysqli_free_result($result);
+ // }
+ // }
+ // else {
+ // throw new SqlException("Cannot execute $string: " . mysqli_error($conn));
+ // }
+ // }
+ // }
+ }
+ }
+
+ /**
+ * Execute query. Ignore the results.
+ *
+ * @param \mysqli|array $conn
+ * The DB to query. Either a mysqli connection, or credentials for
+ * establishing one.
+ * @param string $sql
+ * @throws SqlException
+ */
+ public static function execute($conn, $sql) {
+ $conn = is_array($conn) ? self::connect($conn) : $conn;
+ $result = $conn->query($sql);
+ if (!$result) {
+ throw new SqlException("Cannot execute $sql: " . $conn->error);
+ }
+
+ if ($result && $result !== TRUE) {
+ $result->free_result();
+ }
+
+ }
+
+ /**
+ * Get all the results of a SQL query, as an array.
+ *
+ * @param \mysqli|array $conn
+ * The DB to query. Either a mysqli connection, or credentials for
+ * establishing one.
+ * @param string $sql
+ * @return array
+ * @throws \Exception
+ */
+ public static function fetchAll($conn, $sql) {
+ $conn = is_array($conn) ? self::connect($conn) : $conn;
+ $result = $conn->query($sql);
+ if (!$result) {
+ throw new SqlException("Cannot execute $sql: " . $conn->error);
+ }
+
+ $rows = array();
+ while ($row = $result->fetch_assoc()) {
+ $rows[] = $row;
+ }
+ $result->free_result();
+
+ return $rows;
+ }
+
+ /**
+ * Get a list of views in the given database.
+ *
+ * @param \mysqli|array $conn
+ * The DB to query. Either a mysqli connection, or credentials for
+ * establishing one.
+ * @param string $databaseName
+ * @return array
+ * Ex: ['civicrm_view1', 'civicrm_view2']
+ */
+ public static function findViews($conn, $databaseName) {
+ $sql = sprintf("SELECT table_name FROM information_schema.TABLES WHERE TABLE_SCHEMA='%s' AND TABLE_TYPE = 'VIEW'",
+ $conn->escape_string($databaseName));
+
+ return array_map(function($arr) {
+ return $arr['table_name'];
+ }, self::fetchAll($conn, $sql));
+ }
+
+ /**
+ * Get a list of concrete tables in the given database.
+ *
+ * @param \mysqli|array $conn
+ * The DB to query. Either a mysqli connection, or credentials for
+ * establishing one.
+ * @param string $databaseName
+ * @return array
+ * Ex: ['civicrm_view1', 'civicrm_view2']
+ */
+ public static function findTables($conn, $databaseName) {
+ $sql = sprintf("SELECT table_name FROM information_schema.TABLES WHERE TABLE_SCHEMA='%s' AND TABLE_TYPE = 'BASE TABLE'",
+ $conn->escape_string($databaseName));
+
+ return array_map(function($arr) {
+ return $arr['table_name'];
+ }, self::fetchAll($conn, $sql));
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class DrupalUtil {
+
+ /**
+ * @return bool
+ */
+ public static function isDrush() {
+ return PHP_SAPI === 'cli' && function_exists('drush_main');
+ }
+
+ /**
+ * @param $cmsPath
+ *
+ * @return string
+ */
+ public static function getDrupalSiteDir($cmsPath) {
+ if (function_exists('conf_path')) {
+ return basename(conf_path());
+ }
+ elseif (class_exists('Drupal')) {
+ return basename(\Drupal::service('site.path'));
+ }
+ else {
+ throw new \Exception('Cannot detect path under Drupal "sites/".');
+ // The old 'install/index.php' system duplicated the conf_path() logic so that it could work pre-boot.
+ // With civicrm-setup, the CMS should always be booted first, so we should never go down this path.
+ // For the moment, the code is kept below in case it turns out we do need this for some reason.
+ }
+
+ /*
+ static $siteDir = '';
+
+ if ($siteDir) {
+ return $siteDir;
+ }
+
+ // The SCRIPT_FILENAME check was copied over from the 'install/index.php' system.
+ // It probably doesn't make sense in the context of civicrm-setup b/c we don't know what the SCRIPT will be
+ // and instead rely on $model inputs.
+
+ $sites = DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR;
+ $modules = DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR;
+ preg_match("/" . preg_quote($sites, DIRECTORY_SEPARATOR) .
+ "([\-a-zA-Z0-9_.]+)" .
+ preg_quote($modules, DIRECTORY_SEPARATOR) . "/",
+ $_SERVER['SCRIPT_FILENAME'], $matches
+ );
+ $siteDir = isset($matches[1]) ? $matches[1] : 'default';
+
+ if (strtolower($siteDir) == 'all') {
+ // For this case - use drupal's way of finding out multi-site directory
+ $uri = explode(DIRECTORY_SEPARATOR, $_SERVER['SCRIPT_FILENAME']);
+ $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
+ for ($i = count($uri) - 1; $i > 0; $i--) {
+ for ($j = count($server); $j > 0; $j--) {
+ $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
+ if (file_exists($cmsPath . DIRECTORY_SEPARATOR .
+ 'sites' . DIRECTORY_SEPARATOR . $dir
+ )) {
+ $siteDir = $dir;
+ return $siteDir;
+ }
+ }
+ }
+ $siteDir = 'default';
+ }
+
+ return $siteDir;
+ */
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class BaseSetupEvent extends Event {
+
+ /**
+ * @var \Civi\Setup\Model
+ */
+ protected $model;
+
+ /**
+ * BaseSetupEvent constructor.
+ * @param \Civi\Setup\Model $model
+ */
+ public function __construct(\Civi\Setup\Model $model) {
+ $this->model = $model;
+ }
+
+ /**
+ * @return \Civi\Setup\Model
+ */
+ public function getModel() {
+ return $this->model;
+ }
+
+ /**
+ * @param \Civi\Setup\Model $model
+ */
+ public function setModel($model) {
+ $this->model = $model;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Check if the current user is authorized to perform installations.
+ *
+ * Event Name: 'civi.setup.checkAuthorized'
+ */
+class CheckAuthorizedEvent extends BaseSetupEvent {
+
+ /**
+ * @var bool
+ */
+ private $authorized = FALSE;
+
+ /**
+ * @return bool
+ */
+ public function isAuthorized() {
+ return $this->authorized;
+ }
+
+ /**
+ * @param bool $authorized
+ */
+ public function setAuthorized($authorized) {
+ $this->authorized = $authorized;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Check if CiviCRM is already installed.
+ *
+ * Event Name: 'civi.setup.checkInstalled'
+ */
+class CheckInstalledEvent extends BaseSetupEvent {
+
+ /**
+ * @var bool
+ */
+ private $settingInstalled = NULL;
+
+ /**
+ * @var bool
+ */
+ private $databaseInstalled = NULL;
+
+ /**
+ * @return bool
+ */
+ public function isSettingInstalled() {
+ return $this->settingInstalled;
+ }
+
+ /**
+ * @param bool $settingInstalled
+ */
+ public function setSettingInstalled($settingInstalled) {
+ $this->settingInstalled = $settingInstalled;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isDatabaseInstalled() {
+ return $this->databaseInstalled;
+ }
+
+ /**
+ * @param bool $databaseInstalled
+ */
+ public function setDatabaseInstalled($databaseInstalled) {
+ $this->databaseInstalled = $databaseInstalled;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Check if the local system meets the installation requirements.
+ *
+ * Event Name: 'civi.setup.checkRequirements'
+ */
+class CheckRequirementsEvent extends BaseSetupEvent {
+
+ protected $messages;
+
+ /**
+ * @param string $severity
+ * Severity/level.
+ * Ex: 'info', 'warning', 'error'.
+ * @param string $section
+ * Symbolic machine name for this group of messages.
+ * Ex: 'database' or 'system'.
+ * @param string $name
+ * Symbolic machine name for this particular message.
+ * Ex: 'mysqlThreadstack'
+ * @param string $message
+ * Displayable explanation.
+ * Ex: 'The MySQL thread stack is too small.'
+ * @return $this
+ */
+ public function addMessage($severity, $section, $name, $message) {
+ $this->messages[$name] = array(
+ 'section' => $section,
+ 'name' => $name,
+ 'message' => $message,
+ 'severity' => $severity,
+ );
+ return $this;
+ }
+
+ public function addInfo($section, $name, $message = '') {
+ return $this->addMessage('info', $section, $name, $message);
+ }
+
+ public function addError($section, $name, $message = '') {
+ return $this->addMessage('error', $section, $name, $message);
+ }
+
+ public function addWarning($section, $name, $message = '') {
+ return $this->addMessage('warning', $section, $name, $message);
+ }
+
+ /**
+ * @param string|NULL $severity
+ * Filter by severity of the message.
+ * Ex: 'info', 'error', 'warning'.
+ * @return array
+ * List of messages. Each has fields:
+ * - name: string, symbolic name.
+ * - message: string, displayable message.
+ * - severity: string, ex: 'info', 'warning', 'error'.
+ */
+ public function getMessages($severity = NULL) {
+ if ($severity === NULL) {
+ return $this->messages;
+ }
+ else {
+ return array_filter($this->messages, function ($m) use ($severity) {
+ return $m['severity'] == $severity;
+ });
+ }
+ }
+
+ public function getInfos() {
+ return $this->getMessages('info');
+ }
+
+ public function getErrors() {
+ return $this->getMessages('error');
+ }
+
+ public function getWarnings() {
+ return $this->getMessages('warning');
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Initialize a "Setup" subsystem. This is useful for populating the model with default settings.
+ *
+ * Event Name: 'civi.setup.init'
+ */
+class InitEvent extends BaseSetupEvent {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Initialize the CiviCRM database.
+ *
+ * Event Name: 'civi.setup.installDatabase'
+ */
+class InstallDatabaseEvent extends BaseSetupEvent {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Initialize the CiviCRM support files, such as `civicrm.settings.php` or `templateCompileDir`
+ *
+ * Event Name: 'civi.setup.installFiles'
+ */
+class InstallFilesEvent extends BaseSetupEvent {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Purge any CiviCRM schema (tables, views, functions) from the database.
+ *
+ * Event Name: 'civi.setup.uninstallDatabase'
+ */
+class UninstallDatabaseEvent extends BaseSetupEvent {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Event;
+
+/**
+ * Purge any CiviCRM support files, such as `civicrm.settings.php` or `templateCompileDir`
+ *
+ * Event Name: 'civi.setup.uninstallFiles'
+ */
+class UninstallFilesEvent extends BaseSetupEvent {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Exception;
+
+class InitException extends \RuntimeException {
+}
--- /dev/null
+<?php
+namespace Civi\Setup\Exception;
+
+class SqlException extends \Exception {
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class FileUtil {
+
+ public static function isCreateable($file) {
+ if (file_exists($file)) {
+ return is_writable($file);
+ }
+
+ $next = dirname($file);
+ do {
+ $current = $next;
+ if (file_exists($current)) {
+ return is_writable($current);
+ }
+ $next = dirname($current);
+ } while ($current && $next && $current != $next);
+
+ return FALSE;
+ }
+
+ public static function makeWebWriteable($path) {
+ // Blerg: Setting world-writable works as a default, but
+ // it 'sprone to break systems that rely on umask's or facl's.
+ chmod($path, 0777);
+ }
+
+ public static function isDeletable($path) {
+ return is_writable(dirname($path));
+ }
+
+ /**
+ * @param $prefix
+ *
+ * @return string
+ */
+ public static function createTempDir($prefix) {
+ $newTempDir = tempnam(sys_get_temp_dir(), $prefix) . '.d';
+ mkdir($newTempDir, 0755, TRUE);
+
+ return $newTempDir;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class LocaleUtil {
+
+ /**
+ * Figure out which of $langs is the closest to $lang.
+ *
+ * @param string $preferredLang
+ * The user's preferred language.
+ * Ex: `en`, `fr`, or `fr_CA`.
+ * @param array $availLangs
+ * List of available languages.
+ * Ex: ['en_US' => 'English (US)', 'fr_CA' => 'French (Canadian)'].
+ * @param string $default
+ * The locale to use if none other can be determined.
+ * Ex: 'en_US'.
+ * @return string
+ * Ex: 'en_US'.
+ */
+ public static function pickClosest($preferredLang, $availLangs, $default = 'en_US') {
+ // Perhaps we have this exact language?
+ if (isset($availLangs[$preferredLang])) {
+ return $preferredLang;
+ }
+
+ list ($first) = explode('_', $preferredLang);
+
+ // Do we have a hard-coded preference? Use this for real oddballs.
+ $overrides = array(
+ 'en' => 'en_US',
+ );
+ if (isset($overrides[$preferredLang]) && isset($availLangs[$overrides[$preferredLang]])) {
+ return $overrides[$preferredLang];
+ }
+
+ // Perhaps we have the canonical variant (e.g. `fr` => `fr_FR`)?
+ $canon = $first . '_' . strtoupper($first);
+ if (isset($availLangs[$canon])) {
+ return $canon;
+ }
+
+ // Is there anything else that looks remotely close? (e.g. `cy` => `cy_GB`)
+ ksort($availLangs);
+ foreach ($availLangs as $availLang => $availLabel) {
+ if (strpos($availLang, $first) === 0) {
+ return $availLang;
+ }
+ }
+
+ // Nothing worked.
+ return $default;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+/**
+ * Class Model
+ * @package Civi\Setup
+ *
+ * The `Model` defines the main options and inputs that are used to configure
+ * the installer.
+ *
+ * @property string $srcPath
+ * Path to CiviCRM-core source tree.
+ * Ex: '/var/www/sites/all/modules/civicrm'.
+ * @property string $setupPath
+ * Path to CiviCRM-setup source tree.
+ * Ex: '/var/www/sites/all/modules/civicrm/setup'.
+ * @property string $settingsPath
+ * Ex: '/var/www/sites/default/civicrm.settings.php'.
+ * @property string $templateCompilePath
+ * Ex: '/var/www/sites/default/files/civicrm/templates_c'.
+ * @property string $cms
+ * Ex: 'Backdrop', 'Drupal', 'Drupal8', 'Joomla', 'WordPress'.
+ * @property string $cmsBaseUrl
+ * Ex: 'http://example.org/'.
+ * @property array $db
+ * Ex: ['server'=>'localhost:3306', 'username'=>'admin', 'password'=>'s3cr3t', 'database'=>'mydb']
+ * @property array $cmsDb
+ * Ex: ['server'=>'localhost:3306', 'username'=>'admin', 'password'=>'s3cr3t', 'database'=>'mydb']
+ * @property string $siteKey
+ * Ex: 'abcd1234ABCD9876'.
+ * @property string|NULL $lang
+ * The language of the default dataset.
+ * Ex: 'fr_FR'.
+ * @property bool $loadGenerated
+ * UNSUPPORTED: Load example dataset (in lieu of the standard dataset).
+ * This was copied-in from the previous installer code, but it should probably be
+ * reconceived.
+ * @property array $components
+ * Ex: ['CiviMail', 'CiviContribute', 'CiviEvent', 'CiviMember', 'CiviReport']
+ * @property array $extensions
+ * Ex: ['org.civicrm.flexmailer', 'org.civicrm.shoreditch'].
+ * @property array $paths
+ * List of hard-coded path-overrides.
+ * Ex: ['wp.frontend.base'=>['url'=>'http://example.org/']].
+ * @property array $settings
+ * List of domain settings to apply.
+ * These are defaults during installation; they could be changed by the admin post-install via GUI or API.
+ * Ex: ['ajaxPopupsEnabled' => 0].
+ * @property array $mandatorySettings
+ * List of hard-coded setting-overrides.
+ * These are mandatory settings which are hard-coded into the config file. Changing requires editing the file.
+ * This makes sense for path/URL settings that are generally system-local and not migrated between dev/prod/etc.
+ * Ex: ['ajaxPopupsEnabled' => 0].
+ * @property array $extras
+ * Open-ended list of private, adhoc fields/flags/tags.
+ * Keys should be prefixed based on which plugin manages the field.
+ * Values must only be scalars (bool/int/string) and arrays.
+ * Ex: ['opt-in.version-check' => TRUE].
+ */
+class Model {
+
+ protected $sorted = FALSE;
+ protected $fields = array();
+ protected $values = array();
+
+ public function __construct() {
+ $this->addField(array(
+ 'description' => 'Local path of the CiviCRM-core tree',
+ 'name' => 'srcPath',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Local path of the CiviCRM-setup tree',
+ 'name' => 'setupPath',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Local path to civicrm.settings.php',
+ 'name' => 'settingsPath',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Local path to the PHP compilation cache',
+ 'name' => 'templateCompilePath',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Symbolic name of the CMS/user-framework',
+ 'name' => 'cms',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'The CMS base URL',
+ 'name' => 'cmsBaseUrl',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Credentials for Civi database',
+ 'name' => 'db',
+ 'type' => 'dsn',
+ ));
+ $this->addField(array(
+ 'description' => 'Credentials for CMS database',
+ 'name' => 'cmsDb',
+ 'type' => 'dsn',
+ ));
+ $this->addField(array(
+ 'description' => 'Site key',
+ 'name' => 'siteKey',
+ 'type' => 'string',
+ ));
+ $this->addField(array(
+ 'description' => 'Load example data',
+ 'name' => 'loadGenerated',
+ 'type' => 'bool',
+ ));
+ $this->addField(array(
+ 'description' => 'Language',
+ 'name' => 'lang',
+ 'type' => 'string',
+ 'options' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'List of CiviCRM components to enable',
+ 'name' => 'components',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'List of CiviCRM extensions to enable',
+ 'name' => 'extensions',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'List of mandatory path overrides.',
+ 'name' => 'paths',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'List of setting overrides.',
+ 'name' => 'settings',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'List of mandatory settings',
+ 'name' => 'mandatorySettings',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ $this->addField(array(
+ 'description' => 'Open-ended list of private, adhoc fields/flags/tags',
+ 'name' => 'extras',
+ 'type' => 'array',
+ 'value' => array(),
+ ));
+ }
+
+ /**
+ * @param array $field
+ * - name: string
+ * - description: string
+ * - type: string. One of "checkbox", "string".
+ * - weight: int. (Default: 0)
+ * - visible: bool. (Default: TRUE)
+ * - value: mixed. (Default: NULL)
+ * @return $this
+ */
+ public function addField($field) {
+ $defaults = array(
+ 'weight' => 0,
+ 'visible' => TRUE,
+ );
+ $field = array_merge($defaults, $field);
+
+ if (array_key_exists('value', $field) || !array_key_exists($field['name'], $this->values)) {
+ $this->values[$field['name']] = isset($field['value']) ? $field['value'] : NULL;
+ unset($field['value']);
+ }
+
+ $this->fields[$field['name']] = $field;
+
+ $this->sorted = FALSE;
+ return $this;
+ }
+
+ public function setField($field, $property, $value) {
+ $this->fields[$field][$property] = $value;
+ return $this;
+ }
+
+ /**
+ * @param string $field
+ * The name of the field.
+ * Ex: 'cmsDb', 'lang'.
+ * @param string $property
+ * A specific property of the field to load.
+ * Ex: 'name', 'description', 'type', 'options'.
+ * @return mixed|NULL
+ */
+ public function getField($field, $property = NULL) {
+ if ($property) {
+ return isset($this->fields[$field][$property]) ? $this->fields[$field][$property] : NULL;
+ }
+ else {
+ return isset($this->fields[$field]) ? $this->fields[$field] : NULL;
+ }
+ }
+
+ public function getFields() {
+ if (!$this->sorted) {
+ uasort($this->fields, function ($a, $b) {
+ if ($a['weight'] < $b['weight']) {
+ return -1;
+ }
+ if ($a['weight'] > $b['weight']) {
+ return 1;
+ }
+ return strcmp($a['name'], $b['name']);
+ });
+ }
+ return $this->fields;
+ }
+
+ /**
+ * Set the values of multiple fields.
+ *
+ * @param array $values
+ * Ex: array('root' => '/var/www/sites/default/files/civicrm')
+ * @return $this
+ */
+ public function setValues($values) {
+ foreach ($values as $key => $value) {
+ $this->values[$key] = $value;
+ }
+ return $this;
+ }
+
+ public function getValues() {
+ return $this->values;
+ }
+
+ public function &__get($name) {
+ return $this->values[$name];
+ }
+
+ public function __set($name, $value) {
+ $this->values[$name] = $value;
+ }
+
+ public function __isset($name) {
+ return isset($this->values[$name]);
+ }
+
+ public function __unset($name) {
+ unset($this->values[$name]);
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class PackageUtil {
+
+ /**
+ * Locate the civicrm-packages source tree.
+ *
+ * @param string $srcPath
+ * The path to the civicrm-core source tree.
+ * @return string
+ * The path to the civicrm-packages source tree.
+ */
+ public static function getPath($srcPath) {
+ global $civicrm_paths;
+
+ $candidates = [
+ // TODO: Trace the code-path and allow reading $model for packages dir?
+ $civicrm_paths['civicrm.packages']['path'] ?? NULL,
+ implode(DIRECTORY_SEPARATOR, [$srcPath, 'packages']),
+ implode(DIRECTORY_SEPARATOR, [dirname($srcPath), 'civicrm-packages']),
+ ];
+
+ foreach ($candidates as $candidate) {
+ if (!empty($candidate) && file_exists($candidate)) {
+ return $candidate;
+ }
+ }
+
+ throw new \RuntimeException("Failed to locate civicrm-packages");
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class SchemaGenerator {
+
+ /**
+ * Return translated SQL content using tpl, mainly contain SQL codes on table CREATE/DROP
+ *
+ * @param string $srcPath
+ * @param array $database
+ * @param array $tables
+ * @return string
+ */
+ public static function generateCreateSql($srcPath, $database, $tables) {
+ $template = new Template($srcPath, 'sql');
+
+ $template->assign('database', $database);
+ $template->assign('tables', $tables);
+ $dropOrder = array_reverse(array_keys($tables));
+ $template->assign('dropOrder', $dropOrder);
+ $template->assign('mysql', 'modern');
+
+ return $template->getContent('schema.tpl');
+ }
+
+ /**
+ * Generate an example set of data, including the basic data as well
+ * as some example records/entities (e.g. case-types, membership types).
+ *
+ * @param string $srcPath
+ *
+ * @return string
+ */
+ public static function generateSampleData($srcPath) {
+ $versionFile = implode(DIRECTORY_SEPARATOR, [$srcPath, 'xml', 'version.xml']);
+ $xml = \CRM_Core_CodeGen_Util_Xml::parse($versionFile);
+
+ $template = new Template($srcPath, 'sql');
+ $template->assign('db_version', $xml->version_no);
+
+ // If you're going to use the full data generator...
+ // "DROP TABLE IF EXISTS zipcodes"
+ // .... file_get_contents($sqlPath . DIRECTORY_SEPARATOR . 'zipcodes.mysql')...
+
+ $sections = [
+ 'civicrm_country.tpl',
+ 'civicrm_state_province.tpl',
+ 'civicrm_currency.tpl',
+ 'civicrm_data.tpl',
+ 'civicrm_acl.tpl',
+ 'civicrm_sample.tpl',
+ 'case_sample.tpl',
+ 'civicrm_version_sql.tpl',
+ 'civicrm_navigation.tpl',
+ ];
+
+ // DROP TABLE IF EXISTS zipcodes;
+
+ return $template->getConcatContent($sections);
+ }
+
+ /**
+ * Generate a minimalist set of basic data, such as
+ * common option-values and countries.
+ *
+ * @param string $srcPath
+ *
+ * @return string
+ * SQL
+ */
+ public static function generateBasicData($srcPath) {
+ $versionFile = implode(DIRECTORY_SEPARATOR, [$srcPath, 'xml', 'version.xml']);
+ $xml = \CRM_Core_CodeGen_Util_Xml::parse($versionFile);
+
+ $template = new Template($srcPath, 'sql');
+ $template->assign('db_version', $xml->version_no);
+
+ $sections = [
+ 'civicrm_country.tpl',
+ 'civicrm_state_province.tpl',
+ 'civicrm_currency.tpl',
+ 'civicrm_data.tpl',
+ 'civicrm_acl.tpl',
+ 'civicrm_version_sql.tpl',
+ 'civicrm_navigation.tpl',
+ ];
+ return $template->getConcatContent($sections);
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class SmartyUtil {
+
+ /**
+ * Create a Smarty instance.
+ *
+ * @return \Smarty
+ */
+ public static function createSmarty($srcPath) {
+ require_once 'CRM/Core/I18n.php';
+
+ $packagePath = PackageUtil::getPath($srcPath);
+ require_once $packagePath . DIRECTORY_SEPARATOR . 'Smarty' . DIRECTORY_SEPARATOR . 'Smarty.class.php';
+
+ $smarty = new \Smarty();
+ $smarty->template_dir = implode(DIRECTORY_SEPARATOR, [$srcPath, 'xml', 'templates']);
+ $smarty->plugins_dir = [
+ implode(DIRECTORY_SEPARATOR, [$packagePath, 'Smarty', 'plugins']),
+ implode(DIRECTORY_SEPARATOR, [$srcPath, 'CRM', 'Core', 'Smarty', 'plugins']),
+ ];
+ $smarty->compile_dir = \Civi\Setup\FileUtil::createTempDir('templates_c');
+ $smarty->clear_all_cache();
+
+ // CRM-5308 / CRM-3507 - we need {localize} to work in the templates
+ require_once implode(DIRECTORY_SEPARATOR, [$srcPath, 'CRM', 'Core', 'Smarty', 'plugins', 'block.localize.php']);
+ $smarty->register_block('localize', 'smarty_block_localize');
+ $smarty->assign('gencodeXmlDir', "$srcPath/xml");
+
+ return $smarty;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup;
+
+class Template {
+
+ protected $filetype;
+
+ protected $smarty;
+ protected $beautifier;
+
+ public function __construct($srcPath, $fileType) {
+ $this->filetype = $fileType;
+
+ $this->smarty = \Civi\Setup\SmartyUtil::createSmarty($srcPath);
+
+ $this->assign('generated', "DO NOT EDIT. Generated by Installer");
+
+ if ($this->filetype === 'php') {
+ $packagePath = PackageUtil::getPath($srcPath);
+ require_once implode(DIRECTORY_SEPARATOR, [$packagePath, 'PHP', 'Beautifier.php']);
+ // create an instance
+ $this->beautifier = new PHP_Beautifier();
+ $this->beautifier->addFilter('ArrayNested');
+ // add one or more filters
+ $this->beautifier->setIndentChar(' ');
+ $this->beautifier->setIndentNumber(2);
+ $this->beautifier->setNewLine("\n");
+ }
+ }
+
+ public function assign($tpl_var, $value = NULL) {
+ return $this->smarty->assign($tpl_var, $value);
+ }
+
+ /**
+ * Run template generator.
+ *
+ * @param string $infile
+ * Filename of the template, without a path.
+ * @return string
+ */
+ public function getContent($infile) {
+ $contents = $this->smarty->fetch($infile);
+
+ if ($this->filetype === 'php') {
+ $this->beautifier->setInputString($contents);
+ $this->beautifier->process();
+ $contents = $this->beautifier->get();
+ // The beautifier isn't as beautiful as one would hope. Here's some extra string fudging.
+ $replacements = [
+ ') ,' => '),',
+ "\n }\n}\n" => "\n }\n\n}\n",
+ '=> true,' => '=> TRUE,',
+ '=> false,' => '=> FALSE,',
+ ];
+ $contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
+ $contents = preg_replace('#(\s*)\\/\\*\\*#', "\n\$1/**", $contents);
+ // Convert old array syntax to new square brackets
+ $contents = CRM_Core_CodeGen_Util_ArraySyntaxConverter::convert($contents);
+ }
+
+ return $contents;
+ }
+
+ /**
+ * @param array $inputs
+ * Template filenames.
+ */
+ public function getConcatContent($inputs) {
+ $content = '';
+ foreach ($inputs as $infile) {
+ // FIXME: does not beautify. Document.
+ $content .= $this->smarty->fetch($infile) . "\n";
+ }
+
+ return $content;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class BaseUIEvent extends Event {
+
+ /**
+ * @var \Civi\Setup\UI\SetupController
+ */
+ protected $ctrl;
+
+ /**
+ * @var string
+ * Ex: 'POST', 'GET'.
+ */
+ protected $method;
+
+ /**
+ * @var array
+ */
+ protected $fields;
+
+ /**
+ * RunControllerEvent constructor.
+ *
+ * @param \Civi\Setup\UI\SetupController $ctrl
+ * @param $method
+ * @param $fields
+ */
+ public function __construct($ctrl, $method, $fields) {
+ $this->ctrl = $ctrl;
+ $this->method = $method;
+ $this->fields = $fields;
+ }
+
+ /**
+ * @return \Civi\Setup\UI\SetupController
+ */
+ public function getCtrl() {
+ return $this->ctrl;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMethod() {
+ return $this->method;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFields() {
+ return $this->fields[\Civi\Setup\UI\SetupController::PREFIX];
+ }
+
+ public function getField($name, $default = NULL) {
+ if (isset($this->fields[\Civi\Setup\UI\SetupController::PREFIX][$name])) {
+ return $this->fields[\Civi\Setup\UI\SetupController::PREFIX][$name];
+ }
+ return $default;
+ }
+
+ /**
+ * @return \Civi\Setup\Model
+ */
+ public function getModel() {
+ return $this->ctrl->getSetup()->getModel();
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI\Event;
+
+/**
+ * Run the stock web-based UI.
+ *
+ * Event Name: 'civi.setupui.boot'
+ */
+class UIBootEvent extends BaseUIEvent {
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI\Event;
+
+use Civi\Setup\Event\BaseSetupEvent;
+use Civi\Setup\Event\SetupControllerInterface;
+use Civi\Setup\UI\SetupController;
+
+/**
+ * Create a web-based UI for handling the installation.
+ *
+ * Event Name: 'civi.setupui.construct'
+ */
+class UIConstructEvent extends BaseSetupEvent {
+
+ protected $ctrl;
+
+ /**
+ * @return SetupControllerInterface
+ */
+ public function getCtrl() {
+ return $this->ctrl;
+ }
+
+ /**
+ * @param SetupControllerInterface $ctrl
+ */
+ public function setCtrl($ctrl) {
+ $this->ctrl = $ctrl;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI\Event;
+
+/**
+ * Run the stock web-based UI.
+ *
+ * Event Name: 'civi.setupui.run'
+ */
+class UIRunEvent extends BaseUIEvent {
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI;
+
+use Civi\Setup\UI\Event\UIBootEvent;
+
+class SetupController implements SetupControllerInterface {
+
+ const PREFIX = 'civisetup';
+
+ /**
+ * @var \Civi\Setup
+ */
+ protected $setup;
+
+ /**
+ * @var array
+ * Some mix of the following:
+ * - res: The base URL for loading resource files (images/javascripts) for this
+ * project. Includes trailing slash.
+ * - ctrl: The URL of this setup controller. May be used for POST-backs.
+ */
+ protected $urls;
+
+ /**
+ * @var array
+ * A list of blocks to display on the setup page. Each item has:
+ * - file: string, relative path
+ * - class: string, a space-delimited list of CSS classes
+ * - is_active: bool
+ *
+ * Note: When rendering a block, content of the block's definition
+ * will be available as `$_tpl_block`. For example, `$_tpl_block['is_active']`
+ * would be the same boolean.
+ */
+ public $blocks;
+
+ /**
+ * SetupController constructor.
+ * @param \Civi\Setup $setup
+ */
+ public function __construct(\Civi\Setup $setup) {
+ $this->setup = $setup;
+ $this->blocks = array();
+ }
+
+ /**
+ * @param string $method
+ * Ex: 'GET' or 'POST'.
+ * @param array $fields
+ * List of any HTTP GET/POST fields.
+ * @return array
+ * The HTTP headers and response text.
+ * [0 => array $headers, 1 => string $body].
+ */
+ public function run($method, $fields = array()) {
+ $this->setup->getDispatcher()->dispatch('civi.setupui.run', new UIBootEvent($this, $method, $fields));
+ if (!$this->setup->checkAuthorized()->isAuthorized()) {
+ return $this->createError("Not authorized to perform installation");
+ }
+
+ $this->boot($method, $fields);
+ $action = $this->parseAction($fields, 'Start');
+ $func = 'run' . $action;
+ if (!preg_match('/^[a-zA-Z0-9_]+$/', $action) || !is_callable([$this, $func])) {
+ return $this->createError("Invalid action");
+ }
+ return call_user_func([$this, $func], $method, $fields);
+ }
+
+ /**
+ * Run the main installer page.
+ *
+ * @param string $method
+ * Ex: 'GET' or 'POST'.
+ * @param array $fields
+ * List of any HTTP GET/POST fields.
+ * @return array
+ * The HTTP headers and response text.
+ * [0 => array $headers, 1 => string $body].
+ */
+ public function runStart($method, $fields) {
+ $checkInstalled = $this->setup->checkInstalled();
+ if ($checkInstalled->isDatabaseInstalled() || $checkInstalled->isSettingInstalled()) {
+ return $this->createError("CiviCRM is already installed");
+ }
+
+ /**
+ * @var \Civi\Setup\Model $model
+ */
+ $model = $this->setup->getModel();
+
+ $tplFile = $this->getResourcePath('template.php');
+ $tplVars = [
+ 'ctrl' => $this,
+ 'civicrm_version' => \CRM_Utils_System::version(),
+ 'installURLPath' => $this->urls['res'],
+ 'short_lang_code' => \CRM_Core_I18n_PseudoConstant::shortForLong($GLOBALS['tsLocale']),
+ 'text_direction' => (\CRM_Core_I18n::isLanguageRTL($GLOBALS['tsLocale']) ? 'rtl' : 'ltr'),
+ 'model' => $model,
+ 'reqs' => $this->setup->checkRequirements(),
+ ];
+
+ // $body = "<pre>" . htmlentities(print_r(['method' => $method, 'urls' => $this->urls, 'data' => $fields], 1)) . "</pre>";
+ $body = $this->render($tplFile, $tplVars);
+
+ return array(array(), $body);
+ }
+
+ /**
+ * Perform the installation action.
+ *
+ * @param string $method
+ * Ex: 'GET' or 'POST'.
+ * @param array $fields
+ * List of any HTTP GET/POST fields.
+ * @return array
+ * The HTTP headers and response text.
+ * [0 => array $headers, 1 => string $body].
+ */
+ public function runInstall($method, $fields) {
+ $checkInstalled = $this->setup->checkInstalled();
+ if ($checkInstalled->isDatabaseInstalled() || $checkInstalled->isSettingInstalled()) {
+ return $this->createError("CiviCRM is already installed");
+ }
+
+ $reqs = $this->setup->checkRequirements();
+ if (count($reqs->getErrors())) {
+ return $this->runStart($method, $fields);
+ }
+
+ $this->setup->installFiles();
+ $this->setup->installDatabase();
+
+ $m = $this->setup->getModel();
+ $tplFile = $this->getResourcePath('finished.' . $m->cms . '.php');
+ if (file_exists($tplFile)) {
+ $tplVars = array();
+ return array(array(), $this->render($tplFile, $tplVars));
+ }
+ else {
+ return $this->createError("Installation succeeded. However, the final page ($tplFile) was not available.");
+ }
+ }
+
+ /**
+ * Partially bootstrap Civi services (such as localization).
+ */
+ protected function boot($method, $fields) {
+ $model = $this->setup->getModel();
+
+ define('CIVICRM_UF', $model->cms);
+
+ // Set the Locale (required by CRM_Core_Config)
+ global $tsLocale;
+ $tsLocale = 'en_US';
+
+ // CRM-16801 This validates that lang is valid by looking in $langs.
+ // NB: the variable is initial a $_REQUEST for the initial page reload,
+ // then becomes a $_POST when the installation form is submitted.
+ $langs = $model->getField('lang', 'options');
+ if (array_key_exists('lang', $fields)) {
+ $model->lang = $fields['lang'];
+ }
+ if ($model->lang and isset($langs[$model->lang])) {
+ $tsLocale = $model->lang;
+ }
+
+ \CRM_Core_Config::singleton(FALSE);
+ $GLOBALS['civicrm_default_error_scope'] = NULL;
+
+ // The translation files are in the parent directory (l10n)
+ \CRM_Core_I18n::singleton();
+
+ $this->setup->getDispatcher()->dispatch('civi.setupui.boot', new UIBootEvent($this, $method, $fields));
+ }
+
+ public function createError($message, $title = 'Error') {
+ return [
+ [],
+ $this->render($this->getResourcePath('error.html'), [
+ 'errorTitle' => htmlentities($title),
+ 'errorMsg' => htmlentities($message),
+ 'installURLPath' => $this->urls['res'],
+ ]),
+ ];
+ }
+
+ /**
+ * Render a *.php template file.
+ *
+ * @param string $_tpl_file
+ * The path to the file.
+ * @param array $_tpl_params
+ * Any variables that should be exported to the scope of the template.
+ * @return string
+ */
+ public function render($_tpl_file, $_tpl_params = array()) {
+ extract($_tpl_params);
+ ob_start();
+ require $_tpl_file;
+ return ob_get_clean();
+ }
+
+ public function getResourcePath($parts) {
+ $parts = (array) $parts;
+ array_unshift($parts, 'res');
+ array_unshift($parts, $this->setup->getModel()->setupPath);
+ return implode(DIRECTORY_SEPARATOR, $parts);
+ }
+
+ public function getUrl($name) {
+ return isset($this->urls[$name]) ? $this->urls[$name] : NULL;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setUrls($urls) {
+ foreach ($urls as $k => $v) {
+ $this->urls[$k] = $v;
+ }
+ return $this;
+ }
+
+ /**
+ * Given an HTML submission, determine the name.
+ *
+ * @param array $fields
+ * HTTP inputs -- e.g. with a form element like this:
+ * `<input type="submit" name="civisetup[action][Foo]" value="Do the foo">`
+ * @param string $default
+ * The action-name to return if no other action is identified.
+ * @return string
+ * The name of the action.
+ * Ex: 'Foo'.
+ */
+ protected function parseAction($fields, $default) {
+ if (empty($fields[self::PREFIX]['action'])) {
+ return $default;
+ }
+ else {
+ if (is_array($fields[self::PREFIX]['action'])) {
+ foreach ($fields[self::PREFIX]['action'] as $name => $label) {
+ return $name;
+ }
+ }
+ elseif (is_string($fields[self::PREFIX]['action'])) {
+ return $fields[self::PREFIX]['action'];
+ }
+ }
+ return NULL;
+ }
+
+ public function renderBlocks($_tpl_params) {
+ $buf = '';
+
+ // Cleanup - Ensure 'name' is present.
+ foreach (array_keys($this->blocks) as $name) {
+ $this->blocks[$name]['name'] = $name;
+ }
+
+ // Sort by weight+name.
+ uasort($this->blocks, function($a, $b) {
+ if ($a['weight'] != $b['weight']) {
+ return $a['weight'] - $b['weight'];
+ }
+ return strcmp($a['name'], $b['name']);
+ });
+
+ foreach ($this->blocks as $name => $block) {
+ if (!$block['is_active']) {
+ continue;
+ }
+ $buf .= sprintf("<div class=\"civicrm-setup-block-%s %s\">%s</div>",
+ $name,
+ $block['class'],
+ $this->render(
+ $block['file'],
+ $_tpl_params + array('_tpl_block' => $block)
+ )
+ );
+ }
+ return $buf;
+ }
+
+ /**
+ * @return \Civi\Setup
+ */
+ public function getSetup() {
+ return $this->setup;
+ }
+
+}
--- /dev/null
+<?php
+namespace Civi\Setup\UI;
+
+interface SetupControllerInterface {
+
+ /**
+ * @param string $method
+ * Ex: 'GET' or 'POST'.
+ * @param array $fields
+ * List of any HTTP POST fields.
+ * @return array
+ * The HTTP headers and response text.
+ * [0 => $headers, 1 => $body].
+ */
+ public function run($method, $fields = array());
+
+ /**
+ * @param array $urls
+ * Some mix of the following:
+ * - res: The base URL for loading resource files (images/javascripts) for this
+ * project. Includes trailing slash.
+ * - ctrl: The URL of this setup controller. May be used for POST-backs.
+ * @return $this
+ */
+ public function setUrls($urls);
+
+}