Merge pull request #16898 from civicrm/5.24
[civicrm-core.git] / setup / src / Setup / UI / SetupController.php
CommitLineData
4bcd4c62
TO
1<?php
2namespace Civi\Setup\UI;
3
4use Civi\Setup\UI\Event\UIBootEvent;
5
6class SetupController implements SetupControllerInterface {
7
8 const PREFIX = 'civisetup';
9
10 /**
11 * @var \Civi\Setup
12 */
13 protected $setup;
14
15 /**
16 * @var array
17 * Some mix of the following:
18 * - res: The base URL for loading resource files (images/javascripts) for this
19 * project. Includes trailing slash.
20 * - ctrl: The URL of this setup controller. May be used for POST-backs.
21 */
22 protected $urls;
23
24 /**
25 * @var array
26 * A list of blocks to display on the setup page. Each item has:
27 * - file: string, relative path
28 * - class: string, a space-delimited list of CSS classes
29 * - is_active: bool
30 *
31 * Note: When rendering a block, content of the block's definition
32 * will be available as `$_tpl_block`. For example, `$_tpl_block['is_active']`
33 * would be the same boolean.
34 */
35 public $blocks;
36
37 /**
38 * SetupController constructor.
39 * @param \Civi\Setup $setup
40 */
41 public function __construct(\Civi\Setup $setup) {
42 $this->setup = $setup;
43 $this->blocks = array();
44 }
45
46 /**
47 * @param string $method
48 * Ex: 'GET' or 'POST'.
49 * @param array $fields
50 * List of any HTTP GET/POST fields.
51 * @return array
52 * The HTTP headers and response text.
53 * [0 => array $headers, 1 => string $body].
54 */
55 public function run($method, $fields = array()) {
56 $this->setup->getDispatcher()->dispatch('civi.setupui.run', new UIBootEvent($this, $method, $fields));
57 if (!$this->setup->checkAuthorized()->isAuthorized()) {
58 return $this->createError("Not authorized to perform installation");
59 }
60
61 $this->boot($method, $fields);
62 $action = $this->parseAction($fields, 'Start');
63 $func = 'run' . $action;
64 if (!preg_match('/^[a-zA-Z0-9_]+$/', $action) || !is_callable([$this, $func])) {
65 return $this->createError("Invalid action");
66 }
67 return call_user_func([$this, $func], $method, $fields);
68 }
69
70 /**
71 * Run the main installer page.
72 *
73 * @param string $method
74 * Ex: 'GET' or 'POST'.
75 * @param array $fields
76 * List of any HTTP GET/POST fields.
77 * @return array
78 * The HTTP headers and response text.
79 * [0 => array $headers, 1 => string $body].
80 */
81 public function runStart($method, $fields) {
82 $checkInstalled = $this->setup->checkInstalled();
83 if ($checkInstalled->isDatabaseInstalled() || $checkInstalled->isSettingInstalled()) {
84 return $this->createError("CiviCRM is already installed");
85 }
86
87 /**
88 * @var \Civi\Setup\Model $model
89 */
90 $model = $this->setup->getModel();
91
92 $tplFile = $this->getResourcePath('template.php');
93 $tplVars = [
94 'ctrl' => $this,
95 'civicrm_version' => \CRM_Utils_System::version(),
96 'installURLPath' => $this->urls['res'],
97 'short_lang_code' => \CRM_Core_I18n_PseudoConstant::shortForLong($GLOBALS['tsLocale']),
98 'text_direction' => (\CRM_Core_I18n::isLanguageRTL($GLOBALS['tsLocale']) ? 'rtl' : 'ltr'),
99 'model' => $model,
100 'reqs' => $this->setup->checkRequirements(),
101 ];
102
103 // $body = "<pre>" . htmlentities(print_r(['method' => $method, 'urls' => $this->urls, 'data' => $fields], 1)) . "</pre>";
104 $body = $this->render($tplFile, $tplVars);
105
106 return array(array(), $body);
107 }
108
109 /**
110 * Perform the installation action.
111 *
112 * @param string $method
113 * Ex: 'GET' or 'POST'.
114 * @param array $fields
115 * List of any HTTP GET/POST fields.
116 * @return array
117 * The HTTP headers and response text.
118 * [0 => array $headers, 1 => string $body].
119 */
120 public function runInstall($method, $fields) {
121 $checkInstalled = $this->setup->checkInstalled();
122 if ($checkInstalled->isDatabaseInstalled() || $checkInstalled->isSettingInstalled()) {
123 return $this->createError("CiviCRM is already installed");
124 }
125
126 $reqs = $this->setup->checkRequirements();
127 if (count($reqs->getErrors())) {
128 return $this->runStart($method, $fields);
129 }
130
131 $this->setup->installFiles();
132 $this->setup->installDatabase();
133
134 $m = $this->setup->getModel();
135 $tplFile = $this->getResourcePath('finished.' . $m->cms . '.php');
136 if (file_exists($tplFile)) {
137 $tplVars = array();
138 return array(array(), $this->render($tplFile, $tplVars));
139 }
140 else {
141 return $this->createError("Installation succeeded. However, the final page ($tplFile) was not available.");
142 }
143 }
144
145 /**
146 * Partially bootstrap Civi services (such as localization).
147 */
148 protected function boot($method, $fields) {
149 $model = $this->setup->getModel();
150
151 define('CIVICRM_UF', $model->cms);
152
153 // Set the Locale (required by CRM_Core_Config)
154 global $tsLocale;
155 $tsLocale = 'en_US';
156
157 // CRM-16801 This validates that lang is valid by looking in $langs.
158 // NB: the variable is initial a $_REQUEST for the initial page reload,
159 // then becomes a $_POST when the installation form is submitted.
160 $langs = $model->getField('lang', 'options');
161 if (array_key_exists('lang', $fields)) {
162 $model->lang = $fields['lang'];
163 }
164 if ($model->lang and isset($langs[$model->lang])) {
165 $tsLocale = $model->lang;
166 }
167
168 \CRM_Core_Config::singleton(FALSE);
169 $GLOBALS['civicrm_default_error_scope'] = NULL;
170
171 // The translation files are in the parent directory (l10n)
172 \CRM_Core_I18n::singleton();
173
174 $this->setup->getDispatcher()->dispatch('civi.setupui.boot', new UIBootEvent($this, $method, $fields));
175 }
176
177 public function createError($message, $title = 'Error') {
178 return [
179 [],
180 $this->render($this->getResourcePath('error.html'), [
181 'errorTitle' => htmlentities($title),
182 'errorMsg' => htmlentities($message),
183 'installURLPath' => $this->urls['res'],
184 ]),
185 ];
186 }
187
188 /**
189 * Render a *.php template file.
190 *
191 * @param string $_tpl_file
192 * The path to the file.
193 * @param array $_tpl_params
194 * Any variables that should be exported to the scope of the template.
195 * @return string
196 */
197 public function render($_tpl_file, $_tpl_params = array()) {
198 extract($_tpl_params);
199 ob_start();
200 require $_tpl_file;
201 return ob_get_clean();
202 }
203
204 public function getResourcePath($parts) {
205 $parts = (array) $parts;
206 array_unshift($parts, 'res');
207 array_unshift($parts, $this->setup->getModel()->setupPath);
208 return implode(DIRECTORY_SEPARATOR, $parts);
209 }
210
211 public function getUrl($name) {
2e1f50d6 212 return $this->urls[$name] ?? NULL;
4bcd4c62
TO
213 }
214
215 /**
216 * @inheritdoc
217 */
218 public function setUrls($urls) {
219 foreach ($urls as $k => $v) {
220 $this->urls[$k] = $v;
221 }
222 return $this;
223 }
224
225 /**
226 * Given an HTML submission, determine the name.
227 *
228 * @param array $fields
229 * HTTP inputs -- e.g. with a form element like this:
230 * `<input type="submit" name="civisetup[action][Foo]" value="Do the foo">`
231 * @param string $default
232 * The action-name to return if no other action is identified.
233 * @return string
234 * The name of the action.
235 * Ex: 'Foo'.
236 */
237 protected function parseAction($fields, $default) {
238 if (empty($fields[self::PREFIX]['action'])) {
239 return $default;
240 }
241 else {
242 if (is_array($fields[self::PREFIX]['action'])) {
243 foreach ($fields[self::PREFIX]['action'] as $name => $label) {
244 return $name;
245 }
246 }
247 elseif (is_string($fields[self::PREFIX]['action'])) {
248 return $fields[self::PREFIX]['action'];
249 }
250 }
251 return NULL;
252 }
253
254 public function renderBlocks($_tpl_params) {
255 $buf = '';
256
257 // Cleanup - Ensure 'name' is present.
258 foreach (array_keys($this->blocks) as $name) {
259 $this->blocks[$name]['name'] = $name;
260 }
261
262 // Sort by weight+name.
263 uasort($this->blocks, function($a, $b) {
264 if ($a['weight'] != $b['weight']) {
265 return $a['weight'] - $b['weight'];
266 }
267 return strcmp($a['name'], $b['name']);
268 });
269
270 foreach ($this->blocks as $name => $block) {
271 if (!$block['is_active']) {
272 continue;
273 }
274 $buf .= sprintf("<div class=\"civicrm-setup-block-%s %s\">%s</div>",
275 $name,
276 $block['class'],
277 $this->render(
278 $block['file'],
279 $_tpl_params + array('_tpl_block' => $block)
280 )
281 );
282 }
283 return $buf;
284 }
285
286 /**
287 * @return \Civi\Setup
288 */
289 public function getSetup() {
290 return $this->setup;
291 }
292
293}