3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2020 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
28 define('API_V3_EXTENSION_DELIMITER', ',');
32 * This provides an api interface for CiviCRM extension management.
34 * @package CiviCRM_APIv3
38 * Install an extension.
40 * @param array $params
42 * - key: string, eg "com.example.myextension"
43 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
44 * - path: string, e.g. "/var/www/extensions/*"
46 * Using 'keys' should be more performant than making multiple API calls with 'key'
50 function civicrm_api3_extension_install($params) {
51 $keys = _civicrm_api3_getKeys($params);
53 return civicrm_api3_create_success();
57 $manager = CRM_Extension_System
::singleton()->getManager();
58 $manager->install($manager->findInstallRequirements($keys));
60 catch (CRM_Extension_Exception
$e) {
61 return civicrm_api3_create_error($e->getMessage());
64 return civicrm_api3_create_success();
68 * Spec function for getfields
71 function _civicrm_api3_extension_install_spec(&$fields) {
73 'title' => 'Extension Key(s)',
74 'api.aliases' => ['key'],
75 'type' => CRM_Utils_Type
::T_STRING
,
76 'description' => 'Fully qualified name of one or more extensions',
79 'title' => 'Extension Path',
80 'type' => CRM_Utils_Type
::T_STRING
,
81 'description' => 'The path to the extension. May use wildcard ("*").',
86 * Upgrade an extension - runs upgrade_N hooks and system.flush.
91 function civicrm_api3_extension_upgrade() {
92 CRM_Core_Invoke
::rebuildMenuAndCaches(TRUE);
93 $queue = CRM_Extension_Upgrades
::createQueue();
94 $runner = new CRM_Queue_Runner([
95 'title' => 'Extension Upgrades',
97 'errorMode' => CRM_Queue_Runner
::ERROR_ABORT
,
101 $result = $runner->runAll();
103 catch (CRM_Extension_Exception
$e) {
104 return civicrm_api3_create_error($e->getMessage());
107 if ($result === TRUE) {
108 return civicrm_api3_create_success();
116 * Enable an extension.
118 * @param array $params
120 * - key: string, eg "com.example.myextension"
121 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
122 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
124 * Using 'keys' should be more performant than making multiple API calls with 'key'
128 function civicrm_api3_extension_enable($params) {
129 $keys = _civicrm_api3_getKeys($params);
130 if (count($keys) == 0) {
131 return civicrm_api3_create_success();
134 $manager = CRM_Extension_System
::singleton()->getManager();
135 $manager->enable($manager->findInstallRequirements($keys));
136 return civicrm_api3_create_success();
140 * Spec function for getfields
143 function _civicrm_api3_extension_enable_spec(&$fields) {
144 _civicrm_api3_extension_install_spec($fields);
148 * Disable an extension.
150 * @param array $params
152 * - key: string, eg "com.example.myextension"
153 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
154 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
156 * Using 'keys' should be more performant than making multiple API calls with 'key'
160 function civicrm_api3_extension_disable($params) {
161 $keys = _civicrm_api3_getKeys($params);
162 if (count($keys) == 0) {
163 return civicrm_api3_create_success();
166 CRM_Extension_System
::singleton()->getManager()->disable($keys);
167 return civicrm_api3_create_success();
171 * Spec function for getfields
174 function _civicrm_api3_extension_disable_spec(&$fields) {
175 _civicrm_api3_extension_install_spec($fields);
179 * Uninstall an extension.
181 * @param array $params
183 * - key: string, eg "com.example.myextension"
184 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
185 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
187 * Using 'keys' should be more performant than making multiple API calls with 'key'
189 * @todo: removeFiles as optional param
193 function civicrm_api3_extension_uninstall($params) {
194 $keys = _civicrm_api3_getKeys($params);
195 if (count($keys) == 0) {
196 return civicrm_api3_create_success();
199 CRM_Extension_System
::singleton()->getManager()->uninstall($keys);
200 return civicrm_api3_create_success();
204 * Spec function for getfields
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,
217 * Download and install an extension.
219 * @param array $params
221 * - key: string, eg "com.example.myextension"
222 * - url: string eg "http://repo.com/myextension-1.0.zip"
224 * @throws API_Exception
228 function civicrm_api3_extension_download($params) {
229 if (!array_key_exists('url', $params)) {
230 if (!CRM_Extension_System
::singleton()->getBrowser()->isEnabled()) {
231 throw new API_Exception('Automatic downloading is disabled. Try adding parameter "url"');
233 if ($reqs = CRM_Extension_System
::singleton()->getBrowser()->checkRequirements()) {
234 $first = array_shift($reqs);
235 throw new API_Exception($first['message']);
237 if ($info = CRM_Extension_System
::singleton()->getBrowser()->getExtension($params['key'])) {
238 if ($info->downloadUrl
) {
239 $params['url'] = $info->downloadUrl
;
244 if (!array_key_exists('url', $params)) {
245 throw new API_Exception('Cannot resolve download url for extension. Try adding parameter "url"');
251 foreach (CRM_Extension_System
::singleton()->getDownloader()->checkRequirements($info) as $requirement) {
252 return civicrm_api3_create_error($requirement['message']);
255 if (!CRM_Extension_System
::singleton()->getDownloader()->download($params['key'], $params['url'])) {
256 return civicrm_api3_create_error('Download failed - ZIP file is unavailable or malformed');
258 CRM_Extension_System
::singleton()->getCache()->flush();
259 CRM_Extension_System
::singleton(TRUE);
260 if (CRM_Utils_Array
::value('install', $params, TRUE)) {
261 CRM_Extension_System
::singleton()->getManager()->install([$params['key']]);
264 return civicrm_api3_create_success();
268 * Spec function for getfields
271 function _civicrm_api3_extension_download_spec(&$fields) {
273 'title' => 'Extension Key',
275 'type' => CRM_Utils_Type
::T_STRING
,
276 'description' => 'Fully qualified name of the extension',
279 'title' => 'Download URL',
280 'type' => CRM_Utils_Type
::T_STRING
,
281 'description' => 'Optional as the system can determine the url automatically for public extensions',
283 $fields['install'] = [
284 'title' => 'Auto-install',
285 'type' => CRM_Utils_Type
::T_STRING
,
286 'description' => 'Automatically install the downloaded extension',
287 'api.default' => TRUE,
292 * Download and install an extension.
294 * @param array $params
296 * - local: bool, whether to rescan local filesystem (default: TRUE)
297 * - remote: bool, whether to rescan remote repository (default: TRUE)
302 function civicrm_api3_extension_refresh($params) {
303 $system = CRM_Extension_System
::singleton(TRUE);
305 if ($params['local']) {
306 $system->getManager()->refresh();
307 // force immediate scan
308 $system->getManager()->getStatuses();
311 if ($params['remote']) {
312 if ($system->getBrowser()->isEnabled() && empty($system->getBrowser()->checkRequirements
)) {
313 $system->getBrowser()->refresh();
314 // force immediate download
315 $system->getBrowser()->getExtensions();
319 return civicrm_api3_create_success();
323 * Spec function for getfields
326 function _civicrm_api3_extension_refresh_spec(&$fields) {
328 'title' => 'Rescan Local',
330 'type' => CRM_Utils_Type
::T_BOOLEAN
,
331 'description' => 'Whether to rescan the local filesystem (default TRUE)',
333 $fields['remote'] = [
334 'title' => 'Rescan Remote',
336 'type' => CRM_Utils_Type
::T_BOOLEAN
,
337 'description' => 'Whether to rescan the remote repository (default TRUE)',
342 * Get a list of available extensions.
344 * @param array $params
349 function civicrm_api3_extension_get($params) {
350 $full_names = _civicrm_api3_getKeys($params, 'full_name');
351 $keys = _civicrm_api3_getKeys($params, 'key');
352 $keys = array_merge($full_names, $keys);
353 $statuses = CRM_Extension_System
::singleton()->getManager()->getStatuses();
354 $mapper = CRM_Extension_System
::singleton()->getMapper();
357 foreach ($statuses as $key => $status) {
359 $obj = $mapper->keyToInfo($key);
361 catch (CRM_Extension_Exception
$ex) {
362 CRM_Core_Session
::setStatus(ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]));
365 $info = CRM_Extension_System
::createExtendedInfo($obj);
366 // backward compatibility with indexing scheme
369 if (in_array($key, $keys)) {
378 // These fields have been filtered already, and they have special semantics.
379 unset($params['key']);
380 unset($params['keys']);
381 unset($params['full_name']);
383 $filterableFields = ['id', 'type', 'status', 'path'];
384 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', $filterableFields);
388 * Get a list of remotely available extensions.
390 * @param array $params
395 function civicrm_api3_extension_getremote($params) {
396 $extensions = CRM_Extension_System
::singleton()->getBrowser()->getExtensions();
399 foreach ($extensions as $key => $obj) {
401 // backward compatibility with indexing scheme
403 $info = array_merge($info, (array) $obj);
406 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', CRM_Utils_Array
::value('return', $params, []));
410 * Determine the list of extension keys.
412 * @param array $params
414 * API request params with 'keys' or 'path'.
415 * - keys: A comma-delimited list of extension names
416 * - path: An absolute directory path. May append '*' to match all sub-directories.
420 function _civicrm_api3_getKeys($params, $key = 'keys') {
421 if ($key == 'path') {
422 return CRM_Extension_System
::singleton()->getMapper()->getKeysByPath($params['path']);
424 if (isset($params[$key])) {
425 if (is_array($params[$key])) {
426 return $params[$key];
428 if ($params[$key] == '') {
431 return explode(API_V3_EXTENSION_DELIMITER
, $params[$key]);