<?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');
}
|