Merge pull request #14981 from eileenmcnaughton/load_extract
[civicrm-core.git] / api / v3 / Extension.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
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
c490a46a
CW
28define('API_V3_EXTENSION_DELIMITER', ',');
29
30
6a488035 31/**
244bbdd8 32 * This provides an api interface for CiviCRM extension management.
6a488035
TO
33 *
34 * @package CiviCRM_APIv3
6a488035
TO
35 */
36
37/**
dc64d047 38 * Install an extension.
6a488035 39 *
cf470720
TO
40 * @param array $params
41 * Input parameters.
e5f24f02
CW
42 * - key: string, eg "com.example.myextension"
43 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
3b3f6d23 44 * - path: string, e.g. "/var/www/extensions/*"
e5f24f02
CW
45 *
46 * Using 'keys' should be more performant than making multiple API calls with 'key'
6a488035 47 *
a6c01b45 48 * @return array
6a488035
TO
49 */
50function civicrm_api3_extension_install($params) {
82b474af 51 $keys = _civicrm_api3_getKeys($params);
dbb0dbe6 52 if (!$keys) {
6a488035
TO
53 return civicrm_api3_create_success();
54 }
55
56 try {
4827c43c
TO
57 $manager = CRM_Extension_System::singleton()->getManager();
58 $manager->install($manager->findInstallRequirements($keys));
0db6c3e1
TO
59 }
60 catch (CRM_Extension_Exception $e) {
6a488035
TO
61 return civicrm_api3_create_error($e->getMessage());
62 }
63
64 return civicrm_api3_create_success();
65}
66
e5f24f02
CW
67/**
68 * Spec function for getfields
69 * @param $fields
70 */
71function _civicrm_api3_extension_install_spec(&$fields) {
cf8f0fff 72 $fields['keys'] = [
e5f24f02 73 'title' => 'Extension Key(s)',
cf8f0fff 74 'api.aliases' => ['key'],
e5f24f02
CW
75 'type' => CRM_Utils_Type::T_STRING,
76 'description' => 'Fully qualified name of one or more extensions',
cf8f0fff
CW
77 ];
78 $fields['path'] = [
3b3f6d23
TO
79 'title' => 'Extension Path',
80 'type' => CRM_Utils_Type::T_STRING,
81 'description' => 'The path to the extension. May use wildcard ("*").',
cf8f0fff 82 ];
e5f24f02
CW
83}
84
9a477d10 85/**
d1b0d05e 86 * Upgrade an extension - runs upgrade_N hooks and system.flush.
9a477d10 87 *
a6c01b45 88 * @return array
72b3a70c 89 * API result
9a477d10
FG
90 */
91function civicrm_api3_extension_upgrade() {
92 CRM_Core_Invoke::rebuildMenuAndCaches(TRUE);
93 $queue = CRM_Extension_Upgrades::createQueue();
cf8f0fff 94 $runner = new CRM_Queue_Runner([
9a477d10
FG
95 'title' => 'Extension Upgrades',
96 'queue' => $queue,
97 'errorMode' => CRM_Queue_Runner::ERROR_ABORT,
cf8f0fff 98 ]);
9a477d10
FG
99
100 try {
101 $result = $runner->runAll();
0db6c3e1
TO
102 }
103 catch (CRM_Extension_Exception $e) {
9a477d10
FG
104 return civicrm_api3_create_error($e->getMessage());
105 }
106
107 if ($result === TRUE) {
108 return civicrm_api3_create_success();
0db6c3e1
TO
109 }
110 else {
9a477d10
FG
111 return $result;
112 }
113}
114
6a488035 115/**
dc64d047 116 * Enable an extension.
6a488035 117 *
cf470720
TO
118 * @param array $params
119 * Input parameters.
e5f24f02
CW
120 * - key: string, eg "com.example.myextension"
121 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
3b3f6d23 122 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
e5f24f02
CW
123 *
124 * Using 'keys' should be more performant than making multiple API calls with 'key'
6a488035 125 *
a6c01b45 126 * @return array
6a488035
TO
127 */
128function civicrm_api3_extension_enable($params) {
82b474af 129 $keys = _civicrm_api3_getKeys($params);
6a488035
TO
130 if (count($keys) == 0) {
131 return civicrm_api3_create_success();
132 }
133
4827c43c
TO
134 $manager = CRM_Extension_System::singleton()->getManager();
135 $manager->enable($manager->findInstallRequirements($keys));
6a488035
TO
136 return civicrm_api3_create_success();
137}
138
e5f24f02
CW
139/**
140 * Spec function for getfields
141 * @param $fields
142 */
143function _civicrm_api3_extension_enable_spec(&$fields) {
144 _civicrm_api3_extension_install_spec($fields);
145}
146
6a488035 147/**
9d32e6f7 148 * Disable an extension.
6a488035 149 *
cf470720
TO
150 * @param array $params
151 * Input parameters.
e5f24f02
CW
152 * - key: string, eg "com.example.myextension"
153 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
3b3f6d23 154 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
e5f24f02
CW
155 *
156 * Using 'keys' should be more performant than making multiple API calls with 'key'
6a488035 157 *
a6c01b45 158 * @return array
6a488035
TO
159 */
160function civicrm_api3_extension_disable($params) {
82b474af 161 $keys = _civicrm_api3_getKeys($params);
6a488035
TO
162 if (count($keys) == 0) {
163 return civicrm_api3_create_success();
164 }
165
166 CRM_Extension_System::singleton()->getManager()->disable($keys);
167 return civicrm_api3_create_success();
168}
169
e5f24f02
CW
170/**
171 * Spec function for getfields
172 * @param $fields
173 */
174function _civicrm_api3_extension_disable_spec(&$fields) {
175 _civicrm_api3_extension_install_spec($fields);
176}
177
6a488035 178/**
dc64d047 179 * Uninstall an extension.
6a488035 180 *
cf470720
TO
181 * @param array $params
182 * Input parameters.
e5f24f02
CW
183 * - key: string, eg "com.example.myextension"
184 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
3b3f6d23 185 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
e5f24f02
CW
186 *
187 * Using 'keys' should be more performant than making multiple API calls with 'key'
188 *
189 * @todo: removeFiles as optional param
6a488035 190 *
a6c01b45 191 * @return array
6a488035
TO
192 */
193function civicrm_api3_extension_uninstall($params) {
82b474af 194 $keys = _civicrm_api3_getKeys($params);
6a488035
TO
195 if (count($keys) == 0) {
196 return civicrm_api3_create_success();
197 }
198
6a488035
TO
199 CRM_Extension_System::singleton()->getManager()->uninstall($keys);
200 return civicrm_api3_create_success();
201}
202
e5f24f02
CW
203/**
204 * Spec function for getfields
205 * @param $fields
206 */
207function _civicrm_api3_extension_uninstall_spec(&$fields) {
208 _civicrm_api3_extension_install_spec($fields);
209 //$fields['removeFiles'] = array(
210 // 'title' => 'Remove files',
211 // 'description' => 'Whether to remove the source tree. Default FALSE.',
212 // 'type' => CRM_Utils_Type::T_BOOLEAN,
213 //);
214}
215
6a488035 216/**
dc64d047 217 * Download and install an extension.
6a488035 218 *
cf470720
TO
219 * @param array $params
220 * Input parameters.
244bbdd8
CW
221 * - key: string, eg "com.example.myextension"
222 * - url: string eg "http://repo.com/myextension-1.0.zip"
6a488035 223 *
77b97be7 224 * @throws API_Exception
a6c01b45 225 * @return array
72b3a70c 226 * API result
6a488035
TO
227 */
228function civicrm_api3_extension_download($params) {
6c552737
TO
229 if (!array_key_exists('url', $params)) {
230 if (!CRM_Extension_System::singleton()->getBrowser()->isEnabled()) {
e5f24f02 231 throw new API_Exception('Automatic downloading is disabled. Try adding parameter "url"');
6a488035
TO
232 }
233 if ($reqs = CRM_Extension_System::singleton()->getBrowser()->checkRequirements()) {
234 $first = array_shift($reqs);
235 throw new API_Exception($first['message']);
236 }
237 if ($info = CRM_Extension_System::singleton()->getBrowser()->getExtension($params['key'])) {
238 if ($info->downloadUrl) {
239 $params['url'] = $info->downloadUrl;
240 }
241 }
242 }
243
6c552737 244 if (!array_key_exists('url', $params)) {
6a488035
TO
245 throw new API_Exception('Cannot resolve download url for extension. Try adding parameter "url"');
246 }
247
248 foreach (CRM_Extension_System::singleton()->getDownloader()->checkRequirements() as $requirement) {
249 return civicrm_api3_create_error($requirement['message']);
250 }
251
6c552737 252 if (!CRM_Extension_System::singleton()->getDownloader()->download($params['key'], $params['url'])) {
6a488035
TO
253 return civicrm_api3_create_error('Download failed - ZIP file is unavailable or malformed');
254 }
255 CRM_Extension_System::singleton()->getCache()->flush();
256 CRM_Extension_System::singleton(TRUE);
c62449ca 257 if (CRM_Utils_Array::value('install', $params, TRUE)) {
cf8f0fff 258 CRM_Extension_System::singleton()->getManager()->install([$params['key']]);
c62449ca 259 }
6a488035
TO
260
261 return civicrm_api3_create_success();
262}
263
e5f24f02
CW
264/**
265 * Spec function for getfields
266 * @param $fields
267 */
268function _civicrm_api3_extension_download_spec(&$fields) {
cf8f0fff 269 $fields['key'] = [
e5f24f02
CW
270 'title' => 'Extension Key',
271 'api.required' => 1,
272 'type' => CRM_Utils_Type::T_STRING,
273 'description' => 'Fully qualified name of the extension',
cf8f0fff
CW
274 ];
275 $fields['url'] = [
e5f24f02
CW
276 'title' => 'Download URL',
277 'type' => CRM_Utils_Type::T_STRING,
278 'description' => 'Optional as the system can determine the url automatically for public extensions',
cf8f0fff
CW
279 ];
280 $fields['install'] = [
c62449ca
TO
281 'title' => 'Auto-install',
282 'type' => CRM_Utils_Type::T_STRING,
283 'description' => 'Automatically install the downloaded extension',
284 'api.default' => TRUE,
cf8f0fff 285 ];
e5f24f02
CW
286}
287
6a488035 288/**
dc64d047 289 * Download and install an extension.
6a488035 290 *
cf470720
TO
291 * @param array $params
292 * Input parameters.
244bbdd8
CW
293 * - local: bool, whether to rescan local filesystem (default: TRUE)
294 * - remote: bool, whether to rescan remote repository (default: TRUE)
6a488035 295 *
a6c01b45 296 * @return array
72b3a70c 297 * API result
6a488035
TO
298 */
299function civicrm_api3_extension_refresh($params) {
6a488035
TO
300 $system = CRM_Extension_System::singleton(TRUE);
301
302 if ($params['local']) {
303 $system->getManager()->refresh();
7c31ae57
SL
304 // force immediate scan
305 $system->getManager()->getStatuses();
6a488035
TO
306 }
307
308 if ($params['remote']) {
309 if ($system->getBrowser()->isEnabled() && empty($system->getBrowser()->checkRequirements)) {
310 $system->getBrowser()->refresh();
7c31ae57
SL
311 // force immediate download
312 $system->getBrowser()->getExtensions();
6a488035
TO
313 }
314 }
315
316 return civicrm_api3_create_success();
317}
318
e5f24f02
CW
319/**
320 * Spec function for getfields
321 * @param $fields
322 */
323function _civicrm_api3_extension_refresh_spec(&$fields) {
cf8f0fff 324 $fields['local'] = [
e5f24f02
CW
325 'title' => 'Rescan Local',
326 'api.default' => 1,
327 'type' => CRM_Utils_Type::T_BOOLEAN,
328 'description' => 'Whether to rescan the local filesystem (default TRUE)',
cf8f0fff
CW
329 ];
330 $fields['remote'] = [
e5f24f02
CW
331 'title' => 'Rescan Remote',
332 'api.default' => 1,
333 'type' => CRM_Utils_Type::T_BOOLEAN,
334 'description' => 'Whether to rescan the remote repository (default TRUE)',
cf8f0fff 335 ];
e5f24f02
CW
336}
337
6a488035 338/**
d1b0d05e 339 * Get a list of available extensions.
6a488035 340 *
c490a46a 341 * @param array $params
77b97be7 342 *
a6c01b45 343 * @return array
72b3a70c 344 * API result
6a488035
TO
345 */
346function civicrm_api3_extension_get($params) {
df4848b8
TS
347 $full_names = _civicrm_api3_getKeys($params, 'full_name');
348 $keys = _civicrm_api3_getKeys($params, 'key');
349 $keys = array_merge($full_names, $keys);
6a488035 350 $statuses = CRM_Extension_System::singleton()->getManager()->getStatuses();
517dfba8 351 $mapper = CRM_Extension_System::singleton()->getMapper();
cf8f0fff 352 $result = [];
c36fe5b0 353 $id = 0;
6a488035 354 foreach ($statuses as $key => $status) {
517dfba8
AS
355 try {
356 $obj = $mapper->keyToInfo($key);
357 }
358 catch (CRM_Extension_Exception $ex) {
cf8f0fff 359 CRM_Core_Session::setStatus(ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]));
517dfba8
AS
360 continue;
361 }
362 $info = CRM_Extension_System::createExtendedInfo($obj);
7c31ae57
SL
363 // backward compatibility with indexing scheme
364 $info['id'] = $id++;
df4848b8 365 if (!empty($keys)) {
d4c44c70
SL
366 if (in_array($key, $keys)) {
367 $result[] = $info;
368 }
369 }
370 else {
371 $result[] = $info;
372 }
6a488035 373 }
c3733bf5
TO
374
375 // These fields have been filtered already, and they have special semantics.
376 unset($params['key']);
377 unset($params['keys']);
378 unset($params['full_name']);
379
cf8f0fff 380 $filterableFields = ['id', 'type', 'status', 'path'];
1d470b75 381 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', $filterableFields);
6a488035
TO
382}
383
c74a6c8f
AS
384/**
385 * Get a list of remotely available extensions.
386 *
387 * @param array $params
388 *
389 * @return array
390 * API result
391 */
392function civicrm_api3_extension_getremote($params) {
393 $extensions = CRM_Extension_System::singleton()->getBrowser()->getExtensions();
cf8f0fff 394 $result = [];
c74a6c8f
AS
395 $id = 0;
396 foreach ($extensions as $key => $obj) {
cf8f0fff 397 $info = [];
7c31ae57
SL
398 // backward compatibility with indexing scheme
399 $info['id'] = $id++;
c74a6c8f
AS
400 $info = array_merge($info, (array) $obj);
401 $result[] = $info;
402 }
cf8f0fff 403 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', CRM_Utils_Array::value('return', $params, []));
c74a6c8f
AS
404}
405
6a488035 406/**
d1b0d05e 407 * Determine the list of extension keys.
6a488035 408 *
cf470720 409 * @param array $params
df4848b8 410 * @param string $key
3b3f6d23
TO
411 * API request params with 'keys' or 'path'.
412 * - keys: A comma-delimited list of extension names
413 * - path: An absolute directory path. May append '*' to match all sub-directories.
d1b0d05e 414 *
a6c01b45 415 * @return array
6a488035 416 */
82b474af 417function _civicrm_api3_getKeys($params, $key = 'keys') {
3b3f6d23
TO
418 if ($key == 'path') {
419 return CRM_Extension_System::singleton()->getMapper()->getKeysByPath($params['path']);
420 }
83f51f6a
TS
421 if (isset($params[$key])) {
422 if (is_array($params[$key])) {
423 return $params[$key];
424 }
425 if ($params[$key] == '') {
cf8f0fff 426 return [];
83f51f6a
TS
427 }
428 return explode(API_V3_EXTENSION_DELIMITER, $params[$key]);
6f59c19b 429 }
97488fea 430 else {
cf8f0fff 431 return [];
97488fea 432 }
6a488035 433}