Commit | Line | Data |
---|---|---|
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 |
28 | define('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 | */ |
50 | function 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 | */ | |
71 | function _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 | */ |
91 | function 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 | */ |
128 | function 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 | */ | |
143 | function _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 | */ |
160 | function 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 | */ | |
174 | function _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 | */ |
193 | function 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 | */ | |
207 | function _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 | */ |
228 | function 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 | */ | |
268 | function _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 | */ |
299 | function 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 | */ | |
323 | function _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 | */ |
346 | function 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 | */ | |
392 | function 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 | 417 | function _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 | } |