Commit | Line | Data |
---|---|---|
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 |
12 | define('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 | */ |
34 | function 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 | */ | |
55 | function _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 | */ |
75 | function 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 | */ |
112 | function 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 | */ | |
127 | function _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 | */ |
144 | function 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 | */ | |
158 | function _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 | */ |
177 | function 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 | */ | |
191 | function _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 | */ |
212 | function 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 | */ | |
255 | function _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 | */ |
286 | function 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 | */ | |
310 | function _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 | */ |
333 | function 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 | */ | |
379 | function 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 | 404 | function _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 | } |