Merge pull request #14981 from eileenmcnaughton/load_extract
[civicrm-core.git] / api / v3 / System.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * This api exposes CiviCRM system functionality.
30 *
31 * Includes caching, logging, and checking system functionality.
32 *
33 * @package CiviCRM_APIv3
34 */
35
36 /**
37 * Flush all system caches.
38 *
39 * @param array $params
40 * Input parameters.
41 * - triggers: bool, whether to drop/create SQL triggers; default: FALSE
42 * - session: bool, whether to reset the CiviCRM session data; default: FALSE
43 *
44 * @return array
45 */
46 function civicrm_api3_system_flush($params) {
47 CRM_Core_Invoke::rebuildMenuAndCaches(
48 CRM_Utils_Array::value('triggers', $params, FALSE),
49 CRM_Utils_Array::value('session', $params, FALSE)
50 );
51 return civicrm_api3_create_success();
52 }
53
54 /**
55 * Adjust Metadata for Flush action.
56 *
57 * The metadata is used for setting defaults, documentation & validation.
58 *
59 * @param array $params
60 * Array of parameters determined by getfields.
61 */
62 function _civicrm_api3_system_flush_spec(&$params) {
63 $params['triggers'] = [
64 'title' => 'Triggers',
65 'description' => 'rebuild triggers (boolean)',
66 'type' => CRM_Utils_Type::T_BOOLEAN,
67 ];
68 $params['session'] = [
69 'title' => 'Sessions',
70 'description' => 'refresh sessions (boolean)',
71 'type' => CRM_Utils_Type::T_BOOLEAN,
72 ];
73 }
74
75 /**
76 * System.Check API specification (optional).
77 *
78 * This is used for documentation and validation.
79 *
80 * @param array $spec
81 * Description of fields supported by this API call.
82 *
83 * @see http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards
84 */
85 function _civicrm_api3_system_check_spec(&$spec) {
86 $spec['id'] = [
87 'title' => 'ID',
88 'description' => 'Not a real identifier - do not use',
89 'type' => CRM_Utils_Type::T_INT,
90 ];
91 $spec['name'] = [
92 'title' => 'Name',
93 'description' => 'Unique identifier',
94 'type' => CRM_Utils_Type::T_STRING,
95 ];
96 $spec['title'] = [
97 'title' => 'Title',
98 'description' => 'Short title text',
99 'type' => CRM_Utils_Type::T_STRING,
100 ];
101 $spec['message'] = [
102 'title' => 'Message',
103 'description' => 'Long description html',
104 'type' => CRM_Utils_Type::T_STRING,
105 ];
106 $spec['help'] = [
107 'title' => 'Help',
108 'description' => 'Optional extra help (html string)',
109 'type' => CRM_Utils_Type::T_STRING,
110 ];
111 $spec['severity'] = [
112 'title' => 'Severity',
113 'description' => 'Psr\Log\LogLevel string',
114 'type' => CRM_Utils_Type::T_STRING,
115 'options' => array_combine(CRM_Utils_Check::getSeverityList(), CRM_Utils_Check::getSeverityList()),
116 ];
117 $spec['severity_id'] = [
118 'title' => 'Severity ID',
119 'description' => 'Integer representation of Psr\Log\LogLevel',
120 'type' => CRM_Utils_Type::T_INT,
121 'options' => CRM_Utils_Check::getSeverityList(),
122 ];
123 $spec['is_visible'] = [
124 'title' => 'is visible',
125 'description' => '0 if message has been hidden by the user',
126 'type' => CRM_Utils_Type::T_BOOLEAN,
127 ];
128 $spec['hidden_until'] = [
129 'title' => 'Hidden_until',
130 'description' => 'When will hidden message be visible again?',
131 'type' => CRM_Utils_Type::T_DATE,
132 ];
133 }
134
135 /**
136 * System Check API.
137 *
138 * @param array $params
139 *
140 * @return array
141 * API result descriptor; return items are alert codes/messages
142 * @see civicrm_api3_create_success
143 * @see civicrm_api3_create_error
144 * @throws API_Exception
145 */
146 function civicrm_api3_system_check($params) {
147 // array(array('name'=> $, 'severity'=>$, ...))
148 $id = 1;
149 $returnValues = $fields = [];
150 _civicrm_api3_system_check_spec($fields);
151
152 // array(CRM_Utils_Check_Message)
153 $messages = CRM_Utils_Check::checkAll();
154
155 foreach ($messages as $msg) {
156 $returnValues[] = $msg->toArray() + ['id' => $id++];
157 }
158
159 return _civicrm_api3_basic_array_get('systemCheck', $params, $returnValues, "id", array_keys($fields));
160 }
161
162 /**
163 * Log entry to system log table.
164 *
165 * @param array $params
166 *
167 * @return array
168 */
169 function civicrm_api3_system_log($params) {
170 $log = new CRM_Utils_SystemLogger();
171 // This part means fields with separate db storage are accepted as params which kind of seems more intuitive to me
172 // because I felt like not doing this required a bunch of explanation in the spec function - but perhaps other won't see it as helpful?
173 if (!isset($params['context'])) {
174 $params['context'] = [];
175 }
176 $specialFields = ['contact_id', 'hostname'];
177 foreach ($specialFields as $specialField) {
178 if (isset($params[$specialField]) && !isset($params['context'])) {
179 $params['context'][$specialField] = $params[$specialField];
180 }
181 }
182 $returnValues = $log->log($params['level'], $params['message'], $params['context']);
183 return civicrm_api3_create_success($returnValues, $params, 'System', 'Log');
184 }
185
186 /**
187 * Metadata for log function.
188 *
189 * @param array $params
190 */
191 function _civicrm_api3_system_log_spec(&$params) {
192 $params['level'] = [
193 'title' => 'Log Level',
194 'description' => 'Log level as described in PSR3 (info, debug, warning etc)',
195 'type' => CRM_Utils_Type::T_STRING,
196 'api.required' => TRUE,
197 ];
198 $params['message'] = [
199 'title' => 'Log Message',
200 'description' => 'Standardised message string, you can also ',
201 'type' => CRM_Utils_Type::T_STRING,
202 'api.required' => TRUE,
203 ];
204 $params['context'] = [
205 'title' => 'Log Context',
206 'description' => 'An array of additional data to store.',
207 'type' => CRM_Utils_Type::T_LONGTEXT,
208 'api.default' => [],
209 ];
210 $params['contact_id'] = [
211 'title' => 'Log Contact ID',
212 'description' => 'Optional ID of relevant contact',
213 'type' => CRM_Utils_Type::T_INT,
214 ];
215 $params['hostname'] = [
216 'title' => 'Log Hostname',
217 'description' => 'Optional name of host',
218 'type' => CRM_Utils_Type::T_STRING,
219 ];
220 }
221
222 /**
223 * System.Get API.
224 *
225 * @param array $params
226 *
227 * @return array
228 */
229 function civicrm_api3_system_get($params) {
230 $config = CRM_Core_Config::singleton();
231 $returnValues = [
232 [
233 // deprecated in favor of civi.version
234 'version' => CRM_Utils_System::version(),
235 // deprecated in favor of cms.type
236 'uf' => CIVICRM_UF,
237 'php' => [
238 'version' => phpversion(),
239 'time' => time(),
240 'tz' => date_default_timezone_get(),
241 'sapi' => php_sapi_name(),
242 'extensions' => get_loaded_extensions(),
243 'ini' => _civicrm_api3_system_get_redacted_ini(),
244 ],
245 'mysql' => [
246 'version' => CRM_Core_DAO::singleValueQuery('SELECT @@version'),
247 'time' => CRM_Core_DAO::singleValueQuery('SELECT unix_timestamp()'),
248 'vars' => _civicrm_api3_system_get_redacted_mysql(),
249 ],
250 'cms' => [
251 'version' => $config->userSystem->getVersion(),
252 'type' => CIVICRM_UF,
253 'modules' => CRM_Core_Module::collectStatuses($config->userSystem->getModules()),
254 ],
255 'civi' => [
256 'version' => CRM_Utils_System::version(),
257 'dev' => (bool) CRM_Utils_System::isDevelopment(),
258 'components' => array_keys(CRM_Core_Component::getEnabledComponents()),
259 'extensions' => preg_grep('/^uninstalled$/', CRM_Extension_System::singleton()->getManager()->getStatuses(), PREG_GREP_INVERT),
260 'multidomain' => CRM_Core_DAO::singleValueQuery('SELECT count(*) FROM civicrm_domain') > 1,
261 'settings' => _civicrm_api3_system_get_redacted_settings(),
262 'exampleUrl' => CRM_Utils_System::url('civicrm/example', NULL, TRUE, NULL, FALSE),
263 ],
264 'http' => [
265 'software' => CRM_Utils_Array::value('SERVER_SOFTWARE', $_SERVER),
266 'forwarded' => !empty($_SERVER['HTTP_X_FORWARDED_FOR']) || !empty($_SERVER['X_FORWARDED_PROTO']),
267 'port' => (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443) ? 'Standard' : 'Nonstandard',
268 ],
269 'os' => [
270 'type' => php_uname('s'),
271 'release' => php_uname('r'),
272 'version' => php_uname('v'),
273 'machine' => php_uname('m'),
274 ],
275 ],
276 ];
277
278 return civicrm_api3_create_success($returnValues, $params, 'System', 'get');
279 }
280
281 /**
282 * Generate a sanitized/anonymized/redacted dump of the PHP configuration.
283 *
284 * Some INI fields contain site-identifying information (SII) -- e.g. URLs,
285 * hostnames, file paths, IP addresses, passwords, or free-form comments
286 * could be used to identify a site or gain access to its resources.
287 *
288 * A number of INI fields have been examined to determine whether they
289 * contain SII. Approved fields are put in a whitelist; all other fields
290 * are redacted.
291 *
292 * Redaction hides the substance of a field but does not completely omit
293 * all information. Consider the field 'mail.log' - setting this field
294 * has a functional effect (it enables or disables the logging behavior)
295 * and also points to particular file. Empty values (FALSE/NULL/0/"")
296 * will pass through redaction, but all other values will be replaced
297 * by a string (eg "REDACTED"). This roughly indicates whether the
298 * option is enabled/disabled without giving away its content.
299 *
300 * @return array
301 */
302 function _civicrm_api3_system_get_redacted_ini() {
303 static $whitelist = NULL;
304 if ($whitelist === NULL) {
305 $whitelist = _civicrm_api3_system_get_whitelist(__DIR__ . '/System/ini-whitelist.txt');
306 }
307
308 $inis = ini_get_all(NULL, FALSE);
309 $result = [];
310 foreach ($inis as $k => $v) {
311 if (empty($v) || in_array($k, $whitelist)) {
312 $result[$k] = $v;
313 }
314 else {
315 $result[$k] = 'REDACTED';
316 }
317 }
318
319 return $result;
320 }
321
322 /**
323 * Generate ae sanitized/anonymized/redacted dump of MySQL configuration.
324 *
325 * @return array
326 * @see _civicrm_api3_system_get_redacted_ini
327 */
328 function _civicrm_api3_system_get_redacted_mysql() {
329 static $whitelist = NULL;
330 if ($whitelist === NULL) {
331 $whitelist = _civicrm_api3_system_get_whitelist(__DIR__ . '/System/mysql-whitelist.txt');
332 }
333
334 $inis = ini_get_all(NULL, FALSE);
335 $result = [];
336 $dao = CRM_Core_DAO::executeQuery('SHOW VARIABLES');
337 while ($dao->fetch()) {
338 if (empty($dao->Variable_name) || in_array($dao->Variable_name, $whitelist)) {
339 $result[$dao->Variable_name] = $dao->Value;
340 }
341 else {
342 $result[$dao->Variable_name] = 'REDACTED';
343 }
344 }
345
346 return $result;
347 }
348
349 /**
350 * Get redacted settings.
351 *
352 * @return array
353 * @throws CiviCRM_API3_Exception
354 */
355 function _civicrm_api3_system_get_redacted_settings() {
356 static $whitelist = NULL;
357 if ($whitelist === NULL) {
358 $whitelist = _civicrm_api3_system_get_whitelist(__DIR__ . '/System/setting-whitelist.txt');
359 }
360
361 $apiResult = civicrm_api3('Setting', 'get', []);
362 $result = [];
363 foreach ($apiResult['values'] as $settings) {
364 foreach ($settings as $key => $value) {
365 if (in_array($key, $whitelist)) {
366 $result[$key] = $value;
367 }
368 }
369 }
370
371 return $result;
372 }
373
374 /**
375 * Read a whitelist.
376 *
377 * @param string $whitelistFile
378 * Name of a file. Each line is a field name. Comments begin with "#".
379 * @return array
380 */
381 function _civicrm_api3_system_get_whitelist($whitelistFile) {
382 $whitelist = array_filter(
383 explode("\n", file_get_contents($whitelistFile)),
384 function ($k) {
385 return !empty($k) && !preg_match('/^\s*#/', $k);
386 }
387 );
388 return $whitelist;
389 }
390
391 /**
392 * Update log table structures.
393 *
394 * This updates the engine type if defined in the hook and changes the field type
395 * for log_conn_id to reflect CRM-18193.
396 */
397 function civicrm_api3_system_updatelogtables($params) {
398 $schema = new CRM_Logging_Schema();
399 $updatedTablesCount = $schema->updateLogTableSchema($params);
400 return civicrm_api3_create_success($updatedTablesCount);
401 }
402
403 /**
404 * Adjust Metadata for Flush action.
405 *
406 * The metadata is used for setting defaults, documentation & validation.
407 *
408 * @param array $params
409 * Array of parameters determined by getfields.
410 */
411 function _civicrm_api3_system_updatelogtables_spec(&$params) {
412 $params['updateChangedEngineConfig'] = [
413 'title' => 'Update Engine Config if changed?',
414 'description' => 'By default, we only update if the ENGINE has changed, set this to TRUE to update if the ENGINE_CONFIG has changed.',
415 'type' => CRM_Utils_Type::T_BOOLEAN,
416 'api.default' => FALSE,
417 ];
418 $params['forceEngineMigration'] = [
419 'title' => 'Force storage engine to upgrade to InnoDB?',
420 'description' => 'Older versions of CiviCRM used the ARCHIVE engine by default. Set this to TRUE to migrate the engine to the new default.',
421 'type' => CRM_Utils_Type::T_BOOLEAN,
422 'api.default' => FALSE,
423 ];
424 }
425
426 /**
427 * Update indexes.
428 *
429 * This adds any indexes that exist in the schema but not the database.
430 */
431 function civicrm_api3_system_updateindexes() {
432 CRM_Core_BAO_SchemaHandler::createMissingIndices(CRM_Core_BAO_SchemaHandler::getMissingIndices(TRUE));
433 return civicrm_api3_create_success(1);
434 }
435
436 /**
437 * Creates missing log tables.
438 *
439 * CRM-20838 - This adds any missing log tables into the database.
440 */
441 function civicrm_api3_system_createmissinglogtables() {
442 $schema = new CRM_Logging_Schema();
443 $missingLogTables = $schema->getMissingLogTables();
444 if (!empty($missingLogTables)) {
445 foreach ($missingLogTables as $tableName) {
446 $schema->fixSchemaDifferencesFor($tableName);
447 }
448 }
449 return civicrm_api3_create_success(1);
450 }
451
452 /**
453 * Rebuild Multilingual Schema
454 *
455 */
456 function civicrm_api3_system_rebuildmultilingualschema() {
457 $domain = new CRM_Core_DAO_Domain();
458 $domain->find(TRUE);
459
460 if ($domain->locales) {
461 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
462 CRM_Core_I18n_Schema::rebuildMultilingualSchema($locales);
463 return civicrm_api3_create_success(1);
464 }
465 else {
466 throw new API_Exception('Cannot call rebuild Multilingual schema on non Multilingual database');
467 }
468 }