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