first commit
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup announcement Announcement
|
||||
* Implements announcements that can be presented to website visitors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/announcement/Announcement.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 Announcement
|
||||
*
|
||||
* @ingroup announcement
|
||||
*
|
||||
* @see DAO
|
||||
*
|
||||
* @brief Basic class describing a announcement.
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\file\PublicFileManager;
|
||||
use PKP\db\DAORegistry;
|
||||
|
||||
class Announcement extends \PKP\core\DataObject
|
||||
{
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
/**
|
||||
* Get assoc ID for this announcement.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAssocId()
|
||||
{
|
||||
return $this->getData('assocId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set assoc ID for this announcement.
|
||||
*
|
||||
* @param int $assocId
|
||||
*/
|
||||
public function setAssocId($assocId)
|
||||
{
|
||||
$this->setData('assocId', $assocId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assoc type for this announcement.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAssocType()
|
||||
{
|
||||
return $this->getData('assocType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set assoc type for this announcement.
|
||||
*
|
||||
* @param int $assocType
|
||||
*/
|
||||
public function setAssocType($assocType)
|
||||
{
|
||||
$this->setData('assocType', $assocType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the announcement type of the announcement.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTypeId()
|
||||
{
|
||||
return $this->getData('typeId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the announcement type of the announcement.
|
||||
*
|
||||
* @param int $typeId
|
||||
*/
|
||||
public function setTypeId($typeId)
|
||||
{
|
||||
$this->setData('typeId', $typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the announcement type name of the announcement.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAnnouncementTypeName()
|
||||
{
|
||||
$announcementTypeDao = DAORegistry::getDAO('AnnouncementTypeDAO'); /** @var AnnouncementTypeDAO $announcementTypeDao */
|
||||
$announcementType = $announcementTypeDao->getById($this->getData('typeId'));
|
||||
return $announcementType ? $announcementType->getLocalizedTypeName() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized announcement title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedTitle()
|
||||
{
|
||||
return $this->getLocalizedData('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full localized announcement title including type name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedTitleFull()
|
||||
{
|
||||
$typeName = $this->getAnnouncementTypeName();
|
||||
if (!empty($typeName)) {
|
||||
return $typeName . ': ' . $this->getLocalizedTitle();
|
||||
} else {
|
||||
return $this->getLocalizedTitle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement title.
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle($locale)
|
||||
{
|
||||
return $this->getData('title', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement title.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setTitle($title, $locale)
|
||||
{
|
||||
$this->setData('title', $title, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized short description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedDescriptionShort()
|
||||
{
|
||||
return $this->getLocalizedData('descriptionShort');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement brief description.
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescriptionShort($locale)
|
||||
{
|
||||
return $this->getData('descriptionShort', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement brief description.
|
||||
*
|
||||
* @param string $descriptionShort
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setDescriptionShort($descriptionShort, $locale)
|
||||
{
|
||||
$this->setData('descriptionShort', $descriptionShort, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized full description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedDescription()
|
||||
{
|
||||
return $this->getLocalizedData('description');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement description.
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription($locale)
|
||||
{
|
||||
return $this->getData('description', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement description.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setDescription($description, $locale)
|
||||
{
|
||||
$this->setData('description', $description, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement expiration date.
|
||||
*
|
||||
* @return string (YYYY-MM-DD)
|
||||
*/
|
||||
public function getDateExpire()
|
||||
{
|
||||
return $this->getData('dateExpire');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement expiration date.
|
||||
*
|
||||
* @param string $dateExpire (YYYY-MM-DD)
|
||||
*/
|
||||
public function setDateExpire($dateExpire)
|
||||
{
|
||||
$this->setData('dateExpire', $dateExpire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement posted date.
|
||||
*
|
||||
* @return string (YYYY-MM-DD)
|
||||
*/
|
||||
public function getDatePosted()
|
||||
{
|
||||
return date('Y-m-d', strtotime($this->getData('datePosted')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcement posted datetime.
|
||||
*
|
||||
* @return string (YYYY-MM-DD HH:MM:SS)
|
||||
*/
|
||||
public function getDatetimePosted()
|
||||
{
|
||||
return $this->getData('datePosted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement posted date.
|
||||
*
|
||||
* @param string $datePosted (YYYY-MM-DD)
|
||||
*/
|
||||
public function setDatePosted($datePosted)
|
||||
{
|
||||
$this->setData('datePosted', $datePosted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set announcement posted datetime.
|
||||
*
|
||||
* @param string $datetimePosted (YYYY-MM-DD HH:MM:SS)
|
||||
*/
|
||||
public function setDatetimePosted($datetimePosted)
|
||||
{
|
||||
$this->setData('datePosted', $datetimePosted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the featured image data
|
||||
*/
|
||||
public function getImage(): ?array
|
||||
{
|
||||
return $this->getData('image');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the featured image data
|
||||
*/
|
||||
public function setImage(array $image): void
|
||||
{
|
||||
$this->setData('image', $image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full URL to the image
|
||||
*
|
||||
* @param bool $withTimestamp Pass true to include a query argument with a timestamp
|
||||
* of the date the image was uploaded in order to workaround cache bugs in browsers
|
||||
*/
|
||||
public function getImageUrl(bool $withTimestamp = true): string
|
||||
{
|
||||
$image = $this->getImage();
|
||||
|
||||
if (!$image) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$filename = $image['uploadName'];
|
||||
if ($withTimestamp) {
|
||||
$filename .= '?'. strtotime($image['dateUploaded']);
|
||||
}
|
||||
|
||||
$publicFileManager = new PublicFileManager();
|
||||
|
||||
return join('/', [
|
||||
Application::get()->getRequest()->getBaseUrl(),
|
||||
$this->getAssocId()
|
||||
? $publicFileManager->getContextFilesPath((int) $this->getAssocId())
|
||||
: $publicFileManager->getSiteFilesPath(),
|
||||
Repo::announcement()->getImageSubdirectory(),
|
||||
$filename
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alt text for the image
|
||||
*/
|
||||
public function getImageAltText(): string
|
||||
{
|
||||
$image = $this->getImage();
|
||||
|
||||
if (!$image || !$image['altText']) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $image['altText'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\announcement\Announcement', '\Announcement');
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/announcement/AnnouncementType.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 AnnouncementType
|
||||
*
|
||||
* @ingroup announcement
|
||||
*
|
||||
* @see AnnouncementTypeDAO, AnnouncementTypeForm
|
||||
*
|
||||
* @brief Basic class describing an announcement type.
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
class AnnouncementType extends \PKP\core\DataObject
|
||||
{
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
/**
|
||||
* Get context ID for this announcement.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContextId()
|
||||
{
|
||||
return $this->getData('contextId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set context ID for this announcement.
|
||||
*
|
||||
* @param int $contextId
|
||||
*/
|
||||
public function setContextId($contextId)
|
||||
{
|
||||
$this->setData('contextId', $contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the announcement type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedTypeName()
|
||||
{
|
||||
return $this->getLocalizedData('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the announcement type.
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName($locale)
|
||||
{
|
||||
return $this->getData('name', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of the announcement type.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setName($name, $locale)
|
||||
{
|
||||
$this->setData('name', $name, $locale);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\announcement\AnnouncementType', '\AnnouncementType');
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/announcement/AnnouncementTypeDAO.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 AnnouncementTypeDAO
|
||||
*
|
||||
* @ingroup announcement
|
||||
*
|
||||
* @see AnnouncementType
|
||||
*
|
||||
* @brief Operations for retrieving and modifying AnnouncementType objects.
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
use APP\facades\Repo;
|
||||
|
||||
class AnnouncementTypeDAO extends \PKP\db\DAO
|
||||
{
|
||||
/**
|
||||
* Generate a new data object.
|
||||
*
|
||||
* @return AnnouncementType
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new AnnouncementType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an announcement type by announcement type ID.
|
||||
*
|
||||
* @param ?int $typeId Announcement type ID
|
||||
* @param ?int $contextId Optional context ID
|
||||
*
|
||||
* @return AnnouncementType
|
||||
*/
|
||||
public function getById($typeId, $contextId = null)
|
||||
{
|
||||
$params = [(int) $typeId];
|
||||
if ($contextId !== null) {
|
||||
$params[] = (int) $contextId;
|
||||
}
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM announcement_types WHERE type_id = ?' .
|
||||
($contextId !== null ? ' AND context_id = ?' : ''),
|
||||
$params
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row ? $this->_fromRow((array) $row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale field names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocaleFieldNames()
|
||||
{
|
||||
return ['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to return an AnnouncementType object from a row.
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return AnnouncementType
|
||||
*/
|
||||
public function _fromRow($row)
|
||||
{
|
||||
$announcementType = $this->newDataObject();
|
||||
$announcementType->setId($row['type_id']);
|
||||
$announcementType->setData('contextId', $row['context_id']);
|
||||
$this->getDataObjectSettings('announcement_type_settings', 'type_id', $row['type_id'], $announcementType);
|
||||
|
||||
return $announcementType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the localized settings for this object
|
||||
*
|
||||
* @param AnnouncementType $announcementType
|
||||
*/
|
||||
public function updateLocaleFields($announcementType)
|
||||
{
|
||||
$this->updateDataObjectSettings(
|
||||
'announcement_type_settings',
|
||||
$announcementType,
|
||||
['type_id' => (int) $announcementType->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new AnnouncementType.
|
||||
*
|
||||
* @param AnnouncementType $announcementType
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function insertObject($announcementType)
|
||||
{
|
||||
$this->update(
|
||||
sprintf('INSERT INTO announcement_types
|
||||
(context_id)
|
||||
VALUES
|
||||
(?)'),
|
||||
[
|
||||
$announcementType->getContextId()
|
||||
? (int) $announcementType->getContextId()
|
||||
: null
|
||||
]
|
||||
);
|
||||
$announcementType->setId($this->getInsertId());
|
||||
$this->updateLocaleFields($announcementType);
|
||||
return $announcementType->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing announcement type.
|
||||
*
|
||||
* @param AnnouncementType $announcementType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateObject($announcementType)
|
||||
{
|
||||
$returner = $this->update(
|
||||
'UPDATE announcement_types
|
||||
SET context_id = ?
|
||||
WHERE type_id = ?',
|
||||
[
|
||||
$announcementType->getContextId(),
|
||||
(int) $announcementType->getId()
|
||||
]
|
||||
);
|
||||
|
||||
$this->updateLocaleFields($announcementType);
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an announcement type. Note that all announcements with this type are also
|
||||
* deleted.
|
||||
*
|
||||
* @param AnnouncementType $announcementType
|
||||
*/
|
||||
public function deleteObject($announcementType)
|
||||
{
|
||||
return $this->deleteById($announcementType->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an announcement type by announcement type ID. Note that all announcements with
|
||||
* this type ID are also deleted.
|
||||
*
|
||||
* @param int $typeId
|
||||
*/
|
||||
public function deleteById($typeId)
|
||||
{
|
||||
$this->update('DELETE FROM announcement_type_settings WHERE type_id = ?', [(int) $typeId]);
|
||||
$this->update('DELETE FROM announcement_types WHERE type_id = ?', [(int) $typeId]);
|
||||
|
||||
$collector = Repo::announcement()->getCollector()->filterByTypeIds([(int) $typeId]);
|
||||
Repo::announcement()->deleteMany($collector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete announcement types by context ID.
|
||||
*
|
||||
* @param int $contextId
|
||||
*/
|
||||
public function deleteByContextId($contextId)
|
||||
{
|
||||
foreach ($this->getByContextId($contextId) as $type) {
|
||||
$this->deleteObject($type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of announcement types matching a particular context ID.
|
||||
*
|
||||
* @return \Generator<int,AnnouncementType> Matching AnnouncementTypes
|
||||
*/
|
||||
public function getByContextId(?int $contextId)
|
||||
{
|
||||
if ($contextId) {
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM announcement_types WHERE context_id = ? ORDER BY type_id',
|
||||
[$contextId]
|
||||
);
|
||||
} else {
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM announcement_types WHERE context_id IS NULL ORDER BY type_id'
|
||||
);
|
||||
}
|
||||
foreach ($result as $row) {
|
||||
yield $row->type_id => $this->_fromRow((array) $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\announcement\AnnouncementTypeDAO', '\AnnouncementTypeDAO');
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/announcement/Collector.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 Collector
|
||||
*
|
||||
* @brief A helper class to configure a Query Builder to get a collection of announcements
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
use APP\core\Application;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\interfaces\CollectorInterface;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
/**
|
||||
* @template T of Announcement
|
||||
*/
|
||||
class Collector implements CollectorInterface
|
||||
{
|
||||
public const ORDERBY_DATE_POSTED = 'date_posted';
|
||||
public const ORDERBY_DATE_EXPIRE = 'date_expire';
|
||||
public const ORDER_DIR_ASC = 'ASC';
|
||||
public const ORDER_DIR_DESC = 'DESC';
|
||||
public const SITE_ONLY = 'site';
|
||||
public const SITE_AND_CONTEXTS = 'all';
|
||||
|
||||
public DAO $dao;
|
||||
public ?array $contextIds = null;
|
||||
public ?string $isActive = null;
|
||||
public ?string $searchPhrase = null;
|
||||
public ?array $typeIds = null;
|
||||
public ?string $includeSite = null;
|
||||
public ?int $count = null;
|
||||
public ?int $offset = null;
|
||||
public string $orderBy = self::ORDERBY_DATE_POSTED;
|
||||
public string $orderDirection = self::ORDER_DIR_DESC;
|
||||
|
||||
public function __construct(DAO $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::getCount() */
|
||||
public function getCount(): int
|
||||
{
|
||||
return $this->dao->getCount($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc DAO::getIds()
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(): Collection
|
||||
{
|
||||
return $this->dao->getIds($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc DAO::getMany()
|
||||
*
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(): LazyCollection
|
||||
{
|
||||
return $this->dao->getMany($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements by one or more contexts
|
||||
*/
|
||||
public function filterByContextIds(?array $contextIds): self
|
||||
{
|
||||
$this->contextIds = $contextIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements by those that have not expired
|
||||
*
|
||||
* @param string $date Optionally filter announcements by those
|
||||
* not expired until $date (YYYY-MM-DD).
|
||||
*/
|
||||
public function filterByActive(string $date = ''): self
|
||||
{
|
||||
$this->isActive = empty($date)
|
||||
? Core::getCurrentDate()
|
||||
: $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements by one or more announcement types
|
||||
*/
|
||||
public function filterByTypeIds(array $typeIds): self
|
||||
{
|
||||
$this->typeIds = $typeIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include site-level announcements in the results
|
||||
*/
|
||||
public function withSiteAnnouncements(?string $includeMethod = self::SITE_AND_CONTEXTS): self
|
||||
{
|
||||
$this->includeSite = $includeMethod;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter announcements by those matching a search query
|
||||
*/
|
||||
public function searchPhrase(?string $phrase): self
|
||||
{
|
||||
$this->searchPhrase = $phrase;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of objects retrieved
|
||||
*/
|
||||
public function limit(?int $count): self
|
||||
{
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the number of objects retrieved, for example to
|
||||
* retrieve the second page of contents
|
||||
*/
|
||||
public function offset(?int $offset): self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the results
|
||||
*
|
||||
* Results are ordered by the date posted by default.
|
||||
*
|
||||
* @param string $sorter One of the self::ORDERBY_ constants
|
||||
* @param string $direction One of the self::ORDER_DIR_ constants
|
||||
*/
|
||||
public function orderBy(?string $sorter, string $direction = self::ORDER_DIR_DESC): self
|
||||
{
|
||||
$this->orderBy = $sorter;
|
||||
$this->orderDirection = $direction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc CollectorInterface::getQueryBuilder()
|
||||
*/
|
||||
public function getQueryBuilder(): Builder
|
||||
{
|
||||
$qb = DB::table($this->dao->table . ' as a')
|
||||
->select(['a.*']);
|
||||
|
||||
if (isset($this->contextIds) && $this->includeSite !== self::SITE_ONLY) {
|
||||
$qb->where('a.assoc_type', Application::get()->getContextAssocType());
|
||||
$qb->whereIn('a.assoc_id', $this->contextIds);
|
||||
if ($this->includeSite === self::SITE_AND_CONTEXTS) {
|
||||
$qb->orWhereNull('a.assoc_id');
|
||||
}
|
||||
} elseif ($this->includeSite === self::SITE_ONLY) {
|
||||
$qb->where('a.assoc_type', Application::get()->getContextAssocType());
|
||||
$qb->whereNull('a.assoc_id');
|
||||
}
|
||||
|
||||
if (isset($this->typeIds)) {
|
||||
$qb->whereIn('a.type_id', $this->typeIds);
|
||||
}
|
||||
|
||||
$qb->when($this->isActive, fn ($qb) => $qb->where(function ($qb) {
|
||||
$qb->where('a.date_expire', '>', $this->isActive)
|
||||
->orWhereNull('a.date_expire');
|
||||
}));
|
||||
|
||||
if ($this->searchPhrase !== null) {
|
||||
$words = explode(' ', $this->searchPhrase);
|
||||
if (count($words)) {
|
||||
$qb->whereIn('a.announcement_id', function ($query) use ($words) {
|
||||
$query->select('announcement_id')->from($this->dao->settingsTable);
|
||||
foreach ($words as $word) {
|
||||
$word = strtolower(addcslashes($word, '%_'));
|
||||
$query->where(function ($query) use ($word) {
|
||||
$query->where(function ($query) use ($word) {
|
||||
$query->where('setting_name', 'title');
|
||||
$query->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
|
||||
})
|
||||
->orWhere(function ($query) use ($word) {
|
||||
$query->where('setting_name', 'descriptionShort');
|
||||
$query->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
|
||||
})
|
||||
->orWhere(function ($query) use ($word) {
|
||||
$query->where('setting_name', 'description');
|
||||
$query->where(DB::raw('lower(setting_value)'), 'LIKE', "%{$word}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$qb->orderByDesc('a.date_posted');
|
||||
|
||||
if (isset($this->count)) {
|
||||
$qb->limit($this->count);
|
||||
}
|
||||
|
||||
if (isset($this->offset)) {
|
||||
$qb->offset($this->offset);
|
||||
}
|
||||
|
||||
if (isset($this->orderBy)) {
|
||||
$qb->orderBy('a.' . $this->orderBy, $this->orderDirection);
|
||||
// Add a secondary sort by id to catch cases where two
|
||||
// announcements share the same date
|
||||
if (in_array($this->orderBy, [SELF::ORDERBY_DATE_EXPIRE, SELF::ORDERBY_DATE_POSTED])) {
|
||||
$qb->orderBy('a.announcement_id', $this->orderDirection);
|
||||
}
|
||||
}
|
||||
|
||||
Hook::call('Announcement::Collector', [&$qb, $this]);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/announcement/DAO.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 DAO
|
||||
*
|
||||
* @brief Read and write announcements to the database.
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\EntityDAO;
|
||||
|
||||
/**
|
||||
* @template T of Announcement
|
||||
* @extends EntityDAO<T>
|
||||
*/
|
||||
class DAO extends EntityDAO
|
||||
{
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = \PKP\services\PKPSchemaService::SCHEMA_ANNOUNCEMENT;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'announcements';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'announcement_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryKeyColumn */
|
||||
public $primaryKeyColumn = 'announcement_id';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'id' => 'announcement_id',
|
||||
'assocId' => 'assoc_id',
|
||||
'assocType' => 'assoc_type',
|
||||
'typeId' => 'type_id',
|
||||
'dateExpire' => 'date_expire',
|
||||
'datePosted' => 'date_posted',
|
||||
];
|
||||
|
||||
/**
|
||||
* Instantiate a new DataObject
|
||||
*/
|
||||
public function newDataObject(): Announcement
|
||||
{
|
||||
return app(Announcement::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an announcement exists
|
||||
*/
|
||||
public function exists(int $id): bool
|
||||
{
|
||||
return DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, '=', $id)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an announcement
|
||||
*/
|
||||
public function get(int $id): ?Announcement
|
||||
{
|
||||
$row = DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, $id)
|
||||
->first();
|
||||
return $row ? $this->fromRow($row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of announcements matching the configured query
|
||||
*/
|
||||
public function getCount(Collector $query): int
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->get('a.' . $this->primaryKeyColumn)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ids matching the configured query
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->pluck('a.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of announcements matching the configured query
|
||||
*
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->announcement_id => $this->fromRow($row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::insert()
|
||||
*/
|
||||
public function insert(Announcement $announcement): int
|
||||
{
|
||||
return parent::_insert($announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::update()
|
||||
*/
|
||||
public function update(Announcement $announcement)
|
||||
{
|
||||
parent::_update($announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::delete()
|
||||
*/
|
||||
public function delete(Announcement $announcement)
|
||||
{
|
||||
parent::_delete($announcement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/announcement/Repository.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Repository
|
||||
*
|
||||
* @brief A repository to find and manage announcements.
|
||||
*/
|
||||
|
||||
namespace PKP\announcement;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use APP\file\PublicFileManager;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\exceptions\StoreTemporaryFileException;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\file\TemporaryFile;
|
||||
use PKP\file\TemporaryFileManager;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\user\User;
|
||||
use PKP\validation\ValidatorFactory;
|
||||
|
||||
class Repository
|
||||
{
|
||||
/** @var DAO $dao */
|
||||
public $dao;
|
||||
|
||||
/** @var string $schemaMap The name of the class to map this entity to its schema */
|
||||
public $schemaMap = maps\Schema::class;
|
||||
|
||||
/** @var Request $request */
|
||||
protected $request;
|
||||
|
||||
/** @var PKPSchemaService<Announcement> $schemaService */
|
||||
protected $schemaService;
|
||||
|
||||
|
||||
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
$this->request = $request;
|
||||
$this->schemaService = $schemaService;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::newDataObject() */
|
||||
public function newDataObject(array $params = []): Announcement
|
||||
{
|
||||
$object = $this->dao->newDataObject();
|
||||
if (!empty($params)) {
|
||||
$object->setAllData($params);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::get() */
|
||||
public function get(int $id): ?Announcement
|
||||
{
|
||||
return $this->dao->get($id);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::exists() */
|
||||
public function exists(int $id): bool
|
||||
{
|
||||
return $this->dao->exists($id);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::getCollector() */
|
||||
public function getCollector(): Collector
|
||||
{
|
||||
return app(Collector::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the map class for mapping
|
||||
* announcements to their schema
|
||||
*/
|
||||
public function getSchemaMap(): maps\Schema
|
||||
{
|
||||
return app('maps')->withExtensions($this->schemaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate properties for an announcement
|
||||
*
|
||||
* Perform validation checks on data used to add or edit an announcement.
|
||||
*
|
||||
* @param array $props A key/value array with the new data to validate
|
||||
* @param array $allowedLocales The context's supported locales
|
||||
* @param string $primaryLocale The context's primary locale
|
||||
*
|
||||
* @return array A key/value array with validation errors. Empty if no errors
|
||||
*/
|
||||
public function validate(?Announcement $object, array $props, array $allowedLocales, string $primaryLocale): array
|
||||
{
|
||||
$validator = ValidatorFactory::make(
|
||||
$props,
|
||||
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales),
|
||||
[
|
||||
'dateExpire.date_format' => __('stats.dateRange.invalidDate'),
|
||||
]
|
||||
);
|
||||
|
||||
// Check required fields
|
||||
ValidatorFactory::required(
|
||||
$validator,
|
||||
$object,
|
||||
$this->schemaService->getRequiredProps($this->dao->schema),
|
||||
$this->schemaService->getMultilingualProps($this->dao->schema),
|
||||
$allowedLocales,
|
||||
$primaryLocale
|
||||
);
|
||||
|
||||
// Check for input from disallowed locales
|
||||
ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales);
|
||||
|
||||
$errors = [];
|
||||
|
||||
if ($validator->fails()) {
|
||||
$errors = $this->schemaService->formatValidationErrors($validator->errors());
|
||||
}
|
||||
|
||||
Hook::call('Announcement::validate', [&$errors, $object, $props, $allowedLocales, $primaryLocale]);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::insert() */
|
||||
public function add(Announcement $announcement): int
|
||||
{
|
||||
$announcement->setData('datePosted', Core::getCurrentDate());
|
||||
$id = $this->dao->insert($announcement);
|
||||
$announcement = $this->get($id);
|
||||
|
||||
if ($announcement->getImage()) {
|
||||
$this->handleImageUpload($announcement);
|
||||
}
|
||||
|
||||
Hook::call('Announcement::add', [$announcement]);
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an object in the database
|
||||
*
|
||||
* Deletes the old image if it has been removed, or a new image has
|
||||
* been uploaded.
|
||||
*/
|
||||
public function edit(Announcement $announcement, array $params)
|
||||
{
|
||||
$newAnnouncement = clone $announcement;
|
||||
$newAnnouncement->setAllData(array_merge($newAnnouncement->_data, $params));
|
||||
|
||||
Hook::call('Announcement::edit', [$newAnnouncement, $announcement, $params]);
|
||||
|
||||
$this->dao->update($newAnnouncement);
|
||||
|
||||
$image = $newAnnouncement->getImage();
|
||||
$hasNewImage = $image && $image['temporaryFileId'];
|
||||
|
||||
if ((!$image || $hasNewImage) && $announcement->getImage()) {
|
||||
$this->deleteImage($announcement);
|
||||
}
|
||||
|
||||
if ($hasNewImage) {
|
||||
$this->handleImageUpload($newAnnouncement);
|
||||
}
|
||||
}
|
||||
|
||||
/** @copydoc DAO::delete() */
|
||||
public function delete(Announcement $announcement)
|
||||
{
|
||||
Hook::call('Announcement::delete::before', [$announcement]);
|
||||
|
||||
if ($announcement->getImage()) {
|
||||
$this->deleteImage($announcement);
|
||||
}
|
||||
|
||||
$this->dao->delete($announcement);
|
||||
|
||||
Hook::call('Announcement::delete', [$announcement]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a collection of announcements
|
||||
*/
|
||||
public function deleteMany(Collector $collector)
|
||||
{
|
||||
foreach ($collector->getMany() as $announcement) {
|
||||
$this->delete($announcement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The subdirectory where announcement images are stored
|
||||
*/
|
||||
public function getImageSubdirectory(): string
|
||||
{
|
||||
return 'announcements';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for announcement file uploads
|
||||
*/
|
||||
public function getFileUploadBaseUrl(?Context $context = null): string
|
||||
{
|
||||
return join('/', [
|
||||
Application::get()->getRequest()->getPublicFilesUrl($context),
|
||||
$this->getImageSubdirectory(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle image uploads
|
||||
*
|
||||
* @throws StoreTemporaryFileException Unable to store temporary file upload
|
||||
*/
|
||||
protected function handleImageUpload(Announcement $announcement): void
|
||||
{
|
||||
$image = $announcement->getImage();
|
||||
if ($image && $image['temporaryFileId']) {
|
||||
$user = Application::get()->getRequest()->getUser();
|
||||
$image = $announcement->getImage();
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
$temporaryFile = $temporaryFileManager->getFile((int) $image['temporaryFileId'], $user?->getId());
|
||||
$filePath = $this->getImageSubdirectory() . '/' . $this->getImageFilename($announcement, $temporaryFile);
|
||||
if (!$this->isValidImage($temporaryFile, $filePath, $user, $announcement)) {
|
||||
throw new StoreTemporaryFileException($temporaryFile, $filePath, $user, $announcement);
|
||||
}
|
||||
if ($this->storeTemporaryFile($temporaryFile, $filePath, $user->getId(), $announcement)) {
|
||||
$announcement->setImage(
|
||||
$this->getImageData($announcement, $temporaryFile)
|
||||
);
|
||||
$this->dao->update($announcement);
|
||||
} else {
|
||||
$this->delete($announcement);
|
||||
throw new StoreTemporaryFileException($temporaryFile, $filePath, $user, $announcement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a temporary file upload in the public files directory
|
||||
*
|
||||
* @param string $newPath The new filename with the path relative to the public files directoruy
|
||||
* @return bool Whether or not the operation was successful
|
||||
*/
|
||||
protected function storeTemporaryFile(TemporaryFile $temporaryFile, string $newPath, int $userId, Announcement $announcement): bool
|
||||
{
|
||||
$publicFileManager = new PublicFileManager();
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
|
||||
if ($announcement->getAssocId()) {
|
||||
$result = $publicFileManager->copyContextFile(
|
||||
$announcement->getAssocId(),
|
||||
$temporaryFile->getFilePath(),
|
||||
$newPath
|
||||
);
|
||||
} else {
|
||||
$result = $publicFileManager->copySiteFile(
|
||||
$temporaryFile->getFilePath(),
|
||||
$newPath
|
||||
);
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$temporaryFileManager->deleteById($temporaryFile->getId(), $userId);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data array for a temporary file that has just been stored
|
||||
*
|
||||
* @return array Data about the image, like the upload name, alt text, and date uploaded
|
||||
*/
|
||||
protected function getImageData(Announcement $announcement, TemporaryFile $temporaryFile): array
|
||||
{
|
||||
$image = $announcement->getImage();
|
||||
|
||||
return [
|
||||
'name' => $temporaryFile->getOriginalFileName(),
|
||||
'uploadName' => $this->getImageFilename($announcement, $temporaryFile),
|
||||
'dateUploaded' => Core::getCurrentDate(),
|
||||
'altText' => !empty($image['altText']) ? $image['altText'] : '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the image upload
|
||||
*/
|
||||
protected function getImageFilename(Announcement $announcement, TemporaryFile $temporaryFile): string
|
||||
{
|
||||
$fileManager = new FileManager();
|
||||
|
||||
return $announcement->getId()
|
||||
. $fileManager->getImageExtension($temporaryFile->getFileType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the image related to announcement
|
||||
*/
|
||||
protected function deleteImage(Announcement $announcement): void
|
||||
{
|
||||
$image = $announcement->getImage();
|
||||
if ($image && $image['uploadName']) {
|
||||
$publicFileManager = new PublicFileManager();
|
||||
$filesPath = $announcement->getAssocId()
|
||||
? $publicFileManager->getContextFilesPath($announcement->getAssocId())
|
||||
: $publicFileManager->getSiteFilesPath();
|
||||
|
||||
$publicFileManager->deleteByPath(
|
||||
join('/', [
|
||||
$filesPath,
|
||||
$this->getImageSubdirectory(),
|
||||
$image['uploadName'],
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that temporary file is an image
|
||||
*/
|
||||
protected function isValidImage(TemporaryFile $temporaryFile): bool
|
||||
{
|
||||
if (getimagesize($temporaryFile->getFilePath()) === false) {
|
||||
return false;
|
||||
}
|
||||
$extension = pathinfo($temporaryFile->getOriginalFileName(), PATHINFO_EXTENSION);
|
||||
$fileManager = new FileManager();
|
||||
$extensionFromMimeType = $fileManager->getImageExtension(
|
||||
PKPString::mime_content_type($temporaryFile->getFilePath())
|
||||
);
|
||||
if ($extensionFromMimeType !== '.' . $extension) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/announcement/maps/Schema.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Schema
|
||||
*
|
||||
* @brief Map announcements to the properties defined in the announcement schema
|
||||
*/
|
||||
|
||||
namespace PKP\announcement\maps;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use PKP\announcement\Announcement;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\services\PKPSchemaService;
|
||||
|
||||
class Schema extends \PKP\core\maps\Schema
|
||||
{
|
||||
public Enumerable $collection;
|
||||
|
||||
public string $schema = PKPSchemaService::SCHEMA_ANNOUNCEMENT;
|
||||
|
||||
/**
|
||||
* Map an announcement
|
||||
*
|
||||
* Includes all properties in the announcement schema.
|
||||
*/
|
||||
public function map(Announcement $item): array
|
||||
{
|
||||
return $this->mapByProperties($this->getProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize an announcement
|
||||
*
|
||||
* Includes properties with the apiSummary flag in the announcement schema.
|
||||
*/
|
||||
public function summarize(Announcement $item): array
|
||||
{
|
||||
return $this->mapByProperties($this->getSummaryProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a collection of Announcements
|
||||
*
|
||||
* @see self::map
|
||||
*/
|
||||
public function mapMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) {
|
||||
return $this->map($item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a collection of Announcements
|
||||
*
|
||||
* @see self::summarize
|
||||
*/
|
||||
public function summarizeMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) {
|
||||
return $this->summarize($item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map schema properties of an Announcement to an assoc array
|
||||
*/
|
||||
protected function mapByProperties(array $props, Announcement $item): array
|
||||
{
|
||||
$output = [];
|
||||
foreach ($props as $prop) {
|
||||
switch ($prop) {
|
||||
case '_href':
|
||||
$output[$prop] = $this->getApiUrl('announcements/' . $item->getId());
|
||||
break;
|
||||
case 'url':
|
||||
$output[$prop] = $this->request->getDispatcher()->url(
|
||||
$this->request,
|
||||
PKPApplication::ROUTE_PAGE,
|
||||
$this->getUrlPath(),
|
||||
'announcement',
|
||||
'view',
|
||||
$item->getId()
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$output[$prop] = $item->getData($prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $this->getSupportedLocales());
|
||||
|
||||
ksort($output);
|
||||
|
||||
return $this->withExtensions($output, $item);
|
||||
}
|
||||
|
||||
protected function getUrlPath(): string
|
||||
{
|
||||
if (isset($this->context)) {
|
||||
return $this->context->getData('urlPath');
|
||||
}
|
||||
return 'index';
|
||||
}
|
||||
|
||||
protected function getSupportedLocales(): array
|
||||
{
|
||||
if (isset($this->context)) {
|
||||
return $this->context->getSupportedFormLocales();
|
||||
}
|
||||
return Application::get()->getRequest()->getSite()->getSupportedLocales();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/author/Author.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 \PKP\author\Author
|
||||
*
|
||||
* @ingroup author
|
||||
*
|
||||
* @see DAO
|
||||
*
|
||||
* @brief Author metadata class.
|
||||
*/
|
||||
|
||||
namespace PKP\author;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\identity\Identity;
|
||||
|
||||
class Author extends Identity
|
||||
{
|
||||
/**
|
||||
* Get the default/fall back locale the values should exist for
|
||||
*/
|
||||
public function getDefaultLocale(): ?string
|
||||
{
|
||||
return $this->getSubmissionLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Identity::getLocalizedGivenName()
|
||||
*/
|
||||
public function getLocalizedGivenName()
|
||||
{
|
||||
return $this->getLocalizedData(self::IDENTITY_SETTING_GIVENNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Identity::getLocalizedFamilyName()
|
||||
*/
|
||||
public function getLocalizedFamilyName()
|
||||
{
|
||||
// Prioritize the current locale, then the default locale.
|
||||
$locale = Locale::getLocale();
|
||||
$givenName = $this->getGivenName($locale);
|
||||
// Only use the family name if a given name exists (to avoid mixing locale data)
|
||||
if (!empty($givenName)) {
|
||||
return $this->getFamilyName($locale);
|
||||
}
|
||||
// Fall back on the submission locale.
|
||||
return $this->getFamilyName($this->getSubmissionLocale());
|
||||
}
|
||||
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Get ID of submission.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSubmissionId()
|
||||
{
|
||||
return $this->getData('submissionId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ID of submission.
|
||||
*
|
||||
* @param int $submissionId
|
||||
*/
|
||||
public function setSubmissionId($submissionId)
|
||||
{
|
||||
$this->setData('submissionId', $submissionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get submission locale.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionLocale()
|
||||
{
|
||||
return $this->getData('locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set submission locale.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setSubmissionLocale($locale)
|
||||
{
|
||||
return $this->setData('locale', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user group id
|
||||
*
|
||||
* @param int $userGroupId
|
||||
*/
|
||||
public function setUserGroupId($userGroupId)
|
||||
{
|
||||
$this->setData('userGroupId', $userGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user group id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUserGroupId()
|
||||
{
|
||||
return $this->getData('userGroupId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to include in browse lists.
|
||||
*
|
||||
* @param bool $include
|
||||
*/
|
||||
public function setIncludeInBrowse($include)
|
||||
{
|
||||
$this->setData('includeInBrowse', $include);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not to include in browse lists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeInBrowse()
|
||||
{
|
||||
return $this->getData('includeInBrowse');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "show title" flag (whether or not the title of the role
|
||||
* should be included in the list of submission contributor names).
|
||||
* This is fetched from the user group for performance reasons.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getShowTitle()
|
||||
{
|
||||
return $this->getData('showTitle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "show title" flag. This attribute belongs to the user group,
|
||||
* NOT the author; fetched for performance reasons only.
|
||||
*
|
||||
* @param bool $showTitle
|
||||
*/
|
||||
public function _setShowTitle($showTitle)
|
||||
{
|
||||
$this->setData('showTitle', $showTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary contact.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getPrimaryContact()
|
||||
{
|
||||
return $this->getData('primaryContact');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set primary contact.
|
||||
*
|
||||
* @param bool $primaryContact
|
||||
*/
|
||||
public function setPrimaryContact($primaryContact)
|
||||
{
|
||||
$this->setData('primaryContact', $primaryContact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sequence of author in submissions' author list.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSequence()
|
||||
{
|
||||
return $this->getData('seq');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence of author in submissions' author list.
|
||||
*
|
||||
* @param float $sequence
|
||||
*/
|
||||
public function setSequence($sequence)
|
||||
{
|
||||
$this->setData('seq', $sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user group for this contributor.
|
||||
*
|
||||
* @return \PKP\userGroup\UserGroup
|
||||
*/
|
||||
public function getUserGroup()
|
||||
{
|
||||
//FIXME: should this be queried when fetching Author from DB? - see #5231.
|
||||
static $userGroup; // Frequently we'll fetch the same one repeatedly
|
||||
if (!$userGroup || $this->getUserGroupId() != $userGroup->getId()) {
|
||||
$userGroup = Repo::userGroup()->get($this->getUserGroupId());
|
||||
}
|
||||
return $userGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized version of the User Group
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedUserGroupName()
|
||||
{
|
||||
$userGroup = $this->getUserGroup();
|
||||
return $userGroup->getLocalizedName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get competing interests.
|
||||
* @return string|array|null
|
||||
*/
|
||||
function getCompetingInterests(?string $locale)
|
||||
{
|
||||
return $this->getData('competingInterests', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set competing interests.
|
||||
* @param $competingInterests string|array|null
|
||||
*/
|
||||
function setCompetingInterests($competingInterests, ?string $locale)
|
||||
{
|
||||
$this->setData('competingInterests', $competingInterests, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized version competing interest statement
|
||||
*/
|
||||
function getLocalizedCompetingInterests(): ?string
|
||||
{
|
||||
return $this->getLocalizedData('competingInterests');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/author/Collector.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 Collector
|
||||
*
|
||||
* @brief A helper class to configure a Query Builder to get a collection of announcements
|
||||
*/
|
||||
|
||||
namespace PKP\author;
|
||||
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\interfaces\CollectorInterface;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
/**
|
||||
* @template T of Author
|
||||
*/
|
||||
class Collector implements CollectorInterface
|
||||
{
|
||||
public const ORDERBY_SEQUENCE = 'sequence';
|
||||
public const ORDERBY_ID = 'id';
|
||||
|
||||
/** @var string The default orderBy value for authors collector */
|
||||
public $orderBy = self::ORDERBY_SEQUENCE;
|
||||
|
||||
/** @var DAO */
|
||||
public $dao;
|
||||
|
||||
/** @var int[]|null */
|
||||
public $contextIds = null;
|
||||
|
||||
/** @var int[]|null */
|
||||
public $publicationIds = null;
|
||||
|
||||
/** Get authors with a family name */
|
||||
protected ?string $familyName = null;
|
||||
|
||||
/** Get authors with a given name */
|
||||
protected ?string $givenName = null;
|
||||
|
||||
/** Get authors with a specified country code */
|
||||
protected ?string $country = null;
|
||||
|
||||
/** Get authors with a specified affiliation */
|
||||
protected ?string $affiliation = null;
|
||||
|
||||
public ?int $count = null;
|
||||
|
||||
public ?int $offset = null;
|
||||
|
||||
public ?bool $includeInBrowse = null;
|
||||
|
||||
public function __construct(DAO $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
public function getCount(): int
|
||||
{
|
||||
return $this->dao->getCount($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(): Collection
|
||||
{
|
||||
return $this->dao->getIds($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc DAO::getMany()
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(): LazyCollection
|
||||
{
|
||||
return $this->dao->getMany($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by contexts
|
||||
*/
|
||||
public function filterByContextIds(?array $contextIds): self
|
||||
{
|
||||
$this->contextIds = $contextIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by publications
|
||||
*/
|
||||
public function filterByPublicationIds(?array $publicationIds): self
|
||||
{
|
||||
$this->publicationIds = $publicationIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by include in browse
|
||||
*/
|
||||
public function filterByIncludeInBrowse(?bool $includeInBrowse): self
|
||||
{
|
||||
$this->includeInBrowse = $includeInBrowse;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include orderBy columns to the collector query
|
||||
*/
|
||||
public function orderBy(?string $orderBy): self
|
||||
{
|
||||
$this->orderBy = $orderBy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by the given and family name
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function filterByName(?string $givenName, ?string $familyName): self
|
||||
{
|
||||
$this->givenName = $givenName;
|
||||
$this->familyName = $familyName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by the specified country code
|
||||
*
|
||||
* @param string $country Country code (2-letter)
|
||||
*
|
||||
* */
|
||||
public function filterByCountry(?string $country): self
|
||||
{
|
||||
$this->country = $country;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by the specified affiliation code
|
||||
*
|
||||
* */
|
||||
public function filterByAffiliation(?string $affiliation): self
|
||||
{
|
||||
$this->affiliation = $affiliation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of objects retrieved
|
||||
*/
|
||||
public function limit(?int $count): self
|
||||
{
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the number of objects retrieved, for example to
|
||||
* retrieve the second page of contents
|
||||
*/
|
||||
public function offset(?int $offset): self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc CollectorInterface::getQueryBuilder()
|
||||
*/
|
||||
public function getQueryBuilder(): Builder
|
||||
{
|
||||
$q = DB::table('authors as a')
|
||||
->select(['a.*', 's.locale AS submission_locale'])
|
||||
->join('publications as p', 'a.publication_id', '=', 'p.publication_id')
|
||||
->join('submissions as s', 'p.submission_id', '=', 's.submission_id');
|
||||
|
||||
if (isset($this->contextIds)) {
|
||||
$q->whereIn('s.context_id', $this->contextIds);
|
||||
}
|
||||
|
||||
$q->when($this->familyName !== null, function (Builder $q) {
|
||||
$q->whereIn('a.author_id', function (Builder $q) {
|
||||
$q->select('author_id')
|
||||
->from($this->dao->settingsTable)
|
||||
->where('setting_name', '=', 'familyName')
|
||||
->where('setting_value', $this->familyName);
|
||||
});
|
||||
});
|
||||
|
||||
$q->when($this->givenName !== null, function (Builder $q) {
|
||||
$q->whereIn('a.author_id', function (Builder $q) {
|
||||
$q->select('author_id')
|
||||
->from($this->dao->settingsTable)
|
||||
->where('setting_name', '=', 'givenName')
|
||||
->where('setting_value', $this->givenName);
|
||||
});
|
||||
});
|
||||
|
||||
if (isset($this->publicationIds)) {
|
||||
$q->whereIn('a.publication_id', $this->publicationIds);
|
||||
}
|
||||
|
||||
$q->when($this->country !== null, function (Builder $q) {
|
||||
$q->whereIn('a.author_id', function (Builder $q) {
|
||||
$q->select('author_id')
|
||||
->from($this->dao->settingsTable)
|
||||
->where('setting_name', '=', 'country')
|
||||
->where('setting_value', $this->country);
|
||||
});
|
||||
});
|
||||
|
||||
$q->when($this->affiliation !== null, function (Builder $q) {
|
||||
$q->whereIn('a.author_id', function (Builder $q) {
|
||||
$q->select('author_id')
|
||||
->from($this->dao->settingsTable)
|
||||
->where('setting_name', '=', 'affiliation')
|
||||
->where('setting_value', $this->affiliation);
|
||||
});
|
||||
});
|
||||
|
||||
if ($this->includeInBrowse) {
|
||||
$q->where('a.include_in_browse', $this->includeInBrowse);
|
||||
}
|
||||
|
||||
if (isset($this->count)) {
|
||||
$q->limit($this->count);
|
||||
}
|
||||
|
||||
if (isset($this->offset)) {
|
||||
$q->offset($this->offset);
|
||||
}
|
||||
|
||||
switch ($this->orderBy) {
|
||||
case self::ORDERBY_SEQUENCE:
|
||||
$q->orderBy('a.seq', 'asc');
|
||||
break;
|
||||
case self::ORDERBY_ID:
|
||||
default:
|
||||
$q->orderBy('a.author_id', 'asc');
|
||||
break;
|
||||
}
|
||||
|
||||
// Add app-specific query statements
|
||||
Hook::call('Author::Collector', [&$q, $this]);
|
||||
|
||||
return $q;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/author/DAO.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 DAO
|
||||
*
|
||||
* @ingroup author
|
||||
*
|
||||
* @see \PKP\author\Author
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Author objects.
|
||||
*/
|
||||
|
||||
namespace PKP\author;
|
||||
|
||||
use APP\author\Author;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\facades\Repo;
|
||||
use PKP\services\PKPSchemaService;
|
||||
|
||||
/**
|
||||
* @template T of Author
|
||||
* @extends EntityDAO<T>
|
||||
*/
|
||||
class DAO extends EntityDAO
|
||||
{
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = PKPSchemaService::SCHEMA_AUTHOR;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'authors';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'author_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryKeyColumn */
|
||||
public $primaryKeyColumn = 'author_id';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'id' => 'author_id',
|
||||
'email' => 'email',
|
||||
'includeInBrowse' => 'include_in_browse',
|
||||
'publicationId' => 'publication_id',
|
||||
'seq' => 'seq',
|
||||
'userGroupId' => 'user_group_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent object ID column name
|
||||
*/
|
||||
public function getParentColumn(): string
|
||||
{
|
||||
return 'publication_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new DataObject
|
||||
*/
|
||||
public function newDataObject(): Author
|
||||
{
|
||||
return app(Author::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an author.
|
||||
*
|
||||
* Optionally, pass the publication ID to only get an author
|
||||
* if it exists and is assigned to that publication.
|
||||
*/
|
||||
public function get(int $id, int $publicationId = null): ?Author
|
||||
{
|
||||
// This is overridden due to the need to include submission_locale
|
||||
// to the fromRow function
|
||||
$row = DB::table('authors as a')
|
||||
->join('publications as p', 'a.publication_id', '=', 'p.publication_id')
|
||||
->join('submissions as s', 'p.submission_id', '=', 's.submission_id')
|
||||
->where('a.author_id', '=', $id)
|
||||
->when($publicationId !== null, fn (Builder $query) => $query->where('a.publication_id', '=', $publicationId))
|
||||
->select(['a.*', 's.locale AS submission_locale'])
|
||||
->first();
|
||||
return $row ? $this->fromRow($row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an author exists.
|
||||
*
|
||||
* Optionally, pass the publication ID to check if the author
|
||||
* exists and is assigned to that publication.
|
||||
*/
|
||||
public function exists(int $id, int $publicationId = null): bool
|
||||
{
|
||||
return DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, '=', $id)
|
||||
->when($publicationId !== null, fn (Builder $query) => $query->where($this->getParentColumn(), $publicationId))
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total count of rows matching the configured query
|
||||
*/
|
||||
public function getCount(Collector $query): int
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ids matching the configured query
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->select('a.' . $this->primaryKeyColumn)
|
||||
->pluck('a.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of publications matching the configured query
|
||||
*
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->author_id => $this->fromRow($row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::fromRow()
|
||||
*/
|
||||
public function fromRow(object $row): Author
|
||||
{
|
||||
$author = parent::fromRow($row);
|
||||
|
||||
// Set the primary locale from the submission
|
||||
$author->setData('locale', $row->submission_locale);
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::insert()
|
||||
*/
|
||||
public function insert(Author $author): int
|
||||
{
|
||||
return parent::_insert($author);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::update()
|
||||
*/
|
||||
public function update(Author $author)
|
||||
{
|
||||
parent::_update($author);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::delete()
|
||||
*/
|
||||
public function delete(Author $author)
|
||||
{
|
||||
DB::table('publications')
|
||||
->where('primary_contact_id', $author->getId())
|
||||
->update(['primary_contact_id' => null]);
|
||||
|
||||
parent::_delete($author);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next sequence that should be used when adding a contributor to a publication
|
||||
*/
|
||||
public function getNextSeq(int $publicationId): int
|
||||
{
|
||||
$nextSeq = 0;
|
||||
$seq = DB::table('authors as a')
|
||||
->join('publications as p', 'a.publication_id', '=', 'p.publication_id')
|
||||
->where('p.publication_id', '=', $publicationId)
|
||||
->max('a.seq');
|
||||
|
||||
if ($seq) {
|
||||
$nextSeq = $seq + 1;
|
||||
}
|
||||
|
||||
return $nextSeq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the order of contributors in a publication
|
||||
*
|
||||
* This method resets the seq property for each contributor in a publication
|
||||
* so that they are numbered sequentially without any gaps.
|
||||
*
|
||||
* eg - 1, 3, 4, 6 will become 1, 2, 3, 4
|
||||
*/
|
||||
public function resetContributorsOrder(int $publicationId)
|
||||
{
|
||||
$authorIds = Repo::author()
|
||||
->getCollector()
|
||||
->filterByPublicationIds([$publicationId])
|
||||
->orderBy(Repo::author()->getCollector()::ORDERBY_SEQUENCE)
|
||||
->getIds();
|
||||
|
||||
foreach ($authorIds as $seq => $authorId) {
|
||||
DB::table('authors')->where('author_id', '=', $authorId)->update(['seq' => $seq]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/author/Repository.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Repository
|
||||
*
|
||||
* @brief A repository to find and manage authors.
|
||||
*/
|
||||
|
||||
namespace PKP\author;
|
||||
|
||||
use APP\author\Author;
|
||||
use APP\author\DAO;
|
||||
use APP\core\Request;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use APP\submission\Submission;
|
||||
use PKP\context\Context;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\submission\PKPSubmission;
|
||||
use PKP\user\User;
|
||||
use PKP\validation\ValidatorFactory;
|
||||
|
||||
class Repository
|
||||
{
|
||||
/** @var DAO */
|
||||
public $dao;
|
||||
|
||||
/** @var string $schemaMap The name of the class to map this entity to its schema */
|
||||
public $schemaMap = maps\Schema::class;
|
||||
|
||||
/** @var Request */
|
||||
protected $request;
|
||||
|
||||
/** @var PKPSchemaService<Author> */
|
||||
protected $schemaService;
|
||||
|
||||
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
$this->request = $request;
|
||||
$this->schemaService = $schemaService;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::newDataObject() */
|
||||
public function newDataObject(array $params = []): Author
|
||||
{
|
||||
$object = $this->dao->newDataObject();
|
||||
if (!empty($params)) {
|
||||
$object->setAllData($params);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::get() */
|
||||
public function get(int $id, int $publicationId = null): ?Author
|
||||
{
|
||||
return $this->dao->get($id, $publicationId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::exists() */
|
||||
public function exists(int $id, int $publicationId = null): bool
|
||||
{
|
||||
return $this->dao->exists($id, $publicationId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::getCollector() */
|
||||
public function getCollector(): Collector
|
||||
{
|
||||
return app(Collector::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the map class for mapping
|
||||
* authors to their schema
|
||||
*/
|
||||
public function getSchemaMap(): maps\Schema
|
||||
{
|
||||
return app('maps')->withExtensions($this->schemaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate properties for an author
|
||||
*
|
||||
* Perform validation checks on data used to add or edit an author.
|
||||
*
|
||||
* @param Author|null $author The author being edited. Pass `null` if creating a new author
|
||||
* @param array $props A key/value array with the new data to validate
|
||||
*
|
||||
* @return array A key/value array with validation errors. Empty if no errors
|
||||
*/
|
||||
public function validate($author, $props, Submission $submission, Context $context)
|
||||
{
|
||||
$schemaService = Services::get('schema');
|
||||
$allowedLocales = $context->getSupportedSubmissionLocales();
|
||||
$primaryLocale = $submission->getData('locale');
|
||||
|
||||
$validator = ValidatorFactory::make(
|
||||
$props,
|
||||
$schemaService->getValidationRules(PKPSchemaService::SCHEMA_AUTHOR, $allowedLocales),
|
||||
[
|
||||
'country.regex' => __('validator.country.regex'),
|
||||
]
|
||||
);
|
||||
|
||||
// Check required fields
|
||||
ValidatorFactory::required(
|
||||
$validator,
|
||||
$author,
|
||||
$schemaService->getRequiredProps(PKPSchemaService::SCHEMA_AUTHOR),
|
||||
$schemaService->getMultilingualProps(PKPSchemaService::SCHEMA_AUTHOR),
|
||||
$allowedLocales,
|
||||
$primaryLocale
|
||||
);
|
||||
|
||||
// Check for input from disallowed locales
|
||||
ValidatorFactory::allowedLocales($validator, $schemaService->getMultilingualProps(PKPSchemaService::SCHEMA_AUTHOR), $allowedLocales);
|
||||
|
||||
// The publicationId must match an existing publication that is not yet published
|
||||
$validator->after(function ($validator) use ($props) {
|
||||
if (isset($props['publicationId']) && !$validator->errors()->get('publicationId')) {
|
||||
$publication = Repo::publication()->get($props['publicationId']);
|
||||
if (!$publication) {
|
||||
$validator->errors()->add('publicationId', __('author.publicationNotFound'));
|
||||
} elseif ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) {
|
||||
$validator->errors()->add('publicationId', __('author.editPublishedDisabled'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$errors = [];
|
||||
if ($validator->fails()) {
|
||||
$errors = $schemaService->formatValidationErrors($validator->errors());
|
||||
}
|
||||
|
||||
Hook::call('Author::validate', [$errors, $author, $props, $allowedLocales, $primaryLocale]);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc \PKP\services\entityProperties\EntityWriteInterface::add()
|
||||
*/
|
||||
public function add(Author $author): int
|
||||
{
|
||||
$existingSeq = $author->getData('seq');
|
||||
|
||||
if (!isset($existingSeq)) {
|
||||
$nextSeq = $this->dao->getNextSeq($author->getData('publicationId'));
|
||||
$author->setData('seq', $nextSeq);
|
||||
}
|
||||
|
||||
$authorId = $this->dao->insert($author);
|
||||
$author = Repo::author()->get($authorId);
|
||||
|
||||
Hook::call('Author::add', [$author]);
|
||||
|
||||
return $author->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc \PKP\services\entityProperties\EntityWriteInterface::edit()
|
||||
*/
|
||||
public function edit(Author $author, array $params)
|
||||
{
|
||||
$newAuthor = Repo::author()->newDataObject(array_merge($author->_data, $params));
|
||||
|
||||
Hook::call('Author::edit', [$newAuthor, $author, $params]);
|
||||
|
||||
$this->dao->update($newAuthor);
|
||||
|
||||
Repo::author()->get($newAuthor->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc \PKP\services\entityProperties\EntityWriteInterface::delete()
|
||||
*/
|
||||
public function delete(Author $author)
|
||||
{
|
||||
Hook::call('Author::delete::before', [$author]);
|
||||
$this->dao->delete($author);
|
||||
|
||||
$this->dao->resetContributorsOrder($author->getData('publicationId'));
|
||||
|
||||
Hook::call('Author::delete', [$author]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Author object from a User object
|
||||
*
|
||||
* This does not save the author in the database.
|
||||
*/
|
||||
public function newAuthorFromUser(User $user): Author
|
||||
{
|
||||
$author = Repo::author()->newDataObject();
|
||||
$author->setGivenName($user->getGivenName(null), null);
|
||||
$author->setFamilyName($user->getFamilyName(null), null);
|
||||
$author->setAffiliation($user->getAffiliation(null), null);
|
||||
$author->setCountry($user->getCountry());
|
||||
$author->setEmail($user->getEmail());
|
||||
$author->setUrl($user->getUrl());
|
||||
$author->setBiography($user->getBiography(null), null);
|
||||
$author->setIncludeInBrowse(1);
|
||||
$author->setOrcid($user->getOrcid());
|
||||
|
||||
Hook::call('Author::newAuthorFromUser', [$author, $user]);
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update author names when publication locale changes.
|
||||
*
|
||||
* @param int $publicationId
|
||||
* @param string $oldLocale
|
||||
* @param string $newLocale
|
||||
*/
|
||||
public function changePublicationLocale($publicationId, $oldLocale, $newLocale)
|
||||
{
|
||||
$authors = $this->getCollector()
|
||||
->filterByPublicationIds([$publicationId])
|
||||
->getMany();
|
||||
|
||||
foreach ($authors as $author) {
|
||||
if (empty($author->getGivenName($newLocale))) {
|
||||
if (empty($author->getFamilyName($newLocale)) && empty($author->getPreferredPublicName($newLocale))) {
|
||||
// if no name exists for the new locale
|
||||
// copy all names with the old locale to the new locale
|
||||
$author->setGivenName($author->getGivenName($oldLocale), $newLocale);
|
||||
$author->setFamilyName($author->getFamilyName($oldLocale), $newLocale);
|
||||
$author->setPreferredPublicName($author->getPreferredPublicName($oldLocale), $newLocale);
|
||||
} else {
|
||||
// if the given name does not exist, but one of the other names do exist
|
||||
// copy only the given name with the old locale to the new locale, because the given name is required
|
||||
$author->setGivenName($author->getGivenName($oldLocale), $newLocale);
|
||||
}
|
||||
|
||||
$this->dao->update($author);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the authors of a publication according to the given order of the authors in the provided author array
|
||||
*/
|
||||
public function setAuthorsOrder(int $publicationId, array $authors)
|
||||
{
|
||||
$seq = 0;
|
||||
foreach ($authors as $author) {
|
||||
$author->setData('seq', $seq);
|
||||
|
||||
$this->dao->update($author);
|
||||
|
||||
$seq++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/author/maps/Schema.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Schema
|
||||
*
|
||||
* @brief Map authors to the properties defined in the announcement schema
|
||||
*/
|
||||
|
||||
namespace PKP\author\maps;
|
||||
|
||||
use APP\author\Author;
|
||||
use APP\facades\Repo;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\security\Role;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\userGroup\UserGroup;
|
||||
use stdClass;
|
||||
|
||||
class Schema extends \PKP\core\maps\Schema
|
||||
{
|
||||
public Enumerable $collection;
|
||||
|
||||
public string $schema = PKPSchemaService::SCHEMA_AUTHOR;
|
||||
|
||||
protected LazyCollection $authorUserGroups;
|
||||
|
||||
public function __construct(PKPRequest $request, \PKP\context\Context $context, PKPSchemaService $schemaService)
|
||||
{
|
||||
parent::__construct($request, $context, $schemaService);
|
||||
|
||||
$this->authorUserGroups = Repo::userGroup()->getByRoleIds([Role::ROLE_ID_AUTHOR], $this->context->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an author
|
||||
*
|
||||
* Includes all properties in the announcement schema.
|
||||
*/
|
||||
public function map(Author $item): array
|
||||
{
|
||||
return $this->mapByProperties($this->getProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize an author
|
||||
*
|
||||
* Includes properties with the apiSummary flag in the author schema.
|
||||
*/
|
||||
public function summarize(Author $item): array
|
||||
{
|
||||
return $this->mapByProperties($this->getSummaryProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a collection of Authors
|
||||
*
|
||||
* @see self::map
|
||||
*/
|
||||
public function mapMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) {
|
||||
return $this->map($item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a collection of Authors
|
||||
*
|
||||
* @see self::summarize
|
||||
*/
|
||||
public function summarizeMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) {
|
||||
return $this->summarize($item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map schema properties of an Author to an assoc array
|
||||
*/
|
||||
protected function mapByProperties(array $props, Author $item): array
|
||||
{
|
||||
$output = [];
|
||||
foreach ($props as $prop) {
|
||||
switch ($prop) {
|
||||
case 'userGroupName':
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $this->authorUserGroups->first(fn (UserGroup $userGroup) => $userGroup->getId() === $item->getData('userGroupId'));
|
||||
$output[$prop] = $userGroup ? $userGroup->getName(null) : new stdClass();
|
||||
break;
|
||||
case 'fullName':
|
||||
$output[$prop] = $item->getFullName();
|
||||
break;
|
||||
default:
|
||||
$output[$prop] = $item->getData($prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$output = $this->schemaService->addMissingMultilingualValues($this->schema, $output, $this->context->getSupportedSubmissionLocales());
|
||||
|
||||
ksort($output);
|
||||
|
||||
return $this->withExtensions($output, $item);
|
||||
}
|
||||
}
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cache/APCCache.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 apc_false
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @see GenericCache
|
||||
*
|
||||
* @brief Provides caching based on APC's variable store.
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
class apc_false
|
||||
{
|
||||
};
|
||||
|
||||
class APCCache extends GenericCache
|
||||
{
|
||||
/**
|
||||
* Flush the cache.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$prefix = INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId();
|
||||
$info = apc_cache_info('user');
|
||||
foreach ($info['cache_list'] as $entry) {
|
||||
if (substr($entry['info'], 0, strlen($prefix)) == $prefix) {
|
||||
apc_delete($entry['info']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache.
|
||||
*
|
||||
*/
|
||||
public function getCache($id)
|
||||
{
|
||||
$key = INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId() . ':' . $id;
|
||||
$returner = unserialize(apc_fetch($key));
|
||||
if ($returner === false) {
|
||||
return $this->cacheMiss;
|
||||
}
|
||||
if ($returner instanceof apc_false) {
|
||||
$returner = false;
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
*/
|
||||
public function setCache($id, $value)
|
||||
{
|
||||
$key = INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId() . ':' . $id;
|
||||
if ($value === false) {
|
||||
$value = new apc_false();
|
||||
}
|
||||
apc_store($key, serialize($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which the data was cached.
|
||||
* Not implemented in this type of cache.
|
||||
*/
|
||||
public function getCacheTime()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entire contents of the cache.
|
||||
* WARNING: THIS DOES NOT FLUSH THE CACHE FIRST!
|
||||
*
|
||||
* @param array $contents Complete cache contents.
|
||||
*/
|
||||
public function setEntireCache($contents)
|
||||
{
|
||||
foreach ($contents as $id => $value) {
|
||||
$this->setCache($id, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cache/CacheManager.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.
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @see GenericCache
|
||||
*
|
||||
* @brief Provides cache management functions.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
use PKP\config\Config;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\Registry;
|
||||
|
||||
define('CACHE_TYPE_FILE', 1);
|
||||
define('CACHE_TYPE_OBJECT', 2);
|
||||
|
||||
class CacheManager
|
||||
{
|
||||
/**
|
||||
* Get the static instance of the cache manager.
|
||||
*
|
||||
* @return CacheManager
|
||||
*/
|
||||
public static function getManager()
|
||||
{
|
||||
$manager = & Registry::get('cacheManager', true, null);
|
||||
if ($manager === null) {
|
||||
$manager = new CacheManager();
|
||||
}
|
||||
return $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file cache.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $cacheId
|
||||
* @param callable $fallback
|
||||
*
|
||||
* @return FileCache
|
||||
*/
|
||||
public function getFileCache($context, $cacheId, $fallback)
|
||||
{
|
||||
return new FileCache(
|
||||
$context,
|
||||
$cacheId,
|
||||
$fallback,
|
||||
$this->getFileCachePath()
|
||||
);
|
||||
}
|
||||
|
||||
public function getObjectCache($context, $cacheId, $fallback)
|
||||
{
|
||||
return $this->getCache($context, $cacheId, $fallback, CACHE_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
public function getCacheImplementation($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case CACHE_TYPE_FILE: return 'file';
|
||||
case CACHE_TYPE_OBJECT: return Config::getVar('cache', 'object_cache');
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cache.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $cacheId
|
||||
* @param ?callable $fallback
|
||||
* @param string $type Type of cache: CACHE_TYPE_...
|
||||
*
|
||||
* @return GenericCache
|
||||
*/
|
||||
public function getCache($context, $cacheId, $fallback, $type = CACHE_TYPE_FILE)
|
||||
{
|
||||
switch ($this->getCacheImplementation($type)) {
|
||||
case 'xcache':
|
||||
$cache = new \PKP\cache\XCacheCache(
|
||||
$context,
|
||||
$cacheId,
|
||||
$fallback
|
||||
);
|
||||
break;
|
||||
case 'apc':
|
||||
$cache = new \PKP\cache\APCCache(
|
||||
$context,
|
||||
$cacheId,
|
||||
$fallback
|
||||
);
|
||||
break;
|
||||
case 'memcache':
|
||||
$cache = new \PKP\cache\MemcacheCache(
|
||||
$context,
|
||||
$cacheId,
|
||||
$fallback,
|
||||
Config::getVar('cache', 'memcache_hostname'),
|
||||
Config::getVar('cache', 'memcache_port')
|
||||
);
|
||||
break;
|
||||
case '': // Provide a default if not specified
|
||||
case 'file':
|
||||
$cache = $this->getFileCache($context, $cacheId, $fallback);
|
||||
break;
|
||||
case 'none':
|
||||
$cache = new \PKP\cache\GenericCache(
|
||||
$context,
|
||||
$cacheId,
|
||||
$fallback
|
||||
);
|
||||
break;
|
||||
default:
|
||||
exit("Unknown cache type \"{$type}\"!\n");
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path in which file caches will be stored.
|
||||
*
|
||||
* @return string The full path to the file cache directory
|
||||
*/
|
||||
public static function getFileCachePath()
|
||||
{
|
||||
return Core::getBaseDir() . '/cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush an entire context, if specified, or
|
||||
* the whole cache.
|
||||
*
|
||||
* @param string $context The context to flush, if only one is to be flushed
|
||||
* @param string $type The type of cache to flush
|
||||
*/
|
||||
public function flush($context = null, $type = CACHE_TYPE_FILE)
|
||||
{
|
||||
$cacheImplementation = $this->getCacheImplementation($type);
|
||||
switch ($cacheImplementation) {
|
||||
case 'xcache':
|
||||
case 'apc':
|
||||
case 'memcache':
|
||||
$junkCache = $this->getCache($context, null, null);
|
||||
$junkCache->flush();
|
||||
break;
|
||||
case 'file':
|
||||
$filePath = $this->getFileCachePath();
|
||||
$files = glob("{$filePath}/fc-" . (isset($context) ? $context . '-' : '') . '*.php');
|
||||
foreach ($files as $file) {
|
||||
@unlink($file);
|
||||
}
|
||||
break;
|
||||
case '':
|
||||
case 'none':
|
||||
// Nothing necessary.
|
||||
break;
|
||||
default:
|
||||
exit("Unknown cache type \"{$type}\"!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+150
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup cache Cache
|
||||
* Implements various forms of caching, i.e. object caches, file caches, etc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/cache/FileCache.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 FileCache
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @brief Provides caching based on machine-generated PHP code on the filesystem.
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
use Exception;
|
||||
use PKP\config\Config;
|
||||
use PKP\file\FileManager;
|
||||
|
||||
class FileCache extends GenericCache
|
||||
{
|
||||
/**
|
||||
* Connection to use for caching.
|
||||
*/
|
||||
public $filename;
|
||||
|
||||
/**
|
||||
* @var ?array The cached data
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* Instantiate a cache.
|
||||
*/
|
||||
public function __construct($context, $cacheId, $fallback, $path)
|
||||
{
|
||||
parent::__construct($context, $cacheId, $fallback);
|
||||
|
||||
$this->filename = "{$path}/fc-{$context}-" . str_replace('/', '.', $cacheId) . '.php';
|
||||
|
||||
// If the file couldn't be opened or if a lock couldn't be acquired, quit
|
||||
if (!($fp = @fopen($this->filename, 'r')) || !flock($fp, LOCK_SH)) {
|
||||
$this->cache = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reasoning: When the include below fails, it returns "false" and we have no way to determine if it's an error or a valid cache value
|
||||
set_error_handler(static fn () => throw new Exception('Failed to include file'));
|
||||
try {
|
||||
$this->cache = include $this->filename;
|
||||
} catch (Exception) {
|
||||
$this->cache = null;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the cache
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
unset($this->cache);
|
||||
$this->cache = null;
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
opcache_invalidate($this->filename, true);
|
||||
}
|
||||
@unlink($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function getCache($id)
|
||||
{
|
||||
if (!isset($this->cache)) {
|
||||
return $this->cacheMiss;
|
||||
}
|
||||
return ($this->cache[$id] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setCache($id, $value)
|
||||
{
|
||||
// Flush the cache; it will be regenerated on demand.
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entire contents of the cache.
|
||||
*/
|
||||
public function setEntireCache($contents)
|
||||
{
|
||||
if (@file_put_contents(
|
||||
$this->filename,
|
||||
'<?php return ' . var_export($contents, true) . ';',
|
||||
LOCK_EX
|
||||
) !== false) {
|
||||
$umask = Config::getVar('files', 'umask');
|
||||
if ($umask) {
|
||||
@chmod($this->filename, FileManager::FILE_MODE_MASK & ~$umask);
|
||||
}
|
||||
}
|
||||
$this->cache = $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which the data was cached.
|
||||
* If the file does not exist or an error occurs, null is returned.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getCacheTime()
|
||||
{
|
||||
$result = @filemtime($this->filename);
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
return ((int) $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entire contents of the cache in an associative array.
|
||||
*/
|
||||
public function &getContents()
|
||||
{
|
||||
if (!isset($this->cache)) {
|
||||
// Trigger a cache miss to load the cache.
|
||||
$this->get(null);
|
||||
}
|
||||
return $this->cache;
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cache/GenericCache.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 generic_cache_miss
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @brief Provides implementation-independent caching. Although this class is intended
|
||||
* to be overridden with a more specific implementation, it can be used as the
|
||||
* null cache.
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
// Pseudotype to represent a cache miss
|
||||
class generic_cache_miss
|
||||
{
|
||||
}
|
||||
|
||||
class GenericCache
|
||||
{
|
||||
/**
|
||||
* The unique string identifying the context of this cache.
|
||||
* Must be suitable for a filename.
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* The ID of this particular cache within the context
|
||||
*/
|
||||
public $cacheId;
|
||||
|
||||
public $cacheMiss;
|
||||
|
||||
/**
|
||||
* The getter fallback callback (for a cache miss)
|
||||
* This function is called with two parameters:
|
||||
* 1. The cache object that is suffering a miss
|
||||
* 2. The id of the value to fetch
|
||||
* The function is responsible for loading data into the
|
||||
* cache, using setEntireCache or setCache.
|
||||
*/
|
||||
public $fallback;
|
||||
|
||||
/**
|
||||
* Instantiate a cache.
|
||||
*/
|
||||
public function __construct($context, $cacheId, $fallback)
|
||||
{
|
||||
$this->context = $context;
|
||||
$this->cacheId = $cacheId;
|
||||
$this->fallback = $fallback;
|
||||
$this->cacheMiss = new generic_cache_miss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from cache, using the fallback if necessary.
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
$result = $this->getCache($id);
|
||||
if (is_object($result) && $result instanceof generic_cache_miss) {
|
||||
$result = call_user_func_array($this->fallback, [$this, $id]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*/
|
||||
public function set($id, $value)
|
||||
{
|
||||
return $this->setCache($id, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the cache.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entire contents of the cache. May (should) be overridden
|
||||
* by subclasses.
|
||||
*/
|
||||
public function setEntireCache($contents)
|
||||
{
|
||||
$this->flush();
|
||||
foreach ($contents as $id => $value) {
|
||||
$this->setCache($id, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function getCache($id)
|
||||
{
|
||||
return $this->cacheMiss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setCache($id, $value)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the cache. (Optionally overridden by subclasses.)
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context.
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache ID within its context
|
||||
*/
|
||||
public function getCacheId()
|
||||
{
|
||||
return $this->cacheId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which the data was cached.
|
||||
*/
|
||||
public function getCacheTime()
|
||||
{
|
||||
// Since it's not really cached, we'll consider it to have been cached just now.
|
||||
return time();
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cache/MemcacheCache.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 memcache_null
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @see GenericCache
|
||||
*
|
||||
* @brief Provides caching based on Memcache.
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
use Memcached;
|
||||
|
||||
// WARNING: This cache MUST be loaded in batch, or else many cache
|
||||
// misses will result.
|
||||
|
||||
// Pseudotypes used to represent false and null values in the cache
|
||||
class memcache_false
|
||||
{
|
||||
}
|
||||
class memcache_null
|
||||
{
|
||||
}
|
||||
|
||||
class MemcacheCache extends GenericCache
|
||||
{
|
||||
/**
|
||||
* Connection to use for caching.
|
||||
*/
|
||||
public $connection;
|
||||
|
||||
/**
|
||||
* Flag (used by Memcache::set)
|
||||
*/
|
||||
public $flag;
|
||||
|
||||
/**
|
||||
* Expiry (used by Memcache::set)
|
||||
*/
|
||||
public $expire;
|
||||
|
||||
/**
|
||||
* Instantiate a cache.
|
||||
*/
|
||||
public function __construct($context, $cacheId, $fallback, $hostname, $port)
|
||||
{
|
||||
parent::__construct($context, $cacheId, $fallback);
|
||||
$this->connection = new Memcached();
|
||||
|
||||
// FIXME This should use connection pooling
|
||||
// XXX check whether memcached server is usable
|
||||
if (!$this->connection->addServer($hostname, $port)) {
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
$this->flag = null;
|
||||
$this->expire = 3600; // 1 hour default expiry
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag (used in Memcache::set)
|
||||
*/
|
||||
public function setFlag($flag)
|
||||
{
|
||||
$this->flag = $flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiry time (used in Memcache::set)
|
||||
*/
|
||||
public function setExpiry($expiry)
|
||||
{
|
||||
$this->expire = $expiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the cache.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->connection->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function getCache($id)
|
||||
{
|
||||
$result = $this->connection->get($this->getContext() . ':' . $this->getCacheId() . ':' . $id);
|
||||
if ($this->connection->getResultCode() == Memcached::RES_NOTFOUND) {
|
||||
return $this->cacheMiss;
|
||||
}
|
||||
if ($result instanceof memcache_false) {
|
||||
$result = false;
|
||||
}
|
||||
if ($result instanceof memcache_null) {
|
||||
$result = null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setCache($id, $value)
|
||||
{
|
||||
if ($value === false) {
|
||||
$value = new memcache_false();
|
||||
} elseif ($value === null) {
|
||||
$value = new memcache_null();
|
||||
}
|
||||
return ($this->connection->set($this->getContext() . ':' . $this->getCacheId() . ':' . $id, $value, $this->expire));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the cache and free resources.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->connection->quit();
|
||||
unset($this->connection);
|
||||
$this->contextChecked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which the data was cached.
|
||||
* Note that keys expire in memcache, which means
|
||||
* that it's possible that the date will disappear
|
||||
* before the data -- in this case we'll have to
|
||||
* assume the data is still good.
|
||||
*/
|
||||
public function getCacheTime()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entire contents of the cache.
|
||||
* WARNING: THIS DOES NOT FLUSH THE CACHE FIRST!
|
||||
* This is because there is no "scope restriction"
|
||||
* for flushing within memcache and therefore
|
||||
* a flush here would flush the entire cache,
|
||||
* resulting in more subsequent calls to this function,
|
||||
* resulting in more flushes, etc.
|
||||
*/
|
||||
public function setEntireCache($contents)
|
||||
{
|
||||
foreach ($contents as $id => $value) {
|
||||
$this->setCache($id, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cache/XCacheCache.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 XCacheCache
|
||||
*
|
||||
* @ingroup cache
|
||||
*
|
||||
* @see GenericCache
|
||||
*
|
||||
* @brief Provides caching based on XCache's variable store.
|
||||
*/
|
||||
|
||||
namespace PKP\cache;
|
||||
|
||||
class XCacheCache extends GenericCache
|
||||
{
|
||||
/**
|
||||
* Flush the cache.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$prefix = INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId();
|
||||
if (function_exists('xcache_unset_by_prefix')) {
|
||||
// If possible, just flush the context
|
||||
xcache_unset_by_prefix(prefix);
|
||||
} else {
|
||||
// Otherwise, we need to do this manually
|
||||
for ($i = 0; $i < xcache_count(XC_TYPE_VAR); $i++) {
|
||||
$cache = xcache_list(XC_TYPE_VAR, $i);
|
||||
foreach ($cache['cache_list'] as $entry) {
|
||||
if (substr($entry['name'], 0, strlen($prefix)) == $prefix) {
|
||||
xcache_unset($entry['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function getCache($id)
|
||||
{
|
||||
$key = INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId() . ':' . $id;
|
||||
if (!xcache_isset($key)) {
|
||||
return $this->cacheMiss;
|
||||
}
|
||||
$returner = unserialize(xcache_get($key));
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object in the cache. This function should be overridden
|
||||
* by subclasses.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setCache($id, $value)
|
||||
{
|
||||
return (xcache_set(INDEX_FILE_LOCATION . ':' . $this->getContext() . ':' . $this->getCacheId() . ':' . $id, serialize($value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time at which the data was cached.
|
||||
* Not implemented in this type of cache.
|
||||
*/
|
||||
public function getCacheTime()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entire contents of the cache.
|
||||
* WARNING: THIS DOES NOT FLUSH THE CACHE FIRST!
|
||||
*/
|
||||
public function setEntireCache($contents)
|
||||
{
|
||||
foreach ($contents as $id => $value) {
|
||||
$this->setCache($id, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/category/Category.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 Category
|
||||
*
|
||||
* @brief Describes basic Category properties.
|
||||
*/
|
||||
|
||||
namespace PKP\category;
|
||||
|
||||
class Category extends \PKP\core\DataObject
|
||||
{
|
||||
/**
|
||||
* Get ID of context.
|
||||
*/
|
||||
public function getContextId(): int
|
||||
{
|
||||
return $this->getData('contextId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ID of context.
|
||||
*/
|
||||
public function setContextId(int $contextId)
|
||||
{
|
||||
return $this->setData('contextId', $contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID of parent category.
|
||||
*/
|
||||
public function getParentId(): ?int
|
||||
{
|
||||
return $this->getData('parentId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ID of parent category.
|
||||
*/
|
||||
public function setParentId(?int $parentId)
|
||||
{
|
||||
return $this->setData('parentId', $parentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sequence of category.
|
||||
*/
|
||||
public function getSequence(): float
|
||||
{
|
||||
return (float) $this->getData('sequence');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence of category.
|
||||
*/
|
||||
public function setSequence(float $sequence)
|
||||
{
|
||||
return $this->setData('sequence', $sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category path.
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->getData('path');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set category path.
|
||||
*/
|
||||
public function setPath(string $path)
|
||||
{
|
||||
return $this->setData('path', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized title of the category.
|
||||
*/
|
||||
public function getLocalizedTitle(): string
|
||||
{
|
||||
return $this->getLocalizedData('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title of category.
|
||||
*/
|
||||
public function getTitle(?string $locale = null)
|
||||
{
|
||||
return $this->getData('title', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title of category.
|
||||
*/
|
||||
public function setTitle($title, ?string $locale)
|
||||
{
|
||||
return $this->setData('title', $title, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized description of the category.
|
||||
*/
|
||||
public function getLocalizedDescription(): ?string
|
||||
{
|
||||
return $this->getLocalizedData('description');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description of category.
|
||||
*/
|
||||
public function getDescription(?string $locale)
|
||||
{
|
||||
return $this->getData('description', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description of category.
|
||||
*/
|
||||
public function setDescription($description, ?string $locale)
|
||||
{
|
||||
return $this->setData('description', $description, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image.
|
||||
*/
|
||||
public function getImage(): ?array
|
||||
{
|
||||
return $this->getData('image');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image.
|
||||
*/
|
||||
public function setImage(?array $image)
|
||||
{
|
||||
return $this->setData('image', $image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option how the books in this category should be sorted,
|
||||
* in the form: concat(sortBy, sortDir).
|
||||
*/
|
||||
public function getSortOption(): ?string
|
||||
{
|
||||
return $this->getData('sortOption');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the option how the books in this category should be sorted,
|
||||
* in the form: concat(sortBy, sortDir).
|
||||
*/
|
||||
public function setSortOption(?string $sortOption)
|
||||
{
|
||||
return $this->setData('sortOption', $sortOption);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\category\Category', '\Category');
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/category/Collector.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 Collector
|
||||
*
|
||||
* @brief A helper class to configure a Query Builder to get a collection of categories
|
||||
*/
|
||||
|
||||
namespace PKP\category;
|
||||
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\interfaces\CollectorInterface;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
/**
|
||||
* @template T of Category
|
||||
*/
|
||||
class Collector implements CollectorInterface
|
||||
{
|
||||
public DAO $dao;
|
||||
public ?array $contextIds = null;
|
||||
public ?array $parentIds = null;
|
||||
public ?array $paths = null;
|
||||
public ?array $publicationIds = null;
|
||||
public ?int $count = null;
|
||||
public ?int $offset = null;
|
||||
|
||||
public function __construct(DAO $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
public function getCount(): int
|
||||
{
|
||||
return $this->dao->getCount($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(): Collection
|
||||
{
|
||||
return $this->dao->getIds($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc DAO::getMany()
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(): LazyCollection
|
||||
{
|
||||
return $this->dao->getMany($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter categories by one or more contexts
|
||||
*/
|
||||
public function filterByContextIds(?array $contextIds): self
|
||||
{
|
||||
$this->contextIds = $contextIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter categories by one or more parent category IDs
|
||||
*/
|
||||
public function filterByParentIds(?array $parentIds): self
|
||||
{
|
||||
$this->parentIds = $parentIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter categories by one or more publication IDs
|
||||
*/
|
||||
public function filterByPublicationIds(?array $publicationIds): self
|
||||
{
|
||||
$this->publicationIds = $publicationIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter categories by one or more paths
|
||||
*/
|
||||
public function filterByPaths(?array $paths): self
|
||||
{
|
||||
$this->paths = $paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of objects retrieved
|
||||
*/
|
||||
public function limit(?int $count): self
|
||||
{
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the number of objects retrieved, for example to
|
||||
* retrieve the second page of contents
|
||||
*/
|
||||
public function offset(?int $offset): self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc CollectorInterface::getQueryBuilder()
|
||||
*/
|
||||
public function getQueryBuilder(): Builder
|
||||
{
|
||||
$qb = DB::table($this->dao->table . ' as c')
|
||||
->leftJoin('categories AS pc', 'c.parent_id', '=', 'pc.category_id')
|
||||
->select(['c.*']);
|
||||
|
||||
$qb->when($this->contextIds !== null, function ($query) {
|
||||
$query->whereIn('c.context_id', $this->contextIds);
|
||||
});
|
||||
|
||||
$qb->when($this->paths !== null, function ($query) {
|
||||
$query->whereIn('c.path', $this->paths);
|
||||
});
|
||||
|
||||
$qb->when($this->publicationIds !== null, function ($query) {
|
||||
$query->whereIn('c.category_id', function ($query) {
|
||||
$query->select('category_id')->from('publication_categories')->whereIn('publication_id', $this->publicationIds);
|
||||
});
|
||||
});
|
||||
|
||||
$qb->when($this->parentIds !== null, function ($query) {
|
||||
// parentIds may contain mixed values and nulls; make sure the mix translates into the query accurately
|
||||
$nonNullParentIds = array_filter($this->parentIds);
|
||||
if (count($nonNullParentIds)) {
|
||||
$query->whereIn('c.parent_id', array_filter($this->parentIds));
|
||||
}
|
||||
if (in_array(null, $this->parentIds)) {
|
||||
if (count($nonNullParentIds)) {
|
||||
$query->orWhereNull('c.parent_id');
|
||||
} else {
|
||||
$query->whereNull('c.parent_id');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$qb->orderBy(DB::raw('(COALESCE((pc.seq * 8192) + pc.category_id, 0) * 8192) + CASE WHEN pc.category_id IS NULL THEN 8192 * ((c.seq * 8192) + c.category_id) ELSE c.seq END'));
|
||||
|
||||
if (isset($this->count)) {
|
||||
$qb->limit($this->count);
|
||||
}
|
||||
|
||||
if (isset($this->offset)) {
|
||||
$qb->offset($this->offset);
|
||||
}
|
||||
|
||||
Hook::call('Category::Collector', [&$qb, $this]);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/category/DAO.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 DAO
|
||||
*
|
||||
* @brief Read and write categories to the database.
|
||||
*/
|
||||
|
||||
namespace PKP\category;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\core\traits\EntityWithParent;
|
||||
|
||||
/**
|
||||
* @template T of Category
|
||||
* @extends EntityDAO<T>
|
||||
*/
|
||||
class DAO extends EntityDAO
|
||||
{
|
||||
use EntityWithParent;
|
||||
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = \PKP\services\PKPSchemaService::SCHEMA_CATEGORY;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'categories';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'category_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryKeyColumn */
|
||||
public $primaryKeyColumn = 'category_id';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'id' => 'category_id',
|
||||
'parentId' => 'parent_id',
|
||||
'contextId' => 'context_id',
|
||||
'sequence' => 'seq',
|
||||
'path' => 'path',
|
||||
'image' => 'image',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent object ID column name
|
||||
*/
|
||||
public function getParentColumn(): string
|
||||
{
|
||||
return 'context_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new DataObject
|
||||
*/
|
||||
public function newDataObject(): Category
|
||||
{
|
||||
return app(Category::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of categories matching the configured query
|
||||
*/
|
||||
public function getCount(Collector $query): int
|
||||
{
|
||||
return $query->getQueryBuilder()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ids matching the configured query
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->pluck('c.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of categories matching the configured query
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->category_id => $this->fromRow($row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::fromRow()
|
||||
*/
|
||||
public function fromRow(object $row): Category
|
||||
{
|
||||
return parent::fromRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::insert()
|
||||
*/
|
||||
public function insert(Category $category): int
|
||||
{
|
||||
return parent::_insert($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::update()
|
||||
*/
|
||||
public function update(Category $category)
|
||||
{
|
||||
parent::_update($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::delete()
|
||||
*/
|
||||
public function delete(Category $category)
|
||||
{
|
||||
parent::_delete($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequentially renumber categories in their sequence order by context ID and optionally parent category ID.
|
||||
*
|
||||
* @param int $parentCategoryId Optional parent category ID
|
||||
*/
|
||||
public function resequenceCategories(int $contextId, ?int $parentCategoryId = null)
|
||||
{
|
||||
$categoryIds = DB::table('categories')
|
||||
->where('context_id', '=', $contextId)
|
||||
->when($parentCategoryId !== null, function ($query) use ($parentCategoryId) {
|
||||
$query->where($parentCategoryId, '=', $parentCategoryId);
|
||||
})->pluck('category_id');
|
||||
|
||||
$i = 0;
|
||||
foreach ($categoryIds as $categoryId) {
|
||||
DB::table('categories')->where('category_id', '=', $categoryId)->update(['seq' => ++$i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a publication to a category
|
||||
*/
|
||||
public function insertPublicationAssignment(int $categoryId, int $publicationId)
|
||||
{
|
||||
DB::table('publication_categories')->insert([
|
||||
'category_id' => $categoryId,
|
||||
'publication_id' => $publicationId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the assignment of a category to a publication
|
||||
*/
|
||||
public function deletePublicationAssignments(int $publicationId)
|
||||
{
|
||||
DB::table('publication_categories')->where('publication_id', '=', $publicationId)->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/category/Repository.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Repository
|
||||
*
|
||||
* @brief A repository to find and manage categories.
|
||||
*/
|
||||
|
||||
namespace PKP\category;
|
||||
|
||||
use APP\core\Request;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\validation\ValidatorFactory;
|
||||
|
||||
class Repository
|
||||
{
|
||||
/** @var DAO $dao */
|
||||
public $dao;
|
||||
|
||||
/** @var string $schemaMap The name of the class to map this entity to its schema */
|
||||
public $schemaMap = maps\Schema::class;
|
||||
|
||||
/** @var Request $request */
|
||||
protected $request;
|
||||
|
||||
/** @var PKPSchemaService<Category> $schemaService */
|
||||
protected $schemaService;
|
||||
|
||||
|
||||
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
$this->request = $request;
|
||||
$this->schemaService = $schemaService;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::newDataObject() */
|
||||
public function newDataObject(array $params = []): Category
|
||||
{
|
||||
$object = $this->dao->newDataObject();
|
||||
if (!empty($params)) {
|
||||
$object->setAllData($params);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::get() */
|
||||
public function get(int $id, int $contextId = null): ?Category
|
||||
{
|
||||
return $this->dao->get($id, $contextId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::exists() */
|
||||
public function exists(int $id, int $contextId = null): bool
|
||||
{
|
||||
return $this->dao->exists($id, $contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the breadcrumb of a category
|
||||
*
|
||||
* @return string For example: Social Sciences > Anthropology
|
||||
*/
|
||||
public function getBreadcrumb(Category $category, ?Category $parent = null): string
|
||||
{
|
||||
return !$parent
|
||||
? $category->getLocalizedTitle()
|
||||
: __('common.categorySeparator', [
|
||||
'parent' => $parent->getLocalizedTitle(),
|
||||
'child' => $category->getLocalizedTitle()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the breadcrumbs for a Collection of categories
|
||||
*/
|
||||
public function getBreadcrumbs(LazyCollection $categories): LazyCollection
|
||||
{
|
||||
return $categories->map(function (Category $category) use ($categories) {
|
||||
/** @var ?Category $parent */
|
||||
$parent = $categories->first(
|
||||
fn (Category $c) => $c->getId() === $category->getParentId()
|
||||
);
|
||||
return $this->getBreadcrumb($category, $parent);
|
||||
});
|
||||
}
|
||||
|
||||
/** @copydoc DAO::getCollector() */
|
||||
public function getCollector(): Collector
|
||||
{
|
||||
return app(Collector::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the map class for mapping
|
||||
* announcements to their schema
|
||||
*/
|
||||
public function getSchemaMap(): maps\Schema
|
||||
{
|
||||
return app('maps')->withExtensions($this->schemaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate properties for a category
|
||||
*
|
||||
* Perform validation checks on data used to add or edit a category.
|
||||
*
|
||||
* @param array $props A key/value array with the new data to validate
|
||||
* @param array $allowedLocales The context's supported locales
|
||||
* @param string $primaryLocale The context's primary locale
|
||||
*
|
||||
* @return array A key/value array with validation errors. Empty if no errors
|
||||
*/
|
||||
public function validate(?Category $object, array $props, array $allowedLocales, string $primaryLocale): array
|
||||
{
|
||||
$validator = ValidatorFactory::make(
|
||||
$props,
|
||||
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales),
|
||||
[]
|
||||
);
|
||||
|
||||
// Check required fields
|
||||
ValidatorFactory::required(
|
||||
$validator,
|
||||
$object,
|
||||
$this->schemaService->getRequiredProps($this->dao->schema),
|
||||
$this->schemaService->getMultilingualProps($this->dao->schema),
|
||||
$allowedLocales,
|
||||
$primaryLocale
|
||||
);
|
||||
|
||||
// Check for input from disallowed locales
|
||||
ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales);
|
||||
|
||||
$errors = [];
|
||||
|
||||
if ($validator->fails()) {
|
||||
$errors = $this->schemaService->formatValidationErrors($validator->errors());
|
||||
}
|
||||
|
||||
Hook::call('Category::validate', [&$errors, $object, $props, $allowedLocales, $primaryLocale]);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::insert() */
|
||||
public function add(Category $category): int
|
||||
{
|
||||
$id = $this->dao->insert($category);
|
||||
Hook::call('Category::add', [$category]);
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::update() */
|
||||
public function edit(Category $category, array $params)
|
||||
{
|
||||
$newCategory = clone $category;
|
||||
$newCategory->setAllData(array_merge($newCategory->_data, $params));
|
||||
|
||||
Hook::call('Category::edit', [$newCategory, $category, $params]);
|
||||
|
||||
$this->dao->update($newCategory);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::delete() */
|
||||
public function delete(Category $category)
|
||||
{
|
||||
Hook::call('Category::delete::before', [$category]);
|
||||
$this->dao->delete($category);
|
||||
Hook::call('Category::delete', [$category]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a collection of categories
|
||||
*/
|
||||
public function deleteMany(Collector $collector)
|
||||
{
|
||||
foreach ($collector->getMany() as $category) {
|
||||
/** @var Category $category */
|
||||
$this->delete($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/category/maps/Schema.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Schema
|
||||
*
|
||||
* @brief Map categories to the properties defined in the category schema
|
||||
*/
|
||||
|
||||
namespace PKP\category\maps;
|
||||
|
||||
use Illuminate\Support\Enumerable;
|
||||
use PKP\category\Category;
|
||||
use PKP\services\PKPSchemaService;
|
||||
|
||||
class Schema extends \PKP\core\maps\Schema
|
||||
{
|
||||
public string $schema = PKPSchemaService::SCHEMA_CATEGORY;
|
||||
|
||||
/**
|
||||
* Map a category
|
||||
*
|
||||
* Includes all properties in the category schema.
|
||||
*/
|
||||
public function map(Category $category): array
|
||||
{
|
||||
return $this->mapByProperties($this->getProps(), $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a category
|
||||
*
|
||||
* Includes properties with the apiSummary flag in the category schema.
|
||||
*/
|
||||
public function summarize(Category $category): array
|
||||
{
|
||||
return $this->mapByProperties($this->getSummaryProps(), $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a collection of Categories
|
||||
*
|
||||
* @see self::map
|
||||
*/
|
||||
public function mapMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($category) {
|
||||
return $this->map($category);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a collection of Categories
|
||||
*
|
||||
* @see self::summarize
|
||||
*/
|
||||
public function summarizeMany(Enumerable $collection): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($category) {
|
||||
return $this->summarize($category);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map schema properties of a Category to an assoc array
|
||||
*/
|
||||
protected function mapByProperties(array $props, Category $category): array
|
||||
{
|
||||
$output = [];
|
||||
|
||||
foreach ($props as $prop) {
|
||||
switch ($prop) {
|
||||
default:
|
||||
$output[$prop] = $category->getData($prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup citation Citation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/citation/Citation.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 Citation
|
||||
*
|
||||
* @ingroup citation
|
||||
*
|
||||
* @brief Class representing a citation (bibliographic reference)
|
||||
*/
|
||||
|
||||
namespace PKP\citation;
|
||||
|
||||
use PKP\core\PKPString;
|
||||
|
||||
class Citation extends \PKP\core\DataObject
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $rawCitation an unparsed citation string
|
||||
*/
|
||||
public function __construct($rawCitation = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->setRawCitation($rawCitation);
|
||||
}
|
||||
|
||||
//
|
||||
// Getters and Setters
|
||||
//
|
||||
|
||||
/**
|
||||
* Replace URLs through HTML links, if the citation does not already contain HTML links
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCitationWithLinks()
|
||||
{
|
||||
$citation = $this->getRawCitation();
|
||||
if (stripos($citation, '<a href=') === false) {
|
||||
$citation = preg_replace_callback(
|
||||
'#(http|https|ftp)://[\d\w\.-]+\.[\w\.]{2,6}[^\s\]\[\<\>]*/?#',
|
||||
function ($matches) {
|
||||
$trailingDot = in_array($char = substr($matches[0], -1), ['.', ',']);
|
||||
$url = rtrim($matches[0], '.,');
|
||||
return "<a href=\"{$url}\">{$url}</a>" . ($trailingDot ? $char : '');
|
||||
},
|
||||
$citation
|
||||
);
|
||||
}
|
||||
return $citation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rawCitation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawCitation()
|
||||
{
|
||||
return $this->getData('rawCitation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rawCitation
|
||||
*
|
||||
* @param string $rawCitation
|
||||
*/
|
||||
public function setRawCitation($rawCitation)
|
||||
{
|
||||
$rawCitation = $this->_cleanCitationString($rawCitation);
|
||||
$this->setData('rawCitation', $rawCitation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sequence number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSequence()
|
||||
{
|
||||
return $this->getData('seq');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sequence number
|
||||
*
|
||||
* @param int $seq
|
||||
*/
|
||||
public function setSequence($seq)
|
||||
{
|
||||
$this->setData('seq', $seq);
|
||||
}
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
/**
|
||||
* Take a citation string and clean/normalize it
|
||||
*
|
||||
* @param string $citationString
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _cleanCitationString($citationString)
|
||||
{
|
||||
// 1) Strip slashes and whitespace
|
||||
$citationString = trim(stripslashes($citationString));
|
||||
|
||||
// 2) Normalize whitespace
|
||||
$citationString = PKPString::regexp_replace('/[\s]+/', ' ', $citationString);
|
||||
|
||||
return $citationString;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\citation\Citation', '\Citation');
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/citation/CitationDAO.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 CitationDAO
|
||||
*
|
||||
* @ingroup citation
|
||||
*
|
||||
* @see Citation
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Citation objects
|
||||
*/
|
||||
|
||||
namespace PKP\citation;
|
||||
|
||||
use PKP\db\DAOResultFactory;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class CitationDAO extends \PKP\db\DAO
|
||||
{
|
||||
/**
|
||||
* Insert a new citation.
|
||||
*
|
||||
* @param Citation $citation
|
||||
*
|
||||
* @return int the new citation id
|
||||
*/
|
||||
public function insertObject($citation)
|
||||
{
|
||||
$seq = $citation->getSequence();
|
||||
if (!(is_numeric($seq) && $seq > 0)) {
|
||||
// Find the latest sequence number
|
||||
$result = $this->retrieve(
|
||||
'SELECT MAX(seq) AS lastseq FROM citations
|
||||
WHERE publication_id = ?',
|
||||
[(int)$citation->getData('publicationId')]
|
||||
);
|
||||
$row = $result->current();
|
||||
$citation->setSequence($row ? $row->lastseq + 1 : 1);
|
||||
}
|
||||
|
||||
$this->update(
|
||||
sprintf('INSERT INTO citations
|
||||
(publication_id, raw_citation, seq)
|
||||
VALUES
|
||||
(?, ?, ?)'),
|
||||
[
|
||||
(int) $citation->getData('publicationId'),
|
||||
$citation->getRawCitation(),
|
||||
(int) $seq
|
||||
]
|
||||
);
|
||||
$citation->setId($this->getInsertId());
|
||||
$this->_updateObjectMetadata($citation);
|
||||
return $citation->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a citation by id.
|
||||
*
|
||||
* @param int $citationId
|
||||
*
|
||||
* @return ?Citation
|
||||
*/
|
||||
public function getById($citationId)
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM citations WHERE citation_id = ?',
|
||||
[$citationId]
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row ? $this->_fromRow((array) $row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import citations from a raw citation list of the particular publication.
|
||||
*
|
||||
* @param int $publicationId
|
||||
* @param string $rawCitationList
|
||||
*/
|
||||
public function importCitations($publicationId, $rawCitationList)
|
||||
{
|
||||
assert(is_numeric($publicationId));
|
||||
$publicationId = (int) $publicationId;
|
||||
|
||||
$existingCitations = $this->getByPublicationId($publicationId)->toAssociativeArray();
|
||||
|
||||
// Remove existing citations.
|
||||
$this->deleteByPublicationId($publicationId);
|
||||
|
||||
// Tokenize raw citations
|
||||
$citationTokenizer = new CitationListTokenizerFilter();
|
||||
$citationStrings = $citationTokenizer->execute($rawCitationList);
|
||||
|
||||
// Instantiate and persist citations
|
||||
$importedCitations = [];
|
||||
if (is_array($citationStrings)) {
|
||||
foreach ($citationStrings as $seq => $citationString) {
|
||||
if (!empty(trim($citationString))) {
|
||||
$citation = new Citation($citationString);
|
||||
// Set the publication
|
||||
$citation->setData('publicationId', $publicationId);
|
||||
// Set the counter
|
||||
$citation->setSequence($seq + 1);
|
||||
|
||||
$this->insertObject($citation);
|
||||
|
||||
$importedCitations[] = $citation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Hook::call('CitationDAO::afterImportCitations', [$publicationId, $existingCitations, $importedCitations]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of citations matching a particular publication id.
|
||||
*
|
||||
* @param int $publicationId
|
||||
* @param ?\PKP\db\DBResultRange $rangeInfo
|
||||
*
|
||||
* @return DAOResultFactory<Citation> containing matching Citations
|
||||
*/
|
||||
public function getByPublicationId($publicationId, $rangeInfo = null)
|
||||
{
|
||||
$result = $this->retrieveRange(
|
||||
'SELECT *
|
||||
FROM citations
|
||||
WHERE publication_id = ?
|
||||
ORDER BY seq, citation_id',
|
||||
[(int)$publicationId],
|
||||
$rangeInfo
|
||||
);
|
||||
return new DAOResultFactory($result, $this, '_fromRow', ['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing citation.
|
||||
*
|
||||
* @param Citation $citation
|
||||
*/
|
||||
public function updateObject($citation)
|
||||
{
|
||||
$returner = $this->update(
|
||||
'UPDATE citations
|
||||
SET publication_id = ?,
|
||||
raw_citation = ?,
|
||||
seq = ?
|
||||
WHERE citation_id = ?',
|
||||
[
|
||||
(int) $citation->getData('publicationId'),
|
||||
$citation->getRawCitation(),
|
||||
(int) $citation->getSequence(),
|
||||
(int) $citation->getId()
|
||||
]
|
||||
);
|
||||
$this->_updateObjectMetadata($citation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a citation.
|
||||
*
|
||||
* @param Citation $citation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteObject($citation)
|
||||
{
|
||||
return $this->deleteById($citation->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a citation by id.
|
||||
*
|
||||
* @param int $citationId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteById($citationId)
|
||||
{
|
||||
$this->update('DELETE FROM citation_settings WHERE citation_id = ?', [(int) $citationId]);
|
||||
return $this->update('DELETE FROM citations WHERE citation_id = ?', [(int) $citationId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all citations matching a particular publication id.
|
||||
*
|
||||
* @param int $publicationId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteByPublicationId($publicationId)
|
||||
{
|
||||
$citations = $this->getByPublicationId($publicationId);
|
||||
while ($citation = $citations->next()) {
|
||||
$this->deleteById($citation->getId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Private helper methods
|
||||
//
|
||||
/**
|
||||
* Construct a new citation object.
|
||||
*
|
||||
* @return Citation
|
||||
*/
|
||||
public function _newDataObject()
|
||||
{
|
||||
return new Citation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to return a citation object from a
|
||||
* row.
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return Citation
|
||||
*/
|
||||
public function _fromRow($row)
|
||||
{
|
||||
$citation = $this->_newDataObject();
|
||||
$citation->setId((int)$row['citation_id']);
|
||||
$citation->setData('publicationId', (int)$row['publication_id']);
|
||||
$citation->setRawCitation($row['raw_citation']);
|
||||
$citation->setSequence((int)$row['seq']);
|
||||
|
||||
$this->getDataObjectSettings('citation_settings', 'citation_id', $row['citation_id'], $citation);
|
||||
|
||||
return $citation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the citation meta-data
|
||||
*
|
||||
* @param Citation $citation
|
||||
*/
|
||||
public function _updateObjectMetadata($citation)
|
||||
{
|
||||
$this->updateDataObjectSettings('citation_settings', $citation, ['citation_id' => $citation->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\citation\CitationDAO', '\CitationDAO');
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/citation/CitationListTokenizerFilter.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 CitationListTokenizerFilter
|
||||
*
|
||||
* @ingroup classes_citation
|
||||
*
|
||||
* @brief Class that takes an unformatted list of citations
|
||||
* and returns an array of raw citation strings.
|
||||
*/
|
||||
|
||||
namespace PKP\citation;
|
||||
|
||||
use PKP\core\PKPString;
|
||||
use PKP\filter\Filter;
|
||||
|
||||
class CitationListTokenizerFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setDisplayName('Split a reference list into separate citations');
|
||||
|
||||
parent::__construct('primitive::string', 'primitive::string[]');
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return mixed array
|
||||
*/
|
||||
public function &process(&$input)
|
||||
{
|
||||
// The default implementation assumes that raw citations are
|
||||
// separated with line endings.
|
||||
// 1) Remove empty lines and normalize line endings.
|
||||
$input = PKPString::regexp_replace('/[\r\n]+/s', "\n", $input);
|
||||
// 2) Remove trailing/leading line breaks.
|
||||
$input = trim($input, "\n");
|
||||
// 3) Break up at line endings.
|
||||
if (empty($input)) {
|
||||
$citations = [];
|
||||
} else {
|
||||
$citations = explode("\n", $input);
|
||||
}
|
||||
// 4) Remove numbers from the beginning of each citation.
|
||||
foreach ($citations as $index => $citation) {
|
||||
$citations[$index] = PKPString::regexp_replace('/^\s*[\[#]?[0-9]+[.)\]]?\s*/', '', $citation);
|
||||
}
|
||||
|
||||
return $citations;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\citation\CitationListTokenizerFilter', '\CitationListTokenizerFilter');
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup tools Tools
|
||||
* Implements command-line management tools for PKP software.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/cliTool/CommandLineTool.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 CommandLineTool
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief Initialization code for command-line scripts.
|
||||
*
|
||||
* FIXME: Write a PKPCliRequest and PKPCliRouter class and use the dispatcher
|
||||
* to bootstrap and route tool requests.
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\PageRouter;
|
||||
use APP\facades\Repo;
|
||||
use PKP\core\Registry;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\security\Role;
|
||||
use PKP\session\SessionManager;
|
||||
use PKP\user\User;
|
||||
use PKP\config\Config;
|
||||
|
||||
/** Initialization code */
|
||||
define('PWD', getcwd());
|
||||
chdir(dirname(INDEX_FILE_LOCATION)); /* Change to base directory */
|
||||
if (!defined('STDIN')) {
|
||||
define('STDIN', fopen('php://stdin', 'r'));
|
||||
}
|
||||
require_once './lib/pkp/includes/bootstrap.php';
|
||||
SessionManager::disable();
|
||||
|
||||
class CommandLineTool
|
||||
{
|
||||
/** @var string the script being executed */
|
||||
public $scriptName;
|
||||
|
||||
/** @vary array Command-line arguments */
|
||||
public $argv;
|
||||
|
||||
/** @var string the username provided */
|
||||
public ?string $username = null;
|
||||
|
||||
/** @var \PKP\user\User the user provided */
|
||||
public ?User $user = null;
|
||||
|
||||
public function __construct($argv = [])
|
||||
{
|
||||
// Initialize the request object with a page router
|
||||
$application = Application::get();
|
||||
$request = $application->getRequest();
|
||||
|
||||
// FIXME: Write and use a CLIRouter here (see classdoc)
|
||||
$router = new PageRouter();
|
||||
$router->setApplication($application);
|
||||
$request->setRouter($router);
|
||||
|
||||
// Initialize the locale and load generic plugins.
|
||||
PluginRegistry::loadCategory('generic');
|
||||
|
||||
$this->argv = isset($argv) && is_array($argv) ? $argv : [];
|
||||
|
||||
if (isset($_SERVER['SERVER_NAME'])) {
|
||||
exit('This script can only be executed from the command-line');
|
||||
}
|
||||
|
||||
$this->scriptName = isset($this->argv[0]) ? array_shift($this->argv) : '';
|
||||
|
||||
if (Config::getVar('general', 'installed')) $this->checkArgsForUsername();
|
||||
|
||||
if (isset($this->argv[0]) && $this->argv[0] == '-h') {
|
||||
$this->exitWithUsageMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public function usage()
|
||||
{
|
||||
}
|
||||
|
||||
private function checkArgsForUsername()
|
||||
{
|
||||
$usernameKeyPos = array_search('--user_name', $this->argv);
|
||||
if (!$usernameKeyPos) {
|
||||
$usernameKeyPos = array_search('-u', $this->argv);
|
||||
}
|
||||
|
||||
if ($usernameKeyPos) {
|
||||
$usernamePos = $usernameKeyPos + 1;
|
||||
if (count($this->argv) >= $usernamePos + 1) {
|
||||
$this->username = $this->argv[$usernamePos];
|
||||
|
||||
unset($this->argv[$usernamePos]);
|
||||
}
|
||||
|
||||
unset($this->argv[$usernameKeyPos]);
|
||||
}
|
||||
|
||||
if ($this->username) {
|
||||
$user = Repo::user()->getByUsername($this->username, true);
|
||||
|
||||
$this->setUser($user);
|
||||
}
|
||||
|
||||
if (!$this->user) {
|
||||
$adminGroups = Repo::userGroup()->getArrayIdByRoleId(Role::ROLE_ID_SITE_ADMIN);
|
||||
|
||||
if (count($adminGroups)) {
|
||||
$groupUsers = Repo::user()->getCollector()
|
||||
->filterByUserGroupIds([$adminGroups[0]])
|
||||
->getMany();
|
||||
|
||||
if ($groupUsers->isNotEmpty()) {
|
||||
$this->setUser($groupUsers->first());
|
||||
} else {
|
||||
$this->exitWithUsageMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user for the CLI Tool
|
||||
*
|
||||
* @param \PKP\user\User $user The user to set as the execution user of this CLI command
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
$registeredUser = Registry::get('user', true, null);
|
||||
if (!isset($registeredUser)) {
|
||||
/**
|
||||
* This is used in order to reconcile with possible $request->getUser()
|
||||
* used inside import processes, when the import is done by CLI tool.
|
||||
*/
|
||||
if ($user) {
|
||||
Registry::set('user', $user);
|
||||
$this->user = $user;
|
||||
}
|
||||
} else {
|
||||
$this->user = $registeredUser;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit the CLI tool if an error occurs
|
||||
*/
|
||||
public function exitWithUsageMessage()
|
||||
{
|
||||
$this->usage();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\cliTool\CommandLineTool', '\CommandLineTool');
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file lib/pkp/classes/cliTool/ConvertLogFileTool.php
|
||||
*
|
||||
* Copyright (c) 2022 Simon Fraser University
|
||||
* Copyright (c) 2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ConvertLogFileTool
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief Tool to convert usage stats log file (used in releases < 3.4) into the new format.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use PKP\cliTool\traits\ConvertLogFile;
|
||||
|
||||
abstract class ConvertLogFileTool extends \PKP\cliTool\CommandLineTool
|
||||
{
|
||||
use ConvertLogFile;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $argv command-line arguments (see usage)
|
||||
*/
|
||||
public function __construct($argv = [])
|
||||
{
|
||||
parent::__construct($argv);
|
||||
$this->__constructTrait();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cliTool/InstallTool.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 installTool
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief CLI tool for installing a PKP app.
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use APP\install\Install;
|
||||
use PKP\install\form\InstallForm;
|
||||
|
||||
class InstallTool extends \PKP\cliTool\CommandLineTool
|
||||
{
|
||||
/** @var array installation parameters */
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* Print command usage information.
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
echo "Install tool\n"
|
||||
. "Usage: {$this->scriptName}\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the script.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if ($this->readParams()) {
|
||||
$this->install();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform installation.
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
$installer = new Install($this->params);
|
||||
$installer->setLogger($this);
|
||||
|
||||
if ($installer->execute()) {
|
||||
if (count($installer->getNotes()) > 0) {
|
||||
printf("\nRelease Notes\n");
|
||||
printf("----------------------------------------\n");
|
||||
foreach ($installer->getNotes() as $note) {
|
||||
printf("%s\n\n", $note);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$installer->wroteConfig()) {
|
||||
printf("\nNew config.inc.php:\n");
|
||||
printf("----------------------------------------\n");
|
||||
echo $installer->getConfigContents();
|
||||
printf("----------------------------------------\n");
|
||||
}
|
||||
|
||||
$newVersion = $installer->getNewVersion();
|
||||
printf("Successfully installed version %s\n", $newVersion->getVersionString(false));
|
||||
} else {
|
||||
printf("ERROR: Installation failed: %s\n", $installer->getErrorString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read installation parameters from stdin.
|
||||
* FIXME: May want to implement an abstract "CLIForm" class handling input/validation.
|
||||
* FIXME: Use readline if available?
|
||||
*/
|
||||
public function readParams()
|
||||
{
|
||||
$installForm = new InstallForm(null); // Request object not available to CLI
|
||||
|
||||
// Locale Settings
|
||||
$this->printTitle('installer.localeSettings');
|
||||
$this->readParamOptions('locale', 'locale.primary', $installForm->supportedLocales, 'en');
|
||||
$this->readParamOptions('additionalLocales', 'installer.additionalLocales', $installForm->supportedLocales, '', true);
|
||||
|
||||
// File Settings
|
||||
$this->printTitle('installer.fileSettings');
|
||||
$this->readParam('filesDir', 'installer.filesDir');
|
||||
|
||||
// Administrator Account
|
||||
$this->printTitle('installer.administratorAccount');
|
||||
$this->readParam('adminUsername', 'user.username');
|
||||
@`/bin/stty -echo`;
|
||||
do {
|
||||
$this->readParam('adminPassword', 'user.password');
|
||||
printf("\n");
|
||||
$this->readParam('adminPassword2', 'user.repeatPassword');
|
||||
printf("\n");
|
||||
} while ($this->params['adminPassword'] != $this->params['adminPassword2']);
|
||||
@`/bin/stty echo`;
|
||||
$this->readParam('adminEmail', 'user.email');
|
||||
|
||||
// Database Settings
|
||||
$this->printTitle('installer.databaseSettings');
|
||||
$this->readParamOptions('databaseDriver', 'installer.databaseDriver', $installForm->getDatabaseDriversOptions());
|
||||
$this->readParam('databaseHost', 'installer.databaseHost', '');
|
||||
$this->readParam('databaseUsername', 'installer.databaseUsername', '');
|
||||
$this->readParam('databasePassword', 'installer.databasePassword', '');
|
||||
$this->readParam('databaseName', 'installer.databaseName');
|
||||
|
||||
// Miscellaneous Settings
|
||||
$this->printTitle('installer.miscSettings');
|
||||
$this->readParam('oaiRepositoryId', 'installer.oaiRepositoryId');
|
||||
|
||||
$this->readParamBoolean('enableBeacon', 'installer.beacon.enable', 'Y');
|
||||
|
||||
printf("\n*** ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print input section title.
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
public function printTitle($title)
|
||||
{
|
||||
printf("\n%s\n%s\n%s\n", str_repeat('-', 80), __($title), str_repeat('-', 80));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a line of user input.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function readInput()
|
||||
{
|
||||
$value = trim(fgets(STDIN));
|
||||
if ($value === false || feof(STDIN)) {
|
||||
printf("\n");
|
||||
exit(0);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a string parameter.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $prompt
|
||||
* @param string $defaultValue
|
||||
*/
|
||||
public function readParam($name, $prompt, $defaultValue = null)
|
||||
{
|
||||
do {
|
||||
if (isset($defaultValue)) {
|
||||
printf('%s (%s): ', __($prompt), $defaultValue !== '' ? $defaultValue : __('common.none'));
|
||||
} else {
|
||||
printf('%s: ', __($prompt));
|
||||
}
|
||||
|
||||
$value = $this->readInput();
|
||||
|
||||
if ($value === '' && isset($defaultValue)) {
|
||||
$value = $defaultValue;
|
||||
}
|
||||
} while ($value === '' && $defaultValue !== '');
|
||||
$this->params[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user for yes/no input.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $prompt
|
||||
* @param string $default default value, 'Y' or 'N'
|
||||
*/
|
||||
public function readParamBoolean($name, $prompt, $default = 'N')
|
||||
{
|
||||
if ($default == 'N') {
|
||||
printf('%s [y/N] ', __($prompt));
|
||||
$value = $this->readInput();
|
||||
$this->params[$name] = (int)(strtolower(substr(trim($value), 0, 1)) == 'y');
|
||||
} else {
|
||||
printf('%s [Y/n] ', __($prompt));
|
||||
$value = $this->readInput();
|
||||
$this->params[$name] = (int)(strtolower(substr(trim($value), 0, 1)) != 'n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a parameter from a set of options.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $prompt
|
||||
* @param array $options
|
||||
* @param null|mixed $defaultValue
|
||||
*/
|
||||
public function readParamOptions($name, $prompt, $options, $defaultValue = null, $allowMultiple = false)
|
||||
{
|
||||
do {
|
||||
printf("%s\n", __($prompt));
|
||||
foreach ($options as $k => $v) {
|
||||
printf(" %-10s %s\n", '[' . $k . ']', $v);
|
||||
}
|
||||
if ($allowMultiple) {
|
||||
printf(" (%s)\n", __('installer.form.separateMultiple'));
|
||||
}
|
||||
if (isset($defaultValue)) {
|
||||
printf('%s (%s): ', __('common.select'), $defaultValue !== '' ? $defaultValue : __('common.none'));
|
||||
} else {
|
||||
printf('%s: ', __('common.select'));
|
||||
}
|
||||
|
||||
$value = $this->readInput();
|
||||
|
||||
if ($value === '' && isset($defaultValue)) {
|
||||
$value = $defaultValue;
|
||||
}
|
||||
|
||||
$values = [];
|
||||
if ($value !== '') {
|
||||
if ($allowMultiple) {
|
||||
$values = ($value === '' ? [] : preg_split('/\s*,\s*/', $value));
|
||||
} else {
|
||||
$values = [$value];
|
||||
}
|
||||
foreach ($values as $k) {
|
||||
if (!isset($options[$k])) {
|
||||
$value = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($value === '' && $defaultValue !== '');
|
||||
|
||||
if ($allowMultiple) {
|
||||
$this->params[$name] = $values;
|
||||
} else {
|
||||
$this->params[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log install message to stdout.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function log($message)
|
||||
{
|
||||
printf("[%s]\n", $message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\cliTool\InstallTool', '\InstallTool');
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file lib/pkp/classes/cliTool/MergeUsersTool.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 mergeUsers
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief CLI tool for merging two user accounts.
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use APP\facades\Repo;
|
||||
|
||||
class MergeUsersTool extends \PKP\cliTool\CommandLineTool
|
||||
{
|
||||
/** @var string $targetSpecifier */
|
||||
public $targetSpecifier;
|
||||
|
||||
/** @var array $mergeSpecifier */
|
||||
public $mergeSpecifiers;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $argv command-line arguments
|
||||
*/
|
||||
public function __construct($argv = [])
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
if (!isset($this->argv[0]) || !isset($this->argv[1])) {
|
||||
$this->usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$this->targetSpecifier = $this->argv[0];
|
||||
$this->mergeSpecifiers = array_slice($this->argv, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print command usage information.
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
echo "Merge users tool\n"
|
||||
. "Use this tool to merge two or more user accounts.\n\n"
|
||||
. "Usage: {$this->scriptName} targetUsername mergeUsername1 [mergeUsername2] [...]\n"
|
||||
. "targetUsername: The target username for assets to be transferred to.\n"
|
||||
. "mergeUsername1: The username for the account to be merged. All assets (e.g.\n"
|
||||
. " submissions) associated with this user account will be\n"
|
||||
. " transferred to the user account that corresponds to\n"
|
||||
. " targetUsername. The user account that corresponds\n"
|
||||
. " to mergeUsername1 will be deleted.\n\n"
|
||||
. "Multiple users to merge can be specified in the same command, e.g.:\n\n"
|
||||
. "{$this->scriptName} myUsername spamUser1 spamUser2 spamUser3\n\n"
|
||||
. "This will merge users with username \"spamUser1\", \"spamUser2\", and\n"
|
||||
. "\"spamUser3\" into the account with username \"myUsername\".\n\n"
|
||||
. "Users can be specified by ID by entering usernames of the form \"id=x\"\n"
|
||||
. "with the user ID in place of \"x\", e.g.:\n\n"
|
||||
. "{$this->scriptName} myUsername id=234 id=456\n\n"
|
||||
. "Usernames and IDs may be mixed as desired.\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the merge users command.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$targetUser = $this->_getUserBySpecifier($this->targetSpecifier);
|
||||
if (!$targetUser) {
|
||||
echo "Error: \"{$this->targetSpecifier}\" does not specify a valid user.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Build a list of usernames and IDs, checking for missing users before doing anything.
|
||||
$mergeArray = [];
|
||||
foreach ($this->mergeSpecifiers as $specifier) {
|
||||
$mergeUser = $this->_getUserBySpecifier($specifier);
|
||||
if (!$mergeUser) {
|
||||
echo "Error: \"{$specifier}\" does not specify a valid user.\n";
|
||||
exit(2);
|
||||
}
|
||||
if ($mergeUser->getId() == $targetUser->getId()) {
|
||||
echo "Error: Cannot merge an account into itself.\n";
|
||||
exit(3);
|
||||
}
|
||||
$mergeArray[$mergeUser->getId()] = $mergeUser->getUsername();
|
||||
}
|
||||
|
||||
// Merge the accounts.
|
||||
foreach ($mergeArray as $userId => $username) {
|
||||
Repo::user()->mergeUsers((int) $userId, $targetUser->getId());
|
||||
}
|
||||
|
||||
if (count($mergeArray) == 1) {
|
||||
echo "Merge completed: \"{$username}\" merged into \"" . $targetUser->getUsername() . "\".\n";
|
||||
} else {
|
||||
echo 'Merge completed: ' . count($mergeArray) . ' users merged into "' . $targetUser->getUsername() . "\".\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a username by specifier, i.e. username or id=xyz.
|
||||
*
|
||||
* @param string $specifier The specifier
|
||||
*
|
||||
* @return \PKP\user\User|null
|
||||
*/
|
||||
protected function _getUserBySpecifier($specifier)
|
||||
{
|
||||
if (substr($specifier, 0, 3) == 'id=') {
|
||||
$userId = substr($specifier, 3);
|
||||
if (!ctype_digit($userId)) {
|
||||
return null;
|
||||
}
|
||||
return Repo::user()->get((int) $userId, true);
|
||||
}
|
||||
return Repo::user()->getByUsername($specifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\cliTool\MergeUsersTool', '\MergeUsersTool');
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cliTool/ScheduledTaskTool.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 ScheduledTaskTool
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief CLI tool to execute a set of scheduled tasks.
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\config\Config;
|
||||
use PKP\scheduledTask\ScheduledTaskDAO;
|
||||
use PKP\scheduledTask\ScheduledTaskHelper;
|
||||
use PKP\xml\PKPXMLParser;
|
||||
|
||||
/** Default XML tasks file to parse if none is specified */
|
||||
define('TASKS_REGISTRY_FILE', 'registry/scheduledTasks.xml');
|
||||
|
||||
class ScheduledTaskTool extends \PKP\cliTool\CommandLineTool
|
||||
{
|
||||
/** @var string the XML file listing the tasks to be executed */
|
||||
public $file;
|
||||
|
||||
/** @var ScheduledTaskDAO the DAO object */
|
||||
public $taskDao;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $argv command-line arguments
|
||||
* If specified, the first parameter should be the path to
|
||||
* a tasks XML descriptor file (other than the default)
|
||||
*/
|
||||
public function __construct($argv = [])
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
if (isset($this->argv[0])) {
|
||||
$this->file = $this->argv[0];
|
||||
} else {
|
||||
$this->file = TASKS_REGISTRY_FILE;
|
||||
}
|
||||
|
||||
if (!file_exists($this->file) || !is_readable($this->file)) {
|
||||
printf("Tasks file \"%s\" does not exist or is not readable!\n", $this->file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$this->taskDao = DAORegistry::getDAO('ScheduledTaskDAO');
|
||||
}
|
||||
|
||||
/**
|
||||
* Print command usage information.
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
echo "Script to run a set of scheduled tasks\n"
|
||||
. "Usage: {$this->scriptName} [tasks_file]\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and execute the scheduled tasks.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// Application is set to sandbox mode and will not run any schedule tasks
|
||||
if (Config::getVar('general', 'sandbox', false)) {
|
||||
error_log('Application is set to sandbox mode and will not run any schedule tasks');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->parseTasks($this->file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and execute the scheduled tasks in the specified file.
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public function parseTasks($file)
|
||||
{
|
||||
$xmlParser = new PKPXMLParser();
|
||||
$tree = $xmlParser->parse($file);
|
||||
|
||||
if (!$tree) {
|
||||
printf("Unable to parse file \"%s\"!\n", $file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
foreach ($tree->getChildren() as $task) {
|
||||
$className = $task->getAttribute('class');
|
||||
|
||||
$frequency = $task->getChildByName('frequency');
|
||||
if (isset($frequency)) {
|
||||
$canExecute = ScheduledTaskHelper::checkFrequency($className, $frequency);
|
||||
} else {
|
||||
// Always execute if no frequency is specified
|
||||
$canExecute = true;
|
||||
}
|
||||
|
||||
if ($canExecute) {
|
||||
$this->executeTask($className, ScheduledTaskHelper::getTaskArgs($task));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the specified task.
|
||||
*
|
||||
* @param string $className the class name to execute
|
||||
* @param array $args the array of arguments to pass to the class constructors
|
||||
*/
|
||||
public function executeTask($className, $args)
|
||||
{
|
||||
// Load and execute the task
|
||||
if (preg_match('/^[a-zA-Z0-9_.]+$/', $className)) {
|
||||
// DEPRECATED as of 3.4.0: Use old class.name.style and import() function (pre-PSR classloading) pkp/pkp-lib#8186
|
||||
if (!is_object($task = instantiate($className, null, null, 'execute', $args))) {
|
||||
fatalError('Cannot instantiate task class.');
|
||||
}
|
||||
} else {
|
||||
$task = new $className($args);
|
||||
}
|
||||
$this->taskDao->updateLastRunTime($className);
|
||||
$task->execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\cliTool\ScheduledTaskTool', '\ScheduledTaskTool');
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/cliTool/UpgradeTool.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 upgradeTool
|
||||
*
|
||||
* @ingroup tools
|
||||
*
|
||||
* @brief CLI tool for upgrading the system.
|
||||
*
|
||||
* Note: Some functions require fopen wrappers to be enabled.
|
||||
*/
|
||||
|
||||
namespace PKP\cliTool;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\install\Upgrade;
|
||||
use PKP\site\VersionCheck;
|
||||
|
||||
Application::upgrade();
|
||||
|
||||
class UpgradeTool extends \PKP\cliTool\CommandLineTool
|
||||
{
|
||||
/** @var string command to execute (check|upgrade|download) */
|
||||
public $command;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $argv command-line arguments
|
||||
*/
|
||||
public function __construct($argv = [])
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
if (!isset($this->argv[0]) || !in_array($this->argv[0], ['check', 'latest', 'upgrade', 'download'])) {
|
||||
$this->usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$this->command = $this->argv[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Print command usage information.
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
echo "Upgrade tool\n"
|
||||
. "Usage: {$this->scriptName} command\n"
|
||||
. "Supported commands:\n"
|
||||
. " check perform version check\n"
|
||||
. " latest display latest version info\n"
|
||||
. " upgrade execute upgrade script\n"
|
||||
. " download download latest version (does not unpack/install)\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the specified command.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$command = $this->command;
|
||||
$this->$command();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform version check against latest available version.
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$this->checkVersion(VersionCheck::getLatestVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about the latest available version.
|
||||
*/
|
||||
public function latest()
|
||||
{
|
||||
$this->checkVersion(VersionCheck::getLatestVersion(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run upgrade script.
|
||||
*/
|
||||
public function upgrade()
|
||||
{
|
||||
$installer = new Upgrade([]);
|
||||
$installer->setLogger($this);
|
||||
|
||||
if ($installer->execute()) {
|
||||
if (count($installer->getNotes()) > 0) {
|
||||
printf("\nRelease Notes\n");
|
||||
printf("----------------------------------------\n");
|
||||
foreach ($installer->getNotes() as $note) {
|
||||
printf("%s\n\n", $note);
|
||||
}
|
||||
}
|
||||
|
||||
$newVersion = $installer->getNewVersion();
|
||||
printf("Successfully upgraded to version %s\n", $newVersion->getVersionString(false));
|
||||
} else {
|
||||
printf("ERROR: Upgrade failed: %s\n", $installer->getErrorString());
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download latest package.
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
$versionInfo = VersionCheck::getLatestVersion();
|
||||
if (!$versionInfo) {
|
||||
$application = Application::get();
|
||||
printf("Failed to load version info from %s\n", $application->getVersionDescriptorUrl());
|
||||
exit(3);
|
||||
}
|
||||
|
||||
$download = $versionInfo['package'];
|
||||
$outFile = basename($download);
|
||||
|
||||
printf("Download: %s\n", $download);
|
||||
printf("File will be saved to: %s\n", $outFile);
|
||||
|
||||
if (!$this->promptContinue()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$out = fopen($outFile, 'wb');
|
||||
if (!$out) {
|
||||
printf("Failed to open %s for writing\n", $outFile);
|
||||
exit(5);
|
||||
}
|
||||
|
||||
$in = fopen($download, 'rb');
|
||||
if (!$in) {
|
||||
printf("Failed to open %s for reading\n", $download);
|
||||
fclose($out);
|
||||
exit(6);
|
||||
}
|
||||
|
||||
printf('Downloading file...');
|
||||
|
||||
while (($data = fread($in, 4096)) !== '') {
|
||||
printf('.');
|
||||
fwrite($out, $data);
|
||||
}
|
||||
|
||||
printf("done\n");
|
||||
|
||||
fclose($in);
|
||||
fclose($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform version check.
|
||||
*
|
||||
* @param array $versionInfo latest version info
|
||||
* @param bool $displayInfo just display info, don't perform check
|
||||
*/
|
||||
public function checkVersion($versionInfo, $displayInfo = false)
|
||||
{
|
||||
if (!$versionInfo) {
|
||||
$application = Application::get();
|
||||
printf("Failed to load version info from %s\n", $application->getVersionDescriptorUrl());
|
||||
exit(7);
|
||||
}
|
||||
|
||||
$dbVersion = VersionCheck::getCurrentDBVersion();
|
||||
$codeVersion = VersionCheck::getCurrentCodeVersion();
|
||||
$latestVersion = $versionInfo['version'];
|
||||
|
||||
printf("Code version: %s\n", $codeVersion->getVersionString(false));
|
||||
printf("Database version: %s\n", $dbVersion->getVersionString(false));
|
||||
printf("Latest version: %s\n", $latestVersion->getVersionString(false));
|
||||
|
||||
$compare1 = $codeVersion->compare($latestVersion);
|
||||
$compare2 = $dbVersion->compare($codeVersion);
|
||||
|
||||
if (!$displayInfo) {
|
||||
if ($compare2 < 0) {
|
||||
printf("Database version is older than code version\n");
|
||||
printf("Run \"{$this->scriptName} upgrade\" to update\n");
|
||||
} elseif ($compare2 > 0) {
|
||||
printf("Database version is newer than code version!\n");
|
||||
} elseif ($compare1 == 0) {
|
||||
printf("Your system is up-to-date\n");
|
||||
} elseif ($compare1 < 0) {
|
||||
printf("A newer version is available:\n");
|
||||
$displayInfo = true;
|
||||
} else {
|
||||
printf("Current version is newer than latest!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($displayInfo) {
|
||||
printf(" tag: %s\n", $versionInfo['tag']);
|
||||
printf(" date: %s\n", $versionInfo['date']);
|
||||
printf(" info: %s\n", $versionInfo['info']);
|
||||
printf(" package: %s\n", $versionInfo['package']);
|
||||
}
|
||||
|
||||
return $compare1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user for yes/no input (default no).
|
||||
*
|
||||
* @param string $prompt
|
||||
*/
|
||||
public function promptContinue($prompt = 'Continue?')
|
||||
{
|
||||
printf('%s [y/N] ', $prompt);
|
||||
$continue = fread(STDIN, 255);
|
||||
return (strtolower(substr(trim($continue), 0, 1)) == 'y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Log install message to stdout.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function log($message)
|
||||
{
|
||||
printf("%s [%s]\n", date('Y-m-d H:i:s'), $message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\cliTool\UpgradeTool', '\UpgradeTool');
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/PKPStatsComponent.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 PKPStatsComponent
|
||||
*
|
||||
* @ingroup classes_components_stats
|
||||
*
|
||||
* @brief A class to prepare the data object for a statistics UI component
|
||||
*/
|
||||
|
||||
namespace PKP\components;
|
||||
|
||||
use PKP\statistics\PKPStatisticsHelper;
|
||||
|
||||
class PKPStatsComponent
|
||||
{
|
||||
/** @var string The URL to the /stats API endpoint */
|
||||
public $apiUrl = '';
|
||||
|
||||
/** @var array Configuration for the columns to display in the table */
|
||||
public $tableColumns = [];
|
||||
|
||||
/** @var string Retrieve stats after this date */
|
||||
public $dateStart = '';
|
||||
|
||||
/** @var string Retrieve stats before this date */
|
||||
public $dateEnd = '';
|
||||
|
||||
/** @var array Quick options to provide for configuring the date range */
|
||||
public $dateRangeOptions = [];
|
||||
|
||||
/** @var array|null Configuration assoc array for available filters */
|
||||
public $filters = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $apiUrl The URL to fetch stats from
|
||||
* @param array $args Optional arguments
|
||||
*/
|
||||
public function __construct($apiUrl, $args = [])
|
||||
{
|
||||
$this->apiUrl = $apiUrl;
|
||||
$this->init($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the handler with config parameters
|
||||
*
|
||||
* @param array $args Configuration params
|
||||
*/
|
||||
public function init($args = [])
|
||||
{
|
||||
foreach ($args as $key => $value) {
|
||||
if (property_exists($this, $key)) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the configuration data to be used when initializing this
|
||||
* handler on the frontend
|
||||
*
|
||||
* @return array Configuration data
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = [
|
||||
'apiUrl' => $this->apiUrl,
|
||||
'tableColumns' => $this->tableColumns,
|
||||
'dateStart' => $this->dateStart,
|
||||
'dateStartMin' => PKPStatisticsHelper::STATISTICS_EARLIEST_DATE,
|
||||
'dateEnd' => $this->dateEnd,
|
||||
'dateEndMax' => date('Y-m-d', strtotime('yesterday')),
|
||||
'dateRangeOptions' => $this->dateRangeOptions,
|
||||
'activeFilters' => [],
|
||||
'isLoadingItems' => false,
|
||||
'isSidebarVisible' => false,
|
||||
];
|
||||
|
||||
if ($this->filters) {
|
||||
$config['filters'] = $this->filters;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/PKPStatsContextPage.php
|
||||
*
|
||||
* Copyright (c) 2022 Simon Fraser University
|
||||
* Copyright (c) 2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPStatsContextPage
|
||||
*
|
||||
* @ingroup classes_controllers_stats
|
||||
*
|
||||
* @brief A class to prepare the data object for the context statistics
|
||||
* UI component
|
||||
*/
|
||||
|
||||
namespace PKP\components;
|
||||
|
||||
use PKP\statistics\PKPStatisticsHelper;
|
||||
|
||||
class PKPStatsContextPage extends PKPStatsComponent
|
||||
{
|
||||
/** @var array A timeline of stats (eg - monthly) for a graph */
|
||||
public $timeline = [];
|
||||
|
||||
/** @var string Which time segment (eg - month) is displayed in the graph */
|
||||
public $timelineInterval = PKPStatisticsHelper::STATISTICS_DIMENSION_MONTH;
|
||||
|
||||
/**
|
||||
* Retrieve the configuration data to be used when initializing this
|
||||
* handler on the frontend
|
||||
*
|
||||
* @return array Configuration data
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
$config = array_merge(
|
||||
$config,
|
||||
[
|
||||
'timeline' => $this->timeline,
|
||||
'timelineInterval' => $this->timelineInterval,
|
||||
'dateRangeLabel' => __('stats.dateRange'),
|
||||
'betweenDatesLabel' => __('stats.downloadReport.betweenDates'),
|
||||
'allDatesLabel' => __('stats.dateRange.allDates'),
|
||||
'contextLabel' => __('context.context'),
|
||||
'timelineTypeLabel' => __('stats.timelineType'),
|
||||
'timelineIntervalLabel' => __('stats.timelineInterval'),
|
||||
'viewsLabel' => __('submission.views'),
|
||||
'dayLabel' => __('common.day'),
|
||||
'monthLabel' => __('common.month'),
|
||||
'timelineDescriptionLabel' => __('stats.timeline.downloadReport.description'),
|
||||
'isLoadingTimeline' => false,
|
||||
]
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/PKPStatsEditorialPage.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 PKPStatsEditorialPage
|
||||
*
|
||||
* @ingroup classes_controllers_stats
|
||||
*
|
||||
* @brief A class to prepare the data object for the editorial statistics
|
||||
* UI component
|
||||
*/
|
||||
|
||||
namespace PKP\components;
|
||||
|
||||
class PKPStatsEditorialPage extends PKPStatsComponent
|
||||
{
|
||||
/** @var array A key/value array of active submissions by stage */
|
||||
public $activeByStage = [];
|
||||
|
||||
/** @var string The URL to get the averages from the API */
|
||||
public $averagesApiUrl = [];
|
||||
|
||||
/** @var array List of stats that should be converted to percentages */
|
||||
public $percentageStats = [];
|
||||
|
||||
/** @var array List of stats details to display in the table */
|
||||
public $tableRows = [];
|
||||
|
||||
/**
|
||||
* Retrieve the configuration data to be used when initializing this
|
||||
* handler on the frontend
|
||||
*
|
||||
* @return array Configuration data
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
$config = array_merge(
|
||||
$config,
|
||||
[
|
||||
'activeByStage' => $this->activeByStage,
|
||||
'averagesApiUrl' => $this->averagesApiUrl,
|
||||
'percentageStats' => $this->percentageStats,
|
||||
'tableRows' => $this->tableRows,
|
||||
]
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/PKPStatsPublicationPage.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 PKPStatsPublicationPage
|
||||
*
|
||||
* @ingroup classes_controllers_stats
|
||||
*
|
||||
* @brief A class to prepare the data object for the publication statistics
|
||||
* UI component
|
||||
*/
|
||||
|
||||
namespace PKP\components;
|
||||
|
||||
use PKP\statistics\PKPStatisticsHelper;
|
||||
|
||||
class PKPStatsPublicationPage extends PKPStatsComponent
|
||||
{
|
||||
/** @var array A timeline of stats (eg - monthly) for a graph */
|
||||
public $timeline = [];
|
||||
|
||||
/** @var string Which time segment (eg - month) is displayed in the graph */
|
||||
public $timelineInterval = PKPStatisticsHelper::STATISTICS_DIMENSION_MONTH;
|
||||
|
||||
/** @var string Which views to show in the graph. Supports `abstract` or `galley`. */
|
||||
public $timelineType = '';
|
||||
|
||||
/** @var array List of items to display stats for */
|
||||
public $items = [];
|
||||
|
||||
/** @var int The maximum number of items that stats can be shown for */
|
||||
public $itemsMax = 0;
|
||||
|
||||
/** @var int How many items to show per page */
|
||||
public $count = 30;
|
||||
|
||||
/** @var string Order items by this property */
|
||||
public $orderBy = '';
|
||||
|
||||
/** @var string Order items in this direction: ASC or DESC*/
|
||||
public $orderDirection = 'DESC';
|
||||
|
||||
/** @var string A search phrase to filter the list of items */
|
||||
public $searchPhrase = null;
|
||||
|
||||
/**
|
||||
* Retrieve the configuration data to be used when initializing this
|
||||
* handler on the frontend
|
||||
*
|
||||
* @return array Configuration data
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
$config = array_merge(
|
||||
$config,
|
||||
[
|
||||
'timeline' => $this->timeline,
|
||||
'timelineInterval' => $this->timelineInterval,
|
||||
'timelineType' => $this->timelineType,
|
||||
'items' => $this->items,
|
||||
'dateRangeLabel' => __('stats.dateRange'),
|
||||
'searchPhraseLabel' => __('common.searchPhrase'),
|
||||
'itemsOfTotalLabel' => __('stats.publications.countOfTotal'),
|
||||
'betweenDatesLabel' => __('stats.downloadReport.betweenDates'),
|
||||
'allDatesLabel' => __('stats.dateRange.allDates'),
|
||||
'allFiltersLabel' => __('stats.downloadReport.allFilters'),
|
||||
'commonSubmissionsLabel' => __('common.publications'),
|
||||
'timelineTypeLabel' => __('stats.timelineType'),
|
||||
'timelineIntervalLabel' => __('stats.timelineInterval'),
|
||||
'viewsLabel' => __('submission.views'),
|
||||
'downloadsLabel' => __('submission.downloads'),
|
||||
'dayLabel' => __('common.day'),
|
||||
'monthLabel' => __('common.month'),
|
||||
'timelineDescriptionLabel' => __('stats.timeline.downloadReport.description'),
|
||||
'itemsMax' => $this->itemsMax,
|
||||
'count' => $this->count,
|
||||
'offset' => 0,
|
||||
'searchPhrase' => null,
|
||||
'orderBy' => $this->orderBy,
|
||||
'orderDirection' => $this->orderDirection,
|
||||
'isLoadingTimeline' => false,
|
||||
]
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/fileAttachers/Base.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Base
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A base class for FileAttacher components.
|
||||
*/
|
||||
|
||||
namespace PKP\components\fileAttachers;
|
||||
|
||||
abstract class BaseAttacher
|
||||
{
|
||||
public string $component;
|
||||
public string $label;
|
||||
public string $description;
|
||||
public string $button;
|
||||
|
||||
/**
|
||||
* Initialize the file attacher
|
||||
*
|
||||
* @param string $label The label to display for this file attacher
|
||||
* @param string $description A description of this file attacher
|
||||
* @param string $button The label for the button to activate this file attacher
|
||||
*/
|
||||
public function __construct(string $label, string $description, string $button)
|
||||
{
|
||||
$this->label = $label;
|
||||
$this->description = $description;
|
||||
$this->button = $button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the initial state for this file attacher
|
||||
*/
|
||||
public function getState(): array
|
||||
{
|
||||
return [
|
||||
'component' => $this->component,
|
||||
'label' => $this->label,
|
||||
'description' => $this->description,
|
||||
'button' => $this->button,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/fileAttachers/FileStage.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FileStage
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A class to compile initial state for a FileAttacherFileStage component.
|
||||
*/
|
||||
|
||||
namespace PKP\components\fileAttachers;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\submission\Submission;
|
||||
use PKP\context\Context;
|
||||
use PKP\submission\reviewRound\ReviewRound;
|
||||
|
||||
class FileStage extends BaseAttacher
|
||||
{
|
||||
public string $component = 'FileAttacherFileStage';
|
||||
public Context $context;
|
||||
public Submission $submission;
|
||||
public array $fileStages = [];
|
||||
|
||||
/**
|
||||
* Initialize a file stage attacher
|
||||
*
|
||||
* @param string $label The label to display for this file attacher
|
||||
* @param string $description A description of this file attacher
|
||||
* @param string $button The label for the button to activate this file attacher
|
||||
*/
|
||||
public function __construct(Context $context, Submission $submission, string $label, string $description, string $button)
|
||||
{
|
||||
parent::__construct($label, $description, $button);
|
||||
$this->context = $context;
|
||||
$this->submission = $submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a submission file stage that can be used for attachments
|
||||
*/
|
||||
public function withFileStage(int $fileStage, string $label, ?ReviewRound $reviewRound = null): self
|
||||
{
|
||||
$queryParams = ['fileStages' => [$fileStage]];
|
||||
if ($reviewRound) {
|
||||
$queryParams['reviewRoundIds'] = [$reviewRound->getId()];
|
||||
}
|
||||
$this->fileStages[] = [
|
||||
'label' => $label,
|
||||
'queryParams' => $queryParams,
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the props for this file attacher
|
||||
*/
|
||||
public function getState(): array
|
||||
{
|
||||
$props = parent::getState();
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
$props['submissionFilesApiUrl'] = $request->getDispatcher()->url(
|
||||
$request,
|
||||
Application::ROUTE_API,
|
||||
$this->context->getData('urlPath'),
|
||||
'submissions/' . $this->submission->getId() . '/files'
|
||||
);
|
||||
|
||||
$props['fileStages'] = $this->fileStages;
|
||||
$props['attachSelectedLabel'] = __('common.attachSelected');
|
||||
$props['downloadLabel'] = __('common.download');
|
||||
$props['backLabel'] = __('common.back');
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/fileAttachers/Library.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Library
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A class to compile initial state for a FileAttacherLibrary component.
|
||||
*/
|
||||
|
||||
namespace PKP\components\fileAttachers;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\submission\Submission;
|
||||
use PKP\context\Context;
|
||||
|
||||
class Library extends BaseAttacher
|
||||
{
|
||||
public string $component = 'FileAttacherLibrary';
|
||||
public Context $context;
|
||||
public Submission $submission;
|
||||
|
||||
/**
|
||||
* Initialize this file attacher
|
||||
*
|
||||
*/
|
||||
public function __construct(Context $context, ?Submission $submission = null)
|
||||
{
|
||||
parent::__construct(
|
||||
__('email.addAttachment.libraryFiles'),
|
||||
__('email.addAttachment.libraryFiles.description'),
|
||||
__('email.addAttachment.libraryFiles.attach')
|
||||
);
|
||||
$this->context = $context;
|
||||
$this->submission = $submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the props for this file attacher
|
||||
*/
|
||||
public function getState(): array
|
||||
{
|
||||
$props = parent::getState();
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
$props['libraryApiUrl'] = $request->getDispatcher()->url(
|
||||
$request,
|
||||
Application::ROUTE_API,
|
||||
$this->context->getData('urlPath'),
|
||||
'_library'
|
||||
);
|
||||
if ($this->submission) {
|
||||
$props['includeSubmissionId'] = $this->submission->getId();
|
||||
}
|
||||
$props['attachSelectedLabel'] = __('common.attachSelected');
|
||||
$props['backLabel'] = __('common.back');
|
||||
$props['downloadLabel'] = __('common.download');
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/fileAttachers/ReviewFiles.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ReviewFiles
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A class to compile initial state for a FileAttacherReviewFiles component.
|
||||
*/
|
||||
|
||||
namespace PKP\components\fileAttachers;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use Exception;
|
||||
use PKP\context\Context;
|
||||
use PKP\submission\reviewAssignment\ReviewAssignment;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class ReviewFiles extends BaseAttacher
|
||||
{
|
||||
public string $component = 'FileAttacherReviewFiles';
|
||||
public Context $context;
|
||||
|
||||
/** @var iterable<SubmissionFile> $files */
|
||||
public iterable $files;
|
||||
|
||||
/** @var array<ReviewAssignment> $reviewAssignments */
|
||||
public array $reviewAssignments;
|
||||
|
||||
/**
|
||||
* Initialize this file attacher
|
||||
*
|
||||
* @param string $label The label to display for this file attacher
|
||||
* @param string $description A description of this file attacher
|
||||
* @param string $button The label for the button to activate this file attacher
|
||||
*/
|
||||
public function __construct(string $label, string $description, string $button, iterable $files, array $reviewAssignments, Context $context)
|
||||
{
|
||||
parent::__construct($label, $description, $button);
|
||||
$this->files = $files;
|
||||
$this->reviewAssignments = $reviewAssignments;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the props for this file attacher
|
||||
*/
|
||||
public function getState(): array
|
||||
{
|
||||
$props = parent::getState();
|
||||
$props['attachSelectedLabel'] = __('common.attachSelected');
|
||||
$props['backLabel'] = __('common.back');
|
||||
$props['downloadLabel'] = __('common.download');
|
||||
$props['files'] = $this->getFilesState();
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
protected function getFilesState(): array
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
|
||||
$files = [];
|
||||
/** @var SubmissionFile $file */
|
||||
foreach ($this->files as $file) {
|
||||
if (!isset($this->reviewAssignments[$file->getData('assocId')])) {
|
||||
throw new Exception('Tried to add review file attachment from unknown review assignment.');
|
||||
}
|
||||
$files[] = [
|
||||
'id' => $file->getId(),
|
||||
'name' => $file->getData('name'),
|
||||
'documentType' => Services::get('file')->getDocumentType($file->getData('documentType')),
|
||||
'reviewerName' => $this->reviewAssignments[$file->getData('assocId')]->getReviewerFullName(),
|
||||
'url' => $request->getDispatcher()->url(
|
||||
$request,
|
||||
Application::ROUTE_COMPONENT,
|
||||
$this->context->getData('urlPath'),
|
||||
'api.file.FileApiHandler',
|
||||
'downloadFile',
|
||||
null,
|
||||
[
|
||||
'submissionFileId' => $file->getId(),
|
||||
'submissionId' => $file->getData('submissionId'),
|
||||
'stageId' => Repo::submissionFile()->getWorkflowStageId($file),
|
||||
]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/fileAttachers/Upload.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Upload
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A class to compile initial state for a FileAttacherUpload component.
|
||||
*/
|
||||
|
||||
namespace PKP\components\fileAttachers;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\context\Context;
|
||||
|
||||
class Upload extends BaseAttacher
|
||||
{
|
||||
public string $component = 'FileAttacherUpload';
|
||||
public Context $context;
|
||||
|
||||
/**
|
||||
* Initialize this file attacher
|
||||
*
|
||||
* @param string $label The label to display for this file attacher
|
||||
* @param string $description A description of this file attacher
|
||||
* @param string $button The label for the button to activate this file attacher
|
||||
*/
|
||||
public function __construct(Context $context, string $label, string $description, string $button)
|
||||
{
|
||||
parent::__construct($label, $description, $button);
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the props for this file attacher
|
||||
*/
|
||||
public function getState(): array
|
||||
{
|
||||
$props = parent::getState();
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
$props['temporaryFilesApiUrl'] = $request->getDispatcher()->url(
|
||||
$request,
|
||||
Application::ROUTE_API,
|
||||
$this->context->getData('urlPath'),
|
||||
'temporaryFiles'
|
||||
);
|
||||
$props['dropzoneOptions'] = [
|
||||
'maxFilesize' => Application::getIntMaxFileMBs(),
|
||||
'timeout' => ini_get('max_execution_time') ? ini_get('max_execution_time') * 1000 : 0,
|
||||
'dropzoneDictDefaultMessage' => __('form.dropzone.dictDefaultMessage'),
|
||||
'dropzoneDictFallbackMessage' => __('form.dropzone.dictFallbackMessage'),
|
||||
'dropzoneDictFallbackText' => __('form.dropzone.dictFallbackText'),
|
||||
'dropzoneDictFileTooBig' => __('form.dropzone.dictFileTooBig'),
|
||||
'dropzoneDictInvalidFileType' => __('form.dropzone.dictInvalidFileType'),
|
||||
'dropzoneDictResponseError' => __('form.dropzone.dictResponseError'),
|
||||
'dropzoneDictCancelUpload' => __('form.dropzone.dictCancelUpload'),
|
||||
'dropzoneDictUploadCanceled' => __('form.dropzone.dictUploadCanceled'),
|
||||
'dropzoneDictCancelUploadConfirmation' => __('form.dropzone.dictCancelUploadConfirmation'),
|
||||
'dropzoneDictRemoveFile' => __('form.dropzone.dictRemoveFile'),
|
||||
'dropzoneDictMaxFilesExceeded' => __('form.dropzone.dictMaxFilesExceeded'),
|
||||
];
|
||||
$props['addFilesLabel'] = __('common.addFiles');
|
||||
$props['attachFilesLabel'] = __('common.attachFiles');
|
||||
$props['dragAndDropMessage'] = __('common.dragAndDropHere');
|
||||
$props['dragAndDropOrUploadMessage'] = __('common.orUploadFile');
|
||||
$props['backLabel'] = __('common.back');
|
||||
$props['removeItemLabel'] = __('common.removeItem');
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/Field.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 Field
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A base class representing a single field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
abstract class Field
|
||||
{
|
||||
/** @var string Which UI Library component this field represents */
|
||||
public $component;
|
||||
|
||||
/** @var string The form input name for this field */
|
||||
public $name;
|
||||
|
||||
/** @var string|object Field label or multilingual object matching locales to labels, eg ['en' => 'Label', 'fr_CA' => 'Étiquette'] */
|
||||
public $label = '';
|
||||
|
||||
/** @var string Field description */
|
||||
public $description;
|
||||
|
||||
/** @var string Field tooltip */
|
||||
public $tooltip;
|
||||
|
||||
/** @var string Field help topic. Refers to the /dev/docs file name without .md */
|
||||
public $helpTopic;
|
||||
|
||||
/** @var string Field help section. An optional anchor link to open to when loading the helpTopic. */
|
||||
public $helpSection;
|
||||
|
||||
/** @var string Which group should this field be placed in? */
|
||||
public $groupId;
|
||||
|
||||
/** @var bool Is this field required? */
|
||||
public $isRequired = false;
|
||||
|
||||
/** @var bool Is this field multilingual? */
|
||||
public $isMultilingual = false;
|
||||
|
||||
/** @var mixed The value of this field. If multilingual, expects a key/value array: ['en', => 'English value', 'fr_CA' => 'French value'] */
|
||||
public $value;
|
||||
|
||||
/** @var mixed A default for this field when no value is specified. */
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* Only show this field when the field named here is not empty. Match an exact
|
||||
* value by passing an array:
|
||||
*
|
||||
* $this->showWhen = ['fieldName', 'expectedValue'];
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
public $showWhen;
|
||||
|
||||
/** @var array List of required properties for this field. */
|
||||
private $_requiredProperties = ['name', 'component'];
|
||||
|
||||
/**
|
||||
* Initialize the form field
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $args [
|
||||
*
|
||||
* @option label string|object
|
||||
* @option groupId string
|
||||
* @option isRequired boolean
|
||||
* @option isMultilingual boolean
|
||||
* ]
|
||||
*/
|
||||
public function __construct($name, $args = [])
|
||||
{
|
||||
$this->name = $name;
|
||||
foreach ($args as $key => $value) {
|
||||
if (property_exists($this, $key)) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration object representing this field to be passed to the UI
|
||||
* Library
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
if (!$this->validate()) {
|
||||
throw new \Exception('Form field configuration did not pass validation: ' . print_r($this, true));
|
||||
}
|
||||
$config = [
|
||||
'name' => $this->name,
|
||||
'component' => $this->component,
|
||||
'label' => $this->label,
|
||||
];
|
||||
if (isset($this->description)) {
|
||||
$config['description'] = $this->description;
|
||||
}
|
||||
if (isset($this->tooltip)) {
|
||||
$config['tooltip'] = $this->tooltip;
|
||||
}
|
||||
if (isset($this->helpTopic)) {
|
||||
$config['helpTopic'] = $this->helpTopic;
|
||||
if ($this->helpSection) {
|
||||
$config['helpSection'] = $this->helpSection;
|
||||
}
|
||||
}
|
||||
if (isset($this->groupId)) {
|
||||
$config['groupId'] = $this->groupId;
|
||||
}
|
||||
if (isset($this->isRequired)) {
|
||||
$config['isRequired'] = $this->isRequired;
|
||||
}
|
||||
if (isset($this->isMultilingual)) {
|
||||
$config['isMultilingual'] = $this->isMultilingual;
|
||||
}
|
||||
if (isset($this->showWhen)) {
|
||||
$config['showWhen'] = $this->showWhen;
|
||||
}
|
||||
|
||||
$config['value'] = $this->value ?? $this->default ?? null;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the field configuration
|
||||
*
|
||||
* Check that no required fields are missing
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
foreach ($this->_requiredProperties as $property) {
|
||||
if (!isset($this->{$property})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a default empty value for this field type
|
||||
*
|
||||
* The UI Library expects to receive a value property for each field. If it's
|
||||
* a multilingual field, it expects the value property to contain keys for
|
||||
* each locale in the form.
|
||||
*
|
||||
* This function will provide a default empty value so that a form can fill
|
||||
* in the empty values automatically.
|
||||
*
|
||||
*/
|
||||
public function getEmptyValue()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldControlledVocab.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 FieldAutosuggestPreset
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A type of autosuggest field that preloads all of its options.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldAutosuggestPreset extends FieldBaseAutosuggest
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-autosuggest-preset';
|
||||
|
||||
/** @var array Key/value list of suggestions for this field */
|
||||
public $options = [];
|
||||
|
||||
/** @var array Key/value list of languages this field should support. Key = locale code. Value = locale name */
|
||||
public $locales = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['options'] = $this->options;
|
||||
$config['selected'] = $this->getSelected();
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
protected function getSelected(): array
|
||||
{
|
||||
if ($this->isMultilingual) {
|
||||
$selected = [];
|
||||
foreach ($this->locales as $locale) {
|
||||
if (array_key_exists($locale['key'], $this->value)) {
|
||||
$config['selected'][$locale['key']] = array_map([$this, 'mapSelected'], (array) $this->value[$locale['key']]);
|
||||
} else {
|
||||
$config['selected'][$locale['key']] = [];
|
||||
}
|
||||
}
|
||||
return $selected;
|
||||
}
|
||||
|
||||
return array_map([$this, 'mapSelected'], $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the selected values to the format expected by an
|
||||
* autosuggest field
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function mapSelected($value)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if ($option['value'] === $value) {
|
||||
return $option;
|
||||
}
|
||||
}
|
||||
return [
|
||||
'value' => $value,
|
||||
'label' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldBaseAutosuggest.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 FieldBaseAutosuggest
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A base class for text fields that provide suggested values while typing.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
define('AUTOSUGGEST_POSITION_INLINE', 'inline');
|
||||
define('AUTOSUGGEST_POSITION_BELOW', 'below');
|
||||
|
||||
abstract class FieldBaseAutosuggest extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-base-autosuggest';
|
||||
|
||||
/** @var string A URL to retrieve suggestions. */
|
||||
public $apiUrl;
|
||||
|
||||
/** @var array Query params when getting suggestions. */
|
||||
public $getParams = [];
|
||||
|
||||
/** @var array List of selected items. */
|
||||
public $selected = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['apiUrl'] = $this->apiUrl;
|
||||
$config['deselectLabel'] = __('common.removeItem');
|
||||
$config['getParams'] = empty($this->getParams) ? new \stdClass() : $this->getParams;
|
||||
$config['selectedLabel'] = __('common.selectedPrefix');
|
||||
$config['selected'] = $this->selected;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldColor.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 FieldColor
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A color picker field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldColor extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-color';
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldControlledVocab.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 FieldControlledVocab
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A type of autosuggest field for controlled vocabulary like keywords.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldControlledVocab extends FieldBaseAutosuggest
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-controlled-vocab';
|
||||
|
||||
/** @var array Key/value list of languages this field should support. Key = locale code. Value = locale name */
|
||||
public $locales = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
if ($this->isMultilingual) {
|
||||
$config['selected'] = [];
|
||||
foreach ($this->locales as $locale) {
|
||||
if (array_key_exists($locale['key'], $this->value)) {
|
||||
$config['selected'][$locale['key']] = array_map([$this, 'mapSelected'], (array) $this->value[$locale['key']]);
|
||||
} else {
|
||||
$config['selected'][$locale['key']] = [];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$config['selected'] = array_map([$this, 'mapSelected'], $this->value);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the selected values to the format expected by an
|
||||
* autosuggest field
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function mapSelected($value)
|
||||
{
|
||||
return [
|
||||
'value' => $value,
|
||||
'label' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldHTML.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 FieldHTML
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A component for inserting HTML into a form, when you don't need any
|
||||
* input fields or values stored.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldHTML extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-html';
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldMetadataSetting.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 FieldMetadataSetting
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field to enable a type of metadata and determine when it should be
|
||||
* requested or required.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
use PKP\context\Context;
|
||||
|
||||
class FieldMetadataSetting extends FieldOptions
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-metadata-setting';
|
||||
|
||||
/** @var int What is the value that represents metadata that is disabled */
|
||||
public $disabledValue = Context::METADATA_DISABLE;
|
||||
|
||||
/**
|
||||
* @var int What is the value that represents metadata that is enabled,
|
||||
* but which is not requested or required during submission?
|
||||
*/
|
||||
public $enabledOnlyValue = Context::METADATA_ENABLE;
|
||||
|
||||
/** @var array The options for what to request/require from the author during submission */
|
||||
public $submissionOptions = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['disabledValue'] = $this->disabledValue;
|
||||
$config['enabledOnlyValue'] = $this->enabledOnlyValue;
|
||||
$config['submissionOptions'] = $this->submissionOptions;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldOptions.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 FieldOptions
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field to select from a set of checkbox or radio options.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldOptions extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-options';
|
||||
|
||||
/** @var string Use a checkbox or radio button input type */
|
||||
public $type = 'checkbox';
|
||||
|
||||
/** @var bool Should the user be able to re-order the options? */
|
||||
public $isOrderable = false;
|
||||
|
||||
/** @var array The options which can be selected */
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['type'] = $this->type;
|
||||
$config['isOrderable'] = $this->isOrderable;
|
||||
$config['options'] = $this->options;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldPreparedContent.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FieldPreparedContent
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A rich text editor that can insert prepared content snippets
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldPreparedContent extends FieldRichTextarea
|
||||
{
|
||||
public $component = 'field-prepared-content';
|
||||
|
||||
/**
|
||||
* A list of content that can be inserted from a TinyMCE button.
|
||||
*
|
||||
* @see FieldPreparedContent in the UI Library for details on the expected format
|
||||
*/
|
||||
public array $preparedContent = [];
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['preparedContentLabel'] = __('common.content');
|
||||
$config['insertLabel'] = __('common.insert');
|
||||
$config['insertModalLabel'] = __('common.insertContent');
|
||||
$config['searchLabel'] = __('common.insertContentSearch');
|
||||
$config['preparedContent'] = $this->preparedContent;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldPubId.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 FieldPubId
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field for generating a pub id, like a DOI.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldPubId extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-pub-id';
|
||||
|
||||
/** @var string A localized label for the button to assign the pubid */
|
||||
public $assignIdLabel;
|
||||
|
||||
/** @var string A localized label for the button to clear the pubid */
|
||||
public $clearIdLabel;
|
||||
|
||||
/** @var string The journal/press initials to use when generating a pub id */
|
||||
public $contextInitials;
|
||||
|
||||
/** @var bool If a %p in the pattern should stand for press (OMP). Otherwise it means pages (OJS). */
|
||||
public $isPForPress = false;
|
||||
|
||||
/** @var string The issue number to use when generating a pub id */
|
||||
public $issueNumber;
|
||||
|
||||
/** @var string The issue volume to use when generating a pub id */
|
||||
public $issueVolume;
|
||||
|
||||
/** @var string A localized message when the pub id can not be generated due to missing information */
|
||||
public $missingPartsLabel;
|
||||
|
||||
/** @var string The page numbers use when generating a pub id */
|
||||
public $pages;
|
||||
|
||||
/** @var string The pattern to use when generating a pub id */
|
||||
public $pattern;
|
||||
|
||||
/** @var string The pub id prefix for this context */
|
||||
public $prefix;
|
||||
|
||||
/** @var string The publisher id to use when generating a pub id */
|
||||
public $publisherId;
|
||||
|
||||
/** @var string Optional separator to add between prefix and suffix when generating pub id */
|
||||
public $separator = '';
|
||||
|
||||
/** @var string The submission ID to use when generating a pub id */
|
||||
public $submissionId;
|
||||
|
||||
/** @var string The publication ID to use when generating a pub id */
|
||||
public $publicationId;
|
||||
|
||||
/** @var string The year of publication to use when generating a pub id */
|
||||
public $year;
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['assignIdLabel'] = $this->assignIdLabel;
|
||||
$config['clearIdLabel'] = $this->clearIdLabel;
|
||||
$config['missingPartsLabel'] = $this->missingPartsLabel;
|
||||
if (isset($this->contextInitials)) {
|
||||
$config['contextInitials'] = $this->contextInitials;
|
||||
}
|
||||
if (isset($this->issueNumber)) {
|
||||
$config['issueNumber'] = $this->issueNumber;
|
||||
}
|
||||
if (isset($this->issueVolume)) {
|
||||
$config['issueVolume'] = $this->issueVolume;
|
||||
}
|
||||
if (isset($this->pages)) {
|
||||
$config['pages'] = $this->pages;
|
||||
}
|
||||
if (isset($this->pattern)) {
|
||||
$config['pattern'] = $this->pattern;
|
||||
}
|
||||
if (isset($this->prefix)) {
|
||||
$config['prefix'] = $this->prefix;
|
||||
}
|
||||
if (isset($this->publisherId)) {
|
||||
$config['publisherId'] = $this->publisherId;
|
||||
}
|
||||
if (isset($this->submissionId)) {
|
||||
$config['submissionId'] = $this->submissionId;
|
||||
}
|
||||
if (isset($this->publicationId)) {
|
||||
$config['publicationId'] = $this->publicationId;
|
||||
}
|
||||
if (isset($this->year)) {
|
||||
$config['year'] = $this->year;
|
||||
}
|
||||
$config['isPForPress'] = $this->isPForPress;
|
||||
$config['separator'] = $this->separator;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldRadioInput.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 FieldRadioInput
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field to select one of a set of options, and one option is a text
|
||||
* field for entering a custom value.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldRadioInput extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-radio-input';
|
||||
|
||||
/** @var array The options which can be selected */
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['options'] = $this->options;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldRichText.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 FieldRichText
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A rich single line text editor field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldRichText extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-rich-text';
|
||||
|
||||
/** @var array Optional. An assoc array of init properties to pass to TinyMCE */
|
||||
public $init;
|
||||
|
||||
/** @var string Optional. A preset size option. */
|
||||
public $size = 'oneline';
|
||||
|
||||
/** @var string Optional. A preset toolbar configuration. */
|
||||
public $toolbar = 'formatgroup';
|
||||
|
||||
/** @var array Optional. A list of required plugins. */
|
||||
public $plugins = 'paste';
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
$config['i18nFormattingLabel'] = __('common.formatting');
|
||||
|
||||
$config['toolbar'] = $this->toolbar;
|
||||
$config['plugins'] = $this->plugins;
|
||||
$config['size'] = $this->size;
|
||||
|
||||
if (!empty($this->init)) {
|
||||
$config['init'] = $this->init;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldRichTextarea.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 FieldRichTextarea
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A rich text editor field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldRichTextarea extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-rich-textarea';
|
||||
|
||||
/** @var array Optional. An assoc array of init properties to pass to TinyMCE */
|
||||
public $init;
|
||||
|
||||
/** @var array Optional. A list of required plugins. */
|
||||
public $plugins = 'paste,link,noneditable';
|
||||
|
||||
/** @var string Optional. A preset size option. */
|
||||
public $size;
|
||||
|
||||
/** @var string Optional. A preset toolbar configuration. */
|
||||
public $toolbar = 'bold italic superscript subscript | link';
|
||||
|
||||
/** @var string Optional. The API endpoint to upload images to. Only include if image uploads are supported here. */
|
||||
public $uploadUrl;
|
||||
|
||||
/** @var int Optional. When a word limit is specified a word counter will be shown */
|
||||
public $wordLimit = 0;
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
if (!empty($this->init)) {
|
||||
$config['init'] = $this->init;
|
||||
}
|
||||
$config['plugins'] = $this->plugins;
|
||||
if (!empty($this->size)) {
|
||||
$config['size'] = $this->size;
|
||||
}
|
||||
$config['toolbar'] = $this->toolbar;
|
||||
if (!empty($this->uploadUrl)) {
|
||||
$config['uploadUrl'] = $this->uploadUrl;
|
||||
}
|
||||
if ($this->wordLimit) {
|
||||
$config['wordLimit'] = $this->wordLimit;
|
||||
$config['wordCountLabel'] = __('publication.wordCount');
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldSelect.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 FieldSelect
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A select field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldSelect extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-select';
|
||||
|
||||
/** @var array The options which can be selected */
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['options'] = $this->options;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldSelectSubmissions.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 FieldSelectSubmissions
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A text field to search for and select submissions.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldSelectSubmissions extends FieldBaseAutosuggest
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-select-submissions';
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldSelectUsers.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 FieldSelectUsers
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A text field to search for and select users.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldSelectUsers extends FieldBaseAutosuggest
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-select-users';
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldShowEnsuringLink.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 FieldShowEnsuringLink
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief An extension of the FieldOptions for the configuration setting which
|
||||
* determines whether or not to show a link to reviewers about keeping reviews
|
||||
* anonymous.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldShowEnsuringLink extends FieldOptions
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-show-ensuring-link';
|
||||
|
||||
/** @var string The message to show in a modal when the link is clicked. */
|
||||
public $message = '';
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['message'] = __('review.anonymousPeerReview');
|
||||
$config['modalTitle'] = __('review.anonymousPeerReview.title');
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldText.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 FieldText
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A basic text field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldText extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-text';
|
||||
|
||||
/** @var string What should the <input type=""> be? */
|
||||
public $inputType = 'text';
|
||||
|
||||
/** @var bool Whether the user should have to click a button to edit the field */
|
||||
public $optIntoEdit = false;
|
||||
|
||||
/** @var string The label of the button added by self::$optIntoEdit */
|
||||
public $optIntoEditLabel = '';
|
||||
|
||||
/** @var string Accepts: `small`, `normal` or `large` */
|
||||
public $size = 'normal';
|
||||
|
||||
/** @var string A prefix to display before the input value */
|
||||
public $prefix = '';
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['inputType'] = $this->inputType;
|
||||
$config['optIntoEdit'] = $this->optIntoEdit;
|
||||
$config['optIntoEditLabel'] = $this->optIntoEditLabel;
|
||||
$config['size'] = $this->size;
|
||||
$config['prefix'] = $this->prefix;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldTextarea.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 FieldTextarea
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A multiline textarea field in a form.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldTextarea extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-textarea';
|
||||
|
||||
/** @var string Optional. A preset size option. */
|
||||
public $size;
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
if (isset($this->size)) {
|
||||
$config['size'] = $this->size;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldUpload.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 FieldUpload
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field for uploading a file.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
use APP\core\Application;
|
||||
|
||||
class FieldUpload extends Field
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-upload';
|
||||
|
||||
/**
|
||||
* @var array Options to pass to the dropzone.js instance.
|
||||
*
|
||||
* A `url` key must be included with the value of the API endpoint where files
|
||||
* can be uploaded to: <api-path>/temporaryFiles.
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @copydoc Field::__construct()
|
||||
*/
|
||||
public function __construct($name, $args = [])
|
||||
{
|
||||
parent::__construct($name, $args);
|
||||
|
||||
$this->options['maxFilesize'] = Application::getIntMaxFileMBs();
|
||||
$this->options['timeout'] = ini_get('max_execution_time')
|
||||
? ini_get('max_execution_time') * 1000
|
||||
: 0;
|
||||
|
||||
$this->options = array_merge(
|
||||
[
|
||||
'dropzoneDictDefaultMessage' => __('form.dropzone.dictDefaultMessage'),
|
||||
'dropzoneDictFallbackMessage' => __('form.dropzone.dictFallbackMessage'),
|
||||
'dropzoneDictFallbackText' => __('form.dropzone.dictFallbackText'),
|
||||
'dropzoneDictFileTooBig' => __('form.dropzone.dictFileTooBig'),
|
||||
'dropzoneDictInvalidFileType' => __('form.dropzone.dictInvalidFileType'),
|
||||
'dropzoneDictResponseError' => __('form.dropzone.dictResponseError'),
|
||||
'dropzoneDictCancelUpload' => __('form.dropzone.dictCancelUpload'),
|
||||
'dropzoneDictUploadCanceled' => __('form.dropzone.dictUploadCanceled'),
|
||||
'dropzoneDictCancelUploadConfirmation' => __('form.dropzone.dictCancelUploadConfirmation'),
|
||||
'dropzoneDictRemoveFile' => __('form.dropzone.dictRemoveFile'),
|
||||
'dropzoneDictMaxFilesExceeded' => __('form.dropzone.dictMaxFilesExceeded'),
|
||||
],
|
||||
$this->options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Field::validate()
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (empty($this->options['url'])) {
|
||||
return false;
|
||||
}
|
||||
return parent::validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['options'] = $this->options;
|
||||
$config['uploadFileLabel'] = __('common.upload.addFile');
|
||||
$config['restoreLabel'] = __('common.upload.restore');
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldUploadImage.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 FieldUploadImage
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A field for uploading a file.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
class FieldUploadImage extends FieldUpload
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-upload-image';
|
||||
|
||||
/** @var string Base url for displaying the image */
|
||||
public $baseUrl = '';
|
||||
|
||||
/** @var string Label for the alt text field */
|
||||
public $altTextLabel = '';
|
||||
|
||||
/** @var string Description for the alt text field */
|
||||
public $altTextDescription = '';
|
||||
|
||||
/** @var string Description for the image thumbnail */
|
||||
public $thumbnailDescription = '';
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
if (!array_key_exists('acceptedFiles', $this->options)) {
|
||||
$this->options['acceptedFiles'] = 'image/*';
|
||||
}
|
||||
$config = parent::getConfig();
|
||||
$config['baseUrl'] = $this->baseUrl;
|
||||
|
||||
$config['thumbnailDescription'] = __('common.upload.thumbnailPreview');
|
||||
$config['altTextLabel'] = __('common.altText');
|
||||
$config['altTextDescription'] = __('common.altTextInstructions');
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Field::getEmptyValue()
|
||||
*/
|
||||
public function getEmptyValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FormComponent.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 FormComponent
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A base class for building forms to be passed to the Form component
|
||||
* in the UI Library.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms;
|
||||
|
||||
use Exception;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
define('FIELD_POSITION_BEFORE', 'before');
|
||||
define('FIELD_POSITION_AFTER', 'after');
|
||||
|
||||
class FormComponent
|
||||
{
|
||||
/**
|
||||
* @var string An $action value that will emit an event
|
||||
* when the form is submitted, instead of sending a
|
||||
* HTTP request
|
||||
*/
|
||||
public const ACTION_EMIT = 'emit';
|
||||
|
||||
/** @var string A unique ID for this form */
|
||||
public $id = '';
|
||||
|
||||
/** @var string Form method: POST or PUT */
|
||||
public $method = '';
|
||||
|
||||
/** @var string Where the form should be submitted. */
|
||||
public $action = '';
|
||||
|
||||
/** @var array Key/value list of languages this form should support. Key = locale code. Value = locale name */
|
||||
public $locales = [];
|
||||
|
||||
/** @var array List of fields in this form. */
|
||||
public $fields = [];
|
||||
|
||||
/** @var array List of groups in this form. */
|
||||
public $groups = [];
|
||||
|
||||
/** @var array List of hidden fields in this form. */
|
||||
public $hiddenFields = [];
|
||||
|
||||
/** @var array List of pages in this form. */
|
||||
public $pages = [];
|
||||
|
||||
/** @var array List of error messages */
|
||||
public $errors = [];
|
||||
|
||||
/**
|
||||
* Initialize the form with config parameters
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $method
|
||||
* @param string $action
|
||||
* @param array $locales
|
||||
*/
|
||||
public function __construct($id, $method, $action, $locales)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->action = $action;
|
||||
$this->method = $method;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a form field
|
||||
*
|
||||
* @param Field $field
|
||||
* @param array $position [
|
||||
*
|
||||
* @option string One of FIELD_POSITION_BEFORE or FIELD_POSITION_AFTER
|
||||
* @option string The field to position it before or after
|
||||
* ]
|
||||
*/
|
||||
public function addField($field, $position = []): static
|
||||
{
|
||||
if (empty($position)) {
|
||||
$this->fields[] = $field;
|
||||
} else {
|
||||
$this->fields = $this->addToPosition($position[1], $this->fields, $field, $position[0]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a form field
|
||||
*
|
||||
* @param string $fieldName
|
||||
*/
|
||||
public function removeField($fieldName): static
|
||||
{
|
||||
$this->fields = array_values(array_filter($this->fields, function ($field) use ($fieldName) {
|
||||
return $field->name !== $fieldName;
|
||||
}));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a form field
|
||||
*
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return ?Field
|
||||
*/
|
||||
public function getField($fieldName)
|
||||
{
|
||||
foreach ($this->fields as $field) {
|
||||
if ($field->name === $fieldName) {
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a form group
|
||||
*
|
||||
* @param array $args [
|
||||
*
|
||||
* @option id string Required A unique ID for this form group
|
||||
* @option label string A label to identify this group of fields. Will become the fieldset's <legend>
|
||||
* @option description string A description of this group of fields.
|
||||
* ]
|
||||
*
|
||||
* @param array $position [
|
||||
*
|
||||
* @option string One of FIELD_POSITION_BEFORE or FIELD_POSITION_AFTER
|
||||
* @option string The group to position it before or after
|
||||
* ]
|
||||
*/
|
||||
public function addGroup($args, $position = []): static
|
||||
{
|
||||
if (empty($args['id'])) {
|
||||
throw new Exception('Tried to add a form group without an id.');
|
||||
}
|
||||
if (empty($position)) {
|
||||
$this->groups[] = $args;
|
||||
} else {
|
||||
$this->groups = $this->addToPosition($position[1], $this->groups, $args, $position[0]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a form group
|
||||
*
|
||||
* @param string $groupId
|
||||
*/
|
||||
public function removeGroup($groupId): static
|
||||
{
|
||||
$this->groups = array_filter($this->groups, function ($group) use ($groupId) {
|
||||
return $group['id'] !== $groupId;
|
||||
});
|
||||
$this->fields = array_filter($this->fields, function ($field) use ($groupId) {
|
||||
return $field['groupId'] !== $groupId;
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a form page
|
||||
*
|
||||
* @param array $args [
|
||||
*
|
||||
* @option id string Required A unique ID for this form page
|
||||
* @option label string The name of the page to identify it in the page list
|
||||
* @option submitButton array Required Assoc array defining submission/next button params. Supports any param of the Button component in the UI Library.
|
||||
* @option previousButton array Assoc array defining button params to go back to the previous page. Supports any param of the Button component in the UI Library.
|
||||
* ]
|
||||
*
|
||||
* @param array $position [
|
||||
*
|
||||
* @option string One of FIELD_POSITION_BEFORE or FIELD_POSITION_AFTER
|
||||
* @option string The page to position it before or after
|
||||
* ]
|
||||
*/
|
||||
public function addPage($args, $position = []): static
|
||||
{
|
||||
if (empty($args['id'])) {
|
||||
fatalError('Tried to add a form page without an id.');
|
||||
}
|
||||
if (empty($position)) {
|
||||
$this->pages[] = $args;
|
||||
} else {
|
||||
$this->pages = $this->addToPosition($position[1], $this->pages, $args, $position[0]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a form page
|
||||
*
|
||||
* @param string $pageId
|
||||
*/
|
||||
public function removePage($pageId): static
|
||||
{
|
||||
$this->pages = array_filter($this->pages, function ($page) use ($pageId) {
|
||||
return $page['id'] !== $pageId;
|
||||
});
|
||||
foreach ($this->groups as $group) {
|
||||
if ($group['pageId'] === $pageId) {
|
||||
$this->removeGroup($group['id']);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an field, group or page to a specific position in its array
|
||||
*
|
||||
* @param string $id The id of the item to position before or after
|
||||
* @param array $list The list of fields, groups or pages
|
||||
* @param mixed $item The item to insert
|
||||
* @param string $position FIELD_POSITION_BEFORE or FIELD_POSITION_AFTER
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function addToPosition($id, $list, $item, $position)
|
||||
{
|
||||
$index = count($list);
|
||||
foreach ($list as $key => $val) {
|
||||
if (($val instanceof \PKP\components\forms\Field && $id === $val->name) || (!$val instanceof \PKP\components\forms\Field && $id === $val['id'])) {
|
||||
$index = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$index && $position === FIELD_POSITION_BEFORE) {
|
||||
array_unshift($list, $item);
|
||||
return $list;
|
||||
}
|
||||
|
||||
$slice = $position === FIELD_POSITION_BEFORE ? $index : $index + 1;
|
||||
|
||||
return array_merge(
|
||||
array_slice($list, 0, $slice),
|
||||
[$item],
|
||||
array_slice($list, $slice)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hidden field to this form
|
||||
*/
|
||||
public function addHiddenField(string $name, $value)
|
||||
{
|
||||
$this->hiddenFields[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the configuration data to be used when initializing this
|
||||
* handler on the frontend
|
||||
*
|
||||
* @return array Configuration data
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
if (empty($this->id) || empty($this->action) || ($this->action !== self::ACTION_EMIT && empty($this->method))) {
|
||||
throw new Exception('FormComponent::getConfig() was called but one or more required property is missing: id, method, action.');
|
||||
}
|
||||
|
||||
Hook::run('Form::config::before', [$this]);
|
||||
|
||||
// Add a default page/group if none exist
|
||||
if (!$this->groups) {
|
||||
$this->addGroup(['id' => 'default']);
|
||||
$this->fields = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $this->fields);
|
||||
}
|
||||
|
||||
if (!$this->pages) {
|
||||
$this->addPage(['id' => 'default', 'submitButton' => ['label' => __('common.save')]]);
|
||||
$this->groups = array_map(function ($group) {
|
||||
$group['pageId'] = 'default';
|
||||
return $group;
|
||||
}, $this->groups);
|
||||
}
|
||||
|
||||
$fieldsConfig = array_map([$this, 'getFieldConfig'], $this->fields);
|
||||
|
||||
$visibleLocales = [Locale::getLocale()];
|
||||
if (Locale::getLocale() !== Locale::getPrimaryLocale()) {
|
||||
array_unshift($visibleLocales, Locale::getPrimaryLocale());
|
||||
}
|
||||
|
||||
$config = [
|
||||
'id' => $this->id,
|
||||
'method' => $this->method,
|
||||
'action' => $this->action,
|
||||
'fields' => $fieldsConfig,
|
||||
'groups' => $this->groups,
|
||||
'hiddenFields' => (object) $this->hiddenFields,
|
||||
'pages' => $this->pages,
|
||||
'primaryLocale' => Locale::getPrimaryLocale(),
|
||||
'visibleLocales' => $visibleLocales,
|
||||
'supportedFormLocales' => array_values($this->locales), // See #5690
|
||||
'errors' => (object) [],
|
||||
];
|
||||
|
||||
Hook::call('Form::config::after', [&$config, $this]);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a configuration array for a single field
|
||||
*
|
||||
* @param Field $field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldConfig($field)
|
||||
{
|
||||
$config = $field->getConfig();
|
||||
|
||||
// Add a value property if the field does not include one
|
||||
if (!array_key_exists('value', $config)) {
|
||||
$config['value'] = $field->isMultilingual ? [] : $field->getEmptyValue();
|
||||
}
|
||||
if ($field->isMultilingual) {
|
||||
if (is_null($config['value'])) {
|
||||
$config['value'] = [];
|
||||
}
|
||||
foreach ($this->locales as $locale) {
|
||||
if (!array_key_exists($locale['key'], $config['value'])) {
|
||||
$config['value'][$locale['key']] = $field->getEmptyValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/announcement/PKPAnnouncementForm.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 PKPAnnouncementForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for creating a new announcement
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\announcement;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\announcement\AnnouncementTypeDAO;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\config\Config;
|
||||
use PKP\context\Context;
|
||||
use PKP\db\DAORegistry;
|
||||
|
||||
define('FORM_ANNOUNCEMENT', 'announcement');
|
||||
|
||||
class PKPAnnouncementForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ANNOUNCEMENT;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'POST';
|
||||
|
||||
public ?Context $context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
*/
|
||||
public function __construct($action, $locales, string $baseUrl, string $temporaryFileApiUrl, ?Context $context = null)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->context = $context;
|
||||
|
||||
$announcementTypeOptions = $this->getAnnouncementTypeOptions();
|
||||
|
||||
$this->addField(new FieldText('title', [
|
||||
'label' => __('common.title'),
|
||||
'size' => 'large',
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('descriptionShort', [
|
||||
'label' => __('manager.announcements.form.descriptionShort'),
|
||||
'description' => __('manager.announcements.form.descriptionShortInstructions'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('description', [
|
||||
'label' => __('manager.announcements.form.description'),
|
||||
'description' => __('manager.announcements.form.descriptionInstructions'),
|
||||
'isMultilingual' => true,
|
||||
'size' => 'large',
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist',
|
||||
'plugins' => 'paste,link,lists',
|
||||
]));
|
||||
if (Config::getVar('features', 'announcement_images')) {
|
||||
$this->addField(new FieldUploadImage('image', [
|
||||
'label' => __('manager.image'),
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]));
|
||||
}
|
||||
$this->addField(new FieldText('dateExpire', [
|
||||
'label' => __('manager.announcements.form.dateExpire'),
|
||||
'description' => __('manager.announcements.form.dateExpireInstructions'),
|
||||
'size' => 'small',
|
||||
]));
|
||||
if (!empty($announcementTypeOptions)) {
|
||||
$this->addField(new FieldOptions('typeId', [
|
||||
'label' => __('manager.announcementTypes.typeName'),
|
||||
'type' => 'radio',
|
||||
'options' => $announcementTypeOptions,
|
||||
]));
|
||||
}
|
||||
|
||||
$this->addField(new FieldOptions('sendEmail', [
|
||||
'label' => __('common.sendEmail'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('notification.sendNotificationConfirmation')
|
||||
]
|
||||
]
|
||||
]));
|
||||
}
|
||||
|
||||
protected function getAnnouncementTypeOptions(): array
|
||||
{
|
||||
/** @var AnnouncementTypeDAO */
|
||||
$announcementTypeDao = DAORegistry::getDAO('AnnouncementTypeDAO');
|
||||
|
||||
$announcementTypes = $announcementTypeDao->getByContextId($this->context?->getId());
|
||||
|
||||
$announcementTypeOptions = [];
|
||||
foreach ($announcementTypes as $announcementType) {
|
||||
$announcementTypeOptions[] = [
|
||||
'value' => (int) $announcementType->getId(),
|
||||
'label' => $announcementType->getLocalizedTypeName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $announcementTypeOptions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPAnnouncementSettingsForm.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 PKPAnnouncementSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for enabling and configuring announcements.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
use PKP\site\Site;
|
||||
|
||||
define('FORM_ANNOUNCEMENT_SETTINGS', 'announcementSettings');
|
||||
|
||||
class PKPAnnouncementSettingsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ANNOUNCEMENT_SETTINGS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
*/
|
||||
public function __construct($action, $locales, Context|Site $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldOptions('enableAnnouncements', [
|
||||
'label' => __('manager.setup.announcements'),
|
||||
'description' => __('manager.setup.enableAnnouncements.description'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.enableAnnouncements.enable')]
|
||||
],
|
||||
'value' => (bool) $context->getData('enableAnnouncements'),
|
||||
]))
|
||||
->addField(new FieldRichTextarea('announcementsIntroduction', [
|
||||
'label' => __('manager.setup.announcementsIntroduction'),
|
||||
'tooltip' => __('manager.setup.announcementsIntroduction.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('announcementsIntroduction'),
|
||||
'showWhen' => 'enableAnnouncements',
|
||||
]))
|
||||
->addField(new FieldText('numAnnouncementsHomepage', [
|
||||
'label' => __('manager.setup.numAnnouncementsHomepage'),
|
||||
'description' => __('manager.setup.numAnnouncementsHomepage.description'),
|
||||
'size' => 'small',
|
||||
'value' => $context->getData('numAnnouncementsHomepage'),
|
||||
'showWhen' => 'enableAnnouncements',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPAppearanceAdvancedForm.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 PKPAppearanceAdvancedForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for advanced settings under the website appearance tab.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldUpload;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_APPEARANCE_ADVANCED', 'appearanceAdvanced');
|
||||
|
||||
class PKPAppearanceAdvancedForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_APPEARANCE_ADVANCED;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $baseUrl Site's base URL. Used for image previews.
|
||||
* @param string $temporaryFileApiUrl URL to upload files to
|
||||
* @param string $imageUploadUrl The API endpoint for images uploaded through the rich text field
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $baseUrl, $temporaryFileApiUrl, $imageUploadUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldUpload('styleSheet', [
|
||||
'label' => __('manager.setup.useStyleSheet'),
|
||||
'value' => $context->getData('styleSheet'),
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
'acceptedFiles' => '.css',
|
||||
],
|
||||
]))
|
||||
->addField(new FieldUploadImage('favicon', [
|
||||
'label' => __('manager.setup.favicon'),
|
||||
'value' => $context->getData('favicon'),
|
||||
'isMultilingual' => true,
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
'acceptedFiles' => 'image/x-icon,image/png,image/gif',
|
||||
],
|
||||
]))
|
||||
->addField(new FieldRichTextarea('additionalHomeContent', [
|
||||
'label' => __('manager.setup.additionalContent'),
|
||||
'description' => __('manager.setup.additionalContent.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('additionalHomeContent'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPAppearanceSetupForm.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 PKPAppearanceSetupForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for general website appearance setup, such as uploading
|
||||
* a logo.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
|
||||
define('FORM_APPEARANCE_SETUP', 'appearanceSetup');
|
||||
|
||||
class PKPAppearanceSetupForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_APPEARANCE_SETUP;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $baseUrl Site's base URL. Used for image previews.
|
||||
* @param string $temporaryFileApiUrl URL to upload files to
|
||||
* @param string $imageUploadUrl The API endpoint for images uploaded through the rich text field
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $baseUrl, $temporaryFileApiUrl, $imageUploadUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$sidebarOptions = [];
|
||||
$enabledOptions = [];
|
||||
$disabledOptions = [];
|
||||
|
||||
$currentBlocks = (array) $context->getData('sidebar');
|
||||
|
||||
$plugins = PluginRegistry::loadCategory('blocks', true);
|
||||
|
||||
foreach ($currentBlocks as $plugin) {
|
||||
if (isset($plugins[$plugin])) {
|
||||
$enabledOptions[] = [
|
||||
'value' => $plugin,
|
||||
'label' => htmlspecialchars($plugins[$plugin]->getDisplayName()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($plugins as $pluginName => $plugin) {
|
||||
if (!in_array($pluginName, $currentBlocks)) {
|
||||
$disabledOptions[] = [
|
||||
'value' => $pluginName,
|
||||
'label' => htmlspecialchars($plugin->getDisplayName()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$sidebarOptions = array_merge($enabledOptions, $disabledOptions);
|
||||
|
||||
$this->addField(new FieldUploadImage('pageHeaderLogoImage', [
|
||||
'label' => __('manager.setup.logo'),
|
||||
'value' => $context->getData('pageHeaderLogoImage'),
|
||||
'isMultilingual' => true,
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]))
|
||||
->addField(new FieldUploadImage('homepageImage', [
|
||||
'label' => __('manager.setup.homepageImage'),
|
||||
'tooltip' => __('manager.setup.homepageImage.description'),
|
||||
'value' => $context->getData('homepageImage'),
|
||||
'isMultilingual' => true,
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]))
|
||||
->addField(new FieldRichTextarea('pageFooter', [
|
||||
'label' => __('manager.setup.pageFooter'),
|
||||
'tooltip' => __('manager.setup.pageFooter.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('pageFooter'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]))
|
||||
->addField(new FieldOptions('sidebar', [
|
||||
'label' => __('manager.setup.layout.sidebar'),
|
||||
'isOrderable' => true,
|
||||
'value' => $currentBlocks,
|
||||
'options' => $sidebarOptions,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPContactForm.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 PKPContactForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's contact details.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_CONTACT', 'contact');
|
||||
|
||||
class PKPContactForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_CONTACT;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'principal',
|
||||
'label' => __('manager.setup.principalContact'),
|
||||
'description' => __('manager.setup.principalContactDescription'),
|
||||
])
|
||||
->addField(new FieldText('contactName', [
|
||||
'label' => __('common.name'),
|
||||
'isRequired' => true,
|
||||
'groupId' => 'principal',
|
||||
'value' => $context->getData('contactName'),
|
||||
]))
|
||||
->addField(new FieldText('contactEmail', [
|
||||
'label' => __('user.email'),
|
||||
'isRequired' => true,
|
||||
'groupId' => 'principal',
|
||||
'value' => $context->getData('contactEmail'),
|
||||
]))
|
||||
->addField(new FieldText('contactPhone', [
|
||||
'label' => __('user.phone'),
|
||||
'groupId' => 'principal',
|
||||
'value' => $context->getData('contactPhone'),
|
||||
]))
|
||||
->addField(new FieldText('contactAffiliation', [
|
||||
'label' => __('user.affiliation'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'principal',
|
||||
'value' => $context->getData('contactAffiliation'),
|
||||
]))
|
||||
->addField(new FieldTextarea('mailingAddress', [
|
||||
'label' => __('common.mailingAddress'),
|
||||
'isRequired' => false,
|
||||
'size' => 'small',
|
||||
'groupId' => 'principal',
|
||||
'value' => $context->getData('mailingAddress'),
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'technical',
|
||||
'label' => __('manager.setup.technicalSupportContact'),
|
||||
'description' => __('manager.setup.technicalSupportContactDescription'),
|
||||
])
|
||||
->addField(new FieldText('supportName', [
|
||||
'label' => __('common.name'),
|
||||
'isRequired' => true,
|
||||
'groupId' => 'technical',
|
||||
'value' => $context->getData('supportName'),
|
||||
]))
|
||||
->addField(new FieldText('supportEmail', [
|
||||
'label' => __('user.email'),
|
||||
'isRequired' => true,
|
||||
'groupId' => 'technical',
|
||||
'value' => $context->getData('supportEmail'),
|
||||
]))
|
||||
->addField(new FieldText('supportPhone', [
|
||||
'label' => __('user.phone'),
|
||||
'groupId' => 'technical',
|
||||
'value' => $context->getData('supportPhone'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPContextForm.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 PKPContextForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for adding and editing a context from the admin area.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\facades\Locale;
|
||||
|
||||
define('FORM_CONTEXT', 'context');
|
||||
|
||||
class PKPContextForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_CONTEXT;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'POST';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param string $baseUrl Base URL for the site
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $baseUrl, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->method = $context ? 'PUT' : 'POST';
|
||||
|
||||
$countries = [];
|
||||
foreach (Locale::getCountries() as $country) {
|
||||
$countries[] = [
|
||||
'value' => $country->getAlpha2(),
|
||||
'label' => $country->getLocalName()
|
||||
];
|
||||
}
|
||||
|
||||
usort($countries, function ($a, $b) {
|
||||
return strcmp($a['label'], $b['label']);
|
||||
});
|
||||
|
||||
$this
|
||||
->addField(new FieldText('name', [
|
||||
'label' => __('manager.setup.contextTitle'),
|
||||
'isRequired' => true,
|
||||
'isMultilingual' => true,
|
||||
'value' => $context ? $context->getData('name') : null,
|
||||
]))
|
||||
->addField(new FieldText('acronym', [
|
||||
'label' => __('manager.setup.contextInitials'),
|
||||
'size' => 'small',
|
||||
'isRequired' => true,
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'identity',
|
||||
'value' => $context ? $context->getData('acronym') : null,
|
||||
]))
|
||||
->addField(new FieldText('contactName', [
|
||||
'label' => __('manager.setup.principalContact') . ' ' . __('common.name'),
|
||||
'isRequired' => true,
|
||||
'value' => $context ? $context->getData('contactName') : null,
|
||||
]))
|
||||
->addField(new FieldText('contactEmail', [
|
||||
'label' => __('manager.setup.principalContact') . ' ' . __('user.email'),
|
||||
'isRequired' => true,
|
||||
'value' => $context ? $context->getData('contactEmail') : null,
|
||||
]))
|
||||
->addField(new FieldSelect('country', [
|
||||
'label' => __('common.country'),
|
||||
'description' => __('manager.setup.selectCountry'),
|
||||
'options' => $countries,
|
||||
'value' => $context ? $context->getData('country') : null,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('description', [
|
||||
'label' => __('admin.contexts.contextDescription'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context ? $context->getData('description') : null,
|
||||
]))
|
||||
->addField(new FieldText('urlPath', [
|
||||
'label' => __('context.path'),
|
||||
'isRequired' => true,
|
||||
'value' => $context ? $context->getData('urlPath') : null,
|
||||
'prefix' => $baseUrl . '/',
|
||||
'size' => 'large',
|
||||
]));
|
||||
|
||||
if (!$context && count($locales) > 1) {
|
||||
$localeOptions = [];
|
||||
foreach ($locales as $locale) {
|
||||
$localeOptions[] = [
|
||||
'value' => $locale['key'],
|
||||
'label' => $locale['label'],
|
||||
];
|
||||
}
|
||||
$this->addField(new FieldOptions('supportedLocales', [
|
||||
'label' => __('common.languages'),
|
||||
'isRequired' => true,
|
||||
'value' => [],
|
||||
'options' => $localeOptions,
|
||||
]))
|
||||
->addField(new FieldOptions('primaryLocale', [
|
||||
'label' => __('locale.primary'),
|
||||
'type' => 'radio',
|
||||
'isRequired' => true,
|
||||
'value' => null,
|
||||
'options' => $localeOptions,
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/forms/context/PKPContextStatisticsForm.php
|
||||
*
|
||||
* Copyright (c) 2022 Simon Fraser University
|
||||
* Copyright (c) 2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPContextStatisticsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for the context specific statistics settings.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
use PKP\site\Site;
|
||||
use PKP\statistics\PKPStatisticsHelper;
|
||||
|
||||
define('FORM_CONTEXT_STATISTICS', 'contextStatistics');
|
||||
|
||||
class PKPContextStatisticsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_CONTEXT_STATISTICS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
*/
|
||||
public function __construct(string $action, array $locales, Site $site, Context $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$possibleGeoOptions = [
|
||||
'disabled' => __('manager.settings.statistics.geoUsageStats.disabled'),
|
||||
PKPStatisticsHelper::STATISTICS_SETTING_COUNTRY => __('manager.settings.statistics.geoUsageStats.countryLevel'),
|
||||
PKPStatisticsHelper::STATISTICS_SETTING_REGION => __('manager.settings.statistics.geoUsageStats.regionLevel'),
|
||||
PKPStatisticsHelper::STATISTICS_SETTING_CITY => __('manager.settings.statistics.geoUsageStats.cityLevel'),
|
||||
];
|
||||
$geoOptions = [];
|
||||
foreach ($possibleGeoOptions as $value => $label) {
|
||||
$geoOptions[] = [
|
||||
'value' => $value,
|
||||
'label' => $label,
|
||||
];
|
||||
if ($site->getData('enableGeoUsageStats') === $value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$selectedGeoOption = $site->getData('enableGeoUsageStats');
|
||||
if ($context->getData('enableGeoUsageStats') != null &&
|
||||
str_starts_with($selectedGeoOption, $context->getData('enableGeoUsageStats'))) {
|
||||
$selectedGeoOption = $context->getData('enableGeoUsageStats');
|
||||
}
|
||||
|
||||
if ($site->getData('enableGeoUsageStats') && $site->getData('enableGeoUsageStats') !== 'disabled') {
|
||||
$this->addField(new FieldOptions('enableGeoUsageStats', [
|
||||
'label' => __('manager.settings.statistics.geoUsageStats'),
|
||||
'description' => __('manager.settings.statistics.geoUsageStats.description'),
|
||||
'type' => 'radio',
|
||||
'options' => $geoOptions,
|
||||
'value' => $selectedGeoOption,
|
||||
]));
|
||||
}
|
||||
if ($site->getData('enableInstitutionUsageStats')) {
|
||||
$this->addField(new FieldOptions('enableInstitutionUsageStats', [
|
||||
'label' => __('manager.settings.statistics.institutionUsageStats'),
|
||||
'description' => __('manager.settings.statistics.institutionUsageStats.description'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('manager.settings.statistics.institutionUsageStats.enable'),
|
||||
],
|
||||
],
|
||||
'value' => $context->getData('enableInstitutionUsageStats') !== null ? $context->getData('enableInstitutionUsageStats') : $site->getData('enableInstitutionUsageStats'),
|
||||
]));
|
||||
}
|
||||
if ($site->getData('isSushiApiPublic') !== null && $site->getData('isSushiApiPublic')) {
|
||||
$this->addField(new FieldOptions('isSushiApiPublic', [
|
||||
'label' => __('manager.settings.statistics.publicSushiApi'),
|
||||
'description' => __('manager.settings.statistics.publicSushiApi.description'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('manager.settings.statistics.publicSushiApi.public'),
|
||||
],
|
||||
],
|
||||
'value' => $context->getData('isSushiApiPublic') !== null ? $context->getData('isSushiApiPublic') : $site->getData('isSushiApiPublic'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPDateTimeForm.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 PKPDateTimeForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for presenting date and time on the frontend
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRadioInput;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_DATE_TIME', 'dateTime');
|
||||
|
||||
class PKPDateTimeForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_DATE_TIME;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$localizedOptions = []; // template for localized options to be used for date and time format
|
||||
foreach ($this->locales as $key => $localeValue) {
|
||||
$localizedOptions[$localeValue['key']] = $key;
|
||||
}
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'descriptions',
|
||||
'label' => __('manager.setup.dateTime.descriptionTitle'),
|
||||
'description' => __('manager.setup.dateTime.description'),
|
||||
])
|
||||
//The default date format to use in the editorial and reader interfaces.
|
||||
->addField(new FieldRadioInput('dateFormatLong', [
|
||||
'label' => __('manager.setup.dateTime.longDate'),
|
||||
'isMultilingual' => true,
|
||||
'options' => $this->_setDateOptions([
|
||||
'%B %e, %Y',
|
||||
'%B %e %Y',
|
||||
'%e %B %Y',
|
||||
'%Y %B %e',
|
||||
]),
|
||||
'value' => $context->getDateTimeFormats('dateFormatLong'),
|
||||
'groupId' => 'descriptions',
|
||||
]))
|
||||
// A brief date format that is used when there is less space for the full date.
|
||||
->addField(new FieldRadioInput('dateFormatShort', [
|
||||
'label' => __('manager.setup.dateTime.shortDate'),
|
||||
'isMultilingual' => true,
|
||||
'options' => $this->_setDateOptions([
|
||||
'%Y-%m-%d',
|
||||
'%d-%m-%Y',
|
||||
'%m/%d/%Y',
|
||||
'%d.%m.%Y',
|
||||
]),
|
||||
'value' => $context->getDateTimeFormats('dateFormatShort'),
|
||||
'groupId' => 'descriptions',
|
||||
|
||||
]))
|
||||
->addField(new FieldRadioInput('timeFormat', [
|
||||
'label' => __('manager.setup.dateTime.time'),
|
||||
'isMultilingual' => true,
|
||||
'options' => $this->_setDateOptions([
|
||||
'%H:%M',
|
||||
'%I:%M %p',
|
||||
'%l:%M%P',
|
||||
]),
|
||||
'value' => $context->getDateTimeFormats('timeFormat'),
|
||||
'groupId' => 'descriptions',
|
||||
]))
|
||||
->addField(new FieldRadioInput('datetimeFormatLong', [
|
||||
'label' => __('manager.setup.dateTime.longDateTime'),
|
||||
'isMultilingual' => true,
|
||||
'options' => array_map(function ($value) use ($context, $localizedOptions) {
|
||||
$locale = array_search($value, $localizedOptions);
|
||||
$optionValue = $context->getLocalizedDateFormatLong($locale) . ' - ' . $context->getLocalizedTimeFormat($locale);
|
||||
return [
|
||||
[
|
||||
'value' => $optionValue,
|
||||
'label' => $optionValue,
|
||||
],
|
||||
[
|
||||
'isInput' => true,
|
||||
'label' => __('manager.setup.dateTime.custom'),
|
||||
]
|
||||
];
|
||||
}, $localizedOptions),
|
||||
'value' => $context->getDateTimeFormats('datetimeFormatLong'),
|
||||
'groupId' => 'descriptions',
|
||||
]))
|
||||
->addField(new FieldRadioInput('datetimeFormatShort', [
|
||||
'label' => __('manager.setup.dateTime.shortDateTime'),
|
||||
'isMultilingual' => true,
|
||||
'options' => array_map(function ($value) use ($context, $localizedOptions) {
|
||||
$locale = array_search($value, $localizedOptions);
|
||||
$optionValue = $context->getLocalizedDateFormatShort($locale) . ' ' . $context->getLocalizedTimeFormat($locale);
|
||||
return [
|
||||
[
|
||||
'value' => $optionValue,
|
||||
'label' => $optionValue,
|
||||
],
|
||||
[
|
||||
'isInput' => true,
|
||||
'label' => __('manager.setup.dateTime.custom'),
|
||||
]
|
||||
];
|
||||
}, $localizedOptions),
|
||||
'value' => $context->getDateTimeFormats('datetimeFormatShort'),
|
||||
'groupId' => 'descriptions',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set localized options for date/time fields
|
||||
*
|
||||
* @param array $optionValues options to pass to the field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _setDateOptions($optionValues)
|
||||
{
|
||||
$options = [];
|
||||
foreach ($this->locales as $localeValue) {
|
||||
$locale = $localeValue['key'];
|
||||
foreach ($optionValues as $optionValue) {
|
||||
$options[$locale][] = [
|
||||
'value' => $optionValue,
|
||||
'label' => $optionValue
|
||||
];
|
||||
}
|
||||
|
||||
$options[$locale][] = [
|
||||
'isInput' => true,
|
||||
'label' => __('manager.setup.dateTime.custom'),
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPDisableSubmissionsForm.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 PKPDisableSubmissionsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for disabling new submissions.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_DISABLE_SUBMISSIONS', 'disableSubmissions');
|
||||
|
||||
class PKPDisableSubmissionsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_DISABLE_SUBMISSIONS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$url = Application::get()->getRequest()->getDispatcher()->url(
|
||||
Application::get()->getRequest(),
|
||||
Application::ROUTE_PAGE,
|
||||
null,
|
||||
'management',
|
||||
'settings',
|
||||
'context',
|
||||
null,
|
||||
'sections'
|
||||
);
|
||||
|
||||
$description = __('manager.setup.disableSubmissions.description', ['url' => $url]);
|
||||
|
||||
$this->addField(new FieldOptions('disableSubmissions', [
|
||||
'label' => __('manager.setup.disableSubmissions'),
|
||||
'description' => $description,
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('manager.setup.disableSubmissions'),
|
||||
],
|
||||
],
|
||||
'value' => (bool) $context->getData('disableSubmissions'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPDoiRegistrationSettingsForm.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 PKPDoiRegistrationSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for enabling and configuring DOI settings for a given context
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\plugins\IDoiRegistrationAgency;
|
||||
use PKP\components\forms\Field;
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\plugins\Plugin;
|
||||
|
||||
class PKPDoiRegistrationSettingsForm extends FormComponent
|
||||
{
|
||||
public const FORM_DOI_REGISTRATION_SETTINGS = 'doiRegistrationSettings';
|
||||
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = self::FORM_DOI_REGISTRATION_SETTINGS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
protected const GENERAL_SETTINGS = 'generalSettings';
|
||||
protected const AGENCY_SPECIFIC_SETTINGS = 'agencySpecificSettings';
|
||||
|
||||
/** @var Field[] Registration agency plugin-specific settings, grouped by plugin */
|
||||
protected array $agencyFields;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(string $action, array $locales, Context $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$registrationAgencies = collect();
|
||||
|
||||
Hook::call('DoiSettingsForm::setEnabledRegistrationAgencies', [&$registrationAgencies]);
|
||||
|
||||
// Add registration agency options for each registration agency plugin
|
||||
$options = [
|
||||
[
|
||||
'value' => Context::SETTING_NO_REGISTRATION_AGENCY,
|
||||
'label' => __('doi.manager.settings.registrationAgency.none'),
|
||||
],
|
||||
];
|
||||
|
||||
$this->agencyFields = [];
|
||||
|
||||
$registrationAgencies->each(function (IDoiRegistrationAgency|Plugin $agency) use (&$options, $context) {
|
||||
$options[] = [
|
||||
'value' => $agency->getName(),
|
||||
'label' => $agency->getRegistrationAgencyName(),
|
||||
];
|
||||
|
||||
$this->agencyFields[$agency->getName()] = array_map(function ($field) {
|
||||
$field->groupId = self::AGENCY_SPECIFIC_SETTINGS;
|
||||
return $field;
|
||||
}, $agency->getSettingsObject()->getFields($context));
|
||||
});
|
||||
|
||||
$this->addGroup([
|
||||
'id' => self::GENERAL_SETTINGS,
|
||||
]);
|
||||
|
||||
$this->addGroup([
|
||||
'id' => self::AGENCY_SPECIFIC_SETTINGS,
|
||||
'showWhen' => Context::SETTING_CONFIGURED_REGISTRATION_AGENCY,
|
||||
]);
|
||||
|
||||
if (count($options) > 1) {
|
||||
$this->addField(new FieldSelect(Context::SETTING_CONFIGURED_REGISTRATION_AGENCY, [
|
||||
'label' => __('doi.manager.settings.registrationAgency'),
|
||||
'description' => __('doi.manager.settings.registrationAgency.description'),
|
||||
'options' => $options,
|
||||
'value' => $context->getData(Context::SETTING_CONFIGURED_REGISTRATION_AGENCY) === '' ?
|
||||
null :
|
||||
$context->getData(Context::SETTING_CONFIGURED_REGISTRATION_AGENCY),
|
||||
'groupId' => self::GENERAL_SETTINGS,
|
||||
]))
|
||||
->addField(new FieldOptions(Context::SETTING_DOI_AUTOMATIC_DEPOSIT, [
|
||||
'label' => __('doi.manager.setup.automaticDeposit'),
|
||||
'description' => __('doi.manager.setup.automaticDeposit.description'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('doi.manager.setup.automaticDeposit.enable')]
|
||||
],
|
||||
'value' => (bool) $context->getData(Context::SETTING_DOI_AUTOMATIC_DEPOSIT),
|
||||
'groupId' => self::GENERAL_SETTINGS,
|
||||
'showWhen' => Context::SETTING_CONFIGURED_REGISTRATION_AGENCY,
|
||||
]));
|
||||
} else {
|
||||
$this->addField(new FieldHTML('noPluginsEnabled', [
|
||||
'label' => __('doi.manager.settings.registrationAgency.noPluginsEnabled.label'),
|
||||
'description' => __('doi.manager.settings.registrationAgency.noPluginsEnabled.description'),
|
||||
'groupId' => self::GENERAL_SETTINGS,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
$activeAgencyField = array_filter($this->fields, function ($field) {
|
||||
return $field->name === Context::SETTING_CONFIGURED_REGISTRATION_AGENCY;
|
||||
});
|
||||
$activeAgency = empty($activeAgencyField) ? '' : $activeAgencyField[0]->value;
|
||||
if (!empty($this->agencyFields[$activeAgency])) {
|
||||
$this->fields = array_merge($this->fields, $this->agencyFields[$activeAgency]);
|
||||
}
|
||||
|
||||
$config = parent::getConfig();
|
||||
|
||||
// Set up field config for non-active fields
|
||||
$config['agencyFields'] = array_map(function ($agencyFields) {
|
||||
return array_map(function ($agencyField) {
|
||||
$field = $this->getFieldConfig($agencyField);
|
||||
$field['groupId'] = self::AGENCY_SPECIFIC_SETTINGS;
|
||||
return $field;
|
||||
}, $agencyFields);
|
||||
}, $this->agencyFields);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPDoiSetupSettingsForm.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 PKPDoiSetupSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for enabling and configuring DOI settings for a given context
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
|
||||
abstract class PKPDoiSetupSettingsForm extends FormComponent
|
||||
{
|
||||
public const FORM_DOI_SETUP_SETTINGS = 'doiSetupSettings';
|
||||
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = self::FORM_DOI_SETUP_SETTINGS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/** @var ?string Name of registration agency for checking allowed pub object types for DOI registration */
|
||||
public ?string $enabledRegistrationAgency = null;
|
||||
/** @var array Default list of all possible pubObject types for DOI registration */
|
||||
public array $objectTypeOptions = [];
|
||||
|
||||
protected const DOI_SETTINGS_GROUP = 'doiSettingsGroup';
|
||||
protected const DOI_DEFAULT_GROUP = 'doiDefaultGroup';
|
||||
protected const DOI_CUSTOM_SUFFIX_GROUP = 'doiCustomSuffixGroup';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct(string $action, array $locales, Context $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->enabledRegistrationAgency = $context->getConfiguredDoiAgency()?->getName();
|
||||
|
||||
$doiManagementUrl = Application::get()->getDispatcher()->url(
|
||||
Application::get()->getRequest(),
|
||||
Application::ROUTE_PAGE,
|
||||
$context->getPath(),
|
||||
'dois'
|
||||
);
|
||||
|
||||
$this->addGroup(
|
||||
[
|
||||
'id' => self::DOI_DEFAULT_GROUP,
|
||||
]
|
||||
)
|
||||
->addGroup(
|
||||
[
|
||||
'id' => self::DOI_SETTINGS_GROUP,
|
||||
'showWhen' => Context::SETTING_ENABLE_DOIS,
|
||||
]
|
||||
)
|
||||
->addGroup(
|
||||
[
|
||||
'id' => self::DOI_CUSTOM_SUFFIX_GROUP,
|
||||
'label' => __('doi.manager.settings.doiSuffix.custom'),
|
||||
'description' => __('doi.manager.settings.doiSuffixPattern'),
|
||||
'showWhen' => [Context::SETTING_DOI_SUFFIX_TYPE, Repo::doi()::SUFFIX_CUSTOM_PATTERN],
|
||||
]
|
||||
)
|
||||
->addField(new FieldOptions(Context::SETTING_ENABLE_DOIS, [
|
||||
'label' => __('manager.setup.dois'),
|
||||
'groupId' => self::DOI_DEFAULT_GROUP,
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.enableDois.description')]
|
||||
],
|
||||
'value' => (bool) $context->getData(Context::SETTING_ENABLE_DOIS),
|
||||
]))
|
||||
->addField(new FieldText(Context::SETTING_DOI_PREFIX, [
|
||||
'label' => __('doi.manager.settings.doiPrefix'),
|
||||
'description' => __('doi.manager.settings.doiPrefix.description'),
|
||||
'groupId' => self::DOI_SETTINGS_GROUP,
|
||||
'value' => $context->getData(Context::SETTING_DOI_PREFIX),
|
||||
'size' => 'small',
|
||||
]))
|
||||
->addField(new FieldSelect(Context::SETTING_DOI_CREATION_TIME, [
|
||||
'label' => __('doi.manager.settings.doiCreationTime.label'),
|
||||
'description' => __('doi.manager.settings.doiCreationTime.description'),
|
||||
'groupId' => self::DOI_SETTINGS_GROUP,
|
||||
'options' => [
|
||||
[
|
||||
'value' => Repo::doi()::CREATION_TIME_COPYEDIT,
|
||||
'label' => __('doi.manager.settings.doiCreationTime.copyedit')
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::CREATION_TIME_PUBLICATION,
|
||||
'label' => __('doi.manager.settings.doiCreationTime.publication')
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::CREATION_TIME_NEVER,
|
||||
'label' => __('doi.manager.settings.doiCreationTime.never')
|
||||
]
|
||||
],
|
||||
'value' => $context->getData(Context::SETTING_DOI_CREATION_TIME) ? $context->getData(Context::SETTING_DOI_CREATION_TIME) : Repo::doi()::CREATION_TIME_COPYEDIT,
|
||||
]))
|
||||
->addField(new FieldOptions(Context::SETTING_DOI_SUFFIX_TYPE, [
|
||||
'label' => __('doi.manager.settings.doiSuffix'),
|
||||
'description' => __('doi.manager.settings.doiSuffix.description'),
|
||||
'groupId' => self::DOI_SETTINGS_GROUP,
|
||||
'options' => [
|
||||
[
|
||||
'value' => Repo::doi()::SUFFIX_DEFAULT,
|
||||
'label' => __('doi.manager.settings.doiSuffixDefault')
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::SUFFIX_MANUAL,
|
||||
'label' => __('doi.manager.settings.doiSuffixManual', ['doiManagementUrl' => $doiManagementUrl])
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::SUFFIX_CUSTOM_PATTERN,
|
||||
'label' => __('doi.manager.settings.doiSuffixUserDefined')
|
||||
],
|
||||
],
|
||||
'value' => $context->getData(Context::SETTING_DOI_SUFFIX_TYPE) ? $context->getData(Context::SETTING_DOI_SUFFIX_TYPE) : Repo::doi()::SUFFIX_DEFAULT,
|
||||
'type' => 'radio',
|
||||
]))
|
||||
->addField(new FieldText(Repo::doi()::CUSTOM_PUBLICATION_PATTERN, [
|
||||
'label' => __('manager.language.submissions'),
|
||||
'groupId' => self::DOI_CUSTOM_SUFFIX_GROUP,
|
||||
'value' => $context->getData(Repo::doi()::CUSTOM_PUBLICATION_PATTERN),
|
||||
]))
|
||||
->addField(new FieldText(Repo::doi()::CUSTOM_REPRESENTATION_PATTERN, [
|
||||
'label' => __('doi.manager.settings.enableRepresentationDoi'),
|
||||
'groupId' => self::DOI_CUSTOM_SUFFIX_GROUP,
|
||||
'value' => $context->getData(Repo::doi()::CUSTOM_REPRESENTATION_PATTERN),
|
||||
]));
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['enabledRegistrationAgency'] = $this->enabledRegistrationAgency;
|
||||
$config['objectTypeOptions'] = $this->objectTypeOptions;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPEmailSetupForm.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 PKPEmailSetupForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's email settings.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\mail\variables\ContextEmailVariable;
|
||||
use Illuminate\Support\Arr;
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldPreparedContent;
|
||||
use PKP\components\forms\FieldRadioInput;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\config\Config;
|
||||
use PKP\context\Context;
|
||||
|
||||
class PKPEmailSetupForm extends FormComponent
|
||||
{
|
||||
public const GROUP_EMAIL_TEMPLATES = 'emailTemplates';
|
||||
public const GROUP_NEW_SUBMISSION = 'newSubmission';
|
||||
public const GROUP_EDITORIAL_DECISIONS = 'decisions';
|
||||
public const GROUP_EDITORS = 'editors';
|
||||
public const GROUP_ADVANCED = 'advanced';
|
||||
public const FIELD_SUBMISSION_ACK = 'submissionAcknowledgement';
|
||||
|
||||
public $id = 'emailSetup';
|
||||
public $method = 'PUT';
|
||||
public Context $context;
|
||||
|
||||
|
||||
public function __construct(string $action, array $locales, Context $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->context = $context;
|
||||
|
||||
$this->addGroup([
|
||||
'id' => self::GROUP_EMAIL_TEMPLATES,
|
||||
'label' => __('manager.manageEmails'),
|
||||
'description' => __('manager.manageEmails.description'),
|
||||
])
|
||||
->addEmailTemplatesField()
|
||||
->addSignatureField()
|
||||
->addGroup([
|
||||
'id' => self::GROUP_NEW_SUBMISSION,
|
||||
'label' => __('manager.newSubmission'),
|
||||
'description' => __('manager.newSubmission.description'),
|
||||
])
|
||||
->addSubmissionAcknowledgementField()
|
||||
->addCopySubmissionAckPrimaryContactField()
|
||||
->addCopySubmissionAckAddress()
|
||||
->addGroup([
|
||||
'id' => self::GROUP_EDITORIAL_DECISIONS,
|
||||
'label' => __('manager.editorialDecisions'),
|
||||
'description' => __('manager.editorialDecisions.description'),
|
||||
])
|
||||
->addNotifyAllAuthorsField()
|
||||
->addGroup([
|
||||
'id' => self::GROUP_EDITORS,
|
||||
'label' => __('manager.forEditors'),
|
||||
'description' => __('manager.forEditors.description')
|
||||
])
|
||||
->addStatisticsReportField()
|
||||
->addGroup([
|
||||
'id' => self::GROUP_ADVANCED,
|
||||
'label' => __('manager.setup.advanced'),
|
||||
])
|
||||
->addEnveloperSenderField();
|
||||
}
|
||||
|
||||
protected function addEmailTemplatesField(): self
|
||||
{
|
||||
$manageEmailsUrl = Application::get()->getRequest()->getDispatcher()->url(
|
||||
Application::get()->getRequest(),
|
||||
Application::ROUTE_PAGE,
|
||||
$this->context->getPath(),
|
||||
'management',
|
||||
'settings',
|
||||
'manageEmails'
|
||||
);
|
||||
return $this->addField(new FieldHTML('emailTemplates', [
|
||||
'label' => __('manager.emails.emailTemplates'),
|
||||
'description' => __('manager.manageEmailTemplates.description', ['url' => $manageEmailsUrl]),
|
||||
'groupId' => self::GROUP_EMAIL_TEMPLATES,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function addSignatureField(): self
|
||||
{
|
||||
return $this->addField(new FieldPreparedContent('emailSignature', [
|
||||
'label' => __('manager.setup.emailSignature'),
|
||||
'description' => __('manager.setup.emailSignature.description'),
|
||||
'value' => $this->context->getData('emailSignature'),
|
||||
'preparedContent' => array_values(
|
||||
Arr::sort(
|
||||
Arr::map(
|
||||
Arr::except(ContextEmailVariable::descriptions(), ContextEmailVariable::CONTEXT_SIGNATURE),
|
||||
function ($description, $key) {
|
||||
return [
|
||||
'key' => $key,
|
||||
'description' => $description,
|
||||
'value' => '{$' . $key . '}'
|
||||
];
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
'groupId' => self::GROUP_EMAIL_TEMPLATES,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the submission ack field
|
||||
*/
|
||||
protected function addSubmissionAcknowledgementField(): self
|
||||
{
|
||||
return $this->addField(new FieldOptions(self::FIELD_SUBMISSION_ACK, [
|
||||
'label' => __('mailable.submissionAck.name'),
|
||||
'description' => __('manager.submissionAck.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => Context::SUBMISSION_ACKNOWLEDGEMENT_ALL_AUTHORS, 'label' => __('manager.submissionAck.allAuthors')],
|
||||
['value' => Context::SUBMISSION_ACKNOWLEDGEMENT_SUBMITTING_AUTHOR, 'label' => __('manager.submissionAck.submittingAuthor')],
|
||||
['value' => Context::SUBMISSION_ACKNOWLEDGEMENT_OFF, 'label' => __('manager.submissionAck.off')],
|
||||
],
|
||||
'value' => $this->context->getData(self::FIELD_SUBMISSION_ACK),
|
||||
'groupId' => self::GROUP_NEW_SUBMISSION,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the copy submission ack primary contact field
|
||||
*/
|
||||
protected function addCopySubmissionAckPrimaryContactField(): self
|
||||
{
|
||||
$contactEmail = $this->context->getData('contactEmail');
|
||||
|
||||
if (!empty($contactEmail)) {
|
||||
return $this->addField(new FieldRadioInput('copySubmissionAckPrimaryContact', [
|
||||
'label' => __('manager.setup.notifications.copySubmissionAckPrimaryContact'),
|
||||
'description' => __('manager.setup.notifications.copySubmissionAckPrimaryContact.description'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.notifications.copySubmissionAckPrimaryContact.enabled', ['email' => $contactEmail])],
|
||||
['value' => false, 'label' => __('manager.setup.notifications.copySubmissionAckPrimaryContact.disabled')],
|
||||
],
|
||||
'value' => $this->context->getData('copySubmissionAckPrimaryContact'),
|
||||
'groupId' => self::GROUP_NEW_SUBMISSION,
|
||||
'showWhen' => self::FIELD_SUBMISSION_ACK,
|
||||
]));
|
||||
}
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
|
||||
$pageUrl = $request->getDispatcher()
|
||||
->url($request, Application::ROUTE_PAGE, null, 'management', 'settings', 'context', null, 'contact');
|
||||
|
||||
return $this->addField(new FieldHTML('copySubmissionAckPrimaryContact', [
|
||||
'label' => __('manager.setup.notifications.copySubmissionAckPrimaryContact'),
|
||||
'description' => __('manager.setup.notifications.copySubmissionAckPrimaryContact.disabled.description', ['url' => $pageUrl]),
|
||||
'groupId' => self::GROUP_NEW_SUBMISSION,
|
||||
'showWhen' => self::FIELD_SUBMISSION_ACK,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the field to copy any email address on the submission acknowledgement
|
||||
*/
|
||||
protected function addCopySubmissionAckAddress(): self
|
||||
{
|
||||
return $this->addField(new FieldText('copySubmissionAckAddress', [
|
||||
'label' => __('manager.setup.notifications.copySubmissionAckAddress'),
|
||||
'description' => __('manager.setup.notifications.copySubmissionAckAddress.description'),
|
||||
'size' => 'large',
|
||||
'value' => $this->context->getData('copySubmissionAckAddress'),
|
||||
'groupId' => self::GROUP_NEW_SUBMISSION,
|
||||
'showWhen' => self::FIELD_SUBMISSION_ACK,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the field to notify all authors when an editorial decision is recorded
|
||||
*/
|
||||
protected function addNotifyAllAuthorsField(): self
|
||||
{
|
||||
return $this->addField(new FieldOptions('notifyAllAuthors', [
|
||||
'label' => __('manager.setup.notifyAllAuthors'),
|
||||
'description' => __('manager.setup.notifyAllAuthors.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.notifyAllAuthors.allAuthors')],
|
||||
['value' => false, 'label' => __('manager.setup.notifyAllAuthors.assignedAuthors')],
|
||||
],
|
||||
'value' => $this->context->getData('notifyAllAuthors'),
|
||||
'groupId' => self::GROUP_EDITORIAL_DECISIONS,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the field to enable/disable the editorial statistics report email
|
||||
*/
|
||||
protected function addStatisticsReportField(): self
|
||||
{
|
||||
return $this->addField(new FieldOptions('editorialStatsEmail', [
|
||||
'label' => __('manager.editorialStatistics'),
|
||||
'description' => __('manager.editorialStatistics.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.editorialStatistics.on')],
|
||||
['value' => false, 'label' => __('manager.editorialStatistics.off')],
|
||||
],
|
||||
'value' => $this->context->getData('editorialStatsEmail'),
|
||||
'groupId' => self::GROUP_EDITORS,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function addEnveloperSenderField(): self
|
||||
{
|
||||
$canEnvelopeSender = Config::getVar('email', 'allow_envelope_sender');
|
||||
|
||||
if ($canEnvelopeSender) {
|
||||
return $this->addField(new FieldText('envelopeSender', [
|
||||
'label' => __('manager.setup.emailBounceAddress'),
|
||||
'tooltip' => __('manager.setup.emailBounceAddress.description'),
|
||||
'value' => $this->context->getData('envelopeSender'),
|
||||
'groupId' => self::GROUP_ADVANCED,
|
||||
]));
|
||||
}
|
||||
|
||||
return $this->addField(new FieldHTML('envelopeSender', [
|
||||
'label' => __('manager.setup.emailBounceAddress'),
|
||||
'description' => __('manager.setup.emailBounceAddress.disabled'),
|
||||
'groupId' => self::GROUP_ADVANCED,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPInformationForm.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 PKPInformationForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the information fields for a
|
||||
* context (eg - info for readers, authors and librarians).
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_INFORMATION', 'information');
|
||||
|
||||
class PKPInformationForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_INFORMATION;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $imageUploadUrl The API endpoint for images uploaded through the rich text field
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $imageUploadUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'descriptions',
|
||||
'label' => __('manager.setup.information.descriptionTitle'),
|
||||
'description' => __('manager.setup.information.description'),
|
||||
])
|
||||
->addField(new FieldRichTextarea('readerInformation', [
|
||||
'label' => __('manager.setup.information.forReaders'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'descriptions',
|
||||
'value' => $context->getData('readerInformation'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('authorInformation', [
|
||||
'label' => __('manager.setup.information.forAuthors'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'descriptions',
|
||||
'value' => $context->getData('authorInformation'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('librarianInformation', [
|
||||
'label' => __('manager.setup.information.forLibrarians'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'descriptions',
|
||||
'value' => $context->getData('librarianInformation'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPLicenseForm.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 PKPLicenseForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's default licensing details.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\components\forms\FieldRadioInput;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_LICENSE', 'license');
|
||||
|
||||
class PKPLicenseForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_LICENSE;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$licenseOptions = Application::getCCLicenseOptions();
|
||||
$licenseUrlOptions = [];
|
||||
foreach ($licenseOptions as $url => $label) {
|
||||
$licenseUrlOptions[] = [
|
||||
'value' => $url,
|
||||
'label' => __($label),
|
||||
];
|
||||
}
|
||||
$licenseUrlOptions[] = [
|
||||
'value' => 'other',
|
||||
'label' => __('manager.distribution.license.other'),
|
||||
'isInput' => true,
|
||||
];
|
||||
|
||||
$this->addField(new FieldRadioInput('copyrightHolderType', [
|
||||
'label' => __('submission.copyrightHolder'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => 'author', 'label' => __('user.role.author')],
|
||||
['value' => 'context', 'label' => __('context.context')],
|
||||
['value' => 'other', 'label' => __('submission.copyrightHolder.other')],
|
||||
],
|
||||
'value' => $context->getData('copyrightHolderType'),
|
||||
]))
|
||||
->addField(new FieldText('copyrightHolderOther', [
|
||||
'label' => __('submission.copyrightOther'),
|
||||
'description' => __('submission.copyrightOther.description'),
|
||||
'isMultilingual' => true,
|
||||
'showWhen' => ['copyrightHolderType', 'other'],
|
||||
'value' => $context->getData('copyrightHolderOther'),
|
||||
]))
|
||||
->addField(new FieldRadioInput('licenseUrl', [
|
||||
'label' => __('manager.distribution.license'),
|
||||
'type' => 'radio',
|
||||
'options' => $licenseUrlOptions,
|
||||
'value' => $context->getData('licenseUrl'),
|
||||
]))
|
||||
->addField(new FieldRichTextarea('licenseTerms', [
|
||||
'label' => __('manager.distribution.licenseTerms'),
|
||||
'tooltip' => __('manager.distribution.licenseTerms.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('licenseTerms'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist',
|
||||
'plugins' => 'paste,link,lists',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPListsForm.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 PKPListsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring how a context handles lists of
|
||||
* items in the UI.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_LISTS', 'lists');
|
||||
|
||||
class PKPListsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_LISTS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldText('itemsPerPage', [
|
||||
'label' => __('common.itemsPerPage'),
|
||||
'description' => __('manager.setup.itemsPerPage.description'),
|
||||
'isRequired' => true,
|
||||
'value' => $context->getData('itemsPerPage'),
|
||||
'size' => 'small',
|
||||
]))
|
||||
->addField(new FieldText('numPageLinks', [
|
||||
'label' => __('manager.setup.numPageLinks'),
|
||||
'description' => __('manager.setup.numPageLinks.description'),
|
||||
'isRequired' => true,
|
||||
'value' => $context->getData('numPageLinks'),
|
||||
'size' => 'small',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPMastheadForm.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 PKPMastheadForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's masthead details.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\facades\Locale;
|
||||
|
||||
define('FORM_MASTHEAD', 'masthead');
|
||||
|
||||
class PKPMastheadForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_MASTHEAD;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $imageUploadUrl The API endpoint for images uploaded through the rich text field
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $imageUploadUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$countries = [];
|
||||
foreach (Locale::getCountries() as $country) {
|
||||
$countries[] = [
|
||||
'value' => $country->getAlpha2(),
|
||||
'label' => $country->getLocalName()
|
||||
];
|
||||
}
|
||||
usort($countries, function ($a, $b) {
|
||||
return strcmp($a['label'], $b['label']);
|
||||
});
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'identity',
|
||||
'label' => __('manager.setup.identity'),
|
||||
])
|
||||
->addField(new FieldText('name', [
|
||||
'label' => __('manager.setup.contextTitle'),
|
||||
'size' => 'large',
|
||||
'isRequired' => true,
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'identity',
|
||||
'value' => $context->getData('name'),
|
||||
]))
|
||||
->addField(new FieldText('acronym', [
|
||||
'label' => __('manager.setup.contextInitials'),
|
||||
'size' => 'small',
|
||||
'isRequired' => true,
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'identity',
|
||||
'value' => $context->getData('acronym'),
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'publishing',
|
||||
'label' => __('manager.setup.publishing'),
|
||||
'description' => __('manager.setup.publishingDescription'),
|
||||
])
|
||||
->addField(new FieldSelect('country', [
|
||||
'groupId' => 'publishing',
|
||||
'label' => __('common.country'),
|
||||
'description' => __('manager.setup.selectCountry'),
|
||||
'options' => $countries,
|
||||
'isRequired' => true,
|
||||
'value' => $context ? $context->getData('country') : null,
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'keyInfo',
|
||||
'label' => __('manager.setup.keyInfo'),
|
||||
'description' => __('manager.setup.keyInfo.description'),
|
||||
])
|
||||
->addField(new FieldRichTextarea('description', [
|
||||
'label' => __('manager.setup.contextSummary'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'keyInfo',
|
||||
'value' => $context->getData('description'),
|
||||
]))
|
||||
->addField(new FieldRichTextarea('editorialTeam', [
|
||||
'label' => __('manager.setup.editorialTeam'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'keyInfo',
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
'value' => $context->getData('editorialTeam'),
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'about',
|
||||
'label' => __('common.description'),
|
||||
'description' => __('manager.setup.contextAbout.description'),
|
||||
])
|
||||
->addField(new FieldRichTextarea('about', [
|
||||
'label' => __('manager.setup.contextAbout'),
|
||||
'isMultilingual' => true,
|
||||
'size' => 'large',
|
||||
'groupId' => 'about',
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
'value' => $context->getData('about'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPMetadataSettingsForm.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 PKPMetadataSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for enabling and configuring types of metadata to
|
||||
* attach to submissions.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldMetadataSetting;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
|
||||
define('FORM_METADATA_SETTINGS', 'metadataSettings');
|
||||
|
||||
class PKPMetadataSettingsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_METADATA_SETTINGS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
|
||||
$this
|
||||
->addField(new FieldMetadataSetting('keywords', [
|
||||
'label' => __('common.keywords'),
|
||||
'description' => __('manager.setup.metadata.keywords.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.keywords.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.keywords.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.keywords.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.keywords.require')],
|
||||
],
|
||||
'value' => $context->getData('keywords') ? $context->getData('keywords') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('subjects', [
|
||||
'label' => __('common.subjects'),
|
||||
'description' => __('manager.setup.metadata.subjects.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.subjects.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.subjects.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.subjects.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.subjects.require')],
|
||||
],
|
||||
'value' => $context->getData('subjects') ? $context->getData('subjects') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('disciplines', [
|
||||
'label' => __('search.discipline'),
|
||||
'description' => __('manager.setup.metadata.disciplines.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.disciplines.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.disciplines.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.disciplines.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.disciplines.require')],
|
||||
],
|
||||
'value' => $context->getData('disciplines') ? $context->getData('disciplines') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('languages', [
|
||||
'label' => __('common.languages'),
|
||||
'description' => __('manager.setup.metadata.languages.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.languages.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.languages.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.languages.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.languages.require')],
|
||||
],
|
||||
'value' => $context->getData('languages') ? $context->getData('languages') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('agencies', [
|
||||
'label' => __('submission.supportingAgencies'),
|
||||
'description' => __('manager.setup.metadata.agencies.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.agencies.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.agencies.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.agencies.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.agencies.require')],
|
||||
],
|
||||
'value' => $context->getData('agencies') ? $context->getData('agencies') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('coverage', [
|
||||
'label' => __('manager.setup.metadata.coverage'),
|
||||
'description' => __('manager.setup.metadata.coverage.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.coverage.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.coverage.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.coverage.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.coverage.require')],
|
||||
],
|
||||
'value' => $context->getData('coverage') ? $context->getData('coverage') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('rights', [
|
||||
'label' => __('submission.rights'),
|
||||
'description' => __('manager.setup.metadata.rights.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.rights.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.rights.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.rights.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.rights.require')],
|
||||
],
|
||||
'value' => $context->getData('rights') ? $context->getData('rights') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('source', [
|
||||
'label' => __('submission.source'),
|
||||
'description' => __('manager.setup.metadata.source.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.source.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.source.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.source.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.source.require')],
|
||||
],
|
||||
'value' => $context->getData('source') ? $context->getData('source') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('type', [
|
||||
'label' => __('common.type'),
|
||||
'description' => __('manager.setup.metadata.type.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.type.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.type.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.type.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.type.require')],
|
||||
],
|
||||
'value' => $context->getData('type') ? $context->getData('type') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldOptions('requireAuthorCompetingInterests', [
|
||||
'label' => __('manager.setup.competingInterests'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => 'true',
|
||||
'label' => __('manager.setup.competingInterests.requireAuthors'),
|
||||
],
|
||||
],
|
||||
'value' => (bool) $context->getData('requireAuthorCompetingInterests'),
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('citations', [
|
||||
'label' => __('submission.citations'),
|
||||
'description' => __('manager.setup.metadata.citations.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.citations.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.citations.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.citations.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.citations.require')],
|
||||
],
|
||||
'value' => $context->getData('citations') ? $context->getData('citations') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldMetadataSetting('dataAvailability', [
|
||||
'label' => __('submission.dataAvailability'),
|
||||
'description' => __('manager.setup.metadata.dataAvailability.description'),
|
||||
'options' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.dataAvailability.enable')]
|
||||
],
|
||||
'submissionOptions' => [
|
||||
['value' => Context::METADATA_ENABLE, 'label' => __('manager.setup.metadata.dataAvailability.noRequest')],
|
||||
['value' => Context::METADATA_REQUEST, 'label' => __('manager.setup.metadata.dataAvailability.request')],
|
||||
['value' => Context::METADATA_REQUIRE, 'label' => __('manager.setup.metadata.dataAvailability.require')],
|
||||
],
|
||||
'value' => $context->getData('dataAvailability') ? $context->getData('dataAvailability') : Context::METADATA_DISABLE,
|
||||
]))
|
||||
->addField(new FieldOptions('submitWithCategories', [
|
||||
'label' => __('category.category'),
|
||||
'description' => __('manager.submitWithCategories.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.submitWithCategories.yes')],
|
||||
['value' => false, 'label' => __('manager.submitWithCategories.no')],
|
||||
],
|
||||
'value' => (bool) $context->getData('submitWithCategories')
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPNotifyUsersForm.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 PKPNotifyUsersForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for sending an email notification to users.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_NOTIFY_USERS', 'notifyUsers');
|
||||
|
||||
class PKPNotifyUsersForm extends FormComponent
|
||||
{
|
||||
public const FORM_NOTIFY_USERS = 'notifyUsers';
|
||||
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = self::FORM_NOTIFY_USERS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'POST';
|
||||
|
||||
/** @var array count of users in each group */
|
||||
public $userGroupCounts = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param \PKP\context\Context $context Journal, press or preprint server
|
||||
*/
|
||||
public function __construct($action, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
$userGroups = Repo::userGroup()->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getMany();
|
||||
|
||||
$userCountByGroupId = Repo::userGroup()->getUserCountByContextId($context->getId());
|
||||
|
||||
$userGroupOptions = [];
|
||||
foreach ($userGroups as $userGroup) {
|
||||
if (in_array($userGroup->getId(), (array) $context->getData('disableBulkEmailUserGroups'))) {
|
||||
continue;
|
||||
}
|
||||
$userGroupOptions[] = [
|
||||
'value' => $userGroup->getId(),
|
||||
'label' => $userGroup->getLocalizedData('name'),
|
||||
];
|
||||
$this->userGroupCounts[$userGroup->getId()] = $userCountByGroupId->get($userGroup->getId(), 0);
|
||||
}
|
||||
|
||||
$currentUser = Application::get()->getRequest()->getUser();
|
||||
|
||||
$this->addField(new FieldOptions('userGroupIds', [
|
||||
'label' => __('user.roles'),
|
||||
'description' => __('manager.setup.notifyUsers.description'),
|
||||
'value' => [],
|
||||
'options' => $userGroupOptions,
|
||||
'required' => true,
|
||||
]))
|
||||
->addField(new FieldText('subject', [
|
||||
'label' => __('email.subject'),
|
||||
'value' => '',
|
||||
'required' => true,
|
||||
]))
|
||||
->addField(new FieldRichTextarea('body', [
|
||||
'label' => __('email.email'),
|
||||
'size' => 'large',
|
||||
'value' => '',
|
||||
'required' => true,
|
||||
]))
|
||||
->addField(new FieldOptions('copy', [
|
||||
'label' => __('common.copy'),
|
||||
'value' => 0,
|
||||
'options' => [
|
||||
[
|
||||
'value' => 1,
|
||||
'label' => __('manager.setup.notifyUsers.copyDetails', ['email' => $currentUser->getEmail()]),
|
||||
],
|
||||
]
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc FormComponent::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['confirmLabel'] = __('manager.setup.notifyUsers.confirm');
|
||||
$config['sendLabel'] = __('manager.setup.notifyUsers.send');
|
||||
$config['userGroupCounts'] = $this->userGroupCounts;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPPaymentSettingsForm.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 PKPPaymentSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the general payment settings.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
|
||||
define('FORM_PAYMENT_SETTINGS', 'paymentSettings');
|
||||
|
||||
class PKPPaymentSettingsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_PAYMENT_SETTINGS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$currencies = [];
|
||||
foreach (Locale::getCurrencies() as $currency) {
|
||||
$currencies[] = [
|
||||
'value' => $currency->getLetterCode(),
|
||||
'label' => htmlspecialchars($currency->getLocalName()),
|
||||
];
|
||||
}
|
||||
|
||||
// Ensure payment method plugins can hook in
|
||||
$paymentPlugins = PluginRegistry::loadCategory('paymethod', true);
|
||||
$pluginList = [];
|
||||
foreach ($paymentPlugins as $plugin) {
|
||||
$pluginList[] = [
|
||||
'value' => $plugin->getName(),
|
||||
'label' => htmlspecialchars($plugin->getDisplayName()),
|
||||
];
|
||||
}
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'setup',
|
||||
'label' => __('navigation.setup'),
|
||||
])
|
||||
->addField(new FieldOptions('paymentsEnabled', [
|
||||
'label' => __('common.enable'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.payment.options.enablePayments')]
|
||||
],
|
||||
'value' => (bool) $context->getData('paymentsEnabled'),
|
||||
'groupId' => 'setup',
|
||||
]))
|
||||
->addField(new FieldSelect('currency', [
|
||||
'label' => __('manager.paymentMethod.currency'),
|
||||
'options' => $currencies,
|
||||
'showWhen' => 'paymentsEnabled',
|
||||
'value' => $context->getData('currency'),
|
||||
'groupId' => 'setup',
|
||||
]))
|
||||
->addField(new FieldSelect('paymentPluginName', [
|
||||
'label' => __('plugins.categories.paymethod'),
|
||||
'options' => $pluginList,
|
||||
'showWhen' => 'paymentsEnabled',
|
||||
'value' => $context->getData('paymentPluginName'),
|
||||
'groupId' => 'setup',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPPrivacyForm.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 PKPPrivacyForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's privacy statement.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_PRIVACY', 'privacy');
|
||||
|
||||
class PKPPrivacyForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_PRIVACY;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $imageUploadUrl The API endpoint for images uploaded through the rich text field
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $imageUploadUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldRichTextArea('privacyStatement', [
|
||||
'label' => __('manager.setup.privacyStatement'),
|
||||
'description' => __('manager.setup.privacyStatement.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('privacyStatement'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist | image | code',
|
||||
'plugins' => 'paste,link,lists,image,code',
|
||||
'uploadUrl' => $imageUploadUrl,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/site/PKPRestrictBulkEmailsForm.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 PKPRestrictBulkEmailsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for setting restrictions on the sending of bulk emails in a context.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_RESTRICT_BULK_EMAILS', 'restrictBulkEmails');
|
||||
|
||||
class PKPRestrictBulkEmailsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_RESTRICT_BULK_EMAILS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
*/
|
||||
public function __construct($action, $context, LazyCollection $userGroups)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
$userGroupOptions = [];
|
||||
foreach ($userGroups as $userGroup) {
|
||||
$userGroupOptions[] = [
|
||||
'value' => $userGroup->getId(),
|
||||
'label' => htmlspecialchars($userGroup->getLocalizedData('name')),
|
||||
];
|
||||
}
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
$siteSettingsUrl = $request->getDispatcher()->url($request, Application::ROUTE_PAGE, null, 'admin', 'settings', null, null, 'setup/bulkEmails');
|
||||
|
||||
$this->addField(new FieldOptions('disableBulkEmailUserGroups', [
|
||||
'label' => __('admin.settings.disableBulkEmailRoles.label'),
|
||||
'description' => __('admin.settings.disableBulkEmailRoles.description', ['siteSettingsUrl' => $siteSettingsUrl]),
|
||||
'value' => (array) $context->getData('disableBulkEmailUserGroups'),
|
||||
'options' => $userGroupOptions,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPReviewGuidanceForm.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 PKPReviewGuidanceForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the guidance a reviewer should receive.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldShowEnsuringLink;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_REVIEW_GUIDANCE', 'reviewerGuidance');
|
||||
|
||||
class PKPReviewGuidanceForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_REVIEW_GUIDANCE;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldRichTextarea('reviewGuidelines', [
|
||||
'label' => __('manager.setup.reviewGuidelines'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('reviewGuidelines'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist',
|
||||
'plugins' => 'paste,link,lists',
|
||||
]))
|
||||
->addField(new FieldRichTextarea('competingInterests', [
|
||||
'label' => __('manager.setup.competingInterests'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('competingInterests'),
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist',
|
||||
'plugins' => 'paste,link,lists',
|
||||
]))
|
||||
->addField(new FieldShowEnsuringLink('showEnsuringLink', [
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.reviewOptions.showAnonymousReviewLink')],
|
||||
],
|
||||
'value' => $context->getData('showEnsuringLink'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPReviewSetupForm.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 PKPReviewSetupForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring review options, such as the default
|
||||
* review type and deadlines.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\config\Config;
|
||||
use PKP\submission\reviewAssignment\ReviewAssignment;
|
||||
|
||||
define('FORM_REVIEW_SETUP', 'reviewSetup');
|
||||
|
||||
class PKPReviewSetupForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_REVIEW_SETUP;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldOptions('defaultReviewMode', [
|
||||
'label' => __('manager.setup.reviewOptions.reviewMode'),
|
||||
'type' => 'radio',
|
||||
'value' => $context->getData('defaultReviewMode'),
|
||||
'options' => [
|
||||
['value' => ReviewAssignment::SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS, 'label' => __('editor.submissionReview.doubleAnonymous')],
|
||||
['value' => ReviewAssignment::SUBMISSION_REVIEW_METHOD_ANONYMOUS, 'label' => __('editor.submissionReview.anonymous')],
|
||||
['value' => ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN, 'label' => __('editor.submissionReview.open')],
|
||||
],
|
||||
]))
|
||||
->addField(new FieldOptions('restrictReviewerFileAccess', [
|
||||
'label' => __('manager.setup.reviewOptions.restrictReviewerFileAccess'),
|
||||
'type' => 'checkbox',
|
||||
'value' => $context->getData('restrictReviewerFileAccess'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.reviewOptions.restrictReviewerFileAccess.description')],
|
||||
]
|
||||
]))
|
||||
->addField(new FieldOptions('reviewerAccessKeysEnabled', [
|
||||
'label' => __('manager.setup.reviewOptions.reviewerAccessKeysEnabled'),
|
||||
'description' => __('manager.setup.reviewOptions.reviewerAccessKeysEnabled.description'),
|
||||
'type' => 'checkbox',
|
||||
'value' => $context->getData('reviewerAccessKeysEnabled'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.reviewOptions.reviewerAccessKeysEnabled.label')],
|
||||
]
|
||||
]))
|
||||
->addField(new FieldText('numWeeksPerResponse', [
|
||||
'label' => __('manager.setup.reviewOptions.defaultReviewResponseTime'),
|
||||
'description' => __('manager.setup.reviewOptions.numWeeksPerResponse'),
|
||||
'value' => $context->getData('numWeeksPerResponse'),
|
||||
'size' => 'small',
|
||||
]))
|
||||
->addField(new FieldText('numWeeksPerReview', [
|
||||
'label' => __('manager.setup.reviewOptions.defaultReviewCompletionTime'),
|
||||
'description' => __('manager.setup.reviewOptions.numWeeksPerReview'),
|
||||
'value' => $context->getData('numWeeksPerReview'),
|
||||
'size' => 'small',
|
||||
]));
|
||||
|
||||
if (Config::getVar('general', 'scheduled_tasks')) {
|
||||
$this->addField(new FieldText('numDaysBeforeInviteReminder', [
|
||||
'label' => __('manager.setup.reviewOptions.reminders.response'),
|
||||
'description' => __('manager.setup.reviewOptions.reminders.response.description'),
|
||||
'value' => $context->getData('numDaysBeforeInviteReminder'),
|
||||
'size' => 'small',
|
||||
]))
|
||||
->addField(new FieldText('numDaysBeforeSubmitReminder', [
|
||||
'label' => __('manager.setup.reviewOptions.reminders.submit'),
|
||||
'description' => __('manager.setup.reviewOptions.reminders.submit.description'),
|
||||
'value' => $context->getData('numDaysBeforeSubmitReminder'),
|
||||
'size' => 'small',
|
||||
]));
|
||||
} else {
|
||||
$this->addField(new FieldHTML('reviewRemindersDisabled', [
|
||||
'label' => __('manager.setup.reviewOptions.automatedReminders'),
|
||||
'description' => __('manager.setup.reviewOptions.automatedRemindersDisabled'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPSearchIndexingForm.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 PKPSearchIndexingForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring a context's search indexing settings.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_SEARCH_INDEXING', 'searchIndexing');
|
||||
|
||||
class PKPSearchIndexingForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_SEARCH_INDEXING;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
* @param string $sitemapUrl A URL to the context's sitemap for use in the
|
||||
* search engine indexing group description
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $sitemapUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'search',
|
||||
'label' => __('manager.setup.searchEngineIndexing'),
|
||||
'description' => __('manager.setup.searchEngineIndexing.description', ['sitemapUrl' => $sitemapUrl]),
|
||||
])
|
||||
->addField(new FieldText('searchDescription', [
|
||||
'label' => __('common.description'),
|
||||
'tooltip' => __('manager.setup.searchDescription.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('searchDescription'),
|
||||
'groupId' => 'search',
|
||||
]))
|
||||
->addField(new FieldTextArea('customHeaders', [
|
||||
'label' => __('manager.distribution.customHeaders'),
|
||||
'tooltip' => __('manager.distribution.customHeaders.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('customHeaders'),
|
||||
'groupId' => 'search',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPThemeForm.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 PKPThemeForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for selecting a theme and theme options. Expects to be attached
|
||||
* to a <theme-form> element in the UI.
|
||||
*
|
||||
* This form works similarly to other form components, except that it keeps a
|
||||
* separate store of fields for each theme's options. Only the active theme's
|
||||
* fields are loaded into $this->fields. The <theme-form> UI component chooses
|
||||
* which fields to display as the theme selection is changed.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\plugins\ThemePlugin;
|
||||
|
||||
define('FORM_THEME', 'theme');
|
||||
|
||||
class PKPThemeForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_THEME;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/** @var array A key/value store of theme option fields, keyed by theme name */
|
||||
public $themeFields = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \PKP\context\Context|null $context Journal/Press to change settings for, or null
|
||||
* to change settings for the Site
|
||||
*/
|
||||
public function __construct($action, $locales, $context = null)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
if (!empty($context)) {
|
||||
$activeTheme = $context->getData('themePluginPath');
|
||||
$contextId = $context->getId();
|
||||
} else {
|
||||
$activeTheme = Application::get()->getRequest()->getSite()->getData('themePluginPath');
|
||||
$contextId = \PKP\core\PKPApplication::CONTEXT_ID_NONE;
|
||||
}
|
||||
|
||||
$themes = $themeOptions = [];
|
||||
/** @var ThemePlugin[] */
|
||||
$plugins = PluginRegistry::loadCategory('themes', true);
|
||||
foreach ($plugins as $plugin) {
|
||||
$themes[] = [
|
||||
'value' => $plugin->getDirName(),
|
||||
'label' => htmlspecialchars($plugin->getDisplayName()),
|
||||
];
|
||||
}
|
||||
|
||||
$this->addField(new FieldSelect('themePluginPath', [
|
||||
'label' => __('manager.setup.theme'),
|
||||
'description' => __('manager.setup.theme.description'),
|
||||
'options' => $themes,
|
||||
'value' => $activeTheme,
|
||||
]));
|
||||
|
||||
// Add theme options for each theme
|
||||
foreach ($plugins as $plugin) {
|
||||
// Re-run the init functions for each theme so that any theme options
|
||||
// are set up. Because this is run after PluginRegistry::loadCategory(),
|
||||
// the scripts and styles won't actually be registered against the
|
||||
// template manager. However, if PluginRegistry::loadCategory() is called
|
||||
// again for the themes category, it can cause scripts and styles to be
|
||||
// overwritten by inactive themes.
|
||||
$plugin->init();
|
||||
$themeOptions = $plugin->getOptionsConfig();
|
||||
if (empty($themeOptions)) {
|
||||
continue;
|
||||
}
|
||||
$themeOptionValues = $plugin->getOptionValues($contextId);
|
||||
foreach ($themeOptions as $optionName => $optionField) {
|
||||
$optionField->value = $themeOptionValues[$optionName] ?? null;
|
||||
$this->addThemeField($plugin->getDirName(), $optionField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a form field that should only appear when a particular theme is
|
||||
* selected
|
||||
*
|
||||
* @param string $theme The theme's base plugin path
|
||||
* @param \PKP\components\forms\Field $field
|
||||
* @param array $position [
|
||||
*
|
||||
* @option string One of `before` or `after`
|
||||
* @option string The field to position it before or after
|
||||
* ]
|
||||
*
|
||||
* @return FormComponent
|
||||
*/
|
||||
public function addThemeField($theme, $field, $position = [])
|
||||
{
|
||||
if (empty($position)) {
|
||||
if (!isset($this->themeFields[$theme])) {
|
||||
$this->themeFields[$theme] = [];
|
||||
}
|
||||
$this->themeFields[$theme][] = $field;
|
||||
} else {
|
||||
$this->themeFields[$theme] = $this->addToPosition($position[1], $this->themeFields[$theme], $field, $position[0]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc FormComponent::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
// Add the active theme's option fields to the fields array
|
||||
$activeThemeField = array_filter($this->fields, function ($field) {
|
||||
return $field->name === 'themePluginPath';
|
||||
});
|
||||
$activeTheme = $activeThemeField[0]->value;
|
||||
if (!empty($this->themeFields[$activeTheme])) {
|
||||
$this->fields = array_merge($this->fields, $this->themeFields[$activeTheme]);
|
||||
}
|
||||
|
||||
$config = parent::getConfig();
|
||||
|
||||
// Set up field config for non-active fields
|
||||
if (!$this->groups) {
|
||||
$this->addGroup(['id' => 'default']);
|
||||
$this->fields = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $this->fields);
|
||||
}
|
||||
$defaultGroupId = $this->groups[0]['id'];
|
||||
$config['themeFields'] = array_map(function ($themeOptions) use ($defaultGroupId) {
|
||||
return array_map(function ($themeOption) use ($defaultGroupId) {
|
||||
$field = $this->getFieldConfig($themeOption);
|
||||
$field['groupId'] = $defaultGroupId;
|
||||
return $field;
|
||||
}, $themeOptions);
|
||||
}, $this->themeFields);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPUserAccessForm.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 PKPUserAccessForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the user access settings on the Users
|
||||
* and Roles page of a context.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_USER_ACCESS', 'userAccess');
|
||||
|
||||
class PKPUserAccessForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_USER_ACCESS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param \PKP\context\Context $context Journal or Press to change settings for
|
||||
*/
|
||||
public function __construct($action, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
$this->addField(new FieldOptions('restrictSiteAccess', [
|
||||
'label' => __('manager.setup.siteAccess.view'),
|
||||
'value' => (bool) $context->getData('restrictSiteAccess'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.restrictSiteAccess')],
|
||||
],
|
||||
]))
|
||||
->addField(new FieldOptions('disableUserReg', [
|
||||
'type' => 'radio',
|
||||
'label' => __('manager.setup.userRegistration'),
|
||||
'value' => (bool) $context->getData('disableUserReg'),
|
||||
'options' => [
|
||||
['value' => false, 'label' => __('manager.setup.enableUserRegistration')],
|
||||
['value' => true, 'label' => __('manager.setup.disableUserRegistration')],
|
||||
],
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/counter/PKPCounterReportForm.php
|
||||
*
|
||||
* Copyright (c) 2024 Simon Fraser University
|
||||
* Copyright (c) 2024 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPCounterReportForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for setting a COUNTER R5 report
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\counter;
|
||||
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_COUNTER', 'counter');
|
||||
|
||||
abstract class PKPCounterReportForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_COUNTER;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'GET';
|
||||
|
||||
/** Form fields for each COUNTER R5 report */
|
||||
public $reportFields = [];
|
||||
|
||||
/** Set reportFields, that will contain form fields for each COUNTER R5 report */
|
||||
abstract public function setReportFields(): void;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
*/
|
||||
public function __construct(string $action, array $locales)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addPage(['id' => 'default', 'submitButton' => ['label' => __('common.download')]]);
|
||||
$this->addGroup(['id' => 'default', 'pageId' => 'default']);
|
||||
|
||||
$this->setReportFields();
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['reportFields'] = array_map(function ($reportFields) {
|
||||
return array_map(function ($reportField) {
|
||||
$field = $this->getFieldConfig($reportField);
|
||||
$field['groupId'] = 'default';
|
||||
return $field;
|
||||
}, $reportFields);
|
||||
}, $this->reportFields);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/decision/SelectRevisionDecisionForm.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class SelectRevisionDecisionForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for selecting between revisions or resubmit for review.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\decision;
|
||||
|
||||
use APP\decision\Decision;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_SELECT_REVISION_DECISION', 'selectRevisionDecision');
|
||||
|
||||
class SelectRevisionDecisionForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_SELECT_REVISION_DECISION;
|
||||
|
||||
/** @copydoc FormComponent::$action */
|
||||
public $action = FormComponent::ACTION_EMIT;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->addField(new FieldOptions('decision', [
|
||||
'label' => __('editor.review.newReviewRound'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
[
|
||||
'value' => Decision::PENDING_REVISIONS,
|
||||
'label' => __('editor.review.NotifyAuthorRevisions'),
|
||||
],
|
||||
[
|
||||
'value' => Decision::RESUBMIT,
|
||||
'label' => __('editor.review.NotifyAuthorResubmit'),
|
||||
],
|
||||
],
|
||||
'value' => Decision::PENDING_REVISIONS,
|
||||
'groupId' => 'default',
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'default',
|
||||
'pageId' => 'default',
|
||||
])
|
||||
->addPage([
|
||||
'id' => 'default',
|
||||
'submitButton' => ['label' => __('help.next')]
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/decision/SelectRevisionRecommendationForm.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2000-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class SelectRevisionRecommendationForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for selecting between revisions or resubmit for review.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\decision;
|
||||
|
||||
use APP\decision\Decision;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_SELECT_REVISION_RECOMMENDATION', 'selectRevisionRecommendation');
|
||||
|
||||
class SelectRevisionRecommendationForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_SELECT_REVISION_RECOMMENDATION;
|
||||
|
||||
/** @copydoc FormComponent::$action */
|
||||
public $action = FormComponent::ACTION_EMIT;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->addField(new FieldOptions('decision', [
|
||||
'label' => __('editor.review.newReviewRound'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
[
|
||||
'value' => Decision::RECOMMEND_PENDING_REVISIONS,
|
||||
'label' => __('editor.review.NotifyAuthorRevisions.recommendation'),
|
||||
],
|
||||
[
|
||||
'value' => Decision::RECOMMEND_RESUBMIT,
|
||||
'label' => __('editor.review.NotifyAuthorResubmit.recommendation'),
|
||||
],
|
||||
],
|
||||
'value' => Decision::RECOMMEND_PENDING_REVISIONS,
|
||||
'groupId' => 'default',
|
||||
]))
|
||||
->addGroup([
|
||||
'id' => 'default',
|
||||
'pageId' => 'default',
|
||||
])
|
||||
->addPage([
|
||||
'id' => 'default',
|
||||
'submitButton' => ['label' => __('help.next')]
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/PKPEmailTemplateForm.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 EmailTemplateForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for adding and editing email templates.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\emailTemplate;
|
||||
|
||||
use PKP\components\forms\FieldPreparedContent;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_EMAIL_TEMPLATE', 'editEmailTemplate');
|
||||
|
||||
class EmailTemplateForm extends FormComponent
|
||||
{
|
||||
public $id = FORM_EMAIL_TEMPLATE;
|
||||
|
||||
public function __construct(string $action, array $locales)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->method = 'POST';
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldText('name', [
|
||||
'label' => __('common.name'),
|
||||
'description' => __('manager.emailTemplate.name.description'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldText('subject', [
|
||||
'label' => __('email.subject'),
|
||||
'isMultilingual' => true,
|
||||
'size' => 'large',
|
||||
]))
|
||||
->addField(new FieldPreparedContent('body', [
|
||||
'label' => __('email.body'),
|
||||
'size' => 'large',
|
||||
'isMultilingual' => true,
|
||||
'toolbar' => 'bold italic superscript subscript | link | blockquote bullist numlist',
|
||||
'plugins' => 'paste,link,lists',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/highlight/HighlightForm.php
|
||||
*
|
||||
* Copyright (c) 2023 Simon Fraser University
|
||||
* Copyright (c) 2023 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class HighlightForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for adding or editing a highlight
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\highlight;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\components\forms\FieldRichText;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
|
||||
define('FORM_HIGHLIGHT', 'highlight');
|
||||
|
||||
class HighlightForm extends FormComponent
|
||||
{
|
||||
public $id = FORM_HIGHLIGHT;
|
||||
public $method = 'POST';
|
||||
public ?Context $context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
*/
|
||||
public function __construct(string $action, string $baseUrl, string $temporaryFileApiUrl, ?Context $context = null)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->context = $context;
|
||||
$this->locales = $this->getLocales($context);
|
||||
|
||||
$this->addField(new FieldRichText('title', [
|
||||
'label' => __('common.title'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldRichText('description', [
|
||||
'label' => __('common.description'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldText('url', [
|
||||
'label' => __('common.url'),
|
||||
'description' => __('manager.highlights.url.description'),
|
||||
'size' => 'large',
|
||||
]))
|
||||
->addField(new FieldText('urlText', [
|
||||
'label' => __('manager.highlights.urlText'),
|
||||
'description' => __('manager.highlights.urlText.description'),
|
||||
'size' => 'small',
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldUploadImage('image', [
|
||||
'label' => __('manager.highlights.image'),
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locales formatted for display in the form
|
||||
*/
|
||||
protected function getLocales(?Context $context = null): array
|
||||
{
|
||||
$localeNames = $this?->context?->getSupportedFormLocaleNames()
|
||||
?? Application::get()->getRequest()->getSite()->getSupportedLocaleNames();
|
||||
|
||||
return array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($localeNames), $localeNames);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/institution/PKPInstitutionForm.php
|
||||
*
|
||||
* Copyright (c) 2022 Simon Fraser University
|
||||
* Copyright (c) 2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPInstitutionForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for creating a new institution
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\institution;
|
||||
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_INSTITUTION', 'institution');
|
||||
|
||||
class PKPInstitutionForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_INSTITUTION;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'POST';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
*/
|
||||
public function __construct(string $action, array $locales)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldText('name', [
|
||||
'label' => __('common.name'),
|
||||
'size' => 'large',
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldTextarea('ipRanges', [
|
||||
'label' => __('manager.institutions.form.ipRanges'),
|
||||
'description' => __('manager.institutions.form.ipRangesInstructions'),
|
||||
]))
|
||||
->addField(new FieldText('ror', [
|
||||
'label' => __('manager.institutions.form.ror'),
|
||||
'description' => __('manager.institutions.form.ror.description'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/ContributorForm.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 ContributorForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for adding and editing a contributor for a publication.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\publication;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\submission\Submission;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
use PKP\security\Role;
|
||||
use PKP\userGroup\UserGroup;
|
||||
use Sokil\IsoCodes\IsoCodesFactory;
|
||||
|
||||
define('FORM_CONTRIBUTOR', 'contributor');
|
||||
|
||||
class ContributorForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_CONTRIBUTOR;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'POST';
|
||||
|
||||
public Submission $submission;
|
||||
public Context $context;
|
||||
|
||||
public function __construct(string $action, array $locales, Submission $submission, Context $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->submission = $submission;
|
||||
$this->context = $context;
|
||||
|
||||
$authorUserGroupsOptions = Repo::userGroup()
|
||||
->getCollector()
|
||||
->filterByRoleIds([Role::ROLE_ID_AUTHOR])
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getMany()
|
||||
->map(fn (UserGroup $authorUserGroup) => [
|
||||
'value' => (int) $authorUserGroup->getId(),
|
||||
'label' => $authorUserGroup->getLocalizedName(),
|
||||
]);
|
||||
|
||||
$isoCodes = app(IsoCodesFactory::class);
|
||||
$countries = [];
|
||||
foreach ($isoCodes->getCountries() as $country) {
|
||||
$countries[] = [
|
||||
'value' => $country->getAlpha2(),
|
||||
'label' => $country->getLocalName()
|
||||
];
|
||||
}
|
||||
usort($countries, function ($a, $b) {
|
||||
return strcmp($a['label'], $b['label']);
|
||||
});
|
||||
|
||||
$this->addField(new FieldText('givenName', [
|
||||
'label' => __('user.givenName'),
|
||||
'isMultilingual' => true,
|
||||
'isRequired' => true
|
||||
]))
|
||||
->addField(new FieldText('familyName', [
|
||||
'label' => __('user.familyName'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldText('preferredPublicName', [
|
||||
'label' => __('user.preferredPublicName'),
|
||||
'description' => __('user.preferredPublicName.description'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldText('email', [
|
||||
'label' => __('user.email'),
|
||||
'isRequired' => true,
|
||||
]))
|
||||
->addField(new FieldSelect('country', [
|
||||
'label' => __('common.country'),
|
||||
'options' => $countries,
|
||||
'isRequired' => true,
|
||||
]))
|
||||
->addField(new FieldText('url', [
|
||||
'label' => __('user.url'),
|
||||
]))
|
||||
->addField(new FieldText('orcid', [
|
||||
'label' => __('user.orcid'),
|
||||
]));
|
||||
if ($context->getSetting('requireAuthorCompetingInterests')) $this->addField(new FieldRichTextarea('competingInterests', [
|
||||
'label' => __('author.competingInterests'),
|
||||
'description' => __('author.competingInterests.description'),
|
||||
'isMultilingual' => true,
|
||||
]));
|
||||
$this->addField(new FieldRichTextarea('biography', [
|
||||
'label' => __('user.biography'),
|
||||
'isMultilingual' => true,
|
||||
]))
|
||||
->addField(new FieldText('affiliation', [
|
||||
'label' => __('user.affiliation'),
|
||||
'isMultilingual' => true,
|
||||
]));
|
||||
|
||||
if ($authorUserGroupsOptions->count() > 1) {
|
||||
$this->addField(new FieldOptions('userGroupId', [
|
||||
'label' => __('submission.submit.contributorRole'),
|
||||
'type' => 'radio',
|
||||
'value' => $authorUserGroupsOptions->first()['value'],
|
||||
'options' => $authorUserGroupsOptions->values(),
|
||||
]));
|
||||
} else {
|
||||
$this->addHiddenField('userGroupId', $authorUserGroupsOptions->first()['value']);
|
||||
}
|
||||
|
||||
$this->addField(new FieldOptions('includeInBrowse', [
|
||||
'label' => __('submission.submit.includeInBrowse.title'),
|
||||
'type' => 'checkbox',
|
||||
'value' => true,
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('submission.submit.includeInBrowse')],
|
||||
]
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/Details.php
|
||||
*
|
||||
* Copyright (c) 2014-2023 Simon Fraser University
|
||||
* Copyright (c) 2000-2023 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Details
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief The Details form in the submission wizard.
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\publication;
|
||||
|
||||
use APP\publication\Publication;
|
||||
use PKP\components\forms\FieldControlledVocab;
|
||||
use PKP\context\Context;
|
||||
use PKP\submission\SubmissionKeywordDAO;
|
||||
|
||||
class Details extends TitleAbstractForm
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $suggestionUrlBase The base URL to get suggestions for controlled vocab.
|
||||
*/
|
||||
public function __construct(
|
||||
string $action,
|
||||
array $locales,
|
||||
Publication $publication,
|
||||
public Context $context,
|
||||
public string $suggestionUrlBase,
|
||||
int $abstractWordLimit = 0,
|
||||
bool $isAbstractRequired = false
|
||||
) {
|
||||
parent::__construct($action, $locales, $publication, $abstractWordLimit, $isAbstractRequired);
|
||||
|
||||
$this->removeField('prefix');
|
||||
$this->removeField('subtitle');
|
||||
|
||||
if (in_array($context->getData('keywords'), [Context::METADATA_REQUEST, Context::METADATA_REQUIRE])) {
|
||||
$this->addField(new FieldControlledVocab('keywords', [
|
||||
'label' => __('common.keywords'),
|
||||
'description' => __('manager.setup.metadata.keywords.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionKeywordDAO::CONTROLLED_VOCAB_SUBMISSION_KEYWORD, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('keywords'),
|
||||
'isRequired' => $context->getData('keywords') === Context::METADATA_REQUIRE ? true : false,
|
||||
]), [FIELD_POSITION_AFTER, 'title']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/PKPCitationsForm.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 PKPCitationsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for setting a publication's citations
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\publication;
|
||||
|
||||
use APP\publication\Publication;
|
||||
use PKP\components\forms\FieldTextarea;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_CITATIONS', 'citations');
|
||||
|
||||
class PKPCitationsForm extends FormComponent
|
||||
{
|
||||
public $id = FORM_CITATIONS;
|
||||
public $method = 'PUT';
|
||||
public bool $isRequired;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
*/
|
||||
public function __construct(string $action, Publication $publication, bool $isRequired = false)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->isRequired = $isRequired;
|
||||
|
||||
$this->addField(new FieldTextarea('citationsRaw', [
|
||||
'label' => __('submission.citations'),
|
||||
'description' => __('submission.citations.description'),
|
||||
'value' => $publication->getData('citationsRaw'),
|
||||
'isRequired' => $isRequired
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/PKPMetadataForm.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 PKPMetadataForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for setting a publication's metadata fields
|
||||
*/
|
||||
|
||||
namespace PKP\components\forms\publication;
|
||||
|
||||
use APP\publication\Publication;
|
||||
use PKP\components\forms\FieldControlledVocab;
|
||||
use PKP\components\forms\FieldRichTextarea;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
use PKP\submission\SubmissionAgencyDAO;
|
||||
use PKP\submission\SubmissionDisciplineDAO;
|
||||
use PKP\submission\SubmissionKeywordDAO;
|
||||
use PKP\submission\SubmissionLanguageDAO;
|
||||
use PKP\submission\SubmissionSubjectDAO;
|
||||
|
||||
define('FORM_METADATA', 'metadata');
|
||||
|
||||
class PKPMetadataForm extends FormComponent
|
||||
{
|
||||
public $id = FORM_METADATA;
|
||||
public $method = 'PUT';
|
||||
public Context $context;
|
||||
public Publication $publication;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param Publication $publication The publication to change settings for
|
||||
* @param Context $context The journal or press of the submission.
|
||||
* @param string $suggestionUrlBase The base URL to get suggestions for controlled vocab.
|
||||
*/
|
||||
public function __construct(string $action, array $locales, Publication $publication, Context $context, string $suggestionUrlBase)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
$this->context = $context;
|
||||
$this->publication = $publication;
|
||||
|
||||
if ($this->enabled('keywords')) {
|
||||
$this->addField(new FieldControlledVocab('keywords', [
|
||||
'label' => __('common.keywords'),
|
||||
'tooltip' => __('manager.setup.metadata.keywords.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionKeywordDAO::CONTROLLED_VOCAB_SUBMISSION_KEYWORD, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('keywords'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('subjects')) {
|
||||
$this->addField(new FieldControlledVocab('subjects', [
|
||||
'label' => __('common.subjects'),
|
||||
'tooltip' => __('manager.setup.metadata.subjects.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionSubjectDAO::CONTROLLED_VOCAB_SUBMISSION_SUBJECT, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('subjects'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('disciplines')) {
|
||||
$this->addField(new FieldControlledVocab('disciplines', [
|
||||
'label' => __('search.discipline'),
|
||||
'tooltip' => __('manager.setup.metadata.disciplines.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionDisciplineDAO::CONTROLLED_VOCAB_SUBMISSION_DISCIPLINE, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('disciplines'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('languages')) {
|
||||
$this->addField(new FieldControlledVocab('languages', [
|
||||
'label' => __('common.languages'),
|
||||
'tooltip' => __('manager.setup.metadata.languages.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionLanguageDAO::CONTROLLED_VOCAB_SUBMISSION_LANGUAGE, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('languages'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('agencies')) {
|
||||
$this->addField(new FieldControlledVocab('supportingAgencies', [
|
||||
'label' => __('submission.supportingAgencies'),
|
||||
'tooltip' => __('manager.setup.metadata.agencies.description'),
|
||||
'isMultilingual' => true,
|
||||
'apiUrl' => str_replace('__vocab__', SubmissionAgencyDAO::CONTROLLED_VOCAB_SUBMISSION_AGENCY, $suggestionUrlBase),
|
||||
'locales' => $this->locales,
|
||||
'value' => (array) $publication->getData('supportingAgencies'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('coverage')) {
|
||||
$this->addField(new FieldText('coverage', [
|
||||
'label' => __('manager.setup.metadata.coverage'),
|
||||
'tooltip' => __('manager.setup.metadata.coverage.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $publication->getData('coverage'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('rights')) {
|
||||
$this->addField(new FieldText('rights', [
|
||||
'label' => __('submission.rights'),
|
||||
'tooltip' => __('manager.setup.metadata.rights.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $publication->getData('rights'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('source')) {
|
||||
$this->addField(new FieldText('source', [
|
||||
'label' => __('common.source'),
|
||||
'tooltip' => __('manager.setup.metadata.source.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $publication->getData('source'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('type')) {
|
||||
$this->addField(new FieldText('type', [
|
||||
'label' => __('common.type'),
|
||||
'tooltip' => __('manager.setup.metadata.type.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $publication->getData('type'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('dataAvailability')) {
|
||||
$this->addField(new FieldRichTextarea('dataAvailability', [
|
||||
'label' => __('submission.dataAvailability'),
|
||||
'tooltip' => __('manager.setup.metadata.dataAvailability.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $publication->getData('dataAvailability'),
|
||||
]));
|
||||
}
|
||||
|
||||
if ($this->enabled('pub-id::publisher-id')) {
|
||||
$this->addField(new FieldText('pub-id::publisher-id', [
|
||||
'label' => __('submission.publisherId'),
|
||||
'tooltip' => __('submission.publisherId.description'),
|
||||
'value' => $publication->getData('pub-id::publisher-id'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a metadata field is enabled in this form
|
||||
*/
|
||||
protected function enabled(string $setting): bool
|
||||
{
|
||||
if ($setting === 'pub-id::publisher-id') {
|
||||
return in_array('publication', (array) $this->context->getData('enablePublisherId'));
|
||||
}
|
||||
return (bool) $this->context->getData($setting);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user