1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/limesurvey_ynh.git synced 2024-09-03 19:36:32 +02:00
limesurvey_ynh/sources/application/libraries/PluginManager/PluginManager.php

369 lines
13 KiB
PHP
Raw Normal View History

<?php
/**
* Factory for limesurvey plugin objects.
*/
class PluginManager {
/**
* Object containing any API that the plugins can use.
* @var object
*/
protected $api = null;
/**
* Array mapping guids to question object class names.
* @var type
*/
protected $guidToQuestion = array();
protected $plugins = array();
protected $pluginDirs = array();
protected $stores = array();
protected $subscriptions = array();
/**
* Creates the plugin manager.
*
* @param mixed $api The class name of the API class to load, or
* a reference to an already constructed reference.
*/
public function __construct($api)
{
if (is_object($api))
{
$this->api = $api;
}
else
{
$this->api = new $api();
}
$this->pluginDirs[] = 'webroot.plugins'; // User plugins
$this->pluginDirs[] = 'application.core.plugins'; // Core plugins
}
/**
* Return a list of installed plugins, but only if the files are still there
*
* This prevents errors when a plugin was installed but the files were removed
* from the server.
*
* @return array
*/
public function getInstalledPlugins()
{
$pluginModel = Plugin::model();
$records = $pluginModel->findAll();
$plugins = array();
foreach ($records as $record) {
// Only add plugins we can find
if ($this->loadPlugin($record->name) !== false) {
$plugins[$record->id] = $record;
}
}
return $plugins;
}
/**
* Returns the storage instance of type $storageClass.
* If needed initializes the storage object.
* @param string $storageClass
*/
public function getStore($storageClass)
{
if (!isset($this->stores[$storageClass]))
{
$this->stores[$storageClass] = new $storageClass();
}
return $this->stores[$storageClass];
}
/**
* This function returns an API object, exposing an API to each plugin.
* In the current case this is the LimeSurvey API.
* @return LimesurveyApi
*/
public function getAPI()
{
return $this->api;
}
/**
* Registers a plugin to be notified on some event.
* @param iPlugin $plugin Reference to the plugin.
* @param string $event Name of the event.
* @param string $function Optional function of the plugin to be called.
*/
public function subscribe(iPlugin $plugin, $event, $function = null)
{
if (!isset($this->subscriptions[$event]))
{
$this->subscriptions[$event] = array();
}
if (!$function)
{
$function = $event;
}
$subscription = array($plugin, $function);
// Subscribe only if not yet subscribed.
if (!in_array($subscription, $this->subscriptions[$event]))
{
$this->subscriptions[$event][] = $subscription;
}
}
/**
* Unsubscribes a plugin from an event.
* @param iPlugin $plugin Reference to the plugin being unsubscribed.
* @param string $event Name of the event. Use '*', to unsubscribe all events for the plugin.
* @param string $function Optional function of the plugin that was registered.
*/
public function unsubscribe(iPlugin $plugin, $event)
{
// Unsubscribe recursively.
if ($event == '*')
{
foreach ($this->subscriptions as $event)
{
$this->unsubscribe($plugin, $event);
}
}
elseif (isset($this->subscriptions[$event]))
{
foreach ($this->subscriptions[$event] as $index => $subscription)
{
if ($subscription[0] == $plugin)
{
unset($this->subscriptions[$event][$index]);
}
}
}
}
/**
* This function dispatches an event to all registered plugins.
* @param PluginEvent $event Object holding all event properties
* @param string|array $target Optional name of plugin to fire the event on
*
* @return PluginEvent
*/
public function dispatchEvent(PluginEvent $event, $target = array())
{
$eventName = $event->getEventName();
if (is_string($target)) {
$target = array($target);
}
if (isset($this->subscriptions[$eventName]))
{
foreach($this->subscriptions[$eventName] as $subscription)
{
if (!$event->isStopped()
&& (empty($target) || in_array(get_class($subscription[0]), $target)))
{
$subscription[0]->setEvent($event);
call_user_func($subscription);
}
}
}
return $event;
}
/**
* Scans the plugin directory for plugins.
* This function is not efficient so should only be used in the admin interface
* that specifically deals with enabling / disabling plugins.
*/
public function scanPlugins()
{
$result = array();
foreach ($this->pluginDirs as $pluginDir) {
$currentDir = Yii::getPathOfAlias($pluginDir);
if (is_dir($currentDir)) {
foreach (new DirectoryIterator($currentDir) as $fileInfo)
{
if (!$fileInfo->isDot() && $fileInfo->isDir())
{
// Check if the base plugin file exists.
// Directory name Example most contain file ExamplePlugin.php.
$pluginName = $fileInfo->getFilename();
$file = Yii::getPathOfAlias($pluginDir . ".$pluginName.{$pluginName}") . ".php";
if (file_exists($file))
{
$result[$pluginName] = $this->getPluginInfo($pluginName, $pluginDir);
}
}
}
}
}
return $result;
}
/**
* Gets the description of a plugin. The description is accessed via a
* static function inside the plugin file.
*
* @param string $pluginClass The classname of the plugin
*/
public function getPluginInfo($pluginClass, $pluginDir = null)
{
$result = array();
$class = "{$pluginClass}";
if (!class_exists($class, false)) {
$found = false;
if (!is_null($pluginDir)) {
$dirs = array($pluginDir);
} else {
$dirs = $this->pluginDirs;
}
foreach ($this->pluginDirs as $pluginDir) {
$file = Yii::getPathOfAlias($pluginDir . ".$pluginClass.{$pluginClass}") . ".php";
if (file_exists($file)) {
Yii::import($pluginDir . ".$pluginClass.*");
$found = true;
break;
}
}
if (!$found) {
return false;
}
}
$result['description'] = call_user_func(array($class, 'getDescription'));
$result['pluginName'] = call_user_func(array($class, 'getName'));
$result['pluginClass'] = $class;
return $result;
}
/**
* Returns the instantiated plugin
*
* @param string $pluginName
* @param int $id Identifier used for identifying a specific plugin instance.
* If ommitted will return the first instantiated plugin with the given name.
* @return iPlugin|null The plugin or null when missing
*/
public function loadPlugin($pluginName, $id = null)
{
// If the id is not set we search for the plugin.
if (!isset($id))
{
foreach ($this->plugins as $plugin)
{
if (get_class($plugin) == $pluginName)
{
return $plugin;
}
}
}
else
{
if ((!isset($this->plugins[$id]) || get_class($this->plugins[$id]) !== $pluginName))
{
if ($this->getPluginInfo($pluginName) !== false) {
$this->plugins[$id] = new $pluginName($this, $id);
} else {
$this->plugins[$id] = null;
}
}
return $this->plugins[$id];
}
}
/**
* Handles loading all active plugins
*
* Possible improvement would be to load them for a specific context.
* For instance 'survey' for runtime or 'admin' for backend. This needs
* some thinking before implementing.
*/
public function loadPlugins()
{
try {
$pluginModel = Plugin::model();
$records = $pluginModel->findAllByAttributes(array('active'=>1));
foreach ($records as $record) {
$this->loadPlugin($record->name, $record->id);
}
} catch (Exception $exc) {
// Something went wrong, maybe no database was present so we load no plugins
}
$this->dispatchEvent(new PluginEvent('afterPluginLoad', $this)); // Alow plugins to do stuff after all plugins are loaded
}
/**
* Get a list of question objects and load some information about them.
* This registers the question object classes with Yii.
*/
public function loadQuestionObjects($forceReload = false)
{
if (empty($this->guidToQuestion) || $forceReload)
{
$event = new PluginEvent('listQuestionPlugins');
$this->dispatchEvent($event);
foreach ($event->get('questionplugins', array()) as $pluginClass => $paths)
{
foreach ($paths as $path)
{
Yii::import("webroot.plugins.$pluginClass.$path");
$parts = explode('.', $path);
// Get the class name.
$className = array_pop($parts);
// Get the GUID for the question object.
$guid = forward_static_call(array($className, 'getGUID'));
// Save the GUID-class mapping.
$this->guidToQuestion[$guid] = array(
'class' => $className,
'guid' => $guid,
'plugin' => $pluginClass,
'name' => $className::$info['name']
);
}
}
}
return $this->guidToQuestion;
}
/**
* Construct a question object from a GUID.
* @param string $guid
* @param int $questionId,
* @param int $responseId
* @return iQuestion
*/
public function constructQuestionFromGUID($guid, $questionId = null, $responseId = null)
{
$this->loadQuestionObjects();
if (isset($this->guidToQuestion[$guid]))
{
$questionClass = $this->guidToQuestion[$guid]['class'];
$questionObject = new $questionClass($this->loadPlugin($this->guidToQuestion[$guid]['plugin']), $this->api, $questionId, $responseId);
return $questionObject;
}
}
}
?>