This allows one to enable or disable a series of extensions by path.
This should be useful, for example, when integrating with `composer` or
`drush make`. Without any knowledge of the specific extensions
involved, one might:
* If you download a bunch of extensions to a common dir (e.g.
composer's `vendor/`) and need to enable them all, then run
`cv api extension.install path=$PWD/vendor/*` (circa `post-install-cmd`)
* If you're deleting a specific directory (e.g. via composer's
`uninstall`), then remove it gracefully from the DB by running
`cv api extension.disable path=$PKGDIR` (circa `pre-package-uninstall`)
return $urls;
}
+ /**
+ * Get a list of extension keys, filtered by the corresponding file path.
+ *
+ * @param string $pattern
+ * A file path. To search subdirectories, append "*".
+ * Ex: "/var/www/extensions/*"
+ * Ex: "/var/www/extensions/org.foo.bar"
+ * @return array
+ * Array(string $key).
+ * Ex: array("org.foo.bar").
+ */
+ public function getKeysByPath($pattern) {
+ $keys = array();
+
+ if (CRM_Utils_String::endsWith($pattern, '*')) {
+ $prefix = rtrim($pattern, '*');
+ foreach ($this->container->getKeys() as $key) {
+ $path = CRM_Utils_File::addTrailingSlash($this->container->getPath($key));
+ if (realpath($prefix) == realpath($path) || CRM_Utils_File::isChildPath($prefix, $path)) {
+ $keys[] = $key;
+ }
+ }
+ }
+ else {
+ foreach ($this->container->getKeys() as $key) {
+ $path = CRM_Utils_File::addTrailingSlash($this->container->getPath($key));
+ if (realpath($pattern) == realpath($path)) {
+ $keys[] = $key;
+ }
+ }
+ }
+
+ return $keys;
+ }
+
/**
* @return array
* Ex: $result['org.civicrm.foobar'] = new CRM_Extension_Info(...).
* Input parameters.
* - key: string, eg "com.example.myextension"
* - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
+ * - path: string, e.g. "/var/www/extensions/*"
*
* Using 'keys' should be more performant than making multiple API calls with 'key'
*
function _civicrm_api3_extension_install_spec(&$fields) {
$fields['keys'] = array(
'title' => 'Extension Key(s)',
- 'api.required' => 1,
'api.aliases' => array('key'),
'type' => CRM_Utils_Type::T_STRING,
'description' => 'Fully qualified name of one or more extensions',
);
+ $fields['path'] = array(
+ 'title' => 'Extension Path',
+ 'type' => CRM_Utils_Type::T_STRING,
+ 'description' => 'The path to the extension. May use wildcard ("*").',
+ );
}
/**
* Input parameters.
* - key: string, eg "com.example.myextension"
* - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
+ * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
*
* Using 'keys' should be more performant than making multiple API calls with 'key'
*
* Input parameters.
* - key: string, eg "com.example.myextension"
* - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
+ * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
*
* Using 'keys' should be more performant than making multiple API calls with 'key'
*
* Input parameters.
* - key: string, eg "com.example.myextension"
* - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2")
+ * - path: string, e.g. "/var/www/vendor/foo/myext" or "/var/www/vendor/*"
*
* Using 'keys' should be more performant than making multiple API calls with 'key'
*
*
* @param array $params
* @param string $key
- * API request params with 'keys'.
+ * API request params with 'keys' or 'path'.
+ * - keys: A comma-delimited list of extension names
+ * - path: An absolute directory path. May append '*' to match all sub-directories.
*
* @return array
*/
function _civicrm_api3_getKeys($params, $key = 'keys') {
+ if ($key == 'path') {
+ return CRM_Extension_System::singleton()->getMapper()->getKeysByPath($params['path']);
+ }
if (isset($params[$key])) {
if (is_array($params[$key])) {
return $params[$key];
}
return explode(API_V3_EXTENSION_DELIMITER, $params[$key]);
}
- else {
- return array();
- }
+ throw new API_Exception("Missing required parameter: key, keys, or path");
}
* @group headless
*/
class CRM_Extension_MapperTest extends CiviUnitTestCase {
+
+ /**
+ * @var string
+ */
+ protected $basedir, $basedir2;
+
+ /**
+ * @var CRM_Extension_Container_Interface
+ */
+ protected $container, $containerWithSlash;
+
+ /**
+ * @var CRM_Extension_Mapper
+ */
+ protected $mapper, $mapperWithSlash;
+
public function setUp() {
parent::setUp();
list ($this->basedir, $this->container) = $this->_createContainer();
$this->assertEquals(rtrim($config->resourceBase, '/'), $this->mapperWithSlash->keyToUrl('civicrm'));
}
+ public function testGetKeysByPath() {
+ $mappers = array(
+ $this->basedir => $this->mapper,
+ $this->basedir2 => $this->mapperWithSlash,
+ );
+ foreach ($mappers as $basedir => $mapper) {
+ /** @var CRM_Extension_Mapper $mapper */
+ $this->assertEquals(array(), $mapper->getKeysByPath($basedir));
+ $this->assertEquals(array(), $mapper->getKeysByPath($basedir . '/weird'));
+ $this->assertEquals(array(), $mapper->getKeysByPath($basedir . '/weird/'));
+ $this->assertEquals(array(), $mapper->getKeysByPath($basedir . '/weird//'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/*'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '//*'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/weird/*'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/weird/foobar'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/weird/foobar/'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/weird/foobar//'));
+ $this->assertEquals(array('test.foo.bar'), $mapper->getKeysByPath($basedir . '/weird/foobar/*'));
+ }
+ }
+
/**
* @param CRM_Utils_Cache_Interface $cache
* @param null $cacheKey