first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/BlockPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class BlockPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for block plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
use PKP\core\PKPRequest;
|
||||
|
||||
abstract class BlockPlugin extends LazyLoadPlugin
|
||||
{
|
||||
//
|
||||
// Override public methods from Plugin
|
||||
//
|
||||
/**
|
||||
* Determine whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @param int $contextId Context ID (journal/press)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled($contextId = null)
|
||||
{
|
||||
return $this->getSetting(is_null($contextId) ? $this->getCurrentContextId() : $contextId, 'enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @param bool $enabled
|
||||
* @param int $contextId Context ID (journal/press)
|
||||
*/
|
||||
public function setEnabled($enabled, $contextId = null)
|
||||
{
|
||||
$this->updateSetting(is_null($contextId) ? $this->getCurrentContextId() : $contextId, 'enabled', $enabled, 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the template block. (Default behavior may
|
||||
* be overridden through some combination of this function and the
|
||||
* getContents function.)
|
||||
* Returning null from this function results in an empty display.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBlockTemplateFilename()
|
||||
{
|
||||
return 'block.tpl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML contents for this block.
|
||||
*
|
||||
* @param object $templateMgr
|
||||
* @param PKPRequest $request (Optional for legacy plugins)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContents($templateMgr, $request = null)
|
||||
{
|
||||
$blockTemplateFilename = $this->getBlockTemplateFilename();
|
||||
if ($blockTemplateFilename === null) {
|
||||
return '';
|
||||
}
|
||||
return $templateMgr->fetch($this->getTemplateResource($blockTemplateFilename));
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\BlockPlugin', '\BlockPlugin');
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugin/GalleryPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class GalleryPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Class describing a plugin in the Plugin Gallery.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use PKP\core\Core;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\site\Version;
|
||||
use PKP\site\VersionDAO;
|
||||
|
||||
define('PLUGIN_GALLERY_STATE_AVAILABLE', 0);
|
||||
define('PLUGIN_GALLERY_STATE_INCOMPATIBLE', 0);
|
||||
define('PLUGIN_GALLERY_STATE_UPGRADABLE', 1);
|
||||
define('PLUGIN_GALLERY_STATE_CURRENT', 2);
|
||||
define('PLUGIN_GALLERY_STATE_NEWER', 3);
|
||||
|
||||
class GalleryPlugin extends \PKP\core\DataObject
|
||||
{
|
||||
/**
|
||||
* Get the localized name of the plugin
|
||||
*
|
||||
* @param string $preferredLocale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedName($preferredLocale = null)
|
||||
{
|
||||
return $this->getLocalizedData('name', $preferredLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the plugin
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function setName($name, $locale = null)
|
||||
{
|
||||
$this->setData('name', $name, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the plugin
|
||||
*
|
||||
* @param string $locale optional
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName($locale = null)
|
||||
{
|
||||
return $this->getData('name', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the homepage for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHomepage()
|
||||
{
|
||||
return $this->getData('homepage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the homepage for this plugin
|
||||
*
|
||||
* @param string $homepage
|
||||
*/
|
||||
public function setHomepage($homepage)
|
||||
{
|
||||
$this->setData('homepage', $homepage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product (symbolic name) for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProduct()
|
||||
{
|
||||
return $this->getData('product');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the product (symbolic name) for this plugin
|
||||
*
|
||||
* @param string $product
|
||||
*/
|
||||
public function setProduct($product)
|
||||
{
|
||||
$this->setData('product', $product);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->getData('category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the category for this plugin
|
||||
*
|
||||
* @param string $category
|
||||
*/
|
||||
public function setCategory($category)
|
||||
{
|
||||
$this->setData('category', $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the newest compatible version of this plugin
|
||||
*
|
||||
* @param bool $pad True iff returned version numbers should be
|
||||
* padded to 4 terms, e.g. 1.0.0.0 instead of just 1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($pad = false)
|
||||
{
|
||||
$version = $this->getData('version');
|
||||
if ($pad) {
|
||||
// Ensure there are 4 terms (3 separators)
|
||||
$separators = substr_count($version, '.');
|
||||
if ($separators < 3) {
|
||||
$version .= str_repeat('.0', 3 - $separators);
|
||||
}
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version for this plugin
|
||||
*
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->setData('version', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the release date of this plugin
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return $this->getData('date');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the release date for this plugin
|
||||
*
|
||||
* @param int $date
|
||||
*/
|
||||
public function setDate($date)
|
||||
{
|
||||
$this->setData('date', $date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact name for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContactName()
|
||||
{
|
||||
return $this->getData('contactName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contact name for this plugin
|
||||
*
|
||||
* @param string $contactName
|
||||
*/
|
||||
public function setContactName($contactName)
|
||||
{
|
||||
$this->setData('contactName', $contactName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact institution name for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContactInstitutionName()
|
||||
{
|
||||
return $this->getData('contactInstitutionName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contact institution name for this plugin
|
||||
*
|
||||
* @param string $contactInstitutionName
|
||||
*/
|
||||
public function setContactInstitutionName($contactInstitutionName)
|
||||
{
|
||||
$this->setData('contactInstitutionName', $contactInstitutionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact email for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContactEmail()
|
||||
{
|
||||
return $this->getData('contactEmail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contact email for this plugin
|
||||
*
|
||||
* @param string $contactEmail
|
||||
*/
|
||||
public function setContactEmail($contactEmail)
|
||||
{
|
||||
$this->setData('contactEmail', $contactEmail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin summary.
|
||||
*
|
||||
* @param string $locale optional
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSummary($locale = null)
|
||||
{
|
||||
return $this->getData('summary', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin summary.
|
||||
*
|
||||
* @param string $summary
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function setSummary($summary, $locale = null)
|
||||
{
|
||||
$this->setData('summary', $summary, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin description.
|
||||
*
|
||||
* @param string $locale optional
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription($locale = null)
|
||||
{
|
||||
return $this->getData('description', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin description.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function setDescription($description, $locale = null)
|
||||
{
|
||||
$this->setData('description', $description, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin installation instructions.
|
||||
*
|
||||
* @param string $locale optional
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallationInstructions($locale = null)
|
||||
{
|
||||
return $this->getData('installation', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin installation instructions.
|
||||
*
|
||||
* @param string $installation
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function setInstallationInstructions($installation, $locale = null)
|
||||
{
|
||||
$this->setData('installation', $installation, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get release description.
|
||||
*
|
||||
* @param string $locale optional
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReleaseDescription($locale = null)
|
||||
{
|
||||
return $this->getData('releaseDescription', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin release description.
|
||||
*
|
||||
* @param string $releaseDescription
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function setReleaseDescription($releaseDescription, $locale = null)
|
||||
{
|
||||
$this->setData('releaseDescription', $releaseDescription, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get release MD5 checksum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReleaseMD5()
|
||||
{
|
||||
return $this->getData('releaseMD5');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin release MD5.
|
||||
*
|
||||
* @param string $releaseMD5
|
||||
*/
|
||||
public function setReleaseMD5($releaseMD5)
|
||||
{
|
||||
$this->setData('releaseMD5', $releaseMD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the certifications for this plugin release
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReleaseCertifications()
|
||||
{
|
||||
return $this->getData('releaseCertifications');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the certifications for this plugin release
|
||||
*
|
||||
* @param array $certifications
|
||||
*/
|
||||
public function setReleaseCertifications($certifications)
|
||||
{
|
||||
$this->setData('releaseCertifications', $certifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package URL for this plugin release
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReleasePackage()
|
||||
{
|
||||
return $this->getData('releasePackage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the package URL for this plugin release
|
||||
*/
|
||||
public function setReleasePackage($releasePackage)
|
||||
{
|
||||
$this->setData('releasePackage', $releasePackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized summary of the plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedSummary()
|
||||
{
|
||||
return $this->getLocalizedData('summary');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized installation instructions of the plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedInstallationInstructions()
|
||||
{
|
||||
return $this->getLocalizedData('installation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized description of the plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedDescription()
|
||||
{
|
||||
return $this->getLocalizedData('description');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized release description of the plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedReleaseDescription()
|
||||
{
|
||||
return $this->getLocalizedData('releaseDescription');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the version of this plugin that is currently installed,
|
||||
* if any
|
||||
*
|
||||
* @return Version|null
|
||||
*/
|
||||
public function getInstalledVersion()
|
||||
{
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
|
||||
return $versionDao->getCurrentVersion('plugins.' . $this->getCategory(), $this->getProduct());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of the gallery plugin with respect to this
|
||||
* installation.
|
||||
*
|
||||
* @return int PLUGIN_GALLERY_STATE_...
|
||||
*/
|
||||
public function getCurrentStatus()
|
||||
{
|
||||
$installedVersion = $this->getInstalledVersion();
|
||||
if ($this->getVersion() === null) {
|
||||
return PLUGIN_GALLERY_STATE_INCOMPATIBLE;
|
||||
}
|
||||
if (!$installedVersion) {
|
||||
return PLUGIN_GALLERY_STATE_AVAILABLE;
|
||||
}
|
||||
if ($installedVersion->compare($this->getVersion(true)) > 0) {
|
||||
return PLUGIN_GALLERY_STATE_NEWER;
|
||||
}
|
||||
if ($installedVersion->compare($this->getVersion(true)) < 0) {
|
||||
return PLUGIN_GALLERY_STATE_UPGRADABLE;
|
||||
}
|
||||
|
||||
$targetPath = Core::getBaseDir() . '/plugins/' . $this->getCategory() . '/' . $this->getProduct();
|
||||
if (!is_dir($targetPath)) {
|
||||
return PLUGIN_GALLERY_STATE_UPGRADABLE;
|
||||
}
|
||||
|
||||
return PLUGIN_GALLERY_STATE_CURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\GalleryPlugin', '\GalleryPlugin');
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/GatewayPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class GatewayPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for gateway plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\core\PKPRequest;
|
||||
|
||||
abstract class GatewayPlugin extends Plugin
|
||||
{
|
||||
/**
|
||||
* Handle fetch requests for this plugin.
|
||||
*
|
||||
* @param array $args
|
||||
* @param object $request
|
||||
*/
|
||||
abstract public function fetch($args, $request);
|
||||
|
||||
/**
|
||||
* Determine whether the plugin can be enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCanEnable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the plugin can be disabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCanDisable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled()
|
||||
{
|
||||
return $this->getSetting($this->getCurrentContextId(), 'enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled($enabled)
|
||||
{
|
||||
$this->updateSetting($this->getCurrentContextId(), 'enabled', $enabled, 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current context ID or the site-wide context ID (0) if no context
|
||||
* can be found.
|
||||
*/
|
||||
public function getCurrentContextId()
|
||||
{
|
||||
$context = Application::get()->getRequest()->getContext();
|
||||
return is_null($context) ? 0 : $context->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get policies to the authorization process
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
*
|
||||
* @return array Set of authorization policies
|
||||
*/
|
||||
|
||||
public function getPolicies($request)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\GatewayPlugin', '\GatewayPlugin');
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/GenericPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class GenericPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for generic plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
abstract class GenericPlugin extends LazyLoadPlugin
|
||||
{
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\GenericPlugin', '\GenericPlugin');
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/Hook.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Hook
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Class implementing a registry of hooks for extending core functionality
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use PKP\core\Registry;
|
||||
|
||||
class Hook
|
||||
{
|
||||
public const SEQUENCE_CORE = 0;
|
||||
public const SEQUENCE_NORMAL = 256;
|
||||
public const SEQUENCE_LATE = 512;
|
||||
public const SEQUENCE_LAST = 768;
|
||||
|
||||
public const CONTINUE = false;
|
||||
public const ABORT = true;
|
||||
|
||||
/**
|
||||
* Get the current set of hook registrations.
|
||||
*
|
||||
* @param string $hookName Name of hook to optionally return
|
||||
*
|
||||
* @return mixed Array of all hooks or just those attached to $hookName, or
|
||||
* null if nothing has been attached to $hookName
|
||||
*/
|
||||
public static function &getHooks(?string $hookName = null): ?array
|
||||
{
|
||||
$hooks = & Registry::get('hooks', true, []);
|
||||
if ($hookName === null) {
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
if (isset($hooks[$hookName])) {
|
||||
return $hooks[$hookName];
|
||||
}
|
||||
|
||||
$returner = null;
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear hooks registered against the given name.
|
||||
*/
|
||||
public static function clear(string $hookName): void
|
||||
{
|
||||
$hooks = & static::getHooks();
|
||||
unset($hooks[$hookName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a hook against the given hook name.
|
||||
*
|
||||
* @param string $hookName Name of hook to register against
|
||||
* @param object|array $callback Callback pseudo-type
|
||||
* @param int $hookSequence Optional hook sequence specifier SEQUENCE_...
|
||||
*/
|
||||
public static function add(string $hookName, callable $callback, int $hookSequence = self::SEQUENCE_NORMAL): void
|
||||
{
|
||||
$hooks = & static::getHooks();
|
||||
$hooks[$hookName][$hookSequence][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of Hook::add
|
||||
*
|
||||
* @deprecated 3.4
|
||||
*/
|
||||
public static function register(string $hookName, callable $callback, int $hookSequence = self::SEQUENCE_NORMAL): void
|
||||
{
|
||||
self::add($hookName, $callback, $hookSequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call each callback registered against $hookName in sequence.
|
||||
* The first callback that returns ABORT will interrupt processing and
|
||||
* this function will return ABORT; otherwise, all callbacks will be
|
||||
* called in sequence and the return value of this call will be
|
||||
* CONTINUE.
|
||||
*
|
||||
* The signature of the callback function should be:
|
||||
* function callback($hookName, $args) : bool;
|
||||
* ...and $args will receive the array provided here.
|
||||
*
|
||||
* This function should be considered deprecated in favour of
|
||||
* Hook::call.
|
||||
*/
|
||||
public static function call(string $hookName, array $args = []): mixed
|
||||
{
|
||||
// Called only by Unit Test
|
||||
// This behaviour is DEPRECATED and not replicated in the preferred
|
||||
// Hook::call function.
|
||||
if (self::rememberCalledHooks(true)) {
|
||||
// Remember the called hooks for testing.
|
||||
$calledHooks = & static::getCalledHooks();
|
||||
$calledHooks[] = [
|
||||
$hookName, $args
|
||||
];
|
||||
}
|
||||
|
||||
return self::run($hookName, [$args]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call each callback registered against $hookName in sequence.
|
||||
* The first callback that returns ABORT will interrupt processing and
|
||||
* this function will return ABORT; otherwise, all callbacks will be
|
||||
* called in sequence and the return value of this call will be
|
||||
* CONTINUE.
|
||||
*
|
||||
* The signature of a callback function should be:
|
||||
* function callback($hookName, ...) : bool;
|
||||
* where ... corresponds to the parameters named/listed in the $args
|
||||
* parameter to Hook::call. These may be named if desired,
|
||||
* and may include references.
|
||||
*/
|
||||
public static function run(string $hookName, $args): bool
|
||||
{
|
||||
$hooks = & static::getHooks();
|
||||
if (!isset($hooks[$hookName])) {
|
||||
return self::CONTINUE;
|
||||
}
|
||||
|
||||
ksort($hooks[$hookName], SORT_NUMERIC);
|
||||
foreach ($hooks[$hookName] as $priority => $hookList) {
|
||||
foreach ($hookList as $callback) {
|
||||
if (call_user_func_array($callback, array_merge([$hookName], $args)) === self::ABORT) {
|
||||
return self::ABORT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Methods required for testing only.
|
||||
//
|
||||
/**
|
||||
* Set/query the flag that triggers storing of
|
||||
* called hooks.
|
||||
*
|
||||
* @param bool $askOnly When set to true, the flag will not
|
||||
* be changed but only returned.
|
||||
* @param bool $updateTo When $askOnly is set to 'true' then
|
||||
* this parameter defines the value of the flag.
|
||||
*
|
||||
* @return bool The current value of the flag.
|
||||
*/
|
||||
public static function rememberCalledHooks(bool $askOnly = false, bool $updateTo = true): bool
|
||||
{
|
||||
static $rememberCalledHooks = false;
|
||||
if (!$askOnly) {
|
||||
$rememberCalledHooks = $updateTo;
|
||||
}
|
||||
return $rememberCalledHooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch off the function to store hooks and delete all stored hooks.
|
||||
* Always call this after using otherwise we get a severe memory.
|
||||
*
|
||||
* @param bool $leaveAlive Set this to true if you only want to
|
||||
* delete hooks stored so far but if you want to record future
|
||||
* hook calls, too.
|
||||
*/
|
||||
public static function resetCalledHooks(bool $leaveAlive = false): void
|
||||
{
|
||||
if (!$leaveAlive) {
|
||||
static::rememberCalledHooks(false, false);
|
||||
}
|
||||
$calledHooks = & static::getCalledHooks();
|
||||
$calledHooks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the stored hooks.
|
||||
*/
|
||||
public static function &getCalledHooks(): array
|
||||
{
|
||||
static $calledHooks = [];
|
||||
return $calledHooks;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\Hook', '\HookRegistry');
|
||||
foreach (['SEQUENCE_CORE', 'SEQUENCE_NORMAL', 'SEQUENCE_LATE', 'SEQUENCE_LAST'] as $constantName) {
|
||||
define('HOOK_' . $constantName, constant('\HookRegistry::' . $constantName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/IPKPDoiRegistrationAgency.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class IPKPDoiRegistrationAgency
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Interface that registration agency plugins must implement to support DOI registrations.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use PKP\context\Context;
|
||||
use PKP\doi\RegistrationAgencySettings;
|
||||
|
||||
interface IPKPDoiRegistrationAgency
|
||||
{
|
||||
/**
|
||||
* Includes plugin in list of configurable registration agencies for DOI depositing functionality
|
||||
*
|
||||
* @param string $hookName DoiSettingsForm::setEnabledRegistrationAgencies
|
||||
* @param array $args [
|
||||
*
|
||||
* @option $enabledRegistrationAgencies array
|
||||
* ]
|
||||
*/
|
||||
public function addAsRegistrationAgencyOption(string $hookName, array $args);
|
||||
|
||||
/**
|
||||
* Checks if plugin meets registration agency-specific requirements for being active and handling deposits
|
||||
*
|
||||
*/
|
||||
public function isPluginConfigured(Context $context): bool;
|
||||
|
||||
/**
|
||||
* Get configured registration agency display name for use in DOI management pages
|
||||
*
|
||||
*/
|
||||
public function getRegistrationAgencyName(): string;
|
||||
|
||||
/**
|
||||
* Get key for retrieving error message if one exists on DOI object
|
||||
*
|
||||
*/
|
||||
public function getErrorMessageKey(): ?string;
|
||||
|
||||
/**
|
||||
* Get key for retrieving registered message if one exists on DOI object
|
||||
*
|
||||
*/
|
||||
public function getRegisteredMessageKey(): ?string;
|
||||
|
||||
/**
|
||||
* Get settings management object. Handles saving and validating of form fields.
|
||||
*
|
||||
*/
|
||||
public function getSettingsObject(): RegistrationAgencySettings;
|
||||
|
||||
/**
|
||||
* Get an array of which types of pub objects are allowed to have DOIs assigned.
|
||||
* Should be composed of Repo::doi()::TYPE_* constants.
|
||||
* This will only dictate which pub object types are enabled and will not delete already assigned DOIs.
|
||||
*/
|
||||
public function getAllowedDoiTypes(): array;
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/ImportExportPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ImportExportPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for import/export plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\template\TemplateManager;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use PKP\config\Config;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\RedirectAction;
|
||||
use PKP\plugins\importexport\native\PKPNativeImportExportDeployment;
|
||||
use PKP\plugins\importexport\PKPImportExportDeployment;
|
||||
use PKP\session\SessionManager;
|
||||
|
||||
abstract class ImportExportPlugin extends Plugin
|
||||
{
|
||||
/** @var PKPImportExportDeployment The deployment that processes import/export operations */
|
||||
public $_childDeployment = null;
|
||||
|
||||
/** @var \APP\core\Request Request made available for plugin URL generation */
|
||||
public $_request;
|
||||
|
||||
protected const EXPORT_FILE_DATE_PART_FORMAT = 'Ymd-His';
|
||||
|
||||
/**
|
||||
* Execute import/export tasks using the command-line interface.
|
||||
*
|
||||
* @param string $scriptName The name of the command-line script (displayed as usage info)
|
||||
* @param array $args Parameters to the plugin
|
||||
*/
|
||||
abstract public function executeCLI($scriptName, &$args);
|
||||
|
||||
/**
|
||||
* Display the command-line usage information
|
||||
*
|
||||
* @param string $scriptName
|
||||
*/
|
||||
abstract public function usage($scriptName);
|
||||
|
||||
/**
|
||||
* Whether this plugin provides CLI import/export
|
||||
*/
|
||||
public function supportsCLI(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getActions()
|
||||
*/
|
||||
public function getActions($request, $actionArgs)
|
||||
{
|
||||
$dispatcher = $request->getDispatcher();
|
||||
return array_merge(
|
||||
[
|
||||
new LinkAction(
|
||||
'settings',
|
||||
new RedirectAction($dispatcher->url(
|
||||
$request,
|
||||
PKPApplication::ROUTE_PAGE,
|
||||
null,
|
||||
'management',
|
||||
'importexport',
|
||||
['plugin', $this->getName()]
|
||||
)),
|
||||
__('manager.importExport'),
|
||||
null
|
||||
),
|
||||
],
|
||||
parent::getActions($request, $actionArgs)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the import/export plugin.
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*/
|
||||
public function display($args, $request)
|
||||
{
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->registerPlugin('function', 'plugin_url', [$this, 'pluginUrl']);
|
||||
$this->_request = $request; // Store this for use by the pluginUrl function
|
||||
$templateMgr->assign([
|
||||
'breadcrumbs' => [
|
||||
[
|
||||
'id' => 'tools',
|
||||
'name' => __('navigation.tools'),
|
||||
'url' => $request->getRouter()->url($request, null, 'management', 'tools'),
|
||||
],
|
||||
[
|
||||
'id' => $this->getPluginPath(),
|
||||
'name' => $this->getDisplayName()
|
||||
],
|
||||
],
|
||||
'pageTitle' => $this->getDisplayName(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL into the plugin.
|
||||
*
|
||||
* @see calling conventions at http://www.smarty.net/docsv2/en/api.register.function.tpl
|
||||
*
|
||||
* @param array $params
|
||||
* @param \Smarty $smarty
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pluginUrl($params, $smarty)
|
||||
{
|
||||
$dispatcher = $this->_request->getDispatcher();
|
||||
return $dispatcher->url($this->_request, PKPApplication::ROUTE_PAGE, null, 'management', 'importexport', array_merge(['plugin', $this->getName(), $params['path'] ?? []]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a relative path to the xml document
|
||||
* that describes public identifiers to be imported.
|
||||
*
|
||||
* @param string $url path to the xml file
|
||||
*/
|
||||
public function isRelativePath($url)
|
||||
{
|
||||
// FIXME This is not very comprehensive, but will work for now.
|
||||
if ($this->isAllowedMethod($url)) {
|
||||
return false;
|
||||
}
|
||||
if ($url[0] == '/') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the specified URL describes an allowed protocol.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedMethod($url)
|
||||
{
|
||||
$allowedPrefixes = [
|
||||
'http://',
|
||||
'ftp://',
|
||||
'https://',
|
||||
'ftps://'
|
||||
];
|
||||
foreach ($allowedPrefixes as $prefix) {
|
||||
if (substr($url, 0, strlen($prefix)) === $prefix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin ID used as plugin settings prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginSettingsPrefix()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin export directory.
|
||||
*
|
||||
* @return string The export directory path.
|
||||
*/
|
||||
public function getExportPath()
|
||||
{
|
||||
return Config::getVar('files', 'files_dir') . '/temp/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the whole export file name.
|
||||
*
|
||||
* @param string $basePath Base path for temporary file storage
|
||||
* @param string $objectsFileNamePart Part different for each object type.
|
||||
* @param string $extension
|
||||
* @param ?DateTime $dateFilenamePart
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExportFileName($basePath, $objectsFileNamePart, Context $context, $extension = '.xml', ?DateTime $dateFilenamePart = null)
|
||||
{
|
||||
$dateFilenamePartString = date(self::EXPORT_FILE_DATE_PART_FORMAT);
|
||||
|
||||
if (isset($dateFilenamePart)) {
|
||||
$dateFilenamePartString = $dateFilenamePart->format(self::EXPORT_FILE_DATE_PART_FORMAT);
|
||||
}
|
||||
|
||||
return $basePath . $this->getPluginSettingsPrefix() . '-' . $dateFilenamePartString . '-' . $objectsFileNamePart . '-' . $context->getId() . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display XML validation errors.
|
||||
*
|
||||
* @param array $errors
|
||||
* @param string $xml
|
||||
*/
|
||||
public function displayXMLValidationErrors($errors, $xml)
|
||||
{
|
||||
if (SessionManager::isDisabled()) {
|
||||
echo __('plugins.importexport.common.validationErrors') . "\n";
|
||||
foreach ($errors as $error) {
|
||||
echo trim($error->message) . "\n";
|
||||
}
|
||||
libxml_clear_errors();
|
||||
echo __('plugins.importexport.common.invalidXML') . "\n";
|
||||
echo $xml . "\n";
|
||||
} else {
|
||||
header('Content-type: text/html; charset=utf-8');
|
||||
echo '<html><body>';
|
||||
echo '<h2>' . __('plugins.importexport.common.validationErrors') . '</h2>';
|
||||
foreach ($errors as $error) {
|
||||
echo '<p>' . trim($error->message) . '</p>';
|
||||
}
|
||||
libxml_clear_errors();
|
||||
echo '<h3>' . __('plugins.importexport.common.invalidXML') . '</h3>';
|
||||
echo '<p><pre>' . htmlspecialchars($xml) . '</pre></p>';
|
||||
echo '</body></html>';
|
||||
}
|
||||
throw new Exception(__('plugins.importexport.common.error.validation'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the deployment that processes import/export operations
|
||||
*/
|
||||
public function setDeployment($deployment)
|
||||
{
|
||||
$this->_childDeployment = $deployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deployment that processes import/export operations
|
||||
*
|
||||
* @return PKPImportExportDeployment
|
||||
*/
|
||||
public function getDeployment()
|
||||
{
|
||||
return $this->_childDeployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submissions and proceed to the export
|
||||
*
|
||||
* @param array $submissionIds Array of submissions to export
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
* @param array $opts
|
||||
*/
|
||||
public function getExportSubmissionsDeployment($submissionIds, $deployment, $opts = [])
|
||||
{
|
||||
$filter = $this->getExportFilter('exportSubmissions');
|
||||
|
||||
$submissions = [];
|
||||
foreach ($submissionIds as $submissionId) {
|
||||
$submission = Repo::submission()->get($submissionId);
|
||||
|
||||
if ($submission && $submission->getData('contextId') == $deployment->getContext()->getId()) {
|
||||
$submissions[] = $submission;
|
||||
}
|
||||
}
|
||||
|
||||
$deployment->export($filter, $submissions, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the export result as an XML
|
||||
*
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function exportResultXML($deployment)
|
||||
{
|
||||
$result = $deployment->processResult;
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
|
||||
$xml = null;
|
||||
if (!$foundErrors && $result) {
|
||||
$xml = $result->saveXml();
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets template result for the export process
|
||||
*
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
* @param \PKP\template\PKPTemplateManager $templateMgr
|
||||
* @param string $exportFileName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExportTemplateResult($deployment, $templateMgr, $exportFileName)
|
||||
{
|
||||
$result = $deployment->processResult;
|
||||
$problems = $deployment->getWarningsAndErrors();
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
|
||||
if (!$foundErrors) {
|
||||
$exportXml = $result->saveXml();
|
||||
|
||||
if ($exportXml) {
|
||||
$dateFilenamePart = new DateTime();
|
||||
$this->writeExportedFile($exportFileName, $exportXml, $deployment->getContext(), $dateFilenamePart);
|
||||
$templateMgr->assign('exportedFileDatePart', $dateFilenamePart->format(self::EXPORT_FILE_DATE_PART_FORMAT));
|
||||
$templateMgr->assign('exportedFileContentNamePart', $exportFileName);
|
||||
}
|
||||
}
|
||||
|
||||
$templateMgr->assign('validationErrors', $deployment->getXMLValidationErrors());
|
||||
|
||||
$templateMgr->assign('errorsAndWarnings', $problems);
|
||||
$templateMgr->assign('errorsFound', $foundErrors);
|
||||
|
||||
// Display the results
|
||||
$json = new JSONMessage(true, $templateMgr->fetch('plugins/importexport/resultsExport.tpl'));
|
||||
header('Content-Type: application/json');
|
||||
return $json->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets template result for the import process
|
||||
*
|
||||
* @param string $filter
|
||||
* @param string $xmlString
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
* @param \PKP\template\PKPTemplateManager $templateMgr
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImportTemplateResult($filter, $xmlString, $deployment, $templateMgr)
|
||||
{
|
||||
$deployment->import($filter, $xmlString);
|
||||
|
||||
$templateMgr->assign('content', $deployment->processResult);
|
||||
$templateMgr->assign('validationErrors', $deployment->getXMLValidationErrors());
|
||||
|
||||
$problems = $deployment->getWarningsAndErrors();
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
|
||||
$templateMgr->assign('errorsAndWarnings', $problems);
|
||||
$templateMgr->assign('errorsFound', $foundErrors);
|
||||
|
||||
$templateMgr->assign('importedRootObjects', $deployment->getImportedRootEntitiesWithNames());
|
||||
|
||||
// Display the results
|
||||
$json = new JSONMessage(true, $templateMgr->fetch('plugins/importexport/resultsImport.tpl'));
|
||||
header('Content-Type: application/json');
|
||||
return $json->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the imported file path
|
||||
*
|
||||
* @param int $temporaryFileId
|
||||
* @param \PKP\user\User $user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImportedFilePath($temporaryFileId, $user)
|
||||
{
|
||||
$temporaryFileDao = DAORegistry::getDAO('TemporaryFileDAO'); /** @var \PKP\file\TemporaryFileDAO $temporaryFileDao */
|
||||
|
||||
$temporaryFile = $temporaryFileDao->getTemporaryFile($temporaryFileId, $user->getId());
|
||||
if (!$temporaryFile) {
|
||||
$json = new JSONMessage(true, __('plugins.inportexport.native.uploadFile'));
|
||||
header('Content-Type: application/json');
|
||||
return $json->getString();
|
||||
}
|
||||
$temporaryFilePath = $temporaryFile->getFilePath();
|
||||
|
||||
return $temporaryFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a tab to display after the import/export operation is over
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param string $title
|
||||
* @param string $bounceUrl
|
||||
* @param array $bounceParameterArray
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBounceTab($request, $title, $bounceUrl, $bounceParameterArray)
|
||||
{
|
||||
if (!$request->checkCSRF()) {
|
||||
throw new Exception('CSRF mismatch!');
|
||||
}
|
||||
$json = new JSONMessage(true);
|
||||
$json->setEvent('addTab', [
|
||||
'title' => $title,
|
||||
'url' => $request->url(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
['plugin', $this->getName(), $bounceUrl],
|
||||
array_merge($bounceParameterArray, ['csrfToken' => $request->getSession()->getCSRFToken()])
|
||||
),
|
||||
]);
|
||||
header('Content-Type: application/json');
|
||||
return $json->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file given it's name
|
||||
*/
|
||||
public function downloadExportedFile(string $exportedFileContentNamePart, string $exportedFileDatePart, PKPImportExportDeployment $deployment)
|
||||
{
|
||||
$date = DateTime::createFromFormat(self::EXPORT_FILE_DATE_PART_FORMAT, $exportedFileDatePart);
|
||||
if (!$date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$exportFileName = $this->getExportFileName($this->getExportPath(), $exportedFileContentNamePart, $deployment->getContext(), '.xml', $date);
|
||||
$fileManager = new FileManager();
|
||||
$fileManager->downloadByPath($exportFileName);
|
||||
$fileManager->deleteByPath($exportFileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create file given it's name and content
|
||||
*
|
||||
* @param ?DateTime $dateFilenamePart
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function writeExportedFile(string $filename, string $fileContent, Context $context, ?DateTime $dateFilenamePart = null)
|
||||
{
|
||||
$fileManager = new FileManager();
|
||||
$exportFileName = $this->getExportFileName($this->getExportPath(), $filename, $context, '.xml', $dateFilenamePart);
|
||||
$fileManager->writeFile($exportFileName, $fileContent);
|
||||
|
||||
return $exportFileName;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\ImportExportPlugin', '\ImportExportPlugin');
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/LazyLoadPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class LazyLoadPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for plugins that optionally
|
||||
* support lazy load.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
|
||||
abstract class LazyLoadPlugin extends Plugin
|
||||
{
|
||||
//
|
||||
// Override public methods from Plugin
|
||||
//
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
$this->addLocaleData();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Override protected methods from Plugin
|
||||
//
|
||||
/**
|
||||
* @see Plugin::getName()
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
// Lazy load enabled plug-ins always use the plugin's class name
|
||||
// as plug-in name. Legacy plug-ins will override this method so
|
||||
// this implementation is backwards compatible.
|
||||
// NB: strtolower was required for PHP4 compatibility.
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
return strtolower_codesafe(end($classNameParts));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods required to support lazy load.
|
||||
//
|
||||
/**
|
||||
* Determine whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @param int $contextId To identify if the plugin is enabled
|
||||
* we need a context. This context is usually taken from the
|
||||
* request but sometimes there is no context in the request
|
||||
* (e.g. when executing CLI commands). Then the main context
|
||||
* can be given as an explicit ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled($contextId = null)
|
||||
{
|
||||
if ($contextId == null) {
|
||||
$contextId = $this->getCurrentContextId();
|
||||
if ($this->isSitePlugin()) {
|
||||
$contextId = 0;
|
||||
}
|
||||
}
|
||||
return $this->getSetting($contextId, 'enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this plugin is currently enabled.
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled($enabled)
|
||||
{
|
||||
$contextId = $this->getCurrentContextId();
|
||||
if ($this->isSitePlugin()) {
|
||||
$contextId = 0;
|
||||
}
|
||||
$this->updateSetting($contextId, 'enabled', $enabled, 'bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getCanEnable()
|
||||
*/
|
||||
public function getCanEnable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getCanDisable()
|
||||
*/
|
||||
public function getCanDisable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current context ID or the site-wide context ID (0) if no context
|
||||
* can be found.
|
||||
*/
|
||||
public function getCurrentContextId()
|
||||
{
|
||||
$context = Application::get()->getRequest()->getContext();
|
||||
return is_null($context) ? 0 : $context->getId();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\LazyLoadPlugin', '\LazyLoadPlugin');
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/MetadataPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class MetadataPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for metadata plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
abstract class MetadataPlugin extends Plugin
|
||||
{
|
||||
//
|
||||
// Override public methods from Plugin
|
||||
//
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
$this->addLocaleData();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a unique id for this metadata format
|
||||
*
|
||||
* @param string $format The format to check for support.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function supportsFormat($format);
|
||||
|
||||
/**
|
||||
* Instantiate and return the schema object for this metadata format
|
||||
*
|
||||
* @param string $format The format to return the schema object for in case
|
||||
* the plugin supports multiple formats.
|
||||
*/
|
||||
abstract public function getSchemaObject($format);
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\MetadataPlugin', '\MetadataPlugin');
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file lib/pkp/classes/plugins/OAIMetadataFormatPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class OAIMetadataFormatPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for OAI Metadata format plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
abstract class OAIMetadataFormatPlugin extends Plugin
|
||||
{
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
$this->addLocaleData();
|
||||
if ($this->getEnabled()) {
|
||||
Hook::add('OAI::metadataFormats', [$this, 'callback_formatRequest']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata prefix for this plugin's format.
|
||||
*/
|
||||
public static function getMetadataPrefix()
|
||||
{
|
||||
assert(false); // Should always be overridden
|
||||
}
|
||||
|
||||
public static function getSchema()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function getNamespace()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hold of the class that does the formatting.
|
||||
*/
|
||||
abstract public function getFormatClass();
|
||||
|
||||
public function callback_formatRequest($hookName, $args)
|
||||
{
|
||||
$namesOnly = $args[0];
|
||||
$identifier = $args[1];
|
||||
$formats = & $args[2];
|
||||
|
||||
if ($namesOnly) {
|
||||
$formats = array_merge($formats, [$this->getMetadataPrefix()]);
|
||||
} else {
|
||||
$formatClass = $this->getFormatClass();
|
||||
$formats = array_merge(
|
||||
$formats,
|
||||
[$this->getMetadataPrefix() => new $formatClass($this->getMetadataPrefix(), $this->getSchema(), $this->getNamespace())]
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\OAIMetadataFormatPlugin', '\OAIMetadataFormatPlugin');
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PKPPubIdPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPPubIdPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for public identifiers plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\notification\Notification;
|
||||
use APP\notification\NotificationManager;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\db\DAO;
|
||||
use PKP\db\SchemaDAO;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\AjaxModal;
|
||||
use PKP\template\PKPTemplateManager;
|
||||
|
||||
abstract class PKPPubIdPlugin extends LazyLoadPlugin
|
||||
{
|
||||
//
|
||||
// Implement template methods from Plugin
|
||||
//
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->getEnabled($mainContextId)) {
|
||||
// Enable storage of additional fields.
|
||||
foreach ($this->getDAOs() as $dao) {
|
||||
// Augment the object with the additional properties required by the pub ID plugin.
|
||||
if ($dao instanceof SchemaDAO) {
|
||||
// Schema-backed DAOs need the schema extended.
|
||||
Hook::add('Schema::get::' . $dao->schemaName, [$this, 'addToSchema']);
|
||||
} elseif ($dao instanceof EntityDAO) {
|
||||
// Schema-backed DAOs need the schema extended.
|
||||
Hook::add('Schema::get::' . $dao->schema, [$this, 'addToSchema']);
|
||||
} else {
|
||||
// For non-schema-backed DAOs, DAOName::getAdditionalFieldNames can be used.
|
||||
$classNameParts = explode('\\', get_class($dao)); // Separate namespace info from class name
|
||||
Hook::add(strtolower_codesafe(end($classNameParts)) . '::getAdditionalFieldNames', [$this, 'getAdditionalFieldNames']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addLocaleData();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getActions()
|
||||
*/
|
||||
public function getActions($request, $actionArgs)
|
||||
{
|
||||
$router = $request->getRouter();
|
||||
return array_merge(
|
||||
$this->getEnabled() ? [
|
||||
new LinkAction(
|
||||
'settings',
|
||||
new AjaxModal(
|
||||
$router->url($request, null, null, 'manage', null, $actionArgs),
|
||||
$this->getDisplayName()
|
||||
),
|
||||
__('manager.plugins.settings'),
|
||||
null
|
||||
),
|
||||
] : [],
|
||||
parent::getActions($request, $actionArgs)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::manage()
|
||||
*/
|
||||
public function manage($args, $request)
|
||||
{
|
||||
$user = $request->getUser();
|
||||
$router = $request->getRouter();
|
||||
$context = $router->getContext($request);
|
||||
|
||||
$form = $this->instantiateSettingsForm($context->getId());
|
||||
$notificationManager = new NotificationManager();
|
||||
switch ($request->getUserVar('verb')) {
|
||||
case 'save':
|
||||
$form->readInputData();
|
||||
if ($form->validate()) {
|
||||
$form->execute();
|
||||
$notificationManager->createTrivialNotification($user->getId(), Notification::NOTIFICATION_TYPE_SUCCESS);
|
||||
return new JSONMessage(true);
|
||||
}
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
case 'clearPubIds':
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
$contextDao = Application::getContextDAO();
|
||||
$contextDao->deleteAllPubIds($context->getId(), $this->getPubIdType());
|
||||
return new JSONMessage(true);
|
||||
default:
|
||||
$form->initData();
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Protected template methods to be implemented by sub-classes.
|
||||
//
|
||||
/**
|
||||
* Get the public identifier.
|
||||
*
|
||||
* @param object $pubObject
|
||||
* Publication, Representation, SubmissionFile
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubId($pubObject);
|
||||
|
||||
/**
|
||||
* Construct the public identifier from its prefix and suffix.
|
||||
*
|
||||
* @param string $pubIdPrefix
|
||||
* @param string $pubIdSuffix
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function constructPubId($pubIdPrefix, $pubIdSuffix, $contextId);
|
||||
|
||||
/**
|
||||
* Public identifier type, see
|
||||
* http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubIdType();
|
||||
|
||||
/**
|
||||
* Public identifier type that will be displayed to the reader.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubIdDisplayType();
|
||||
|
||||
/**
|
||||
* Full name of the public identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubIdFullName();
|
||||
|
||||
/**
|
||||
* Get the whole resolving URL.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param string $pubId
|
||||
*
|
||||
* @return string resolving URL
|
||||
*/
|
||||
abstract public function getResolvingURL($contextId, $pubId);
|
||||
|
||||
/**
|
||||
* Get the file (path + filename)
|
||||
* to be included into the object's
|
||||
* identifiers tab, e.g. for suffix editing.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubIdMetadataFile();
|
||||
|
||||
/**
|
||||
* Add JavaScript files to be loaded in the metadata file.
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param PKPTemplateManager $templateMgr
|
||||
*/
|
||||
public function addJavaScript($request, $templateMgr)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file (path + filename)
|
||||
* for the pub id assignment
|
||||
* to be included into other pages.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPubIdAssignFile();
|
||||
|
||||
/**
|
||||
* Get the settings form.
|
||||
*
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return object Settings form
|
||||
*/
|
||||
abstract public function instantiateSettingsForm($contextId);
|
||||
|
||||
/**
|
||||
* Get the additional form field names,
|
||||
* for metadata, e.g. suffix field name.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFormFieldNames();
|
||||
|
||||
/**
|
||||
* Get the assign option form field name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getAssignFormFieldName();
|
||||
|
||||
/**
|
||||
* Get the the prefix form field name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPrefixFieldName();
|
||||
|
||||
/**
|
||||
* Get the the suffix form field name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getSuffixFieldName();
|
||||
|
||||
/**
|
||||
* Get the link actions used in the pub id forms,
|
||||
* e.g. clear pub id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getLinkActions($pubObject);
|
||||
|
||||
/**
|
||||
* Get the suffix patterns form field names for all objects.
|
||||
*
|
||||
* @return array (pub object type => suffix pattern field name)
|
||||
*/
|
||||
abstract public function getSuffixPatternsFieldNames();
|
||||
|
||||
/**
|
||||
* Get additional field names to be considered for storage.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getDAOFieldNames();
|
||||
|
||||
/**
|
||||
* Get the possible publication object types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPubObjectTypes()
|
||||
{
|
||||
return [
|
||||
'Publication' => '\APP\publication\Publication',
|
||||
'Representation' => '\PKP\submission\Representation',
|
||||
'SubmissionFile' => '\PKP\submissionFile\SubmissionFile',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this object type enabled in plugin settings
|
||||
*
|
||||
* @param string $pubObjectType
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isObjectTypeEnabled($pubObjectType, $contextId);
|
||||
|
||||
/**
|
||||
* Get the error message for not unique pub id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getNotUniqueErrorMsg();
|
||||
|
||||
/**
|
||||
* Verify form data.
|
||||
*
|
||||
* @param string $fieldName The form field to be checked.
|
||||
* @param string $fieldValue The value of the form field.
|
||||
* @param object $pubObject
|
||||
* @param int $contextId
|
||||
* @param string $errorMsg Return validation error messages here.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyData($fieldName, $fieldValue, $pubObject, $contextId, &$errorMsg)
|
||||
{
|
||||
// Verify pub id uniqueness.
|
||||
if ($fieldName == $this->getSuffixFieldName()) {
|
||||
if (empty($fieldValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Construct the potential new pub id with the posted suffix.
|
||||
$pubIdPrefix = $this->getSetting($contextId, $this->getPrefixFieldName());
|
||||
if (empty($pubIdPrefix)) {
|
||||
return true;
|
||||
}
|
||||
$newPubId = $this->constructPubId($pubIdPrefix, $fieldValue, $contextId);
|
||||
|
||||
if (!$this->checkDuplicate($newPubId, get_class($pubObject), $pubObject->getId(), $contextId)) {
|
||||
$errorMsg = $this->getNotUniqueErrorMsg();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given pubId is valid.
|
||||
*
|
||||
* @param string $pubId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validatePubId($pubId)
|
||||
{
|
||||
return true; // Assume a valid ID by default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of publication object types and
|
||||
* the corresponding DAOs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDAOs()
|
||||
{
|
||||
return [
|
||||
Repo::publication()->dao,
|
||||
Application::getRepresentationDAO(),
|
||||
Repo::submissionFile()->dao,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Can a pub id be assigned to the object.
|
||||
*
|
||||
* @param object $pubObject
|
||||
* Publication, Representation, SubmissionFile, OJS Issue, OMP Chapter
|
||||
*
|
||||
* @return bool
|
||||
* false, if the pub id contains an unresolved pattern i.e. '%' or
|
||||
* if the custom suffix is empty i.e. the pub id null.
|
||||
*/
|
||||
public function canBeAssigned($pubObject)
|
||||
{
|
||||
// Has the pub id already been assigned.
|
||||
$pubIdType = $this->getPubIdType();
|
||||
$storedPubId = $pubObject->getStoredPubId($pubIdType);
|
||||
if ($storedPubId) {
|
||||
return false;
|
||||
}
|
||||
// Get the pub id.
|
||||
$pubId = $this->getPubId($pubObject);
|
||||
// Is the custom suffix empty i.e. the pub id null.
|
||||
if (!$pubId) {
|
||||
return false;
|
||||
}
|
||||
// Does the suffix contain unresolved pattern.
|
||||
$containPatterns = strpos($pubId, '%') !== false;
|
||||
return !$containPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties for this type of public identifier to the entity's list for
|
||||
* storage in the database.
|
||||
* This is used for SchemaDAO-backed entities only.
|
||||
*
|
||||
* @see PKPPubIdPlugin::getAdditionalFieldNames()
|
||||
*
|
||||
* @param string $hookName `Schema::get::publication`
|
||||
* @param array $params
|
||||
*/
|
||||
public function addToSchema($hookName, $params)
|
||||
{
|
||||
$schema = & $params[0];
|
||||
foreach (array_merge($this->getFormFieldNames(), $this->getDAOFieldNames()) as $fieldName) {
|
||||
$schema->properties->{$fieldName} = (object) [
|
||||
'type' => 'string',
|
||||
'apiSummary' => true,
|
||||
'validation' => ['nullable'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties for this type of public identifier to the entity's list for
|
||||
* storage in the database.
|
||||
* This is used for non-SchemaDAO-backed entities only.
|
||||
*
|
||||
* @see PKPPubIdPlugin::addToSchema()
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param DAO $dao
|
||||
* @param array $additionalFields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAdditionalFieldNames($hookName, $dao, &$additionalFields)
|
||||
{
|
||||
foreach (array_merge($this->getFormFieldNames(), $this->getDAOFieldNames()) as $fieldName) {
|
||||
$additionalFields[] = $fieldName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object type.
|
||||
*
|
||||
* @param object $pubObject
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getPubObjectType($pubObject)
|
||||
{
|
||||
$allowedTypes = $this->getPubObjectTypes();
|
||||
$pubObjectType = null;
|
||||
foreach ($allowedTypes as $type => $fqcn) {
|
||||
if ($pubObject instanceof $fqcn) {
|
||||
$pubObjectType = $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_null($pubObjectType)) {
|
||||
// This must be a dev error, so bail with an assertion.
|
||||
assert(false);
|
||||
return null;
|
||||
}
|
||||
return $pubObjectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and store a public identifier.
|
||||
*
|
||||
* @param object $pubObject
|
||||
* @param string $pubId
|
||||
*/
|
||||
public function setStoredPubId(&$pubObject, $pubId)
|
||||
{
|
||||
$dao = $pubObject->getDAO();
|
||||
$dao->changePubId($pubObject->getId(), $this->getPubIdType(), $pubId);
|
||||
$pubObject->setStoredPubId($this->getPubIdType(), $pubId);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
/**
|
||||
* Check for duplicate public identifiers.
|
||||
*
|
||||
* Checks to see if a pubId has already been assigned to any object
|
||||
* in the context.
|
||||
*
|
||||
* @param string $pubId
|
||||
* @param string $pubObjectType Class name of the pub object being checked
|
||||
* @param int $excludeId This object id will not be checked for duplicates
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkDuplicate($pubId, $pubObjectType, $excludeId, $contextId)
|
||||
{
|
||||
foreach ($this->getPubObjectTypes() as $type => $fqcn) {
|
||||
if ($type === 'Publication') {
|
||||
$typeDao = Repo::publication()->dao;
|
||||
} elseif ($type === 'Representation') {
|
||||
$typeDao = Application::getRepresentationDAO();
|
||||
} elseif ($type === 'SubmissionFile') {
|
||||
$typeDao = Repo::submissionFile()->dao;
|
||||
}
|
||||
$excludeTypeId = $type === $pubObjectType ? $excludeId : null;
|
||||
if (isset($typeDao) && $typeDao->pubIdExists($this->getPubIdType(), $pubId, $excludeTypeId, $contextId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context object.
|
||||
*
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return object Context
|
||||
*/
|
||||
public function getContext($contextId)
|
||||
{
|
||||
assert(is_numeric($contextId));
|
||||
|
||||
// Get the context object from the context (optimized).
|
||||
$request = Application::get()->getRequest();
|
||||
$router = $request->getRouter();
|
||||
$context = $router->getContext($request);
|
||||
if ($context && $context->getId() == $contextId) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
// Fall back the database.
|
||||
$contextDao = Application::getContextDAO();
|
||||
return $contextDao->getById($contextId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PKPPubIdPlugin', '\PKPPubIdPlugin');
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PKPPubIdPluginDAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPPubIdPluginDAO
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Interface that DAOs would need to implement in order for pub ID support to be added.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
interface PKPPubIdPluginDAO
|
||||
{
|
||||
/**
|
||||
* Checks if public identifier exists (other than for the specified
|
||||
* submission ID, which is treated as an exception).
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
* @param int $excludePubObjectId ID of the pub object to be excluded from the search.
|
||||
* @param int $contextId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId);
|
||||
|
||||
/**
|
||||
* Change the public ID of a submission.
|
||||
*
|
||||
* @param int $pubObjectId ID of the pub object
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
*/
|
||||
public function changePubId($pubObjectId, $pubIdType, $pubId);
|
||||
|
||||
/**
|
||||
* Delete the public ID of a submission.
|
||||
*
|
||||
* @param int $pubObjectId ID of the pub object
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
*/
|
||||
public function deletePubId($pubObjectId, $pubIdType);
|
||||
|
||||
/**
|
||||
* Delete the public IDs of all submissions in this context.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
*/
|
||||
public function deleteAllPubIds($contextId, $pubIdType);
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PKPPubIdPluginDAO', '\PKPPubIdPluginDAO');
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PKPPubIdPluginHelper.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPPubIdPluginHelper
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Helper class for public identifiers plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\form\Form;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
use PKP\template\PKPTemplateManager;
|
||||
|
||||
class PKPPubIdPluginHelper
|
||||
{
|
||||
/**
|
||||
* Validate the additional form fields from public identifier plugins.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param object $form PKPPublicIdentifiersForm
|
||||
* @param object $pubObject
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function validate($contextId, $form, $pubObject)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$fieldNames = $pubIdPlugin->getFormFieldNames();
|
||||
foreach ($fieldNames as $fieldName) {
|
||||
$fieldValue = $form->getData($fieldName);
|
||||
$errorMsg = '';
|
||||
if (!$pubIdPlugin->verifyData($fieldName, $fieldValue, $pubObject, $contextId, $errorMsg)) {
|
||||
$form->addError($fieldName, $errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form link actions.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param object $form PKPPublicIdentifiersForm
|
||||
* @param object $pubObject
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function setLinkActions($contextId, $form, $pubObject)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$linkActions = $pubIdPlugin->getLinkActions($pubObject);
|
||||
foreach ($linkActions as $linkActionName => $linkAction) {
|
||||
$form->setData($linkActionName, $linkAction);
|
||||
unset($linkAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add pub id plugins JavaScripts.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param PKPRequest $request
|
||||
* @param PKPTemplateManager $templateMgr
|
||||
*/
|
||||
public function addJavaScripts($contextId, $request, $templateMgr)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$pubIdPlugin->addJavaScript($request, $templateMgr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the additional form fields from public identifier plugins.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param Form $form PKPPublicIdentifiersForm|CatalogEntryFormatMetadataForm
|
||||
* @param object $pubObject
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function init($contextId, $form, $pubObject)
|
||||
{
|
||||
if (isset($pubObject)) {
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$fieldNames = $pubIdPlugin->getFormFieldNames();
|
||||
foreach ($fieldNames as $fieldName) {
|
||||
$form->setData($fieldName, $pubObject->getData($fieldName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the additional input data from public identifier plugins.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param Form $form PKPPublicIdentifiersForm
|
||||
*/
|
||||
public function readInputData($contextId, $form)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$form->readUserVars($pubIdPlugin->getFormFieldNames());
|
||||
$form->readUserVars([$pubIdPlugin->getAssignFormFieldName()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the the public identifiers' assign form field data.
|
||||
*
|
||||
* @param object $form Form containing the assign check box
|
||||
* PKPAssignPublicIdentifiersForm
|
||||
* OJS IssueEntryPublicationMetadataForm
|
||||
*/
|
||||
public function readAssignInputData($form)
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
$context = $request->getContext();
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $context->getId());
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$form->readUserVars([$pubIdPlugin->getAssignFormFieldName()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the additional data from public identifier plugins.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param object $form PKPPublicIdentifiersForm
|
||||
* @param object $pubObject
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function execute($contextId, $form, $pubObject)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
// Public ID data can only be changed as long
|
||||
// as no ID has been generated.
|
||||
$storedId = $pubObject->getStoredPubId($pubIdPlugin->getPubIdType());
|
||||
if (!$storedId) {
|
||||
$fieldNames = $pubIdPlugin->getFormFieldNames();
|
||||
foreach ($fieldNames as $fieldName) {
|
||||
$data = $form->getData($fieldName);
|
||||
$pubObject->setData($fieldName, $data);
|
||||
}
|
||||
if ($form->getData($pubIdPlugin->getAssignFormFieldName())) {
|
||||
$pubId = $pubIdPlugin->getPubId($pubObject);
|
||||
$pubObject->setStoredPubId($pubIdPlugin->getPubIdType(), $pubId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign public identifier.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param object $form
|
||||
* @param object $pubObject
|
||||
* @param bool $save Whether the pub id shall be saved here
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function assignPubId($contextId, $form, $pubObject, $save = false)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
if ($form->getData($pubIdPlugin->getAssignFormFieldName())) {
|
||||
$pubId = $pubIdPlugin->getPubId($pubObject);
|
||||
if ($save) {
|
||||
$pubIdPlugin->setStoredPubId($pubObject, $pubId);
|
||||
} else {
|
||||
$pubObject->setStoredPubId($pubIdPlugin->getPubIdType(), $pubId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pubId from a pubObject.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param string $pubIdPlugInClassName
|
||||
* @param object $pubObject
|
||||
* Submission, Representation, SubmissionFile + OJS Issue
|
||||
*/
|
||||
public function clearPubId($contextId, $pubIdPlugInClassName, $pubObject)
|
||||
{
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $contextId);
|
||||
if (!empty($pubIdPlugins)) {
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$classNameParts = explode('\\', get_class($pubIdPlugin)); // Separate namespace info from class name
|
||||
if (end($classNameParts) == $pubIdPlugInClassName) {
|
||||
// clear the pubId:
|
||||
// delete the pubId from the DB
|
||||
$dao = $pubObject->getDAO();
|
||||
$pubObjectId = $pubObject->getId();
|
||||
$dao->deletePubId($pubObjectId, $pubIdPlugin->getPubIdType());
|
||||
// set the object setting/data 'pub-id::...' to null, in order
|
||||
// not to be considered in the DB object update later in the form
|
||||
$settingName = 'pub-id::' . $pubIdPlugin->getPubIdType();
|
||||
$pubObject->setData($settingName, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PKPPubIdPluginHelper', '\PKPPubIdPluginHelper');
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PKPViewableFilePlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPViewableFilePlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for article galley plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
abstract class PKPViewableFilePlugin extends GenericPlugin
|
||||
{
|
||||
/**
|
||||
* Get the filename of the template. (Default behavior may
|
||||
* be overridden through some combination of this function and the
|
||||
* displayArticleGalley function.)
|
||||
* Returning null from this function results in an empty display.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateFilename()
|
||||
{
|
||||
return 'display.tpl';
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PKPViewableFilePlugin', '\PKPViewableFilePlugin');
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/GenericPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PaymethodPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for generic plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use PKP\context\Context;
|
||||
|
||||
abstract class PaymethodPlugin extends LazyLoadPlugin
|
||||
{
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Hook::add('API::payments::settings::edit', [$this, 'saveSettings']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payment form for this plugin.
|
||||
*
|
||||
* @param Context $context
|
||||
* @param \PKP\payment\QueuedPayment $queuedPayment
|
||||
*
|
||||
* @return \PKP\form\Form
|
||||
*/
|
||||
abstract public function getPaymentForm($context, $queuedPayment);
|
||||
|
||||
/**
|
||||
* Check whether this plugin is fully configured and ready for use.
|
||||
*
|
||||
* @param Context $context
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfigured($context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented in a child class to save payment settings and attach updated data to the response
|
||||
*/
|
||||
abstract public function saveSettings(string $hookName, array $args);
|
||||
|
||||
/**
|
||||
* Handle incoming requests/notifications
|
||||
*
|
||||
* @param array $args
|
||||
* @param \APP\core\Request $request
|
||||
*/
|
||||
abstract public function handle($args, $request);
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PaymethodPlugin', '\PaymethodPlugin');
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup plugins Plugins
|
||||
* Implements a plugin structure that can be used to flexibly extend PKP
|
||||
* software via the use of a set of plugin categories.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/plugins/Plugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Plugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @see PluginRegistry, PluginSettingsDAO
|
||||
*
|
||||
* @brief Abstract class for plugins
|
||||
*
|
||||
* For best performance, a plug-in should not be instantiated if it is
|
||||
* disabled or the current page/operation does not require the plug-in's
|
||||
* functionality.
|
||||
*
|
||||
* Newer plug-ins support enable/disable and request filter settings that
|
||||
* enable the PKP library plug-in framework to lazy-load plug-ins only
|
||||
* when their functionality is actually being required for a request.
|
||||
*
|
||||
* For backwards compatibility we need to assume that older plug-ins
|
||||
* do not support lazy-load because their register() method and hooks
|
||||
* may have side-effects required on all requests. We have no way of
|
||||
* knowing on which pages these side effects are important so we need
|
||||
* to load legacy plug-ins on all pages.
|
||||
*
|
||||
* In these cases the register() function will be called on every request
|
||||
* when the category the plug-in belongs to is being loaded. This was the
|
||||
* default behavior before plug-in lazy load was introduced.
|
||||
*
|
||||
* Plug-ins that want to enable lazy-load have to include a 'lazy-load'
|
||||
* setting in their version.xml:
|
||||
*
|
||||
* <lazy-load>1</lazy-load>
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\template\TemplateManager;
|
||||
use Exception;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\core\Registry;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\facades\Repo;
|
||||
use PKP\install\Installer;
|
||||
use PKP\observers\events\PluginSettingChanged;
|
||||
use PKP\site\Version;
|
||||
use PKP\site\VersionDAO;
|
||||
use PKP\template\PKPTemplateResource;
|
||||
|
||||
// Define the well-known file name for filter configuration data.
|
||||
define('PLUGIN_FILTER_DATAFILE', 'filterConfig.xml');
|
||||
define('PLUGIN_TEMPLATE_RESOURCE_PREFIX', 'plugins');
|
||||
|
||||
abstract class Plugin
|
||||
{
|
||||
/** @var string Path name to files for this plugin */
|
||||
public $pluginPath;
|
||||
|
||||
/** @var string Category name this plugin is registered to*/
|
||||
public $pluginCategory;
|
||||
|
||||
/** @var PKPRequest the current request object */
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Plugin API (Registration and Initialization)
|
||||
*/
|
||||
/**
|
||||
* Load and initialize the plug-in and register plugin hooks.
|
||||
*
|
||||
* For backwards compatibility this method will be called whenever
|
||||
* the plug-in's category is being loaded. If, however, registerOn()
|
||||
* returns an array then this method will only be called when
|
||||
* the plug-in is enabled and an entry in the result set of
|
||||
* registerOn() matches the current request operation. An empty array
|
||||
* matches all request operations.
|
||||
*
|
||||
* @param string $category Name of category plugin was registered to
|
||||
* @param string $path The path the plugin was found in
|
||||
* @param int $mainContextId To identify if the plugin is enabled
|
||||
* we need a context. This context is usually taken from the
|
||||
* request but sometimes there is no context in the request
|
||||
* (e.g. when executing CLI commands). Then the main context
|
||||
* can be given as an explicit ID.
|
||||
*
|
||||
* @return bool True iff plugin registered successfully; if false,
|
||||
* the plugin will not be executed.
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
$this->pluginPath = $path;
|
||||
$this->pluginCategory = $category;
|
||||
if ($this->getInstallMigration()) {
|
||||
Hook::add('Installer::postInstall', [$this, 'updateSchema']);
|
||||
}
|
||||
if ($this->getInstallSitePluginSettingsFile()) {
|
||||
Hook::add('Installer::postInstall', [$this, 'installSiteSettings']);
|
||||
}
|
||||
if ($this->getInstallEmailTemplatesFile()) {
|
||||
Hook::add('Installer::postInstall', [$this, 'installEmailTemplates']);
|
||||
Hook::add('Locale::installLocale', [$this, 'installLocale']);
|
||||
}
|
||||
if ($this->getInstallEmailTemplateDataFile()) {
|
||||
Hook::add('Installer::postInstall', [$this, 'installEmailTemplateData']);
|
||||
}
|
||||
if ($this->getContextSpecificPluginSettingsFile()) {
|
||||
Hook::add('Context::add', [$this, 'installContextSpecificSettings']);
|
||||
}
|
||||
|
||||
Hook::add('Installer::postInstall', [$this, 'installFilters']);
|
||||
|
||||
$this->_registerTemplateResource();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected methods (may be overridden by custom plugins)
|
||||
*/
|
||||
|
||||
//
|
||||
// Plugin Display
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the name of this plugin. The name must be unique within
|
||||
* its category, and should be suitable for part of a filename
|
||||
* (ie short, no spaces, and no dependencies on cases being unique).
|
||||
*
|
||||
* @return string name of plugin
|
||||
*/
|
||||
abstract public function getName();
|
||||
|
||||
/**
|
||||
* Get the display name for this plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getDisplayName();
|
||||
|
||||
/**
|
||||
* Get a description of this plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getDescription();
|
||||
|
||||
//
|
||||
// Plugin Behavior and Management
|
||||
//
|
||||
|
||||
/**
|
||||
* Return a number indicating the sequence in which this plugin
|
||||
* should be registered compared to others of its category.
|
||||
* Higher = later.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSeq()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Site-wide plugins should override this function to return true.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSitePlugin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a management function.
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*
|
||||
* @return JSONMessage A JSON-encoded response
|
||||
*/
|
||||
public function manage($args, $request)
|
||||
{
|
||||
throw new Exception('Unhandled management action!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not this plugin should be hidden from the
|
||||
* management interface. Useful in the case of derivative plugins,
|
||||
* i.e. when a generic plugin registers a feed plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getHideManagement()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Plugin Installation
|
||||
//
|
||||
|
||||
/**
|
||||
* @deprecated See https://github.com/pkp/pkp-lib/issues/2493
|
||||
*/
|
||||
final public function getInstallSchemaFile()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the installation migration for this plugin.
|
||||
*
|
||||
* @return ?Migration
|
||||
*/
|
||||
public function getInstallMigration()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the settings data for this plugin to install
|
||||
* when the system is installed (i.e. site-level plugin settings).
|
||||
* Subclasses using default settings should override this.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallSitePluginSettingsFile()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the settings data for this plugin to install
|
||||
* when a new application context (e.g. journal, conference or press)
|
||||
* is installed.
|
||||
*
|
||||
* Subclasses using default settings should override this.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContextSpecificPluginSettingsFile()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the email templates for this plugin.
|
||||
* Subclasses using email templates should override this.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallEmailTemplatesFile()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the email template data for this plugin.
|
||||
* Subclasses using email templates should override this.
|
||||
*
|
||||
* @deprecated Starting with OJS/OMP 3.2, localized content should be specified via getInstallEmailTemplatesFile(). (pkp/pkp-lib#5461)
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallEmailTemplateDataFile()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename(s) of the filter configuration data for
|
||||
* this plugin. Subclasses using filters can override this.
|
||||
*
|
||||
* The default implementation establishes "well known" locations
|
||||
* for the filter configuration. If you keep your files in these
|
||||
* locations then there's no need to override this method.
|
||||
*
|
||||
* @return string|array one or more file locations.
|
||||
*/
|
||||
public function getInstallFilterConfigFiles()
|
||||
{
|
||||
// Construct the well-known filter configuration file names.
|
||||
$filterConfigFile = $this->getPluginPath() . '/filter/' . PLUGIN_FILTER_DATAFILE;
|
||||
$filterConfigFiles = [
|
||||
'./lib/pkp/' . $filterConfigFile,
|
||||
'./' . $filterConfigFile
|
||||
];
|
||||
return $filterConfigFiles;
|
||||
}
|
||||
|
||||
/*
|
||||
* Protected helper methods (can be used by custom plugins but
|
||||
* should not be overridden by custom plugins)
|
||||
*/
|
||||
/**
|
||||
* Get the name of the category this plugin is registered to.
|
||||
*
|
||||
* @return string category
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->pluginCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path this plugin's files are located in.
|
||||
*
|
||||
* @return string pathname
|
||||
*/
|
||||
public function getPluginPath()
|
||||
{
|
||||
return $this->pluginPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory name of the plugin
|
||||
*
|
||||
* @return string directory name
|
||||
*/
|
||||
public function getDirName()
|
||||
{
|
||||
return basename($this->pluginPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Resource Name for templates in this plugin, or if specified, the full resource locator
|
||||
* for a specific template.
|
||||
*
|
||||
* @param string $template path/filename, if desired
|
||||
* @param bool $inCore True if a "core" template should be used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateResource($template = null, $inCore = false)
|
||||
{
|
||||
$pluginPath = $this->getPluginPath();
|
||||
if ($inCore) {
|
||||
$pluginPath = PKP_LIB_PATH . "/{$pluginPath}";
|
||||
}
|
||||
$plugin = basename($pluginPath);
|
||||
$category = basename(dirname($pluginPath));
|
||||
|
||||
$contextId = PKPApplication::CONTEXT_SITE;
|
||||
if (Application::isInstalled()) {
|
||||
$context = Application::get()->getRequest()->getContext();
|
||||
if ($context instanceof \PKP\context\Context) {
|
||||
$contextId = $context->getId();
|
||||
}
|
||||
}
|
||||
|
||||
// Slash characters (/) are not allowed in resource names, so use dashes (-) instead.
|
||||
$resourceName = strtr(join('/', [PLUGIN_TEMPLATE_RESOURCE_PREFIX, $contextId, $pluginPath, $category, $plugin]), '/', '-');
|
||||
return $resourceName . ($template !== null ? ":{$template}" : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the canonical template path of this plug-in
|
||||
*
|
||||
* @param bool $inCore Return the core template path if true.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTemplatePath($inCore = false)
|
||||
{
|
||||
$templatePath = ($inCore ? PKP_LIB_PATH . '/' : '') . "{$this->getPluginPath()}/templates";
|
||||
if (is_dir($templatePath)) {
|
||||
return $templatePath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this plugin's templates as a template resource
|
||||
*
|
||||
* @param bool $inCore True iff this is a core resource.
|
||||
*/
|
||||
protected function _registerTemplateResource($inCore = false)
|
||||
{
|
||||
if ($templatePath = $this->getTemplatePath($inCore)) {
|
||||
$templateMgr = TemplateManager::getManager(Application::get()->getRequest());
|
||||
$pluginTemplateResource = new PKPTemplateResource($templatePath);
|
||||
$templateMgr->registerResource($this->getTemplateResource(null, $inCore), $pluginTemplateResource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when an enabled plugin is registered in order to override
|
||||
* template files. Any plugin which calls this method can
|
||||
* override template files by adding their own templates to:
|
||||
* <overridingPlugin>/templates/plugins/<category>/<originalPlugin>/templates/<path>.tpl
|
||||
*
|
||||
* @param string $hookName TemplateResource::getFilename
|
||||
* @param array $args [
|
||||
*
|
||||
* @option string File path to preferred template. Leave as-is to not
|
||||
* override template.
|
||||
* @option string Template file requested
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function _overridePluginTemplates($hookName, $args)
|
||||
{
|
||||
$filePath = & $args[0];
|
||||
$template = $args[1];
|
||||
$checkFilePath = $filePath;
|
||||
|
||||
// If there's a templates/ prefix on the template, clean up the test path.
|
||||
if (strpos($filePath, 'plugins/') === 0) {
|
||||
$checkFilePath = 'templates/' . $checkFilePath;
|
||||
}
|
||||
|
||||
// If there's a lib/pkp/ prefix on the template, test without it.
|
||||
$libPkpPrefix = 'lib/pkp/';
|
||||
if (strpos($checkFilePath, $libPkpPrefix) === 0) {
|
||||
$checkFilePath = substr($filePath, strlen($libPkpPrefix));
|
||||
}
|
||||
|
||||
// Check if an overriding plugin exists in the plugin path.
|
||||
if ($overriddenFilePath = $this->_findOverriddenTemplate($checkFilePath)) {
|
||||
$filePath = $overriddenFilePath;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive check for existing templates
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function _findOverriddenTemplate($path)
|
||||
{
|
||||
$fullPath = sprintf('%s/%s', $this->getPluginPath(), $path);
|
||||
|
||||
if (file_exists($fullPath)) {
|
||||
return $fullPath;
|
||||
}
|
||||
|
||||
// Backward compatibility for OJS prior to 3.1.2; changed path to templates for plugins.
|
||||
if (($fullPath = preg_replace("/templates\/(?!.*templates\/)/", '', $fullPath)) && file_exists($fullPath)) {
|
||||
if (Config::getVar('debug', 'deprecation_warnings')) {
|
||||
trigger_error('Deprecated: The template at ' . $fullPath . ' has moved and will not be found in the future.');
|
||||
}
|
||||
return $fullPath;
|
||||
}
|
||||
|
||||
// Recursive check for templates in ancestors of a current theme plugin
|
||||
if ($this instanceof ThemePlugin
|
||||
&& $this->parent
|
||||
&& $fullPath = $this->parent->_findOverriddenTemplate($path)) {
|
||||
return $fullPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load locale data for this plugin.
|
||||
*/
|
||||
public function addLocaleData(): void
|
||||
{
|
||||
$basePath = $this->getPluginPath() . '/locale';
|
||||
foreach ([$basePath, "lib/pkp/{$basePath}"] as $path) {
|
||||
if (is_dir($path)) {
|
||||
Locale::registerPath($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a plugin setting within the given context
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $name Setting name
|
||||
*/
|
||||
public function getSetting($contextId, $name)
|
||||
{
|
||||
if (!Application::isUpgrading() && !Application::isInstalled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pluginSettingsDao = DAORegistry::getDAO('PluginSettingsDAO'); /** @var PluginSettingsDAO $pluginSettingsDao */
|
||||
return $pluginSettingsDao->getSetting($contextId, $this->getName(), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a plugin setting within the given context.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $name The name of the setting
|
||||
* @param mixed $value Setting value
|
||||
* @param string $type optional
|
||||
*/
|
||||
public function updateSetting($contextId, $name, $value, $type = null)
|
||||
{
|
||||
$pluginSettingsDao = DAORegistry::getDAO('PluginSettingsDAO'); /** @var PluginSettingsDAO $pluginSettingsDao */
|
||||
$pluginSettingsDao->updateSetting($contextId, $this->getName(), $name, $value, $type);
|
||||
|
||||
event(new PluginSettingChanged($this, $name, $value, $contextId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a PHP file from this plugin's installation directory.
|
||||
*
|
||||
* @deprecated 3.4.0 pkp/pkp-lib#8186
|
||||
*
|
||||
* @param string $class
|
||||
*/
|
||||
public function import($class)
|
||||
{
|
||||
require_once $this->getPluginPath() . '/' . str_replace('.', '/', $class) . '.inc.php';
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper methods (for internal use only, should not
|
||||
* be used by custom plug-ins)
|
||||
*
|
||||
* NB: These methods may change without notice in the future!
|
||||
*/
|
||||
/**
|
||||
* Callback used to install settings on system install.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function installSiteSettings($hookName, $args)
|
||||
{
|
||||
$pluginSettingsDao = DAORegistry::getDAO('PluginSettingsDAO'); /** @var PluginSettingsDAO $pluginSettingsDao */
|
||||
$pluginSettingsDao->installSettings(PKPApplication::CONTEXT_SITE, $this->getName(), $this->getInstallSitePluginSettingsFile());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to install settings on new context
|
||||
* (e.g. journal, conference or press) creation.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function installContextSpecificSettings($hookName, $args)
|
||||
{
|
||||
$context = $args[0];
|
||||
$pluginSettingsDao = DAORegistry::getDAO('PluginSettingsDAO'); /** @var PluginSettingsDAO $pluginSettingsDao */
|
||||
$pluginSettingsDao->installSettings($context->getId(), $this->getName(), $this->getContextSpecificPluginSettingsFile());
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to install email templates.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function installEmailTemplates($hookName, $args)
|
||||
{
|
||||
$installer = & $args[0]; /** @var Installer $installer */
|
||||
$result = & $args[1];
|
||||
|
||||
// Load email template data as required from the locale files.
|
||||
$locales = [];
|
||||
foreach ($installer->installedLocales as $locale) {
|
||||
if (file_exists($this->getPluginPath() . "/locale/{$locale}/emails.po")) {
|
||||
$locales[] = $locale;
|
||||
}
|
||||
}
|
||||
// Localized data is needed by the email installation
|
||||
$this->addLocaleData();
|
||||
$status = Repo::emailTemplate()->dao->installEmailTemplates($this->getInstallEmailTemplatesFile(), $locales, null, true);
|
||||
|
||||
if ($status === false) {
|
||||
// The template file seems to be invalid.
|
||||
$installer->setError(Installer::INSTALLER_ERROR_DB, str_replace('{$file}', $this->getInstallEmailTemplatesFile(), __('installer.installParseEmailTemplatesFileError')));
|
||||
$result = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to install email template data.
|
||||
*
|
||||
* @deprecated Email template data should be installed via installEmailTemplates (pkp/pkp-lib#5461)
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function installEmailTemplateData($hookName, $args)
|
||||
{
|
||||
$installer = & $args[0];
|
||||
$result = & $args[1];
|
||||
|
||||
foreach ($installer->installedLocales as $locale) {
|
||||
$filename = str_replace('{$installedLocale}', $locale, $this->getInstallEmailTemplateDataFile());
|
||||
if (!file_exists($filename)) {
|
||||
continue;
|
||||
}
|
||||
$sql = Repo::emailTemplate()->dao->installEmailTemplateData($filename, $locale, true);
|
||||
if ($sql) {
|
||||
$result = $installer->executeSQL($sql);
|
||||
} else {
|
||||
$installer->setError(Installer::INSTALLER_ERROR_DB, str_replace('{$file}', $filename, __('installer.installParseEmailTemplatesFileError')));
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to install email template data on locale install.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function installLocale($hookName, $args)
|
||||
{
|
||||
$locale = & $args[0];
|
||||
$filename = str_replace('{$installedLocale}', $locale, $this->getInstallEmailTemplateDataFile());
|
||||
|
||||
// Since pkp/pkp-lib#5461, there are two ways to specify localized email data in plugins.
|
||||
// Install locale data specified in the old form. (Deprecated!)
|
||||
if ($this->getInstallEmailTemplateDataFile()) {
|
||||
Repo::emailTemplate()->dao->installEmailTemplateData($filename, $locale);
|
||||
}
|
||||
|
||||
// Install locale data specified in the new form.
|
||||
if (file_exists($this->getPluginPath() . "/locale/{$locale}/emails.po")) {
|
||||
$this->addLocaleData();
|
||||
Repo::emailTemplate()->dao->installEmailTemplateLocaleData($this->getInstallEmailTemplatesFile(), [$locale]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to install filters.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*/
|
||||
public function installFilters($hookName, $args)
|
||||
{
|
||||
$installer = & $args[0]; /** @var Installer $installer */
|
||||
$result = & $args[1]; /** @var bool $result */
|
||||
|
||||
// Get the filter configuration file name(s).
|
||||
$filterConfigFiles = $this->getInstallFilterConfigFiles();
|
||||
if (is_scalar($filterConfigFiles)) {
|
||||
$filterConfigFiles = [$filterConfigFiles];
|
||||
}
|
||||
|
||||
// Run through the config file positions and see
|
||||
// whether one of these exists and needs to be installed.
|
||||
foreach ($filterConfigFiles as $filterConfigFile) {
|
||||
// Is there a filter configuration?
|
||||
if (!file_exists($filterConfigFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Install the filter configuration.
|
||||
$result = $installer->installFilterConfig($filterConfigFile);
|
||||
if (!$result) {
|
||||
// The filter configuration file seems to be invalid.
|
||||
$installer->setError(Installer::INSTALLER_ERROR_DB, str_replace('{$file}', $filterConfigFile, __('installer.installParseFilterConfigFileError')));
|
||||
}
|
||||
}
|
||||
|
||||
// Do not stop installation.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during the install process to install the plugin schema,
|
||||
* if applicable.
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateSchema($hookName, $args)
|
||||
{
|
||||
$installer = & $args[0];
|
||||
$result = & $args[1];
|
||||
|
||||
if ($migration = $this->getInstallMigration()) {
|
||||
try {
|
||||
$migration->up();
|
||||
} catch (Exception $e) {
|
||||
$installer->setError(Installer::INSTALLER_ERROR_DB, __('installer.installMigrationError', ['class' => get_class($migration), 'message' => $e->getMessage()]));
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the {url ...} smarty to support plugins.
|
||||
*
|
||||
* @param array $params
|
||||
* @param \Smarty $smarty
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function smartyPluginUrl($params, $smarty)
|
||||
{
|
||||
$path = [$this->getCategory(), $this->getName()];
|
||||
if (is_array($params['path'])) {
|
||||
$params['path'] = array_merge($path, $params['path']);
|
||||
} elseif (!empty($params['path'])) {
|
||||
$params['path'] = array_merge($path, [$params['path']]);
|
||||
} else {
|
||||
$params['path'] = $path;
|
||||
}
|
||||
return $smarty->smartyUrl($params, $smarty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current version of this plugin
|
||||
*
|
||||
* @return ?Version
|
||||
*/
|
||||
public function getCurrentVersion()
|
||||
{
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
|
||||
$pluginPath = $this->getPluginPath();
|
||||
$product = basename($pluginPath);
|
||||
$category = basename(dirname($pluginPath));
|
||||
$installedPlugin = $versionDao->getCurrentVersion('plugins.' . $category, $product);
|
||||
|
||||
if ($installedPlugin) {
|
||||
return $installedPlugin;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current request object
|
||||
*
|
||||
* @return PKPRequest
|
||||
*/
|
||||
public function &getRequest()
|
||||
{
|
||||
if (!$this->request) {
|
||||
$this->request = & Registry::get('request');
|
||||
}
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private helper methods
|
||||
*/
|
||||
/**
|
||||
* Get a list of link actions for plugin management.
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param array $actionArgs The list of action args to be included in request URLs.
|
||||
*
|
||||
* @return array List of LinkActions
|
||||
*/
|
||||
public function getActions($request, $actionArgs)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the plugin can be enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCanEnable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the plugin can be disabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCanDisable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the plugin is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\Plugin', '\Plugin');
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PluginGalleryDAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PluginGalleryDAO
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @see DAO
|
||||
*
|
||||
* @brief Operations for retrieving content from the PKP plugin gallery.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use PKP\cache\CacheManager;
|
||||
use PKP\cache\FileCache;
|
||||
use PKP\controllers\grid\plugins\PluginGalleryGridHandler;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\site\VersionDAO;
|
||||
use Throwable;
|
||||
|
||||
class PluginGalleryDAO extends \PKP\db\DAO
|
||||
{
|
||||
public const PLUGIN_GALLERY_XML_URL = 'https://pkp.sfu.ca/ojs/xml/plugins.xml';
|
||||
|
||||
/**
|
||||
* The default timeout (in seconds) to wait plugins.xml request
|
||||
*
|
||||
* @see https://docs.guzzlephp.org/en/6.5/request-options.html#timeout
|
||||
*/
|
||||
public const DEFAULT_TIMEOUT = 10;
|
||||
|
||||
/**
|
||||
* TTL's Cache in seconds
|
||||
*/
|
||||
public const TTL_CACHE_SECONDS = 86400;
|
||||
|
||||
/**
|
||||
* Get a set of GalleryPlugin objects describing the available
|
||||
* compatible plugins in their newest versions.
|
||||
*
|
||||
* @param PKPApplication $application
|
||||
* @param string $category Optional category name to use as filter
|
||||
* @param string $search Optional text to use as filter
|
||||
*
|
||||
* @return array GalleryPlugin objects
|
||||
*/
|
||||
public function getNewestCompatible($application, $category = null, $search = null)
|
||||
{
|
||||
$doc = $this->_getDocument();
|
||||
$plugins = [];
|
||||
|
||||
foreach ($doc->getElementsByTagName('plugin') as $index => $element) {
|
||||
$plugin = $this->_compatibleFromElement($element, $application);
|
||||
// May be null if no compatible version exists; also
|
||||
// apply search filters if any supplied.
|
||||
if (
|
||||
$plugin &&
|
||||
($category == '' || $category == PluginGalleryGridHandler::PLUGIN_GALLERY_ALL_CATEGORY_SEARCH_VALUE || $plugin->getCategory() == $category) &&
|
||||
($search == '' || PKPString::strpos(PKPString::strtolower(serialize($plugin)), PKPString::strtolower($search)) !== false)
|
||||
) {
|
||||
$plugins[$index] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the external Plugin XML document
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
protected function getExternalDocument(): ?string
|
||||
{
|
||||
$application = Application::get();
|
||||
$client = $application->getHttpClient();
|
||||
/** @var VersionDAO */
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO');
|
||||
$currentVersion = $versionDao->getCurrentVersion();
|
||||
try {
|
||||
$response = $client->request(
|
||||
'GET',
|
||||
static::PLUGIN_GALLERY_XML_URL,
|
||||
[
|
||||
'query' => [
|
||||
'application' => $application->getName(),
|
||||
'version' => $currentVersion->getVersionString()
|
||||
],
|
||||
'timeout' => self::DEFAULT_TIMEOUT,
|
||||
]
|
||||
);
|
||||
|
||||
return $response->getBody();
|
||||
} catch (Throwable $e) {
|
||||
error_log($e->getMessage());
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached Plugin XML document
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
protected function getCachedDocument(): ?string
|
||||
{
|
||||
$cacheManager = CacheManager::getManager();
|
||||
/** @var FileCache */
|
||||
$cache = $cacheManager->getCache(
|
||||
'loadPluginsXML',
|
||||
Application::CONTEXT_SITE,
|
||||
function (FileCache $cache) {
|
||||
$cache->setEntireCache($this->getExternalDocument());
|
||||
}
|
||||
);
|
||||
|
||||
$cacheTime = $cache->getCacheTime();
|
||||
|
||||
// Checking if the cache is older than 1 day, or its null
|
||||
if ($cacheTime === null || (time() - $cacheTime > self::TTL_CACHE_SECONDS)) {
|
||||
// This cache is out of date; so, lets request a new version.
|
||||
$response = $this->getExternalDocument();
|
||||
|
||||
// The plugins.xml request wasnt empty, so lets replace it
|
||||
if ($response !== null) {
|
||||
$cache->setEntireCache($response);
|
||||
}
|
||||
}
|
||||
|
||||
return $cache->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DOM document for the plugin gallery.
|
||||
*
|
||||
* @return DOMDocument
|
||||
*/
|
||||
private function _getDocument()
|
||||
{
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->loadXML($this->getCachedDocument());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new data object.
|
||||
*
|
||||
* @return GalleryPlugin
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new GalleryPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a GalleryPlugin from a DOM element, using the newest compatible
|
||||
* release with the supplied Application.
|
||||
*
|
||||
* @param DOMElement $element
|
||||
* @param Application $application
|
||||
*
|
||||
* @return GalleryPlugin|null, if no compatible plugin was available
|
||||
*/
|
||||
protected function _compatibleFromElement($element, $application)
|
||||
{
|
||||
$plugin = $this->newDataObject();
|
||||
$plugin->setCategory($element->getAttribute('category'));
|
||||
$plugin->setProduct($element->getAttribute('product'));
|
||||
$doc = $element->ownerDocument;
|
||||
$foundRelease = false;
|
||||
for ($n = $element->firstChild; $n; $n = $n->nextSibling) {
|
||||
if (!($n instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
switch ($n->tagName) {
|
||||
case 'name':
|
||||
$plugin->setName($n->nodeValue, $n->getAttribute('locale'));
|
||||
break;
|
||||
case 'homepage':
|
||||
$plugin->setHomepage($n->nodeValue);
|
||||
break;
|
||||
case 'description':
|
||||
$plugin->setDescription($n->nodeValue, $n->getAttribute('locale'));
|
||||
break;
|
||||
case 'installation':
|
||||
$plugin->setInstallationInstructions($n->nodeValue, $n->getAttribute('locale'));
|
||||
break;
|
||||
case 'summary':
|
||||
$plugin->setSummary($n->nodeValue, $n->getAttribute('locale'));
|
||||
break;
|
||||
case 'maintainer':
|
||||
$this->_handleMaintainer($n, $plugin);
|
||||
break;
|
||||
case 'release':
|
||||
// If a compatible release couldn't be
|
||||
// found, return null.
|
||||
if ($this->_handleRelease($n, $plugin, $application)) {
|
||||
$foundRelease = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Not erroring out here so that future
|
||||
// additions won't break old releases.
|
||||
}
|
||||
}
|
||||
if (!$foundRelease) {
|
||||
// No compatible release was found.
|
||||
return null;
|
||||
}
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a maintainer element
|
||||
*
|
||||
* @param GalleryPlugin $plugin
|
||||
*/
|
||||
public function _handleMaintainer($element, $plugin)
|
||||
{
|
||||
for ($n = $element->firstChild; $n; $n = $n->nextSibling) {
|
||||
if (!($n instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
switch ($n->tagName) {
|
||||
case 'name':
|
||||
$plugin->setContactName($n->nodeValue);
|
||||
break;
|
||||
case 'institution':
|
||||
$plugin->setContactInstitutionName($n->nodeValue);
|
||||
break;
|
||||
case 'email':
|
||||
$plugin->setContactEmail($n->nodeValue);
|
||||
break;
|
||||
default:
|
||||
// Not erroring out here so that future
|
||||
// additions won't break old releases.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a release element
|
||||
*
|
||||
* @param GalleryPlugin $plugin
|
||||
* @param PKPApplication $application
|
||||
*/
|
||||
public function _handleRelease($element, $plugin, $application)
|
||||
{
|
||||
$release = [
|
||||
'date' => strtotime($element->getAttribute('date')),
|
||||
'version' => $element->getAttribute('version'),
|
||||
'md5' => $element->getAttribute('md5'),
|
||||
];
|
||||
|
||||
$compatible = false;
|
||||
for ($n = $element->firstChild; $n; $n = $n->nextSibling) {
|
||||
if (!($n instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
switch ($n->tagName) {
|
||||
case 'description':
|
||||
$release[$n->tagName][$n->getAttribute('locale')] = $n->nodeValue;
|
||||
break;
|
||||
case 'package':
|
||||
$release['package'] = $n->nodeValue;
|
||||
break;
|
||||
case 'compatibility':
|
||||
// If a compatible release couldn't be
|
||||
// found, return null.
|
||||
if ($this->_handleCompatibility($n, $plugin, $application)) {
|
||||
$compatible = true;
|
||||
}
|
||||
break;
|
||||
case 'certification':
|
||||
$release[$n->tagName][] = $n->getAttribute('type');
|
||||
break;
|
||||
default:
|
||||
// Not erroring out here so that future
|
||||
// additions won't break old releases.
|
||||
}
|
||||
}
|
||||
|
||||
if ($compatible && (!$plugin->getData('version') || version_compare($plugin->getData('version'), $release['version'], '<'))) {
|
||||
// This release is newer than the one found earlier, or
|
||||
// this is the first compatible release we've found.
|
||||
$plugin->setDate($release['date']);
|
||||
$plugin->setVersion($release['version']);
|
||||
$plugin->setReleaseMD5($release['md5']);
|
||||
$plugin->setReleaseDescription($release['description']);
|
||||
$plugin->setReleaseCertifications($release['certification'] ?? []);
|
||||
$plugin->setReleasePackage($release['package']);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a compatibility element, fishing out the most recent statement
|
||||
* of compatibility.
|
||||
*
|
||||
* @param GalleryPlugin $plugin
|
||||
* @param PKPApplication $application
|
||||
*
|
||||
* @return bool True iff a compatibility statement matched this app
|
||||
*/
|
||||
public function _handleCompatibility($element, $plugin, $application)
|
||||
{
|
||||
// Check that the compatibility statement refers to this app
|
||||
if ($element->getAttribute('application') != $application->getName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($n = $element->firstChild; $n; $n = $n->nextSibling) {
|
||||
if (!($n instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
switch ($n->tagName) {
|
||||
case 'version':
|
||||
$installedVersion = $application->getCurrentVersion();
|
||||
if ($installedVersion->isCompatible($n->nodeValue)) {
|
||||
// Compatibility was determined.
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No applicable compatibility statement found.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PluginGalleryDAO', '\PluginGalleryDAO');
|
||||
define('PLUGIN_GALLERY_XML_URL', PluginGalleryDAO::PLUGIN_GALLERY_XML_URL);
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PluginHelper.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PluginHelper
|
||||
*
|
||||
* @ingroup classes_plugins
|
||||
*
|
||||
* @brief Helper class implementing plugin administration functions.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\install\Install;
|
||||
use APP\install\Upgrade;
|
||||
use DirectoryIterator;
|
||||
use Exception;
|
||||
use Illuminate\Support\Arr;
|
||||
use PharData;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\Core;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\site\SiteDAO;
|
||||
use PKP\site\Version;
|
||||
use PKP\site\VersionCheck;
|
||||
use PKP\site\VersionDAO;
|
||||
use Throwable;
|
||||
|
||||
class PluginHelper
|
||||
{
|
||||
public const PLUGIN_ACTION_UPLOAD = 'upload';
|
||||
public const PLUGIN_ACTION_UPGRADE = 'upgrade';
|
||||
|
||||
public const PLUGIN_VERSION_FILE = 'version.xml';
|
||||
public const PLUGIN_INSTALL_FILE = 'install.xml';
|
||||
public const PLUGIN_UPGRADE_FILE = 'upgrade.xml';
|
||||
|
||||
/**
|
||||
* Extract the plugin, executes the callback, then cleanup the files
|
||||
*
|
||||
* @template T of mixed
|
||||
*
|
||||
* @param string $filePath Full path to plugin archive
|
||||
* @param string $originalFileName Original filename of plugin archive
|
||||
* @param callable(string $pluginDirectory):T $onExtracted The function will receive as parameter the directory of the plugin
|
||||
*
|
||||
* @throws Exception If any unexpected error happens (failure to unpack, absence of version.xml, etc)
|
||||
*
|
||||
* @return T Returns the result of the $onExtracted call
|
||||
*/
|
||||
private function extractPlugin(string $filePath, string $originalFileName, callable $onExtracted): mixed
|
||||
{
|
||||
$fileManager = new FileManager();
|
||||
$extension = $this->sanitizeFilename($fileManager->parseFileExtension($originalFileName));
|
||||
$baseName = $this->sanitizeFilename(basename($originalFileName, ".{$extension}")) ?: 'plugin';
|
||||
|
||||
// If the extension doesn't match the original one, copy (we don't know the original file) the file to another location to avoid issues with the PharData class
|
||||
$filePathWithExtension = null;
|
||||
if ($fileManager->parseFileExtension($filePath) !== $extension) {
|
||||
$filePathWithExtension = ($fileManager->getTemporaryFile($baseName, ".{$extension}"))->getPathname();
|
||||
$fileManager->copyFile($filePath, $filePathWithExtension) || throw new Exception('Failed to copy plugin file');
|
||||
}
|
||||
$extractPath = null;
|
||||
try {
|
||||
// Create a random directory to avoid symlink attacks.
|
||||
$extractPath = rtrim(sys_get_temp_dir(), '\\/') . "/{$baseName}" . substr(md5(random_int(0, PHP_INT_MAX)), 0, 10) . '/';
|
||||
$fileManager->mkdir($extractPath) || throw new Exception("Could not create directory {$extractPath}");
|
||||
|
||||
// Extract files
|
||||
(new PharData($filePathWithExtension ?? $filePath))->extractTo($extractPath, null, true);
|
||||
|
||||
// Ensure there's a file named "version.xml" at the main directory or at the direct sub-directories
|
||||
foreach (new DirectoryIterator($extractPath) as $current) {
|
||||
if ($current->isDir() && $current->getBasename() !== '..' && is_file(($path = "{$current->getPathname()}/") . static::PLUGIN_VERSION_FILE)) {
|
||||
return $onExtracted($path);
|
||||
}
|
||||
}
|
||||
throw new Exception(__('manager.plugins.invalidPluginArchive'));
|
||||
} finally {
|
||||
// Cleanup the extracted folder on failure and rethrow
|
||||
if ($extractPath) {
|
||||
$fileManager->rmtree($extractPath);
|
||||
}
|
||||
// Cleanup the temporary archive file in case it was created
|
||||
if ($filePathWithExtension) {
|
||||
unlink($filePathWithExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an extracted plugin
|
||||
*
|
||||
* @param string $path path to plugin archive
|
||||
* @param string $originalFileName Original filename of plugin archive
|
||||
*
|
||||
* @return Version Version of installed plugin on success
|
||||
*/
|
||||
public function installPlugin(string $path, string $originalFileName): Version
|
||||
{
|
||||
return $this->extractPlugin($path, $originalFileName, function (string $pluginFolder): Version {
|
||||
$fileManager = new FileManager();
|
||||
$versionFile = $pluginFolder . static::PLUGIN_VERSION_FILE;
|
||||
$pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile);
|
||||
/** @var VersionDAO */
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO');
|
||||
$installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct());
|
||||
$baseDir = Core::getBaseDir() . '/';
|
||||
$destinyPath = $baseDir . strtr($pluginVersion->getProductType(), '.', '/') . "/{$pluginVersion->getProduct()}";
|
||||
|
||||
if ($installedPlugin && is_dir($destinyPath)) {
|
||||
throw new Exception(
|
||||
$installedPlugin->compare($pluginVersion) < 0
|
||||
? __('manager.plugins.pleaseUpgrade')
|
||||
: __('manager.plugins.installedVersionNewest')
|
||||
);
|
||||
}
|
||||
|
||||
// Copy the plug-in from the temporary folder to the target folder.
|
||||
$fileManager->copyDir($pluginFolder, $destinyPath) || throw new Exception('Failed to copy plugin to destination folder');
|
||||
|
||||
try {
|
||||
// Upgrade the database with the new plug-in.
|
||||
$installFile = Arr::first(
|
||||
["{$destinyPath}/" . static::PLUGIN_INSTALL_FILE, $baseDir . PKP_LIB_PATH . '/xml/defaultPluginInstall.xml'],
|
||||
fn (string $path) => is_file($path)
|
||||
)
|
||||
?? throw new Exception('Missing installation file');
|
||||
|
||||
$siteDao = DAORegistry::getDAO('SiteDAO'); /** @var SiteDAO $siteDao */
|
||||
$site = $siteDao->getSite();
|
||||
$params = $this->_getConnectionParams();
|
||||
$params['locale'] = $site->getPrimaryLocale();
|
||||
$params['additionalLocales'] = $site->getSupportedLocales();
|
||||
$installer = new Install($params, $installFile, true);
|
||||
$installer->setCurrentVersion($pluginVersion);
|
||||
$installer->execute() || throw new Exception(__('manager.plugins.installFailed', ['errorString' => $installer->getErrorString()]));
|
||||
$versionDao->insertVersion($pluginVersion, true);
|
||||
return $pluginVersion;
|
||||
} catch (Throwable $e) {
|
||||
// Delete the plugin files on failure
|
||||
$fileManager->rmtree($destinyPath);
|
||||
throw $e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load database connection parameters into an array (needed for upgrade).
|
||||
*/
|
||||
protected function _getConnectionParams(): array
|
||||
{
|
||||
return [
|
||||
'connectionCharset' => Config::getVar('i18n', 'connection_charset'),
|
||||
'databaseDriver' => Config::getVar('database', 'driver'),
|
||||
'databaseHost' => Config::getVar('database', 'host'),
|
||||
'databasePort' => Config::getVar('database', 'port'),
|
||||
'unixSocket' => Config::getVar('database', 'unix_socket'),
|
||||
'databaseUsername' => Config::getVar('database', 'username'),
|
||||
'databasePassword' => Config::getVar('database', 'password'),
|
||||
'databaseName' => Config::getVar('database', 'name')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade a plugin to a newer version from the user's filesystem
|
||||
*
|
||||
* @param string $path path to plugin archive
|
||||
* @param string $originalFileName Original filename of plugin archive
|
||||
*
|
||||
*/
|
||||
public function upgradePlugin(string $category, string $plugin, string $path, string $originalFileName): Version
|
||||
{
|
||||
return $this->extractPlugin($path, $originalFileName, function (string $pluginFolder) use ($category, $plugin): Version {
|
||||
$fileManager = new FileManager();
|
||||
$versionFile = $pluginFolder . static::PLUGIN_VERSION_FILE;
|
||||
$pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile);
|
||||
|
||||
// Check whether the uploaded plug-in fits the original plug-in.
|
||||
if ("plugins.{$category}" !== $pluginVersion->getProductType()) {
|
||||
throw new Exception(__('manager.plugins.wrongCategory'));
|
||||
}
|
||||
|
||||
if ($plugin !== $pluginVersion->getProduct()) {
|
||||
throw new Exception(__('manager.plugins.wrongName'));
|
||||
}
|
||||
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
|
||||
$installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct());
|
||||
if (!$installedPlugin) {
|
||||
throw new Exception(__('manager.plugins.pleaseInstall'));
|
||||
}
|
||||
|
||||
if ($installedPlugin->compare($pluginVersion) >= 0) {
|
||||
throw new Exception(__('manager.plugins.installedVersionNewer'));
|
||||
}
|
||||
|
||||
$destinyPath = Core::getBaseDir() . "/plugins/{$category}/{$plugin}";
|
||||
|
||||
// Delete existing files.
|
||||
$fileManager->rmtree($destinyPath);
|
||||
|
||||
// Check whether deleting has worked.
|
||||
if (is_dir($destinyPath)) {
|
||||
throw new Exception(__('manager.plugins.deleteError', ['pluginName' => $pluginVersion->getProduct()]));
|
||||
}
|
||||
|
||||
// Copy the plug-in from the temporary folder to the target folder.
|
||||
$fileManager->copyDir($pluginFolder, $destinyPath) || throw new Exception('Could not copy plugin to destination!');
|
||||
|
||||
try {
|
||||
$upgradeFile = "{$destinyPath}/" . static::PLUGIN_UPGRADE_FILE;
|
||||
if ($fileManager->fileExists($upgradeFile)) {
|
||||
/** @var SiteDAO */
|
||||
$siteDao = DAORegistry::getDAO('SiteDAO');
|
||||
$site = $siteDao->getSite();
|
||||
$params = $this->_getConnectionParams();
|
||||
$params['locale'] = $site->getPrimaryLocale();
|
||||
$params['additionalLocales'] = $site->getSupportedLocales();
|
||||
$installer = new Upgrade($params, $upgradeFile, true);
|
||||
// Run the upgrade/migration
|
||||
$installer->execute() || throw new Exception(__('manager.plugins.upgradeFailed', ['errorString' => $installer->getErrorString()]));
|
||||
}
|
||||
|
||||
// Add the new version to the database
|
||||
$pluginVersion->setCurrent(1);
|
||||
$versionDao->insertVersion($pluginVersion, true);
|
||||
return $pluginVersion;
|
||||
} catch (Throwable $e) {
|
||||
// Delete the plugin files on failure
|
||||
$fileManager->rmtree($destinyPath);
|
||||
throw $e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops risky characters from a filename
|
||||
*/
|
||||
private function sanitizeFilename(string $filename): string
|
||||
{
|
||||
return preg_replace('/[^\w.-]/', '', $filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PluginHelper', '\PluginHelper');
|
||||
foreach ([
|
||||
'PLUGIN_ACTION_UPLOAD',
|
||||
'PLUGIN_ACTION_UPGRADE',
|
||||
'PLUGIN_VERSION_FILE',
|
||||
'PLUGIN_INSTALL_FILE',
|
||||
'PLUGIN_UPGRADE_FILE',
|
||||
] as $constantName) {
|
||||
define($constantName, constant('\PluginHelper::' . $constantName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PluginRegistry.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PluginRegistry
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @see Plugin
|
||||
*
|
||||
* @brief Registry class for managing plugins.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use Exception;
|
||||
use FilesystemIterator;
|
||||
use Illuminate\Support\Arr;
|
||||
use PKP\core\Registry;
|
||||
use ReflectionObject;
|
||||
|
||||
class PluginRegistry
|
||||
{
|
||||
/** Base path of plugins */
|
||||
public const PLUGINS_PREFIX = 'plugins/';
|
||||
|
||||
/**
|
||||
* Return all plugins in the given category as an array, or, if the
|
||||
* category is not specified, all plugins in an associative array of
|
||||
* arrays by category.
|
||||
*/
|
||||
public static function &getPlugins(?string $category = null): array
|
||||
{
|
||||
$plugins = & Registry::get('plugins', true, []); // Reference necessary
|
||||
if ($category !== null) {
|
||||
$plugins[$category] ??= [];
|
||||
return $plugins[$category];
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all plugins in a single array.
|
||||
*/
|
||||
public static function getAllPlugins(): array
|
||||
{
|
||||
return array_reduce(static::getPlugins(), fn (array $output, array $pluginsByCategory) => $output += $pluginsByCategory, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a plugin with the registry in the given category.
|
||||
*
|
||||
* @param string $category the name of the category to extend
|
||||
* @param Plugin $plugin The instantiated plugin to add
|
||||
* @param string $path The path the plugin was found in
|
||||
* @param int $mainContextId To identify enabled plug-ins
|
||||
* we need a context. This context is usually taken from the
|
||||
* request but sometimes there is no context in the request
|
||||
* (e.g. when executing CLI commands). Then the main context
|
||||
* can be given as an explicit ID.
|
||||
*
|
||||
* @return bool True IFF the plugin was registered successfully
|
||||
*/
|
||||
public static function register(string $category, Plugin $plugin, string $path, ?int $mainContextId = null): bool
|
||||
{
|
||||
$pluginName = $plugin->getName();
|
||||
$plugins = & static::getPlugins();
|
||||
|
||||
// If the plugin is already loaded or failed/refused to register
|
||||
if (isset($plugins[$category][$pluginName]) || !$plugin->register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugins[$category][$pluginName] = $plugin;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plugin by category and name.
|
||||
*/
|
||||
public static function getPlugin(string $category, string $name): ?Plugin
|
||||
{
|
||||
return static::getPlugins()[$category][$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all plugins for a given category.
|
||||
*
|
||||
* @param string $category The name of the category to load
|
||||
* @param bool $enabledOnly if true load only enabled
|
||||
* plug-ins (db-installation required), otherwise look on
|
||||
* disk and load all available plug-ins (no db required).
|
||||
* @param int $mainContextId To identify enabled plug-ins
|
||||
* we need a context. This context is usually taken from the
|
||||
* request but sometimes there is no context in the request
|
||||
* (e.g. when executing CLI commands). Then the main context
|
||||
* can be given as an explicit ID.
|
||||
*
|
||||
* @return array Set of plugins, sorted in sequence.
|
||||
*/
|
||||
public static function loadCategory(string $category, bool $enabledOnly = false, ?int $mainContextId = null): array
|
||||
{
|
||||
static $cache;
|
||||
$key = implode("\0", func_get_args());
|
||||
$plugins = $cache[$key] ??= $enabledOnly && Application::isInstalled()
|
||||
? static::_loadFromDatabase($category, $mainContextId)
|
||||
: static::_loadFromDisk($category);
|
||||
|
||||
// Fire a hook prior to registering plugins for a category
|
||||
// n.b.: this should not be used from a PKPPlugin::register() call to "jump categories"
|
||||
Hook::call('PluginRegistry::loadCategory', [&$category, &$plugins]);
|
||||
|
||||
// Register the plugins in sequence.
|
||||
ksort($plugins);
|
||||
array_walk_recursive($plugins, fn (Plugin $plugin, string $pluginPath) => static::register($category, $plugin, $pluginPath, $mainContextId));
|
||||
|
||||
// Return the list of successfully-registered plugins.
|
||||
$plugins = & static::getPlugins($category);
|
||||
|
||||
// Fire a hook after all plugins of a category have been loaded, so they
|
||||
// are able to interact if required
|
||||
Hook::call("PluginRegistry::categoryLoaded::{$category}", [&$plugins]);
|
||||
|
||||
// Sort the plugins by priority before returning.
|
||||
uasort($plugins, fn (Plugin $a, Plugin $b) => $a->getSeq() - $b->getSeq());
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific plugin from a category by path name.
|
||||
* Similar to loadCategory, except that it only loads a single plugin
|
||||
* within a category rather than loading all.
|
||||
*
|
||||
* @param int $mainContextId To identify enabled plug-ins
|
||||
* we need a context. This context is usually taken from the
|
||||
* request but sometimes there is no context in the request
|
||||
* (e.g. when executing CLI commands). Then the main context
|
||||
* can be given as an explicit ID.
|
||||
*/
|
||||
public static function loadPlugin(string $category, string $pluginName, ?int $mainContextId = null): ?Plugin
|
||||
{
|
||||
if ($plugin = static::_instantiatePlugin($category, $pluginName)) {
|
||||
static::register($category, $plugin, self::PLUGINS_PREFIX . "{$category}/{$pluginName}", $mainContextId);
|
||||
}
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the various plugin categories available.
|
||||
*
|
||||
* NB: The categories are returned in the order in which they
|
||||
* have to be registered and/or installed. Plug-ins in categories
|
||||
* later in the list may depend on plug-ins in earlier
|
||||
* categories.
|
||||
*/
|
||||
public static function getCategories(): array
|
||||
{
|
||||
$categories = Application::get()->getPluginCategories();
|
||||
Hook::call('PluginRegistry::getCategories', [&$categories]);
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all plugins in the system and return them in a single array.
|
||||
*/
|
||||
public static function loadAllPlugins(bool $enabledOnly = false): array
|
||||
{
|
||||
// Retrieve and register categories (order is significant).
|
||||
$categories = static::getCategories();
|
||||
return array_reduce($categories, fn (array $plugins, string $category) => $plugins + static::loadCategory($category, $enabledOnly), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a plugin.
|
||||
*/
|
||||
private static function _instantiatePlugin(string $category, string $pluginName, ?string $classToCheck = null): ?Plugin
|
||||
{
|
||||
if (!preg_match('/^[a-z0-9]+$/i', $pluginName)) {
|
||||
throw new Exception("Invalid product name \"{$pluginName}\"");
|
||||
}
|
||||
|
||||
// First, try a namespaced class name matching the installation directory.
|
||||
$pluginClassName = "\\APP\\plugins\\{$category}\\{$pluginName}\\" . ucfirst($pluginName) . 'Plugin';
|
||||
$plugin = class_exists($pluginClassName)
|
||||
? new $pluginClassName()
|
||||
: static::_deprecatedInstantiatePlugin($category, $pluginName);
|
||||
|
||||
$classToCheck = $classToCheck ?: Plugin::class;
|
||||
$isObject = is_object($plugin);
|
||||
// Complements $classToCheck with a namespace when needed
|
||||
if (!str_contains($classToCheck, '\\') && $isObject && ($reflection = new ReflectionObject($plugin))->inNamespace()) {
|
||||
$classToCheck = "{$reflection->getNamespaceName()}\\{$classToCheck}";
|
||||
}
|
||||
if ($plugin !== null && !($plugin instanceof $classToCheck)) {
|
||||
$type = $isObject ? $plugin::class : gettype($plugin);
|
||||
error_log(new Exception("Plugin {$pluginName} expected to inherit from {$classToCheck}, actual type {$type}"));
|
||||
return null;
|
||||
}
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve plugins from the database.
|
||||
*/
|
||||
private static function _loadFromDatabase(string $category, ?int $mainContextId = null): array
|
||||
{
|
||||
$plugins = [];
|
||||
$categoryDir = static::PLUGINS_PREFIX . $category;
|
||||
$products = Application::get()->getEnabledProducts("plugins.{$category}", $mainContextId);
|
||||
foreach ($products as $product) {
|
||||
$name = $product->getProduct();
|
||||
if ($plugin = static::_instantiatePlugin($category, $name, $product->getProductClassname())) {
|
||||
$plugins[$plugin->getSeq()]["{$categoryDir}/{$name}"] = $plugin;
|
||||
}
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all plug-ins from disk without querying the database, used during installation.
|
||||
*/
|
||||
private static function _loadFromDisk(string $category): array
|
||||
{
|
||||
$categoryDir = static::PLUGINS_PREFIX . $category;
|
||||
if (!is_dir($categoryDir)) {
|
||||
return [];
|
||||
}
|
||||
$plugins = [];
|
||||
foreach (new FilesystemIterator($categoryDir) as $path) {
|
||||
if (!$path->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$pluginName = $path->getFilename();
|
||||
if ($plugin = static::_instantiatePlugin($category, $pluginName)) {
|
||||
$plugins[$plugin->getSeq()]["{$categoryDir}/{$pluginName}"] = $plugin;
|
||||
}
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a plugin.
|
||||
*
|
||||
* @deprecated 3.4.0 Old way to instantiate a plugin
|
||||
*/
|
||||
private static function _deprecatedInstantiatePlugin(string $category, string $pluginName): ?Plugin
|
||||
{
|
||||
$pluginPath = static::PLUGINS_PREFIX . "{$category}/{$pluginName}";
|
||||
// Try the plug-in wrapper for backwards compatibility.
|
||||
$pluginWrapper = "{$pluginPath}/index.php";
|
||||
if (file_exists($pluginWrapper)) {
|
||||
return include $pluginWrapper;
|
||||
}
|
||||
|
||||
// Try the well-known plug-in class name next (with and without ".inc.php")
|
||||
$pluginClassName = ucfirst($pluginName) . ucfirst($category) . 'Plugin';
|
||||
if (Arr::first(['.inc.php', '.php'], fn (string $suffix) => file_exists("{$pluginPath}/{$pluginClassName}{$suffix}"))) {
|
||||
$pluginPackage = "plugins.{$category}.{$pluginName}";
|
||||
return instantiate("{$pluginPackage}.{$pluginClassName}", $pluginClassName, $pluginPackage, 'register');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PluginRegistry', '\PluginRegistry');
|
||||
define('PLUGINS_PREFIX', PluginRegistry::PLUGINS_PREFIX);
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PluginSettingsDAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PluginSettingsDAO
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @see Plugin
|
||||
*
|
||||
* @brief Operations for retrieving and modifying plugin settings.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PKP\cache\CacheManager;
|
||||
use PKP\cache\GenericCache;
|
||||
use PKP\xml\PKPXMLParser;
|
||||
|
||||
class PluginSettingsDAO extends \PKP\db\DAO
|
||||
{
|
||||
/**
|
||||
* Get the cache for plugin settings.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $pluginName Plugin symbolic name
|
||||
*
|
||||
* @return GenericCache
|
||||
*/
|
||||
public function _getCache($contextId, $pluginName)
|
||||
{
|
||||
static $settingCache = [];
|
||||
return $settingCache[(int) $contextId][$pluginName] ??= CacheManager::getManager()->getCache(
|
||||
'pluginSettings-' . $contextId,
|
||||
$pluginName,
|
||||
[$this, '_cacheMiss']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a plugin setting value.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $pluginName Plugin symbolic name
|
||||
* @param string $name Setting name
|
||||
*/
|
||||
public function getSetting($contextId, $pluginName, $name)
|
||||
{
|
||||
// Normalize the plug-in name to lower case.
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
|
||||
// Retrieve the setting.
|
||||
$cache = $this->_getCache($contextId, $pluginName);
|
||||
return $cache->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the plugin setting exist.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $pluginName Plugin symbolic name
|
||||
* @param string $name Setting name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function settingExists($contextId, $pluginName, $name)
|
||||
{
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
$result = $this->retrieve(
|
||||
'SELECT COUNT(*) AS row_count FROM plugin_settings WHERE plugin_name = ? AND context_id = ? AND setting_name = ?',
|
||||
[$pluginName, (int) $contextId, $name]
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row ? (bool) $row->row_count : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for a cache miss.
|
||||
*
|
||||
* @param object $cache Cache object
|
||||
* @param string $id Identifier to look up in cache
|
||||
*/
|
||||
public function _cacheMiss($cache, $id)
|
||||
{
|
||||
$contextParts = explode('-', $cache->getContext());
|
||||
$contextId = array_pop($contextParts);
|
||||
$settings = $this->getPluginSettings($contextId, $cache->getCacheId());
|
||||
if (!isset($settings[$id])) {
|
||||
// Make sure that even null values are cached
|
||||
$cache->setCache($id, null);
|
||||
return null;
|
||||
}
|
||||
return $settings[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and cache all settings for a plugin.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $pluginName Plugin symbolic name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginSettings($contextId, $pluginName)
|
||||
{
|
||||
// Normalize plug-in name to lower case.
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
|
||||
$result = $this->retrieve(
|
||||
'SELECT setting_name, setting_value, setting_type FROM plugin_settings WHERE plugin_name = ? AND context_id = ?',
|
||||
[$pluginName, (int) $contextId]
|
||||
);
|
||||
|
||||
$pluginSettings = [];
|
||||
foreach ($result as $row) {
|
||||
$pluginSettings[$row->setting_name] = $this->convertFromDB($row->setting_value, $row->setting_type);
|
||||
}
|
||||
|
||||
$cache = $this->_getCache($contextId, $pluginName);
|
||||
$cache->setEntireCache($pluginSettings);
|
||||
|
||||
return $pluginSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/update a plugin setting.
|
||||
*
|
||||
* @param int $contextId Context ID
|
||||
* @param string $pluginName Symbolic plugin name
|
||||
* @param string $name Setting name
|
||||
* @param mixed $value Setting value
|
||||
* @param string $type data type of the setting. If omitted, type will be guessed
|
||||
*/
|
||||
public function updateSetting($contextId, $pluginName, $name, $value, $type = null)
|
||||
{
|
||||
// Normalize the plug-in name to lower case.
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
|
||||
$cache = $this->_getCache($contextId, $pluginName);
|
||||
$cache->setCache($name, $value);
|
||||
|
||||
$value = $this->convertToDB($value, $type);
|
||||
|
||||
DB::table('plugin_settings')->updateOrInsert(
|
||||
['context_id' => (int) $contextId, 'plugin_name' => $pluginName, 'setting_name' => $name],
|
||||
['setting_value' => $value, 'setting_type' => $type]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a plugin setting.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param int $pluginName
|
||||
* @param string $name
|
||||
*/
|
||||
public function deleteSetting($contextId, $pluginName, $name)
|
||||
{
|
||||
// Normalize the plug-in name to lower case.
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
|
||||
$cache = $this->_getCache($contextId, $pluginName);
|
||||
$cache->setCache($name, null);
|
||||
|
||||
return $this->update(
|
||||
'DELETE FROM plugin_settings WHERE plugin_name = ? AND setting_name = ? AND context_id = ?',
|
||||
[$pluginName, $name, (int) $contextId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all settings for a plugin.
|
||||
*
|
||||
* @param int $contextId
|
||||
* @param string $pluginName
|
||||
*/
|
||||
public function deleteSettingsByPlugin($contextId, $pluginName)
|
||||
{
|
||||
// Normalize the plug-in name to lower case.
|
||||
$pluginName = strtolower_codesafe($pluginName);
|
||||
|
||||
$cache = $this->_getCache($contextId, $pluginName);
|
||||
$cache->flush();
|
||||
|
||||
return $this->update(
|
||||
'DELETE FROM plugin_settings WHERE context_id = ? AND plugin_name = ?',
|
||||
[(int) $contextId, $pluginName]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all settings for a context.
|
||||
*
|
||||
* @param int $contextId
|
||||
*/
|
||||
public function deleteByContextId($contextId)
|
||||
{
|
||||
return $this->update(
|
||||
'DELETE FROM plugin_settings WHERE context_id = ?',
|
||||
[(int) $contextId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by installSettings to perform variable and translation replacements.
|
||||
*
|
||||
* @param string $rawInput contains text including variable and/or translate replacements.
|
||||
* @param array $paramArray contains variables for replacement
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _performReplacement($rawInput, $paramArray = [])
|
||||
{
|
||||
$value = preg_replace_callback('{{translate key="([^"]+)"}}', fn ($matches) => __($matches[1]), (string) $rawInput);
|
||||
foreach ($paramArray as $pKey => $pValue) {
|
||||
$value = str_replace('{$' . $pKey . '}', $pValue, $value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by installSettings to recursively build nested arrays.
|
||||
* Deals with translation and variable replacement calls.
|
||||
*
|
||||
* @param object $node XMLNode <array> tag
|
||||
* @param array $paramArray Parameters to be replaced in key/value contents
|
||||
*/
|
||||
public function _buildObject($node, $paramArray = [])
|
||||
{
|
||||
$value = [];
|
||||
foreach ($node->getChildren() as $element) {
|
||||
$key = $element->getAttribute('key');
|
||||
$childArray = $element->getChildByName('array');
|
||||
if (isset($childArray)) {
|
||||
$content = $this->_buildObject($childArray, $paramArray);
|
||||
} else {
|
||||
$content = $this->_performReplacement($element->getValue(), $paramArray);
|
||||
}
|
||||
if (!empty($key)) {
|
||||
$key = $this->_performReplacement($key, $paramArray);
|
||||
$value[$key] = $content;
|
||||
} else {
|
||||
$value[] = $content;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install plugin settings from an XML file.
|
||||
*
|
||||
* @param string $pluginName name of plugin for settings to apply to
|
||||
* @param string $filename Name of XML file to parse and install
|
||||
* @param array $paramArray Optional parameters for variable replacement in settings
|
||||
*/
|
||||
public function installSettings($contextId, $pluginName, $filename, $paramArray = [])
|
||||
{
|
||||
$xmlParser = new PKPXMLParser();
|
||||
$tree = $xmlParser->parse($filename);
|
||||
|
||||
if (!$tree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for existing settings and leave them if they are already in place.
|
||||
$currentSettings = $this->getPluginSettings($contextId, $pluginName);
|
||||
|
||||
foreach ($tree->getChildren() as $setting) {
|
||||
$nameNode = $setting->getChildByName('name');
|
||||
$valueNode = $setting->getChildByName('value');
|
||||
|
||||
if (isset($nameNode) && isset($valueNode)) {
|
||||
$type = $setting->getAttribute('type');
|
||||
$name = $nameNode->getValue();
|
||||
|
||||
// If the setting already exists, respect it.
|
||||
if (isset($currentSettings[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type == 'object') {
|
||||
$arrayNode = $valueNode->getChildByName('array');
|
||||
$value = $this->_buildObject($arrayNode, $paramArray);
|
||||
} else {
|
||||
$value = $this->_performReplacement($valueNode->getValue(), $paramArray);
|
||||
}
|
||||
|
||||
// Replace translate calls with translated content
|
||||
$this->updateSetting($contextId, $pluginName, $name, $value, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by plugin setting installation code to perform translation
|
||||
* function.
|
||||
*
|
||||
* @param array $matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function _installer_plugin_regexp_callback($matches)
|
||||
{
|
||||
return __($matches[1]);
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\PluginSettingsDAO', '\PluginSettingsDAO');
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/ReportPlugin.php
|
||||
*
|
||||
* Copyright (c) 2013-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ReportPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Abstract class for report plugins
|
||||
*/
|
||||
|
||||
namespace PKP\plugins;
|
||||
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\RedirectAction;
|
||||
|
||||
abstract class ReportPlugin extends Plugin
|
||||
{
|
||||
//
|
||||
// Public methods.
|
||||
//
|
||||
/**
|
||||
* @copydoc Plugin::getActions()
|
||||
*/
|
||||
public function getActions($request, $actionArgs)
|
||||
{
|
||||
$dispatcher = $request->getDispatcher();
|
||||
return array_merge(
|
||||
$this->getEnabled() ? [
|
||||
new LinkAction(
|
||||
'settings',
|
||||
new RedirectAction($dispatcher->url(
|
||||
$request,
|
||||
PKPApplication::ROUTE_PAGE,
|
||||
null,
|
||||
'stats',
|
||||
'reports',
|
||||
'report',
|
||||
['pluginName' => $this->getName()]
|
||||
)),
|
||||
__('manager.statistics.reports'),
|
||||
null
|
||||
)
|
||||
] : [],
|
||||
parent::getActions($request, $actionArgs)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the report
|
||||
*
|
||||
* @param array $args
|
||||
* @param PKPRequest $request
|
||||
*/
|
||||
abstract public function display($args, $request);
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\ReportPlugin', '\ReportPlugin');
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,732 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup classes_plugins_importexport import/export deployment
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/plugins/importexport/PKPImportExportDeployment.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPImportExportDeployment
|
||||
*
|
||||
* @ingroup plugins_importexport
|
||||
*
|
||||
* @brief Base class configuring the import/export process to an
|
||||
* application's specifics.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\publication\Publication;
|
||||
use APP\submission\Submission;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use InvalidArgumentException;
|
||||
use LibXMLError;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\user\User;
|
||||
|
||||
class PKPImportExportDeployment
|
||||
{
|
||||
/** @var array Array of possible validation errors */
|
||||
private $xmlValidationErrors = [];
|
||||
|
||||
/** @var bool Indicator that the import/export process has failed */
|
||||
private $processFailed = false;
|
||||
|
||||
/** @var mixed The import/export process result */
|
||||
public $processResult = null;
|
||||
|
||||
/** @var Context The current import/export context */
|
||||
private Context $_context;
|
||||
|
||||
/** @var ?User The current import/export user */
|
||||
private $_user;
|
||||
|
||||
/** @var ?Submission The current import/export submission */
|
||||
private $_submission;
|
||||
|
||||
/** @var Publication The current import/export publication */
|
||||
private ?Publication $_publication;
|
||||
|
||||
/** @var array The processed import objects IDs */
|
||||
private $_processedObjectsIds = [];
|
||||
|
||||
/** @var array Warnings keyed by object IDs */
|
||||
private $_processedObjectsErrors = [];
|
||||
|
||||
/** @var array Errors keyed by object IDs */
|
||||
private $_processedObjectsWarnings = [];
|
||||
|
||||
/** @var array Connection between the file from the XML import file and the new IDs after they are imported */
|
||||
private $_fileDBIds;
|
||||
|
||||
/** @var array Connection between the submission file IDs from the XML import file and the new IDs after they are imported */
|
||||
private $_submissionFileDBIds;
|
||||
|
||||
/** @var array Connection between the author id from the XML import file and the DB file IDs */
|
||||
private $_authorDBIds;
|
||||
|
||||
/** @var string Base path for the import source */
|
||||
private $_baseImportPath = '';
|
||||
|
||||
/** @var array A list of imported root elements to display to the user after the import is complete */
|
||||
private $_importedRootEntities;
|
||||
|
||||
/** @var array A list of exported root elements to display to the user after the export is complete */
|
||||
private $_exportRootEntities;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Context $context
|
||||
* @param ?User $user optional
|
||||
*/
|
||||
public function __construct($context, $user = null)
|
||||
{
|
||||
$this->setContext($context);
|
||||
$this->setUser($user);
|
||||
$this->setSubmission(null);
|
||||
$this->setPublication(null);
|
||||
$this->setFileDBIds([]);
|
||||
$this->setSubmissionFileDBIds([]);
|
||||
$this->_processedObjectsIds = [];
|
||||
$this->_importedRootEntities = [];
|
||||
}
|
||||
|
||||
//
|
||||
// Deployment items for subclasses to override
|
||||
//
|
||||
/**
|
||||
* Get the submission node name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionNodeName()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submissions node name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionsNodeName()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the representation node name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRepresentationNodeName()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the namespace URN
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the schema filename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchemaFilename()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getter/setters
|
||||
//
|
||||
/**
|
||||
* Set the import/export context.
|
||||
*
|
||||
* @param Context $context
|
||||
*/
|
||||
public function setContext($context)
|
||||
{
|
||||
$this->_context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import/export context.
|
||||
*
|
||||
*/
|
||||
public function getContext(): Context
|
||||
{
|
||||
return $this->_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import/export submission.
|
||||
*/
|
||||
public function setSubmission(?Submission $submission)
|
||||
{
|
||||
$this->_submission = $submission;
|
||||
if ($submission) {
|
||||
$this->addProcessedObjectId(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import/export submission.
|
||||
*/
|
||||
public function getSubmission(): ?Submission
|
||||
{
|
||||
return $this->_submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import/export publication.
|
||||
*/
|
||||
public function setPublication(?Publication $publication)
|
||||
{
|
||||
$this->_publication = $publication;
|
||||
if ($publication) {
|
||||
$this->addProcessedObjectId(PKPApplication::ASSOC_TYPE_PUBLICATION, $publication->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import/export publication.
|
||||
*
|
||||
* @return Publication
|
||||
*/
|
||||
public function getPublication(): ?Publication
|
||||
{
|
||||
return $this->_publication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the processed object ID.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
* @param int $assocId
|
||||
*/
|
||||
public function addProcessedObjectId($assocType, $assocId)
|
||||
{
|
||||
$this->_processedObjectsIds[$assocType][] = $assocId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the error message to the processed object ID.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
* @param ?int $assocId
|
||||
* @param string $errorMsg
|
||||
*/
|
||||
public function addError($assocType, $assocId, $errorMsg)
|
||||
{
|
||||
if (($this->getObjectTypes()[$assocType] ?? null) === null) {
|
||||
error_log(new InvalidArgumentException("Invalid assocType \"{$assocType}\""));
|
||||
}
|
||||
$this->_processedObjectsErrors[$assocType][$assocId][] = $errorMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the warning message to the processed object ID.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
* @param int $assocId
|
||||
* @param string $warningMsg
|
||||
*/
|
||||
public function addWarning($assocType, $assocId, $warningMsg)
|
||||
{
|
||||
if (($this->getObjectTypes()[$assocType] ?? null) === null) {
|
||||
error_log(new InvalidArgumentException("Invalid assocType \"{$assocType}\""));
|
||||
}
|
||||
$this->_processedObjectsWarnings[$assocType][$assocId][] = $warningMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the processed objects IDs.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
public function getProcessedObjectsIds($assocType)
|
||||
{
|
||||
if (array_key_exists($assocType, $this->_processedObjectsIds)) {
|
||||
return $this->_processedObjectsIds[$assocType];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the processed objects errors.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
public function getProcessedObjectsErrors($assocType)
|
||||
{
|
||||
if (array_key_exists($assocType, $this->_processedObjectsErrors)) {
|
||||
return $this->_processedObjectsErrors[$assocType];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get the processed objects errors.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
|
||||
public function getProcessedObjectsWarnings($assocType)
|
||||
{
|
||||
if (array_key_exists($assocType, $this->_processedObjectsWarnings)) {
|
||||
return $this->_processedObjectsWarnings[$assocType];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the processed objects.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*/
|
||||
public function removeImportedObjects($assocType)
|
||||
{
|
||||
switch ($assocType) {
|
||||
case PKPApplication::ASSOC_TYPE_SUBMISSION:
|
||||
$processedSubmissionsIds = $this->getProcessedObjectsIds(PKPApplication::ASSOC_TYPE_SUBMISSION);
|
||||
if (!empty($processedSubmissionsIds)) {
|
||||
foreach ($processedSubmissionsIds as $submissionId) {
|
||||
if ($submissionId) {
|
||||
Repo::submission()->dao->deleteById($submissionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import/export user.
|
||||
*
|
||||
* @param ?User $user
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
$this->_user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import/export user.
|
||||
*
|
||||
* @return ?User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of the inserted file DB Ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFileDBIds()
|
||||
{
|
||||
return $this->_fileDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the array of the inserted file DB Ids.
|
||||
*
|
||||
* @param array $fileDBIds
|
||||
*/
|
||||
public function setFileDBIds($fileDBIds)
|
||||
{
|
||||
return $this->_fileDBIds = $fileDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file DB Id.
|
||||
*
|
||||
* @param int $fileId The old file id
|
||||
*
|
||||
* @return ?int The new file id
|
||||
*/
|
||||
public function getFileDBId($fileId)
|
||||
{
|
||||
if (array_key_exists($fileId, $this->_fileDBIds)) {
|
||||
return $this->_fileDBIds[$fileId];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file DB Id.
|
||||
*
|
||||
* @param int $fileId The old file id
|
||||
* @param int $DBId The new file id
|
||||
*/
|
||||
public function setFileDBId($fileId, $DBId)
|
||||
{
|
||||
return $this->_fileDBIds[$fileId] = $DBId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of the inserted submission file DB Ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSubmissionFileDBIds()
|
||||
{
|
||||
return $this->_submissionFileDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the array of the inserted submission file DB Ids.
|
||||
*
|
||||
* @param array $submissionFileDBIds
|
||||
*/
|
||||
public function setSubmissionFileDBIds($submissionFileDBIds)
|
||||
{
|
||||
return $this->_submissionFileDBIds = $submissionFileDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submission file DB Id.
|
||||
*
|
||||
* @return ?int The new submission file id
|
||||
*/
|
||||
public function getSubmissionFileDBId($submissionFileDBId)
|
||||
{
|
||||
if (array_key_exists($submissionFileDBId, $this->_submissionFileDBIds)) {
|
||||
return $this->_submissionFileDBIds[$submissionFileDBId];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the submission file DB Id.
|
||||
*
|
||||
* @param int $submissionFileDBId The old submission file id
|
||||
* @param int $DBId The new submission file id
|
||||
*/
|
||||
public function setSubmissionFileDBId($submissionFileDBId, $DBId)
|
||||
{
|
||||
return $this->_submissionFileDBIds[$submissionFileDBId] = $DBId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the array of the inserted author DB Ids.
|
||||
*
|
||||
* @param array $authorDBIds
|
||||
*/
|
||||
public function setAuthorDBIds($authorDBIds)
|
||||
{
|
||||
return $this->_authorDBIds = $authorDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of the inserted author DB Ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthorDBIds()
|
||||
{
|
||||
return $this->_authorDBIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author DB Id.
|
||||
*
|
||||
* @param int $authorId
|
||||
*
|
||||
* @return ?int
|
||||
*/
|
||||
public function getAuthorDBId($authorId)
|
||||
{
|
||||
if (array_key_exists($authorId, $this->_authorDBIds)) {
|
||||
return $this->_authorDBIds[$authorId];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the author DB Id.
|
||||
*
|
||||
* @param int $authorId
|
||||
* @param int $DBId
|
||||
*/
|
||||
public function setAuthorDBId($authorId, $DBId)
|
||||
{
|
||||
return $this->_authorDBIds[$authorId] = $DBId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory location for the import source
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function setImportPath($path)
|
||||
{
|
||||
$this->_baseImportPath = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory location for the import source
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImportPath()
|
||||
{
|
||||
return $this->_baseImportPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the imported root entities.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*/
|
||||
public function addImportedRootEntity($assocType, $entity)
|
||||
{
|
||||
$this->_importedRootEntities[$assocType][] = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the imported root entities.
|
||||
*
|
||||
* @param int $assocType Application::ASSOC_TYPE_...
|
||||
*/
|
||||
public function getImportedRootEntities($assocType)
|
||||
{
|
||||
if (array_key_exists($assocType, $this->_importedRootEntities)) {
|
||||
return $this->_importedRootEntities[$assocType];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set export root entities
|
||||
*
|
||||
* @param array $exportRootEntities
|
||||
*/
|
||||
public function setExportRootEntities($exportRootEntities)
|
||||
{
|
||||
$this->_exportRootEntities = $exportRootEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get export root entities
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExportRootEntities()
|
||||
{
|
||||
return $this->_exportRootEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the import process
|
||||
*
|
||||
* @param string $rootFilter
|
||||
* @param string $importXml
|
||||
*/
|
||||
public function import($rootFilter, $importXml)
|
||||
{
|
||||
$dbConnection = DB::connection();
|
||||
try {
|
||||
$currentFilter = PKPImportExportFilter::getFilter($rootFilter, $this);
|
||||
|
||||
$dbConnection->beginTransaction();
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$result = $currentFilter->execute($importXml);
|
||||
|
||||
$dbConnection->commit();
|
||||
|
||||
$this->processResult = $result;
|
||||
} catch (Error | Exception $e) {
|
||||
$this->addError(PKPApplication::ASSOC_TYPE_NONE, 0, $e->getMessage());
|
||||
$dbConnection->rollBack();
|
||||
|
||||
$this->processFailed = true;
|
||||
} finally {
|
||||
$this->xmlValidationErrors = array_filter(libxml_get_errors(), fn (LibXMLError $error) => in_array($error->level, [LIBXML_ERR_ERROR, LIBXML_ERR_FATAL]));
|
||||
libxml_clear_errors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the export process
|
||||
*
|
||||
* @param string $rootFilter
|
||||
* @param array $exportObjects
|
||||
* @param array $opts
|
||||
*/
|
||||
public function export($rootFilter, $exportObjects, $opts = [])
|
||||
{
|
||||
try {
|
||||
$this->setExportRootEntities($exportObjects);
|
||||
|
||||
$currentFilter = PKPImportExportFilter::getFilter($rootFilter, $this, $opts);
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$result = $currentFilter->execute($exportObjects, true);
|
||||
|
||||
$this->xmlValidationErrors = array_filter(libxml_get_errors(), function ($a) {
|
||||
return $a->level == LIBXML_ERR_ERROR || $a->level == LIBXML_ERR_FATAL;
|
||||
});
|
||||
|
||||
libxml_clear_errors();
|
||||
|
||||
if (!$result) {
|
||||
$this->addError(PKPApplication::ASSOC_TYPE_NONE, 0, 'Export result is empty.');
|
||||
$this->processFailed = true;
|
||||
}
|
||||
|
||||
$this->processResult = $result;
|
||||
} catch (Error | Exception $e) {
|
||||
$this->addError(PKPApplication::ASSOC_TYPE_NONE, 0, $e->getMessage());
|
||||
|
||||
$this->processFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter method for XMLValidation Errors
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getXMLValidationErrors()
|
||||
{
|
||||
return $this->xmlValidationErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all public objects, with their
|
||||
* respective names as array values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getObjectTypes()
|
||||
{
|
||||
$objectTypes = [
|
||||
PKPApplication::ASSOC_TYPE_NONE => __('plugins.importexport.native.common.any'),
|
||||
PKPApplication::ASSOC_TYPE_SUBMISSION => __('submission.submission'),
|
||||
PKPApplication::ASSOC_TYPE_AUTHOR => __('user.role.author'),
|
||||
PKPApplication::ASSOC_TYPE_PUBLICATION => __('submission.publication'),
|
||||
PKPApplication::ASSOC_TYPE_SECTION => __('section.section'),
|
||||
PKPApplication::ASSOC_TYPE_SUBMISSION_FILE => __('submission.submissionFile'),
|
||||
];
|
||||
|
||||
return $objectTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object type string.
|
||||
*
|
||||
* @param mixed $assocType int or null (optional)
|
||||
*
|
||||
* @return mixed string or array
|
||||
*/
|
||||
public function getObjectTypeString($assocType = null)
|
||||
{
|
||||
$objectTypes = $this->getObjectTypes();
|
||||
|
||||
if (is_null($assocType)) {
|
||||
return $objectTypes;
|
||||
} else {
|
||||
if (isset($objectTypes[$assocType])) {
|
||||
return $objectTypes[$assocType];
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get possible Warnings and Errors from the import/export process
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getWarningsAndErrors()
|
||||
{
|
||||
$problems = [];
|
||||
$objectTypes = $this->getObjectTypes();
|
||||
foreach ($objectTypes as $assocType => $name) {
|
||||
$foundWarnings = $this->getProcessedObjectsWarnings($assocType);
|
||||
if (!empty($foundWarnings)) {
|
||||
$problems['warnings'][$name][] = $foundWarnings;
|
||||
}
|
||||
|
||||
$foundErrors = $this->getProcessedObjectsErrors($assocType);
|
||||
if (!empty($foundErrors)) {
|
||||
$problems['errors'][$name][] = $foundErrors;
|
||||
}
|
||||
}
|
||||
if (count($validationErrors = $this->getXMLValidationErrors())) {
|
||||
$validationErrors = array_map(fn (LibXMLError $e) => "Line {$e->line} Column {$e->column}: {$e->message}", $validationErrors);
|
||||
$genericError = $objectTypes[PKPApplication::ASSOC_TYPE_NONE];
|
||||
$problems['errors'][$genericError][] = [$validationErrors];
|
||||
}
|
||||
|
||||
return $problems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import entities with their names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getImportedRootEntitiesWithNames()
|
||||
{
|
||||
$rootEntities = [];
|
||||
$objectTypes = $this->getObjectTypes();
|
||||
foreach ($objectTypes as $assocType => $name) {
|
||||
$entities = $this->getImportedRootEntities($assocType);
|
||||
if (!empty($entities)) {
|
||||
$rootEntities[$name][] = $entities;
|
||||
}
|
||||
}
|
||||
|
||||
return $rootEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an indication that the import/export process has failed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isProcessFailed()
|
||||
{
|
||||
if ($this->processFailed || count($this->xmlValidationErrors) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\importexport\PKPImportExportDeployment', '\PKPImportExportDeployment');
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/importexport/PKPImportExportFilter.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPImportExportFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base helper class for import/export filters
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport;
|
||||
|
||||
// FIXME: Add namespacing
|
||||
|
||||
use APP\plugins\importexport\native\NativeImportExportDeployment;
|
||||
use Exception;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\filter\Filter;
|
||||
use PKP\filter\FilterDAO;
|
||||
use PKP\filter\PersistableFilter;
|
||||
use PKP\plugins\importexport\native\filter\NativeExportFilter;
|
||||
use PKP\plugins\importexport\native\PKPNativeImportExportDeployment;
|
||||
|
||||
class PKPImportExportFilter extends PersistableFilter
|
||||
{
|
||||
/** @var PKPNativeImportExportDeployment */
|
||||
private $_deployment;
|
||||
|
||||
//
|
||||
// Deployment management
|
||||
//
|
||||
/**
|
||||
* Set the import/export deployment
|
||||
*
|
||||
* @param NativeImportExportDeployment $deployment
|
||||
*/
|
||||
public function setDeployment($deployment)
|
||||
{
|
||||
$this->_deployment = $deployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import/export deployment
|
||||
*
|
||||
* @return PKPNativeImportExportDeployment
|
||||
*/
|
||||
public function getDeployment()
|
||||
{
|
||||
return $this->_deployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method that gets the filter object given its name
|
||||
*
|
||||
* @param string $filter
|
||||
* @param PKPImportExportDeployment $deployment
|
||||
* @param array $opts
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
public static function getFilter($filter, $deployment, $opts = [])
|
||||
{
|
||||
$filterDao = DAORegistry::getDAO('FilterDAO'); /** @var FilterDAO $filterDao */
|
||||
$filters = $filterDao->getObjectsByGroup($filter);
|
||||
|
||||
if (count($filters) != 1) {
|
||||
throw new Exception(
|
||||
__(
|
||||
'plugins.importexport.native.common.error.filter.configuration.count',
|
||||
[
|
||||
'filterName' => $filter,
|
||||
'filterCount' => count($filters)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$currentFilter = array_shift($filters);
|
||||
$currentFilter->setDeployment($deployment);
|
||||
|
||||
if ($currentFilter instanceof NativeExportFilter) {
|
||||
$currentFilter->setOpts($opts);
|
||||
}
|
||||
|
||||
return $currentFilter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\importexport\PKPImportExportFilter', '\PKPImportExportFilter');
|
||||
}
|
||||
Reference in New Issue
Block a user