tao-test/app/generis/common/ext/class.Extension.php

479 lines
14 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2008-2010 (original work) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
* 2013-2014 (update and modification) Open Assessment Technologies SA;
*
*/
use oat\oatbox\extension\exception\ManifestException;
use common_ext_UpdaterNotFoundException as UpdaterNotFoundException;
use oat\oatbox\service\ServiceManagerAwareInterface;
use oat\oatbox\service\ServiceManagerAwareTrait;
use oat\oatbox\service\ServiceNotFoundException;
use oat\oatbox\service\ConfigurableService;
use oat\oatbox\config\ConfigurationService;
use oat\oatbox\extension\exception\ManifestNotFoundException;
use oat\oatbox\extension\Manifest;
/**
* Short description of class common_ext_Extension
*
* @access public
* @author lionel.lecaque@tudor.lu
* @package generis
* @see @license GNU General Public (GPL) Version 2 http://www.opensource.org/licenses/gpl-2.0.php
*/
class common_ext_Extension implements ServiceManagerAwareInterface
{
use ServiceManagerAwareTrait;
/**
* Filename of the manifest
*
* @var string
*/
const MANIFEST_NAME = 'manifest.php';
/**
* List of extension dependencies
*
* @var array
*/
private $dependencies = [];
/**
* Short description of attribute id
*
* @access private
* @var string
*/
private $id = '';
/**
* The manifest of the extension
*
* @var Manifest
*/
protected $manifest;
/**
* Whenever or not an extension has already been loaded
*
* @access private
* @var boolean
*/
protected $loaded = false;
/**
* Should not be called directly, please use ExtensionsManager
*
* @access public
* @author Joel Bout, <joel@taotesting.com>
*
* @param string $id
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* returns the id of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* returns all constants of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return array
*/
public function getConstants()
{
return (array) $this->getManifest()->getConstants();
}
/**
* checks if a configuration value exists
*
* @param string $key
* @return boolean
*/
public function hasConfig($key)
{
return $this->getServiceLocator()->has($this->getId() . '/' . $key);
}
/**
* sets a configuration value
*
* @param string $key
* @param string $value
* @return bool always returns true for backward compatibility
* @throws common_exception_Error On error
*/
public function setConfig($key, $value)
{
if (! is_object($value) || ! $value instanceof ConfigurableService) {
$value = new ConfigurationService([ConfigurationService::OPTION_CONFIG => $value]);
}
$value->setHeader($this->getConfigHeader($key));
$this->registerService($this->getId() . '/' . $key, $value);
return true;
}
/**
* retrieves a configuration value
* returns false if not found
*
* @param string $key
* @return mixed
*/
public function getConfig($key)
{
if (! $this->getServiceLocator()->has($this->getId() . '/' . $key)) {
return false;
}
try {
$config = $this->getServiceLocator()->get($this->getId() . '/' . $key);
if ($config instanceof ConfigurationService) {
$config = $config->getConfig();
}
} catch (ServiceNotFoundException $e) {
$config = false;
}
return $config;
}
/**
* removes a configuration entry
*
* @param string $key
* @return mixed
*/
public function unsetConfig($key)
{
return $this->getServiceManager()->unregister($this->getId() . '/' . $key);
}
/**
* returns the version of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return string
*/
public function getVersion()
{
return (string) $this->getManifest()->getVersion();
}
/**
* returns the author of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return string
*/
public function getAuthor()
{
return (string) $this->getManifest()->getAuthor();
}
/**
* returns the name of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return string
*/
public function getName()
{
return (string) $this->getManifest()->getName();
}
/**
* returns the base dir of the extension
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return string
* @throws common_ext_ExtensionException
*/
public function getDir()
{
if (! defined('EXTENSION_PATH')) {
throw new common_ext_ExtensionException('System constants are not yet defined');
}
return EXTENSION_PATH . $this->getId() . DIRECTORY_SEPARATOR;
}
/**
* @param $key
*
* @return bool
*/
public function hasConstant($key)
{
$constants = $this->getConstants();
return isset($constants[$key]);
}
/**
* Retrieves a constant from the manifest.php file of the extension.
*
* @author Joel Bout, <joel@taotesting.com>
* @param string $key
* @return mixed
* @throws common_exception_Error If the constant cannot be found.
*/
public function getConstant($key)
{
$returnValue = null;
$constants = $this->getConstants();
if (isset($constants[$key])) {
$returnValue = $constants[$key];
} elseif (defined($key)) {
common_logger::w('constant outside of extension called: ' . $key);
$returnValue = constant($key);
} else {
throw new common_exception_Error('Unknown constant \'' . $key . '\' for extension ' . $this->id);
}
return $returnValue;
}
/**
* get all modules of the extension
* by searching the actions directory, not the ontology
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @return array
*/
public function getAllModules()
{
$returnValue = [];
// routes
$namespaces = [];
foreach ($this->getManifest()->getRoutes() as $mapedPath => $ns) {
$namespaces[] = trim($ns, '\\');
}
if (!empty($namespaces)) {
common_Logger::d('Namespace not empty for extension ' . $this->getId());
$recDir = new RecursiveDirectoryIterator($this->getDir());
$recIt = new RecursiveIteratorIterator($recDir);
$regexIt = new RegexIterator($recIt, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
foreach ($regexIt as $entry) {
$info = helpers_PhpTools::getClassInfo($entry[0]);
if (!empty($info['ns'])) {
$ns = trim($info['ns'], '\\');
if (!empty($info['ns']) && in_array($ns, $namespaces)) {
$returnValue[$info['class']] = $ns . '\\' . $info['class'];
}
}
}
}
// legacy
if ($this->hasConstant('DIR_ACTIONS') && file_exists($this->getConstant('DIR_ACTIONS'))) {
$dir = new DirectoryIterator($this->getConstant('DIR_ACTIONS'));
foreach ($dir as $fileinfo) {
if (preg_match('/^class\.[^.]*\.php$/', $fileinfo->getFilename())) {
$module = substr($fileinfo->getFilename(), 6, -4);
$returnValue[$module] = $this->getId() . '_actions_' . $module;
}
}
}
// validate the classes
foreach (array_keys($returnValue) as $key) {
$class = $returnValue[$key];
if (!class_exists($class)) {
common_Logger::w($class . ' not found');
unset($returnValue[$key]);
} elseif (!is_subclass_of($class, 'Module')) {
common_Logger::w($class . ' does not inherit Module');
unset($returnValue[$key]);
}
}
return (array) $returnValue;
}
/**
* returns a module by ID
*
* @access public
* @author firstname and lastname of author, <author@example.org>
* @param string $id
* @return Module
*/
public function getModule($id)
{
$returnValue = null;
$className = $this->getId() . '_actions_' . $id;
if (class_exists($className)) {
$returnValue = new $className();
} else {
common_Logger::e('could not load ' . $className);
}
return $returnValue;
}
/**
* Returns the extension the current extension depends on recursively
*
* @access public
* @return array Where key is name of extension and value is required version.
*/
public function getDependencies()
{
if (empty($this->dependencies)) {
foreach ($this->getManifest()->getDependencies() as $id => $version) {
$this->dependencies[$id] = $version;
$dependence = $this->getExtensionManager()->getExtensionById($id);
$this->dependencies = array_merge($this->dependencies, $dependence->getDependencies());
}
}
return $this->dependencies;
}
/**
* Returns the manifest of the extension
*
* @return Manifest
* @throws ManifestNotFoundException
*/
public function getManifest()
{
if (! $this->manifest) {
$manifestFile = $this->getDir() . self::MANIFEST_NAME;
if (is_file($manifestFile) && is_readable($manifestFile)) {
$this->manifest = new Manifest($manifestFile);
} else {
throw new ManifestNotFoundException("Extension Manifest not found for extension '" . $this->id . "'.", $this->id);
}
}
$this->manifest->setServiceLocator($this->getServiceLocator());
return $this->manifest;
}
public function getPhpNamespace()
{
return $this->getManifest()->getPhpNamespace();
}
/**
* Whenever or not the extension and it's constants have been loaded
* @return boolean
*/
public function isLoaded()
{
return $this->loaded;
}
/**
* Loads the extension if it hasn't been loaded (using load), yet
* All the dependent extensions will be loaded too.
*/
public function load()
{
if (!$this->isLoaded()) {
try {
$dependencies = $this->getManifest()->getDependencies();
foreach ($dependencies as $extId => $extVersion) {
$this->getExtensionManager()->getExtensionById($extId);
}
} catch (ManifestNotFoundException $e) {
throw new common_ext_MissingExtensionException($e->getExtensionId() . ' not found but required for ' . $this->getId(), $e->getExtensionId());
}
$loader = new common_ext_ExtensionLoader($this);
$loader->load();
//load all dependent extensions
$this->loaded = true;
}
}
/**
* Get the ExtensionManager service
*
* @return common_ext_ExtensionsManager|mixed
*/
protected function getExtensionManager()
{
if ($this->getServiceLocator()->has(common_ext_ExtensionsManager::SERVICE_ID)) {
$service = $this->getServiceLocator()->get(common_ext_ExtensionsManager::SERVICE_ID);
} else {
$service = new common_ext_ExtensionsManager();
$this->getServiceLocator()->propagate($service);
}
return $service;
}
/**
* Get the documentation header for extension config located at key path
*
* @param $key
* @return null|string
*/
public function getConfigHeader($key)
{
$path = $this->getDir() . 'config/header/' . $key . '.conf.php';
if (is_readable($path) && is_file($path)) {
return file_get_contents($path);
}
return null;
}
/**
* @throws ManifestException
* @throws ManifestNotFoundException
* @throws common_ext_UpdaterNotFoundException
*/
public function getUpdater(): common_ext_ExtensionUpdater
{
$updaterClass = $this->getManifest()->getUpdateHandler();
if ($updaterClass === null) {
throw new UpdaterNotFoundException('No Updater found for ' . $this->getName());
} elseif (!class_exists($updaterClass)) {
throw new ManifestException('Updater ' . $updaterClass . ' not found');
}
/** @var common_ext_ExtensionUpdater $updater */
$updater = new $updaterClass($this);
$updater->setServiceLocator($this->getServiceLocator());
return $updater;
}
}