Build plugin info from both package.xml and composer.json files; allow plugins to override the meta info method

pull/63/merge
Thomas Bruederli 11 years ago
parent b4d50cb41c
commit 378d6cf97f

@ -91,6 +91,16 @@ abstract class rcube_plugin
*/
abstract function init();
/**
* Provide information about this
*
* @return array Meta information about a plugin or false if not implemented
*/
public static function info()
{
return false;
}
/**
* Attempt to load the given plugin which is required for the current plugin
*

@ -227,6 +227,120 @@ class rcube_plugin_api
return false;
}
/**
* Get information about a specific plugin.
* This is either provided my a plugin's info() method or extracted from a package.xml or a composer.json file
*
* @param string Plugin name
* @return array Meta information about a plugin or False if plugin was not found
*/
public function get_info($plugin_name)
{
static $composer_lock, $license_uris = array(
'Apache' => 'http://www.apache.org/licenses/LICENSE-2.0.html',
'Apache-2' => 'http://www.apache.org/licenses/LICENSE-2.0.html',
'Apache-1' => 'http://www.apache.org/licenses/LICENSE-1.0',
'Apache-1.1' => 'http://www.apache.org/licenses/LICENSE-1.1',
'GPL' => 'http://www.gnu.org/licenses/gpl.html',
'GPLv2' => 'http://www.gnu.org/licenses/gpl-2.0.html',
'GPL-2.0' => 'http://www.gnu.org/licenses/gpl-2.0.html',
'GPLv3' => 'http://www.gnu.org/licenses/gpl-3.0.html',
'GPL-3.0' => 'http://www.gnu.org/licenses/gpl-3.0.html',
'GPL-3.0+' => 'http://www.gnu.org/licenses/gpl.html',
'GPL-2.0+' => 'http://www.gnu.org/licenses/gpl.html',
'LGPL' => 'http://www.gnu.org/licenses/lgpl.html',
'LGPLv2' => 'http://www.gnu.org/licenses/lgpl-2.0.html',
'LGPLv2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html',
'LGPLv3' => 'http://www.gnu.org/licenses/lgpl.html',
'LGPL-2.0' => 'http://www.gnu.org/licenses/lgpl-2.0.html',
'LGPL-2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html',
'LGPL-3.0' => 'http://www.gnu.org/licenses/lgpl.html',
'LGPL-3.0+' => 'http://www.gnu.org/licenses/lgpl.html',
'BSD' => 'http://opensource.org/licenses/bsd-license.html',
'BSD-2-Clause' => 'http://opensource.org/licenses/BSD-2-Clause',
'BSD-3-Clause' => 'http://opensource.org/licenses/BSD-3-Clause',
'FreeBSD' => 'http://opensource.org/licenses/BSD-2-Clause',
'MIT' => 'http://www.opensource.org/licenses/mit-license.php',
'PHP' => 'http://opensource.org/licenses/PHP-3.0',
'PHP-3' => 'http://www.php.net/license/3_01.txt',
'PHP-3.0' => 'http://www.php.net/license/3_0.txt',
'PHP-3.01' => 'http://www.php.net/license/3_01.txt',
);
$dir = dir($this->dir);
$fn = unslashify($dir->path) . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php';
$info = false;
if (!class_exists($plugin_name))
include($fn);
if (class_exists($plugin_name))
$info = $plugin_name::info();
// fall back to composer.json file
if (!$info) {
$composer = INSTALL_PATH . "/plugins/$plugin_name/composer.json";
if (file_exists($composer) && ($json = @json_decode(file_get_contents($composer), true))) {
list($info['vendor'], $info['name']) = explode('/', $json['name']);
$info['license'] = $json['license'];
if ($license_uri = $license_uris[$info['license']])
$info['license_uri'] = $license_uri;
}
// read local composer.lock file (once)
if (!isset($composer_lock)) {
$composer_lock = @json_decode(@file_get_contents(INSTALL_PATH . "/composer.lock"), true);
if ($composer_lock['packages']) {
foreach ($composer_lock['packages'] as $i => $package) {
$composer_lock['installed'][$package['name']] = $package;
}
}
}
// load additional information from local composer.lock file
if ($lock = $composer_lock['installed'][$json['name']]) {
$info['version'] = $lock['version'];
$info['uri'] = $lock['homepage'] ? $lock['homepage'] : $lock['source']['uri'];
$info['src_uri'] = $lock['dist']['uri'] ? $lock['dist']['uri'] : $lock['source']['uri'];
}
}
// fall back to package.xml file
if (!$info) {
$package = INSTALL_PATH . "/plugins/$plugin_name/package.xml";
if (file_exists($package) && ($file = file_get_contents($package))) {
$doc = new DOMDocument();
$doc->loadXML($file);
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0");
$data = array();
// XPaths of plugin metadata elements
$metadata = array(
'name' => 'string(//rc:package/rc:name)',
'version' => 'string(//rc:package/rc:version/rc:release)',
'license' => 'string(//rc:package/rc:license)',
'license_uri' => 'string(//rc:package/rc:license/@uri)',
'src_uri' => 'string(//rc:package/rc:srcuri)',
'uri' => 'string(//rc:package/rc:uri)',
);
foreach ($metadata as $key => $path) {
$info[$key] = $xpath->evaluate($path);
}
// dependent required plugins (can be used, but not included in config)
$deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name');
for ($i = 0; $i < $deps->length; $i++) {
$dn = $deps->item($i)->nodeValue;
$info['requires'][] = $dn;
}
}
}
return $info;
}
/**
* Allows a plugin object to register a callback for a certain hook
*

@ -40,17 +40,28 @@ function rcmail_plugins_list($attrib)
$attrib['id'] = 'rcmpluginlist';
$plugins = array_filter((array) $RCMAIL->config->get('plugins'));
$plugins = array_flip($plugins);
$plugin_info = array();
foreach ($plugins as $name => $plugin) {
rcube_plugin_data($name, $plugins);
foreach ($plugins as $name) {
if ($info = $RCMAIL->plugins->get_info($name))
$plugin_info[$name] = $info;
}
if (empty($plugins)) {
// load info from required plugins, too
foreach ($plugin_info as $name => $info) {
if (is_array($info['required']) && !empty($info['required'])) {
foreach ($info['required'] as $req_name) {
if (!isset($plugin_info[$req_name]) && ($req_info = $RCMAIL->plugins->get_info($req_name)))
$plugin_info[$req_name] = $req_info;
}
}
}
if (empty($plugin_info)) {
return '';
}
ksort($plugins, SORT_LOCALE_STRING);
ksort($plugin_info, SORT_LOCALE_STRING);
$table = new html_table($attrib);
@ -60,8 +71,8 @@ function rcmail_plugins_list($attrib)
$table->add_header('license', rcube_label('license'));
$table->add_header('source', rcube_label('source'));
foreach ($plugins as $name => $data) {
$uri = $data['srcuri'] ? $data['srcuri'] : $data['uri'];
foreach ($plugin_info as $name => $data) {
$uri = $data['src_uri'] ? $data['src_uri'] : $data['uri'];
if ($uri && stripos($uri, 'http') !== 0) {
$uri = 'http://' . $uri;
}
@ -78,48 +89,6 @@ function rcmail_plugins_list($attrib)
return $table->show();
}
function rcube_plugin_data($name, &$plugins = array())
{
// XPaths of plugin metadata elements
$metadata = array(
'name' => 'string(//rc:package/rc:name)',
'version' => 'string(//rc:package/rc:version/rc:release)',
'license' => 'string(//rc:package/rc:license)',
'license_uri' => 'string(//rc:package/rc:license/@uri)',
'srcuri' => 'string(//rc:package/rc:srcuri)',
'uri' => 'string(//rc:package/rc:uri)',
);
$package = INSTALL_PATH . "/plugins/$name/package.xml";
if (file_exists($package) && ($file = file_get_contents($package))) {
$doc = new DOMDocument();
$doc->loadXML($file);
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0");
$data = array();
foreach ($metadata as $key => $path) {
$data[$key] = $xpath->evaluate($path);
}
$plugins[$name] = $data;
// dependent required plugins (can be used, but not included in config)
$deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name');
$cnt = $deps->length;
for ($i=0; $i<$cnt; $i++) {
$dn = $deps->item($i)->nodeValue;
if (!array_key_exists($dn, $plugins)) {
rcube_plugin_data($dn, $plugins);
}
}
}
else {
unset($plugins[$name]);
}
}
$OUTPUT->set_pagetitle(rcube_label('about'));

Loading…
Cancel
Save