Commit | Line | Data |
---|---|---|
4bcd4c62 TO |
1 | <?php |
2 | namespace Civi\Setup\UI; | |
3 | ||
4 | use Civi\Setup\UI\Event\UIBootEvent; | |
5 | ||
6 | class 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) { | |
212 | return isset($this->urls[$name]) ? $this->urls[$name] : NULL; | |
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 | } |