3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 |
9 +--------------------------------------------------------------------+
12 define('API_V3_EXTENSION_DELIMITER', ',');
16 * This provides an api interface for CiviCRM extension management.
18 * @package CiviCRM_APIv3
22 * Install an extension.
24 * @param array $params
26 * - key: string, eg "com.example.myextension"
27 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
28 * - path: string, e.g. "/var/www/extensions/*"
30 * Using 'keys' should be more performant than making multiple API calls with 'key'
34 function civicrm_api3_extension_install($params) {
35 $keys = _civicrm_api3_getKeys($params);
37 return civicrm_api3_create_success();
41 $manager = CRM_Extension_System
::singleton()->getManager();
42 $manager->install($manager->findInstallRequirements($keys));
44 catch (CRM_Extension_Exception
$e) {
45 return civicrm_api3_create_error($e->getMessage());
48 return civicrm_api3_create_success();
52 * Spec function for getfields
55 function _civicrm_api3_extension_install_spec(&$fields) {
57 'title' => 'Extension Key(s)',
58 'api.aliases' => ['key'],
59 'type' => CRM_Utils_Type
::T_STRING
,
60 'description' => 'Fully qualified name of one or more extensions',
63 'title' => 'Extension Path',
64 'type' => CRM_Utils_Type
::T_STRING
,
65 'description' => 'The path to the extension. May use wildcard ("*").',
70 * Upgrade an extension - runs upgrade_N hooks and system.flush.
75 function civicrm_api3_extension_upgrade() {
76 CRM_Core_Invoke
::rebuildMenuAndCaches(TRUE);
77 $queue = CRM_Extension_Upgrades
::createQueue();
78 $runner = new CRM_Queue_Runner([
79 'title' => 'Extension Upgrades',
81 'errorMode' => CRM_Queue_Runner
::ERROR_ABORT
,
85 $result = $runner->runAll();
87 catch (CRM_Extension_Exception
$e) {
88 return civicrm_api3_create_error($e->getMessage());
91 if ($result === TRUE) {
92 return civicrm_api3_create_success();
100 * Enable an extension.
102 * @param array $params
104 * - key: string, eg "com.example.myextension"
105 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
106 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
108 * Using 'keys' should be more performant than making multiple API calls with 'key'
112 function civicrm_api3_extension_enable($params) {
113 $keys = _civicrm_api3_getKeys($params);
114 if (count($keys) == 0) {
115 return civicrm_api3_create_success();
118 $manager = CRM_Extension_System
::singleton()->getManager();
119 $manager->enable($manager->findInstallRequirements($keys));
120 return civicrm_api3_create_success();
124 * Spec function for getfields
127 function _civicrm_api3_extension_enable_spec(&$fields) {
128 _civicrm_api3_extension_install_spec($fields);
132 * Disable an extension.
134 * @param array $params
136 * - key: string, eg "com.example.myextension"
137 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
138 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
140 * Using 'keys' should be more performant than making multiple API calls with 'key'
144 function civicrm_api3_extension_disable($params) {
145 $keys = _civicrm_api3_getKeys($params);
146 if (count($keys) == 0) {
147 return civicrm_api3_create_success();
150 CRM_Extension_System
::singleton()->getManager()->disable($keys);
151 return civicrm_api3_create_success();
155 * Spec function for getfields
158 function _civicrm_api3_extension_disable_spec(&$fields) {
159 _civicrm_api3_extension_install_spec($fields);
163 * Uninstall an extension.
165 * @param array $params
167 * - key: string, eg "com.example.myextension"
168 * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
169 * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
171 * Using 'keys' should be more performant than making multiple API calls with 'key'
173 * @todo: removeFiles as optional param
177 function civicrm_api3_extension_uninstall($params) {
178 $keys = _civicrm_api3_getKeys($params);
179 if (count($keys) == 0) {
180 return civicrm_api3_create_success();
183 CRM_Extension_System
::singleton()->getManager()->uninstall($keys);
184 return civicrm_api3_create_success();
188 * Spec function for getfields
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,
201 * Download and install an extension.
203 * @param array $params
205 * - key: string, eg "com.example.myextension"
206 * - url: string eg "http://repo.com/myextension-1.0.zip"
208 * @throws API_Exception
212 function civicrm_api3_extension_download($params) {
213 if (!array_key_exists('url', $params)) {
214 if (!CRM_Extension_System
::singleton()->getBrowser()->isEnabled()) {
215 throw new API_Exception('Automatic downloading is disabled. Try adding parameter "url"');
217 if ($reqs = CRM_Extension_System
::singleton()->getBrowser()->checkRequirements()) {
218 $first = array_shift($reqs);
219 throw new API_Exception($first['message']);
221 if ($info = CRM_Extension_System
::singleton()->getBrowser()->getExtension($params['key'])) {
222 if ($info->downloadUrl
) {
223 $params['url'] = $info->downloadUrl
;
228 if (!array_key_exists('url', $params)) {
229 throw new API_Exception('Cannot resolve download url for extension. Try adding parameter "url"');
235 foreach (CRM_Extension_System
::singleton()->getDownloader()->checkRequirements($info) as $requirement) {
236 return civicrm_api3_create_error($requirement['message']);
239 if (!CRM_Extension_System
::singleton()->getDownloader()->download($params['key'], $params['url'])) {
240 return civicrm_api3_create_error('Download failed - ZIP file is unavailable or malformed');
242 CRM_Extension_System
::singleton()->getCache()->flush();
243 CRM_Extension_System
::singleton(TRUE);
244 if (CRM_Utils_Array
::value('install', $params, TRUE)) {
245 CRM_Extension_System
::singleton()->getManager()->install([$params['key']]);
248 return civicrm_api3_create_success();
252 * Spec function for getfields
255 function _civicrm_api3_extension_download_spec(&$fields) {
257 'title' => 'Extension Key',
259 'type' => CRM_Utils_Type
::T_STRING
,
260 'description' => 'Fully qualified name of the extension',
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',
267 $fields['install'] = [
268 'title' => 'Auto-install',
269 'type' => CRM_Utils_Type
::T_STRING
,
270 'description' => 'Automatically install the downloaded extension',
271 'api.default' => TRUE,
276 * Download and install an extension.
278 * @param array $params
280 * - local: bool, whether to rescan local filesystem (default: TRUE)
281 * - remote: bool, whether to rescan remote repository (default: TRUE)
286 function civicrm_api3_extension_refresh($params) {
287 $system = CRM_Extension_System
::singleton(TRUE);
289 if ($params['local']) {
290 $system->getManager()->refresh();
291 // force immediate scan
292 $system->getManager()->getStatuses();
295 if ($params['remote']) {
296 if ($system->getBrowser()->isEnabled() && empty($system->getBrowser()->checkRequirements
)) {
297 $system->getBrowser()->refresh();
298 // force immediate download
299 $system->getBrowser()->getExtensions();
303 return civicrm_api3_create_success();
307 * Spec function for getfields
310 function _civicrm_api3_extension_refresh_spec(&$fields) {
312 'title' => 'Rescan Local',
314 'type' => CRM_Utils_Type
::T_BOOLEAN
,
315 'description' => 'Whether to rescan the local filesystem (default TRUE)',
317 $fields['remote'] = [
318 'title' => 'Rescan Remote',
320 'type' => CRM_Utils_Type
::T_BOOLEAN
,
321 'description' => 'Whether to rescan the remote repository (default TRUE)',
326 * Get a list of available extensions.
328 * @param array $params
333 function civicrm_api3_extension_get($params) {
334 $full_names = _civicrm_api3_getKeys($params, 'full_name');
335 $keys = _civicrm_api3_getKeys($params, 'key');
336 $keys = array_merge($full_names, $keys);
337 $statuses = CRM_Extension_System
::singleton()->getManager()->getStatuses();
338 $mapper = CRM_Extension_System
::singleton()->getMapper();
341 foreach ($statuses as $key => $status) {
343 $obj = $mapper->keyToInfo($key);
345 catch (CRM_Extension_Exception
$ex) {
346 CRM_Core_Session
::setStatus(ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]));
349 $info = CRM_Extension_System
::createExtendedInfo($obj);
350 // backward compatibility with indexing scheme
353 if (in_array($key, $keys)) {
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']);
367 $filterableFields = ['id', 'type', 'status', 'path'];
368 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', $filterableFields);
372 * Get a list of remotely available extensions.
374 * @param array $params
379 function civicrm_api3_extension_getremote($params) {
380 $extensions = CRM_Extension_System
::singleton()->getBrowser()->getExtensions();
383 foreach ($extensions as $key => $obj) {
385 // backward compatibility with indexing scheme
387 $info = array_merge($info, (array) $obj);
390 return _civicrm_api3_basic_array_get('Extension', $params, $result, 'id', CRM_Utils_Array
::value('return', $params, []));
394 * Determine the list of extension keys.
396 * @param array $params
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.
404 function _civicrm_api3_getKeys($params, $key = 'keys') {
405 if ($key == 'path') {
406 return CRM_Extension_System
::singleton()->getMapper()->getKeysByPath($params['path']);
408 if (isset($params[$key])) {
409 if (is_array($params[$key])) {
410 return $params[$key];
412 if ($params[$key] == '') {
415 return explode(API_V3_EXTENSION_DELIMITER
, $params[$key]);